Análisis de implementación de audiomixer en Android.

La biblioteca del módulo de procesamiento de audio del marco de Android, libaudioprocessing (ubicada en frameworks/av/media/libaudioprocessing ), proporciona un componente mezclador AudioMixer, que se utiliza principalmente en audioflinger para mezclar datos de origen de audio multicanal para facilitar el envío al dispositivo de audio para su reproducción. .

La operación de mezcla de audio en sí generalmente solo suma los datos de muestreo de cada fuente de audio, sin embargo, al diseñar un mezclador, generalmente hay que considerar cómo resolver los siguientes problemas:

  • ¿Cómo representar los datos de la fuente de audio que se van a mezclar?
  • ¿Cómo configurar la fuente de datos de audio para mezclar una fuente de audio?
  • ¿Cómo configurar el búfer de datos para la salida de datos después de la mezcla o cómo obtener los datos después de la mezcla?
  • ¿Cómo determinar el formato de datos de salida y la configuración del mezclador, es decir, los establece un usuario externo a través de la interfaz proporcionada o se calcula dinámicamente en función del formato de datos y la configuración de cada fuente de audio?
  • Cuando el formato de datos y la configuración de la fuente de audio son diferentes del formato y la configuración de los datos de salida, ¿cómo convertir el formato y la configuración de los datos?
  • ¿Cómo agregar o crear una fuente de audio al mezclador?
  • ¿Cómo modificar los parámetros de una fuente de audio del mezclador?
  • ¿Cómo eliminar una fuente de audio del mezclador?
  • Al mezclar, ¿qué debo hacer si los datos después de la mezcla cruzan el límite?
  • ¿Cómo se conduce la operación de mezcla? ¿El mezclador inicia un hilo interno para realizar la operación de mezcla y arroja el resultado a través de la devolución de llamada, o el usuario llama activamente a la operación de mezcla?

Aquí analizamos principalmente la implementación del componente mezclador de Android en función de los problemas anteriores AudioMixer. AudioMixerLa definición de la clase (ubicada en frameworks/av/media/libaudioprocessing/include/media/AudioMixer.h ) es la siguiente:

class AudioMixer : public AudioMixerBase
{
public:
    // maximum number of channels supported for the content
    static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX;

    enum { // extension of AudioMixerBase parameters
        DOWNMIX_TYPE    = 0x4004,
        // for haptic
        HAPTIC_ENABLED  = 0x4007, // Set haptic data from this track should be played or not.
        HAPTIC_INTENSITY = 0x4008, // Set the intensity to play haptic data.
        HAPTIC_MAX_AMPLITUDE = 0x4009, // Set the max amplitude allowed for haptic data.
        // for target TIMESTRETCH
        PLAYBACK_RATE   = 0x4300, // Configure timestretch on this track name;
                                  // parameter 'value' is a pointer to the new playback rate.
    };

    AudioMixer(size_t frameCount, uint32_t sampleRate)
            : AudioMixerBase(frameCount, sampleRate) {
        pthread_once(&sOnceControl, &sInitRoutine);
    }

    bool isValidChannelMask(audio_channel_mask_t channelMask) const override;

    void setParameter(int name, int target, int param, void *value) override;
    void setBufferProvider(int name, AudioBufferProvider* bufferProvider);

private:
 . . . . . .
    inline std::shared_ptr<Track> getTrack(int name) {
        return std::static_pointer_cast<Track>(mTracks[name]);
    }

    std::shared_ptr<TrackBase> preCreateTrack() override;
    status_t postCreateTrack(TrackBase *track) override;

    void preProcess() override;
    void postProcess() override;

    bool setChannelMasks(int name,
            audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) override;

    static void sInitRoutine();

    static pthread_once_t sOnceControl; // initialized in constructor by first new
};

AudioMixerLa clase hereda de la clase AudioMixerBase, que contiene una estructura interna struct Trackque struct Trackhereda de AudioMixerBase::TrackBase. AudioMixerBaseLa definición de la clase (ubicada en frameworks/av/media/libaudioprocessing/include/media/AudioMixerBase.h ) es la siguiente:

class AudioMixerBase
{
public:
    // Do not change these unless underlying code changes.
    static constexpr uint32_t MAX_NUM_CHANNELS = FCC_LIMIT;
    static constexpr uint32_t MAX_NUM_VOLUMES = FCC_2; // stereo volume only

    static const uint16_t UNITY_GAIN_INT = 0x1000;
    static const CONSTEXPR float UNITY_GAIN_FLOAT = 1.0f;

    enum { // names
        // setParameter targets
        TRACK           = 0x3000,
        RESAMPLE        = 0x3001,
        RAMP_VOLUME     = 0x3002, // ramp to new volume
        VOLUME          = 0x3003, // don't ramp
        TIMESTRETCH     = 0x3004,

        // set Parameter names
        // for target TRACK
        CHANNEL_MASK    = 0x4000,
        FORMAT          = 0x4001,
        MAIN_BUFFER     = 0x4002,
        AUX_BUFFER      = 0x4003,
        // 0x4004 reserved
        MIXER_FORMAT    = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
        MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output
        // for target RESAMPLE
        SAMPLE_RATE     = 0x4100, // Configure sample rate conversion on this track name;
                                  // parameter 'value' is the new sample rate in Hz.
                                  // Only creates a sample rate converter the first time that
                                  // the track sample rate is different from the mix sample rate.
                                  // If the new sample rate is the same as the mix sample rate,
                                  // and a sample rate converter already exists,
                                  // then the sample rate converter remains present but is a no-op.
        RESET           = 0x4101, // Reset sample rate converter without changing sample rate.
                                  // This clears out the resampler's input buffer.
        REMOVE          = 0x4102, // Remove the sample rate converter on this track name;
                                  // the track is restored to the mix sample rate.
        // for target RAMP_VOLUME and VOLUME (8 channels max)
        // FIXME use float for these 3 to improve the dynamic range
        VOLUME0         = 0x4200,
        VOLUME1         = 0x4201,
        AUXLEVEL        = 0x4210,
    };

    AudioMixerBase(size_t frameCount, uint32_t sampleRate)
        : mSampleRate(sampleRate)
        , mFrameCount(frameCount) {
    }

    virtual ~AudioMixerBase() {}

    virtual bool isValidFormat(audio_format_t format) const;
    virtual bool isValidChannelMask(audio_channel_mask_t channelMask) const;

    // Create a new track in the mixer.
    //
    // \param name        a unique user-provided integer associated with the track.
    //                    If name already exists, the function will abort.
    // \param channelMask output channel mask.
    // \param format      PCM format
    // \param sessionId   Session id for the track. Tracks with the same
    //                    session id will be submixed together.
    //
    // \return OK        on success.
    //         BAD_VALUE if the format does not satisfy isValidFormat()
    //                   or the channelMask does not satisfy isValidChannelMask().
    status_t    create(
            int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId);

    bool        exists(int name) const {
        return mTracks.count(name) > 0;
    }

    // Free an allocated track by name.
    void        destroy(int name);

    // Enable or disable an allocated track by name
    void        enable(int name);
    void        disable(int name);

    virtual void setParameter(int name, int target, int param, void *value);

    void        process() {
        preProcess();
        (this->*mHook)();
        postProcess();
    }

    size_t      getUnreleasedFrames(int name) const;

    std::string trackNames() const;

  protected:
    // Set kUseNewMixer to true to use the new mixer engine always. Otherwise the
    // original code will be used for stereo sinks, the new mixer for everything else.
    static constexpr bool kUseNewMixer = true;

    // Set kUseFloat to true to allow floating input into the mixer engine.
    // If kUseNewMixer is false, this is ignored or may be overridden internally
    static constexpr bool kUseFloat = true;

#ifdef FLOAT_AUX
    using TYPE_AUX = float;
    static_assert(kUseNewMixer && kUseFloat,
            "kUseNewMixer and kUseFloat must be true for FLOAT_AUX option");
#else
    using TYPE_AUX = int32_t; // q4.27
#endif

