Merge changes from topic "hapticInterface"

* changes:
  Interface between audio server and vibrator service
  Apply intensity control for haptic data.
gugelfrei
Eric Laurent 6 years ago committed by Android (Google) Code Review
commit aaaa003cb5

@ -19,7 +19,8 @@ LOCAL_SHARED_LIBRARIES := \
libnbaio \
libnblog \
libsoundtriggerservice \
libutils
libutils \
libvibrator
# TODO oboeservice is the old folder name for aaudioservice. It will be changed.
LOCAL_C_INCLUDES := \

@ -80,6 +80,7 @@ public:
MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output
// for haptic
HAPTIC_ENABLED = 0x4007, // Set haptic data from this track should be played or not.
HAPTIC_INTENSITY = 0x4008, // Set the intensity to play haptic data.
// for target RESAMPLE
SAMPLE_RATE = 0x4100, // Configure sample rate conversion on this track name;
// parameter 'value' is the new sample rate in Hz.
@ -102,6 +103,31 @@ public:
// parameter 'value' is a pointer to the new playback rate.
};
enum { // Haptic intensity, should keep consistent with VibratorService
HAPTIC_SCALE_VERY_LOW = -2,
HAPTIC_SCALE_LOW = -1,
HAPTIC_SCALE_NONE = 0,
HAPTIC_SCALE_HIGH = 1,
HAPTIC_SCALE_VERY_HIGH = 2,
};
typedef int32_t haptic_intensity_t;
static constexpr float HAPTIC_SCALE_VERY_LOW_RATIO = 2 / 3;
static constexpr float HAPTIC_SCALE_LOW_RATIO = 3 / 4;
static const CONSTEXPR float HAPTIC_MAX_AMPLITUDE_FLOAT = 1.0f;
static inline bool isValidHapticIntensity(haptic_intensity_t hapticIntensity) {
switch (hapticIntensity) {
case HAPTIC_SCALE_VERY_LOW:
case HAPTIC_SCALE_LOW:
case HAPTIC_SCALE_NONE:
case HAPTIC_SCALE_HIGH:
case HAPTIC_SCALE_VERY_HIGH:
return true;
default:
return false;
}
}
AudioMixer(size_t frameCount, uint32_t sampleRate)
: mSampleRate(sampleRate)
, mFrameCount(frameCount) {
@ -147,6 +173,7 @@ public:
}
}
(this->*mHook)();
processHapticData();
}
size_t getUnreleasedFrames(int name) const;
@ -364,6 +391,7 @@ private:
// Haptic
bool mHapticPlaybackEnabled;
haptic_intensity_t mHapticIntensity;
audio_channel_mask_t mHapticChannelMask;
uint32_t mHapticChannelCount;
audio_channel_mask_t mMixerHapticChannelMask;
@ -374,6 +402,37 @@ private:
uint32_t mAdjustNonDestructiveOutChannelCount;
bool mKeepContractedChannels;
float getHapticScaleGamma() const {
// Need to keep consistent with the value in VibratorService.
switch (mHapticIntensity) {
case HAPTIC_SCALE_VERY_LOW:
return 2.0f;
case HAPTIC_SCALE_LOW:
return 1.5f;
case HAPTIC_SCALE_HIGH:
return 0.5f;
case HAPTIC_SCALE_VERY_HIGH:
return 0.25f;
default:
return 1.0f;
}
}
float getHapticMaxAmplitudeRatio() const {
// Need to keep consistent with the value in VibratorService.
switch (mHapticIntensity) {
case HAPTIC_SCALE_VERY_LOW:
return HAPTIC_SCALE_VERY_LOW_RATIO;
case HAPTIC_SCALE_LOW:
return HAPTIC_SCALE_LOW_RATIO;
case HAPTIC_SCALE_NONE:
case HAPTIC_SCALE_HIGH:
case HAPTIC_SCALE_VERY_HIGH:
default:
return 1.0f;
}
}
private:
// hooks
void track__genericResample(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
@ -410,6 +469,8 @@ private:
template <int MIXTYPE, typename TO, typename TI, typename TA>
void process__noResampleOneTrack();
void processHapticData();
static process_hook_t getProcessHook(int processType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat);

@ -167,6 +167,7 @@ status_t AudioMixer::create(
t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
// haptic
t->mHapticPlaybackEnabled = false;
t->mHapticIntensity = HAPTIC_SCALE_NONE;
t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
t->mMixerHapticChannelCount = 0;
t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
@ -717,6 +718,12 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
track->prepareForAdjustChannels();
}
} break;
case HAPTIC_INTENSITY: {
const haptic_intensity_t hapticIntensity = static_cast<haptic_intensity_t>(valueInt);
if (track->mHapticIntensity != hapticIntensity) {
track->mHapticIntensity = hapticIntensity;
}
} break;
default:
LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
}
@ -1846,6 +1853,40 @@ void AudioMixer::process__noResampleOneTrack()
}
}
void AudioMixer::processHapticData()
{
// Need to keep consistent with VibrationEffect.scale(int, float, int)
for (const auto &pair : mGroups) {
// process by group of tracks with same output main buffer.
const auto &group = pair.second;
for (const int name : group) {
const std::shared_ptr<Track> &t = mTracks[name];
if (t->mHapticPlaybackEnabled) {
size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount;
float gamma = t->getHapticScaleGamma();
float maxAmplitudeRatio = t->getHapticMaxAmplitudeRatio();
uint8_t* buffer = (uint8_t*)pair.first + mFrameCount * audio_bytes_per_frame(
t->mMixerChannelCount, t->mMixerFormat);
switch (t->mMixerFormat) {
// Mixer format should be AUDIO_FORMAT_PCM_FLOAT.
case AUDIO_FORMAT_PCM_FLOAT: {
float* fout = (float*) buffer;
for (size_t i = 0; i < sampleCount; i++) {
float mul = fout[i] >= 0 ? 1.0 : -1.0;
fout[i] = powf(fabsf(fout[i] / HAPTIC_MAX_AMPLITUDE_FLOAT), gamma)
* maxAmplitudeRatio * HAPTIC_MAX_AMPLITUDE_FLOAT * mul;
}
} break;
default:
LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat);
break;
}
break;
}
}
}
}
/* This track hook is called to do resampling then mixing,
* pulling from the track's upstream AudioBufferProvider.
*

@ -38,7 +38,8 @@ LOCAL_SHARED_LIBRARIES := \
libpowermanager \
libmediautils \
libmemunreachable \
libmedia_helper
libmedia_helper \
libvibrator
LOCAL_STATIC_LIBRARIES := \
libcpustats \

@ -27,6 +27,7 @@
#include <sys/time.h>
#include <sys/resource.h>
#include <android/os/IExternalVibratorService.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <utils/Log.h>
@ -122,6 +123,21 @@ static void sMediaLogInit()
}
}
// Keep a strong reference to external vibrator service
static sp<os::IExternalVibratorService> sExternalVibratorService;
static sp<os::IExternalVibratorService> getExternalVibratorService() {
if (sExternalVibratorService == 0) {
sp <IBinder> binder = defaultServiceManager()->getService(
String16("external_vibrator_service"));
if (binder != 0) {
sExternalVibratorService =
interface_cast<os::IExternalVibratorService>(binder);
}
}
return sExternalVibratorService;
}
// ----------------------------------------------------------------------------
std::string formatToString(audio_format_t format) {
@ -318,6 +334,27 @@ status_t AudioFlinger::openMmapStream(MmapStreamInterface::stream_direction_t di
return ret;
}
/* static */
int AudioFlinger::onExternalVibrationStart(const sp<os::ExternalVibration>& externalVibration) {
sp<os::IExternalVibratorService> evs = getExternalVibratorService();
if (evs != 0) {
int32_t ret;
binder::Status status = evs->onExternalVibrationStart(*externalVibration, &ret);
if (status.isOk()) {
return ret;
}
}
return AudioMixer::HAPTIC_SCALE_NONE;
}
/* static */
void AudioFlinger::onExternalVibrationStop(const sp<os::ExternalVibration>& externalVibration) {
sp<os::IExternalVibratorService> evs = getExternalVibratorService();
if (evs != 0) {
evs->onExternalVibrationStop(*externalVibration);
}
}
static const char * const audio_interfaces[] = {
AUDIO_HARDWARE_MODULE_ID_PRIMARY,
AUDIO_HARDWARE_MODULE_ID_A2DP,

@ -30,6 +30,7 @@
#include <sys/types.h>
#include <limits.h>
#include <android/os/BnExternalVibrationController.h>
#include <android-base/macros.h>
#include <cutils/atomic.h>
@ -84,6 +85,8 @@
#include <private/media/AudioEffectShared.h>
#include <private/media/AudioTrackShared.h>
#include <vibrator/ExternalVibration.h>
#include "android/media/BnAudioRecord.h"
namespace android {
@ -284,6 +287,9 @@ public:
const sp<MmapStreamCallback>& callback,
sp<MmapStreamInterface>& interface,
audio_port_handle_t *handle);
static int onExternalVibrationStart(const sp<os::ExternalVibration>& externalVibration);
static void onExternalVibrationStop(const sp<os::ExternalVibration>& externalVibration);
private:
// FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
static const size_t kLogMemorySize = 400 * 1024;

@ -295,6 +295,8 @@ void FastMixer::onStateChange()
(void *)(uintptr_t)mSinkChannelMask);
mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
(void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
(void *)(uintptr_t)fastTrack->mHapticIntensity);
mMixer->enable(name);
}
mGenerations[i] = fastTrack->mGeneration;
@ -333,6 +335,8 @@ void FastMixer::onStateChange()
(void *)(uintptr_t)mSinkChannelMask);
mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_ENABLED,
(void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
mMixer->setParameter(name, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
(void *)(uintptr_t)fastTrack->mHapticIntensity);
// already enabled
}
mGenerations[i] = fastTrack->mGeneration;

