Issue 2667801: [Audio Effect Framework] AudioFlinger, AudioMixer AudioTrack modifications.
First drop of audio framework modifications for audio effects support. - AudioTrack/AudioRecord: Added support for auxiliary effects in AudioTrack Added support for audio sessions Fixed left right channel inversion in setVolume() - IAudioFlinger: Added interface methods for effect enumeraiton and instantiation Added support for audio sessions. - IAudioTrack: Added method to attach auxiliary effect. - AudioFlinger Created new classes to control effect engines in effect library and manage effect connections to tracks or output mix: EffectModule: wrapper object controlling the effect engine implementation in the effect library. There is one EffectModule per instance of an effect in a given audio session EffectChain: group of effects associated to one audio session. There is one EffectChain per audio session. EffectChain for session 0 is for output mix effects, other chains are attached to audio tracks with same session ID. Each chain contains a variable number of EffectModules EffectHandle: implements the IEffect interface. There is one EffectHandle object for each application controlling (or using) an effect module. THe EffectModule maintians a list of EffectHandles. Added support for effect modules and effect chains creation in PlaybackThread. modified mixer thread loop to allow track volume control by effect modules and call effect processing. -AudioMixer Each track now specifies its output buffer used by mixer for accumulation Modified mixer process functions to process tracks by groups of tracks with same buffer Modified track process functions to support accumulation to auxiliary channel Change-Id: I26d5f7c9e070a89bdd383e1a659f8b7ca150379c
This commit is contained in:
@@ -142,7 +142,8 @@ public:
|
||||
uint32_t flags = 0,
|
||||
callback_t cbf = 0,
|
||||
void* user = 0,
|
||||
int notificationFrames = 0);
|
||||
int notificationFrames = 0,
|
||||
int sessionId = 0);
|
||||
|
||||
|
||||
/* Terminates the AudioRecord and unregisters it from AudioFlinger.
|
||||
@@ -168,7 +169,8 @@ public:
|
||||
callback_t cbf = 0,
|
||||
void* user = 0,
|
||||
int notificationFrames = 0,
|
||||
bool threadCanCallJava = false);
|
||||
bool threadCanCallJava = false,
|
||||
int sessionId = 0);
|
||||
|
||||
|
||||
/* Result of constructing the AudioRecord. This must be checked
|
||||
@@ -270,6 +272,16 @@ public:
|
||||
*/
|
||||
audio_io_handle_t getInput();
|
||||
|
||||
/* returns the audio session ID associated to this AudioRecord.
|
||||
*
|
||||
* Parameters:
|
||||
* none.
|
||||
*
|
||||
* Returned value:
|
||||
* AudioRecord session ID.
|
||||
*/
|
||||
int getSessionId();
|
||||
|
||||
/* obtains a buffer of "frameCount" frames. The buffer must be
|
||||
* filled entirely. If the track is stopped, obtainBuffer() returns
|
||||
* STOPPED instead of NO_ERROR as long as there are buffers availlable,
|
||||
@@ -356,6 +368,7 @@ private:
|
||||
uint32_t mFlags;
|
||||
uint32_t mChannels;
|
||||
audio_io_handle_t mInput;
|
||||
int mSessionId;
|
||||
};
|
||||
|
||||
}; // namespace android
|
||||
|
||||
@@ -230,6 +230,8 @@ public:
|
||||
static status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int stream = DEFAULT);
|
||||
|
||||
static unsigned int getInputFramesLost(audio_io_handle_t ioHandle);
|
||||
|
||||
static int newAudioSessionId();
|
||||
//
|
||||
// AudioPolicyService interface
|
||||
//
|
||||
|
||||
@@ -138,7 +138,8 @@ public:
|
||||
uint32_t flags = 0,
|
||||
callback_t cbf = 0,
|
||||
void* user = 0,
|
||||
int notificationFrames = 0);
|
||||
int notificationFrames = 0,
|
||||
int sessionId = 0);
|
||||
|
||||
/* Creates an audio track and registers it with AudioFlinger. With this constructor,
|
||||
* The PCM data to be rendered by AudioTrack is passed in a shared memory buffer
|
||||
@@ -157,7 +158,8 @@ public:
|
||||
uint32_t flags = 0,
|
||||
callback_t cbf = 0,
|
||||
void* user = 0,
|
||||
int notificationFrames = 0);
|
||||
int notificationFrames = 0,
|
||||
int sessionId = 0);
|
||||
|
||||
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
|
||||
* Also destroys all resources assotiated with the AudioTrack.
|
||||
@@ -182,7 +184,8 @@ public:
|
||||
void* user = 0,
|
||||
int notificationFrames = 0,
|
||||
const sp<IMemory>& sharedBuffer = 0,
|
||||
bool threadCanCallJava = false);
|
||||
bool threadCanCallJava = false,
|
||||
int sessionId = 0);
|
||||
|
||||
|
||||
/* Result of constructing the AudioTrack. This must be checked
|
||||
@@ -239,10 +242,17 @@ public:
|
||||
|
||||
|
||||
/* set volume for this track, mostly used for games' sound effects
|
||||
* left and right volumes. Levels must be <= 1.0.
|
||||
*/
|
||||
void setVolume(float left, float right);
|
||||
status_t setVolume(float left, float right);
|
||||
void getVolume(float* left, float* right);
|
||||
|
||||
/* set the send level for this track. An auxiliary effect should be attached
|
||||
* to the track with attachEffect(). Level must be <= 1.0.
|
||||
*/
|
||||
status_t setSendLevel(float level);
|
||||
void getSendLevel(float* level);
|
||||
|
||||
/* set sample rate for this track, mostly used for games' sound effects
|
||||
*/
|
||||
status_t setSampleRate(int sampleRate);
|
||||
@@ -340,6 +350,31 @@ public:
|
||||
*/
|
||||
audio_io_handle_t getOutput();
|
||||
|
||||
/* returns the unique ID associated to this track.
|
||||
*
|
||||
* Parameters:
|
||||
* none.
|
||||
*
|
||||
* Returned value:
|
||||
* AudioTrack ID.
|
||||
*/
|
||||
int getSessionId();
|
||||
|
||||
|
||||
/* Attach track auxiliary output to specified effect. Used effectId = 0
|
||||
* to detach track from effect.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* effectId: effectId obtained from AudioEffect::id().
|
||||
*
|
||||
* Returned status (from utils/Errors.h) can be:
|
||||
* - NO_ERROR: successful operation
|
||||
* - INVALID_OPERATION: the effect is not an auxiliary effect.
|
||||
* - BAD_VALUE: The specified effect ID is invalid
|
||||
*/
|
||||
status_t attachAuxEffect(int effectId);
|
||||
|
||||
/* obtains a buffer of "frameCount" frames. The buffer must be
|
||||
* filled entirely. If the track is stopped, obtainBuffer() returns
|
||||
* STOPPED instead of NO_ERROR as long as there are buffers availlable,
|
||||
@@ -406,6 +441,7 @@ private:
|
||||
sp<AudioTrackThread> mAudioTrackThread;
|
||||
|
||||
float mVolume[2];
|
||||
float mSendLevel;
|
||||
uint32_t mFrameCount;
|
||||
|
||||
audio_track_cblk_t* mCblk;
|
||||
@@ -431,6 +467,7 @@ private:
|
||||
uint32_t mNewPosition;
|
||||
uint32_t mUpdatePeriod;
|
||||
uint32_t mFlags;
|
||||
int mSessionId;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -114,7 +114,8 @@ typedef struct effect_descriptor_s {
|
||||
// +---------------------------+-----------+-----------------------------------
|
||||
// | Volume management | 5..6 | 0 none
|
||||
// | | | 1 implements volume control
|
||||
// | | | 2..3 reserved
|
||||
// | | | 2 requires volume indication
|
||||
// | | | 3 reserved
|
||||
// +---------------------------+-----------+-----------------------------------
|
||||
// | Device management | 7..8 | 0 none
|
||||
// | | | 1 requires device updates
|
||||
@@ -154,6 +155,7 @@ typedef struct effect_descriptor_s {
|
||||
// volume control
|
||||
#define EFFECT_FLAG_VOLUME_MASK 0x00000060
|
||||
#define EFFECT_FLAG_VOLUME_CTRL 0x00000020
|
||||
#define EFFECT_FLAG_VOLUME_IND 0x00000040
|
||||
#define EFFECT_FLAG_VOLUME_NONE 0x00000000
|
||||
|
||||
// device control
|
||||
@@ -296,10 +298,12 @@ struct effect_interface_s {
|
||||
// | Set and get volume. Used by | EFFECT_CMD_SET_VOLUME | size: n * sizeof(uint32_t) | size: n * sizeof(uint32_t)
|
||||
// | audio framework to delegate | | data: volume for each channel | data: volume for each channel
|
||||
// | volume control to effect engine| | defined in effect_config_t in | defined in effect_config_t in
|
||||
// | The engine must return the | | 8.24 fixed point format | 8.24 fixed point format
|
||||
// | volume that should be applied | | |
|
||||
// | before the effect is processed | | |
|
||||
// | The overall volume (the volume | | |
|
||||
// | If volume control flag is set | | 8.24 fixed point format | 8.24 fixed point format
|
||||
// | in the effect descriptor, the | | | It is legal to receive a null
|
||||
// | effect engine must return the | | | pointer as pReplyData in which
|
||||
// | volume that should be applied | | | case the effect framework has
|
||||
// | before the effect is processed | | | delegated volume control to
|
||||
// | The overall volume (the volume | | | another effect.
|
||||
// | actually applied by the effect | | |
|
||||
// | multiplied by the returned | | |
|
||||
// | value) should match the | | |
|
||||
@@ -370,7 +374,7 @@ typedef struct buffer_provider_s {
|
||||
// structure that defines both input and output buffer configurations and is
|
||||
// passed by the EFFECT_CMD_CONFIGURE command.
|
||||
typedef struct buffer_config_s {
|
||||
audio_buffer_t buffer; // buffer for use by process() function is not passed explicitly
|
||||
audio_buffer_t buffer; // buffer for use by process() function if not passed explicitly
|
||||
uint32_t samplingRate; // sampling rate
|
||||
uint32_t channels; // channel mask (see audio_channels_e in AudioCommon.h)
|
||||
buffer_provider_t bufferProvider; // buffer provider
|
||||
@@ -457,7 +461,7 @@ typedef struct effect_param_s {
|
||||
//
|
||||
// Function: EffectQueryNumberEffects
|
||||
//
|
||||
// Description: Returns the number of different effect exposed by the
|
||||
// Description: Returns the number of different effects exposed by the
|
||||
// library. Each effect must have a unique effect uuid (see
|
||||
// effect_descriptor_t). This function together with EffectQueryNext()
|
||||
// is used to enumerate all effects present in the library.
|
||||
@@ -475,7 +479,7 @@ typedef struct effect_param_s {
|
||||
// *pNumEffects: updated with number of effects in library
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
typedef int32_t (*effect_QueryNumberEffects_t)(int32_t *pNumEffects);
|
||||
typedef int32_t (*effect_QueryNumberEffects_t)(uint32_t *pNumEffects);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -521,7 +525,7 @@ typedef int32_t (*effect_QueryNextEffect_t)(effect_descriptor_t *pDescriptor);
|
||||
// returned value: 0 successful operation.
|
||||
// -ENODEV library failed to initialize
|
||||
// -EINVAL invalid pEffectUuid or pInterface
|
||||
// -ENOENT No effect with this uuid found
|
||||
// -ENOENT no effect with this uuid found
|
||||
// *pInterface: updated with the effect interface handle.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -34,7 +34,7 @@ extern "C" {
|
||||
//
|
||||
// Function: EffectQueryNumberEffects
|
||||
//
|
||||
// Description: Returns the number of different effect in all loaded libraries.
|
||||
// Description: Returns the number of different effects in all loaded libraries.
|
||||
// Each effect must have a different effect uuid (see
|
||||
// effect_descriptor_t). This function together with EffectQueryNext()
|
||||
// is used to enumerate all effects present in all loaded libraries.
|
||||
@@ -52,7 +52,7 @@ extern "C" {
|
||||
// *pNumEffects: updated with number of effects in factory
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int EffectQueryNumberEffects(int *pNumEffects);
|
||||
int EffectQueryNumberEffects(uint32_t *pNumEffects);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -98,7 +98,7 @@ int EffectQueryNext(effect_descriptor_t *pDescriptor);
|
||||
// returned value: 0 successful operation.
|
||||
// -ENODEV factory failed to initialize
|
||||
// -EINVAL invalid pEffectUuid or pInterface
|
||||
// -ENOENT No effect with this uuid found
|
||||
// -ENOENT no effect with this uuid found
|
||||
// *pInterface: updated with the effect interface.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -140,7 +140,7 @@ int EffectRelease(effect_interface_t interface);
|
||||
//
|
||||
// Output:
|
||||
// returned value: 0 successful operation.
|
||||
// -ENODEV Effect factory not initialized or
|
||||
// -ENODEV effect factory not initialized or
|
||||
// library could not be loaded or
|
||||
// library does not implement required functions
|
||||
// -EINVAL invalid libPath string or handle
|
||||
@@ -159,7 +159,7 @@ int EffectLoadLibrary(const char *libPath, int *handle);
|
||||
//
|
||||
// Output:
|
||||
// returned value: 0 successful operation.
|
||||
// -ENODEV Effect factory not initialized
|
||||
// -ENODEV effect factory not initialized
|
||||
// -ENOENT invalid handle
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -184,7 +184,7 @@ int EffectUnloadLibrary(int handle);
|
||||
// returned value: 0 successful operation.
|
||||
// -ENODEV factory failed to initialize
|
||||
// -EINVAL invalid pEffectUuid or pDescriptor
|
||||
// -ENOENT No effect with this uuid found
|
||||
// -ENOENT no effect with this uuid found
|
||||
// *pDescriptor: updated with the effect descriptor.
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
#include <media/IAudioTrack.h>
|
||||
#include <media/IAudioRecord.h>
|
||||
#include <media/IAudioFlingerClient.h>
|
||||
#include <media/EffectApi.h>
|
||||
#include <media/IEffect.h>
|
||||
#include <media/IEffectClient.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
namespace android {
|
||||
@@ -51,6 +54,7 @@ public:
|
||||
uint32_t flags,
|
||||
const sp<IMemory>& sharedBuffer,
|
||||
int output,
|
||||
int *sessionId,
|
||||
status_t *status) = 0;
|
||||
|
||||
virtual sp<IAudioRecord> openRecord(
|
||||
@@ -61,6 +65,7 @@ public:
|
||||
int channelCount,
|
||||
int frameCount,
|
||||
uint32_t flags,
|
||||
int *sessionId,
|
||||
status_t *status) = 0;
|
||||
|
||||
/* query the audio hardware state. This state never changes,
|
||||
@@ -134,6 +139,28 @@ public:
|
||||
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output) = 0;
|
||||
|
||||
virtual unsigned int getInputFramesLost(int ioHandle) = 0;
|
||||
|
||||
virtual int newAudioSessionId() = 0;
|
||||
|
||||
virtual status_t loadEffectLibrary(const char *libPath, int *handle) = 0;
|
||||
|
||||
virtual status_t unloadEffectLibrary(int handle) = 0;
|
||||
|
||||
virtual status_t queryNumberEffects(uint32_t *numEffects) = 0;
|
||||
|
||||
virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor) = 0;
|
||||
|
||||
virtual status_t getEffectDescriptor(effect_uuid_t *pEffectUUID, effect_descriptor_t *pDescriptor) = 0;
|
||||
|
||||
virtual sp<IEffect> createEffect(pid_t pid,
|
||||
effect_descriptor_t *pDesc,
|
||||
const sp<IEffectClient>& client,
|
||||
int32_t priority,
|
||||
int output,
|
||||
int sessionId,
|
||||
status_t *status,
|
||||
int *id,
|
||||
int *enabled) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +62,11 @@ public:
|
||||
*/
|
||||
virtual void pause() = 0;
|
||||
|
||||
/* Attach track auxiliary output to specified effect. Use effectId = 0
|
||||
* to detach track from effect.
|
||||
*/
|
||||
virtual status_t attachAuxEffect(int effectId) = 0;
|
||||
|
||||
/* get this tracks control block */
|
||||
virtual sp<IMemory> getCblk() const = 0;
|
||||
};
|
||||
|
||||
51
include/private/media/AudioEffectShared.h
Normal file
51
include/private/media/AudioEffectShared.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_EFFECTCBASESHARED_H
|
||||
#define ANDROID_EFFECTCBASESHARED_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <utils/threads.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Size of buffer used to exchange parameters between application and mediaserver processes.
|
||||
#define EFFECT_PARAM_BUFFER_SIZE 1024
|
||||
|
||||
|
||||
// Shared memory area used to exchange parameters between application and mediaserver
|
||||
// process.
|
||||
struct effect_param_cblk_t
|
||||
{
|
||||
Mutex lock;
|
||||
volatile uint32_t clientIndex; // Current read/write index for application
|
||||
volatile uint32_t serverIndex; // Current read/write index for mediaserver
|
||||
uint8_t* buffer; // start of parameter buffer
|
||||
|
||||
effect_param_cblk_t()
|
||||
: lock(Mutex::SHARED), clientIndex(0), serverIndex(0) {}
|
||||
};
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_EFFECTCBASESHARED_H
|
||||
@@ -78,7 +78,8 @@ struct audio_track_cblk_t
|
||||
uint16_t bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger
|
||||
uint16_t waitTimeMs; // Cumulated wait time
|
||||
|
||||
uint32_t reserved;
|
||||
uint16_t sendLevel;
|
||||
uint16_t reserved;
|
||||
// Cache line boundary (32 bytes)
|
||||
audio_track_cblk_t();
|
||||
uint32_t stepUser(uint32_t frameCount);
|
||||
|
||||
@@ -87,7 +87,8 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
libutils \
|
||||
libbinder \
|
||||
libmedia \
|
||||
libhardware_legacy
|
||||
libhardware_legacy \
|
||||
libeffects
|
||||
|
||||
ifeq ($(strip $(BOARD_USES_GENERIC_AUDIO)),true)
|
||||
LOCAL_STATIC_LIBRARIES += libaudiointerface libaudiopolicybase
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,6 +42,7 @@
|
||||
namespace android {
|
||||
|
||||
class audio_track_cblk_t;
|
||||
class effect_param_cblk_t;
|
||||
class AudioMixer;
|
||||
class AudioBuffer;
|
||||
class AudioResampler;
|
||||
@@ -75,6 +76,7 @@ public:
|
||||
uint32_t flags,
|
||||
const sp<IMemory>& sharedBuffer,
|
||||
int output,
|
||||
int *sessionId,
|
||||
status_t *status);
|
||||
|
||||
virtual uint32_t sampleRate(int output) const;
|
||||
@@ -139,6 +141,28 @@ public:
|
||||
|
||||
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output);
|
||||
|
||||
virtual int newAudioSessionId();
|
||||
|
||||
virtual status_t loadEffectLibrary(const char *libPath, int *handle);
|
||||
|
||||
virtual status_t unloadEffectLibrary(int handle);
|
||||
|
||||
virtual status_t queryNumberEffects(uint32_t *numEffects);
|
||||
|
||||
virtual status_t queryNextEffect(effect_descriptor_t *descriptor);
|
||||
|
||||
virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *descriptor);
|
||||
|
||||
virtual sp<IEffect> createEffect(pid_t pid,
|
||||
effect_descriptor_t *pDesc,
|
||||
const sp<IEffectClient>& effectClient,
|
||||
int32_t priority,
|
||||
int output,
|
||||
int sessionId,
|
||||
status_t *status,
|
||||
int *id,
|
||||
int *enabled);
|
||||
|
||||
enum hardware_call_state {
|
||||
AUDIO_HW_IDLE = 0,
|
||||
AUDIO_HW_INIT,
|
||||
@@ -167,6 +191,7 @@ public:
|
||||
int channelCount,
|
||||
int frameCount,
|
||||
uint32_t flags,
|
||||
int *sessionId,
|
||||
status_t *status);
|
||||
|
||||
virtual status_t onTransact(
|
||||
@@ -233,6 +258,9 @@ private:
|
||||
class DuplicatingThread;
|
||||
class Track;
|
||||
class RecordTrack;
|
||||
class EffectModule;
|
||||
class EffectHandle;
|
||||
class EffectChain;
|
||||
|
||||
class ThreadBase : public Thread {
|
||||
public:
|
||||
@@ -268,13 +296,15 @@ private:
|
||||
int channelCount,
|
||||
int frameCount,
|
||||
uint32_t flags,
|
||||
const sp<IMemory>& sharedBuffer);
|
||||
const sp<IMemory>& sharedBuffer,
|
||||
int sessionId);
|
||||
~TrackBase();
|
||||
|
||||
virtual status_t start() = 0;
|
||||
virtual void stop() = 0;
|
||||
sp<IMemory> getCblk() const;
|
||||
audio_track_cblk_t* cblk() const { return mCblk; }
|
||||
int sessionId() { return mSessionId; }
|
||||
|
||||
protected:
|
||||
friend class ThreadBase;
|
||||
@@ -323,6 +353,7 @@ private:
|
||||
int mClientTid;
|
||||
uint8_t mFormat;
|
||||
uint32_t mFlags;
|
||||
int mSessionId;
|
||||
};
|
||||
|
||||
class ConfigEvent {
|
||||
@@ -405,7 +436,8 @@ private:
|
||||
int format,
|
||||
int channelCount,
|
||||
int frameCount,
|
||||
const sp<IMemory>& sharedBuffer);
|
||||
const sp<IMemory>& sharedBuffer,
|
||||
int sessionId);
|
||||
~Track();
|
||||
|
||||
void dump(char* buffer, size_t size);
|
||||
@@ -424,6 +456,12 @@ private:
|
||||
int type() const {
|
||||
return mStreamType;
|
||||
}
|
||||
status_t attachAuxEffect(int EffectId);
|
||||
void setAuxBuffer(int EffectId, int32_t *buffer);
|
||||
int32_t *auxBuffer() { return mAuxBuffer; }
|
||||
void setMainBuffer(int16_t *buffer) { mMainBuffer = buffer; }
|
||||
int16_t *mainBuffer() { return mMainBuffer; }
|
||||
int auxEffectId() { return mAuxEffectId; }
|
||||
|
||||
|
||||
protected:
|
||||
@@ -464,6 +502,9 @@ private:
|
||||
bool mResetDone;
|
||||
int mStreamType;
|
||||
int mName;
|
||||
int16_t *mMainBuffer;
|
||||
int32_t *mAuxBuffer;
|
||||
int mAuxEffectId;
|
||||
}; // end of Track
|
||||
|
||||
|
||||
@@ -505,7 +546,7 @@ private:
|
||||
DuplicatingThread* mSourceThread;
|
||||
}; // end of OutputTrack
|
||||
|
||||
PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
|
||||
PlaybackThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
|
||||
virtual ~PlaybackThread();
|
||||
|
||||
virtual status_t dump(int fd, const Vector<String16>& args);
|
||||
@@ -538,6 +579,7 @@ private:
|
||||
int channelCount,
|
||||
int frameCount,
|
||||
const sp<IMemory>& sharedBuffer,
|
||||
int sessionId,
|
||||
status_t *status);
|
||||
|
||||
AudioStreamOut* getOutput() { return mOutput; }
|
||||
@@ -549,6 +591,29 @@ private:
|
||||
virtual String8 getParameters(const String8& keys);
|
||||
virtual void audioConfigChanged_l(int event, int param = 0);
|
||||
virtual status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames);
|
||||
int16_t *mixBuffer() { return mMixBuffer; };
|
||||
|
||||
sp<EffectHandle> createEffect_l(
|
||||
const sp<AudioFlinger::Client>& client,
|
||||
const sp<IEffectClient>& effectClient,
|
||||
int32_t priority,
|
||||
int sessionId,
|
||||
effect_descriptor_t *desc,
|
||||
int *enabled,
|
||||
status_t *status);
|
||||
|
||||
bool hasAudioSession(int sessionId);
|
||||
sp<EffectChain> getEffectChain(int sessionId);
|
||||
sp<EffectChain> getEffectChain_l(int sessionId);
|
||||
status_t addEffectChain_l(const sp<EffectChain>& chain);
|
||||
size_t removeEffectChain_l(const sp<EffectChain>& chain);
|
||||
void lockEffectChains_l();
|
||||
void unlockEffectChains();
|
||||
|
||||
sp<AudioFlinger::EffectModule> getEffect_l(int sessionId, int effectId);
|
||||
void detachAuxEffect_l(int effectId);
|
||||
status_t attachAuxEffect(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
|
||||
status_t attachAuxEffect_l(const sp<AudioFlinger::PlaybackThread::Track> track, int EffectId);
|
||||
|
||||
struct stream_type_t {
|
||||
stream_type_t()
|
||||
@@ -591,8 +656,11 @@ private:
|
||||
|
||||
void readOutputParameters();
|
||||
|
||||
uint32_t device() { return mDevice; }
|
||||
|
||||
virtual status_t dumpInternals(int fd, const Vector<String16>& args);
|
||||
status_t dumpTracks(int fd, const Vector<String16>& args);
|
||||
status_t dumpEffectChains(int fd, const Vector<String16>& args);
|
||||
|
||||
SortedVector< sp<Track> > mTracks;
|
||||
// mStreamTypes[] uses 1 additionnal stream type internally for the OutputTrack used by DuplicatingThread
|
||||
@@ -603,11 +671,13 @@ private:
|
||||
int mNumWrites;
|
||||
int mNumDelayedWrites;
|
||||
bool mInWrite;
|
||||
Vector< sp<EffectChain> > mEffectChains;
|
||||
uint32_t mDevice;
|
||||
};
|
||||
|
||||
class MixerThread : public PlaybackThread {
|
||||
public:
|
||||
MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
|
||||
MixerThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
|
||||
virtual ~MixerThread();
|
||||
|
||||
// Thread virtuals
|
||||
@@ -630,7 +700,7 @@ private:
|
||||
class DirectOutputThread : public PlaybackThread {
|
||||
public:
|
||||
|
||||
DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id);
|
||||
DirectOutputThread (const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device);
|
||||
~DirectOutputThread();
|
||||
|
||||
// Thread virtuals
|
||||
@@ -645,8 +715,12 @@ private:
|
||||
virtual uint32_t idleSleepTimeUs();
|
||||
|
||||
private:
|
||||
float mLeftVolume;
|
||||
float mRightVolume;
|
||||
void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
|
||||
|
||||
float mLeftVolFloat;
|
||||
float mRightVolFloat;
|
||||
uint16_t mLeftVolShort;
|
||||
uint16_t mRightVolShort;
|
||||
};
|
||||
|
||||
class DuplicatingThread : public MixerThread {
|
||||
@@ -676,6 +750,8 @@ private:
|
||||
float streamVolumeInternal(int stream) const { return mStreamTypes[stream].volume; }
|
||||
void audioConfigChanged_l(int event, int ioHandle, void *param2);
|
||||
|
||||
int nextUniqueId();
|
||||
|
||||
friend class AudioBuffer;
|
||||
|
||||
class TrackHandle : public android::BnAudioTrack {
|
||||
@@ -689,6 +765,7 @@ private:
|
||||
virtual void pause();
|
||||
virtual void setVolume(float left, float right);
|
||||
virtual sp<IMemory> getCblk() const;
|
||||
virtual status_t attachAuxEffect(int effectId);
|
||||
virtual status_t onTransact(
|
||||
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
|
||||
private:
|
||||
@@ -717,7 +794,8 @@ private:
|
||||
int format,
|
||||
int channelCount,
|
||||
int frameCount,
|
||||
uint32_t flags);
|
||||
uint32_t flags,
|
||||
int sessionId);
|
||||
~RecordTrack();
|
||||
|
||||
virtual status_t start();
|
||||
@@ -792,6 +870,215 @@ private:
|
||||
sp<RecordThread::RecordTrack> mRecordTrack;
|
||||
};
|
||||
|
||||
//--- Audio Effect Management
|
||||
|
||||
// EffectModule and EffectChain classes both have their own mutex to protect
|
||||
// state changes or resource modifications. Always respect the following order
|
||||
// if multiple mutexes must be acquired to avoid cross deadlock:
|
||||
// AudioFlinger -> ThreadBase -> EffectChain -> EffectModule
|
||||
|
||||
// The EffectModule class is a wrapper object controlling the effect engine implementation
|
||||
// in the effect library. It prevents concurrent calls to process() and command() functions
|
||||
// from different client threads. It keeps a list of EffectHandle objects corresponding
|
||||
// to all client applications using this effect and notifies applications of effect state,
|
||||
// control or parameter changes. It manages the activation state machine to send appropriate
|
||||
// reset, enable, disable commands to effect engine and provide volume
|
||||
// ramping when effects are activated/deactivated.
|
||||
// When controlling an auxiliary effect, the EffectModule also provides an input buffer used by
|
||||
// the attached track(s) to accumulate their auxiliary channel.
|
||||
class EffectModule: public RefBase {
|
||||
public:
|
||||
EffectModule(const wp<ThreadBase>& wThread,
|
||||
const wp<AudioFlinger::EffectChain>& chain,
|
||||
effect_descriptor_t *desc,
|
||||
int id,
|
||||
int sessionId);
|
||||
~EffectModule();
|
||||
|
||||
enum effect_state {
|
||||
IDLE,
|
||||
RESET,
|
||||
STARTING,
|
||||
ACTIVE,
|
||||
STOPPING,
|
||||
STOPPED
|
||||
};
|
||||
|
||||
int id() { return mId; }
|
||||
void process();
|
||||
status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
|
||||
|
||||
void reset();
|
||||
status_t configure();
|
||||
status_t init();
|
||||
uint32_t state() {
|
||||
return mState;
|
||||
}
|
||||
uint32_t status() {
|
||||
return mStatus;
|
||||
}
|
||||
status_t setEnabled(bool enabled);
|
||||
bool isEnabled();
|
||||
|
||||
void setInBuffer(int16_t *buffer) { mConfig.inputCfg.buffer.s16 = buffer; }
|
||||
int16_t *inBuffer() { return mConfig.inputCfg.buffer.s16; }
|
||||
void setOutBuffer(int16_t *buffer) { mConfig.outputCfg.buffer.s16 = buffer; }
|
||||
int16_t *outBuffer() { return mConfig.outputCfg.buffer.s16; }
|
||||
|
||||
status_t addHandle(sp<EffectHandle>& handle);
|
||||
void disconnect(const wp<EffectHandle>& handle);
|
||||
size_t removeHandle (const wp<EffectHandle>& handle);
|
||||
|
||||
effect_descriptor_t& desc() { return mDescriptor; }
|
||||
|
||||
status_t setDevice(uint32_t device);
|
||||
status_t setVolume(uint32_t *left, uint32_t *right, bool controller);
|
||||
|
||||
status_t dump(int fd, const Vector<String16>& args);
|
||||
|
||||
protected:
|
||||
|
||||
EffectModule(const EffectModule&);
|
||||
EffectModule& operator = (const EffectModule&);
|
||||
|
||||
status_t start();
|
||||
status_t stop();
|
||||
|
||||
Mutex mLock; // mutex for process, commands and handles list protection
|
||||
wp<ThreadBase> mThread; // parent thread
|
||||
wp<EffectChain> mChain; // parent effect chain
|
||||
int mId; // this instance unique ID
|
||||
int mSessionId; // audio session ID
|
||||
effect_descriptor_t mDescriptor;// effect descriptor received from effect engine
|
||||
effect_config_t mConfig; // input and output audio configuration
|
||||
effect_interface_t mEffectInterface; // Effect module C API
|
||||
status_t mStatus; // initialization status
|
||||
uint32_t mState; // current activation state (effect_state)
|
||||
Vector< wp<EffectHandle> > mHandles; // list of client handles
|
||||
};
|
||||
|
||||
// The EffectHandle class implements the IEffect interface. It provides resources
|
||||
// to receive parameter updates, keeps track of effect control
|
||||
// ownership and state and has a pointer to the EffectModule object it is controlling.
|
||||
// There is one EffectHandle object for each application controlling (or using)
|
||||
// an effect module.
|
||||
// The EffectHandle is obtained by calling AudioFlinger::createEffect().
|
||||
class EffectHandle: public android::BnEffect {
|
||||
public:
|
||||
|
||||
EffectHandle(const sp<EffectModule>& effect,
|
||||
const sp<AudioFlinger::Client>& client,
|
||||
const sp<IEffectClient>& effectClient,
|
||||
int32_t priority);
|
||||
virtual ~EffectHandle();
|
||||
|
||||
// IEffect
|
||||
virtual status_t enable();
|
||||
virtual status_t disable();
|
||||
virtual status_t command(int cmdCode, int cmdSize, void *pCmdData, int *replySize, void *pReplyData);
|
||||
virtual void disconnect();
|
||||
virtual sp<IMemory> getCblk() const;
|
||||
virtual status_t onTransact(uint32_t code, const Parcel& data,
|
||||
Parcel* reply, uint32_t flags);
|
||||
|
||||
|
||||
// Give or take control of effect module
|
||||
void setControl(bool hasControl, bool signal);
|
||||
void commandExecuted(int cmdCode, int cmdSize, void *pCmdData, int replySize, void *pReplyData);
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
// Getters
|
||||
int id() { return mEffect->id(); }
|
||||
int priority() { return mPriority; }
|
||||
bool hasControl() { return mHasControl; }
|
||||
sp<EffectModule> effect() { return mEffect; }
|
||||
|
||||
void dump(char* buffer, size_t size);
|
||||
|
||||
protected:
|
||||
|
||||
EffectHandle(const EffectHandle&);
|
||||
EffectHandle& operator =(const EffectHandle&);
|
||||
|
||||
sp<EffectModule> mEffect; // pointer to controlled EffectModule
|
||||
sp<IEffectClient> mEffectClient; // callback interface for client notifications
|
||||
sp<Client> mClient; // client for shared memory allocation
|
||||
sp<IMemory> mCblkMemory; // shared memory for control block
|
||||
effect_param_cblk_t* mCblk; // control block for deferred parameter setting via shared memory
|
||||
uint8_t* mBuffer; // pointer to parameter area in shared memory
|
||||
int mPriority; // client application priority to control the effect
|
||||
bool mHasControl; // true if this handle is controlling the effect
|
||||
};
|
||||
|
||||
// the EffectChain class represents a group of effects associated to one audio session.
|
||||
// There can be any number of EffectChain objects per output mixer thread (PlaybackThread).
|
||||
// The EffecChain with session ID 0 contains global effects applied to the output mix.
|
||||
// Effects in this chain can be insert or auxiliary. Effects in other chains (attached to tracks)
|
||||
// are insert only. The EffectChain maintains an ordered list of effect module, the order corresponding
|
||||
// in the effect process order. When attached to a track (session ID != 0), it also provide it's own
|
||||
// input buffer used by the track as accumulation buffer.
|
||||
class EffectChain: public RefBase {
|
||||
public:
|
||||
EffectChain(const wp<ThreadBase>& wThread, int sessionId);
|
||||
~EffectChain();
|
||||
|
||||
void process_l();
|
||||
|
||||
void lock() {
|
||||
mLock.lock();
|
||||
}
|
||||
void unlock() {
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
status_t addEffect(sp<EffectModule>& handle);
|
||||
size_t removeEffect(const sp<EffectModule>& handle);
|
||||
|
||||
int sessionId() {
|
||||
return mSessionId;
|
||||
}
|
||||
sp<EffectModule> getEffectFromDesc(effect_descriptor_t *descriptor);
|
||||
sp<EffectModule> getEffectFromId(int id);
|
||||
sp<EffectModule> getVolumeController();
|
||||
bool setVolume(uint32_t *left, uint32_t *right);
|
||||
void setDevice(uint32_t device);
|
||||
|
||||
void setInBuffer(int16_t *buffer, bool ownsBuffer = false) {
|
||||
mInBuffer = buffer;
|
||||
mOwnInBuffer = ownsBuffer;
|
||||
}
|
||||
int16_t *inBuffer() {
|
||||
return mInBuffer;
|
||||
}
|
||||
void setOutBuffer(int16_t *buffer) {
|
||||
mOutBuffer = buffer;
|
||||
}
|
||||
int16_t *outBuffer() {
|
||||
return mOutBuffer;
|
||||
}
|
||||
|
||||
void startTrack() {mActiveTrackCnt++;}
|
||||
void stopTrack() {mActiveTrackCnt--;}
|
||||
int activeTracks() { return mActiveTrackCnt;}
|
||||
|
||||
status_t dump(int fd, const Vector<String16>& args);
|
||||
|
||||
protected:
|
||||
|
||||
EffectChain(const EffectChain&);
|
||||
EffectChain& operator =(const EffectChain&);
|
||||
|
||||
wp<ThreadBase> mThread; // parent mixer thread
|
||||
Mutex mLock; // mutex protecting effect list
|
||||
Vector<sp<EffectModule> > mEffects; // list of effect modules
|
||||
int mSessionId; // audio session ID
|
||||
int16_t *mInBuffer; // chain input buffer
|
||||
int16_t *mOutBuffer; // chain output buffer
|
||||
int mVolumeCtrlIdx; // index of insert effect having control over volume
|
||||
int mActiveTrackCnt; // number of active tracks connected
|
||||
bool mOwnInBuffer; // true if the chain owns its input buffer
|
||||
};
|
||||
|
||||
friend class RecordThread;
|
||||
friend class PlaybackThread;
|
||||
|
||||
@@ -813,7 +1100,7 @@ private:
|
||||
DefaultKeyedVector< int, sp<RecordThread> > mRecordThreads;
|
||||
|
||||
DefaultKeyedVector< pid_t, sp<NotificationClient> > mNotificationClients;
|
||||
int mNextThreadId;
|
||||
volatile int32_t mNextUniqueId;
|
||||
#ifdef LVMX
|
||||
int mLifeVibesClientPid;
|
||||
#endif
|
||||
|
||||
@@ -56,6 +56,8 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate)
|
||||
t->volume[1] = UNITY_GAIN;
|
||||
t->volumeInc[0] = 0;
|
||||
t->volumeInc[1] = 0;
|
||||
t->auxLevel = 0;
|
||||
t->auxInc = 0;
|
||||
t->channelCount = 2;
|
||||
t->enabled = 0;
|
||||
t->format = 16;
|
||||
@@ -65,6 +67,8 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate)
|
||||
t->resampler = 0;
|
||||
t->sampleRate = mSampleRate;
|
||||
t->in = 0;
|
||||
t->mainBuffer = NULL;
|
||||
t->auxBuffer = NULL;
|
||||
t++;
|
||||
}
|
||||
}
|
||||
@@ -169,28 +173,48 @@ status_t AudioMixer::setActiveTrack(int track)
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
status_t AudioMixer::setParameter(int target, int name, int value)
|
||||
status_t AudioMixer::setParameter(int target, int name, void *value)
|
||||
{
|
||||
int valueInt = (int)value;
|
||||
int32_t *valueBuf = (int32_t *)value;
|
||||
|
||||
switch (target) {
|
||||
case TRACK:
|
||||
if (name == CHANNEL_COUNT) {
|
||||
if ((uint32_t(value) <= MAX_NUM_CHANNELS) && (value)) {
|
||||
if (mState.tracks[ mActiveTrack ].channelCount != value) {
|
||||
mState.tracks[ mActiveTrack ].channelCount = value;
|
||||
LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", value);
|
||||
if ((uint32_t(valueInt) <= MAX_NUM_CHANNELS) && (valueInt)) {
|
||||
if (mState.tracks[ mActiveTrack ].channelCount != valueInt) {
|
||||
mState.tracks[ mActiveTrack ].channelCount = valueInt;
|
||||
LOGV("setParameter(TRACK, CHANNEL_COUNT, %d)", valueInt);
|
||||
invalidateState(1<<mActiveTrack);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
if (name == MAIN_BUFFER) {
|
||||
if (mState.tracks[ mActiveTrack ].mainBuffer != valueBuf) {
|
||||
mState.tracks[ mActiveTrack ].mainBuffer = valueBuf;
|
||||
LOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
|
||||
invalidateState(1<<mActiveTrack);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
if (name == AUX_BUFFER) {
|
||||
if (mState.tracks[ mActiveTrack ].auxBuffer != valueBuf) {
|
||||
mState.tracks[ mActiveTrack ].auxBuffer = valueBuf;
|
||||
LOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
|
||||
invalidateState(1<<mActiveTrack);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
break;
|
||||
case RESAMPLE:
|
||||
if (name == SAMPLE_RATE) {
|
||||
if (value > 0) {
|
||||
if (valueInt > 0) {
|
||||
track_t& track = mState.tracks[ mActiveTrack ];
|
||||
if (track.setResampler(uint32_t(value), mSampleRate)) {
|
||||
if (track.setResampler(uint32_t(valueInt), mSampleRate)) {
|
||||
LOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
|
||||
uint32_t(value));
|
||||
uint32_t(valueInt));
|
||||
invalidateState(1<<mActiveTrack);
|
||||
}
|
||||
return NO_ERROR;
|
||||
@@ -201,18 +225,39 @@ status_t AudioMixer::setParameter(int target, int name, int value)
|
||||
case VOLUME:
|
||||
if ((uint32_t(name-VOLUME0) < MAX_NUM_CHANNELS)) {
|
||||
track_t& track = mState.tracks[ mActiveTrack ];
|
||||
if (track.volume[name-VOLUME0] != value) {
|
||||
if (track.volume[name-VOLUME0] != valueInt) {
|
||||
LOGV("setParameter(VOLUME, VOLUME0/1: %04x)", valueInt);
|
||||
track.prevVolume[name-VOLUME0] = track.volume[name-VOLUME0] << 16;
|
||||
track.volume[name-VOLUME0] = value;
|
||||
track.volume[name-VOLUME0] = valueInt;
|
||||
if (target == VOLUME) {
|
||||
track.prevVolume[name-VOLUME0] = value << 16;
|
||||
track.prevVolume[name-VOLUME0] = valueInt << 16;
|
||||
track.volumeInc[name-VOLUME0] = 0;
|
||||
} else {
|
||||
int32_t d = (value<<16) - track.prevVolume[name-VOLUME0];
|
||||
int32_t d = (valueInt<<16) - track.prevVolume[name-VOLUME0];
|
||||
int32_t volInc = d / int32_t(mState.frameCount);
|
||||
track.volumeInc[name-VOLUME0] = volInc;
|
||||
if (volInc == 0) {
|
||||
track.prevVolume[name-VOLUME0] = value << 16;
|
||||
track.prevVolume[name-VOLUME0] = valueInt << 16;
|
||||
}
|
||||
}
|
||||
invalidateState(1<<mActiveTrack);
|
||||
}
|
||||
return NO_ERROR;
|
||||
} else if (name == AUXLEVEL) {
|
||||
track_t& track = mState.tracks[ mActiveTrack ];
|
||||
if (track.auxLevel != valueInt) {
|
||||
LOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt);
|
||||
track.prevAuxLevel = track.auxLevel << 16;
|
||||
track.auxLevel = valueInt;
|
||||
if (target == VOLUME) {
|
||||
track.prevAuxLevel = valueInt << 16;
|
||||
track.auxInc = 0;
|
||||
} else {
|
||||
int32_t d = (valueInt<<16) - track.prevAuxLevel;
|
||||
int32_t volInc = d / int32_t(mState.frameCount);
|
||||
track.auxInc = volInc;
|
||||
if (volInc == 0) {
|
||||
track.prevAuxLevel = valueInt << 16;
|
||||
}
|
||||
}
|
||||
invalidateState(1<<mActiveTrack);
|
||||
@@ -245,7 +290,7 @@ bool AudioMixer::track_t::doesResample() const
|
||||
}
|
||||
|
||||
inline
|
||||
void AudioMixer::track_t::adjustVolumeRamp()
|
||||
void AudioMixer::track_t::adjustVolumeRamp(bool aux)
|
||||
{
|
||||
for (int i=0 ; i<2 ; i++) {
|
||||
if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) ||
|
||||
@@ -254,6 +299,13 @@ void AudioMixer::track_t::adjustVolumeRamp()
|
||||
prevVolume[i] = volume[i]<<16;
|
||||
}
|
||||
}
|
||||
if (aux) {
|
||||
if (((auxInc>0) && (((prevAuxLevel+auxInc)>>16) >= auxLevel)) ||
|
||||
((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) {
|
||||
auxInc = 0;
|
||||
prevAuxLevel = auxLevel<<16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -265,13 +317,13 @@ status_t AudioMixer::setBufferProvider(AudioBufferProvider* buffer)
|
||||
|
||||
|
||||
|
||||
void AudioMixer::process(void* output)
|
||||
void AudioMixer::process()
|
||||
{
|
||||
mState.hook(&mState, output);
|
||||
mState.hook(&mState);
|
||||
}
|
||||
|
||||
|
||||
void AudioMixer::process__validate(state_t* state, void* output)
|
||||
void AudioMixer::process__validate(state_t* state)
|
||||
{
|
||||
LOGW_IF(!state->needsChanged,
|
||||
"in process__validate() but nothing's invalid");
|
||||
@@ -308,7 +360,10 @@ void AudioMixer::process__validate(state_t* state, void* output)
|
||||
n |= NEEDS_CHANNEL_1 + t.channelCount - 1;
|
||||
n |= NEEDS_FORMAT_16;
|
||||
n |= t.doesResample() ? NEEDS_RESAMPLE_ENABLED : NEEDS_RESAMPLE_DISABLED;
|
||||
|
||||
if (t.auxLevel != 0 && t.auxBuffer != NULL) {
|
||||
n |= NEEDS_AUX_ENABLED;
|
||||
}
|
||||
|
||||
if (t.volumeInc[0]|t.volumeInc[1]) {
|
||||
volumeRamp = 1;
|
||||
} else if (!t.doesResample() && t.volumeRL == 0) {
|
||||
@@ -319,6 +374,9 @@ void AudioMixer::process__validate(state_t* state, void* output)
|
||||
if ((n & NEEDS_MUTE__MASK) == NEEDS_MUTE_ENABLED) {
|
||||
t.hook = track__nop;
|
||||
} else {
|
||||
if ((n & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
|
||||
all16BitsStereoNoResample = 0;
|
||||
}
|
||||
if ((n & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
|
||||
all16BitsStereoNoResample = 0;
|
||||
resampling = 1;
|
||||
@@ -369,7 +427,7 @@ void AudioMixer::process__validate(state_t* state, void* output)
|
||||
countActiveTracks, state->enabledTracks,
|
||||
all16BitsStereoNoResample, resampling, volumeRamp);
|
||||
|
||||
state->hook(state, output);
|
||||
state->hook(state);
|
||||
|
||||
// Now that the volume ramp has been done, set optimal state and
|
||||
// track hooks for subsequent mixer process
|
||||
@@ -390,7 +448,7 @@ void AudioMixer::process__validate(state_t* state, void* output)
|
||||
}
|
||||
if (allMuted) {
|
||||
state->hook = process__nop;
|
||||
} else if (!resampling && all16BitsStereoNoResample) {
|
||||
} else if (all16BitsStereoNoResample) {
|
||||
if (countActiveTracks == 1) {
|
||||
state->hook = process__OneTrack16BitsStereoNoResampling;
|
||||
}
|
||||
@@ -481,30 +539,44 @@ int32_t mulRL(int left, uint32_t inRL, uint32_t vRL)
|
||||
}
|
||||
|
||||
|
||||
void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
|
||||
void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
|
||||
{
|
||||
t->resampler->setSampleRate(t->sampleRate);
|
||||
|
||||
// ramp gain - resample to temp buffer and scale/mix in 2nd step
|
||||
if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
|
||||
if (aux != NULL) {
|
||||
// always resample with unity gain when sending to auxiliary buffer to be able
|
||||
// to apply send level after resampling
|
||||
// TODO: modify each resampler to support aux channel?
|
||||
t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
|
||||
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
|
||||
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
|
||||
volumeRampStereo(t, out, outFrameCount, temp);
|
||||
}
|
||||
if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
|
||||
volumeRampStereo(t, out, outFrameCount, temp, aux);
|
||||
} else {
|
||||
volumeStereo(t, out, outFrameCount, temp, aux);
|
||||
}
|
||||
} else {
|
||||
if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
|
||||
t->resampler->setVolume(UNITY_GAIN, UNITY_GAIN);
|
||||
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
|
||||
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
|
||||
volumeRampStereo(t, out, outFrameCount, temp, aux);
|
||||
}
|
||||
|
||||
// constant gain
|
||||
else {
|
||||
t->resampler->setVolume(t->volume[0], t->volume[1]);
|
||||
t->resampler->resample(out, outFrameCount, t->bufferProvider);
|
||||
// constant gain
|
||||
else {
|
||||
t->resampler->setVolume(t->volume[0], t->volume[1]);
|
||||
t->resampler->resample(out, outFrameCount, t->bufferProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp)
|
||||
void AudioMixer::track__nop(track_t* t, int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
|
||||
{
|
||||
}
|
||||
|
||||
void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
|
||||
void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
|
||||
{
|
||||
int32_t vl = t->prevVolume[0];
|
||||
int32_t vr = t->prevVolume[1];
|
||||
@@ -514,98 +586,238 @@ void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, i
|
||||
//LOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
|
||||
// t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
|
||||
// (vl + vlInc*frameCount)/65536.0f, frameCount);
|
||||
|
||||
|
||||
// ramp volume
|
||||
do {
|
||||
*out++ += (vl >> 16) * (*temp++ >> 12);
|
||||
*out++ += (vr >> 16) * (*temp++ >> 12);
|
||||
vl += vlInc;
|
||||
vr += vrInc;
|
||||
} while (--frameCount);
|
||||
|
||||
t->prevVolume[0] = vl;
|
||||
t->prevVolume[1] = vr;
|
||||
t->adjustVolumeRamp();
|
||||
}
|
||||
|
||||
void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
|
||||
{
|
||||
int16_t const *in = static_cast<int16_t const *>(t->in);
|
||||
|
||||
// ramp gain
|
||||
if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
|
||||
int32_t vl = t->prevVolume[0];
|
||||
int32_t vr = t->prevVolume[1];
|
||||
const int32_t vlInc = t->volumeInc[0];
|
||||
const int32_t vrInc = t->volumeInc[1];
|
||||
|
||||
// LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
|
||||
// t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
|
||||
// (vl + vlInc*frameCount)/65536.0f, frameCount);
|
||||
if UNLIKELY(aux != NULL) {
|
||||
int32_t va = t->prevAuxLevel;
|
||||
const int32_t vaInc = t->auxInc;
|
||||
int32_t l;
|
||||
int32_t r;
|
||||
|
||||
do {
|
||||
*out++ += (vl >> 16) * (int32_t) *in++;
|
||||
*out++ += (vr >> 16) * (int32_t) *in++;
|
||||
l = (*temp++ >> 12);
|
||||
r = (*temp++ >> 12);
|
||||
*out++ += (vl >> 16) * l;
|
||||
*out++ += (vr >> 16) * r;
|
||||
*aux++ += (va >> 17) * (l + r);
|
||||
vl += vlInc;
|
||||
vr += vrInc;
|
||||
va += vaInc;
|
||||
} while (--frameCount);
|
||||
t->prevAuxLevel = va;
|
||||
} else {
|
||||
do {
|
||||
*out++ += (vl >> 16) * (*temp++ >> 12);
|
||||
*out++ += (vr >> 16) * (*temp++ >> 12);
|
||||
vl += vlInc;
|
||||
vr += vrInc;
|
||||
} while (--frameCount);
|
||||
|
||||
t->prevVolume[0] = vl;
|
||||
t->prevVolume[1] = vr;
|
||||
t->adjustVolumeRamp();
|
||||
}
|
||||
t->prevVolume[0] = vl;
|
||||
t->prevVolume[1] = vr;
|
||||
t->adjustVolumeRamp((aux != NULL));
|
||||
}
|
||||
|
||||
// constant gain
|
||||
else {
|
||||
const uint32_t vrl = t->volumeRL;
|
||||
void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
|
||||
{
|
||||
const int16_t vl = t->volume[0];
|
||||
const int16_t vr = t->volume[1];
|
||||
|
||||
if UNLIKELY(aux != NULL) {
|
||||
const int16_t va = (int16_t)t->auxLevel;
|
||||
do {
|
||||
uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
|
||||
in += 2;
|
||||
out[0] = mulAddRL(1, rl, vrl, out[0]);
|
||||
out[1] = mulAddRL(0, rl, vrl, out[1]);
|
||||
int16_t l = (int16_t)(*temp++ >> 12);
|
||||
int16_t r = (int16_t)(*temp++ >> 12);
|
||||
out[0] = mulAdd(l, vl, out[0]);
|
||||
int16_t a = (int16_t)(((int32_t)l + r) >> 1);
|
||||
out[1] = mulAdd(r, vr, out[1]);
|
||||
out += 2;
|
||||
aux[0] = mulAdd(a, va, aux[0]);
|
||||
aux++;
|
||||
} while (--frameCount);
|
||||
} else {
|
||||
do {
|
||||
int16_t l = (int16_t)(*temp++ >> 12);
|
||||
int16_t r = (int16_t)(*temp++ >> 12);
|
||||
out[0] = mulAdd(l, vl, out[0]);
|
||||
out[1] = mulAdd(r, vr, out[1]);
|
||||
out += 2;
|
||||
} while (--frameCount);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioMixer::track__16BitsStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
|
||||
{
|
||||
int16_t const *in = static_cast<int16_t const *>(t->in);
|
||||
|
||||
if UNLIKELY(aux != NULL) {
|
||||
int32_t l;
|
||||
int32_t r;
|
||||
// ramp gain
|
||||
if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
|
||||
int32_t vl = t->prevVolume[0];
|
||||
int32_t vr = t->prevVolume[1];
|
||||
int32_t va = t->prevAuxLevel;
|
||||
const int32_t vlInc = t->volumeInc[0];
|
||||
const int32_t vrInc = t->volumeInc[1];
|
||||
const int32_t vaInc = t->auxInc;
|
||||
// LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
|
||||
// t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
|
||||
// (vl + vlInc*frameCount)/65536.0f, frameCount);
|
||||
|
||||
do {
|
||||
l = (int32_t)*in++;
|
||||
r = (int32_t)*in++;
|
||||
*out++ += (vl >> 16) * l;
|
||||
*out++ += (vr >> 16) * r;
|
||||
*aux++ += (va >> 17) * (l + r);
|
||||
vl += vlInc;
|
||||
vr += vrInc;
|
||||
va += vaInc;
|
||||
} while (--frameCount);
|
||||
|
||||
t->prevVolume[0] = vl;
|
||||
t->prevVolume[1] = vr;
|
||||
t->prevAuxLevel = va;
|
||||
t->adjustVolumeRamp(true);
|
||||
}
|
||||
|
||||
// constant gain
|
||||
else {
|
||||
const uint32_t vrl = t->volumeRL;
|
||||
const int16_t va = (int16_t)t->auxLevel;
|
||||
do {
|
||||
uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
|
||||
int16_t a = (int16_t)(((int32_t)in[0] + in[1]) >> 1);
|
||||
in += 2;
|
||||
out[0] = mulAddRL(1, rl, vrl, out[0]);
|
||||
out[1] = mulAddRL(0, rl, vrl, out[1]);
|
||||
out += 2;
|
||||
aux[0] = mulAdd(a, va, aux[0]);
|
||||
aux++;
|
||||
} while (--frameCount);
|
||||
}
|
||||
} else {
|
||||
// ramp gain
|
||||
if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
|
||||
int32_t vl = t->prevVolume[0];
|
||||
int32_t vr = t->prevVolume[1];
|
||||
const int32_t vlInc = t->volumeInc[0];
|
||||
const int32_t vrInc = t->volumeInc[1];
|
||||
|
||||
// LOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
|
||||
// t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
|
||||
// (vl + vlInc*frameCount)/65536.0f, frameCount);
|
||||
|
||||
do {
|
||||
*out++ += (vl >> 16) * (int32_t) *in++;
|
||||
*out++ += (vr >> 16) * (int32_t) *in++;
|
||||
vl += vlInc;
|
||||
vr += vrInc;
|
||||
} while (--frameCount);
|
||||
|
||||
t->prevVolume[0] = vl;
|
||||
t->prevVolume[1] = vr;
|
||||
t->adjustVolumeRamp(false);
|
||||
}
|
||||
|
||||
// constant gain
|
||||
else {
|
||||
const uint32_t vrl = t->volumeRL;
|
||||
do {
|
||||
uint32_t rl = *reinterpret_cast<uint32_t const *>(in);
|
||||
in += 2;
|
||||
out[0] = mulAddRL(1, rl, vrl, out[0]);
|
||||
out[1] = mulAddRL(0, rl, vrl, out[1]);
|
||||
out += 2;
|
||||
} while (--frameCount);
|
||||
}
|
||||
}
|
||||
t->in = in;
|
||||
}
|
||||
|
||||
void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp)
|
||||
void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
|
||||
{
|
||||
int16_t const *in = static_cast<int16_t const *>(t->in);
|
||||
|
||||
// ramp gain
|
||||
if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
|
||||
int32_t vl = t->prevVolume[0];
|
||||
int32_t vr = t->prevVolume[1];
|
||||
const int32_t vlInc = t->volumeInc[0];
|
||||
const int32_t vrInc = t->volumeInc[1];
|
||||
if UNLIKELY(aux != NULL) {
|
||||
// ramp gain
|
||||
if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc) {
|
||||
int32_t vl = t->prevVolume[0];
|
||||
int32_t vr = t->prevVolume[1];
|
||||
int32_t va = t->prevAuxLevel;
|
||||
const int32_t vlInc = t->volumeInc[0];
|
||||
const int32_t vrInc = t->volumeInc[1];
|
||||
const int32_t vaInc = t->auxInc;
|
||||
|
||||
// LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
|
||||
// t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
|
||||
// (vl + vlInc*frameCount)/65536.0f, frameCount);
|
||||
// LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
|
||||
// t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
|
||||
// (vl + vlInc*frameCount)/65536.0f, frameCount);
|
||||
|
||||
do {
|
||||
int32_t l = *in++;
|
||||
*out++ += (vl >> 16) * l;
|
||||
*out++ += (vr >> 16) * l;
|
||||
vl += vlInc;
|
||||
vr += vrInc;
|
||||
} while (--frameCount);
|
||||
|
||||
t->prevVolume[0] = vl;
|
||||
t->prevVolume[1] = vr;
|
||||
t->adjustVolumeRamp();
|
||||
}
|
||||
// constant gain
|
||||
else {
|
||||
const int16_t vl = t->volume[0];
|
||||
const int16_t vr = t->volume[1];
|
||||
do {
|
||||
int16_t l = *in++;
|
||||
out[0] = mulAdd(l, vl, out[0]);
|
||||
out[1] = mulAdd(l, vr, out[1]);
|
||||
out += 2;
|
||||
} while (--frameCount);
|
||||
do {
|
||||
int32_t l = *in++;
|
||||
*out++ += (vl >> 16) * l;
|
||||
*out++ += (vr >> 16) * l;
|
||||
*aux++ += (va >> 16) * l;
|
||||
vl += vlInc;
|
||||
vr += vrInc;
|
||||
va += vaInc;
|
||||
} while (--frameCount);
|
||||
|
||||
t->prevVolume[0] = vl;
|
||||
t->prevVolume[1] = vr;
|
||||
t->prevAuxLevel = va;
|
||||
t->adjustVolumeRamp(true);
|
||||
}
|
||||
// constant gain
|
||||
else {
|
||||
const int16_t vl = t->volume[0];
|
||||
const int16_t vr = t->volume[1];
|
||||
const int16_t va = (int16_t)t->auxLevel;
|
||||
do {
|
||||
int16_t l = *in++;
|
||||
out[0] = mulAdd(l, vl, out[0]);
|
||||
out[1] = mulAdd(l, vr, out[1]);
|
||||
out += 2;
|
||||
aux[0] = mulAdd(l, va, aux[0]);
|
||||
aux++;
|
||||
} while (--frameCount);
|
||||
}
|
||||
} else {
|
||||
// ramp gain
|
||||
if UNLIKELY(t->volumeInc[0]|t->volumeInc[1]) {
|
||||
int32_t vl = t->prevVolume[0];
|
||||
int32_t vr = t->prevVolume[1];
|
||||
const int32_t vlInc = t->volumeInc[0];
|
||||
const int32_t vrInc = t->volumeInc[1];
|
||||
|
||||
// LOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
|
||||
// t, vlInc/65536.0f, vl/65536.0f, t->volume[0],
|
||||
// (vl + vlInc*frameCount)/65536.0f, frameCount);
|
||||
|
||||
do {
|
||||
int32_t l = *in++;
|
||||
*out++ += (vl >> 16) * l;
|
||||
*out++ += (vr >> 16) * l;
|
||||
vl += vlInc;
|
||||
vr += vrInc;
|
||||
} while (--frameCount);
|
||||
|
||||
t->prevVolume[0] = vl;
|
||||
t->prevVolume[1] = vr;
|
||||
t->adjustVolumeRamp(false);
|
||||
}
|
||||
// constant gain
|
||||
else {
|
||||
const int16_t vl = t->volume[0];
|
||||
const int16_t vr = t->volume[1];
|
||||
do {
|
||||
int16_t l = *in++;
|
||||
out[0] = mulAdd(l, vl, out[0]);
|
||||
out[1] = mulAdd(l, vr, out[1]);
|
||||
out += 2;
|
||||
} while (--frameCount);
|
||||
}
|
||||
}
|
||||
t->in = in;
|
||||
}
|
||||
@@ -624,37 +836,56 @@ void AudioMixer::ditherAndClamp(int32_t* out, int32_t const *sums, size_t c)
|
||||
}
|
||||
|
||||
// no-op case
|
||||
void AudioMixer::process__nop(state_t* state, void* output)
|
||||
void AudioMixer::process__nop(state_t* state)
|
||||
{
|
||||
// this assumes output 16 bits stereo, no resampling
|
||||
memset(output, 0, state->frameCount*4);
|
||||
uint32_t en = state->enabledTracks;
|
||||
while (en) {
|
||||
const int i = 31 - __builtin_clz(en);
|
||||
en &= ~(1<<i);
|
||||
track_t& t = state->tracks[i];
|
||||
size_t outFrames = state->frameCount;
|
||||
while (outFrames) {
|
||||
t.buffer.frameCount = outFrames;
|
||||
t.bufferProvider->getNextBuffer(&t.buffer);
|
||||
if (!t.buffer.raw) break;
|
||||
outFrames -= t.buffer.frameCount;
|
||||
t.bufferProvider->releaseBuffer(&t.buffer);
|
||||
uint32_t e0 = state->enabledTracks;
|
||||
size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS;
|
||||
while (e0) {
|
||||
// process by group of tracks with same output buffer to
|
||||
// avoid multiple memset() on same buffer
|
||||
uint32_t e1 = e0, e2 = e0;
|
||||
int i = 31 - __builtin_clz(e1);
|
||||
track_t& t1 = state->tracks[i];
|
||||
e2 &= ~(1<<i);
|
||||
while (e2) {
|
||||
i = 31 - __builtin_clz(e2);
|
||||
e2 &= ~(1<<i);
|
||||
track_t& t2 = state->tracks[i];
|
||||
if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
|
||||
e1 &= ~(1<<i);
|
||||
}
|
||||
}
|
||||
e0 &= ~(e1);
|
||||
|
||||
memset(t1.mainBuffer, 0, bufSize);
|
||||
|
||||
while (e1) {
|
||||
i = 31 - __builtin_clz(e1);
|
||||
e1 &= ~(1<<i);
|
||||
t1 = state->tracks[i];
|
||||
size_t outFrames = state->frameCount;
|
||||
while (outFrames) {
|
||||
t1.buffer.frameCount = outFrames;
|
||||
t1.bufferProvider->getNextBuffer(&t1.buffer);
|
||||
if (!t1.buffer.raw) break;
|
||||
outFrames -= t1.buffer.frameCount;
|
||||
t1.bufferProvider->releaseBuffer(&t1.buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generic code without resampling
|
||||
void AudioMixer::process__genericNoResampling(state_t* state, void* output)
|
||||
void AudioMixer::process__genericNoResampling(state_t* state)
|
||||
{
|
||||
int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));
|
||||
|
||||
// acquire each track's buffer
|
||||
uint32_t enabledTracks = state->enabledTracks;
|
||||
uint32_t en = enabledTracks;
|
||||
while (en) {
|
||||
const int i = 31 - __builtin_clz(en);
|
||||
en &= ~(1<<i);
|
||||
uint32_t e0 = enabledTracks;
|
||||
while (e0) {
|
||||
const int i = 31 - __builtin_clz(e0);
|
||||
e0 &= ~(1<<i);
|
||||
track_t& t = state->tracks[i];
|
||||
t.buffer.frameCount = state->frameCount;
|
||||
t.bufferProvider->getNextBuffer(&t.buffer);
|
||||
@@ -666,110 +897,156 @@ void AudioMixer::process__genericNoResampling(state_t* state, void* output)
|
||||
enabledTracks &= ~(1<<i);
|
||||
}
|
||||
|
||||
// this assumes output 16 bits stereo, no resampling
|
||||
int32_t* out = static_cast<int32_t*>(output);
|
||||
size_t numFrames = state->frameCount;
|
||||
do {
|
||||
memset(outTemp, 0, sizeof(outTemp));
|
||||
|
||||
en = enabledTracks;
|
||||
while (en) {
|
||||
const int i = 31 - __builtin_clz(en);
|
||||
en &= ~(1<<i);
|
||||
track_t& t = state->tracks[i];
|
||||
size_t outFrames = BLOCKSIZE;
|
||||
|
||||
while (outFrames) {
|
||||
size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
|
||||
if (inFrames) {
|
||||
(t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp);
|
||||
t.frameCount -= inFrames;
|
||||
outFrames -= inFrames;
|
||||
}
|
||||
if (t.frameCount == 0 && outFrames) {
|
||||
t.bufferProvider->releaseBuffer(&t.buffer);
|
||||
t.buffer.frameCount = numFrames - (BLOCKSIZE - outFrames);
|
||||
t.bufferProvider->getNextBuffer(&t.buffer);
|
||||
t.in = t.buffer.raw;
|
||||
if (t.in == NULL) {
|
||||
enabledTracks &= ~(1<<i);
|
||||
break;
|
||||
}
|
||||
t.frameCount = t.buffer.frameCount;
|
||||
}
|
||||
e0 = enabledTracks;
|
||||
while (e0) {
|
||||
// process by group of tracks with same output buffer to
|
||||
// optimize cache use
|
||||
uint32_t e1 = e0, e2 = e0;
|
||||
int j = 31 - __builtin_clz(e1);
|
||||
track_t& t1 = state->tracks[j];
|
||||
e2 &= ~(1<<j);
|
||||
while (e2) {
|
||||
j = 31 - __builtin_clz(e2);
|
||||
e2 &= ~(1<<j);
|
||||
track_t& t2 = state->tracks[j];
|
||||
if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
|
||||
e1 &= ~(1<<j);
|
||||
}
|
||||
}
|
||||
|
||||
ditherAndClamp(out, outTemp, BLOCKSIZE);
|
||||
out += BLOCKSIZE;
|
||||
numFrames -= BLOCKSIZE;
|
||||
} while (numFrames);
|
||||
|
||||
e0 &= ~(e1);
|
||||
// this assumes output 16 bits stereo, no resampling
|
||||
int32_t *out = t1.mainBuffer;
|
||||
size_t numFrames = 0;
|
||||
do {
|
||||
memset(outTemp, 0, sizeof(outTemp));
|
||||
e2 = e1;
|
||||
while (e2) {
|
||||
const int i = 31 - __builtin_clz(e2);
|
||||
e2 &= ~(1<<i);
|
||||
track_t& t = state->tracks[i];
|
||||
size_t outFrames = BLOCKSIZE;
|
||||
int32_t *aux = NULL;
|
||||
if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
|
||||
aux = t.auxBuffer + numFrames;
|
||||
}
|
||||
while (outFrames) {
|
||||
size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount;
|
||||
if (inFrames) {
|
||||
(t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux);
|
||||
t.frameCount -= inFrames;
|
||||
outFrames -= inFrames;
|
||||
if UNLIKELY(aux != NULL) {
|
||||
aux += inFrames;
|
||||
}
|
||||
}
|
||||
if (t.frameCount == 0 && outFrames) {
|
||||
t.bufferProvider->releaseBuffer(&t.buffer);
|
||||
t.buffer.frameCount = (state->frameCount - numFrames) - (BLOCKSIZE - outFrames);
|
||||
t.bufferProvider->getNextBuffer(&t.buffer);
|
||||
t.in = t.buffer.raw;
|
||||
if (t.in == NULL) {
|
||||
enabledTracks &= ~(1<<i);
|
||||
e1 &= ~(1<<i);
|
||||
break;
|
||||
}
|
||||
t.frameCount = t.buffer.frameCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
ditherAndClamp(out, outTemp, BLOCKSIZE);
|
||||
out += BLOCKSIZE;
|
||||
numFrames += BLOCKSIZE;
|
||||
} while (numFrames < state->frameCount);
|
||||
}
|
||||
|
||||
// release each track's buffer
|
||||
en = enabledTracks;
|
||||
while (en) {
|
||||
const int i = 31 - __builtin_clz(en);
|
||||
en &= ~(1<<i);
|
||||
e0 = enabledTracks;
|
||||
while (e0) {
|
||||
const int i = 31 - __builtin_clz(e0);
|
||||
e0 &= ~(1<<i);
|
||||
track_t& t = state->tracks[i];
|
||||
t.bufferProvider->releaseBuffer(&t.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// generic code with resampling
|
||||
void AudioMixer::process__genericResampling(state_t* state, void* output)
|
||||
|
||||
// generic code with resampling
|
||||
void AudioMixer::process__genericResampling(state_t* state)
|
||||
{
|
||||
int32_t* const outTemp = state->outputTemp;
|
||||
const size_t size = sizeof(int32_t) * MAX_NUM_CHANNELS * state->frameCount;
|
||||
memset(outTemp, 0, size);
|
||||
|
||||
int32_t* out = static_cast<int32_t*>(output);
|
||||
size_t numFrames = state->frameCount;
|
||||
|
||||
uint32_t en = state->enabledTracks;
|
||||
while (en) {
|
||||
const int i = 31 - __builtin_clz(en);
|
||||
en &= ~(1<<i);
|
||||
track_t& t = state->tracks[i];
|
||||
|
||||
// this is a little goofy, on the resampling case we don't
|
||||
// acquire/release the buffers because it's done by
|
||||
// the resampler.
|
||||
if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
|
||||
(t.hook)(&t, outTemp, numFrames, state->resampleTemp);
|
||||
} else {
|
||||
|
||||
size_t outFrames = numFrames;
|
||||
|
||||
while (outFrames) {
|
||||
t.buffer.frameCount = outFrames;
|
||||
t.bufferProvider->getNextBuffer(&t.buffer);
|
||||
t.in = t.buffer.raw;
|
||||
// t.in == NULL can happen if the track was flushed just after having
|
||||
// been enabled for mixing.
|
||||
if (t.in == NULL) break;
|
||||
|
||||
(t.hook)(&t, outTemp + (numFrames-outFrames)*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp);
|
||||
outFrames -= t.buffer.frameCount;
|
||||
t.bufferProvider->releaseBuffer(&t.buffer);
|
||||
uint32_t e0 = state->enabledTracks;
|
||||
while (e0) {
|
||||
// process by group of tracks with same output buffer
|
||||
// to optimize cache use
|
||||
uint32_t e1 = e0, e2 = e0;
|
||||
int j = 31 - __builtin_clz(e1);
|
||||
track_t& t1 = state->tracks[j];
|
||||
e2 &= ~(1<<j);
|
||||
while (e2) {
|
||||
j = 31 - __builtin_clz(e2);
|
||||
e2 &= ~(1<<j);
|
||||
track_t& t2 = state->tracks[j];
|
||||
if UNLIKELY(t2.mainBuffer != t1.mainBuffer) {
|
||||
e1 &= ~(1<<j);
|
||||
}
|
||||
}
|
||||
}
|
||||
e0 &= ~(e1);
|
||||
int32_t *out = t1.mainBuffer;
|
||||
while (e1) {
|
||||
const int i = 31 - __builtin_clz(e1);
|
||||
e1 &= ~(1<<i);
|
||||
track_t& t = state->tracks[i];
|
||||
int32_t *aux = NULL;
|
||||
if UNLIKELY((t.needs & NEEDS_AUX__MASK) == NEEDS_AUX_ENABLED) {
|
||||
aux = t.auxBuffer;
|
||||
}
|
||||
|
||||
ditherAndClamp(out, outTemp, numFrames);
|
||||
// this is a little goofy, on the resampling case we don't
|
||||
// acquire/release the buffers because it's done by
|
||||
// the resampler.
|
||||
if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) {
|
||||
(t.hook)(&t, outTemp, numFrames, state->resampleTemp, aux);
|
||||
} else {
|
||||
|
||||
size_t outFrames = 0;
|
||||
|
||||
while (outFrames < numFrames) {
|
||||
t.buffer.frameCount = numFrames - outFrames;
|
||||
t.bufferProvider->getNextBuffer(&t.buffer);
|
||||
t.in = t.buffer.raw;
|
||||
// t.in == NULL can happen if the track was flushed just after having
|
||||
// been enabled for mixing.
|
||||
if (t.in == NULL) break;
|
||||
|
||||
if UNLIKELY(aux != NULL) {
|
||||
aux += outFrames;
|
||||
}
|
||||
(t.hook)(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux);
|
||||
outFrames += t.buffer.frameCount;
|
||||
t.bufferProvider->releaseBuffer(&t.buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
ditherAndClamp(out, outTemp, numFrames);
|
||||
}
|
||||
}
|
||||
|
||||
// one track, 16 bits stereo without resampling is the most common case
|
||||
void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void* output)
|
||||
void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state)
|
||||
{
|
||||
const int i = 31 - __builtin_clz(state->enabledTracks);
|
||||
const track_t& t = state->tracks[i];
|
||||
|
||||
AudioBufferProvider::Buffer& b(t.buffer);
|
||||
|
||||
int32_t* out = static_cast<int32_t*>(output);
|
||||
|
||||
int32_t* out = t.mainBuffer;
|
||||
size_t numFrames = state->frameCount;
|
||||
|
||||
|
||||
const int16_t vl = t.volume[0];
|
||||
const int16_t vr = t.volume[1];
|
||||
const uint32_t vrl = t.volumeRL;
|
||||
@@ -787,7 +1064,7 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void*
|
||||
return;
|
||||
}
|
||||
size_t outFrames = b.frameCount;
|
||||
|
||||
|
||||
if (UNLIKELY(uint32_t(vl) > UNITY_GAIN || uint32_t(vr) > UNITY_GAIN)) {
|
||||
// volume is boosted, so we might need to clamp even though
|
||||
// we process only one track.
|
||||
@@ -816,7 +1093,9 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, void*
|
||||
}
|
||||
|
||||
// 2 tracks is also a common case
|
||||
void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output)
|
||||
// NEVER used in current implementation of process__validate()
|
||||
// only use if the 2 tracks have the same output buffer
|
||||
void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state)
|
||||
{
|
||||
int i;
|
||||
uint32_t en = state->enabledTracks;
|
||||
@@ -829,24 +1108,25 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void
|
||||
i = 31 - __builtin_clz(en);
|
||||
const track_t& t1 = state->tracks[i];
|
||||
AudioBufferProvider::Buffer& b1(t1.buffer);
|
||||
|
||||
|
||||
int16_t const *in0;
|
||||
const int16_t vl0 = t0.volume[0];
|
||||
const int16_t vr0 = t0.volume[1];
|
||||
size_t frameCount0 = 0;
|
||||
|
||||
|
||||
int16_t const *in1;
|
||||
const int16_t vl1 = t1.volume[0];
|
||||
const int16_t vr1 = t1.volume[1];
|
||||
size_t frameCount1 = 0;
|
||||
|
||||
int32_t* out = static_cast<int32_t*>(output);
|
||||
|
||||
//FIXME: only works if two tracks use same buffer
|
||||
int32_t* out = t0.mainBuffer;
|
||||
size_t numFrames = state->frameCount;
|
||||
int16_t const *buff = NULL;
|
||||
|
||||
|
||||
|
||||
while (numFrames) {
|
||||
|
||||
|
||||
if (frameCount0 == 0) {
|
||||
b0.frameCount = numFrames;
|
||||
t0.bufferProvider->getNextBuffer(&b0);
|
||||
@@ -875,13 +1155,13 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void
|
||||
}
|
||||
frameCount1 = b1.frameCount;
|
||||
}
|
||||
|
||||
|
||||
size_t outFrames = frameCount0 < frameCount1?frameCount0:frameCount1;
|
||||
|
||||
numFrames -= outFrames;
|
||||
frameCount0 -= outFrames;
|
||||
frameCount1 -= outFrames;
|
||||
|
||||
|
||||
do {
|
||||
int32_t l0 = *in0++;
|
||||
int32_t r0 = *in0++;
|
||||
@@ -896,17 +1176,17 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, void
|
||||
r = clamp16(r);
|
||||
*out++ = (r<<16) | (l & 0xFFFF);
|
||||
} while (--outFrames);
|
||||
|
||||
|
||||
if (frameCount0 == 0) {
|
||||
t0.bufferProvider->releaseBuffer(&b0);
|
||||
}
|
||||
if (frameCount1 == 0) {
|
||||
t1.bufferProvider->releaseBuffer(&b1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (buff != NULL) {
|
||||
delete [] buff;
|
||||
delete [] buff;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,11 +63,14 @@ public:
|
||||
// for target TRACK
|
||||
CHANNEL_COUNT = 0x4000,
|
||||
FORMAT = 0x4001,
|
||||
MAIN_BUFFER = 0x4002,
|
||||
AUX_BUFFER = 0x4003,
|
||||
// for TARGET RESAMPLE
|
||||
SAMPLE_RATE = 0x4100,
|
||||
// for TARGET VOLUME (8 channels max)
|
||||
VOLUME0 = 0x4200,
|
||||
VOLUME1 = 0x4201,
|
||||
AUXLEVEL = 0x4210,
|
||||
};
|
||||
|
||||
|
||||
@@ -78,10 +81,10 @@ public:
|
||||
status_t disable(int name);
|
||||
|
||||
status_t setActiveTrack(int track);
|
||||
status_t setParameter(int target, int name, int value);
|
||||
status_t setParameter(int target, int name, void *value);
|
||||
|
||||
status_t setBufferProvider(AudioBufferProvider* bufferProvider);
|
||||
void process(void* output);
|
||||
void process();
|
||||
|
||||
uint32_t trackNames() const { return mTrackNames; }
|
||||
|
||||
@@ -94,6 +97,7 @@ private:
|
||||
NEEDS_FORMAT__MASK = 0x000000F0,
|
||||
NEEDS_MUTE__MASK = 0x00000100,
|
||||
NEEDS_RESAMPLE__MASK = 0x00001000,
|
||||
NEEDS_AUX__MASK = 0x00010000,
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -107,6 +111,9 @@ private:
|
||||
|
||||
NEEDS_RESAMPLE_DISABLED = 0x00000000,
|
||||
NEEDS_RESAMPLE_ENABLED = 0x00001000,
|
||||
|
||||
NEEDS_AUX_DISABLED = 0x00000000,
|
||||
NEEDS_AUX_ENABLED = 0x00010000,
|
||||
};
|
||||
|
||||
static inline int32_t applyVolume(int32_t in, int32_t v) {
|
||||
@@ -115,9 +122,10 @@ private:
|
||||
|
||||
|
||||
struct state_t;
|
||||
struct track_t;
|
||||
|
||||
typedef void (*mix_t)(state_t* state, void* output);
|
||||
|
||||
typedef void (*mix_t)(state_t* state);
|
||||
typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
|
||||
static const int BLOCKSIZE = 16; // 4 cache lines
|
||||
|
||||
struct track_t {
|
||||
@@ -131,6 +139,9 @@ private:
|
||||
int32_t prevVolume[2];
|
||||
|
||||
int32_t volumeInc[2];
|
||||
int32_t auxLevel;
|
||||
int32_t auxInc;
|
||||
int32_t prevAuxLevel;
|
||||
|
||||
uint16_t frameCount;
|
||||
|
||||
@@ -142,15 +153,17 @@ private:
|
||||
AudioBufferProvider* bufferProvider;
|
||||
mutable AudioBufferProvider::Buffer buffer;
|
||||
|
||||
void (*hook)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp);
|
||||
hook_t hook;
|
||||
void const* in; // current location in buffer
|
||||
|
||||
AudioResampler* resampler;
|
||||
uint32_t sampleRate;
|
||||
int32_t* mainBuffer;
|
||||
int32_t* auxBuffer;
|
||||
|
||||
bool setResampler(uint32_t sampleRate, uint32_t devSampleRate);
|
||||
bool doesResample() const;
|
||||
void adjustVolumeRamp();
|
||||
void adjustVolumeRamp(bool aux);
|
||||
};
|
||||
|
||||
// pad to 32-bytes to fill cache line
|
||||
@@ -173,18 +186,19 @@ private:
|
||||
|
||||
void invalidateState(uint32_t mask);
|
||||
|
||||
static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
|
||||
static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
|
||||
static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp);
|
||||
static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
|
||||
static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp);
|
||||
static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
|
||||
static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
|
||||
static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
|
||||
static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
|
||||
static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
|
||||
static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
|
||||
|
||||
static void process__validate(state_t* state, void* output);
|
||||
static void process__nop(state_t* state, void* output);
|
||||
static void process__genericNoResampling(state_t* state, void* output);
|
||||
static void process__genericResampling(state_t* state, void* output);
|
||||
static void process__OneTrack16BitsStereoNoResampling(state_t* state, void* output);
|
||||
static void process__TwoTracks16BitsStereoNoResampling(state_t* state, void* output);
|
||||
static void process__validate(state_t* state);
|
||||
static void process__nop(state_t* state);
|
||||
static void process__genericNoResampling(state_t* state);
|
||||
static void process__genericResampling(state_t* state);
|
||||
static void process__OneTrack16BitsStereoNoResampling(state_t* state);
|
||||
static void process__TwoTracks16BitsStereoNoResampling(state_t* state);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
@@ -114,7 +114,7 @@ int Equalizer_setParameter(AudioEqualizer * pEqualizer, int32_t *pParam, void *p
|
||||
//--- Effect Library Interface Implementation
|
||||
//
|
||||
|
||||
extern "C" int EffectQueryNumberEffects(int *pNumEffects) {
|
||||
extern "C" int EffectQueryNumberEffects(uint32_t *pNumEffects) {
|
||||
*pNumEffects = 1;
|
||||
gEffectIndex = 0;
|
||||
return 0;
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
//
|
||||
#define LOG_NDEBUG 0
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include "EffectReverb.h"
|
||||
#include "EffectsMath.h"
|
||||
@@ -86,7 +87,7 @@ static const effect_descriptor_t * const gDescriptors[] = {
|
||||
|
||||
/*--- Effect Library Interface Implementation ---*/
|
||||
|
||||
int EffectQueryNumberEffects(int *pNumEffects) {
|
||||
int EffectQueryNumberEffects(uint32_t *pNumEffects) {
|
||||
*pNumEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *)
|
||||
- 1;
|
||||
gEffectIndex = 0;
|
||||
|
||||
@@ -292,7 +292,7 @@ typedef struct reverb_module_s {
|
||||
* Effect API
|
||||
*------------------------------------
|
||||
*/
|
||||
int EffectQueryNumberEffects(int *pNumEffects);
|
||||
int EffectQueryNumberEffects(uint32_t *pNumEffects);
|
||||
int EffectQueryNext(effect_descriptor_t *pDescriptor);
|
||||
int EffectCreate(effect_uuid_t *effectUID, effect_interface_t *pInterface);
|
||||
int EffectRelease(effect_interface_t interface);
|
||||
|
||||
@@ -39,7 +39,7 @@ static int gInitDone; // true is global initialization has been preformed
|
||||
static int init();
|
||||
static int loadLibrary(const char *libPath, int *handle);
|
||||
static int unloadLibrary(int handle);
|
||||
static int numEffectModules();
|
||||
static uint32_t numEffectModules();
|
||||
static int findEffect(effect_uuid_t *uuid, lib_entry_t **lib, effect_descriptor_t **desc);
|
||||
static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
|
||||
|
||||
@@ -96,7 +96,7 @@ const struct effect_interface_s gInterface = {
|
||||
// Effect Factory Interface functions
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
int EffectQueryNumberEffects(int *pNumEffects)
|
||||
int EffectQueryNumberEffects(uint32_t *pNumEffects)
|
||||
{
|
||||
int ret = init();
|
||||
if (ret < 0) {
|
||||
@@ -353,8 +353,8 @@ int loadLibrary(const char *libPath, int *handle)
|
||||
effect_QueryNextEffect_t queryFx;
|
||||
effect_CreateEffect_t createFx;
|
||||
effect_ReleaseEffect_t releaseFx;
|
||||
int numFx;
|
||||
int fx;
|
||||
uint32_t numFx;
|
||||
uint32_t fx;
|
||||
int ret;
|
||||
list_elem_t *e, *descHead = NULL;
|
||||
lib_entry_t *l;
|
||||
@@ -525,9 +525,9 @@ int unloadLibrary(int handle)
|
||||
|
||||
|
||||
|
||||
int numEffectModules() {
|
||||
uint32_t numEffectModules() {
|
||||
list_elem_t *e = gLibraryList;
|
||||
int cnt = 0;
|
||||
uint32_t cnt = 0;
|
||||
|
||||
// Reset pointers for EffectQueryNext()
|
||||
gCurLib = e;
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace android {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
AudioRecord::AudioRecord()
|
||||
: mStatus(NO_INIT)
|
||||
: mStatus(NO_INIT), mSessionId(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -58,11 +58,12 @@ AudioRecord::AudioRecord(
|
||||
uint32_t flags,
|
||||
callback_t cbf,
|
||||
void* user,
|
||||
int notificationFrames)
|
||||
: mStatus(NO_INIT)
|
||||
int notificationFrames,
|
||||
int sessionId)
|
||||
: mStatus(NO_INIT), mSessionId(0)
|
||||
{
|
||||
mStatus = set(inputSource, sampleRate, format, channels,
|
||||
frameCount, flags, cbf, user, notificationFrames);
|
||||
frameCount, flags, cbf, user, notificationFrames, sessionId);
|
||||
}
|
||||
|
||||
AudioRecord::~AudioRecord()
|
||||
@@ -91,7 +92,8 @@ status_t AudioRecord::set(
|
||||
callback_t cbf,
|
||||
void* user,
|
||||
int notificationFrames,
|
||||
bool threadCanCallJava)
|
||||
bool threadCanCallJava,
|
||||
int sessionId)
|
||||
{
|
||||
|
||||
LOGV("set(): sampleRate %d, channels %d, frameCount %d",sampleRate, channels, frameCount);
|
||||
@@ -119,6 +121,7 @@ status_t AudioRecord::set(
|
||||
if (!AudioSystem::isInputChannel(channels)) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
int channelCount = AudioSystem::popCount(channels);
|
||||
|
||||
audio_io_handle_t input = AudioSystem::getInput(inputSource,
|
||||
@@ -164,6 +167,8 @@ status_t AudioRecord::set(
|
||||
notificationFrames = frameCount/2;
|
||||
}
|
||||
|
||||
mSessionId = sessionId;
|
||||
|
||||
// create the IAudioRecord
|
||||
status_t status = openRecord(sampleRate, format, channelCount,
|
||||
frameCount, flags, input);
|
||||
@@ -414,6 +419,7 @@ status_t AudioRecord::openRecord(
|
||||
channelCount,
|
||||
frameCount,
|
||||
((uint16_t)flags) << 16,
|
||||
&mSessionId,
|
||||
&status);
|
||||
if (record == 0) {
|
||||
LOGE("AudioFlinger could not create record track, status: %d", status);
|
||||
@@ -532,6 +538,11 @@ audio_io_handle_t AudioRecord::getInput()
|
||||
return mInput;
|
||||
}
|
||||
|
||||
int AudioRecord::getSessionId()
|
||||
{
|
||||
return mSessionId;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
ssize_t AudioRecord::read(void* buffer, size_t userSize)
|
||||
|
||||
@@ -364,6 +364,12 @@ unsigned int AudioSystem::getInputFramesLost(audio_io_handle_t ioHandle) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int AudioSystem::newAudioSessionId() {
|
||||
const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
|
||||
if (af == 0) return 0;
|
||||
return af->newAudioSessionId();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
|
||||
|
||||
@@ -58,7 +58,8 @@ AudioTrack::AudioTrack(
|
||||
uint32_t flags,
|
||||
callback_t cbf,
|
||||
void* user,
|
||||
int notificationFrames)
|
||||
int notificationFrames,
|
||||
int sessionId)
|
||||
: mStatus(NO_INIT)
|
||||
{
|
||||
mStatus = set(streamType, sampleRate, format, channels,
|
||||
@@ -74,7 +75,8 @@ AudioTrack::AudioTrack(
|
||||
uint32_t flags,
|
||||
callback_t cbf,
|
||||
void* user,
|
||||
int notificationFrames)
|
||||
int notificationFrames,
|
||||
int sessionId)
|
||||
: mStatus(NO_INIT)
|
||||
{
|
||||
mStatus = set(streamType, sampleRate, format, channels,
|
||||
@@ -110,7 +112,8 @@ status_t AudioTrack::set(
|
||||
void* user,
|
||||
int notificationFrames,
|
||||
const sp<IMemory>& sharedBuffer,
|
||||
bool threadCanCallJava)
|
||||
bool threadCanCallJava,
|
||||
int sessionId)
|
||||
{
|
||||
|
||||
LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size());
|
||||
@@ -171,8 +174,11 @@ status_t AudioTrack::set(
|
||||
|
||||
mVolume[LEFT] = 1.0f;
|
||||
mVolume[RIGHT] = 1.0f;
|
||||
mSendLevel = 0;
|
||||
mFrameCount = frameCount;
|
||||
mNotificationFramesReq = notificationFrames;
|
||||
mSessionId = sessionId;
|
||||
|
||||
// create the IAudioTrack
|
||||
status_t status = createTrack(streamType, sampleRate, format, channelCount,
|
||||
frameCount, flags, sharedBuffer, output, true);
|
||||
@@ -396,19 +402,49 @@ bool AudioTrack::muted() const
|
||||
return mMuted;
|
||||
}
|
||||
|
||||
void AudioTrack::setVolume(float left, float right)
|
||||
status_t AudioTrack::setVolume(float left, float right)
|
||||
{
|
||||
if (left > 1.0f || right > 1.0f) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mVolume[LEFT] = left;
|
||||
mVolume[RIGHT] = right;
|
||||
|
||||
// write must be atomic
|
||||
mCblk->volumeLR = (int32_t(int16_t(left * 0x1000)) << 16) | int16_t(right * 0x1000);
|
||||
mCblk->volumeLR = (uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void AudioTrack::getVolume(float* left, float* right)
|
||||
{
|
||||
*left = mVolume[LEFT];
|
||||
*right = mVolume[RIGHT];
|
||||
if (left != NULL) {
|
||||
*left = mVolume[LEFT];
|
||||
}
|
||||
if (right != NULL) {
|
||||
*right = mVolume[RIGHT];
|
||||
}
|
||||
}
|
||||
|
||||
status_t AudioTrack::setSendLevel(float level)
|
||||
{
|
||||
if (level > 1.0f) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
|
||||
mSendLevel = level;
|
||||
|
||||
mCblk->sendLevel = uint16_t(level * 0x1000);
|
||||
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
void AudioTrack::getSendLevel(float* level)
|
||||
{
|
||||
if (level != NULL) {
|
||||
*level = mSendLevel;
|
||||
}
|
||||
}
|
||||
|
||||
status_t AudioTrack::setSampleRate(int rate)
|
||||
@@ -563,6 +599,16 @@ audio_io_handle_t AudioTrack::getOutput()
|
||||
mCblk->sampleRate, mFormat, mChannels, (AudioSystem::output_flags)mFlags);
|
||||
}
|
||||
|
||||
int AudioTrack::getSessionId()
|
||||
{
|
||||
return mSessionId;
|
||||
}
|
||||
|
||||
status_t AudioTrack::attachAuxEffect(int effectId)
|
||||
{
|
||||
return mAudioTrack->attachAuxEffect(effectId);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
status_t AudioTrack::createTrack(
|
||||
@@ -647,6 +693,7 @@ status_t AudioTrack::createTrack(
|
||||
((uint16_t)flags) << 16,
|
||||
sharedBuffer,
|
||||
output,
|
||||
&mSessionId,
|
||||
&status);
|
||||
|
||||
if (track == 0) {
|
||||
@@ -672,7 +719,8 @@ status_t AudioTrack::createTrack(
|
||||
mCblk->stepUser(mCblk->frameCount);
|
||||
}
|
||||
|
||||
mCblk->volumeLR = (int32_t(int16_t(mVolume[LEFT] * 0x1000)) << 16) | int16_t(mVolume[RIGHT] * 0x1000);
|
||||
mCblk->volumeLR = (uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000);
|
||||
mCblk->sendLevel = uint16_t(mSendLevel * 0x1000);
|
||||
mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
|
||||
mCblk->waitTimeMs = 0;
|
||||
mRemainingFrames = mNotificationFramesAct;
|
||||
@@ -1016,7 +1064,7 @@ audio_track_cblk_t::audio_track_cblk_t()
|
||||
: lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
|
||||
userBase(0), serverBase(0), buffers(0), frameCount(0),
|
||||
loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), volumeLR(0),
|
||||
flags(0)
|
||||
flags(0), sendLevel(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,14 @@ enum {
|
||||
SET_STREAM_OUTPUT,
|
||||
SET_VOICE_VOLUME,
|
||||
GET_RENDER_POSITION,
|
||||
GET_INPUT_FRAMES_LOST
|
||||
GET_INPUT_FRAMES_LOST,
|
||||
NEW_AUDIO_SESSION_ID,
|
||||
LOAD_EFFECT_LIBRARY,
|
||||
UNLOAD_EFFECT_LIBRARY,
|
||||
QUERY_NUM_EFFECTS,
|
||||
QUERY_NEXT_EFFECT,
|
||||
GET_EFFECT_DESCRIPTOR,
|
||||
CREATE_EFFECT
|
||||
};
|
||||
|
||||
class BpAudioFlinger : public BpInterface<IAudioFlinger>
|
||||
@@ -83,6 +90,7 @@ public:
|
||||
uint32_t flags,
|
||||
const sp<IMemory>& sharedBuffer,
|
||||
int output,
|
||||
int *sessionId,
|
||||
status_t *status)
|
||||
{
|
||||
Parcel data, reply;
|
||||
@@ -97,10 +105,19 @@ public:
|
||||
data.writeInt32(flags);
|
||||
data.writeStrongBinder(sharedBuffer->asBinder());
|
||||
data.writeInt32(output);
|
||||
int lSessionId = 0;
|
||||
if (sessionId != NULL) {
|
||||
lSessionId = *sessionId;
|
||||
}
|
||||
data.writeInt32(lSessionId);
|
||||
status_t lStatus = remote()->transact(CREATE_TRACK, data, &reply);
|
||||
if (lStatus != NO_ERROR) {
|
||||
LOGE("createTrack error: %s", strerror(-lStatus));
|
||||
} else {
|
||||
lSessionId = reply.readInt32();
|
||||
if (sessionId != NULL) {
|
||||
*sessionId = lSessionId;
|
||||
}
|
||||
lStatus = reply.readInt32();
|
||||
track = interface_cast<IAudioTrack>(reply.readStrongBinder());
|
||||
}
|
||||
@@ -118,6 +135,7 @@ public:
|
||||
int channelCount,
|
||||
int frameCount,
|
||||
uint32_t flags,
|
||||
int *sessionId,
|
||||
status_t *status)
|
||||
{
|
||||
Parcel data, reply;
|
||||
@@ -130,10 +148,19 @@ public:
|
||||
data.writeInt32(channelCount);
|
||||
data.writeInt32(frameCount);
|
||||
data.writeInt32(flags);
|
||||
int lSessionId = 0;
|
||||
if (sessionId != NULL) {
|
||||
lSessionId = *sessionId;
|
||||
}
|
||||
data.writeInt32(lSessionId);
|
||||
status_t lStatus = remote()->transact(OPEN_RECORD, data, &reply);
|
||||
if (lStatus != NO_ERROR) {
|
||||
LOGE("openRecord error: %s", strerror(-lStatus));
|
||||
} else {
|
||||
lSessionId = reply.readInt32();
|
||||
if (sessionId != NULL) {
|
||||
*sessionId = lSessionId;
|
||||
}
|
||||
lStatus = reply.readInt32();
|
||||
record = interface_cast<IAudioRecord>(reply.readStrongBinder());
|
||||
}
|
||||
@@ -497,6 +524,157 @@ public:
|
||||
remote()->transact(GET_INPUT_FRAMES_LOST, data, &reply);
|
||||
return reply.readInt32();
|
||||
}
|
||||
|
||||
virtual int newAudioSessionId()
|
||||
{
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
|
||||
status_t status = remote()->transact(NEW_AUDIO_SESSION_ID, data, &reply);
|
||||
int id = 0;
|
||||
if (status == NO_ERROR) {
|
||||
id = reply.readInt32();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
virtual status_t loadEffectLibrary(const char *libPath, int *handle)
|
||||
{
|
||||
if (libPath == NULL || handle == NULL) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
*handle = 0;
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
|
||||
data.writeCString(libPath);
|
||||
status_t status = remote()->transact(LOAD_EFFECT_LIBRARY, data, &reply);
|
||||
if (status == NO_ERROR) {
|
||||
status = reply.readInt32();
|
||||
if (status == NO_ERROR) {
|
||||
*handle = reply.readInt32();
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual status_t unloadEffectLibrary(int handle)
|
||||
{
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
|
||||
data.writeInt32(handle);
|
||||
status_t status = remote()->transact(UNLOAD_EFFECT_LIBRARY, data, &reply);
|
||||
if (status == NO_ERROR) {
|
||||
status = reply.readInt32();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
virtual status_t queryNumberEffects(uint32_t *numEffects)
|
||||
{
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
|
||||
status_t status = remote()->transact(QUERY_NUM_EFFECTS, data, &reply);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
status = reply.readInt32();
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
if (numEffects) {
|
||||
*numEffects = (uint32_t)reply.readInt32();
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
virtual status_t queryNextEffect(effect_descriptor_t *pDescriptor)
|
||||
{
|
||||
if (pDescriptor == NULL) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
|
||||
status_t status = remote()->transact(QUERY_NEXT_EFFECT, data, &reply);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
status = reply.readInt32();
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
reply.read(pDescriptor, sizeof(effect_descriptor_t));
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
virtual status_t getEffectDescriptor(effect_uuid_t *pUuid, effect_descriptor_t *pDescriptor)
|
||||
{
|
||||
if (pUuid == NULL || pDescriptor == NULL) {
|
||||
return BAD_VALUE;
|
||||
}
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
|
||||
data.write(pUuid, sizeof(effect_uuid_t));
|
||||
status_t status = remote()->transact(GET_EFFECT_DESCRIPTOR, data, &reply);
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
status = reply.readInt32();
|
||||
if (status != NO_ERROR) {
|
||||
return status;
|
||||
}
|
||||
reply.read(pDescriptor, sizeof(effect_descriptor_t));
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
virtual sp<IEffect> createEffect(pid_t pid,
|
||||
effect_descriptor_t *pDesc,
|
||||
const sp<IEffectClient>& client,
|
||||
int32_t priority,
|
||||
int output,
|
||||
int sessionId,
|
||||
status_t *status,
|
||||
int *id,
|
||||
int *enabled)
|
||||
{
|
||||
Parcel data, reply;
|
||||
sp<IEffect> effect;
|
||||
|
||||
if (pDesc == NULL) {
|
||||
return effect;
|
||||
if (status) {
|
||||
*status = BAD_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
|
||||
data.writeInt32(pid);
|
||||
data.write(pDesc, sizeof(effect_descriptor_t));
|
||||
data.writeStrongBinder(client->asBinder());
|
||||
data.writeInt32(priority);
|
||||
data.writeInt32(output);
|
||||
data.writeInt32(sessionId);
|
||||
|
||||
status_t lStatus = remote()->transact(CREATE_EFFECT, data, &reply);
|
||||
if (lStatus != NO_ERROR) {
|
||||
LOGE("createEffect error: %s", strerror(-lStatus));
|
||||
} else {
|
||||
lStatus = reply.readInt32();
|
||||
int tmp = reply.readInt32();
|
||||
if (id) {
|
||||
*id = tmp;
|
||||
}
|
||||
tmp = reply.readInt32();
|
||||
if (enabled) {
|
||||
*enabled = tmp;
|
||||
}
|
||||
effect = interface_cast<IEffect>(reply.readStrongBinder());
|
||||
reply.read(pDesc, sizeof(effect_descriptor_t));
|
||||
}
|
||||
if (status) {
|
||||
*status = lStatus;
|
||||
}
|
||||
|
||||
return effect;
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_META_INTERFACE(AudioFlinger, "android.media.IAudioFlinger");
|
||||
@@ -518,10 +696,12 @@ status_t BnAudioFlinger::onTransact(
|
||||
uint32_t flags = data.readInt32();
|
||||
sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder());
|
||||
int output = data.readInt32();
|
||||
int sessionId = data.readInt32();
|
||||
status_t status;
|
||||
sp<IAudioTrack> track = createTrack(pid,
|
||||
streamType, sampleRate, format,
|
||||
channelCount, bufferCount, flags, buffer, output, &status);
|
||||
channelCount, bufferCount, flags, buffer, output, &sessionId, &status);
|
||||
reply->writeInt32(sessionId);
|
||||
reply->writeInt32(status);
|
||||
reply->writeStrongBinder(track->asBinder());
|
||||
return NO_ERROR;
|
||||
@@ -535,9 +715,11 @@ status_t BnAudioFlinger::onTransact(
|
||||
int channelCount = data.readInt32();
|
||||
size_t bufferCount = data.readInt32();
|
||||
uint32_t flags = data.readInt32();
|
||||
int sessionId = data.readInt32();
|
||||
status_t status;
|
||||
sp<IAudioRecord> record = openRecord(pid, input,
|
||||
sampleRate, format, channelCount, bufferCount, flags, &status);
|
||||
sampleRate, format, channelCount, bufferCount, flags, &sessionId, &status);
|
||||
reply->writeInt32(sessionId);
|
||||
reply->writeInt32(status);
|
||||
reply->writeStrongBinder(record->asBinder());
|
||||
return NO_ERROR;
|
||||
@@ -768,7 +950,79 @@ status_t BnAudioFlinger::onTransact(
|
||||
reply->writeInt32(getInputFramesLost(ioHandle));
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case NEW_AUDIO_SESSION_ID: {
|
||||
CHECK_INTERFACE(IAudioFlinger, data, reply);
|
||||
reply->writeInt32(newAudioSessionId());
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
case LOAD_EFFECT_LIBRARY: {
|
||||
CHECK_INTERFACE(IAudioFlinger, data, reply);
|
||||
int handle;
|
||||
status_t status = loadEffectLibrary(data.readCString(), &handle);
|
||||
reply->writeInt32(status);
|
||||
if (status == NO_ERROR) {
|
||||
reply->writeInt32(handle);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
case UNLOAD_EFFECT_LIBRARY: {
|
||||
CHECK_INTERFACE(IAudioFlinger, data, reply);
|
||||
reply->writeInt32(unloadEffectLibrary(data.readInt32()));
|
||||
return NO_ERROR;
|
||||
}
|
||||
case QUERY_NUM_EFFECTS: {
|
||||
CHECK_INTERFACE(IAudioFlinger, data, reply);
|
||||
uint32_t numEffects;
|
||||
status_t status = queryNumberEffects(&numEffects);
|
||||
reply->writeInt32(status);
|
||||
if (status == NO_ERROR) {
|
||||
reply->writeInt32((int32_t)numEffects);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
case QUERY_NEXT_EFFECT: {
|
||||
CHECK_INTERFACE(IAudioFlinger, data, reply);
|
||||
effect_descriptor_t desc;
|
||||
status_t status = queryNextEffect(&desc);
|
||||
reply->writeInt32(status);
|
||||
if (status == NO_ERROR) {
|
||||
reply->write(&desc, sizeof(effect_descriptor_t));
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
case GET_EFFECT_DESCRIPTOR: {
|
||||
CHECK_INTERFACE(IAudioFlinger, data, reply);
|
||||
effect_uuid_t uuid;
|
||||
data.read(&uuid, sizeof(effect_uuid_t));
|
||||
effect_descriptor_t desc;
|
||||
status_t status = getEffectDescriptor(&uuid, &desc);
|
||||
reply->writeInt32(status);
|
||||
if (status == NO_ERROR) {
|
||||
reply->write(&desc, sizeof(effect_descriptor_t));
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
case CREATE_EFFECT: {
|
||||
CHECK_INTERFACE(IAudioFlinger, data, reply);
|
||||
pid_t pid = data.readInt32();
|
||||
effect_descriptor_t desc;
|
||||
data.read(&desc, sizeof(effect_descriptor_t));
|
||||
sp<IEffectClient> client = interface_cast<IEffectClient>(data.readStrongBinder());
|
||||
int32_t priority = data.readInt32();
|
||||
int output = data.readInt32();
|
||||
int sessionId = data.readInt32();
|
||||
status_t status;
|
||||
int id;
|
||||
int enabled;
|
||||
|
||||
sp<IEffect> effect = createEffect(pid, &desc, client, priority, output, sessionId, &status, &id, &enabled);
|
||||
reply->writeInt32(status);
|
||||
reply->writeInt32(id);
|
||||
reply->writeInt32(enabled);
|
||||
reply->writeStrongBinder(effect->asBinder());
|
||||
reply->write(&desc, sizeof(effect_descriptor_t));
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
default:
|
||||
return BBinder::onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ enum {
|
||||
STOP,
|
||||
FLUSH,
|
||||
MUTE,
|
||||
PAUSE
|
||||
PAUSE,
|
||||
ATTACH_AUX_EFFECT
|
||||
};
|
||||
|
||||
class BpAudioTrack : public BpInterface<IAudioTrack>
|
||||
@@ -97,7 +98,21 @@ public:
|
||||
cblk = interface_cast<IMemory>(reply.readStrongBinder());
|
||||
}
|
||||
return cblk;
|
||||
}
|
||||
}
|
||||
|
||||
virtual status_t attachAuxEffect(int effectId)
|
||||
{
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
|
||||
data.writeInt32(effectId);
|
||||
status_t status = remote()->transact(ATTACH_AUX_EFFECT, data, &reply);
|
||||
if (status == NO_ERROR) {
|
||||
status = reply.readInt32();
|
||||
} else {
|
||||
LOGW("attachAuxEffect() error: %s", strerror(-status));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
};
|
||||
|
||||
IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack");
|
||||
@@ -138,6 +153,11 @@ status_t BnAudioTrack::onTransact(
|
||||
pause();
|
||||
return NO_ERROR;
|
||||
}
|
||||
case ATTACH_AUX_EFFECT: {
|
||||
CHECK_INTERFACE(IAudioTrack, data, reply);
|
||||
reply->writeInt32(attachAuxEffect(data.readInt32()));
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
default:
|
||||
return BBinder::onTransact(code, data, reply, flags);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user