diff options
Diffstat (limited to 'services')
-rw-r--r-- | services/audioflinger/AudioFlinger.cpp | 3 | ||||
-rw-r--r-- | services/audioflinger/AudioMixer.cpp | 186 | ||||
-rw-r--r-- | services/audioflinger/AudioMixer.h | 34 | ||||
-rw-r--r-- | services/audioflinger/Threads.cpp | 100 | ||||
-rw-r--r-- | services/audioflinger/Threads.h | 3 | ||||
-rw-r--r-- | services/audioflinger/Tracks.cpp | 1 | ||||
-rwxr-xr-x | services/audioflinger/tests/mixer_to_wav_tests.sh | 12 | ||||
-rw-r--r-- | services/audioflinger/tests/test-mixer.cpp | 92 |
8 files changed, 298 insertions, 133 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 71e6f83..aee805a 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -401,6 +401,9 @@ status_t AudioFlinger::dump(int fd, const Vector<String16>& args) String8 result(kClientLockedString); write(fd, result.string(), result.size()); } + + EffectDumpEffects(fd); + dumpClients(fd, args); if (clientLocked) { mClientLock.unlock(); diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index fd28ea1..0d4b358 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -430,6 +430,10 @@ void AudioMixer::setLog(NBLog::Writer *log) mState.mLog = log; } +static inline audio_format_t selectMixerInFormat(audio_format_t inputFormat __unused) { + return kUseFloat && kUseNewMixer ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; +} + int AudioMixer::getTrackName(audio_channel_mask_t channelMask, audio_format_t format, int sessionId) { @@ -492,24 +496,23 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask, t->mInputBufferProvider = NULL; t->mReformatBufferProvider = NULL; t->downmixerBufferProvider = NULL; + t->mPostDownmixReformatBufferProvider = 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->mMixerInFormat = selectMixerInFormat(format); + t->mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; // no format required 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); // Check the downmixing (or upmixing) requirements. - status_t status = initTrackDownmix(t, n); + status_t status = t->prepareForDownmix(); if (status != OK) { ALOGE("AudioMixer::getTrackName invalid channelMask (%#x)", channelMask); return -1; } - // initTrackDownmix() may change the input format requirement. - // If you desire floating point input to the mixer, it may change - // to integer because the downmixer requires integer to process. + // prepareForDownmix() may change mDownmixRequiresFormat ALOGVV("mMixerFormat:%#x mMixerInFormat:%#x\n", t->mMixerFormat, t->mMixerInFormat); - prepareTrackForReformat(t, n); + t->prepareForReformat(); mTrackNames |= 1 << n; return TRACK0 + n; } @@ -526,7 +529,7 @@ void AudioMixer::invalidateState(uint32_t mask) } // Called when channel masks have changed for a track name -// TODO: Fix Downmixbufferprofider not to (possibly) change mixer input format, +// TODO: Fix DownmixerBufferProvider not to (possibly) change mixer input format, // which will simplify this logic. bool AudioMixer::setChannelMasks(int name, audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask) { @@ -551,21 +554,18 @@ bool AudioMixer::setChannelMasks(int name, // channel masks have changed, does this track need a downmixer? // update to try using our desired format (if we aren't already using it) - const audio_format_t prevMixerInFormat = track.mMixerInFormat; - track.mMixerInFormat = kUseFloat && kUseNewMixer - ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; - const status_t status = initTrackDownmix(&mState.tracks[name], name); + const audio_format_t prevDownmixerFormat = track.mDownmixRequiresFormat; + const status_t status = mState.tracks[name].prepareForDownmix(); ALOGE_IF(status != OK, - "initTrackDownmix error %d, track channel mask %#x, mixer channel mask %#x", + "prepareForDownmix error %d, track channel mask %#x, mixer channel mask %#x", status, track.channelMask, track.mMixerChannelMask); - const bool mixerInFormatChanged = prevMixerInFormat != track.mMixerInFormat; - if (mixerInFormatChanged) { - prepareTrackForReformat(&track, name); // because of downmixer, track format may change! + if (prevDownmixerFormat != track.mDownmixRequiresFormat) { + track.prepareForReformat(); // because of downmixer, track format may change! } - if (track.resampler && (mixerInFormatChanged || mixerChannelCountChanged)) { - // resampler input format or channels may have changed. + if (track.resampler && mixerChannelCountChanged) { + // resampler channels may have changed. const uint32_t resetToSampleRate = track.sampleRate; delete track.resampler; track.resampler = NULL; @@ -576,99 +576,122 @@ bool AudioMixer::setChannelMasks(int name, return true; } -status_t AudioMixer::initTrackDownmix(track_t* pTrack, int trackName) -{ - // Only remix (upmix or downmix) if the track and mixer/device channel masks - // are not the same and not handled internally, as mono -> stereo currently is. - if (pTrack->channelMask != pTrack->mMixerChannelMask - && !(pTrack->channelMask == AUDIO_CHANNEL_OUT_MONO - && pTrack->mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) { - return prepareTrackForDownmix(pTrack, trackName); - } - // no remix necessary - unprepareTrackForDownmix(pTrack, trackName); - return NO_ERROR; -} - -void AudioMixer::unprepareTrackForDownmix(track_t* pTrack, int trackName __unused) { - ALOGV("AudioMixer::unprepareTrackForDownmix(%d)", trackName); +void AudioMixer::track_t::unprepareForDownmix() { + ALOGV("AudioMixer::unprepareForDownmix(%p)", this); - if (pTrack->downmixerBufferProvider != NULL) { + mDownmixRequiresFormat = AUDIO_FORMAT_INVALID; + if (downmixerBufferProvider != NULL) { // this track had previously been configured with a downmixer, delete it ALOGV(" deleting old downmixer"); - delete pTrack->downmixerBufferProvider; - pTrack->downmixerBufferProvider = NULL; - reconfigureBufferProviders(pTrack); + delete downmixerBufferProvider; + downmixerBufferProvider = NULL; + reconfigureBufferProviders(); } else { ALOGV(" nothing to do, no downmixer to delete"); } } -status_t AudioMixer::prepareTrackForDownmix(track_t* pTrack, int trackName) +status_t AudioMixer::track_t::prepareForDownmix() { - ALOGV("AudioMixer::prepareTrackForDownmix(%d) with mask 0x%x", trackName, pTrack->channelMask); + ALOGV("AudioMixer::prepareForDownmix(%p) with mask 0x%x", + this, channelMask); // discard the previous downmixer if there was one - unprepareTrackForDownmix(pTrack, trackName); + unprepareForDownmix(); + // Only remix (upmix or downmix) if the track and mixer/device channel masks + // are not the same and not handled internally, as mono -> stereo currently is. + if (channelMask == mMixerChannelMask + || (channelMask == AUDIO_CHANNEL_OUT_MONO + && mMixerChannelMask == AUDIO_CHANNEL_OUT_STEREO)) { + return NO_ERROR; + } if (DownmixerBufferProvider::isMultichannelCapable()) { - DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(pTrack->channelMask, - pTrack->mMixerChannelMask, - AUDIO_FORMAT_PCM_16_BIT /* TODO: use pTrack->mMixerInFormat, now only PCM 16 */, - pTrack->sampleRate, pTrack->sessionId, kCopyBufferFrameCount); + DownmixerBufferProvider* pDbp = new DownmixerBufferProvider(channelMask, + mMixerChannelMask, + AUDIO_FORMAT_PCM_16_BIT /* TODO: use mMixerInFormat, now only PCM 16 */, + sampleRate, sessionId, kCopyBufferFrameCount); if (pDbp->isValid()) { // if constructor completed properly - pTrack->mMixerInFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix - pTrack->downmixerBufferProvider = pDbp; - reconfigureBufferProviders(pTrack); + mDownmixRequiresFormat = AUDIO_FORMAT_PCM_16_BIT; // PCM 16 bit required for downmix + downmixerBufferProvider = pDbp; + reconfigureBufferProviders(); return NO_ERROR; } delete pDbp; } // Effect downmixer does not accept the channel conversion. Let's use our remixer. - RemixBufferProvider* pRbp = new RemixBufferProvider(pTrack->channelMask, - pTrack->mMixerChannelMask, pTrack->mMixerInFormat, kCopyBufferFrameCount); + RemixBufferProvider* pRbp = new RemixBufferProvider(channelMask, + mMixerChannelMask, mMixerInFormat, kCopyBufferFrameCount); // Remix always finds a conversion whereas Downmixer effect above may fail. - pTrack->downmixerBufferProvider = pRbp; - reconfigureBufferProviders(pTrack); + downmixerBufferProvider = pRbp; + reconfigureBufferProviders(); return NO_ERROR; } -void AudioMixer::unprepareTrackForReformat(track_t* pTrack, int trackName __unused) { - ALOGV("AudioMixer::unprepareTrackForReformat(%d)", trackName); - if (pTrack->mReformatBufferProvider != NULL) { - delete pTrack->mReformatBufferProvider; - pTrack->mReformatBufferProvider = NULL; - reconfigureBufferProviders(pTrack); +void AudioMixer::track_t::unprepareForReformat() { + ALOGV("AudioMixer::unprepareForReformat(%p)", this); + bool requiresReconfigure = false; + if (mReformatBufferProvider != NULL) { + delete mReformatBufferProvider; + mReformatBufferProvider = NULL; + requiresReconfigure = true; + } + if (mPostDownmixReformatBufferProvider != NULL) { + delete mPostDownmixReformatBufferProvider; + mPostDownmixReformatBufferProvider = NULL; + requiresReconfigure = true; + } + if (requiresReconfigure) { + reconfigureBufferProviders(); } } -status_t AudioMixer::prepareTrackForReformat(track_t* pTrack, int trackName) +status_t AudioMixer::track_t::prepareForReformat() { - ALOGV("AudioMixer::prepareTrackForReformat(%d) with format %#x", trackName, pTrack->mFormat); - // discard the previous reformatter if there was one - unprepareTrackForReformat(pTrack, trackName); - // only configure reformatter if needed - if (pTrack->mFormat != pTrack->mMixerInFormat) { - pTrack->mReformatBufferProvider = new ReformatBufferProvider( - audio_channel_count_from_out_mask(pTrack->channelMask), - pTrack->mFormat, pTrack->mMixerInFormat, + 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 = new ReformatBufferProvider( + audio_channel_count_from_out_mask(channelMask), + mFormat, + targetFormat, kCopyBufferFrameCount); - reconfigureBufferProviders(pTrack); + requiresReconfigure = true; + } + if (targetFormat != mMixerInFormat) { + mPostDownmixReformatBufferProvider = new ReformatBufferProvider( + audio_channel_count_from_out_mask(mMixerChannelMask), + targetFormat, + mMixerInFormat, + kCopyBufferFrameCount); + requiresReconfigure = true; + } + if (requiresReconfigure) { + reconfigureBufferProviders(); } return NO_ERROR; } -void AudioMixer::reconfigureBufferProviders(track_t* pTrack) +void AudioMixer::track_t::reconfigureBufferProviders() { - pTrack->bufferProvider = pTrack->mInputBufferProvider; - if (pTrack->mReformatBufferProvider) { - pTrack->mReformatBufferProvider->setBufferProvider(pTrack->bufferProvider); - pTrack->bufferProvider = pTrack->mReformatBufferProvider; + bufferProvider = mInputBufferProvider; + if (mReformatBufferProvider) { + mReformatBufferProvider->setBufferProvider(bufferProvider); + bufferProvider = mReformatBufferProvider; + } + if (downmixerBufferProvider) { + downmixerBufferProvider->setBufferProvider(bufferProvider); + bufferProvider = downmixerBufferProvider; } - if (pTrack->downmixerBufferProvider) { - pTrack->downmixerBufferProvider->setBufferProvider(pTrack->bufferProvider); - pTrack->bufferProvider = pTrack->downmixerBufferProvider; + if (mPostDownmixReformatBufferProvider) { + mPostDownmixReformatBufferProvider->setBufferProvider(bufferProvider); + bufferProvider = mPostDownmixReformatBufferProvider; } } @@ -687,9 +710,9 @@ void AudioMixer::deleteTrackName(int name) delete track.resampler; track.resampler = NULL; // delete the downmixer - unprepareTrackForDownmix(&mState.tracks[name], name); + mState.tracks[name].unprepareForDownmix(); // delete the reformatter - unprepareTrackForReformat(&mState.tracks[name], name); + mState.tracks[name].unprepareForReformat(); mTrackNames &= ~(1<<name); } @@ -828,7 +851,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) ALOG_ASSERT(audio_is_linear_pcm(format), "Invalid format %#x", format); track.mFormat = format; ALOGV("setParameter(TRACK, FORMAT, %#x)", format); - prepareTrackForReformat(&track, name); + track.prepareForReformat(); invalidateState(1 << name); } } break; @@ -1032,10 +1055,13 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* bufferProvider if (mState.tracks[name].mReformatBufferProvider != NULL) { mState.tracks[name].mReformatBufferProvider->reset(); } else if (mState.tracks[name].downmixerBufferProvider != NULL) { + mState.tracks[name].downmixerBufferProvider->reset(); + } else if (mState.tracks[name].mPostDownmixReformatBufferProvider != NULL) { + mState.tracks[name].mPostDownmixReformatBufferProvider->reset(); } mState.tracks[name].mInputBufferProvider = bufferProvider; - reconfigureBufferProviders(&mState.tracks[name]); + mState.tracks[name].reconfigureBufferProviders(); } diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index f4f142b..c2ff985 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -205,17 +205,34 @@ private: int32_t* auxBuffer; // 16-byte boundary + + /* Buffer providers are constructed to translate the track input data as needed. + * + * 1) mInputBufferProvider: The AudioTrack buffer provider. + * 2) mReformatBufferProvider: If not NULL, performs the audio reformat to + * match either mMixerInFormat or mDownmixRequiresFormat, if the downmixer + * requires reformat. For example, it may convert floating point input to + * PCM_16_bit if that's required by the downmixer. + * 3) downmixerBufferProvider: If not NULL, performs the channel remixing to match + * the number of channels required by the mixer sink. + * 4) mPostDownmixReformatBufferProvider: If not NULL, performs reformatting from + * the downmixer requirements to the mixer engine input requirements. + */ AudioBufferProvider* mInputBufferProvider; // externally provided buffer provider. CopyBufferProvider* mReformatBufferProvider; // provider wrapper for reformatting. CopyBufferProvider* downmixerBufferProvider; // wrapper for channel conversion. + CopyBufferProvider* mPostDownmixReformatBufferProvider; + // 16-byte boundary int32_t sessionId; - // 16-byte boundary 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 @@ -225,7 +242,6 @@ private: float mPrevAuxLevel; // floating point prev aux level float mAuxInc; // floating point aux increment - // 16-byte boundary audio_channel_mask_t mMixerChannelMask; uint32_t mMixerChannelCount; @@ -236,6 +252,12 @@ private: void adjustVolumeRamp(bool aux, bool useFloat = false); size_t getUnreleasedFrames() const { return resampler != NULL ? resampler->getUnreleasedFrames() : 0; }; + + status_t prepareForDownmix(); + void unprepareForDownmix(); + status_t prepareForReformat(); + void unprepareForReformat(); + void reconfigureBufferProviders(); }; typedef void (*process_hook_t)(state_t* state, int64_t pts); @@ -382,14 +404,6 @@ private: bool setChannelMasks(int name, audio_channel_mask_t trackChannelMask, audio_channel_mask_t mixerChannelMask); - // TODO: remove unused trackName/trackNum from functions below. - static status_t initTrackDownmix(track_t* pTrack, int trackName); - static status_t prepareTrackForDownmix(track_t* pTrack, int trackNum); - static void unprepareTrackForDownmix(track_t* pTrack, int trackName); - static status_t prepareTrackForReformat(track_t* pTrack, int trackNum); - static void unprepareTrackForReformat(track_t* pTrack, int trackName); - static void reconfigureBufferProviders(track_t* pTrack); - static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 3f4ff3e..d5952bf 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -314,6 +314,64 @@ void CpuStats::sample(const String8 &title // ThreadBase // ---------------------------------------------------------------------------- +// static +const char *AudioFlinger::ThreadBase::threadTypeToString(AudioFlinger::ThreadBase::type_t type) +{ + switch (type) { + case MIXER: + return "MIXER"; + case DIRECT: + return "DIRECT"; + case DUPLICATING: + return "DUPLICATING"; + case RECORD: + return "RECORD"; + case OFFLOAD: + return "OFFLOAD"; + default: + return "unknown"; + } +} + +static String8 outputFlagsToString(audio_output_flags_t flags) +{ + static const struct mapping { + audio_output_flags_t mFlag; + const char * mString; + } mappings[] = { + AUDIO_OUTPUT_FLAG_DIRECT, "DIRECT", + AUDIO_OUTPUT_FLAG_PRIMARY, "PRIMARY", + AUDIO_OUTPUT_FLAG_FAST, "FAST", + AUDIO_OUTPUT_FLAG_DEEP_BUFFER, "DEEP_BUFFER", + AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD, "COMPRESS_OFFLOAAD", + AUDIO_OUTPUT_FLAG_NON_BLOCKING, "NON_BLOCKING", + AUDIO_OUTPUT_FLAG_HW_AV_SYNC, "HW_AV_SYNC", + AUDIO_OUTPUT_FLAG_NONE, "NONE", // must be last + }; + String8 result; + audio_output_flags_t allFlags = AUDIO_OUTPUT_FLAG_NONE; + const mapping *entry; + for (entry = mappings; entry->mFlag != AUDIO_OUTPUT_FLAG_NONE; entry++) { + allFlags = (audio_output_flags_t) (allFlags | entry->mFlag); + if (flags & entry->mFlag) { + if (!result.isEmpty()) { + result.append("|"); + } + result.append(entry->mString); + } + } + if (flags & ~allFlags) { + if (!result.isEmpty()) { + result.append("|"); + } + result.appendFormat("0x%X", flags & ~allFlags); + } + if (result.isEmpty()) { + result.append(entry->mString); + } + return result; +} + AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, audio_devices_t outDevice, audio_devices_t inDevice, type_t type) : Thread(false /*canCallJava*/), @@ -577,20 +635,21 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args __u bool locked = AudioFlinger::dumpTryLock(mLock); if (!locked) { - dprintf(fd, "thread %p maybe dead locked\n", this); + dprintf(fd, "thread %p may be deadlocked\n", this); } dprintf(fd, " I/O handle: %d\n", mId); dprintf(fd, " TID: %d\n", getTid()); dprintf(fd, " Standby: %s\n", mStandby ? "yes" : "no"); - dprintf(fd, " Sample rate: %u\n", mSampleRate); + dprintf(fd, " Sample rate: %u Hz\n", mSampleRate); dprintf(fd, " HAL frame count: %zu\n", mFrameCount); + dprintf(fd, " HAL format: 0x%x (%s)\n", mHALFormat, formatToString(mHALFormat)); dprintf(fd, " HAL buffer size: %u bytes\n", mBufferSize); - dprintf(fd, " Channel Count: %u\n", mChannelCount); - dprintf(fd, " Channel Mask: 0x%08x (%s)\n", mChannelMask, + dprintf(fd, " Channel count: %u\n", mChannelCount); + dprintf(fd, " Channel mask: 0x%08x (%s)\n", mChannelMask, channelMaskToString(mChannelMask, mType != RECORD).string()); - dprintf(fd, " Format: 0x%x (%s)\n", mHALFormat, formatToString(mHALFormat)); - dprintf(fd, " Frame size: %zu\n", mFrameSize); + dprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); + dprintf(fd, " Frame size: %zu bytes\n", mFrameSize); dprintf(fd, " Pending config events:"); size_t numConfig = mConfigEvents.size(); if (numConfig) { @@ -1314,7 +1373,7 @@ void AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& ar void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) { - dprintf(fd, "\nOutput thread %p:\n", this); + dprintf(fd, "\nOutput thread %p type %d (%s):\n", this, type(), threadTypeToString(type())); dprintf(fd, " Normal frame count: %zu\n", mNormalFrameCount); dprintf(fd, " Last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); dprintf(fd, " Total writes: %d\n", mNumWrites); @@ -1325,6 +1384,10 @@ void AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& dprintf(fd, " Mixer buffer: %p\n", mMixerBuffer); dprintf(fd, " Effect buffer: %p\n", mEffectBuffer); dprintf(fd, " Fast track availMask=%#x\n", mFastTrackAvailMask); + AudioStreamOut *output = mOutput; + audio_output_flags_t flags = output != NULL ? output->flags : AUDIO_OUTPUT_FLAG_NONE; + String8 flagsAsString = outputFlagsToString(flags); + dprintf(fd, " AudioStreamOut: %p flags %#x (%s)\n", output, flags, flagsAsString.string()); dumpBase(fd, args); } @@ -2123,6 +2186,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write() } else { bytesWritten = framesWritten; } + mLatchDValid = false; status_t status = mNormalSink->getTimestamp(mLatchD.mTimestamp); if (status == NO_ERROR) { size_t totalFramesWritten = mNormalSink->framesWritten(); @@ -2620,7 +2684,9 @@ bool AudioFlinger::PlaybackThread::threadLoop() } } else { + ATRACE_BEGIN("sleep"); usleep(sleepTime); + ATRACE_END(); } } @@ -2821,6 +2887,7 @@ AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, Aud NBAIO_Format format = mOutputSink->format(); NBAIO_Format origformat = format; // adjust format to match that of the Fast Mixer + ALOGV("format changed from %d to %d", format.mFormat, fastMixerFormat); format.mFormat = fastMixerFormat; format.mFrameSize = audio_bytes_per_sample(format.mFormat) * format.mChannelCount; @@ -3384,6 +3451,23 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } size_t framesReady = track->framesReady(); + if (ATRACE_ENABLED()) { + // I wish we had formatted trace names + char traceName[16]; + strcpy(traceName, "nRdy"); + int name = track->name(); + if (AudioMixer::TRACK0 <= name && + name < (int) (AudioMixer::TRACK0 + AudioMixer::MAX_NUM_TRACKS)) { + name -= AudioMixer::TRACK0; + traceName[4] = (name / 10) + '0'; + traceName[5] = (name % 10) + '0'; + } else { + traceName[4] = '?'; + traceName[5] = '?'; + } + traceName[6] = '\0'; + ATRACE_INT(traceName, framesReady); + } if ((framesReady >= minFrames) && track->isReady() && !track->isPaused() && !track->isTerminated()) { @@ -5034,7 +5118,9 @@ reacquire_wakelock: // sleep with mutex unlocked if (sleepUs > 0) { + ATRACE_BEGIN("sleep"); usleep(sleepUs); + ATRACE_END(); sleepUs = 0; } diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 119e495..09383b6 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -32,6 +32,8 @@ public: OFFLOAD // Thread class is OffloadThread }; + static const char *threadTypeToString(type_t type); + ThreadBase(const sp<AudioFlinger>& audioFlinger, audio_io_handle_t id, audio_devices_t outDevice, audio_devices_t inDevice, type_t type); virtual ~ThreadBase(); @@ -406,6 +408,7 @@ protected: audio_channel_mask_t mChannelMask; uint32_t mChannelCount; size_t mFrameSize; + // not HAL frame size, this is for output sink (to pipe to fast mixer) audio_format_t mFormat; // Source format for Recording and // Sink format for Playback. // Sink format may be different than diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index fcbf8f8..faea6ea 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -860,6 +860,7 @@ void AudioFlinger::PlaybackThread::Track::reset() if (mState == FLUSHED) { mState = IDLE; } + mPreviousValid = false; } } diff --git a/services/audioflinger/tests/mixer_to_wav_tests.sh b/services/audioflinger/tests/mixer_to_wav_tests.sh index 9b39e77..e60e6d5 100755 --- a/services/audioflinger/tests/mixer_to_wav_tests.sh +++ b/services/audioflinger/tests/mixer_to_wav_tests.sh @@ -63,8 +63,18 @@ function createwav() { # process__genericResampling # track__Resample / track__genericResample adb shell test-mixer $1 -s 48000 \ + -o /sdcard/tm48000grif.wav \ + sine:2,4000,7520 chirp:2,9200 sine:1,3000,18000 \ + sine:f,6,6000,19000 chirp:i,4,30000 + adb pull /sdcard/tm48000grif.wav $2 + +# Test: +# process__genericResampling +# track__Resample / track__genericResample + adb shell test-mixer $1 -s 48000 \ -o /sdcard/tm48000gr.wav \ - sine:2,4000,7520 chirp:2,9200 sine:1,3000,18000 + sine:2,4000,7520 chirp:2,9200 sine:1,3000,18000 \ + sine:6,6000,19000 adb pull /sdcard/tm48000gr.wav $2 # Test: diff --git a/services/audioflinger/tests/test-mixer.cpp b/services/audioflinger/tests/test-mixer.cpp index 9a4fad6..8da6245 100644 --- a/services/audioflinger/tests/test-mixer.cpp +++ b/services/audioflinger/tests/test-mixer.cpp @@ -39,7 +39,7 @@ static void usage(const char* name) { fprintf(stderr, "Usage: %s [-f] [-m] [-c channels]" " [-s sample-rate] [-o <output-file>] [-a <aux-buffer-file>] [-P csv]" " (<input-file> | <command>)+\n", name); - fprintf(stderr, " -f enable floating point input track\n"); + fprintf(stderr, " -f enable floating point input track by default\n"); fprintf(stderr, " -m enable floating point mixer output\n"); fprintf(stderr, " -c number of mixer output channels\n"); fprintf(stderr, " -s mixer sample-rate\n"); @@ -47,8 +47,8 @@ static void usage(const char* name) { fprintf(stderr, " -a <aux-buffer-file>\n"); fprintf(stderr, " -P # frames provided per call to resample() in CSV format\n"); fprintf(stderr, " <input-file> is a WAV file\n"); - fprintf(stderr, " <command> can be 'sine:<channels>,<frequency>,<samplerate>'\n"); - fprintf(stderr, " 'chirp:<channels>,<samplerate>'\n"); + fprintf(stderr, " <command> can be 'sine:[(i|f),]<channels>,<frequency>,<samplerate>'\n"); + fprintf(stderr, " 'chirp:[(i|f),]<channels>,<samplerate>'\n"); } static int writeFile(const char *filename, const void *buffer, @@ -78,6 +78,18 @@ static int writeFile(const char *filename, const void *buffer, return EXIT_SUCCESS; } +const char *parseFormat(const char *s, bool *useFloat) { + if (!strncmp(s, "f,", 2)) { + *useFloat = true; + return s + 2; + } + if (!strncmp(s, "i,", 2)) { + *useFloat = false; + return s + 2; + } + return s; +} + int main(int argc, char* argv[]) { const char* const progname = argv[0]; bool useInputFloat = false; @@ -88,8 +100,9 @@ int main(int argc, char* argv[]) { std::vector<int> Pvalues; const char* outputFilename = NULL; const char* auxFilename = NULL; - std::vector<int32_t> Names; - std::vector<SignalProvider> Providers; + std::vector<int32_t> names; + std::vector<SignalProvider> providers; + std::vector<audio_format_t> formats; for (int ch; (ch = getopt(argc, argv, "fmc:s:o:a:P:")) != -1;) { switch (ch) { @@ -138,54 +151,65 @@ int main(int argc, char* argv[]) { size_t outputFrames = 0; // create providers for each track - Providers.resize(argc); + names.resize(argc); + providers.resize(argc); + formats.resize(argc); for (int i = 0; i < argc; ++i) { static const char chirp[] = "chirp:"; static const char sine[] = "sine:"; static const double kSeconds = 1; + bool useFloat = useInputFloat; if (!strncmp(argv[i], chirp, strlen(chirp))) { std::vector<int> v; + const char *s = parseFormat(argv[i] + strlen(chirp), &useFloat); - parseCSV(argv[i] + strlen(chirp), v); + parseCSV(s, v); if (v.size() == 2) { printf("creating chirp(%d %d)\n", v[0], v[1]); - if (useInputFloat) { - Providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds); + if (useFloat) { + providers[i].setChirp<float>(v[0], 0, v[1]/2, v[1], kSeconds); + formats[i] = AUDIO_FORMAT_PCM_FLOAT; } else { - Providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds); + providers[i].setChirp<int16_t>(v[0], 0, v[1]/2, v[1], kSeconds); + formats[i] = AUDIO_FORMAT_PCM_16_BIT; } - Providers[i].setIncr(Pvalues); + providers[i].setIncr(Pvalues); } else { fprintf(stderr, "malformed input '%s'\n", argv[i]); } } else if (!strncmp(argv[i], sine, strlen(sine))) { std::vector<int> v; + const char *s = parseFormat(argv[i] + strlen(sine), &useFloat); - parseCSV(argv[i] + strlen(sine), v); + parseCSV(s, v); if (v.size() == 3) { printf("creating sine(%d %d %d)\n", v[0], v[1], v[2]); - if (useInputFloat) { - Providers[i].setSine<float>(v[0], v[1], v[2], kSeconds); + if (useFloat) { + providers[i].setSine<float>(v[0], v[1], v[2], kSeconds); + formats[i] = AUDIO_FORMAT_PCM_FLOAT; } else { - Providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds); + providers[i].setSine<int16_t>(v[0], v[1], v[2], kSeconds); + formats[i] = AUDIO_FORMAT_PCM_16_BIT; } - Providers[i].setIncr(Pvalues); + providers[i].setIncr(Pvalues); } else { fprintf(stderr, "malformed input '%s'\n", argv[i]); } } else { printf("creating filename(%s)\n", argv[i]); if (useInputFloat) { - Providers[i].setFile<float>(argv[i]); + providers[i].setFile<float>(argv[i]); + formats[i] = AUDIO_FORMAT_PCM_FLOAT; } else { - Providers[i].setFile<short>(argv[i]); + providers[i].setFile<short>(argv[i]); + formats[i] = AUDIO_FORMAT_PCM_16_BIT; } - Providers[i].setIncr(Pvalues); + providers[i].setIncr(Pvalues); } // calculate the number of output frames - size_t nframes = (int64_t) Providers[i].getNumFrames() * outputSampleRate - / Providers[i].getSampleRate(); + size_t nframes = (int64_t) providers[i].getNumFrames() * outputSampleRate + / providers[i].getSampleRate(); if (i == 0 || outputFrames > nframes) { // choose minimum for outputFrames outputFrames = nframes; } @@ -213,22 +237,20 @@ int main(int argc, char* argv[]) { // create the mixer. const size_t mixerFrameCount = 320; // typical numbers may range from 240 or 960 AudioMixer *mixer = new AudioMixer(mixerFrameCount, outputSampleRate); - audio_format_t inputFormat = useInputFloat - ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; audio_format_t mixerFormat = useMixerFloat ? AUDIO_FORMAT_PCM_FLOAT : AUDIO_FORMAT_PCM_16_BIT; - float f = AudioMixer::UNITY_GAIN_FLOAT / Providers.size(); // normalize volume by # tracks + float f = AudioMixer::UNITY_GAIN_FLOAT / providers.size(); // normalize volume by # tracks static float f0; // zero // set up the tracks. - for (size_t i = 0; i < Providers.size(); ++i) { - //printf("track %d out of %d\n", i, Providers.size()); - uint32_t channelMask = audio_channel_out_mask_from_count(Providers[i].getNumChannels()); + for (size_t i = 0; i < providers.size(); ++i) { + //printf("track %d out of %d\n", i, providers.size()); + uint32_t channelMask = audio_channel_out_mask_from_count(providers[i].getNumChannels()); int32_t name = mixer->getTrackName(channelMask, - inputFormat, AUDIO_SESSION_OUTPUT_MIX); + formats[i], AUDIO_SESSION_OUTPUT_MIX); ALOG_ASSERT(name >= 0); - Names.push_back(name); - mixer->setBufferProvider(name, &Providers[i]); + names[i] = name; + mixer->setBufferProvider(name, &providers[i]); mixer->setParameter(name, AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (void *)outputAddr); mixer->setParameter( @@ -240,7 +262,7 @@ int main(int argc, char* argv[]) { name, AudioMixer::TRACK, AudioMixer::FORMAT, - (void *)(uintptr_t)inputFormat); + (void *)(uintptr_t)formats[i]); mixer->setParameter( name, AudioMixer::TRACK, @@ -255,7 +277,7 @@ int main(int argc, char* argv[]) { name, AudioMixer::RESAMPLE, AudioMixer::SAMPLE_RATE, - (void *)(uintptr_t)Providers[i].getSampleRate()); + (void *)(uintptr_t)providers[i].getSampleRate()); if (useRamp) { mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME0, &f0); mixer->setParameter(name, AudioMixer::VOLUME, AudioMixer::VOLUME1, &f0); @@ -277,11 +299,11 @@ int main(int argc, char* argv[]) { // pump the mixer to process data. size_t i; for (i = 0; i < outputFrames - mixerFrameCount; i += mixerFrameCount) { - for (size_t j = 0; j < Names.size(); ++j) { - mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, + for (size_t j = 0; j < names.size(); ++j) { + mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::MAIN_BUFFER, (char *) outputAddr + i * outputFrameSize); if (auxFilename) { - mixer->setParameter(Names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER, + mixer->setParameter(names[j], AudioMixer::TRACK, AudioMixer::AUX_BUFFER, (char *) auxAddr + i * auxFrameSize); } } |