@ -19,6 +19,7 @@
#include <audio_utils/minifloat.h>
#include <system/audio.h>
#include <media/AudioMixer.h>
#include <media/ExtendedAudioBufferProvider.h>
#include <media/nbaio/NBAIO.h>
#include <media/nblog/NBLog.h>
@ -48,6 +49,8 @@ struct FastTrack {
audio_format_t mFormat; // track format
int mGeneration; // increment when any field is assigned
bool mHapticPlaybackEnabled = false; // haptic playback is enabled or not
AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE; // intensity of
// haptic data
};
// Represents a single state of the fast mixer

@ -119,6 +119,15 @@ public:
void setHapticPlaybackEnabled(bool hapticPlaybackEnabled) {
mHapticPlaybackEnabled = hapticPlaybackEnabled;
}
/** Return at what intensity to play haptics, used in mixer. */
AudioMixer::haptic_intensity_t getHapticIntensity() const { return mHapticIntensity; }
/** Set intensity of haptic playback, should be set after querying vibrator service. */
void setHapticIntensity(AudioMixer::haptic_intensity_t hapticIntensity) {
if (AudioMixer::isValidHapticIntensity(hapticIntensity)) {
mHapticIntensity = hapticIntensity;
}
}
sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
protected:
// for numerous
@ -197,6 +206,18 @@ protected:
sp<media::VolumeHandler> mVolumeHandler; // handles multiple VolumeShaper configs and operations
bool mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not
// intensity to play haptic data
AudioMixer::haptic_intensity_t mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE;
class AudioVibrationController : public os::BnExternalVibrationController {
public:
explicit AudioVibrationController(Track* track) : mTrack(track) {}
binder::Status mute(/*out*/ bool *ret) override;
binder::Status unmute(/*out*/ bool *ret) override;
private:
Track* const mTrack;
};
sp<AudioVibrationController> mAudioVibrationController;
sp<os::ExternalVibration> mExternalVibration;
private:
// The following fields are only for fast tracks, and should be in a subclass