    /* 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,
    };

    enum {
        NEEDS_CHANNEL_1             = 0x00000000,   // mono
        NEEDS_CHANNEL_2             = 0x00000001,   // stereo

        // sample format is not explicitly specified, and is assumed to be AUDIO_FORMAT_PCM_16_BIT

        NEEDS_MUTE                  = 0x00000100,
        NEEDS_RESAMPLE              = 0x00001000,
        NEEDS_AUX                   = 0x00010000,
    };

    // hook types
    enum {
        PROCESSTYPE_NORESAMPLEONETRACK, // others set elsewhere
    };

    enum {
        TRACKTYPE_NOP,
        TRACKTYPE_RESAMPLE,
        TRACKTYPE_RESAMPLEMONO,
        TRACKTYPE_RESAMPLESTEREO,
        TRACKTYPE_NORESAMPLE,
        TRACKTYPE_NORESAMPLEMONO,
        TRACKTYPE_NORESAMPLESTEREO,
    };

    // process hook functionality
    using process_hook_t = void(AudioMixerBase::*)();

    static bool isAudioChannelPositionMask(audio_channel_mask_t channelMask) {
        return audio_channel_mask_get_representation(channelMask)
                == AUDIO_CHANNEL_REPRESENTATION_POSITION;
    }

    struct TrackBase;
    using hook_t = void(TrackBase::*)(
            int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);

    struct TrackBase {
        TrackBase()
            : bufferProvider(nullptr)
        {
            // TODO: move additional initialization here.
        }
        virtual ~TrackBase() {}

        virtual uint32_t getOutputChannelCount() { return channelCount; }
        virtual uint32_t getMixerChannelCount() { return mMixerChannelCount; }

        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        recreateResampler(uint32_t devSampleRate);
        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; };

        bool        useStereoVolume() const { return channelMask == AUDIO_CHANNEL_OUT_STEREO
                                        && isAudioChannelPositionMask(mMixerChannelMask); }

        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);

        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);

        uint32_t    needs;

        // TODO: Eventually remove legacy integer volume settings
        union {
        int16_t     volume[MAX_NUM_VOLUMES]; // U4.12 fixed point (top bit should be zero)
        int32_t     volumeRL;
        };

        int32_t     prevVolume[MAX_NUM_VOLUMES];
        int32_t     volumeInc[MAX_NUM_VOLUMES];
        int32_t     auxInc;
        int32_t     prevAuxLevel;
        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)
        uint8_t     unused_padding; // formerly format, was always 16
        uint16_t    enabled;        // actually bool
        audio_channel_mask_t channelMask;

        // actual buffer provider used by the track hooks
        AudioBufferProvider*                bufferProvider;

        mutable AudioBufferProvider::Buffer buffer; // 8 bytes

        hook_t      hook;
        const void  *mIn;             // current location in buffer

        std::unique_ptr<AudioResampler> mResampler;
        uint32_t    sampleRate;
        int32_t*    mainBuffer;
        int32_t*    auxBuffer;

        int32_t     sessionId;

        audio_format_t mMixerFormat;     // output mix format: AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
        audio_format_t mFormat;          // input track format
        audio_format_t mMixerInFormat;   // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
                                         // each track must be converted to this format.

        float          mVolume[MAX_NUM_VOLUMES];     // floating point set volume
        float          mPrevVolume[MAX_NUM_VOLUMES]; // floating point previous volume
        float          mVolumeInc[MAX_NUM_VOLUMES];  // floating point volume increment

        float          mAuxLevel;                     // floating point set aux level
        float          mPrevAuxLevel;                 // floating point prev aux level
        float          mAuxInc;                       // floating point aux increment

        audio_channel_mask_t mMixerChannelMask;
        uint32_t             mMixerChannelCount;

      protected:

        // 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);

        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);

        // 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);
    };

    // preCreateTrack must create an instance of a proper TrackBase descendant.
    // postCreateTrack is called after filling out fields of TrackBase. It can
    // abort track creation by returning non-OK status. See the implementation
    // of create() for details.
    virtual std::shared_ptr<TrackBase> preCreateTrack();
    virtual status_t postCreateTrack(TrackBase *track __unused) { return OK; }

    // preProcess is called before the process hook, postProcess after,
    // see the implementation of process() method.
    virtual void preProcess() {}
    virtual void postProcess() {}

    virtual bool setChannelMasks(int name,
            audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask);

    // Called when track info changes and a new process hook should be determined.
    void invalidate() {
        mHook = &AudioMixerBase::process__validate;
    }

    void process__validate();
    void process__nop();
    void process__genericNoResampling();
    void process__genericResampling();
    void process__oneTrack16BitsStereoNoResampling();

    template <int MIXTYPE, typename TO, typename TI, typename TA>
    void process__noResampleOneTrack();

    static process_hook_t getProcessHook(int processType, uint32_t channelCount,
            audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
            bool useStereoVolume);

    static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
            void *in, audio_format_t mixerInFormat, size_t sampleCount);

    // initialization constants
    const uint32_t mSampleRate;
    const size_t mFrameCount;

    process_hook_t mHook = &AudioMixerBase::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;

    // 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<TrackBase>> mTracks;
};

AudioMixerCuando se construye el objeto, es necesario pasarlo para realizar una operación de mezcla, el número de cuadro de salida deseado y la frecuencia de muestreo. Los valores específicos de estos parámetros se pueden obtener del dispositivo. Como en AudioFlinger::MixerThready FastMixer::onStateChange(), al crear AudioMixerun objeto, los parámetros como la frecuencia de muestreo se obtendrán del flujo de salida de audio abierto. AudioMixerLa frecuencia de muestreo de salida la establece el usuario a través de la interfaz proporcionada.

AudioMixerUtilice AudioMixer::Track/ AudioMixerBase::TrackBasepara representar los datos de la fuente de audio que se van a mezclar. AudioMixer::Track/ AudioMixerBase::TrackBaseMantener diversa información relacionada con los datos de la fuente de audio, incluidos los formatos y parámetros de los datos de audio de entrada, varios objetos utilizados en la conversión de formato necesarios antes de realizar operaciones de mezcla reales, etc.

Crear, agregar y eliminar fuentes de audio

AudioMixerAudioMixerBaseLa operación / create()se utiliza para crear y agregar una fuente de audio al mezclador. La definición de esta operación (ubicada en frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp ) es la siguiente:

bool AudioMixerBase::isValidFormat(audio_format_t format) const
{
    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;
    }
}

bool AudioMixerBase::isValidChannelMask(audio_channel_mask_t channelMask) const
{
    return audio_channel_count_from_out_mask(channelMask) <= MAX_NUM_CHANNELS;
}

std::shared_ptr<AudioMixerBase::TrackBase> AudioMixerBase::preCreateTrack()
{
    return std::make_shared<TrackBase>();
}

status_t AudioMixerBase::create(
        int name, audio_channel_mask_t channelMask, audio_format_t format, int sessionId)
{
    LOG_ALWAYS_FATAL_IF(exists(name), "name %d already exists", name);

    if (!isValidChannelMask(channelMask)) {
        ALOGE("%s invalid channelMask: %#x", __func__, channelMask);
        return BAD_VALUE;
    }
    if (!isValidFormat(format)) {
        ALOGE("%s invalid format: %#x", __func__, format);
        return BAD_VALUE;
    }

    auto t = preCreateTrack();
    {
        // TODO: move initialization to the Track constructor.
        // assume default parameters for the track, except where noted below
        t->needs = 0;

        // Integer volume.
        // Currently integer volume is kept for the legacy integer mixer.
        // Will be removed when the legacy mixer path is removed.
        t->volume[0] = 0;
        t->volume[1] = 0;
        t->prevVolume[0] = 0 << 16;
        t->prevVolume[1] = 0 << 16;
        t->volumeInc[0] = 0;
        t->volumeInc[1] = 0;
        t->auxLevel = 0;
        t->auxInc = 0;
        t->prevAuxLevel = 0;

        // Floating point volume.
        t->mVolume[0] = 0.f;
        t->mVolume[1] = 0.f;
        t->mPrevVolume[0] = 0.f;
        t->mPrevVolume[1] = 0.f;
        t->mVolumeInc[0] = 0.;
        t->mVolumeInc[1] = 0.;
        t->mAuxLevel = 0.;
        t->mAuxInc = 0.;
        t->mPrevAuxLevel = 0.;

        // no initialization needed
        // t->frameCount
        t->channelCount = audio_channel_count_from_out_mask(channelMask);
        t->enabled = false;
        ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO,
                "Non-stereo channel mask: %d\n", channelMask);
        t->channelMask = channelMask;
        t->sessionId = sessionId;
        // setBufferProvider(name, AudioBufferProvider *) is required before enable(name)
        t->bufferProvider = NULL;
        t->buffer.raw = NULL;
        // no initialization needed
        // t->buffer.frameCount
        t->hook = NULL;
        t->mIn = NULL;
        t->sampleRate = mSampleRate;
        // setParameter(name, TRACK, MAIN_BUFFER, mixBuffer) is required before enable(name)
        t->mainBuffer = NULL;
        t->auxBuffer = NULL;
        t->mMixerFormat = AUDIO_FORMAT_PCM_16_BIT;
        t->mFormat = format;
        t->mMixerInFormat = kUseFloat && kUseNewMixer ?
                AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT;
        t->mMixerChannelMask = audio_channel_mask_from_representation_and_bits(
                AUDIO_CHANNEL_REPRESENTATION_POSITION, AUDIO_CHANNEL_OUT_STEREO);
        t->mMixerChannelCount = audio_channel_count_from_out_mask(t->mMixerChannelMask);
        status_t status = postCreateTrack(t.get());
        if (status != OK) return status;
        mTracks[name] = t;
        return OK;
    }
}

Al llamar create()a la operación, debe pasar el identificador de la fuente de audio name, la máscara de canal (número de canales) channelMask, el formato de muestreo formaty el ID de sesión de los datos de entrada de la fuente de audio. El proceso de ejecución de esta operación es el siguiente:

  1. nameVerifique si la fuente de audio correspondiente ya existe de acuerdo con , si ya existe, falle y salga; de lo contrario, continúe la ejecución;
  2. Verifique si la máscara de canal (número de canales) de la fuente de audio channelMaskes válida, principalmente verificando si el número de canales excede el límite de 12. Si no es válido, se devolverá un código de error; de lo contrario, la ejecución continuará;
  3. Compruebe si el formato de muestreo formates válido; de lo contrario, devuelva un código de error; de lo contrario, continúe con la ejecución;
  4. Llame preCreateTrack()para crear un objeto Track;
  5. Inicialice el objeto Track, como inicializar la máscara de canal, el número de canal, el formato de muestreo y el ID de sesión de los datos de entrada de la fuente de audio como parámetros entrantes, inicializar la frecuencia de muestreo de la fuente de audio de un solo canal como la frecuencia de muestreo de salida, inicializar el formato de muestreo del mezclador AudioMixer, formato de muestreo de entrada del mezclador, máscara de canal del mezclador, número de canal del mezclador, etc. El formato de muestreo del mezclador solo puede ser AUDIO_FORMAT_PCM_FLOATo AUDIO_FORMAT_PCM_16_BIT;
  6. Llame postCreateTrack()para inicializar aún más el objeto Track;
  7. Guarde el objeto Track creado e inicializado en un mapa.

Desde AudioMixerla perspectiva de , create()no es muy razonable que el usuario pase el identificador de fuente de audio para la operación. Esto destruye AudioMixerla cohesión de . Es mejor AudioMixergenerar este identificador internamente y devolvérselo al usuario.

AudioMixerpreCreateTrack()La clase anula las funciones y vistas arriba postCreateTrack(). Las definiciones de estas dos funciones en AudioMixerla clase (ubicadas en frameworks/av/media/libaudioprocessing/AudioMixer.cpp ) son las siguientes:

std::shared_ptr<AudioMixerBase::TrackBase> AudioMixer::preCreateTrack()
{
    return std::make_shared<Track>();
}

status_t AudioMixer::postCreateTrack(TrackBase *track)
{
    Track* t = static_cast<Track*>(track);

    audio_channel_mask_t channelMask = t->channelMask;
    t->mHapticChannelMask = static_cast<audio_channel_mask_t>(
            channelMask & AUDIO_CHANNEL_HAPTIC_ALL);
    t->mHapticChannelCount = audio_channel_count_from_out_mask(t->mHapticChannelMask);
    channelMask = static_cast<audio_channel_mask_t>(channelMask & ~AUDIO_CHANNEL_HAPTIC_ALL);
    t->channelCount = audio_channel_count_from_out_mask(channelMask);
    ALOGV_IF(audio_channel_mask_get_bits(channelMask) != AUDIO_CHANNEL_OUT_STEREO,
            "Non-stereo channel mask: %d\n", channelMask);
    t->channelMask = channelMask;
    t->mInputBufferProvider = NULL;
    t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required
    t->mPlaybackRate = AUDIO_PLAYBACK_RATE_DEFAULT;
    // haptic
    t->mHapticPlaybackEnabled = false;
    t->mHapticIntensity = os::HapticScale::NONE;
    t->mHapticMaxAmplitude = NAN;
    t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
    t->mMixerHapticChannelCount = 0;
    t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
    t->mAdjustOutChannelCount = t->channelCount;
    t->mKeepContractedChannels = false;
    // Check the downmixing (or upmixing) requirements.
    status_t status = t->prepareForDownmix();
    if (status != OK) {
        ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask);
        return BAD_VALUE;
    }
    // prepareForDownmix() may change mDownmixRequiresFormat
    ALOGVV("mMixerFormat:%#x  mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat);
    t->prepareForReformat();
    t->prepareForAdjustChannels(mFrameCount);
    return OK;
}

AudioMixer::preCreateTrack()Simplemente crea el objeto. AudioMixer::postCreateTrack()Se inicializarán AudioMixer::Trackalgunos campos únicos , la máscara del canal se corregirá para incluir solo la máscara del canal de datos de audio y no el canal háptico, y se creará una canalización de procesamiento de datos para la fuente de audio mixta.

AudioMixerAudioMixerBaseLa operación / destroy()se utiliza para eliminar una fuente de audio del mezclador. La definición de esta operación (ubicada en frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp ) es la siguiente:

void AudioMixerBase::destroy(int name)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    ALOGV("deleteTrackName(%d)", name);

    if (mTracks[name]->enabled) {
        invalidate();
    }
    mTracks.erase(name); // deallocate track
}

AudioMixerBase::destroy(int name)Se ejecuta cuando la fuente de audio a eliminar está habilitada invalidate(), solicitando una reinicialización del procesamiento de la mezcla. Después de eso, simplemente elimine la fuente de audio que desea eliminar.

Canal de procesamiento de datos de fuentes de audio

Cada fuente de audio del mezclador AudioMixer, es decir, Track, mantiene un proceso de procesamiento de datos de audio para convertir los datos de la fuente de audio de entrada al AudioMixerformato requerido y a los datos de configuración que se pueden usar para realizar operaciones de mezcla. Los nodos de la canalización de procesamiento de datos de audio de Track están representados por AudioBufferProvider/ PassthruBufferProviderArriba vemos AudioMixer::Trackque la definición de contiene múltiples variables miembro de este tipo de objeto:

        AudioBufferProvider* mInputBufferProvider;    // externally provided buffer provider.
        std::unique_ptr<PassthruBufferProvider> mAdjustChannelsBufferProvider;
        std::unique_ptr<PassthruBufferProvider> mReformatBufferProvider;
        std::unique_ptr<PassthruBufferProvider> mDownmixerBufferProvider;
        std::unique_ptr<PassthruBufferProvider> mPostDownmixReformatBufferProvider;
        std::unique_ptr<PassthruBufferProvider> mTimestretchBufferProvider;

La jerarquía de herencia de AudioBufferProviderclases relacionadas en libaudioprocessing es la siguiente:PassthruBufferProvider

AudioBufferProvider/PassthruBufferProvider

AudioBufferProviderEs una clase de interfaz y todas las funciones de interfaz que contiene se muestran en la figura anterior. En frameworks/av/media/libaudioprocessing/include/media/BufferProviders.h , CopyBufferProviderlas PassthruBufferProviderclases y se definen de la siguiente manera:

class PassthruBufferProvider : public AudioBufferProvider {
public:
    PassthruBufferProvider() : mTrackBufferProvider(NULL) { }

    virtual ~PassthruBufferProvider() { }

    // call this to release the buffer to the upstream provider.
    // treat it as an audio discontinuity for future samples.
    virtual void reset() { }

    // set the upstream buffer provider. Consider calling "reset" before this function.
    virtual void setBufferProvider(AudioBufferProvider *p) {
        mTrackBufferProvider = p;
    }

protected:
    AudioBufferProvider *mTrackBufferProvider;
};

// Base AudioBufferProvider class used for DownMixerBufferProvider, RemixBufferProvider,
// and ReformatBufferProvider.
// It handles a private buffer for use in converting format or channel masks from the
// input data to a form acceptable by the mixer.
// TODO: Make a ResamplerBufferProvider when integers are entirely removed from the
// processing pipeline.
class CopyBufferProvider : public PassthruBufferProvider {
public:
    // Use a private buffer of bufferFrameCount frames (each frame is outputFrameSize bytes).
    // If bufferFrameCount is 0, no private buffer is created and in-place modification of
    // the upstream buffer provider's buffers is performed by copyFrames().
    CopyBufferProvider(size_t inputFrameSize, size_t outputFrameSize,
            size_t bufferFrameCount);
    virtual ~CopyBufferProvider();

    // Overrides AudioBufferProvider methods
    virtual status_t getNextBuffer(Buffer *buffer);
    virtual void releaseBuffer(Buffer *buffer);

    // Overrides PassthruBufferProvider
    virtual void reset();
    void setBufferProvider(AudioBufferProvider *p) override;

    // this function should be supplied by the derived class.  It converts
    // #frames in the *src pointer to the *dst pointer.  It is public because
    // some providers will allow this to work on arbitrary buffers outside
    // of the internal buffers.
    virtual void copyFrames(void *dst, const void *src, size_t frames) = 0;

protected:
    const size_t         mInputFrameSize;
    const size_t         mOutputFrameSize;
private:
    AudioBufferProvider::Buffer mBuffer;
    const size_t         mLocalBufferFrameCount;
    void                *mLocalBufferData;
    size_t               mConsumed;
};

PassthruBufferProviderLa clase agrega setBufferProvider()funciones para facilitar la conexión de diferentes nodos de canalización de procesamiento de datos de audio, y CopyBufferProviderla clase agrega copyFrames()funciones para facilitar la transferencia de datos entre diferentes nodos de canalización de procesamiento de datos de audio. Las funciones implementadas por cada nodo de canalización de procesamiento de datos de audio específico copyFrames()implementan operaciones de conversión de formato o procesamiento de datos correspondientes.

CopyBufferProviderCuando se construye un objeto de clase, se asigna un búfer de memoria según sea necesario. Cuando se destruye el objeto, se libera el búfer usado. La definición lógica relevante (ubicada en frameworks/av/media/libaudioprocessing/BufferProviders.cpp ) es la siguiente:

CopyBufferProvider::CopyBufferProvider(size_t inputFrameSize,
        size_t outputFrameSize, size_t bufferFrameCount) :
        mInputFrameSize(inputFrameSize),
        mOutputFrameSize(outputFrameSize),
        mLocalBufferFrameCount(bufferFrameCount),
        mLocalBufferData(NULL),
        mConsumed(0)
{
    ALOGV("CopyBufferProvider(%p)(%zu, %zu, %zu)", this,
            inputFrameSize, outputFrameSize, bufferFrameCount);
    LOG_ALWAYS_FATAL_IF(inputFrameSize < outputFrameSize && bufferFrameCount == 0,
            "Requires local buffer if inputFrameSize(%zu) < outputFrameSize(%zu)",
            inputFrameSize, outputFrameSize);
    if (mLocalBufferFrameCount) {
        (void)posix_memalign(&mLocalBufferData, 32, mLocalBufferFrameCount * mOutputFrameSize);
    }
    mBuffer.frameCount = 0;
}

CopyBufferProvider::~CopyBufferProvider()
{
    ALOGV("%s(%p) %zu %p %p",
           __func__, this, mBuffer.frameCount, mTrackBufferProvider, mLocalBufferData);
    if (mBuffer.frameCount != 0) {
        mTrackBufferProvider->releaseBuffer(&mBuffer);
    }
    free(mLocalBufferData);
}

Para evitar un consumo innecesario de memoria, CopyBufferProviderno es necesaria la asignación de memoria durante la construcción del objeto de clase, pero cuando el tamaño del cuadro de audio de entrada es menor que el tamaño del cuadro de audio de salida, se debe asignar un búfer. CopyBufferProviderEl constructor de la clase determina el tamaño del búfer asignado en función del número de fotogramas del búfer local pasados ​​y el tamaño del fotograma de audio de salida.

La canalización de procesamiento de datos de audio de la pista está impulsada por la función de su último nodo getNextBuffer(). En CopyBufferProviderla clase, la definición de esta función (ubicada en frameworks/av/media/libaudioprocessing/BufferProviders.cpp ) es la siguiente:

status_t CopyBufferProvider::getNextBuffer(AudioBufferProvider::Buffer *pBuffer)
{
    //ALOGV("CopyBufferProvider(%p)::getNextBuffer(%p (%zu))",
    //        this, pBuffer, pBuffer->frameCount);
    if (mLocalBufferFrameCount == 0) {
        status_t res = mTrackBufferProvider->getNextBuffer(pBuffer);
        if (res == OK) {
            copyFrames(pBuffer->raw, pBuffer->raw, pBuffer->frameCount);
        }
        return res;
    }
    if (mBuffer.frameCount == 0) {
        mBuffer.frameCount = pBuffer->frameCount;
        status_t res = mTrackBufferProvider->getNextBuffer(&mBuffer);
        // At one time an upstream buffer provider had
        // res == OK and mBuffer.frameCount == 0, doesn't seem to happen now 7/18/2014.
        //
        // By API spec, if res != OK, then mBuffer.frameCount == 0.
        // but there may be improper implementations.
        ALOG_ASSERT(res == OK || mBuffer.frameCount == 0);
        if (res != OK || mBuffer.frameCount == 0) { // not needed by API spec, but to be safe.
            pBuffer->raw = NULL;
            pBuffer->frameCount = 0;
            return res;
        }
        mConsumed = 0;
    }
    ALOG_ASSERT(mConsumed < mBuffer.frameCount);
    size_t count = std::min(mLocalBufferFrameCount, mBuffer.frameCount - mConsumed);
    count = std::min(count, pBuffer->frameCount);
    pBuffer->raw = mLocalBufferData;
    pBuffer->frameCount = count;
    copyFrames(pBuffer->raw, (uint8_t*)mBuffer.raw + mConsumed * mInputFrameSize,
            pBuffer->frameCount);
    return OK;
}

Si el número de fotograma del búfer local entrante es 0 cuando se construye el objeto, esto generalmente significa que el búfer no se asignará localmente y el tamaño del búfer requerido por el nodo actual del proceso de procesamiento de datos de audio de Track no será mayor que el del nodo anterior.Si la necesidad es grande, obtenga los datos procesados ​​del nodo anterior, luego llame a copyFrames()la función del nodo actual para realizar el procesamiento del nodo actual y devuélvalo a la persona que llama. Para esta situación, se requiere una subclase de implementación específica para garantizar que su procesamiento de datos de audio no necesite cambiar la frecuencia de muestreo de los datos de audio procesados ​​por el nodo anterior.

Si el número de marcos de búfer local pasados ​​cuando se construye el objeto no es 0, se utiliza un búfer mBufferpara guardar los datos obtenidos del nodo anterior. En la función CopyBufferProviderde clase getNextBuffer(), si mBufferel número de fotogramas es 0, significa que los datos obtenidos previamente del nodo anterior se han consumido y se vuelve a obtener un dato del nodo anterior. Luego llame a la función del nodo actual copyFrames()para procesar los datos obtenidos del nodo anterior y devolverlos a la persona que llama. Aquí comparamos directamente el número de fotogramas del nodo actual mLocalBufferFrameCountcon el número de fotogramas de los datos del nodo anterior mBuffer.frameCount - mConsumed, pero los dos no deben compararse directamente. Por ejemplo, según la configuración, el nodo actual necesita generar datos de audio. con una frecuencia de muestreo de 48 kHz y el número de cuadros de salida es de 10 ms de datos, es decir, 480 cuadros. Si el nodo anterior genera datos con una frecuencia de muestreo de 16 kHz de acuerdo con la configuración, entonces el nodo actual solo necesita 160 cuadros de datos del nodo anterior para generar datos una vez. En otras palabras, también implica que el procesamiento de datos del nodo actual no cambia la frecuencia de muestreo.

Los datos de audio obtenidos de CopyBufferProviderla getNextBuffer()función de clase deben liberarse después de su uso. CopyBufferProvider::releaseBuffer()La función se define de la siguiente manera:

void CopyBufferProvider::releaseBuffer(AudioBufferProvider::Buffer *pBuffer)
{
    //ALOGV("CopyBufferProvider(%p)::releaseBuffer(%p(%zu))",
    //        this, pBuffer, pBuffer->frameCount);
    if (mLocalBufferFrameCount == 0) {
        mTrackBufferProvider->releaseBuffer(pBuffer);
        return;
    }
    // LOG_ALWAYS_FATAL_IF(pBuffer->frameCount == 0, "Invalid framecount");
    mConsumed += pBuffer->frameCount; // TODO: update for efficiency to reuse existing content
    if (mConsumed != 0 && mConsumed >= mBuffer.frameCount) {
        mTrackBufferProvider->releaseBuffer(&mBuffer);
        ALOG_ASSERT(mBuffer.frameCount == 0);
    }
    pBuffer->raw = NULL;
    pBuffer->frameCount = 0;
}

El buffer se libera dondequiera que se obtenga. mConsumedIndica el número de cuadro de los datos de audio del nodo anterior e pBuffer->frameCountindica el número de cuadro de los datos de audio del nodo actual. Si las velocidades de muestreo de los dos nodos son diferentes, los dos números de cuadro tienen significados diferentes y, por lo tanto, no se pueden añadido directamente. También está implícito aquí que el procesamiento de datos del nodo actual no cambia la frecuencia de muestreo.

CopyBufferProviderreset()Las operaciones y de la clase setBufferProvider()se definen de la siguiente manera:

void CopyBufferProvider::reset()
{
    if (mBuffer.frameCount != 0) {
        mTrackBufferProvider->releaseBuffer(&mBuffer);
    }
    mConsumed = 0;
}

void CopyBufferProvider::setBufferProvider(AudioBufferProvider *p) {
    ALOGV("%s(%p): mTrackBufferProvider:%p  mBuffer.frameCount:%zu",
            __func__, p, mTrackBufferProvider, mBuffer.frameCount);
    if (mTrackBufferProvider == p) {
        return;
    }
    mBuffer.frameCount = 0;
    PassthruBufferProvider::setBufferProvider(p);
}

Las diferentes PassthruBufferProvidersubclases CopyBufferProviderque vimos anteriormente proporcionan diferentes operaciones de procesamiento. DownmixerBufferProviderSe utiliza para convertir el formato de máscara de canal/número de canal de datos de audio. La frecuencia de muestreo y el formato de muestreo de los datos de audio permanecen sin cambios. Se implementa en base al efecto de sonido HAL. DownmixerBufferProviderLa implementación de cada función miembro de la clase es la siguiente:

DownmixerBufferProvider::DownmixerBufferProvider(
        audio_channel_mask_t inputChannelMask,
        audio_channel_mask_t outputChannelMask, audio_format_t format,
        uint32_t sampleRate, int32_t sessionId, size_t bufferFrameCount) :
        CopyBufferProvider(
            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask),
            audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask),
            bufferFrameCount)  // set bufferFrameCount to 0 to do in-place
{
    ALOGV("DownmixerBufferProvider(%p)(%#x, %#x, %#x %u %d %d)",
            this, inputChannelMask, outputChannelMask, format,
            sampleRate, sessionId, (int)bufferFrameCount);
    if (!sIsMultichannelCapable) {
        ALOGE("DownmixerBufferProvider() error: not multichannel capable");
        return;
    }
    mEffectsFactory = EffectsFactoryHalInterface::create();
    if (mEffectsFactory == 0) {
        ALOGE("DownmixerBufferProvider() error: could not obtain the effects factory");
        return;
    }
    if (mEffectsFactory->createEffect(&sDwnmFxDesc.uuid,
                                      sessionId,
                                      SESSION_ID_INVALID_AND_IGNORED,
                                      AUDIO_PORT_HANDLE_NONE,
                                      &mDownmixInterface) != 0) {
         ALOGE("DownmixerBufferProvider() error creating downmixer effect");
         mDownmixInterface.clear();
         mEffectsFactory.clear();
         return;
     }
     // channel input configuration will be overridden per-track
     mDownmixConfig.inputCfg.channels = inputChannelMask;   // FIXME: Should be bits
     mDownmixConfig.outputCfg.channels = outputChannelMask; // FIXME: should be bits
     mDownmixConfig.inputCfg.format = format;
     mDownmixConfig.outputCfg.format = format;
     mDownmixConfig.inputCfg.samplingRate = sampleRate;
     mDownmixConfig.outputCfg.samplingRate = sampleRate;
     mDownmixConfig.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
     mDownmixConfig.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
     // input and output buffer provider, and frame count will not be used as the downmix effect
     // process() function is called directly (see DownmixerBufferProvider::getNextBuffer())
     mDownmixConfig.inputCfg.mask = EFFECT_CONFIG_SMP_RATE | EFFECT_CONFIG_CHANNELS |
             EFFECT_CONFIG_FORMAT | EFFECT_CONFIG_ACC_MODE;
     mDownmixConfig.outputCfg.mask = mDownmixConfig.inputCfg.mask;

     mInFrameSize =
             audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(inputChannelMask);
     mOutFrameSize =
             audio_bytes_per_sample(format) * audio_channel_count_from_out_mask(outputChannelMask);
     status_t status;
     status = mEffectsFactory->mirrorBuffer(
             nullptr, mInFrameSize * bufferFrameCount, &mInBuffer);
     if (status != 0) {
         ALOGE("DownmixerBufferProvider() error %d while creating input buffer", status);
         mDownmixInterface.clear();
         mEffectsFactory.clear();
         return;
     }
     status = mEffectsFactory->mirrorBuffer(
             nullptr, mOutFrameSize * bufferFrameCount, &mOutBuffer);
     if (status != 0) {
         ALOGE("DownmixerBufferProvider() error %d while creating output buffer", status);
         mInBuffer.clear();
         mDownmixInterface.clear();
         mEffectsFactory.clear();
         return;
     }
     mDownmixInterface->setInBuffer(mInBuffer);
     mDownmixInterface->setOutBuffer(mOutBuffer);

     int cmdStatus;
     uint32_t replySize = sizeof(int);

     // Configure downmixer
     status = mDownmixInterface->command(
             EFFECT_CMD_SET_CONFIG /*cmdCode*/, sizeof(effect_config_t) /*cmdSize*/,
             &mDownmixConfig /*pCmdData*/,
             &replySize, &cmdStatus /*pReplyData*/);
     if (status != 0 || cmdStatus != 0) {
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while configuring downmixer",
                 status, cmdStatus);
         mOutBuffer.clear();
         mInBuffer.clear();
         mDownmixInterface.clear();
         mEffectsFactory.clear();
         return;
     }

     // Enable downmixer
     replySize = sizeof(int);
     status = mDownmixInterface->command(
             EFFECT_CMD_ENABLE /*cmdCode*/, 0 /*cmdSize*/, NULL /*pCmdData*/,
             &replySize, &cmdStatus /*pReplyData*/);
     if (status != 0 || cmdStatus != 0) {
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while enabling downmixer",
                 status, cmdStatus);
         mOutBuffer.clear();
         mInBuffer.clear();
         mDownmixInterface.clear();
         mEffectsFactory.clear();
         return;
     }

     // Set downmix type
     // parameter size rounded for padding on 32bit boundary
     const int psizePadded = ((sizeof(downmix_params_t) - 1)/sizeof(int) + 1) * sizeof(int);
     const int downmixParamSize =
             sizeof(effect_param_t) + psizePadded + sizeof(downmix_type_t);
     effect_param_t * const param = (effect_param_t *) malloc(downmixParamSize);
     param->psize = sizeof(downmix_params_t);
     const downmix_params_t downmixParam = DOWNMIX_PARAM_TYPE;
     memcpy(param->data, &downmixParam, param->psize);
     const downmix_type_t downmixType = DOWNMIX_TYPE_FOLD;
     param->vsize = sizeof(downmix_type_t);
     memcpy(param->data + psizePadded, &downmixType, param->vsize);
     replySize = sizeof(int);
     status = mDownmixInterface->command(
             EFFECT_CMD_SET_PARAM /* cmdCode */, downmixParamSize /* cmdSize */,
             param /*pCmdData*/, &replySize, &cmdStatus /*pReplyData*/);
     free(param);
     if (status != 0 || cmdStatus != 0) {
         ALOGE("DownmixerBufferProvider() error %d cmdStatus %d while setting downmix type",
                 status, cmdStatus);
         mOutBuffer.clear();
         mInBuffer.clear();
         mDownmixInterface.clear();
         mEffectsFactory.clear();
         return;
     }
     ALOGV("DownmixerBufferProvider() downmix type set to %d", (int) downmixType);
}

