Merge changes I52ec5b02,I376656e3,I3f005583,Icba4e33d,I78400f6c, ...

* changes:
  Use PassthruPatchRecord for DIRECT to DIRECT connections
  Add PassthruPatchRecord for low latency software patches
  Abstract access to HAL stream via Source in RecordThread
  Move PlaybackThread::Track::writeFrames to PatchRecord
  audioflinger: Add tracing of buffer frames to PatchTrack/Record
  libaudioprocessing: Extract vendor-available part of AudioMixer
  AudioMixer: Cleanups
  libaudioprocessing: Trivial dependency cleanups
gugelfrei
Treehugger Robot 5 years ago committed by Gerrit Code Review
commit d7a1506715

@ -9,6 +9,7 @@ LOCAL_SHARED_LIBRARIES := \
libaaudioservice \
libaudioflinger \
libaudiopolicyservice \
libaudioprocessing \
libbinder \
libcutils \
liblog \

@ -18,87 +18,38 @@
#ifndef ANDROID_AUDIO_MIXER_H
#define ANDROID_AUDIO_MIXER_H
#include <map>
#include <pthread.h>
#include <sstream>
#include <stdint.h>
#include <sys/types.h>
#include <unordered_map>
#include <vector>
#include <android/os/IExternalVibratorService.h>
#include <media/AudioBufferProvider.h>
#include <media/AudioResampler.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioMixerBase.h>
#include <media/BufferProviders.h>
#include <system/audio.h>
#include <utils/Compat.h>
#include <utils/threads.h>
// FIXME This is actually unity gain, which might not be max in future, expressed in U.12
#define MAX_GAIN_INT AudioMixer::UNITY_GAIN_INT
// This must match frameworks/av/services/audioflinger/Configuration.h
#define FLOAT_AUX
#define MAX_GAIN_INT AudioMixerBase::UNITY_GAIN_INT
namespace android {
namespace NBLog {
class Writer;
} // namespace NBLog
// ----------------------------------------------------------------------------
class AudioMixer
// AudioMixer extends AudioMixerBase by adding support for down- and up-mixing
// and time stretch that are implemented via Effects HAL, and adding support
// for haptic channels which depends on Vibrator service. This is the version
// that is used by Audioflinger.
class AudioMixer : public AudioMixerBase
{
public:
// Do not change these unless underlying code changes.
// This mixer has a hard-coded upper limit of 8 channels for output.
static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8;
static constexpr uint32_t MAX_NUM_VOLUMES = FCC_2; // stereo volume only
// maximum number of channels supported for the content
static const uint32_t MAX_NUM_CHANNELS_TO_DOWNMIX = AUDIO_CHANNEL_COUNT_MAX;
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,
DOWNMIX_TYPE = 0X4004,
MIXER_FORMAT = 0x4005, // AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
MIXER_CHANNEL_MASK = 0x4006, // Channel mask for mixer output
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.
// 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,
// for target TIMESTRETCH
PLAYBACK_RATE = 0x4300, // Configure timestretch on this track name;
// parameter 'value' is a pointer to the new playback rate.
@ -131,142 +82,23 @@ public:
}
AudioMixer(size_t frameCount, uint32_t sampleRate)
: mSampleRate(sampleRate)
, mFrameCount(frameCount) {
: AudioMixerBase(frameCount, sampleRate) {
pthread_once(&sOnceControl, &sInitRoutine);
}
// 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);
void setParameter(int name, int target, int param, void *value);
void setBufferProvider(int name, AudioBufferProvider* bufferProvider);
void process() {
for (const auto &pair : mTracks) {
// Clear contracted buffer before processing if contracted channels are saved
const std::shared_ptr<Track> &t = pair.second;
if (t->mKeepContractedChannels) {
t->clearContractedBuffer();
}
}
(this->*mHook)();
processHapticData();
}
size_t getUnreleasedFrames(int name) const;
std::string trackNames() const {
std::stringstream ss;
for (const auto &pair : mTracks) {
ss << pair.first << " ";
}
return ss.str();
}
void setNBLogWriter(NBLog::Writer *logWriter) {
mNBLogWriter = logWriter;
}
static inline bool isValidFormat(audio_format_t format) {
switch (format) {
case AUDIO_FORMAT_PCM_8_BIT:
case AUDIO_FORMAT_PCM_16_BIT:
case AUDIO_FORMAT_PCM_24_BIT_PACKED:
case AUDIO_FORMAT_PCM_32_BIT:
case AUDIO_FORMAT_PCM_FLOAT:
return true;
default:
return false;
}
}
bool isValidChannelMask(audio_channel_mask_t channelMask) const override;
static inline bool isValidChannelMask(audio_channel_mask_t channelMask) {
return audio_channel_mask_is_valid(channelMask); // the RemixBufferProvider is flexible.
}
void setParameter(int name, int target, int param, void *value) override;
void setBufferProvider(int name, AudioBufferProvider* bufferProvider);
private:
/* For multi-format functions (calls template functions
* in AudioMixerOps.h). The template parameters are as follows:
*
* MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
* USEFLOATVOL (set to true if float volume is used)
* ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards)
* TO: int32_t (Q4.27) or float
* TI: int32_t (Q4.27) or int16_t (Q0.15) or float
* TA: int32_t (Q4.27)
*/
enum {
// FIXME this representation permits up to 8 channels
NEEDS_CHANNEL_COUNT__MASK = 0x00000007,
};
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_NORESAMPLE,
TRACKTYPE_NORESAMPLEMONO,
};
// process hook functionality
using process_hook_t = void(AudioMixer::*)();
struct Track;
using hook_t = void(Track::*)(int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
struct Track {
Track()
: bufferProvider(nullptr)
{
// TODO: move additional initialization here.
}
struct Track : public TrackBase {
Track() : TrackBase() {}
~Track()
{
// bufferProvider, mInputBufferProvider need not be deleted.
mResampler.reset(nullptr);
// mInputBufferProvider need not be deleted.
// Ensure the order of destruction of buffer providers as they
// release the upstream provider in the destructor.
mTimestretchBufferProvider.reset(nullptr);
@ -277,13 +109,12 @@ private:
mAdjustChannelsBufferProvider.reset(nullptr);
}
bool needsRamp() { return (volumeInc[0] | volumeInc[1] | auxInc) != 0; }
bool setResampler(uint32_t trackSampleRate, uint32_t devSampleRate);
bool doesResample() const { return mResampler.get() != nullptr; }
void resetResampler() { if (mResampler.get() != nullptr) mResampler->reset(); }
void adjustVolumeRamp(bool aux, bool useFloat = false);
size_t getUnreleasedFrames() const { return mResampler.get() != nullptr ?
mResampler->getUnreleasedFrames() : 0; };
uint32_t getOutputChannelCount() override {
return mDownmixerBufferProvider.get() != nullptr ? mMixerChannelCount : channelCount;
}
uint32_t getMixerChannelCount() override {
return mMixerChannelCount + mMixerHapticChannelCount;
}
status_t prepareForDownmix();
void unprepareForDownmix();
@ -297,51 +128,9 @@ private:
bool setPlaybackRate(const AudioPlaybackRate &playbackRate);
void reconfigureBufferProviders();
static hook_t getTrackHook(int trackType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
void track__nop(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
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, see DownmixerBufferProvider below
// for how the Track buffer provider is wrapped by another one when dowmixing is required
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;
/* Buffer providers are constructed to translate the track input data as needed.
* See DownmixerBufferProvider below for how the Track buffer provider
* is wrapped by another one when dowmixing is required.
*
* TODO: perhaps make a single PlaybackConverterProvider class to move
* all pre-mixer track buffer conversions outside the AudioMixer class.
@ -363,7 +152,7 @@ private:
* the downmixer requirements to the mixer engine input requirements.
* 7) mTimestretchBufferProvider: Adds timestretching for playback rate
*/
AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider.
// TODO: combine mAdjustChannelsBufferProvider and
// mContractChannelsNonDestructiveBufferProvider
std::unique_ptr<PassthruBufferProvider> mAdjustChannelsBufferProvider;
@ -373,27 +162,10 @@ private:
std::unique_ptr<PassthruBufferProvider> mPostDownmixReformatBufferProvider;
std::unique_ptr<PassthruBufferProvider> mTimestretchBufferProvider;
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.
audio_format_t mDownmixRequiresFormat; // required downmixer format
// AUDIO_FORMAT_PCM_16_BIT if 16 bit necessary
// AUDIO_FORMAT_INVALID if no required 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;
AudioPlaybackRate mPlaybackRate;
// Haptic
@ -440,76 +212,23 @@ private:
return 0.0f;
}
}
private:
// hooks
void track__genericResample(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
void track__16BitsStereo(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
void track__16BitsMono(int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
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);
};
// TODO: remove BLOCKSIZE unit of processing - it isn't needed anymore.
static constexpr int BLOCKSIZE = 16;
bool setChannelMasks(int name,
audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask);
// Called when track info changes and a new process hook should be determined.
void invalidate() {
mHook = &AudioMixer::process__validate;
inline std::shared_ptr<Track> getTrack(int name) {
return std::static_pointer_cast<Track>(mTracks[name]);
}
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();
std::shared_ptr<TrackBase> preCreateTrack() override;
status_t postCreateTrack(TrackBase *track) override;
void processHapticData();
void preProcess() override;
void postProcess() override;
static process_hook_t getProcessHook(int processType, uint32_t channelCount,
audio_format_t mixerInFormat, audio_format_t mixerOutFormat);
static void convertMixerFormat(void *out, audio_format_t mixerOutFormat,
void *in, audio_format_t mixerInFormat, size_t sampleCount);
bool setChannelMasks(int name,
audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) override;
static void sInitRoutine();
// initialization constants
const uint32_t mSampleRate;
const size_t mFrameCount;
NBLog::Writer *mNBLogWriter = nullptr; // associated NBLog::Writer
process_hook_t mHook = &AudioMixer::process__nop; // one of process__*, never nullptr
// the size of the type (int32_t) should be the largest of all types supported
// by the mixer.
std::unique_ptr<int32_t[]> mOutputTemp;
std::unique_ptr<int32_t[]> mResampleTemp;
// track names grouped by main buffer, in no particular order of main buffer.
// however names for a particular main buffer are in order (by construction).
std::unordered_map<void * /* mainBuffer */, std::vector<int /* name */>> mGroups;
// track names that are enabled, in increasing order (by construction).
std::vector<int /* name */> mEnabled;
// track smart pointers, by name, in increasing order of name.
std::map<int /* name */, std::shared_ptr<Track>> mTracks;
static pthread_once_t sOnceControl; // initialized in constructor by first new
};

@ -3,20 +3,13 @@ cc_defaults {
export_include_dirs: ["include"],
header_libs: ["libaudioclient_headers"],
shared_libs: [
"libaudiohal",
"libaudioutils",
"libcutils",
"liblog",
"libnbaio",
"libnblog",
"libsonic",
"libutils",
"libvibrator",
],
header_libs: [
"libbase_headers",
],
cflags: [
@ -33,18 +26,31 @@ cc_library_shared {
defaults: ["libaudioprocessing_defaults"],
srcs: [
"AudioMixer.cpp",
"BufferProviders.cpp",
"RecordBufferConverter.cpp",
],
whole_static_libs: ["libaudioprocessing_arm"],
header_libs: [
"libbase_headers",
],
shared_libs: [
"libaudiohal",
"libsonic",
"libvibrator",
],
whole_static_libs: ["libaudioprocessing_base"],
}
cc_library_static {
name: "libaudioprocessing_arm",
name: "libaudioprocessing_base",
defaults: ["libaudioprocessing_defaults"],
vendor_available: true,
srcs: [
"AudioMixer.cpp",
"AudioMixerBase.cpp",
"AudioResampler.cpp",
"AudioResamplerCubic.cpp",
"AudioResamplerSinc.cpp",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,359 @@
/*
**
** Copyright 2019, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#ifndef ANDROID_AUDIO_MIXER_BASE_H
#define ANDROID_AUDIO_MIXER_BASE_H
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
#include <media/AudioBufferProvider.h>
#include <media/AudioResampler.h>
#include <media/AudioResamplerPublic.h>
#include <system/audio.h>
#include <utils/Compat.h>
// This must match frameworks/av/services/audioflinger/Configuration.h
// when used with the Audio Framework.
#define FLOAT_AUX
namespace android {
// ----------------------------------------------------------------------------
// AudioMixerBase is functional on its own if only mixing and resampling
// is needed.
class AudioMixerBase
{
public:
// Do not change these unless underlying code changes.
// This mixer has a hard-coded upper limit of 8 channels for output.
static constexpr uint32_t MAX_NUM_CHANNELS = FCC_8;
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_NORESAMPLE,
TRACKTYPE_NORESAMPLEMONO,
};
// process hook functionality
using process_hook_t = void(AudioMixerBase::*)();
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; };
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);
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;
};
} // namespace android
#endif // ANDROID_AUDIO_MIXER_BASE_H

@ -547,6 +547,16 @@ private:
bool mute;
};
// Abstraction for the Audio Source for the RecordThread (HAL or PassthruPatchRecord).
struct Source
{
virtual ~Source() = default;
// The following methods have the same signatures as in StreamHalInterface.
virtual status_t read(void *buffer, size_t bytes, size_t *read) = 0;
virtual status_t getCapturePosition(int64_t *frames, int64_t *time) = 0;
virtual status_t standby() = 0;
};
// --- PlaybackThread ---
#ifdef FLOAT_EFFECT_CHAIN
#define EFFECT_BUFFER_FORMAT AUDIO_FORMAT_PCM_FLOAT
@ -749,7 +759,7 @@ using effect_buffer_t = int16_t;
// For emphasis, we could also make all pointers to them be "const *",
// but that would clutter the code unnecessarily.
struct AudioStreamIn {
struct AudioStreamIn : public Source {
AudioHwDevice* const audioHwDev;
sp<StreamInHalInterface> stream;
audio_input_flags_t flags;
@ -758,6 +768,13 @@ using effect_buffer_t = int16_t;
AudioStreamIn(AudioHwDevice *dev, sp<StreamInHalInterface> in, audio_input_flags_t flags) :
audioHwDev(dev), stream(in), flags(flags) {}
status_t read(void *buffer, size_t bytes, size_t *read) override {
return stream->read(buffer, bytes, read);
}
status_t getCapturePosition(int64_t *frames, int64_t *time) override {
return stream->getCapturePosition(frames, time);
}
status_t standby() override { return stream->standby(); }
};
struct TeePatch {

@ -105,13 +105,8 @@ const FastThreadState *FastMixer::poll()
return mSQ.poll();
}
void FastMixer::setNBLogWriter(NBLog::Writer *logWriter)
void FastMixer::setNBLogWriter(NBLog::Writer *logWriter __unused)
{
// FIXME If mMixer is set or changed prior to this, we don't inform correctly.
// Should cache logWriter and re-apply it at the assignment to mMixer.
if (mMixer != NULL) {
mMixer->setNBLogWriter(logWriter);
}
}
void FastMixer::onIdle()

@ -124,7 +124,7 @@ bool FastThread::threadLoop()
mDumpState = next->mDumpState != NULL ? next->mDumpState : mDummyDumpState;
tlNBLogWriter = next->mNBLogWriter != NULL ?
next->mNBLogWriter : mDummyNBLogWriter.get();
setNBLogWriter(tlNBLogWriter); // FastMixer informs its AudioMixer, FastCapture ignores
setNBLogWriter(tlNBLogWriter); // This is used for debugging only
// We want to always have a valid reference to the previous (non-idle) state.
// However, the state queue only guarantees access to current and previous states.

@ -483,19 +483,6 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel)
// Fast mode is not available in this case.
inputFlags = (audio_input_flags_t) (inputFlags & ~AUDIO_INPUT_FLAG_FAST);
}
sp<RecordThread::PatchRecord> tempRecordTrack = new (std::nothrow) RecordThread::PatchRecord(
mRecord.thread().get(),
sampleRate,
inChannelMask,
format,
frameCount,
NULL,
(size_t)0 /* bufferSize */,
inputFlags);
status = mRecord.checkTrack(tempRecordTrack.get());
if (status != NO_ERROR) {
return status;
}
audio_output_flags_t outputFlags = mAudioPatch.sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ?
mAudioPatch.sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE;
@ -512,9 +499,34 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel)
outputFlags = (audio_output_flags_t) (outputFlags & ~AUDIO_OUTPUT_FLAG_FAST);
}
sp<RecordThread::PatchRecord> tempRecordTrack;
if ((inputFlags & AUDIO_INPUT_FLAG_DIRECT) && (outputFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
tempRecordTrack = new RecordThread::PassthruPatchRecord(
mRecord.thread().get(),
sampleRate,
inChannelMask,
format,
frameCount,
inputFlags);
} else {
tempRecordTrack = new RecordThread::PatchRecord(
mRecord.thread().get(),
sampleRate,
inChannelMask,
format,
frameCount,
nullptr,
(size_t)0 /* bufferSize */,
inputFlags);
}
status = mRecord.checkTrack(tempRecordTrack.get());
if (status != NO_ERROR) {
return status;
}
// create a special playback track to render to playback thread.
// this track is given the same buffer as the PatchRecord buffer
sp<PlaybackThread::PatchTrack> tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack(
sp<PlaybackThread::PatchTrack> tempPatchTrack = new PlaybackThread::PatchTrack(
mPlayback.thread().get(),
streamType,
sampleRate,

@ -266,8 +266,6 @@ protected:
private:
void interceptBuffer(const AudioBufferProvider::Buffer& buffer);
/** Write the source data in the buffer provider. @return written frame count. */
size_t writeFrames(AudioBufferProvider* dest, const void* src, size_t frameCount);
template <class F>
void forEachTeePatchTrack(F f) {
for (auto& tp : mTeePatches) { f(tp.patchTrack); }
@ -387,6 +385,8 @@ public:
const Timeout& timeout = {});
virtual ~PatchTrack();
size_t framesReady() const override;
virtual status_t start(AudioSystem::sync_event_t event =
AudioSystem::SYNC_EVENT_NONE,
audio_session_t triggerSession = AUDIO_SESSION_NONE);

@ -128,6 +128,8 @@ public:
const Timeout& timeout = {});
virtual ~PatchRecord();
virtual Source* getSource() { return nullptr; }
// AudioBufferProvider interface
virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer);
virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer);
@ -136,4 +138,71 @@ public:
virtual status_t obtainBuffer(Proxy::Buffer *buffer,
const struct timespec *timeOut = NULL);
virtual void releaseBuffer(Proxy::Buffer *buffer);
size_t writeFrames(const void* src, size_t frameCount, size_t frameSize) {
return writeFrames(this, src, frameCount, frameSize);
}
protected:
/** Write the source data into the buffer provider. @return written frame count. */
static size_t writeFrames(AudioBufferProvider* dest, const void* src,
size_t frameCount, size_t frameSize);
}; // end of PatchRecord
class PassthruPatchRecord : public PatchRecord, public Source {
public:
PassthruPatchRecord(RecordThread *recordThread,
uint32_t sampleRate,
audio_channel_mask_t channelMask,
audio_format_t format,
size_t frameCount,
audio_input_flags_t flags);
Source* getSource() override { return static_cast<Source*>(this); }
// Source interface
status_t read(void *buffer, size_t bytes, size_t *read) override;
status_t getCapturePosition(int64_t *frames, int64_t *time) override;
status_t standby() override;
// AudioBufferProvider interface
// This interface is used by RecordThread to pass the data obtained
// from HAL or other source to the client. PassthruPatchRecord receives
// the data in 'obtainBuffer' so these calls are stubbed out.
status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override;
void releaseBuffer(AudioBufferProvider::Buffer* buffer) override;
// PatchProxyBufferProvider interface
// This interface is used from DirectOutputThread to acquire data from HAL.
bool producesBufferOnDemand() const override { return true; }
status_t obtainBuffer(Proxy::Buffer *buffer, const struct timespec *timeOut = nullptr) override;
void releaseBuffer(Proxy::Buffer *buffer) override;
private:
// This is to use with PatchRecord::writeFrames
struct PatchRecordAudioBufferProvider : public AudioBufferProvider {
explicit PatchRecordAudioBufferProvider(PassthruPatchRecord& passthru) :
mPassthru(passthru) {}
status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override {
return mPassthru.PatchRecord::getNextBuffer(buffer);
}
void releaseBuffer(AudioBufferProvider::Buffer* buffer) override {
return mPassthru.PatchRecord::releaseBuffer(buffer);
}
private:
PassthruPatchRecord& mPassthru;
};
sp<StreamInHalInterface> obtainStream(sp<ThreadBase>* thread);
PatchRecordAudioBufferProvider mPatchRecordAudioBufferProvider;
std::unique_ptr<void, decltype(free)*> mSinkBuffer; // frame size aligned continuous buffer
std::unique_ptr<void, decltype(free)*> mStubBuffer; // buffer used for AudioBufferProvider
size_t mUnconsumedFrames = 0;
std::mutex mReadLock;
std::condition_variable mReadCV;
size_t mReadBytes = 0; // GUARDED_BY(mReadLock)
status_t mReadError = NO_ERROR; // GUARDED_BY(mReadLock)
int64_t mLastReadFrames = 0; // accessed on RecordThread only
};

@ -5289,11 +5289,11 @@ bool AudioFlinger::MixerThread::isTrackAllowed_l(
return false;
}
// Check validity as we don't call AudioMixer::create() here.
if (!AudioMixer::isValidFormat(format)) {
if (!mAudioMixer->isValidFormat(format)) {
ALOGW("%s: invalid format: %#x", __func__, format);
return false;
}
if (!AudioMixer::isValidChannelMask(channelMask)) {
if (!mAudioMixer->isValidChannelMask(channelMask)) {
ALOGW("%s: invalid channelMask: %#x", __func__, channelMask);
return false;
}
@ -6658,6 +6658,7 @@ AudioFlinger::RecordThread::RecordThread(const sp<AudioFlinger>& audioFlinger,
) :
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady),
mInput(input),
mSource(mInput),
mActiveTracks(&this->mLocalLog),
mRsmpInBuffer(NULL),
// mRsmpInFrames, mRsmpInFramesP2, and mRsmpInFramesOA are set by readInputParameters_l()
@ -7110,7 +7111,7 @@ reacquire_wakelock:
} else {
ATRACE_BEGIN("read");
size_t bytesRead;
status_t result = mInput->stream->read(
status_t result = mSource->read(
(uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize, &bytesRead);
ATRACE_END();
if (result < 0) {
@ -7132,7 +7133,7 @@ reacquire_wakelock:
int64_t position, time;
if (mStandby) {
mTimestampVerifier.discontinuity();
} else if (mInput->stream->getCapturePosition(&position, &time) == NO_ERROR
} else if (mSource->getCapturePosition(&position, &time) == NO_ERROR
&& time > mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]) {
mTimestampVerifier.add(position, time, mSampleRate);
@ -7413,7 +7414,7 @@ void AudioFlinger::RecordThread::inputStandBy()
sq->end(false /*didModify*/);
}
}
status_t result = mInput->stream->standby();
status_t result = mSource->standby();
ALOGE_IF(result != OK, "Error when putting input stream into standby: %d", result);
// If going into standby, flush the pipe source.
@ -8398,11 +8399,17 @@ void AudioFlinger::RecordThread::addPatchTrack(const sp<PatchRecord>& record)
{
Mutex::Autolock _l(mLock);
mTracks.add(record);
if (record->getSource()) {
mSource = record->getSource();
}
}
void AudioFlinger::RecordThread::deletePatchTrack(const sp<PatchRecord>& record)
{
Mutex::Autolock _l(mLock);
if (mSource == record->getSource()) {
mSource = mInput;
}
destroyTrack_l(record);
}

@ -1645,6 +1645,7 @@ private:
void checkBtNrec_l();
AudioStreamIn *mInput;
Source *mSource;
SortedVector < sp<RecordTrack> > mTracks;
// mActiveTracks has dual roles: it indicates the current active track(s), and
// is used together with mStartStopCond to indicate start()/stop() progress

@ -325,6 +325,7 @@ public:
virtual ~PatchProxyBufferProvider() {}
virtual bool producesBufferOnDemand() const = 0;
virtual status_t obtainBuffer(Proxy::Buffer* buffer,
const struct timespec *requested = NULL) = 0;
virtual void releaseBuffer(Proxy::Buffer* buffer) = 0;
@ -347,6 +348,8 @@ public:
mPeerProxy = nullptr;
}
bool producesBufferOnDemand() const override { return false; }
protected:
const sp<ClientProxy> mProxy;
sp<RefBase> mPeerReferenceHold; // keeps mPeerProxy alive during access.

@ -18,12 +18,14 @@
#define LOG_TAG "AudioFlinger"
//#define LOG_NDEBUG 0
#define ATRACE_TAG ATRACE_TAG_AUDIO
#include "Configuration.h"
#include <linux/futex.h>
#include <math.h>
#include <sys/syscall.h>
#include <utils/Log.h>
#include <utils/Trace.h>
#include <private/media/AudioTrackShared.h>
@ -820,16 +822,9 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer(
}
for (auto& teePatch : mTeePatches) {
RecordThread::PatchRecord* patchRecord = teePatch.patchRecord.get();
size_t framesWritten = writeFrames(patchRecord, sourceBuffer.i8, frameCount);
// On buffer wrap, the buffer frame count will be less than requested,
// when this happens a second buffer needs to be used to write the leftover audio
size_t framesLeft = frameCount - framesWritten;
if (framesWritten != 0 && framesLeft != 0) {
framesWritten +=
writeFrames(patchRecord, sourceBuffer.i8 + framesWritten * mFrameSize, framesLeft);
framesLeft = frameCount - framesWritten;
}
const size_t framesWritten = patchRecord->writeFrames(
sourceBuffer.i8, frameCount, mFrameSize);
const size_t framesLeft = frameCount - framesWritten;
ALOGW_IF(framesLeft != 0, "%s(%d) PatchRecord %d can not provide big enough "
"buffer %zu/%zu, dropping %zu frames", __func__, mId, patchRecord->mId,
framesWritten, frameCount, framesLeft);
@ -841,26 +836,6 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer(
spent.count(), mTeePatches.size());
}
size_t AudioFlinger::PlaybackThread::Track::writeFrames(AudioBufferProvider* dest,
const void* src,
size_t frameCount) {
AudioBufferProvider::Buffer patchBuffer;
patchBuffer.frameCount = frameCount;
auto status = dest->getNextBuffer(&patchBuffer);
if (status != NO_ERROR) {
ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
__func__, status, strerror(-status));
return 0;
}
ALOG_ASSERT(patchBuffer.frameCount <= frameCount);
memcpy(patchBuffer.raw, src, patchBuffer.frameCount * mFrameSize);
auto framesWritten = patchBuffer.frameCount;
dest->releaseBuffer(&patchBuffer);
return framesWritten;
}
// releaseBuffer() is not overridden
// ExtendedAudioBufferProvider interface
// framesReady() may return an approximation of the number of frames if called
@ -1810,6 +1785,15 @@ AudioFlinger::PlaybackThread::PatchTrack::~PatchTrack()
ALOGV("%s(%d)", __func__, mId);
}
size_t AudioFlinger::PlaybackThread::PatchTrack::framesReady() const
{
if (mPeerProxy && mPeerProxy->producesBufferOnDemand()) {
return std::numeric_limits<size_t>::max();
} else {
return Track::framesReady();
}
}
status_t AudioFlinger::PlaybackThread::PatchTrack::start(AudioSystem::sync_event_t event,
audio_session_t triggerSession)
{
@ -1828,9 +1812,19 @@ status_t AudioFlinger::PlaybackThread::PatchTrack::getNextBuffer(
ALOG_ASSERT(mPeerProxy != 0, "%s(%d): called without peer proxy", __func__, mId);
Proxy::Buffer buf;
buf.mFrameCount = buffer->frameCount;
if (ATRACE_ENABLED()) {
std::string traceName("PTnReq");
traceName += std::to_string(id());
ATRACE_INT(traceName.c_str(), buf.mFrameCount);
}
status_t status = mPeerProxy->obtainBuffer(&buf, &mPeerTimeout);
ALOGV_IF(status != NO_ERROR, "%s(%d): getNextBuffer status %d", __func__, mId, status);
buffer->frameCount = buf.mFrameCount;
if (ATRACE_ENABLED()) {
std::string traceName("PTnObt");
traceName += std::to_string(id());
ATRACE_INT(traceName.c_str(), buf.mFrameCount);
}
if (buf.mFrameCount == 0) {
return WOULD_BLOCK;
}
@ -2283,6 +2277,39 @@ AudioFlinger::RecordThread::PatchRecord::~PatchRecord()
ALOGV("%s(%d)", __func__, mId);
}
static size_t writeFramesHelper(
AudioBufferProvider* dest, const void* src, size_t frameCount, size_t frameSize)
{
AudioBufferProvider::Buffer patchBuffer;
patchBuffer.frameCount = frameCount;
auto status = dest->getNextBuffer(&patchBuffer);
if (status != NO_ERROR) {
ALOGW("%s PathRecord getNextBuffer failed with error %d: %s",
__func__, status, strerror(-status));
return 0;
}
ALOG_ASSERT(patchBuffer.frameCount <= frameCount);
memcpy(patchBuffer.raw, src, patchBuffer.frameCount * frameSize);
size_t framesWritten = patchBuffer.frameCount;
dest->releaseBuffer(&patchBuffer);
return framesWritten;
}
// static
size_t AudioFlinger::RecordThread::PatchRecord::writeFrames(
AudioBufferProvider* dest, const void* src, size_t frameCount, size_t frameSize)
{
size_t framesWritten = writeFramesHelper(dest, src, frameCount, frameSize);
// On buffer wrap, the buffer frame count will be less than requested,
// when this happens a second buffer needs to be used to write the leftover audio
const size_t framesLeft = frameCount - framesWritten;
if (framesWritten != 0 && framesLeft != 0) {
framesWritten += writeFramesHelper(dest, (const char*)src + framesWritten * frameSize,
framesLeft, frameSize);
}
return framesWritten;
}
// AudioBufferProvider interface
status_t AudioFlinger::RecordThread::PatchRecord::getNextBuffer(
AudioBufferProvider::Buffer* buffer)
@ -2294,6 +2321,11 @@ status_t AudioFlinger::RecordThread::PatchRecord::getNextBuffer(
ALOGV_IF(status != NO_ERROR,
"%s(%d): mPeerProxy->obtainBuffer status %d", __func__, mId, status);
buffer->frameCount = buf.mFrameCount;
if (ATRACE_ENABLED()) {
std::string traceName("PRnObt");
traceName += std::to_string(id());
ATRACE_INT(traceName.c_str(), buf.mFrameCount);
}
if (buf.mFrameCount == 0) {
return WOULD_BLOCK;
}
@ -2322,6 +2354,180 @@ void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(Proxy::Buffer* buffe
mProxy->releaseBuffer(buffer);
}
#undef LOG_TAG
#define LOG_TAG "AF::PthrPatchRecord"
static std::unique_ptr<void, decltype(free)*> allocAligned(size_t alignment, size_t size)
{
void *ptr = nullptr;
(void)posix_memalign(&ptr, alignment, size);
return std::unique_ptr<void, decltype(free)*>(ptr, free);
}
AudioFlinger::RecordThread::PassthruPatchRecord::PassthruPatchRecord(
RecordThread *recordThread,
uint32_t sampleRate,
audio_channel_mask_t channelMask,
audio_format_t format,
size_t frameCount,
audio_input_flags_t flags)
: PatchRecord(recordThread, sampleRate, channelMask, format, frameCount,
nullptr /*buffer*/, 0 /*bufferSize*/, flags),
mPatchRecordAudioBufferProvider(*this),
mSinkBuffer(allocAligned(32, mFrameCount * mFrameSize)),
mStubBuffer(allocAligned(32, mFrameCount * mFrameSize))
{
memset(mStubBuffer.get(), 0, mFrameCount * mFrameSize);
}
sp<StreamInHalInterface> AudioFlinger::RecordThread::PassthruPatchRecord::obtainStream(
sp<ThreadBase>* thread)
{
*thread = mThread.promote();
if (!*thread) return nullptr;
RecordThread *recordThread = static_cast<RecordThread*>((*thread).get());
Mutex::Autolock _l(recordThread->mLock);
return recordThread->mInput ? recordThread->mInput->stream : nullptr;
}
// PatchProxyBufferProvider methods are called on DirectOutputThread
status_t AudioFlinger::RecordThread::PassthruPatchRecord::obtainBuffer(
Proxy::Buffer* buffer, const struct timespec* timeOut)
{
if (mUnconsumedFrames) {
buffer->mFrameCount = std::min(buffer->mFrameCount, mUnconsumedFrames);
// mUnconsumedFrames is decreased in releaseBuffer to use actual frame consumption figure.
return PatchRecord::obtainBuffer(buffer, timeOut);
}
// Otherwise, execute a read from HAL and write into the buffer.
nsecs_t startTimeNs = 0;
if (timeOut && (timeOut->tv_sec != 0 || timeOut->tv_nsec != 0) && timeOut->tv_sec != INT_MAX) {
// Will need to correct timeOut by elapsed time.
startTimeNs = systemTime();
}
const size_t framesToRead = std::min(buffer->mFrameCount, mFrameCount);
buffer->mFrameCount = 0;
buffer->mRaw = nullptr;
sp<ThreadBase> thread;
sp<StreamInHalInterface> stream = obtainStream(&thread);
if (!stream) return NO_INIT; // If there is no stream, RecordThread is not reading.
status_t result = NO_ERROR;
struct timespec newTimeOut = *timeOut;
size_t bytesRead = 0;
{
ATRACE_NAME("read");
result = stream->read(mSinkBuffer.get(), framesToRead * mFrameSize, &bytesRead);
if (result != NO_ERROR) goto stream_error;
if (bytesRead == 0) return NO_ERROR;
}
{
std::lock_guard<std::mutex> lock(mReadLock);
mReadBytes += bytesRead;
mReadError = NO_ERROR;
}
mReadCV.notify_one();
// writeFrames handles wraparound and should write all the provided frames.
// If it couldn't, there is something wrong with the client/server buffer of the software patch.
buffer->mFrameCount = writeFrames(
&mPatchRecordAudioBufferProvider,
mSinkBuffer.get(), bytesRead / mFrameSize, mFrameSize);
ALOGW_IF(buffer->mFrameCount < bytesRead / mFrameSize,
"Lost %zu frames obtained from HAL", bytesRead / mFrameSize - buffer->mFrameCount);
mUnconsumedFrames = buffer->mFrameCount;
// Correct newTimeOut by elapsed time.
if (startTimeNs) {
nsecs_t newTimeOutNs =
audio_utils_ns_from_timespec(&newTimeOut) - (systemTime() - startTimeNs);
if (newTimeOutNs < 0) newTimeOutNs = 0;
newTimeOut.tv_sec = newTimeOutNs / NANOS_PER_SECOND;
newTimeOut.tv_nsec = newTimeOutNs - newTimeOut.tv_sec * NANOS_PER_SECOND;
}
return PatchRecord::obtainBuffer(buffer, &newTimeOut);
stream_error:
stream->standby();
{
std::lock_guard<std::mutex> lock(mReadLock);
mReadError = result;
}
mReadCV.notify_one();
return result;
}
void AudioFlinger::RecordThread::PassthruPatchRecord::releaseBuffer(Proxy::Buffer* buffer)
{
if (buffer->mFrameCount <= mUnconsumedFrames) {
mUnconsumedFrames -= buffer->mFrameCount;
} else {
ALOGW("Write side has consumed more frames than we had: %zu > %zu",
buffer->mFrameCount, mUnconsumedFrames);
mUnconsumedFrames = 0;
}
PatchRecord::releaseBuffer(buffer);
}
// AudioBufferProvider and Source methods are called on RecordThread
// 'read' emulates actual audio data with 0's. This is OK as 'getNextBuffer'
// and 'releaseBuffer' are stubbed out and ignore their input.
// It's not possible to retrieve actual data here w/o blocking 'obtainBuffer'
// until we copy it.
status_t AudioFlinger::RecordThread::PassthruPatchRecord::read(
void* buffer, size_t bytes, size_t* read)
{
bytes = std::min(bytes, mFrameCount * mFrameSize);
{
std::unique_lock<std::mutex> lock(mReadLock);
mReadCV.wait(lock, [&]{ return mReadError != NO_ERROR || mReadBytes != 0; });
if (mReadError != NO_ERROR) {
mLastReadFrames = 0;
return mReadError;
}
*read = std::min(bytes, mReadBytes);
mReadBytes -= *read;
}
mLastReadFrames = *read / mFrameSize;
memset(buffer, 0, *read);
return 0;
}
status_t AudioFlinger::RecordThread::PassthruPatchRecord::getCapturePosition(
int64_t* frames, int64_t* time)
{
sp<ThreadBase> thread;
sp<StreamInHalInterface> stream = obtainStream(&thread);
return stream ? stream->getCapturePosition(frames, time) : NO_INIT;
}
status_t AudioFlinger::RecordThread::PassthruPatchRecord::standby()
{
// RecordThread issues 'standby' command in two major cases:
// 1. Error on read--this case is handled in 'obtainBuffer'.
// 2. Track is stopping--as PassthruPatchRecord assumes continuous
// output, this can only happen when the software patch
// is being torn down. In this case, the RecordThread
// will terminate and close the HAL stream.
return 0;
}
// As the buffer gets filled in obtainBuffer, here we only simulate data consumption.
status_t AudioFlinger::RecordThread::PassthruPatchRecord::getNextBuffer(
AudioBufferProvider::Buffer* buffer)
{
buffer->frameCount = mLastReadFrames;
buffer->raw = buffer->frameCount != 0 ? mStubBuffer.get() : nullptr;
return NO_ERROR;
}
void AudioFlinger::RecordThread::PassthruPatchRecord::releaseBuffer(
AudioBufferProvider::Buffer* buffer)
{
buffer->frameCount = 0;
buffer->raw = nullptr;
}
// ----------------------------------------------------------------------------
#undef LOG_TAG
#define LOG_TAG "AF::MmapTrack"

Loading…
Cancel
Save