@ -2358,15 +2358,23 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
}
// Disable all haptic playback for all other active tracks when haptic playback is supported
// and the track contains haptic channels. Enable haptic playback for current track.
// TODO: Request actual haptic playback status from vibrator service
if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
&& mHapticChannelMask != AUDIO_CHANNEL_NONE) {
for (auto &t : mActiveTracks) {
t->setHapticPlaybackEnabled(false);
// Unlock due to VibratorService will lock for this call and will
// call Tracks.mute/unmute which also require thread's lock.
mLock.unlock();
const int intensity = AudioFlinger::onExternalVibrationStart(
track->getExternalVibration());
mLock.lock();
// Haptic playback should be enabled by vibrator service.
if (track->getHapticPlaybackEnabled()) {
// Disable haptic playback of all active track to ensure only
// one track playing haptic if current track should play haptic.
for (const auto &t : mActiveTracks) {
t->setHapticPlaybackEnabled(false);
}
}
track->setHapticPlaybackEnabled(true);
track->setHapticIntensity(intensity);
}
track->mResetDone = false;
@ -3760,7 +3768,6 @@ bool AudioFlinger::PlaybackThread::threadLoop()
// removeTracks_l() must be called with ThreadBase::mLock held
void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove)
{
bool enabledHapticTracksRemoved = false;
for (const auto& track : tracksToRemove) {
mActiveTracks.remove(track);
ALOGV("%s(%d): removing track on session %d", __func__, track->id(), track->sessionId());
@ -3782,17 +3789,13 @@ void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tra
// remove from our tracks vector
removeTrack_l(track);
}
enabledHapticTracksRemoved |= track->getHapticPlaybackEnabled();
}
// If the thread supports haptic playback and the track playing haptic data was removed,
// enable haptic playback on the first active track that contains haptic channels.
// TODO: Query vibrator service to know which track should enable haptic playback.
if (enabledHapticTracksRemoved && mHapticChannelMask != AUDIO_CHANNEL_NONE) {
for (auto &t : mActiveTracks) {
if (t->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) {
t->setHapticPlaybackEnabled(true);
break;
}
if ((track->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
&& mHapticChannelCount > 0) {
mLock.unlock();
// Unlock due to VibratorService will lock for this call and will
// call Tracks.mute/unmute which also require thread's lock.
AudioFlinger::onExternalVibrationStop(track->getExternalVibration());
mLock.lock();
}
}
}
@ -4615,6 +4618,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
fastTrack->mChannelMask = track->mChannelMask;
fastTrack->mFormat = track->mFormat;
fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled();
fastTrack->mHapticIntensity = track->getHapticIntensity();
fastTrack->mGeneration++;
state->mTrackMask |= 1 << j;
didModify = true;
@ -4937,6 +4941,10 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
trackId,
AudioMixer::TRACK,
AudioMixer::HAPTIC_ENABLED, (void *)(uintptr_t)track->getHapticPlaybackEnabled());
mAudioMixer->setParameter(
trackId,
AudioMixer::TRACK,
AudioMixer::HAPTIC_INTENSITY, (void *)(uintptr_t)track->getHapticIntensity());
// reset retry count
track->mRetryCount = kMaxTrackRetries;