DownmixerBufferProvider::~DownmixerBufferProvider()
{
    ALOGV("~DownmixerBufferProvider (%p)", this);
    if (mDownmixInterface != 0) {
        mDownmixInterface->close();
    }
}

void DownmixerBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
{
    mInBuffer->setExternalData(const_cast<void*>(src));
    mInBuffer->setFrameCount(frames);
    mInBuffer->update(mInFrameSize * frames);
    mOutBuffer->setFrameCount(frames);
    mOutBuffer->setExternalData(dst);
    if (dst != src) {
        // Downmix may be accumulating, need to populate the output buffer
        // with the dst data.
        mOutBuffer->update(mOutFrameSize * frames);
    }
    // may be in-place if src == dst.
    status_t res = mDownmixInterface->process();
    if (res == OK) {
        mOutBuffer->commit(mOutFrameSize * frames);
    } else {
        ALOGE("DownmixBufferProvider error %d", res);
    }
}

/* call once in a pthread_once handler. */
/*static*/ status_t DownmixerBufferProvider::init()
{
    // find multichannel downmix effect if we have to play multichannel content
    sp<EffectsFactoryHalInterface> effectsFactory = EffectsFactoryHalInterface::create();
    if (effectsFactory == 0) {
        ALOGE("AudioMixer() error: could not obtain the effects factory");
        return NO_INIT;
    }
    uint32_t numEffects = 0;
    int ret = effectsFactory->queryNumberEffects(&numEffects);
    if (ret != 0) {
        ALOGE("AudioMixer() error %d querying number of effects", ret);
        return NO_INIT;
    }
    ALOGV("EffectQueryNumberEffects() numEffects=%d", numEffects);

    for (uint32_t i = 0 ; i < numEffects ; i++) {
        if (effectsFactory->getDescriptor(i, &sDwnmFxDesc) == 0) {
            ALOGV("effect %d is called %s", i, sDwnmFxDesc.name);
            if (memcmp(&sDwnmFxDesc.type, EFFECT_UIID_DOWNMIX, sizeof(effect_uuid_t)) == 0) {
                ALOGI("found effect \"%s\" from %s",
                        sDwnmFxDesc.name, sDwnmFxDesc.implementor);
                sIsMultichannelCapable = true;
                break;
            }
        }
    }
    ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
    return NO_INIT;
}

/*static*/ bool DownmixerBufferProvider::sIsMultichannelCapable = false;
/*static*/ effect_descriptor_t DownmixerBufferProvider::sDwnmFxDesc;

DownmixerBufferProviderEsta clase encapsula la interfaz de audio HAL e implementa la conversión del número de canales de datos de audio. AudioMixerCuando se crea el primer objeto, DownmixerBufferProviderse realiza la inicialización a nivel de clase, principalmente para verificar si el efecto de sonido HAL admite la conversión de formato de máscara de canal/número de canal para datos de audio. Cuando se crea el objeto de clase, prepare los objetos de interfaz como y etc. DownmixerBufferProvidernecesarios para realizar la conversión , así como el búfer de entrada y salida HAL del efecto de sonido, y envíe comandos al efecto de sonido HAL para configurarlo y habilitarlo. Clase Transforma un dato. La estrategia específica para convertir datos de audio en formato de máscara de canal/número de canal depende de la implementación específica del efecto de sonido HAL.sp<EffectsFactoryHalInterface>sp<EffectHalInterface>DownmixerBufferProvidercopyFrames()

RemixBufferProviderSe utiliza para convertir el formato de máscara de canal/número de canal de datos de audio. La frecuencia de muestreo y el formato de muestreo de los datos de audio permanecen sin cambios. La RemixBufferProviderimplementación de cada función miembro de la clase es la siguiente:

RemixBufferProvider::RemixBufferProvider(audio_channel_mask_t inputChannelMask,
        audio_channel_mask_t outputChannelMask, audio_format_t format,
        size_t bufferFrameCount) :
        CopyBufferProvider(
                audio_bytes_per_sample(format)
                    * audio_channel_count_from_out_mask(inputChannelMask),
                audio_bytes_per_sample(format)
                    * audio_channel_count_from_out_mask(outputChannelMask),
                bufferFrameCount),
        mFormat(format),
        mSampleSize(audio_bytes_per_sample(format)),
        mInputChannels(audio_channel_count_from_out_mask(inputChannelMask)),
        mOutputChannels(audio_channel_count_from_out_mask(outputChannelMask))
{
    ALOGV("RemixBufferProvider(%p)(%#x, %#x, %#x) %zu %zu",
            this, format, inputChannelMask, outputChannelMask,
            mInputChannels, mOutputChannels);
    (void) memcpy_by_index_array_initialization_from_channel_mask(
            mIdxAry, ARRAY_SIZE(mIdxAry), outputChannelMask, inputChannelMask);
}

void RemixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
{
    memcpy_by_index_array(dst, mOutputChannels,
            src, mInputChannels, mIdxAry, mSampleSize, frames);
}

RemixBufferProviderLa forma de convertir el formato de máscara de canal/número de canal de datos de audio es encontrar primero el índice en cada cuadro de datos de entrada correspondiente a cada canal de datos de salida de audio de acuerdo con los formatos de máscara de canal de audio de entrada y salida, y luego en función de la matriz de índice obtenida, copia los datos de audio de entrada en el búfer de datos de salida cuadro por cuadro. memcpy_by_index_array_initialization_from_channel_mask()La función utilizada para obtener la matriz de índice se define en el archivo system/media/audio_utils/format.c :

size_t memcpy_by_index_array_initialization_from_channel_mask(int8_t *idxary, size_t arysize,
        audio_channel_mask_t dst_channel_mask, audio_channel_mask_t src_channel_mask)
{
    const audio_channel_representation_t src_representation =
            audio_channel_mask_get_representation(src_channel_mask);
    const audio_channel_representation_t dst_representation =
            audio_channel_mask_get_representation(dst_channel_mask);
    const uint32_t src_bits = audio_channel_mask_get_bits(src_channel_mask);
    const uint32_t dst_bits = audio_channel_mask_get_bits(dst_channel_mask);

    switch (src_representation) {
    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
        switch (dst_representation) {
        case AUDIO_CHANNEL_REPRESENTATION_POSITION:
            return memcpy_by_index_array_initialization(idxary, arysize,
                    dst_bits, src_bits);
        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
            return memcpy_by_index_array_initialization_dst_index(idxary, arysize,
                    dst_bits, src_bits);
        default:
            return 0;
        }
        break;
    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
        switch (dst_representation) {
        case AUDIO_CHANNEL_REPRESENTATION_POSITION:
            return memcpy_by_index_array_initialization_src_index(idxary, arysize,
                    dst_bits, src_bits);
        case AUDIO_CHANNEL_REPRESENTATION_INDEX:
            return memcpy_by_index_array_initialization(idxary, arysize,
                    dst_bits, src_bits);
        default:
            return 0;
        }
        break;
    default:
        return 0;
    }
}

memcpy_by_index_array_initialization_from_channel_mask()La función crea matrices de índice a través de diferentes funciones según el formato de máscara de canal de los datos de entrada y salida. audio_channel_mask_tHay dos tipos de máscaras de canales de audio representadas. Para POSITIONel tipo de máscara de canal de audio, contiene el número de canales; para INDEXel tipo de máscara de canal de audio, a cada canal de audio se le asigna un bit binario. Cuando los datos de audio contienen el canal correspondiente Cuando, la posición correspondiente se establece en 1; de lo contrario, se establece en 0. La máscara del canal de audio se obtiene mediante la operación bit a bit de cada canal de audio, audio_channel_mask_tpuede representar hasta 30 canales y los bits 30 y 31 se utilizan para representar el tipo. . Esto se puede ver a través de la función en system/media/audio/include/system/audio.h que obtiene el número de canales según la máscara del canal de audio:audio_channel_count_from_out_mask()

static inline uint32_t audio_channel_count_from_out_mask(audio_channel_mask_t channel)
{
    uint32_t bits = audio_channel_mask_get_bits(channel);
    switch (audio_channel_mask_get_representation(channel)) {
    case AUDIO_CHANNEL_REPRESENTATION_POSITION:
        // TODO: We can now merge with from_in_mask and remove anding
        bits &= AUDIO_CHANNEL_OUT_ALL;
        FALLTHROUGH_INTENDED;
    case AUDIO_CHANNEL_REPRESENTATION_INDEX:
        return __builtin_popcount(bits);
    default:
        return 0;
    }
}

memcpy_by_index_array_initialization_from_channel_mask()Varias funciones llamadas por la función se definen en el archivo system/media/audio_utils/primitives.c :

size_t memcpy_by_index_array_initialization(int8_t *idxary, size_t idxcount,
        uint32_t dst_mask, uint32_t src_mask)
{
    size_t n = 0;
    int srcidx = 0;
    uint32_t bit, ormask = src_mask | dst_mask;

    while (ormask && n < idxcount) {
        bit = ormask & -ormask;          /* get lowest bit */
        ormask ^= bit;                   /* remove lowest bit */
        if (src_mask & dst_mask & bit) { /* matching channel */
            idxary[n++] = srcidx++;
        } else if (src_mask & bit) {     /* source channel only */
            ++srcidx;
        } else {                         /* destination channel only */
            idxary[n++] = -1;
        }
    }
    return n + __builtin_popcount(ormask & dst_mask);
}

size_t memcpy_by_index_array_initialization_src_index(int8_t *idxary, size_t idxcount,
        uint32_t dst_mask, uint32_t src_mask) {
    size_t dst_count = __builtin_popcount(dst_mask);
    if (idxcount == 0) {
        return dst_count;
    }
    if (dst_count > idxcount) {
        dst_count = idxcount;
    }

    size_t src_idx, dst_idx;
    for (src_idx = 0, dst_idx = 0; dst_idx < dst_count; ++dst_idx) {
        if (src_mask & 1) {
            idxary[dst_idx] = src_idx++;
        } else {
            idxary[dst_idx] = -1;
        }
        src_mask >>= 1;
    }
    return dst_idx;
}

size_t memcpy_by_index_array_initialization_dst_index(int8_t *idxary, size_t idxcount,
        uint32_t dst_mask, uint32_t src_mask) {
    size_t src_idx, dst_idx;
    size_t dst_count = __builtin_popcount(dst_mask);
    size_t src_count = __builtin_popcount(src_mask);
    if (idxcount == 0) {
        return dst_count;
    }
    if (dst_count > idxcount) {
        dst_count = idxcount;
    }
    for (src_idx = 0, dst_idx = 0; dst_idx < dst_count; ++src_idx) {
        if (dst_mask & 1) {
            idxary[dst_idx++] = src_idx < src_count ? (signed)src_idx : -1;
        }
        dst_mask >>= 1;
    }
    return dst_idx;
}

memcpy_by_index_array_initialization_src_index()Sospecho firmemente que las funciones y implementadas aquí memcpy_by_index_array_initialization_dst_index()son incorrectas. Para memcpy_by_index_array_initialization_src_index()la función, su dst_maskparámetro es el número de canales, por lo que no es necesario llamar __builtin_popcount()para calcular el número de canales de salida. Para memcpy_by_index_array_initialization_dst_index()la función, no es necesario calcular el número de canales de entrada, pero sí el número de canales de salida. Además, memcpy_by_index_array_initialization_from_channel_mask()en la función, POSITIONcuando la máscara del canal de audio de entrada y la máscara del canal de audio de salida son ambas del mismo tipo, memcpy_by_index_array_initialization()no es correcto llamar a la función para crear una matriz de índice.

La definición de función para copiar datos según la matriz de índice memcpy_by_index_array()(ubicada en system/media/audio_utils/primitives.c ) es la siguiente:

/*
 * C macro to do copying by index array, to rearrange samples
 * within a frame.  This is independent of src/dst sample type.
 * Don't pass in any expressions for the macro arguments here.
 */
#define copy_frame_by_idx(dst, dst_channels, src, src_channels, idxary, count, zero) \
{ \
    unsigned i; \
    int index; \
    for (; (count) > 0; --(count)) { \
        for (i = 0; i < (dst_channels); ++i) { \
            index = (idxary)[i]; \
            *(dst)++ = index < 0 ? (zero) : (src)[index]; \
        } \
        (src) += (src_channels); \
    } \
}

void memcpy_by_index_array(void *dst, uint32_t dst_channels,
        const void *src, uint32_t src_channels,
        const int8_t *idxary, size_t sample_size, size_t count)
{
    switch (sample_size) {
    case 1: {
        uint8_t *udst = (uint8_t*)dst;
        const uint8_t *usrc = (const uint8_t*)src;

        copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, 0);
    } break;
    case 2: {
        uint16_t *udst = (uint16_t*)dst;
        const uint16_t *usrc = (const uint16_t*)src;

        copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, 0);
    } break;
    case 3: { /* could be slow.  use a struct to represent 3 bytes of data. */
        uint8x3_t *udst = (uint8x3_t*)dst;
        const uint8x3_t *usrc = (const uint8x3_t*)src;
        static const uint8x3_t zero;

        copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, zero);
    } break;
    case 4: {
        uint32_t *udst = (uint32_t*)dst;
        const uint32_t *usrc = (const uint32_t*)src;

        copy_frame_by_idx(udst, dst_channels, usrc, src_channels, idxary, count, 0);
    } break;
    default:
        abort(); /* illegal value */
        break;
    }
}

ChannelMixBufferProviderTambién se utiliza para convertir el formato de máscara de canal/número de canal de datos de audio. La frecuencia de muestreo y el formato de muestreo de los datos de audio permanecen sin cambios. La ChannelMixBufferProviderimplementación de cada función miembro de la clase es la siguiente:

