@ -18,8 +18,11 @@
# ifndef ANDROID_AUDIO_MIXER_H
# define ANDROID_AUDIO_MIXER_H
# include <pthread.h>
# include <sstream>
# include <stdint.h>
# include <sys/types.h>
# include <unordered_map>
# include <media/AudioBufferProvider.h>
# include <media/AudioResampler.h>
@ -43,20 +46,14 @@ namespace android {
class AudioMixer
{
public :
AudioMixer ( size_t frameCount , uint32_t sampleRate ,
uint32_t maxNumTracks = MAX_NUM_TRACKS ) ;
/*virtual*/ ~ AudioMixer ( ) ; // non-virtual saves a v-table, restore if sub-classed
// This mixer has a hard-coded upper limit of 32 active track inputs.
// Adding support for > 32 tracks would require more than simply changing this value.
static const uint32_t MAX_NUM_TRACKS = 32 ;
// maximum number of channels supported by the mixer
// This mixer has a hard-coded upper limit of active track inputs;
// the value is arbitrary but should be less than TRACK0 to avoid confusion.
static constexpr int32_t MAX_NUM_TRACKS = 256 ;
// Do not change these unless underlying code changes.
// This mixer has a hard-coded upper limit of 8 channels for output.
static const uint32_t MAX_NUM_CHANNELS = 8;
static const uint32_t MAX_NUM_VOLUMES = 2; // stereo volume only
static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8 ;
static constexpr uint32_t MAX_NUM_VOLUMES = FCC_2 ; // stereo volume only
// maximum number of channels supported for the content
static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX ;
@ -108,6 +105,12 @@ public:
// parameter 'value' is a pointer to the new playback rate.
} ;
AudioMixer ( size_t frameCount , uint32_t sampleRate , int32_t maxNumTracks = MAX_NUM_TRACKS )
: mMaxNumTracks ( maxNumTracks )
, mSampleRate ( sampleRate )
, mFrameCount ( frameCount ) {
pthread_once ( & sOnceControl , & sInitRoutine ) ;
}
// For all APIs with "name": TRACK0 <= name < TRACK0 + MAX_NUM_TRACKS
@ -127,27 +130,38 @@ public:
void setParameter ( int name , int target , int param , void * value ) ;
void setBufferProvider ( int name , AudioBufferProvider * bufferProvider ) ;
void process ( ) ;
uint32_t trackNames ( ) const { return mTrackNames ; }
void process ( ) {
( this - > * mHook ) ( ) ;
}
size_t getUnreleasedFrames ( int name ) const ;
static inline bool isValidPcmTrackFormat ( audio_format_t format ) {
switch ( format ) {
case AUDIO_FORMAT_PCM_8_BIT :
case AUDIO_FORMAT_PCM_16_BIT :
case AUDIO_FORMAT_PCM_24_BIT_PACKED :
case AUDIO_FORMAT_PCM_32_BIT :
case AUDIO_FORMAT_PCM_FLOAT :
return true ;
default :
return false ;
std : : string trackNames ( ) const {
std : : stringstream ss ;
for ( const auto & pair : mTracks ) {
ss < < pair . first < < " " ;
}
return ss . str ( ) ;
}
void setNBLogWriter ( NBLog : : Writer * logWriter ) {
mNBLogWriter = logWriter ;
}
private :
/* For multi-format functions (calls template functions
* in AudioMixerOps . h ) . The template parameters are as follows :
*
* MIXTYPE ( see AudioMixerOps . h MIXTYPE_ * enumeration )
* USEFLOATVOL ( set to true if float volume is used )
* ADJUSTVOL ( set to true if volume ramp parameters needs adjustment afterwards )
* TO : int32_t ( Q4 .27 ) or float
* TI : int32_t ( Q4 .27 ) or int16_t ( Q0 .15 ) or float
* TA : int32_t ( Q4 .27 )
*/
enum {
// FIXME this representation permits up to 8 channels
NEEDS_CHANNEL_COUNT__MASK = 0x00000007 ,
@ -164,14 +178,67 @@ private:
NEEDS_AUX = 0x00010000 ,
} ;
struct state_t ;
struct track_t ;
// hook types
enum {
PROCESSTYPE_NORESAMPLEONETRACK , // others set elsewhere
} ;
enum {
TRACKTYPE_NOP ,
TRACKTYPE_RESAMPLE ,
TRACKTYPE_NORESAMPLE ,
TRACKTYPE_NORESAMPLEMONO ,
} ;
// process hook functionality
using process_hook_t = void ( AudioMixer : : * ) ( ) ;
struct Track ;
using hook_t = void ( Track : : * ) ( int32_t * output , size_t numOutFrames , int32_t * temp , int32_t * aux ) ;
struct Track {
Track ( )
: bufferProvider ( nullptr )
{
// TODO: move additional initialization here.
}
~ Track ( )
{
// bufferProvider, mInputBufferProvider need not be deleted.
mResampler . reset ( nullptr ) ;
// Ensure the order of destruction of buffer providers as they
// release the upstream provider in the destructor.
mTimestretchBufferProvider . reset ( nullptr ) ;
mPostDownmixReformatBufferProvider . reset ( nullptr ) ;
mDownmixerBufferProvider . reset ( nullptr ) ;
mReformatBufferProvider . reset ( nullptr ) ;
}
bool needsRamp ( ) { return ( volumeInc [ 0 ] | volumeInc [ 1 ] | auxInc ) ! = 0 ; }
bool setResampler ( uint32_t trackSampleRate , uint32_t devSampleRate ) ;
bool doesResample ( ) const { return mResampler . get ( ) ! = nullptr ; }
void resetResampler ( ) { if ( mResampler . get ( ) ! = nullptr ) mResampler - > reset ( ) ; }
void adjustVolumeRamp ( bool aux , bool useFloat = false ) ;
size_t getUnreleasedFrames ( ) const { return mResampler . get ( ) ! = nullptr ?
mResampler - > getUnreleasedFrames ( ) : 0 ; } ;
status_t prepareForDownmix ( ) ;
void unprepareForDownmix ( ) ;
status_t prepareForReformat ( ) ;
void unprepareForReformat ( ) ;
bool setPlaybackRate ( const AudioPlaybackRate & playbackRate ) ;
void reconfigureBufferProviders ( ) ;
static hook_t getTrackHook ( int trackType , uint32_t channelCount ,
audio_format_t mixerInFormat , audio_format_t mixerOutFormat ) ;
void track__nop ( int32_t * out , size_t numFrames , int32_t * temp , int32_t * aux ) ;
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
template < int MIXTYPE , bool USEFLOATVOL , bool ADJUSTVOL ,
typename TO , typename TI , typename TA >
void volumeMix ( TO * out , size_t outFrames , const TI * in , TA * aux , bool ramp ) ;
struct track_t {
uint32_t needs ;
// TODO: Eventually remove legacy integer volume settings
@ -181,16 +248,11 @@ private:
} ;
int32_t prevVolume [ MAX_NUM_VOLUMES ] ;
// 16-byte boundary
int32_t volumeInc [ MAX_NUM_VOLUMES ] ;
int32_t auxInc ;
int32_t prevAuxLevel ;
// 16-byte boundary
int16_t auxLevel ; // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance
uint16_t frameCount ;
uint8_t channelCount ; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK)
@ -202,22 +264,16 @@ private:
// for how the Track buffer provider is wrapped by another one when dowmixing is required
AudioBufferProvider * bufferProvider ;
// 16-byte boundary
mutable AudioBufferProvider : : Buffer buffer ; // 8 bytes
hook_t hook ;
const void * i n; // current location in buffer
const void * mI n; // current location in buffer
// 16-byte boundary
AudioResampler * resampler ;
std : : unique_ptr < AudioResampler > mResampler ;
uint32_t sampleRate ;
int32_t * mainBuffer ;
int32_t * auxBuffer ;
// 16-byte boundary
/* Buffer providers are constructed to translate the track input data as needed.
*
* TODO : perhaps make a single PlaybackConverterProvider class to move
@ -228,17 +284,17 @@ private:
* match either mMixerInFormat or mDownmixRequiresFormat , if the downmixer
* requires reformat . For example , it may convert floating point input to
* PCM_16_bit if that ' s required by the downmixer .
* 3 ) d ownmixerBufferProvider: If not NULL , performs the channel remixing to match
* 3 ) mD ownmixerBufferProvider: If not NULL , performs the channel remixing to match
* the number of channels required by the mixer sink .
* 4 ) mPostDownmixReformatBufferProvider : If not NULL , performs reformatting from
* the downmixer requirements to the mixer engine input requirements .
* 5 ) mTimestretchBufferProvider : Adds timestretching for playback rate
*/
AudioBufferProvider * mInputBufferProvider ; // externally provided buffer provider.
PassthruBufferProvider* mReformatBufferProvider ; // provider wrapper for reformatting.
PassthruBufferProvider* downmixerBufferProvider ; // wrapper for channel conversion.
PassthruBufferProvider* mPostDownmixReformatBufferProvider ;
PassthruBufferProvider* mTimestretchBufferProvider ;
std: : unique_ptr < PassthruBufferProvider > mReformatBufferProvider ;
std: : unique_ptr < PassthruBufferProvider > mDownmixerBufferProvider ;
std: : unique_ptr < PassthruBufferProvider > mPostDownmixReformatBufferProvider ;
std: : unique_ptr < PassthruBufferProvider > mTimestretchBufferProvider ;
int32_t sessionId ;
@ -263,129 +319,94 @@ private:
AudioPlaybackRate mPlaybackRate ;
bool needsRamp ( ) { return ( volumeInc [ 0 ] | volumeInc [ 1 ] | auxInc ) ! = 0 ; }
bool setResampler ( uint32_t trackSampleRate , uint32_t devSampleRate ) ;
bool doesResample ( ) const { return resampler ! = NULL ; }
void resetResampler ( ) { if ( resampler ! = NULL ) resampler - > reset ( ) ; }
void adjustVolumeRamp ( bool aux , bool useFloat = false ) ;
size_t getUnreleasedFrames ( ) const { return resampler ! = NULL ?
resampler - > getUnreleasedFrames ( ) : 0 ; } ;
private :
// hooks
void track__genericResample ( int32_t * out , size_t numFrames , int32_t * temp , int32_t * aux ) ;
void track__16BitsStereo ( int32_t * out , size_t numFrames , int32_t * temp , int32_t * aux ) ;
void track__16BitsMono ( int32_t * out , size_t numFrames , int32_t * temp , int32_t * aux ) ;
status_t prepareForDownmix ( ) ;
void unprepareForDownmix ( ) ;
status_t prepareForReformat ( ) ;
void unprepareForReformat ( ) ;
bool setPlaybackRate ( const AudioPlaybackRate & playbackRate ) ;
void reconfigureBufferProviders ( ) ;
} ;
void volumeRampStereo ( int32_t * out , size_t frameCount , int32_t * temp , int32_t * aux ) ;
void volumeStereo ( int32_t * out , size_t frameCount , int32_t * temp , int32_t * aux ) ;
typedef void ( * process_hook_t ) ( state_t * state ) ;
// pad to 32-bytes to fill cache line
struct state_t {
uint32_t enabledTracks ;
uint32_t needsChanged ;
size_t frameCount ;
process_hook_t hook ; // one of process__*, never NULL
int32_t * outputTemp ;
int32_t * resampleTemp ;
NBLog : : Writer * mNBLogWriter ; // associated NBLog::Writer or &mDummyLog
int32_t reserved [ 1 ] ;
// FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
track_t tracks [ MAX_NUM_TRACKS ] __attribute__ ( ( aligned ( 32 ) ) ) ;
// multi-format track hooks
template < int MIXTYPE , typename TO , typename TI , typename TA >
void track__Resample ( TO * out , size_t frameCount , TO * temp __unused , TA * aux ) ;
template < int MIXTYPE , typename TO , typename TI , typename TA >
void track__NoResample ( TO * out , size_t frameCount , TO * temp __unused , TA * aux ) ;
} ;
// bitmask of allocated track names, where bit 0 corresponds to TRACK0 etc.
uint32_t mTrackNames ;
// bitmask of configured track names; ~0 if maxNumTracks == MAX_NUM_TRACKS,
// but will have fewer bits set if maxNumTracks < MAX_NUM_TRACKS
const uint32_t mConfiguredNames ;
const uint32_t mSampleRate ;
NBLog : : Writer mDummyLogWriter ;
public :
// Called by FastMixer to inform AudioMixer of it's associated NBLog::Writer.
// FIXME It would be safer to use TLS for this, so we don't accidentally use wrong one.
void setNBLogWriter ( NBLog : : Writer * log ) ;
private :
state_t mState __attribute__ ( ( aligned ( 32 ) ) ) ;
// Call after changing either the enabled status of a track, or parameters of an enabled track.
// OK to call more often than that, but unnecessary.
void invalidateState ( uint32_t mask ) ;
// TODO: remove BLOCKSIZE unit of processing - it isn't needed anymore.
static constexpr int BLOCKSIZE = 16 ;
bool setChannelMasks ( int name ,
audio_channel_mask_t trackChannelMask , audio_channel_mask_t mixerChannelMask ) ;
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 ) ;
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 pthread_once_t sOnceControl ;
static void sInitRoutine ( ) ;
/* multi-format volume mixing function (calls template functions
* in AudioMixerOps . h ) . The template parameters are as follows :
*
* MIXTYPE ( see AudioMixerOps . h MIXTYPE_ * enumeration )
* USEFLOATVOL ( set to true if float volume is used )
* ADJUSTVOL ( set to true if volume ramp parameters needs adjustment afterwards )
* TO : int32_t ( Q4 .27 ) or float
* TI : int32_t ( Q4 .27 ) or int16_t ( Q0 .15 ) or float
* TA : int32_t ( Q4 .27 )
*/
template < int MIXTYPE , bool USEFLOATVOL , bool ADJUSTVOL ,
typename TO , typename TI , typename TA >
static void volumeMix ( TO * out , size_t outFrames ,
const TI * in , TA * aux , bool ramp , AudioMixer : : track_t * t ) ;
// Called when track info changes and a new process hook should be determined.
void invalidate ( ) {
mHook = & AudioMixer : : process__validate ;
}
// multi-format process hooks
template < int MIXTYPE , typename TO , typename TI , typename TA >
static void process_NoResampleOneTrack ( state_t * state ) ;
void process__validate ( ) ;
void process__nop ( ) ;
void process__genericNoResampling ( ) ;
void process__genericResampling ( ) ;
void process__oneTrack16BitsStereoNoResampling ( ) ;
// multi-format track hooks
template < int MIXTYPE , typename TO , typename TI , typename TA >
static void track__Resample ( track_t * t , TO * out , size_t frameCount ,
TO * temp __unused , TA * aux ) ;
template < int MIXTYPE , typename TO , typename TI , typename TA >
static void track__NoResample ( track_t * t , TO * out , size_t frameCount ,
TO * temp __unused , TA * aux ) ;
void process__noResampleOneTrack ( ) ;
static process_hook_t getProcessHook ( int processType , uint32_t channelCount ,
audio_format_t mixerInFormat , audio_format_t mixerOutFormat ) ;
static void convertMixerFormat ( void * out , audio_format_t mixerOutFormat ,
void * in , audio_format_t mixerInFormat , size_t sampleCount ) ;
// hook types
enum {
PROCESSTYPE_NORESAMPLEONETRACK ,
} ;
enum {
TRACKTYPE_NOP ,
TRACKTYPE_RESAMPLE ,
TRACKTYPE_NORESAMPLE ,
TRACKTYPE_NORESAMPLEMONO ,
} ;
static inline bool isValidPcmTrackFormat ( audio_format_t format ) {
switch ( format ) {
case AUDIO_FORMAT_PCM_8_BIT :
case AUDIO_FORMAT_PCM_16_BIT :
case AUDIO_FORMAT_PCM_24_BIT_PACKED :
case AUDIO_FORMAT_PCM_32_BIT :
case AUDIO_FORMAT_PCM_FLOAT :
return true ;
default :
return false ;
}
}
// functions for determining the proper process and track hooks.
static process_hook_t getProcessHook ( int processType , uint32_t channelCount ,
audio_format_t mixerInFormat , audio_format_t mixerOutFormat ) ;
static hook_t getTrackHook ( int trackType , uint32_t channelCount ,
audio_format_t mixerInFormat , audio_format_t mixerOutFormat ) ;
static void sInitRoutine ( ) ;
// initialization constants
const int mMaxNumTracks ;
const uint32_t mSampleRate ;
const size_t mFrameCount ;
NBLog : : Writer * mNBLogWriter = nullptr ; // associated NBLog::Writer
process_hook_t mHook = & AudioMixer : : process__nop ; // one of process__*, never nullptr
// the size of the type (int32_t) should be the largest of all types supported
// by the mixer.
std : : unique_ptr < int32_t [ ] > mOutputTemp ;
std : : unique_ptr < int32_t [ ] > mResampleTemp ;
// fast lookup of previously deleted track names for reuse.
// the AudioMixer tries to return the smallest unused name -
// this is an arbitrary decision (actually any non-negative
// integer that isn't in mTracks could be used).
std : : set < int /* name */ > mUnusedNames ; // set of unused track names (may be empty)
// track names grouped by main buffer, in no particular order of main buffer.
// however names for a particular main buffer are in order (by construction).
std : : unordered_map < void * /* mainBuffer */ , std : : vector < int /* name */ > > mGroups ;
// track names that are enabled, in increasing order (by construction).
std : : vector < int /* name */ > mEnabled ;
// track smart pointers, by name, in increasing order of name.
std : : map < int /* name */ , std : : shared_ptr < Track > > mTracks ;
static pthread_once_t sOnceControl ; // initialized in constructor by first new
} ;
// ----------------------------------------------------------------------------