@ -451,6 +451,12 @@ AudioFlinger::PlaybackThread::Track::Track(
mTee.setId(std::string("_") + std::to_string(mThreadIoHandle)
+ "_" + std::to_string(mId));
#endif
if (channelMask & AUDIO_CHANNEL_HAPTIC_ALL) {
mAudioVibrationController = new AudioVibrationController(this);
mExternalVibration = new os::ExternalVibration(
mUid, "" /* pkg */, mAttr, mAudioVibrationController);
}
}
AudioFlinger::PlaybackThread::Track::~Track()
@ -1336,6 +1342,40 @@ void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo(
mServerLatencyMs.store(latencyMs);
}
binder::Status AudioFlinger::PlaybackThread::Track::AudioVibrationController::mute(
/*out*/ bool *ret) {
*ret = false;
sp<ThreadBase> thread = mTrack->mThread.promote();
if (thread != 0) {
// Lock for updating mHapticPlaybackEnabled.
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
&& playbackThread->mHapticChannelCount > 0) {
mTrack->setHapticPlaybackEnabled(false);
*ret = true;
}
}
return binder::Status::ok();
}
binder::Status AudioFlinger::PlaybackThread::Track::AudioVibrationController::unmute(
/*out*/ bool *ret) {
*ret = false;
sp<ThreadBase> thread = mTrack->mThread.promote();
if (thread != 0) {
// Lock for updating mHapticPlaybackEnabled.
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
if ((mTrack->channelMask() & AUDIO_CHANNEL_HAPTIC_ALL) != AUDIO_CHANNEL_NONE
&& playbackThread->mHapticChannelCount > 0) {
mTrack->setHapticPlaybackEnabled(true);
*ret = true;
}
}
return binder::Status::ok();
}
// ----------------------------------------------------------------------------
#undef LOG_TAG
#define LOG_TAG "AF::OutputTrack"

Loading…
Cancel
Save