ChannelMixBufferProvider::ChannelMixBufferProvider(audio_channel_mask_t inputChannelMask,
        audio_channel_mask_t outputChannelMask, audio_format_t format,
        size_t bufferFrameCount) :
        CopyBufferProvider(
                audio_bytes_per_sample(format)
                    * audio_channel_count_from_out_mask(inputChannelMask),
                audio_bytes_per_sample(format)
                    * audio_channel_count_from_out_mask(outputChannelMask),
                bufferFrameCount)
{
    ALOGV("ChannelMixBufferProvider(%p)(%#x, %#x, %#x)",
            this, format, inputChannelMask, outputChannelMask);
    if (outputChannelMask == AUDIO_CHANNEL_OUT_STEREO && format == AUDIO_FORMAT_PCM_FLOAT) {
        mIsValid = mChannelMix.setInputChannelMask(inputChannelMask);
    }
}

void ChannelMixBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
{
    mChannelMix.process(static_cast<const float *>(src), static_cast<float *>(dst),
            frames, false /* accumulate */);
}

ChannelMixBufferProviderSegún audio_utils::channels::ChannelMixla implementación, se requiere que los datos de salida sean estéreo de dos canales y el formato de muestreo sea de punto flotante.

ReformatBufferProviderSe utiliza para convertir el formato de muestreo de datos de audio. La frecuencia de muestreo y el formato de máscara de canal de los datos de audio permanecen sin cambios. La ReformatBufferProviderimplementación de cada función miembro de la clase es la siguiente:

ReformatBufferProvider::ReformatBufferProvider(int32_t channelCount,
        audio_format_t inputFormat, audio_format_t outputFormat,
        size_t bufferFrameCount) :
        CopyBufferProvider(
                channelCount * audio_bytes_per_sample(inputFormat),
                channelCount * audio_bytes_per_sample(outputFormat),
                bufferFrameCount),
        mChannelCount(channelCount),
        mInputFormat(inputFormat),
        mOutputFormat(outputFormat)
{
    ALOGV("ReformatBufferProvider(%p)(%u, %#x, %#x)",
            this, channelCount, inputFormat, outputFormat);
}

void ReformatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
{
    memcpy_by_audio_format(dst, mOutputFormat, src, mInputFormat, frames * mChannelCount);
}

ClampFloatBufferProviderSe utiliza para limitar la intensidad de los datos de audio a -3 dB, mientras que otras cosas permanecen sin cambios. ClampFloatBufferProviderLa implementación de cada función miembro de la clase es la siguiente:

ClampFloatBufferProvider::ClampFloatBufferProvider(int32_t channelCount, size_t bufferFrameCount) :
        CopyBufferProvider(
                channelCount * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT),
                channelCount * audio_bytes_per_sample(AUDIO_FORMAT_PCM_FLOAT),
                bufferFrameCount),
        mChannelCount(channelCount)
{
    ALOGV("ClampFloatBufferProvider(%p)(%u)", this, channelCount);
}

void ClampFloatBufferProvider::copyFrames(void *dst, const void *src, size_t frames)
{
    memcpy_to_float_from_float_with_clamping((float*)dst, (const float*)src,
                                             frames * mChannelCount,
                                             FLOAT_NOMINAL_RANGE_HEADROOM);
}

ClampFloatBufferProviderSe requiere que el formato de muestreo de entrada y salida sea de punto flotante.

TimestretchBufferProviderSe utiliza para estirar el tiempo de datos de audio y se implementa en base a la biblioteca de terceros sonic . TimestretchBufferProviderSe utiliza cuando se juega al doble de velocidad.

AdjustChannelsBufferProviderSe utiliza para ajustar datos muestreados, expandiéndolos o contrayéndolos de un formato de canal entrelazado a otro. Los canales extendidos adicionales se rellenan con ceros y se colocan al final de cada cuadro de audio. Los canales reducidos se copian al final del búfer de salida (el almacenamiento debe asignarse adecuadamente). Los canales reducidos se pueden escribir en el búfer de salida y ajustarse. Cuando se ajustan los canales de reducción en el búfer de reducción, el recuento de canales de entrada se calculará como 'inChannelCount - outChannelCount'. El recuento de canales de salida lo proporciona la persona que llama, es 'contratadoOutChannelCount'. Actualmente, la reproducción háptica acoplada a audio adopta principalmente el método de ajustar el canal de contracción. Si el dispositivo admite dos canales hápticos y la aplicación solo proporciona un canal háptico, el segundo canal háptico duplicará los datos del primer canal háptico. Si el dispositivo admite un único canal háptico y la aplicación proporciona dos canales hápticos, el segundo canal se reducirá.

En AudioMixer::postCreateTrack()la función, después de inicializar el objeto Track, se crearán nodos para la canalización de procesamiento de datos de audio y se construirá la canalización:

void AudioMixer::Track::unprepareForDownmix() {
    ALOGV("AudioMixer::unprepareForDownmix(%p)", this);

    if (mPostDownmixReformatBufferProvider.get() != nullptr) {
        // release any buffers held by the mPostDownmixReformatBufferProvider
        // before deallocating the mDownmixerBufferProvider.
        mPostDownmixReformatBufferProvider->reset();
    }

    mDownmixRequiresFormat = AUDIO_FORMAT_INVALID;
    if (mDownmixerBufferProvider.get() != nullptr) {
        // this track had previously been configured with a downmixer, delete it
        mDownmixerBufferProvider.reset(nullptr);
        reconfigureBufferProviders();
    } else {
        ALOGV(" nothing to do, no downmixer to delete");
    }
}

status_t AudioMixer::Track::prepareForDownmix()
{
    ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x",
            this, channelMask);

    // discard the previous downmixer if there was one
    unprepareForDownmix();
    // MONO_HACK Only remix (upmix or downmix) if the track and mixer/device channel masks
    // are not the same and not handled internally, as mono for channel position masks is.
    if (channelMask == mMixerChannelMask
            || (channelMask == AUDIO_CHANNEL_OUT_MONO
                    && isAudioChannelPositionMask(mMixerChannelMask))) {
        return NO_ERROR;
    }
    // DownmixerBufferProvider is only used for position masks.
    if (audio_channel_mask_get_representation(channelMask)
                == AUDIO_CHANNEL_REPRESENTATION_POSITION
            && DownmixerBufferProvider::isMultichannelCapable()) {

        // Check if we have a float or int16 downmixer, in that order.
        for (const audio_format_t format : { AUDIO_FORMAT_PCM_FLOAT, AUDIO_FORMAT_PCM_16_BIT }) {
            mDownmixerBufferProvider.reset(new DownmixerBufferProvider(
                    channelMask, mMixerChannelMask,
                    format,
                    sampleRate, sessionId, kCopyBufferFrameCount));
            if (static_cast<DownmixerBufferProvider *>(mDownmixerBufferProvider.get())
                    ->isValid()) {
                mDownmixRequiresFormat = format;
                reconfigureBufferProviders();
                return NO_ERROR;
            }
        }
        // mDownmixerBufferProvider reset below.
    }

    // See if we should use our built-in non-effect downmixer.
    if (mMixerInFormat == AUDIO_FORMAT_PCM_FLOAT
            && mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO
            && audio_channel_mask_get_representation(channelMask)
                    == AUDIO_CHANNEL_REPRESENTATION_POSITION) {
        mDownmixerBufferProvider.reset(new ChannelMixBufferProvider(channelMask,
                mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount));
        if (static_cast<ChannelMixBufferProvider *>(mDownmixerBufferProvider.get())
                ->isValid()) {
            mDownmixRequiresFormat = mMixerInFormat;
            reconfigureBufferProviders();
            ALOGD("%s: Fallback using ChannelMix", __func__);
            return NO_ERROR;
        } else {
            ALOGD("%s: ChannelMix not supported for channel mask %#x", __func__, channelMask);
        }
    }

    // Effect downmixer does not accept the channel conversion.  Let's use our remixer.
    mDownmixerBufferProvider.reset(new RemixBufferProvider(channelMask,
            mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount));
    // Remix always finds a conversion whereas Downmixer effect above may fail.
    reconfigureBufferProviders();
    return NO_ERROR;
}

void AudioMixer::Track::unprepareForReformat() {
    ALOGV("AudioMixer::unprepareForReformat(%p)", this);
    bool requiresReconfigure = false;
    if (mReformatBufferProvider.get() != nullptr) {
        mReformatBufferProvider.reset(nullptr);
        requiresReconfigure = true;
    }
    if (mPostDownmixReformatBufferProvider.get() != nullptr) {
        mPostDownmixReformatBufferProvider.reset(nullptr);
        requiresReconfigure = true;
    }
    if (requiresReconfigure) {
        reconfigureBufferProviders();
    }
}

status_t AudioMixer::Track::prepareForReformat()
{
    ALOGV("AudioMixer::prepareForReformat(%p) with format %#x", this, mFormat);
    // discard previous reformatters
    unprepareForReformat();
    // only configure reformatters as needed
    const audio_format_t targetFormat = mDownmixRequiresFormat != AUDIO_FORMAT_INVALID
            ? mDownmixRequiresFormat : mMixerInFormat;
    bool requiresReconfigure = false;
    if (mFormat != targetFormat) {
        mReformatBufferProvider.reset(new ReformatBufferProvider(
                audio_channel_count_from_out_mask(channelMask),
                mFormat,
                targetFormat,
                kCopyBufferFrameCount));
        requiresReconfigure = true;
    } else if (mFormat == AUDIO_FORMAT_PCM_FLOAT) {
        // Input and output are floats, make sure application did not provide > 3db samples
        // that would break volume application (b/68099072)
        // TODO: add a trusted source flag to avoid the overhead
        mReformatBufferProvider.reset(new ClampFloatBufferProvider(
                audio_channel_count_from_out_mask(channelMask),
                kCopyBufferFrameCount));
        requiresReconfigure = true;
    }
    if (targetFormat != mMixerInFormat) {
        mPostDownmixReformatBufferProvider.reset(new ReformatBufferProvider(
                audio_channel_count_from_out_mask(mMixerChannelMask),
                targetFormat,
                mMixerInFormat,
                kCopyBufferFrameCount));
        requiresReconfigure = true;
    }
    if (requiresReconfigure) {
        reconfigureBufferProviders();
    }
    return NO_ERROR;
}

void AudioMixer::Track::unprepareForAdjustChannels()
{
    ALOGV("AUDIOMIXER::unprepareForAdjustChannels");
    if (mAdjustChannelsBufferProvider.get() != nullptr) {
        mAdjustChannelsBufferProvider.reset(nullptr);
        reconfigureBufferProviders();
    }
}

status_t AudioMixer::Track::prepareForAdjustChannels(size_t frames)
{
    ALOGV("AudioMixer::prepareForAdjustChannels(%p) with inChannelCount: %u, outChannelCount: %u",
            this, mAdjustInChannelCount, mAdjustOutChannelCount);
    unprepareForAdjustChannels();
    if (mAdjustInChannelCount != mAdjustOutChannelCount) {
        uint8_t* buffer = mKeepContractedChannels
                ? (uint8_t*)mainBuffer + frames * audio_bytes_per_frame(
                        mMixerChannelCount, mMixerFormat)
                : nullptr;
        mAdjustChannelsBufferProvider.reset(new AdjustChannelsBufferProvider(
                mFormat, mAdjustInChannelCount, mAdjustOutChannelCount, frames,
                mKeepContractedChannels ? mMixerFormat : AUDIO_FORMAT_INVALID,
                buffer, mMixerHapticChannelCount));
        reconfigureBufferProviders();
    }
    return NO_ERROR;
}

void AudioMixer::Track::clearContractedBuffer()
{
    if (mAdjustChannelsBufferProvider.get() != nullptr) {
        static_cast<AdjustChannelsBufferProvider*>(
                mAdjustChannelsBufferProvider.get())->clearContractedFrames();
    }
}

Después de crear nodos para la canalización de procesamiento de datos de audio, AudioMixer::Track::reconfigureBufferProviders()cree la canalización mediante:

void AudioMixer::Track::reconfigureBufferProviders()
{
    // configure from upstream to downstream buffer providers.
    bufferProvider = mInputBufferProvider;
    if (mAdjustChannelsBufferProvider.get() != nullptr) {
        mAdjustChannelsBufferProvider->setBufferProvider(bufferProvider);
        bufferProvider = mAdjustChannelsBufferProvider.get();
    }
    if (mReformatBufferProvider.get() != nullptr) {
        mReformatBufferProvider->setBufferProvider(bufferProvider);
        bufferProvider = mReformatBufferProvider.get();
    }
    if (mDownmixerBufferProvider.get() != nullptr) {
        mDownmixerBufferProvider->setBufferProvider(bufferProvider);
        bufferProvider = mDownmixerBufferProvider.get();
    }
    if (mPostDownmixReformatBufferProvider.get() != nullptr) {
        mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider);
        bufferProvider = mPostDownmixReformatBufferProvider.get();
    }
    if (mTimestretchBufferProvider.get() != nullptr) {
        mTimestretchBufferProvider->setBufferProvider(bufferProvider);
        bufferProvider = mTimestretchBufferProvider.get();
    }
}

AudioMixer::TrackEl proceso de procesamiento de datos de audio creado se verá como en la siguiente figura:

Canalización de audio en AudioMixer::Track

AudioMixer::TrackLa canalización de procesamiento de datos de audio integrada no realiza remuestreo.

AudioMixerSe proporciona una función setBufferProvider()para configurar la fuente de datos de audio para una fuente de audio del mezclador. La definición de esta función es la siguiente:

void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    const std::shared_ptr<Track> &track = getTrack(name);

    if (track->mInputBufferProvider == bufferProvider) {
        return; // don't reset any buffer providers if identical.
    }
    // reset order from downstream to upstream buffer providers.
    if (track->mTimestretchBufferProvider.get() != nullptr) {
        track->mTimestretchBufferProvider->reset();
    } else if (track->mPostDownmixReformatBufferProvider.get() != nullptr) {
        track->mPostDownmixReformatBufferProvider->reset();
    } else if (track->mDownmixerBufferProvider != nullptr) {
        track->mDownmixerBufferProvider->reset();
    } else if (track->mReformatBufferProvider.get() != nullptr) {
        track->mReformatBufferProvider->reset();
    } else if (track->mAdjustChannelsBufferProvider.get() != nullptr) {
        track->mAdjustChannelsBufferProvider->reset();
    }

    track->mInputBufferProvider = bufferProvider;
    track->reconfigureBufferProviders();
}

setBufferProvider()La función necesita pasar el identificador y el objeto de una fuente de audio AudioBufferProvider. Al agregar el nodo de fuente de datos de audio, AudioMixer::Trackel proceso de procesamiento de datos de audio creado de esta manera se verá como en la siguiente figura:

Canalización de audio en AudioMixer::Pista 2

Procesamiento de mezclas

AudioMixerEl usuario llama a su process()función para realizar operaciones de mezcla. Los datos de cada fuente de audio durante la mezcla provienen en última instancia AudioBufferProviderdel objeto configurado para ella. Los datos después de la mezcla se guardan en el conjunto de objetos para cada fuente de audio MAIN_BUFFER. AudioMixerCada pista de fuente de audio contenida en se MAIN_BUFFERagrupará según su y MAIN_BUFFERlos datos de audio que compartan la misma fuente de audio se mezclarán.

AudioMixerfunction , es decir, la función de process()su clase principal , la definición de la función (ubicada en frameworks/av/media/libaudioprocessing/include/media/AudioMixerBase.h ) es la siguiente:AudioMixerBaseprocess()

    void        process() {
        preProcess();
        (this->*mHook)();
        postProcess();
    }
 . . . . . .
    // preProcess is called before the process hook, postProcess after,
    // see the implementation of process() method.
    virtual void preProcess() {}
    virtual void postProcess() {}
 . . . . . .
    // Called when track info changes and a new process hook should be determined.
    void invalidate() {
        mHook = &AudioMixerBase::process__validate;
    }
 . . . . . .
    process_hook_t mHook = &AudioMixerBase::process__nop;   // one of process__*, never nullptr

process()En diferentes situaciones, la función mHookllama a diferentes funciones a través del puntero de función miembro de la clase process__*para realizar operaciones de procesamiento, se mHookllama antes de ser llamada para realizar el preprocesamiento preProcess()y se llama después de ser llamada para postProcess()realizar el posprocesamiento. AudioMixerEl preprocesamiento y posprocesamiento de la mezcla se muestran en el siguiente código (ubicado en frameworks/av/media/libaudioprocessing/AudioMixer.cpp ):

void AudioMixer::preProcess()
{
    for (const auto &pair : mTracks) {
        // Clear contracted buffer before processing if contracted channels are saved
        const std::shared_ptr<TrackBase> &tb = pair.second;
        Track *t = static_cast<Track*>(tb.get());
        if (t->mKeepContractedChannels) {
            t->clearContractedBuffer();
        }
    }
}

void AudioMixer::postProcess()
{
    // Process haptic data.
    // Need to keep consistent with VibrationEffect.scale(int, float, int)
    for (const auto &pair : mGroups) {
        // process by group of tracks with same output main buffer.
        const auto &group = pair.second;
        for (const int name : group) {
            const std::shared_ptr<Track> &t = getTrack(name);
            if (t->mHapticPlaybackEnabled) {
                size_t sampleCount = mFrameCount * t->mMixerHapticChannelCount;
                uint8_t* buffer = (uint8_t*)pair.first + mFrameCount * audio_bytes_per_frame(
                        t->mMixerChannelCount, t->mMixerFormat);
                switch (t->mMixerFormat) {
                // Mixer format should be AUDIO_FORMAT_PCM_FLOAT.
                case AUDIO_FORMAT_PCM_FLOAT: {
                    os::scaleHapticData((float*) buffer, sampleCount, t->mHapticIntensity,
                                        t->mHapticMaxAmplitude);
                } break;
                default:
                    LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat);
                    break;
                }
                break;
            }
        }
    }
}

Se ocupan principalmente del canal táctil.

AudioMixerBaseEl mHookvalor predeterminado apunta a AudioMixerBase::process__nop()la función, AudioMixerBase::process__nop()la definición de la función (ubicada en frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp ) es la siguiente:

void AudioMixerBase::process__nop()
{
    ALOGVV("process__nop\n");

    for (const auto &pair : mGroups) {
        // process by group of tracks with same output buffer to
        // avoid multiple memset() on same buffer
        const auto &group = pair.second;

        const std::shared_ptr<TrackBase> &t = mTracks[group[0]];
        memset(t->mainBuffer, 0,
                mFrameCount * audio_bytes_per_frame(t->getMixerChannelCount(), t->mMixerFormat));

        // now consume data
        for (const int name : group) {
            const std::shared_ptr<TrackBase> &t = mTracks[name];
            size_t outFrames = mFrameCount;
            while (outFrames) {
                t->buffer.frameCount = outFrames;
                t->bufferProvider->getNextBuffer(&t->buffer);
                if (t->buffer.raw == NULL) break;
                outFrames -= t->buffer.frameCount;
                t->bufferProvider->releaseBuffer(&t->buffer);
            }
        }
    }
}

AudioMixerBase::process__nop()La función se procesa de acuerdo con el grupo de fuentes de audio, borra el primer elemento del grupo de fuentes de audio MAIN_BUFFERy obtiene un dato de cada pista de fuente de audio para evitar la sincronización causada por la acumulación de datos de audio en el proceso de procesamiento de datos de audio de la pista. pregunta. En cuanto a AudioMixerBase::process__validate()la definición de funciones, AudioMixerBase::process__nop()parece que las funciones también deberían agruparse en primer lugar.

Cuando AudioMixerla fuente de audio de está habilitada, deshabilitada o se modifican algunos parámetros, se llamará a la función AudioMixerde para restablecer la función de señalización. Como se muestra en el siguiente código (ubicado en frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp ):invalidate()mHookAudioMixerBase::process__validate()

void AudioMixerBase::destroy(int name)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    ALOGV("deleteTrackName(%d)", name);

    if (mTracks[name]->enabled) {
        invalidate();
    }
    mTracks.erase(name); // deallocate track
}

void AudioMixerBase::enable(int name)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    const std::shared_ptr<TrackBase> &track = mTracks[name];

    if (!track->enabled) {
        track->enabled = true;
        ALOGV("enable(%d)", name);
        invalidate();
    }
}

void AudioMixerBase::disable(int name)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    const std::shared_ptr<TrackBase> &track = mTracks[name];

    if (track->enabled) {
        track->enabled = false;
        ALOGV("disable(%d)", name);
        invalidate();
    }
}
 . . . . . .
void AudioMixerBase::setParameter(int name, int target, int param, void *value)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    const std::shared_ptr<TrackBase> &track = mTracks[name];

    int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
    int32_t *valueBuf = reinterpret_cast<int32_t*>(value);

    switch (target) {

    case TRACK:
        switch (param) {
        case CHANNEL_MASK: {
            const audio_channel_mask_t trackChannelMask =
                static_cast<audio_channel_mask_t>(valueInt);
            if (setChannelMasks(name, trackChannelMask, track->mMixerChannelMask)) {
                ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask);
                invalidate();
            }
            } break;
        case MAIN_BUFFER:
            if (track->mainBuffer != valueBuf) {
                track->mainBuffer = valueBuf;
                ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
                invalidate();
            }
            break;
        case AUX_BUFFER:
            if (track->auxBuffer != valueBuf) {
                track->auxBuffer = valueBuf;
                ALOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
                invalidate();
            }
            break;
        case FORMAT: {
            audio_format_t format = static_cast<audio_format_t>(valueInt);
            if (track->mFormat != format) {
                ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
                track->mFormat = format;
                ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
                invalidate();
            }
            } break;

AudioMixerBase::process__validate()La función es la entrada a la operación de procesamiento de mezcla real. La definición de la función (ubicada en frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp ) es la siguiente:

void AudioMixerBase::process__validate()
{
    // TODO: fix all16BitsStereNoResample logic to
    // either properly handle muted tracks (it should ignore them)
    // or remove altogether as an obsolete optimization.
    bool all16BitsStereoNoResample = true;
    bool resampling = false;
    bool volumeRamp = false;

    mEnabled.clear();
    mGroups.clear();
    for (const auto &pair : mTracks) {
        const int name = pair.first;
        const std::shared_ptr<TrackBase> &t = pair.second;
        if (!t->enabled) continue;

        mEnabled.emplace_back(name);  // we add to mEnabled in order of name.
        mGroups[t->mainBuffer].emplace_back(name); // mGroups also in order of name.

        uint32_t n = 0;
        // FIXME can overflow (mask is only 3 bits)
        n |= NEEDS_CHANNEL_1 + t->channelCount - 1;
        if (t->doesResample()) {
            n |= NEEDS_RESAMPLE;
        }
        if (t->auxLevel != 0 && t->auxBuffer != NULL) {
            n |= NEEDS_AUX;
        }

        if (t->volumeInc[0]|t->volumeInc[1]) {
            volumeRamp = true;
        } else if (!t->doesResample() && t->volumeRL == 0) {
            n |= NEEDS_MUTE;
        }
        t->needs = n;

        if (n & NEEDS_MUTE) {
            t->hook = &TrackBase::track__nop;
        } else {
            if (n & NEEDS_AUX) {
                all16BitsStereoNoResample = false;
            }
            if (n & NEEDS_RESAMPLE) {
                all16BitsStereoNoResample = false;
                resampling = true;
                if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1
                        && t->channelMask == AUDIO_CHANNEL_OUT_MONO // MONO_HACK
                        && isAudioChannelPositionMask(t->mMixerChannelMask)) {
                    t->hook = TrackBase::getTrackHook(
                            TRACKTYPE_RESAMPLEMONO, t->mMixerChannelCount,
                            t->mMixerInFormat, t->mMixerFormat);
                } else if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2
                        && t->useStereoVolume()) {
                    t->hook = TrackBase::getTrackHook(
                            TRACKTYPE_RESAMPLESTEREO, t->mMixerChannelCount,
                            t->mMixerInFormat, t->mMixerFormat);
                } else {
                    t->hook = TrackBase::getTrackHook(
                            TRACKTYPE_RESAMPLE, t->mMixerChannelCount,
                            t->mMixerInFormat, t->mMixerFormat);
                }
                ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                        "Track %d needs downmix + resample", name);
            } else {
                if ((n & NEEDS_CHANNEL_COUNT__MASK) == NEEDS_CHANNEL_1){
                    t->hook = TrackBase::getTrackHook(
                            (isAudioChannelPositionMask(t->mMixerChannelMask)  // TODO: MONO_HACK
                                    && t->channelMask == AUDIO_CHANNEL_OUT_MONO)
                                ? TRACKTYPE_NORESAMPLEMONO : TRACKTYPE_NORESAMPLE,
                            t->mMixerChannelCount,
                            t->mMixerInFormat, t->mMixerFormat);
                    all16BitsStereoNoResample = false;
                }
                if ((n & NEEDS_CHANNEL_COUNT__MASK) >= NEEDS_CHANNEL_2){
                    t->hook = TrackBase::getTrackHook(
                            t->useStereoVolume() ? TRACKTYPE_NORESAMPLESTEREO
                                    : TRACKTYPE_NORESAMPLE,
                            t->mMixerChannelCount, t->mMixerInFormat,
                            t->mMixerFormat);
                    ALOGV_IF((n & NEEDS_CHANNEL_COUNT__MASK) > NEEDS_CHANNEL_2,
                            "Track %d needs downmix", name);
                }
            }
        }
    }

    // select the processing hooks
    mHook = &AudioMixerBase::process__nop;
    if (mEnabled.size() > 0) {
        if (resampling) {
            if (mOutputTemp.get() == nullptr) {
                mOutputTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
            }
            if (mResampleTemp.get() == nullptr) {
                mResampleTemp.reset(new int32_t[MAX_NUM_CHANNELS * mFrameCount]);
            }
            mHook = &AudioMixerBase::process__genericResampling;
        } else {
            // we keep temp arrays around.
            mHook = &AudioMixerBase::process__genericNoResampling;
            if (all16BitsStereoNoResample && !volumeRamp) {
                if (mEnabled.size() == 1) {
                    const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];
                    if ((t->needs & NEEDS_MUTE) == 0) {
                        // The check prevents a muted track from acquiring a process hook.
                        //
                        // This is dangerous if the track is MONO as that requires
                        // special case handling due to implicit channel duplication.
                        // Stereo or Multichannel should actually be fine here.
                        mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
                                t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
                                t->useStereoVolume());
                    }
                }
            }
        }
    }

    ALOGV("mixer configuration change: %zu "
        "all16BitsStereoNoResample=%d, resampling=%d, volumeRamp=%d",
        mEnabled.size(), all16BitsStereoNoResample, resampling, volumeRamp);

    process();

    // Now that the volume ramp has been done, set optimal state and
    // track hooks for subsequent mixer process
    if (mEnabled.size() > 0) {
        bool allMuted = true;

        for (const int name : mEnabled) {
            const std::shared_ptr<TrackBase> &t = mTracks[name];
            if (!t->doesResample() && t->volumeRL == 0) {
                t->needs |= NEEDS_MUTE;
                t->hook = &TrackBase::track__nop;
            } else {
                allMuted = false;
            }
        }
        if (allMuted) {
            mHook = &AudioMixerBase::process__nop;
        } else if (all16BitsStereoNoResample) {
            if (mEnabled.size() == 1) {
                //const int i = 31 - __builtin_clz(enabledTracks);
                const std::shared_ptr<TrackBase> &t = mTracks[mEnabled[0]];
                // Muted single tracks handled by allMuted above.
                mHook = getProcessHook(PROCESSTYPE_NORESAMPLEONETRACK,
                        t->mMixerChannelCount, t->mMixerInFormat, t->mMixerFormat,
                        t->useStereoVolume());
            }
        }
    }
}

AudioMixerBase::process__validate()El proceso de ejecución de la función es el siguiente:

  1. MAIN_BUFFERAgrupe todas las fuentes de audio habilitadas según sus hooke inicialice cada fuente de audio. Este último se utiliza para mezclar los datos de la fuente de audio actual en los datos de salida, mientras se verifica si es necesario volver a muestrear y si se realiza una rampa de volumen. necesidades, y si todos son estéreo de dos canales de 16 bits y no requieren remuestreo. El código de inicialización grande t->hookaquí parece definir una función específica para TrackBase, y es más razonable poner este código en ;
  2. De acuerdo con la situación general de todas las fuentes de audio en el mezclador, reinicie AudioMixerpara mHookapuntar a la función de procesamiento adecuada y asigne el búfer temporal y el búfer de salida utilizados en el remuestreo según sea necesario;
  3. Llame recursivamente a process()la función para realizar operaciones de procesamiento de mezcla;
  4. En este momento, la operación de rampa de volumen se ha completado y la pista de fuente de audio del mezclador hooky la devolución de llamada de la operación de procesamiento de mezcla del mezclador se restablecen mHooksegún sea necesario para operaciones de mezcla posteriores.

Si AudioMixerse aplana la operación de procesamiento de mezcla, tendrá un proceso de ejecución similar a la siguiente figura:

Proceso del mezclador de audio

Cuando AudioMixerlas fuentes de audio están habilitadas, deshabilitadas o se modifican ciertos parámetros, AudioMixerBase::process__validate()primero se ejecutará la función para MAIN_BUFFERagruparlas según las fuentes de audio del mezclador e inicializar el procesamiento de mezcla posterior según su configuración, para luego AudioMixerBase::process__*realizar la mezcla. procesamiento a través de otras funciones.

Cuando hay una fuente de audio que necesita ser remuestreada durante la operación de mezcla, la operación de mezcla se AudioMixerBase::process__genericResampling()completa principalmente mediante la función, que se define de la siguiente manera:

// generic code with resampling
void AudioMixerBase::process__genericResampling()
{
    ALOGVV("process__genericResampling\n");
    int32_t * const outTemp = mOutputTemp.get(); // naked ptr
    size_t numFrames = mFrameCount;

    for (const auto &pair : mGroups) {
        const auto &group = pair.second;
        const std::shared_ptr<TrackBase> &t1 = mTracks[group[0]];

        // clear temp buffer
        memset(outTemp, 0, sizeof(*outTemp) * t1->mMixerChannelCount * mFrameCount);
        for (const int name : group) {
            const std::shared_ptr<TrackBase> &t = mTracks[name];
            int32_t *aux = NULL;
            if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {
                aux = t->auxBuffer;
            }

            // 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) {
                (t.get()->*t->hook)(outTemp, numFrames, mResampleTemp.get() /* naked ptr */, aux);
            } else {

                size_t outFrames = 0;

                while (outFrames < numFrames) {
                    t->buffer.frameCount = numFrames - outFrames;
                    t->bufferProvider->getNextBuffer(&t->buffer);
                    t->mIn = t->buffer.raw;
                    // t->mIn == nullptr can happen if the track was flushed just after having
                    // been enabled for mixing.
                    if (t->mIn == nullptr) break;

                    (t.get()->*t->hook)(
                            outTemp + outFrames * t->mMixerChannelCount, t->buffer.frameCount,
                            mResampleTemp.get() /* naked ptr */,
                            aux != nullptr ? aux + outFrames : nullptr);
                    outFrames += t->buffer.frameCount;

                    t->bufferProvider->releaseBuffer(&t->buffer);
                }
            }
        }
        convertMixerFormat(t1->mainBuffer, t1->mMixerFormat,
                outTemp, t1->mMixerInFormat, numFrames * t1->mMixerChannelCount);
    }
}

El proceso de ejecución de esta función es aproximadamente el siguiente:

  1. Realizar operaciones de mezcla para cada grupo de fuentes de audio del mezclador;
  2. Para cada fuente de audio en el grupo de fuentes de audio, si es necesario volver a muestrear, llame a la pista de la fuente de audio para hookvolver a muestrear los datos de audio de la fuente de audio y mezclarlos en un búfer temporal. Si no es necesario volver a muestrear, repita Obtener un fragmento de datos de audio de la pista de fuente de audio y mézclelo en un búfer temporal hasta que los datos obtenidos alcancen el número de fotogramas de audio de salida solicitados por el mezclador. El código aquí implica que cada vez que se obtiene de la pista de fuente de audio El número Los datos de audio y el número de datos de salida del mezclador están alineados, es decir, este último es un múltiplo entero del primero. Como puede ver aquí, la semántica de la interfaz de la pista de la fuente de audio hookes muy confusa e inconsistente. Cuando la fuente de audio necesita ser remuestreado, hookla operación genera y mezcla todos los fotogramas de audio solicitados por el mezclador a la vez. Cuando la fuente de audio no requiere remuestreo, cada llamada solo mezcla una pequeña parte de los datos que se han obtenido. De hecho, cuando el audio la fuente no requiere remuestreo. El bucle completo aquí no es tan TrackBaseclaro y ordenado como encapsularlo en una función, similar a la situación donde se requiere mezcla ;
  3. Llame convertMixerFormat()a la función para convertir los datos mezclados al formato y guardarlos en el grupo de fuentes de audio MAIN_BUFFER.

AudioMixerBase::convertMixerFormat()La función se define de la siguiente manera:

void AudioMixerBase::convertMixerFormat(void *out, audio_format_t mixerOutFormat,
        void *in, audio_format_t mixerInFormat, size_t sampleCount)
{
    switch (mixerInFormat) {
    case AUDIO_FORMAT_PCM_FLOAT:
        switch (mixerOutFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            memcpy(out, in, sampleCount * sizeof(float)); // MEMCPY. TODO optimize out
            break;
        case AUDIO_FORMAT_PCM_16_BIT:
            memcpy_to_i16_from_float((int16_t*)out, (float*)in, sampleCount);
            break;
        default:
            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
            break;
        }
        break;
    case AUDIO_FORMAT_PCM_16_BIT:
        switch (mixerOutFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            memcpy_to_float_from_q4_27((float*)out, (const int32_t*)in, sampleCount);
            break;
        case AUDIO_FORMAT_PCM_16_BIT:
            memcpy_to_i16_from_q4_27((int16_t*)out, (const int32_t*)in, sampleCount);
            break;
        default:
            LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
            break;
        }
        break;
    default:
        LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
        break;
    }
}

AudioMixerBase::convertMixerFormat()memcpy*Las funciones realizan la conversión de formato a través de diferentes funciones.

Cuando no hay ninguna fuente de audio que deba volver a muestrearse durante la operación de mezcla, la operación de mezcla se AudioMixerBase::process__genericNoResampling()completa principalmente mediante la función, que se define de la siguiente manera:

void AudioMixerBase::process__genericNoResampling()
{
    ALOGVV("process__genericNoResampling\n");
    int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32)));

    for (const auto &pair : mGroups) {
        // process by group of tracks with same output main buffer to
        // avoid multiple memset() on same buffer
        const auto &group = pair.second;

        // acquire buffer
        for (const int name : group) {
            const std::shared_ptr<TrackBase> &t = mTracks[name];
            t->buffer.frameCount = mFrameCount;
            t->bufferProvider->getNextBuffer(&t->buffer);
            t->frameCount = t->buffer.frameCount;
            t->mIn = t->buffer.raw;
        }

        int32_t *out = (int *)pair.first;
        size_t numFrames = 0;
        do {
            const size_t frameCount = std::min((size_t)BLOCKSIZE, mFrameCount - numFrames);
            memset(outTemp, 0, sizeof(outTemp));
            for (const int name : group) {
                const std::shared_ptr<TrackBase> &t = mTracks[name];
                int32_t *aux = NULL;
                if (CC_UNLIKELY(t->needs & NEEDS_AUX)) {
                    aux = t->auxBuffer + numFrames;
                }
                for (int outFrames = frameCount; outFrames > 0; ) {
                    // t->in == nullptr can happen if the track was flushed just after having
                    // been enabled for mixing.
                    if (t->mIn == nullptr) {
                        break;
                    }
                    size_t inFrames = (t->frameCount > outFrames)?outFrames:t->frameCount;
                    if (inFrames > 0) {
                        (t.get()->*t->hook)(
                                outTemp + (frameCount - outFrames) * t->mMixerChannelCount,
                                inFrames, mResampleTemp.get() /* naked ptr */, aux);
                        t->frameCount -= inFrames;
                        outFrames -= inFrames;
                        if (CC_UNLIKELY(aux != NULL)) {
                            aux += inFrames;
                        }
                    }
                    if (t->frameCount == 0 && outFrames) {
                        t->bufferProvider->releaseBuffer(&t->buffer);
                        t->buffer.frameCount = (mFrameCount - numFrames) -
                                (frameCount - outFrames);
                        t->bufferProvider->getNextBuffer(&t->buffer);
                        t->mIn = t->buffer.raw;
                        if (t->mIn == nullptr) {
                            break;
                        }
                        t->frameCount = t->buffer.frameCount;
                    }
                }
            }

            const std::shared_ptr<TrackBase> &t1 = mTracks[group[0]];
            convertMixerFormat(out, t1->mMixerFormat, outTemp, t1->mMixerInFormat,
                    frameCount * t1->mMixerChannelCount);
            // TODO: fix ugly casting due to choice of out pointer type
            out = reinterpret_cast<int32_t*>((uint8_t*)out
                    + frameCount * t1->mMixerChannelCount
                    * audio_bytes_per_sample(t1->mMixerFormat));
            numFrames += frameCount;
        } while (numFrames < mFrameCount);

        // release each track's buffer
        for (const int name : group) {
            const std::shared_ptr<TrackBase> &t = mTracks[name];
            t->bufferProvider->releaseBuffer(&t->buffer);
        }
    }
}

El proceso de ejecución de esta función se puede AudioMixerBase::process__genericResampling()comparar con el proceso de ejecución de la función. Ambos realizan operaciones de mezcla para cada grupo de fuentes de audio del mezclador, pero el primero toma una pequeña porción de datos de todas las fuentes de audio cada vez. se procesa una pequeña porción de datos, se procesa otra pequeña porción de datos hasta que se alcanza el número de cuadros de audio de salida solicitados por el mezclador; este último es procesar una fuente de audio a la vez y convertir una cantidad suficiente de fuentes de audio en El Los datos de audio se mezclan con los datos de salida y luego se mezcla una cantidad suficiente de datos de audio de la siguiente fuente de audio hasta que se hayan procesado todas las fuentes de audio. Además, esta última puede procesar hasta 16 cuadros de audio a la vez, lo cual es 16 fotogramas de audio BLOCKSIZE.

Si AudioMixerBase::process__genericResampling()la implementación de funciones ya parece confusa, AudioMixerBase::process__genericNoResampling()la implementación de funciones lo es aún más. Si observa estas dos funciones con atención, pensará: ¿cuál es el significado de definir estas dos funciones por separado? Fusionarlos se verá mejor. Además, ¿no veo AudioMixerBase::process__genericNoResampling()la necesidad de procesar hasta 16 fotogramas de audio a la vez? Está claro que la mayor parte de la lógica de estas dos funciones es la misma, incluso si hay una ligera diferencia en el método de escritura de las dos funciones, es difícil ocultar el hecho de que son códigos duplicados.

Ver un código tan pobre escrito en Android AOSP realmente hace que la gente se pregunte qué papel juega la calidad del código en el éxito de Android.

AudioMixerBaseAudioMixerBase::process__oneTrack16BitsStereoNoResampling()También hay dos funciones de procesamiento optimizadas y para situaciones en las que solo hay una fuente de audio en el mezclador y no se requiere remuestreo AudioMixerBase::process__noResampleOneTrack(), las condiciones de uso de estas dos funciones son las que se AudioMixerBase::getProcessHook()muestran en la definición de la función:

/* Returns the proper process hook for mixing tracks. Currently works only for
 * PROCESSTYPE_NORESAMPLEONETRACK, a mix involving one track, no resampling.
 *
 * TODO: Due to the special mixing considerations of duplicating to
 * a stereo output track, the input track cannot be MONO.  This should be
 * prevented by the caller.
 */
/* static */
AudioMixerBase::process_hook_t AudioMixerBase::getProcessHook(
        int processType, uint32_t channelCount,
        audio_format_t mixerInFormat, audio_format_t mixerOutFormat,
        bool stereoVolume)
{
    if (processType != PROCESSTYPE_NORESAMPLEONETRACK) { // Only NORESAMPLEONETRACK
        LOG_ALWAYS_FATAL("bad processType: %d", processType);
        return NULL;
    }
    if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
        return &AudioMixerBase::process__oneTrack16BitsStereoNoResampling;
    }
    LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);

    if (stereoVolume) { // templated arguments require explicit values.
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            switch (mixerOutFormat) {
            case AUDIO_FORMAT_PCM_FLOAT:
                return &AudioMixerBase::process__noResampleOneTrack<
                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
                        float /*TI*/, TYPE_AUX>;
            case AUDIO_FORMAT_PCM_16_BIT:
                return &AudioMixerBase::process__noResampleOneTrack<
                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
                        float /*TI*/, TYPE_AUX>;
            default:
                LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
                break;
            }
            break;
        case AUDIO_FORMAT_PCM_16_BIT:
            switch (mixerOutFormat) {
            case AUDIO_FORMAT_PCM_FLOAT:
                return &AudioMixerBase::process__noResampleOneTrack<
                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, float /*TO*/,
                        int16_t /*TI*/, TYPE_AUX>;
            case AUDIO_FORMAT_PCM_16_BIT:
                return &AudioMixerBase::process__noResampleOneTrack<
                        MIXTYPE_MULTI_SAVEONLY_STEREOVOL, int16_t /*TO*/,
                        int16_t /*TI*/, TYPE_AUX>;
            default:
                LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
                break;
            }
            break;
        default:
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
    } else {
          switch (mixerInFormat) {
          case AUDIO_FORMAT_PCM_FLOAT:
              switch (mixerOutFormat) {
              case AUDIO_FORMAT_PCM_FLOAT:
                  return &AudioMixerBase::process__noResampleOneTrack<
                          MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
                          float /*TI*/, TYPE_AUX>;
              case AUDIO_FORMAT_PCM_16_BIT:
                  return &AudioMixerBase::process__noResampleOneTrack<
                          MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
                          float /*TI*/, TYPE_AUX>;
              default:
                  LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
                  break;
              }
              break;
          case AUDIO_FORMAT_PCM_16_BIT:
              switch (mixerOutFormat) {
              case AUDIO_FORMAT_PCM_FLOAT:
                  return &AudioMixerBase::process__noResampleOneTrack<
                          MIXTYPE_MULTI_SAVEONLY, float /*TO*/,
                          int16_t /*TI*/, TYPE_AUX>;
              case AUDIO_FORMAT_PCM_16_BIT:
                  return &AudioMixerBase::process__noResampleOneTrack<
                          MIXTYPE_MULTI_SAVEONLY, int16_t /*TO*/,
                          int16_t /*TI*/, TYPE_AUX>;
              default:
                  LOG_ALWAYS_FATAL("bad mixerOutFormat: %#x", mixerOutFormat);
                  break;
              }
              break;
          default:
              LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
              break;
          }
    }
    return NULL;
}

Los procesos detallados de estas dos operaciones de procesamiento no se analizarán aquí.

Vimos anteriormente que la pista de flujo de audio hookmezcla los datos de audio del flujo actual con los datos de salida. hookLa función real TrackBase::getTrackHook()está determinada por parámetros como si se requiere remuestreo, el formato de muestreo de entrada y salida y el número de canales. La estrategia específica es el siguiente: Mostrar:

AudioMixerBase::hook_t AudioMixerBase::TrackBase::getTrackHook(int trackType, uint32_t channelCount,
        audio_format_t mixerInFormat, audio_format_t mixerOutFormat __unused)
{
    if (!kUseNewMixer && channelCount == FCC_2 && mixerInFormat == AUDIO_FORMAT_PCM_16_BIT) {
        switch (trackType) {
        case TRACKTYPE_NOP:
            return &TrackBase::track__nop;
        case TRACKTYPE_RESAMPLE:
            return &TrackBase::track__genericResample;
        case TRACKTYPE_NORESAMPLEMONO:
            return &TrackBase::track__16BitsMono;
        case TRACKTYPE_NORESAMPLE:
            return &TrackBase::track__16BitsStereo;
        default:
            LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
            break;
        }
    }
    LOG_ALWAYS_FATAL_IF(channelCount > MAX_NUM_CHANNELS);
    switch (trackType) {
    case TRACKTYPE_NOP:
        return &TrackBase::track__nop;
    case TRACKTYPE_RESAMPLE:
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
                    MIXTYPE_MULTI, float /*TO*/, float /*TI*/, TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
                    MIXTYPE_MULTI, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
        break;
    case TRACKTYPE_RESAMPLESTEREO:
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
                    MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
                    TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
                    MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
                    TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
        break;
    // RESAMPLEMONO needs MIXTYPE_STEREOEXPAND since resampler will upmix mono
    // track to stereo track
    case TRACKTYPE_RESAMPLEMONO:
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
                    MIXTYPE_STEREOEXPAND, float /*TO*/, float /*TI*/,
                    TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return (AudioMixerBase::hook_t) &TrackBase::track__Resample<
                    MIXTYPE_STEREOEXPAND, int32_t /*TO*/, int16_t /*TI*/,
                    TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
        break;
    case TRACKTYPE_NORESAMPLEMONO:
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
                            MIXTYPE_MONOEXPAND, float /*TO*/, float /*TI*/, TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
                            MIXTYPE_MONOEXPAND, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
        break;
    case TRACKTYPE_NORESAMPLE:
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
                    MIXTYPE_MULTI, float /*TO*/, float /*TI*/, TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
                    MIXTYPE_MULTI, int32_t /*TO*/, int16_t /*TI*/, TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
        break;
    case TRACKTYPE_NORESAMPLESTEREO:
        switch (mixerInFormat) {
        case AUDIO_FORMAT_PCM_FLOAT:
            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
                    MIXTYPE_MULTI_STEREOVOL, float /*TO*/, float /*TI*/,
                    TYPE_AUX>;
        case AUDIO_FORMAT_PCM_16_BIT:
            return (AudioMixerBase::hook_t) &TrackBase::track__NoResample<
                    MIXTYPE_MULTI_STEREOVOL, int32_t /*TO*/, int16_t /*TI*/,
                    TYPE_AUX>;
        default:
            LOG_ALWAYS_FATAL("bad mixerInFormat: %#x", mixerInFormat);
            break;
        }
        break;
    default:
        LOG_ALWAYS_FATAL("bad trackType: %d", trackType);
        break;
    }
    return NULL;
}

Cuando no es necesario mezclar la fuente de audio del mezclador, la Pista de la fuente de audio es hook, TrackBase::track__nop()no hace nada, la función se define (ubica frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp) de la siguiente manera:

void AudioMixerBase::TrackBase::track__nop(int32_t* out __unused,
        size_t outFrameCount __unused, int32_t* temp __unused, int32_t* aux __unused)
{
}

Cuando es necesario volver a muestrear la pista de fuente de audio del mezclador antes de mezclar, y el formato de muestreo de salida de la pista de fuente de audio es, y el número de canales de salida del mezclador es estéreo de dos canales, AUDIO_FORMAT_PCM_16_BITla pista de fuente de audio hookes TrackBase::track__genericResample(), y esto La función se define (en frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp) de la siguiente manera:

void AudioMixerBase::TrackBase::track__genericResample(
        int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
{
    ALOGVV("track__genericResample\n");
    mResampler->setSampleRate(sampleRate);

    // ramp gain - resample to temp buffer and scale/mix in 2nd step
    if (aux != NULL) {
        // always resample with unity gain when sending to auxiliary buffer to be able
        // to apply send level after resampling
        mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
        memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(int32_t));
        mResampler->resample(temp, outFrameCount, bufferProvider);
        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
            volumeRampStereo(out, outFrameCount, temp, aux);
        } else {
            volumeStereo(out, outFrameCount, temp, aux);
        }
    } else {
        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
            mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
            memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
            mResampler->resample(temp, outFrameCount, bufferProvider);
            volumeRampStereo(out, outFrameCount, temp, aux);
        }

        // constant gain
        else {
            mResampler->setVolume(mVolume[0], mVolume[1]);
            mResampler->resample(out, outFrameCount, bufferProvider);
        }
    }
}

void AudioMixerBase::TrackBase::track__nop(int32_t* out __unused,
        size_t outFrameCount __unused, int32_t* temp __unused, int32_t* aux __unused)
{
}

void AudioMixerBase::TrackBase::volumeRampStereo(
        int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
{
    int32_t vl = prevVolume[0];
    int32_t vr = prevVolume[1];
    const int32_t vlInc = volumeInc[0];
    const int32_t vrInc = volumeInc[1];

    //ALOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
    //        t, vlInc/65536.0f, vl/65536.0f, volume[0],
    //       (vl + vlInc*frameCount)/65536.0f, frameCount);

    // ramp volume
    if (CC_UNLIKELY(aux != NULL)) {
        int32_t va = prevAuxLevel;
        const int32_t vaInc = auxInc;
        int32_t l;
        int32_t r;

        do {
            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);
        prevAuxLevel = va;
    } else {
        do {
            *out++ += (vl >> 16) * (*temp++ >> 12);
            *out++ += (vr >> 16) * (*temp++ >> 12);
            vl += vlInc;
            vr += vrInc;
        } while (--frameCount);
    }
    prevVolume[0] = vl;
    prevVolume[1] = vr;
    adjustVolumeRamp(aux != NULL);
}

void AudioMixerBase::TrackBase::volumeStereo(
        int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
{
    const int16_t vl = volume[0];
    const int16_t vr = volume[1];

    if (CC_UNLIKELY(aux != NULL)) {
        const int16_t va = auxLevel;
        do {
            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);
    }
}

TrackBase::track__genericResample()El flujo de procesamiento básico de la función es aproximadamente el siguiente:

  1. Vuelva a muestrear los datos de audio de la pista fuente de audio;
  2. La mezcla se realiza mientras se procesa el volumen. Si hay datos auxiliares, también se procesan al mismo tiempo. El procesamiento de volumen aquí tiene dos significados, es decir, si es necesario realizar una rampa de volumen, se realiza la rampa de volumen, de lo contrario se aplica la ganancia de volumen.

El resampler de Android admite el remuestreo mientras aplica ganancia de volumen y mezcla los datos de audio procesados ​​con los datos de salida. Por lo tanto, si no es necesario aumentar el volumen en la pista fuente de audio y no hay datos auxiliares, el remuestreo, la aplicación de ganancia de volumen y la mezcla se pueden completar directamente con el remuestreador. TrackBase::track__genericResample()La implementación de la función parece tener algún código repetitivo. Si se escribe de una manera que sea más consistente con su flujo de ejecución general, se puede escribir de la siguiente manera:

void AudioMixerBase::TrackBase::track__genericResample(
        int32_t* out, size_t outFrameCount, int32_t* temp, int32_t* aux)
{
    ALOGVV("track__genericResample\n");
    mResampler->setSampleRate(sampleRate);

    float left = mVolume[0];
    float right = mVolume[1];
    int32_t* out_inter = out;
    if (aux != NULL || CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
        left = UNITY_GAIN_FLOAT;
        right = UNITY_GAIN_FLOAT;

        memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(int32_t));
        out_inter = temp;
    }

    mResampler->setVolume(left, right);
    mResampler->resample(out_inter, outFrameCount, bufferProvider);

    if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]) || (aux != NULL && auxInc)) {
        volumeRampStereo(out, outFrameCount, out_inter, aux);
    } else if (aux != NULL) {
        volumeStereo(out, outFrameCount, out_inter, aux);
    }
}

TrackBase::track__genericResample()Las funciones llaman a volumeRampStereo()y volumeStereo()para mezclar los datos remuestreados; la primera se llama cuando es necesario realizar un aumento de volumen y la segunda se llama cuando no es necesario. Los datos AUX se generan a partir de datos de los canales de audio izquierdo y derecho. Estas dos funciones también se pueden escribir de forma más concisa, como sigue:

void AudioMixerBase::TrackBase::volumeRampStereo(
        int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
{
    int32_t vl = prevVolume[0];
    int32_t vr = prevVolume[1];
    const int32_t vlInc = volumeInc[0];
    const int32_t vrInc = volumeInc[1];

    //ALOGD("[0] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
    //        t, vlInc/65536.0f, vl/65536.0f, volume[0],
    //       (vl + vlInc*frameCount)/65536.0f, frameCount);

    // ramp volume
    int32_t va = prevAuxLevel;
    const int32_t vaInc = auxInc;
    int32_t l;
    int32_t r;

    do {
        l = (*temp++ >> 12);
        r = (*temp++ >> 12);
        *out++ += (vl >> 16) * l;
        *out++ += (vr >> 16) * r;
        vl += vlInc;
        vr += vrInc;
        if (CC_UNLIKELY(aux != NULL)) {
            *aux++ += (va >> 17) * (l + r);
            va += vaInc;
        }
    } while (--frameCount);
    prevAuxLevel = va;
    prevVolume[0] = vl;
    prevVolume[1] = vr;
    adjustVolumeRamp(aux != NULL);
}

void AudioMixerBase::TrackBase::volumeStereo(
        int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux)
{
    const int16_t vl = volume[0];
    const int16_t vr = volume[1];
    const int16_t va = auxLevel;
    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;

        if (CC_UNLIKELY(aux != NULL)) {
            int16_t a = (int16_t)(((int32_t)l + r) >> 1);
            aux[0] = mulAdd(a, va, aux[0]);
            aux++;
        }
    } while (--frameCount);
}

Cuando la pista de fuente de audio del mezclador se puede mezclar sin remuestreo y el formato de muestreo de salida de la pista de fuente de audio es AUDIO_FORMAT_PCM_16_BIT, el número de canales de salida de la pista de fuente de audio es mono y el número de canales de salida del mezclador es dos -canal estéreo. , la pista fuente de audio hookes TrackBase::track__16BitsMono(), la función se define (ubica frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp) de la siguiente manera:

void AudioMixerBase::TrackBase::track__16BitsMono(
        int32_t* out, size_t frameCount, int32_t* temp __unused, int32_t* aux)
{
    ALOGVV("track__16BitsMono\n");
    const int16_t *in = static_cast<int16_t const *>(mIn);

    if (CC_UNLIKELY(aux != NULL)) {
        // ramp gain
        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
            int32_t vl = prevVolume[0];
            int32_t vr = prevVolume[1];
            int32_t va = prevAuxLevel;
            const int32_t vlInc = volumeInc[0];
            const int32_t vrInc = volumeInc[1];
            const int32_t vaInc = auxInc;

            // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
            //         t, vlInc/65536.0f, vl/65536.0f, volume[0],
            //         (vl + vlInc*frameCount)/65536.0f, 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);

            prevVolume[0] = vl;
            prevVolume[1] = vr;
            prevAuxLevel = va;
            adjustVolumeRamp(true);
        }
        // constant gain
        else {
            const int16_t vl = volume[0];
            const int16_t vr = volume[1];
            const int16_t va = (int16_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 (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
            int32_t vl = prevVolume[0];
            int32_t vr = prevVolume[1];
            const int32_t vlInc = volumeInc[0];
            const int32_t vrInc = volumeInc[1];

            // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
            //         t, vlInc/65536.0f, vl/65536.0f, 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);

            prevVolume[0] = vl;
            prevVolume[1] = vr;
            adjustVolumeRamp(false);
        }
        // constant gain
        else {
            const int16_t vl = volume[0];
            const int16_t vr = 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);
        }
    }
    mIn = in;
}

Esta función maneja principalmente cuatro situaciones:

  1. Es necesario generar datos auxiliares y realizar un procesamiento de rampa de volumen;
  2. Es necesario generar datos auxiliares y no es necesario realizar un procesamiento de rampa de volumen;
  3. No es necesario generar datos auxiliares y es necesario realizar un procesamiento de rampa de volumen;
  4. No es necesario generar datos auxiliares ni realizar ningún procesamiento de rampa de volumen.

Esta función también se puede escribir de una manera más concisa y clara, de la siguiente manera: Esta

void AudioMixerBase::TrackBase::track__16BitsMono(
        int32_t* out, size_t frameCount, int32_t* temp , int32_t* aux)
{
    ALOGVV("track__16BitsMono\n");
    const int16_t *in = static_cast<int16_t const *>(mIn);

    // ramp gain
    if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]) || (CC_UNLIKELY(aux != NULL && auxInc))) {
        int32_t vl = prevVolume[0];
        int32_t vr = prevVolume[1];
        int32_t va = prevAuxLevel;
        const int32_t vlInc = volumeInc[0];
        const int32_t vrInc = volumeInc[1];
        const int32_t vaInc = auxInc;

        // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
        //         t, vlInc/65536.0f, vl/65536.0f, 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;
            if (aux != NULL) {
                *aux++ += (va >> 16) * l;
                va += vaInc;
            }
        } while (--frameCount);

        prevVolume[0] = vl;
        prevVolume[1] = vr;
        prevAuxLevel = va;
        adjustVolumeRamp(true);
    }
    // constant gain
    else {
        const int16_t vl = volume[0];
        const int16_t vr = volume[1];
        const int16_t va = (int16_t)auxLevel;
        do {
            int16_t l = *in++;
            out[0] = mulAdd(l, vl, out[0]);
            out[1] = mulAdd(l, vr, out[1]);
            out += 2;
            if (aux != NULL) {
                aux[0] = mulAdd(l, va, aux[0]);
                aux++;
            }
        } while (--frameCount);
    }
    mIn = in;
}

Es decir, si es necesario ejecutar la rampa de volumen es la dimensión de división lógica principal y si es necesario generar datos AUX es la dimensión de división lógica secundaria.

Cuando la pista de fuente de audio del mezclador se puede mezclar sin remuestreo y el formato de muestreo de salida de la pista de fuente de audio es AUDIO_FORMAT_PCM_16_BIT, el número de canales de salida de la pista de fuente de audio es estéreo de dos canales y el número de canales de salida de la pista de fuente de audio es estéreo de dos canales. El mezclador es estéreo de dos canales. Cuando , la pista de fuente de audio hookes TrackBase::track__16BitsStereo(), y la función se define (ubica frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp) de la siguiente manera:

void AudioMixerBase::TrackBase::track__16BitsStereo(
        int32_t* out, size_t frameCount, int32_t* temp __unused, int32_t* aux)
{
    ALOGVV("track__16BitsStereo\n");
    const int16_t *in = static_cast<const int16_t *>(mIn);

    if (CC_UNLIKELY(aux != NULL)) {
        int32_t l;
        int32_t r;
        // ramp gain
        if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]|auxInc)) {
            int32_t vl = prevVolume[0];
            int32_t vr = prevVolume[1];
            int32_t va = prevAuxLevel;
            const int32_t vlInc = volumeInc[0];
            const int32_t vrInc = volumeInc[1];
            const int32_t vaInc = auxInc;
            // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
            //        t, vlInc/65536.0f, vl/65536.0f, 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);

            prevVolume[0] = vl;
            prevVolume[1] = vr;
            prevAuxLevel = va;
            adjustVolumeRamp(true);
        }

        // constant gain
        else {
            const uint32_t vrl = volumeRL;
            const int16_t va = (int16_t)auxLevel;
            do {
                uint32_t rl = *reinterpret_cast<const uint32_t *>(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 (CC_UNLIKELY(volumeInc[0]|volumeInc[1])) {
            int32_t vl = prevVolume[0];
            int32_t vr = prevVolume[1];
            const int32_t vlInc = volumeInc[0];
            const int32_t vrInc = volumeInc[1];

            // ALOGD("[1] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
            //        t, vlInc/65536.0f, vl/65536.0f, 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);

            prevVolume[0] = vl;
            prevVolume[1] = vr;
            adjustVolumeRamp(false);
        }

        // constant gain
        else {
            const uint32_t vrl = volumeRL;
            do {
                uint32_t rl = *reinterpret_cast<const uint32_t *>(in);
                in += 2;
                out[0] = mulAddRL(1, rl, vrl, out[0]);
                out[1] = mulAddRL(0, rl, vrl, out[1]);
                out += 2;
            } while (--frameCount);
        }
    }
    mIn = in;
}

TrackBase::track__16BitsStereo()La función es aproximadamente la misma que la función anterior TrackBase::track__16BitsMono(), pero existen dos diferencias entre ellas:

  1. Los dos canales de datos en los datos de audio de salida provienen de diferentes fuentes. Para el primero, los datos de los dos canales de datos de audio de salida provienen de los datos de diferentes canales en los datos de entrada. El segundo copia los datos de un solo canal en los datos de entrada y lo expande en la salida.Datos de doble canal en los datos;
  2. El primero utiliza una operación en algunas plataformas que puede utilizar instrucciones de la máquina para optimizar el rendimiento cuando no es necesario realizar una operación de rampa de volumen mulAddRL(), pero esta operación también se puede utilizar cuando es necesario realizar una operación de rampa de volumen .

Parece que no es imposible fusionar TrackBase::track__16BitsStereo()las funciones y en una sola función, como se muestra a continuación, la versión que no utiliza la operación:TrackBase::track__16BitsMono()mulAddRL()

template<bool stereo>
void AudioMixerBase::TrackBase::track__16Bits(
        int32_t* out, size_t frameCount, int32_t* temp , int32_t* aux)
{
    ALOGVV("track__16BitsMono\n");
    const int16_t *in = static_cast<int16_t const *>(mIn);

    // ramp gain
    if (CC_UNLIKELY(volumeInc[0]|volumeInc[1]) || (CC_UNLIKELY(aux != NULL && auxInc))) {
        int32_t l;
        int32_t r;

        int32_t vl = prevVolume[0];
        int32_t vr = prevVolume[1];
        int32_t va = prevAuxLevel;
        const int32_t vlInc = volumeInc[0];
        const int32_t vrInc = volumeInc[1];
        const int32_t vaInc = auxInc;

        // ALOGD("[2] %p: inc=%f, v0=%f, v1=%d, final=%f, count=%d",
        //         t, vlInc/65536.0f, vl/65536.0f, volume[0],
        //         (vl + vlInc*frameCount)/65536.0f, frameCount);

        do {
            l = *in++;
            if (stereo) {
                r = *in++;
            } else {
                r = l;
            }
            *out++ += (vl >> 16) * l;
            *out++ += (vr >> 16) * r;
            vl += vlInc;
            vr += vrInc;
            if (aux != NULL) {
                if (stereo) {
                    *aux++ += (va >> 17) * (l + r);;
                } else {
                    *aux++ += (va >> 16) * l;
                }
                va += vaInc;
            }
        } while (--frameCount);

        prevVolume[0] = vl;
        prevVolume[1] = vr;
        prevAuxLevel = va;
        adjustVolumeRamp(true);
    }
    // constant gain
    else {
        const int16_t vl = volume[0];
        const int16_t vr = volume[1];
        const int16_t va = (int16_t)auxLevel;
        int16_t l;
        int16_t r;
        do {
            l = *in++;
            if (stereo) {
                r = *in++;
            } else {
                r = l;
            }
            out[0] = mulAdd(l, vl, out[0]);
            out[1] = mulAdd(r, vr, out[1]);
            out += 2;
            if (aux != NULL) {
                if (stereo) {
                    aux[0] = mulAdd((l + r) >> 1, va, aux[0]);
                } else {
                    aux[0] = mulAdd(l, va, aux[0]);
                }
                aux++;
            }
        } while (--frameCount);
    }

    mIn = in;
}

Las funciones de operación de mezcla de varias pistas fuente de audio anteriores están disponibles cuando no se fuerza el uso de un nuevo mezclador. Cuando se fuerza el uso de un nuevo mezclador, o los parámetros de configuración del mezclador y la pista fuente de audio son diferentes de los enumerados anteriormente, principalmente dependiendo de si se requiere mezcla, las funciones de plantilla y se usan respectivamente. Las definiciones de estas AudioMixerBase::TrackBase::track__Resample()dos AudioMixerBase::TrackBase::track__NoResample()funciones (ubicados frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp) son los siguientes:

/* This track hook is called to do resampling then mixing,
 * pulling from the track's upstream AudioBufferProvider.
 *
 * MIXTYPE     (see AudioMixerOps.h MIXTYPE_* enumeration)
 * TO: int32_t (Q4.27) or float
 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
 * TA: int32_t (Q4.27) or float
 */
template <int MIXTYPE, typename TO, typename TI, typename TA>
void AudioMixerBase::TrackBase::track__Resample(TO* out, size_t outFrameCount, TO* temp, TA* aux)
{
    ALOGVV("track__Resample\n");
    mResampler->setSampleRate(sampleRate);
    const bool ramp = needsRamp();
    if (MIXTYPE == MIXTYPE_MONOEXPAND || MIXTYPE == MIXTYPE_STEREOEXPAND // custom volume handling
            || ramp || aux != NULL) {
        // if ramp:        resample with unity gain to temp buffer and scale/mix in 2nd step.
        // if aux != NULL: resample with unity gain to temp buffer then apply send level.

        mResampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
        memset(temp, 0, outFrameCount * mMixerChannelCount * sizeof(TO));
        mResampler->resample((int32_t*)temp, outFrameCount, bufferProvider);

        volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
                out, outFrameCount, temp, aux, ramp);

    } else { // constant volume gain
        mResampler->setVolume(mVolume[0], mVolume[1]);
        mResampler->resample((int32_t*)out, outFrameCount, bufferProvider);
    }
}

/* This track hook is called to mix a track, when no resampling is required.
 * The input buffer should be present in in.
 *
 * MIXTYPE     (see AudioMixerOps.h MIXTYPE_* enumeration)
 * TO: int32_t (Q4.27) or float
 * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
 * TA: int32_t (Q4.27) or float
 */
template <int MIXTYPE, typename TO, typename TI, typename TA>
void AudioMixerBase::TrackBase::track__NoResample(
        TO* out, size_t frameCount, TO* temp __unused, TA* aux)
{
    ALOGVV("track__NoResample\n");
    const TI *in = static_cast<const TI *>(mIn);

    volumeMix<MIXTYPE, std::is_same_v<TI, float> /* USEFLOATVOL */, true /* ADJUSTVOL */>(
            out, frameCount, in, aux, needsRamp());

    // MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
    // MIXTYPE_MULTI reads NCHAN input channels and places to NCHAN output channels.
    in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * mMixerChannelCount;
    mIn = in;
}

AudioMixerBase::TrackBase::track__Resample()La función es AudioMixerBase::TrackBase::track__genericResample()algo similar a , pero la primera es una implementación más general.

Modificar o configurar el mezclador y sus parámetros de fuente de audio.

AudioMixerBasey AudioMixerambos proporcionan setParameter()funciones para modificar o configurar los parámetros del mezclador y su fuente de audio.La AudioMixer::setParameter()definición de la función (ubicada frameworks/av/media/libaudioprocessing/AudioMixer.cpp) es la siguiente:

void AudioMixer::setParameter(int name, int target, int param, void *value)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    const std::shared_ptr<Track> &track = getTrack(name);

    int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
    int32_t *valueBuf = reinterpret_cast<int32_t*>(value);

    switch (target) {

    case TRACK:
        switch (param) {
        case CHANNEL_MASK: {
            const audio_channel_mask_t trackChannelMask =
                static_cast<audio_channel_mask_t>(valueInt);
            if (setChannelMasks(name, trackChannelMask,
                    static_cast<audio_channel_mask_t>(
                            track->mMixerChannelMask | track->mMixerHapticChannelMask))) {
                ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask);
                invalidate();
            }
            } break;
        case MAIN_BUFFER:
            if (track->mainBuffer != valueBuf) {
                track->mainBuffer = valueBuf;
                ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
                if (track->mKeepContractedChannels) {
                    track->prepareForAdjustChannels(mFrameCount);
                }
                invalidate();
            }
            break;
        case AUX_BUFFER:
            AudioMixerBase::setParameter(name, target, param, value);
            break;
        case FORMAT: {
            audio_format_t format = static_cast<audio_format_t>(valueInt);
            if (track->mFormat != format) {
                ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
                track->mFormat = format;
                ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
                track->prepareForReformat();
                invalidate();
            }
            } break;
        // FIXME do we want to support setting the downmix type from AudioFlinger?
        //         for a specific track? or per mixer?
        /* case DOWNMIX_TYPE:
            break          */
        case MIXER_FORMAT: {
            audio_format_t format = static_cast<audio_format_t>(valueInt);
            if (track->mMixerFormat != format) {
                track->mMixerFormat = format;
                ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
                if (track->mKeepContractedChannels) {
                    track->prepareForAdjustChannels(mFrameCount);
                }
            }
            } break;
        case MIXER_CHANNEL_MASK: {
            const audio_channel_mask_t mixerChannelMask =
                    static_cast<audio_channel_mask_t>(valueInt);
            if (setChannelMasks(name, static_cast<audio_channel_mask_t>(
                                    track->channelMask | track->mHapticChannelMask),
                    mixerChannelMask)) {
                ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask);
                invalidate();
            }
            } break;
        case HAPTIC_ENABLED: {
            const bool hapticPlaybackEnabled = static_cast<bool>(valueInt);
            if (track->mHapticPlaybackEnabled != hapticPlaybackEnabled) {
                track->mHapticPlaybackEnabled = hapticPlaybackEnabled;
                track->mKeepContractedChannels = hapticPlaybackEnabled;
                track->prepareForAdjustChannels(mFrameCount);
            }
            } break;
        case HAPTIC_INTENSITY: {
            const os::HapticScale hapticIntensity = static_cast<os::HapticScale>(valueInt);
            if (track->mHapticIntensity != hapticIntensity) {
                track->mHapticIntensity = hapticIntensity;
            }
            } break;
        case HAPTIC_MAX_AMPLITUDE: {
            const float hapticMaxAmplitude = *reinterpret_cast<float*>(value);
            if (track->mHapticMaxAmplitude != hapticMaxAmplitude) {
                track->mHapticMaxAmplitude = hapticMaxAmplitude;
            }
            } break;
        default:
            LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
        }
        break;

    case RESAMPLE:
    case RAMP_VOLUME:
    case VOLUME:
        AudioMixerBase::setParameter(name, target, param, value);
        break;
    case TIMESTRETCH:
        switch (param) {
        case PLAYBACK_RATE: {
            const AudioPlaybackRate *playbackRate =
                    reinterpret_cast<AudioPlaybackRate*>(value);
            ALOGW_IF(!isAudioPlaybackRateValid(*playbackRate),
                    "bad parameters speed %f, pitch %f",
                    playbackRate->mSpeed, playbackRate->mPitch);
            if (track->setPlaybackRate(*playbackRate)) {
                ALOGV("setParameter(TIMESTRETCH, PLAYBACK_RATE, STRETCH_MODE, FALLBACK_MODE "
                        "%f %f %d %d",
                        playbackRate->mSpeed,
                        playbackRate->mPitch,
                        playbackRate->mStretchMode,
                        playbackRate->mFallbackMode);
                // invalidate();  (should not require reconfigure)
            }
        } break;
        default:
            LOG_ALWAYS_FATAL("setParameter timestretch: bad param %d", param);
        }
        break;

    default:
        LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
    }
}

AudioMixer::setParameter()Los parámetros admitidos por la función incluyen los siguientes:

  • PISTA
    • CHANNEL_MASK : establece el formato de máscara de canal para una única fuente de audio del mezclador;
    • MAIN_BUFFER : establece el búfer de datos de salida de mezcla para una sola fuente de audio del mezclador. Este valor no debe pertenecer a una sola fuente de audio ;
    • AUX_BUFFER : AudioMixerBase::setParameter()Manejado por;
    • FORMATO : establece el formato de muestreo para una única fuente de audio del mezclador;
    • MIXER_FORMAT : establece el formato de muestreo del mezclador para una sola fuente de audio del mezclador. Este valor no debe pertenecer a una sola fuente de audio ;
    • MIXER_CHANNEL_MASK : establece el formato de máscara de canal del mezclador para una sola fuente de audio del mezclador. Este valor no debe pertenecer a una sola fuente de audio ;
    • HAPTIC_ENABLED : establece un interruptor háptico para una única fuente de audio del mezclador;
    • HAPTIC_INTENSITY : establece la intensidad háptica para una única fuente de audio en el mezclador;
    • HAPTIC_MAX_AMPLITUDE : establece la amplitud háptica máxima para una única fuente de audio en el mezclador;
  • RESAMPLE / RAMP_VOLUME / VOLUME : AudioMixerBase::setParameter()Manejado por;
  • ESTIRAMIENTO DEL TIEMPO
    • PLAYBACK_RATE : establece la velocidad de reproducción para las fuentes de audio individuales del mezclador.

AudioMixerBase::setParameter()La definición de la función (ubicada frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp) es la siguiente:

void AudioMixerBase::setParameter(int name, int target, int param, void *value)
{
    LOG_ALWAYS_FATAL_IF(!exists(name), "invalid name: %d", name);
    const std::shared_ptr<TrackBase> &track = mTracks[name];

    int valueInt = static_cast<int>(reinterpret_cast<uintptr_t>(value));
    int32_t *valueBuf = reinterpret_cast<int32_t*>(value);

    switch (target) {

    case TRACK:
        switch (param) {
        case CHANNEL_MASK: {
            const audio_channel_mask_t trackChannelMask =
                static_cast<audio_channel_mask_t>(valueInt);
            if (setChannelMasks(name, trackChannelMask, track->mMixerChannelMask)) {
                ALOGV("setParameter(TRACK, CHANNEL_MASK, %x)", trackChannelMask);
                invalidate();
            }
            } break;
        case MAIN_BUFFER:
            if (track->mainBuffer != valueBuf) {
                track->mainBuffer = valueBuf;
                ALOGV("setParameter(TRACK, MAIN_BUFFER, %p)", valueBuf);
                invalidate();
            }
            break;
        case AUX_BUFFER:
            if (track->auxBuffer != valueBuf) {
                track->auxBuffer = valueBuf;
                ALOGV("setParameter(TRACK, AUX_BUFFER, %p)", valueBuf);
                invalidate();
            }
            break;
        case FORMAT: {
            audio_format_t format = static_cast<audio_format_t>(valueInt);
            if (track->mFormat != format) {
                ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format);
                track->mFormat = format;
                ALOGV("setParameter(TRACK, FORMAT, %#x)", format);
                invalidate();
            }
            } break;
        case MIXER_FORMAT: {
            audio_format_t format = static_cast<audio_format_t>(valueInt);
            if (track->mMixerFormat != format) {
                track->mMixerFormat = format;
                ALOGV("setParameter(TRACK, MIXER_FORMAT, %#x)", format);
            }
            } break;
        case MIXER_CHANNEL_MASK: {
            const audio_channel_mask_t mixerChannelMask =
                    static_cast<audio_channel_mask_t>(valueInt);
            if (setChannelMasks(name, track->channelMask, mixerChannelMask)) {
                ALOGV("setParameter(TRACK, MIXER_CHANNEL_MASK, %#x)", mixerChannelMask);
                invalidate();
            }
            } break;
        default:
            LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
        }
        break;

    case RESAMPLE:
        switch (param) {
        case SAMPLE_RATE:
            ALOG_ASSERT(valueInt > 0, "bad sample rate %d", valueInt);
            if (track->setResampler(uint32_t(valueInt), mSampleRate)) {
                ALOGV("setParameter(RESAMPLE, SAMPLE_RATE, %u)",
                        uint32_t(valueInt));
                invalidate();
            }
            break;
        case RESET:
            track->resetResampler();
            invalidate();
            break;
        case REMOVE:
            track->mResampler.reset(nullptr);
            track->sampleRate = mSampleRate;
            invalidate();
            break;
        default:
            LOG_ALWAYS_FATAL("setParameter resample: bad param %d", param);
        }
        break;

    case RAMP_VOLUME:
    case VOLUME:
        switch (param) {
        case AUXLEVEL:
            if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
                    target == RAMP_VOLUME ? mFrameCount : 0,
                    &track->auxLevel, &track->prevAuxLevel, &track->auxInc,
                    &track->mAuxLevel, &track->mPrevAuxLevel, &track->mAuxInc)) {
                ALOGV("setParameter(%s, AUXLEVEL: %04x)",
                        target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track->auxLevel);
                invalidate();
            }
            break;
        default:
            if ((unsigned)param >= VOLUME0 && (unsigned)param < VOLUME0 + MAX_NUM_VOLUMES) {
                if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
                        target == RAMP_VOLUME ? mFrameCount : 0,
                        &track->volume[param - VOLUME0],
                        &track->prevVolume[param - VOLUME0],
                        &track->volumeInc[param - VOLUME0],
                        &track->mVolume[param - VOLUME0],
                        &track->mPrevVolume[param - VOLUME0],
                        &track->mVolumeInc[param - VOLUME0])) {
                    ALOGV("setParameter(%s, VOLUME%d: %04x)",
                            target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0,
                                    track->volume[param - VOLUME0]);
                    invalidate();
                }
            } else {
                LOG_ALWAYS_FATAL("setParameter volume: bad param %d", param);
            }
        }
        break;

    default:
        LOG_ALWAYS_FATAL("setParameter: bad target %d", target);
    }
}

AudioMixerBase::setParameter()Los parámetros admitidos por la función incluyen los siguientes:

  • PISTA
    • CHANNEL_MASK / MAIN_BUFFER / FORMAT / MIXER_FORMAT / MIXER_CHANNEL_MASK : AudioMixer::setParameter()Estos parámetros también se procesan en la función ;
    • AUX_BUFFER : establece el búfer de datos auxiliar para una única fuente de audio del mezclador;
  • VOLVER A MUESTRAR
    • SAMPLE_RATE : establece la frecuencia de muestreo de entrada del resampler para una única fuente de audio del mezclador, que también es la frecuencia de muestreo de la fuente de audio en sí, y la frecuencia de muestreo de salida es la frecuencia de muestreo de salida del mezclador;
    • RESET : Restablece el resampler del mezclador para una sola fuente de audio;
    • REMOVE : Elimina el remuestreador de una única fuente de audio del mezclador;
  • RAMPA_VOLUMEN / VOLUMEN
    • AUXLEVEL : Establece el volumen de datos auxiliares para una única fuente de audio del mezclador;
    • predeterminado : establece el volumen para una única fuente de audio del mezclador.

Estos parámetros, independientemente de si pertenecen lógicamente a una única fuente de audio, están todos configurados para una única fuente de audio. La modificación de algunos parámetros de una única fuente de audio, como el formato de máscara de canal, el formato de muestreo y la reproducción a doble velocidad, puede desencadenar la reconfiguración del proceso de procesamiento de datos de audio de una única fuente de audio. Los cambios en muchos parámetros harán que el mezclador invalidate()ejecute y reinicie el proceso de mezcla, incluida la agrupación de fuentes de audio, etc.

Aquí se presenta un vistazo más de cerca a la configuración de la frecuencia de muestreo de entrada del resampler para una sola fuente de audio en el mezclador. Esta configuración es AudioMixerBase::TrackBase::setResampler()manejada por la función, que se define (ubicada frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp) de la siguiente manera:

bool AudioMixerBase::TrackBase::setResampler(uint32_t trackSampleRate, uint32_t devSampleRate)
{
    if (trackSampleRate != devSampleRate || mResampler.get() != nullptr) {
        if (sampleRate != trackSampleRate) {
            sampleRate = trackSampleRate;
            if (mResampler.get() == nullptr) {
                ALOGV("Creating resampler from track %d Hz to device %d Hz",
                        trackSampleRate, devSampleRate);
                AudioResampler::src_quality quality;
                // force lowest quality level resampler if use case isn't music or video
                // FIXME this is flawed for dynamic sample rates, as we choose the resampler
                // quality level based on the initial ratio, but that could change later.
                // Should have a way to distinguish tracks with static ratios vs. dynamic ratios.
                if (isMusicRate(trackSampleRate)) {
                    quality = AudioResampler::DEFAULT_QUALITY;
                } else {
                    quality = AudioResampler::DYN_LOW_QUALITY;
                }

                // TODO: Remove MONO_HACK. Resampler sees #channels after the downmixer
                // but if none exists, it is the channel count (1 for mono).
                const int resamplerChannelCount = getOutputChannelCount();
                ALOGVV("Creating resampler:"
                        " format(%#x) channels(%d) devSampleRate(%u) quality(%d)\n",
                        mMixerInFormat, resamplerChannelCount, devSampleRate, quality);
                mResampler.reset(AudioResampler::create(
                        mMixerInFormat,
                        resamplerChannelCount,
                        devSampleRate, quality));
            }
            return true;
        }
    }
    return false;
}

AudioMixerBase::TrackBase::setResampler()La función recibe dos parámetros, uno es la frecuencia de muestreo de la fuente de audio y el otro es la frecuencia de muestreo del dispositivo, que es la frecuencia de muestreo de salida del resampler. AudioMixerBase::TrackBase::setResampler()Cuando se llama a la función, la frecuencia de muestreo del dispositivo pasada es siempre la frecuencia de muestreo de salida del mezclador. Para un mezclador específico, permanecerá sin cambios, lo que significa que el AudioMixerBase::TrackBase::setResampler()parámetro de frecuencia de muestreo del dispositivo de la función será un valor fijo.

Se requiere un remuestreador cuando la frecuencia de muestreo de la fuente de audio y la frecuencia de muestreo del dispositivo son diferentes. Si el resampler no existe, se creará el resampler. Al crear un remuestreador, solo necesita pasar la frecuencia de muestreo de salida, no la frecuencia de muestreo de entrada. Al crear un resampler, el parámetro de calidad de entrada se calcula en función de la frecuencia de muestreo de la fuente de audio, es decir, la frecuencia de muestreo de entrada. Al crear un resampler, el número de canales de datos de audio de entrada se obtiene de la configuración de la fuente de audio.

Para la frecuencia de muestreo establecida de la fuente de audio, simplemente realice una grabación simple después de crear el remuestreador. Durante el procesamiento de la operación de mezcla, AudioMixerBase::TrackBasela función relacionada establece la frecuencia de muestreo de entrada para el resampler.

AudioMixerBase::TrackBase::setResampler()Hay algunos problemas con la implementación de la función:

  1. Desde el punto de vista de la interfaz, parámetros como el número de canal y la calidad pueden cambiar después de crear el remuestreador, pero después de crear el remuestreador de Android, estos parámetros no cambiarán;
  2. Pasar un parámetro de frecuencia de muestreo del dispositivo básicamente fijo parece bastante aburrido.

Además, el remuestreo es una operación que no se puede finalizar una vez iniciada. Si la frecuencia de muestreo de la fuente de audio es inicialmente diferente de la frecuencia de muestreo del dispositivo, pero luego llega a ser la misma que la frecuencia de muestreo del dispositivo, el resampler no se destruirá en este momento.

La configuración del volumen setVolumeRampVariables()se realiza mediante la función, que se define (ubica frameworks/av/media/libaudioprocessing/AudioMixerBase.cpp) de la siguiente manera:

static inline bool setVolumeRampVariables(float newVolume, int32_t ramp,
        int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc,
        float *pSetVolume, float *pPrevVolume, float *pVolumeInc) {
    // check floating point volume to see if it is identical to the previously
    // set volume.
    // We do not use a tolerance here (and reject changes too small)
    // as it may be confusing to use a different value than the one set.
    // If the resulting volume is too small to ramp, it is a direct set of the volume.
    if (newVolume == *pSetVolume) {
        return false;
    }
    if (newVolume < 0) {
        newVolume = 0; // should not have negative volumes
    } else {
        switch (fpclassify(newVolume)) {
        case FP_SUBNORMAL:
        case FP_NAN:
            newVolume = 0;
            break;
        case FP_ZERO:
            break; // zero volume is fine
        case FP_INFINITE:
            // Infinite volume could be handled consistently since
            // floating point math saturates at infinities,
            // but we limit volume to unity gain float.
            // ramp = 0; break;
            //
            newVolume = AudioMixerBase::UNITY_GAIN_FLOAT;
            break;
        case FP_NORMAL:
        default:
            // Floating point does not have problems with overflow wrap
            // that integer has.  However, we limit the volume to
            // unity gain here.
            // TODO: Revisit the volume limitation and perhaps parameterize.
            if (newVolume > AudioMixerBase::UNITY_GAIN_FLOAT) {
                newVolume = AudioMixerBase::UNITY_GAIN_FLOAT;
            }
            break;
        }
    }

    // set floating point volume ramp
    if (ramp != 0) {
        // when the ramp completes, *pPrevVolume is set to *pSetVolume, so there
        // is no computational mismatch; hence equality is checked here.
        ALOGD_IF(*pPrevVolume != *pSetVolume, "previous float ramp hasn't finished,"
                " prev:%f  set_to:%f", *pPrevVolume, *pSetVolume);
        const float inc = (newVolume - *pPrevVolume) / ramp; // could be inf, nan, subnormal
        // could be inf, cannot be nan, subnormal
        const float maxv = std::max(newVolume, *pPrevVolume);

        if (isnormal(inc) // inc must be a normal number (no subnormals, infinite, nan)
                && maxv + inc != maxv) { // inc must make forward progress
            *pVolumeInc = inc;
            // ramp is set now.
            // Note: if newVolume is 0, then near the end of the ramp,
            // it may be possible that the ramped volume may be subnormal or
            // temporarily negative by a small amount or subnormal due to floating
            // point inaccuracies.
        } else {
            ramp = 0; // ramp not allowed
        }
    }

    // compute and check integer volume, no need to check negative values
    // The integer volume is limited to "unity_gain" to avoid wrapping and other
    // audio artifacts, so it never reaches the range limit of U4.28.
    // We safely use signed 16 and 32 bit integers here.
    const float scaledVolume = newVolume * AudioMixerBase::UNITY_GAIN_INT; // not neg, subnormal, nan
    const int32_t intVolume = (scaledVolume >= (float)AudioMixerBase::UNITY_GAIN_INT) ?
            AudioMixerBase::UNITY_GAIN_INT : (int32_t)scaledVolume;

    // set integer volume ramp
    if (ramp != 0) {
        // integer volume is U4.12 (to use 16 bit multiplies), but ramping uses U4.28.
        // when the ramp completes, *pIntPrevVolume is set to *pIntSetVolume << 16, so there
        // is no computational mismatch; hence equality is checked here.
        ALOGD_IF(*pIntPrevVolume != *pIntSetVolume << 16, "previous int ramp hasn't finished,"
                " prev:%d  set_to:%d", *pIntPrevVolume, *pIntSetVolume << 16);
        const int32_t inc = ((intVolume << 16) - *pIntPrevVolume) / ramp;

        if (inc != 0) { // inc must make forward progress
            *pIntVolumeInc = inc;
        } else {
            ramp = 0; // ramp not allowed
        }
    }

    // if no ramp, or ramp not allowed, then clear float and integer increments
    if (ramp == 0) {
        *pVolumeInc = 0;
        *pPrevVolume = newVolume;
        *pIntVolumeInc = 0;
        *pIntPrevVolume = intVolume << 16;
    }
    *pSetVolume = newVolume;
    *pIntSetVolume = intVolume;
    return true;
}

El usuario del mezclador establece el valor de ganancia de volumen como flotante. Esta función maneja la rampa de volumen y genera un valor de volumen entero.

por fin

Regrese y lea las preguntas al principio del artículo y respóndalas según el contenido del artículo.

¿Cómo representar los datos de la fuente de audio que se van a mezclar?
El mezclador de Android usa AudioMixerBase::TrackBase/ AudioMixer::Trackpara representar los datos de la fuente de audio que se van a mezclar.

¿Cómo configurar la fuente de datos de audio para mezclar una fuente de audio?
La fuente de datos de audio de una fuente de audio que se va a mezclar está AudioBufferProviderrepresentada por y AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider)la función puede configurar la fuente de datos de audio para una fuente de audio que se va a mezclar.

¿Cómo configurar el búfer para la salida de datos después de la mezcla o cómo obtener los datos después de la mezcla?
El búfer para la salida de datos después de la mezcla se llama búfer principal. AudioMixer::setParameter(int name, int target, int param, void *value)Puede configurar el búfer principal para que se mezcle la fuente de audio. Desde setParameter()el punto de vista de la interfaz, el búfer principal parece ser específico para una fuente de audio que se va a mezclar, pero en realidad puede ser para configurar el mismo búfer principal para que se mezclen múltiples fuentes de audio. Se mezclarán fuentes de audio con el mismo búfer principal. El usuario del mezclador mantiene el búfer principal. Después de que el usuario del mezclador conduce el mezclador para realizar la mezcla, lee los datos del búfer principal para su posterior procesamiento, como enviarlos al dispositivo para su reproducción.

¿Cómo determinar el formato de datos de salida y la configuración del mezclador, es decir, los establece un usuario externo a través de la interfaz proporcionada o se calcula dinámicamente en función del formato de datos y la configuración de cada fuente de audio?
Esta información se establece a través de varias interfaces de configuración. Por ejemplo, la frecuencia de muestreo AudioMixerse pasa cuando se construye el objeto. El número de canales de salida y el formato de muestreo del mezclador son similares al procesamiento del búfer principal y se configuran según la fuente de audio que se va a mezclar.

Cuando el formato de datos y la configuración de la fuente de audio son diferentes del formato y la configuración de los datos de salida, ¿cómo convertir el formato y la configuración de los datos?
AudioMixer::TrackSe mantiene un proceso de procesamiento de datos de audio para realizar una variedad de procesamiento de datos de audio, incluida la conversión de formato de datos de audio, limitación de volumen y reproducción de velocidad variable. El remuestreo no lo AudioMixer::Trackmaneja la canalización de procesamiento de datos de audio, pero tiene un remuestreo para manejar el remuestreo.

¿Cómo agregar o crear una fuente de audio al mezclador?
AudioMixerBase::create()Las funciones se pueden utilizar para crear y agregar una fuente de audio al mezclador.

¿Cómo modificar los parámetros de una fuente de audio del mezclador?
AudioMixerBase::TrackBase/ AudioMixer::TrackProporciona relativamente pocas interfaces, generalmente configuradas a través de AudioMixerBase::setParameter(int name, int target, int param, void *value)funciones y AudioMixer::setParameter(int name, int target, int param, void *value)funciones. Estos componentes del mezclador de Android no están bien empaquetados o incluso mal encapsulados.

¿Cómo eliminar una fuente de audio del mezclador?
AudioMixerBase::disable(int name)Esta función desactiva una fuente de audio sin quitarla del mezclador. AudioMixerBase::destroy(int name)Esta función elimina una fuente de audio del mezclador.

Al mezclar, ¿qué debo hacer si los datos después de la mezcla cruzan el límite?
Límite simple.

¿Cómo se conduce la operación de mezcla? ¿El mezclador inicia un hilo interno para realizar la operación de mezcla y arroja el resultado a través de la devolución de llamada, o el usuario llama activamente a la operación de mezcla?
AudioMixerBaseLa clase tiene una process()función y el usuario del mezclador llama a este método para controlar la ejecución de la operación de mezcla. Después de ejecutar esta función, el usuario del mezclador obtiene los datos mezclados del búfer principal.

El mezclador de Android puede realizar múltiples conjuntos de operaciones de mezcla al mismo tiempo, es decir, puede producir múltiples salidas al mismo tiempo.

Hecho.

Supongo que te gusta

Origin blog.csdn.net/tq08g2z/article/details/130114227
Recomendado
Clasificación