| /* //device/include/server/AudioFlinger/AudioFlinger.cpp |
| ** |
| ** Copyright 2007, 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. |
| */ |
| |
| |
| #define LOG_TAG "AudioFlinger" |
| //#define LOG_NDEBUG 0 |
| |
| #include <math.h> |
| #include <signal.h> |
| #include <sys/time.h> |
| #include <sys/resource.h> |
| |
| #include <binder/IPCThreadState.h> |
| #include <binder/IServiceManager.h> |
| #include <utils/Log.h> |
| #include <binder/Parcel.h> |
| #include <binder/IPCThreadState.h> |
| #include <utils/String16.h> |
| #include <utils/threads.h> |
| #include <utils/Atomic.h> |
| |
| #include <cutils/bitops.h> |
| #include <cutils/properties.h> |
| |
| #include <media/AudioTrack.h> |
| #include <media/AudioRecord.h> |
| #include <media/IMediaPlayerService.h> |
| |
| #include <private/media/AudioTrackShared.h> |
| #include <private/media/AudioEffectShared.h> |
| |
| #include <system/audio.h> |
| #include <hardware/audio.h> |
| |
| #include "AudioMixer.h" |
| #include "AudioFlinger.h" |
| |
| #include <media/EffectsFactoryApi.h> |
| #include <audio_effects/effect_visualizer.h> |
| |
| // ---------------------------------------------------------------------------- |
| |
| |
| namespace android { |
| |
| static const char* kDeadlockedString = "AudioFlinger may be deadlocked\n"; |
| static const char* kHardwareLockedString = "Hardware lock is taken\n"; |
| |
| //static const nsecs_t kStandbyTimeInNsecs = seconds(3); |
| static const float MAX_GAIN = 4096.0f; |
| static const float MAX_GAIN_INT = 0x1000; |
| |
| // retry counts for buffer fill timeout |
| // 50 * ~20msecs = 1 second |
| static const int8_t kMaxTrackRetries = 50; |
| static const int8_t kMaxTrackStartupRetries = 50; |
| // allow less retry attempts on direct output thread. |
| // direct outputs can be a scarce resource in audio hardware and should |
| // be released as quickly as possible. |
| static const int8_t kMaxTrackRetriesDirect = 2; |
| |
| static const int kDumpLockRetries = 50; |
| static const int kDumpLockSleep = 20000; |
| |
| static const nsecs_t kWarningThrottle = seconds(5); |
| |
| |
| // ---------------------------------------------------------------------------- |
| |
| static bool recordingAllowed() { |
| if (getpid() == IPCThreadState::self()->getCallingPid()) return true; |
| bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO")); |
| if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO"); |
| return ok; |
| } |
| |
| static bool settingsAllowed() { |
| if (getpid() == IPCThreadState::self()->getCallingPid()) return true; |
| bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")); |
| if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS"); |
| return ok; |
| } |
| |
| // To collect the amplifier usage |
| static void addBatteryData(uint32_t params) { |
| sp<IBinder> binder = |
| defaultServiceManager()->getService(String16("media.player")); |
| sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); |
| if (service.get() == NULL) { |
| LOGW("Cannot connect to the MediaPlayerService for battery tracking"); |
| return; |
| } |
| |
| service->addBatteryData(params); |
| } |
| |
| static int load_audio_interface(const char *if_name, const hw_module_t **mod, |
| audio_hw_device_t **dev) |
| { |
| int rc; |
| |
| rc = hw_get_module_by_class(AUDIO_HARDWARE_MODULE_ID, if_name, mod); |
| if (rc) |
| goto out; |
| |
| rc = audio_hw_device_open(*mod, dev); |
| LOGE_IF(rc, "couldn't open audio hw device in %s.%s (%s)", |
| AUDIO_HARDWARE_MODULE_ID, if_name, strerror(-rc)); |
| if (rc) |
| goto out; |
| |
| return 0; |
| |
| out: |
| *mod = NULL; |
| *dev = NULL; |
| return rc; |
| } |
| |
| static const char *audio_interfaces[] = { |
| "primary", |
| "a2dp", |
| "usb", |
| }; |
| #define ARRAY_SIZE(x) (sizeof((x))/sizeof(((x)[0]))) |
| |
| // ---------------------------------------------------------------------------- |
| |
| AudioFlinger::AudioFlinger() |
| : BnAudioFlinger(), |
| mPrimaryHardwareDev(0), mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1) |
| { |
| } |
| |
| void AudioFlinger::onFirstRef() |
| { |
| int rc = 0; |
| |
| Mutex::Autolock _l(mLock); |
| |
| /* TODO: move all this work into an Init() function */ |
| mHardwareStatus = AUDIO_HW_IDLE; |
| |
| for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) { |
| const hw_module_t *mod; |
| audio_hw_device_t *dev; |
| |
| rc = load_audio_interface(audio_interfaces[i], &mod, &dev); |
| if (rc) |
| continue; |
| |
| LOGI("Loaded %s audio interface from %s (%s)", audio_interfaces[i], |
| mod->name, mod->id); |
| mAudioHwDevs.push(dev); |
| |
| if (!mPrimaryHardwareDev) { |
| mPrimaryHardwareDev = dev; |
| LOGI("Using '%s' (%s.%s) as the primary audio interface", |
| mod->name, mod->id, audio_interfaces[i]); |
| } |
| } |
| |
| mHardwareStatus = AUDIO_HW_INIT; |
| |
| if (!mPrimaryHardwareDev || mAudioHwDevs.size() == 0) { |
| LOGE("Primary audio interface not found"); |
| return; |
| } |
| |
| for (size_t i = 0; i < mAudioHwDevs.size(); i++) { |
| audio_hw_device_t *dev = mAudioHwDevs[i]; |
| |
| mHardwareStatus = AUDIO_HW_INIT; |
| rc = dev->init_check(dev); |
| if (rc == 0) { |
| AutoMutex lock(mHardwareLock); |
| |
| mMode = AUDIO_MODE_NORMAL; |
| mHardwareStatus = AUDIO_HW_SET_MODE; |
| dev->set_mode(dev, mMode); |
| mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; |
| dev->set_master_volume(dev, 1.0f); |
| mHardwareStatus = AUDIO_HW_IDLE; |
| } |
| } |
| } |
| |
| status_t AudioFlinger::initCheck() const |
| { |
| Mutex::Autolock _l(mLock); |
| if (mPrimaryHardwareDev == NULL || mAudioHwDevs.size() == 0) |
| return NO_INIT; |
| return NO_ERROR; |
| } |
| |
| AudioFlinger::~AudioFlinger() |
| { |
| int num_devs = mAudioHwDevs.size(); |
| |
| while (!mRecordThreads.isEmpty()) { |
| // closeInput() will remove first entry from mRecordThreads |
| closeInput(mRecordThreads.keyAt(0)); |
| } |
| while (!mPlaybackThreads.isEmpty()) { |
| // closeOutput() will remove first entry from mPlaybackThreads |
| closeOutput(mPlaybackThreads.keyAt(0)); |
| } |
| |
| for (int i = 0; i < num_devs; i++) { |
| audio_hw_device_t *dev = mAudioHwDevs[i]; |
| audio_hw_device_close(dev); |
| } |
| mAudioHwDevs.clear(); |
| } |
| |
| audio_hw_device_t* AudioFlinger::findSuitableHwDev_l(uint32_t devices) |
| { |
| /* first matching HW device is returned */ |
| for (size_t i = 0; i < mAudioHwDevs.size(); i++) { |
| audio_hw_device_t *dev = mAudioHwDevs[i]; |
| if ((dev->get_supported_devices(dev) & devices) == devices) |
| return dev; |
| } |
| return NULL; |
| } |
| |
| status_t AudioFlinger::dumpClients(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| |
| result.append("Clients:\n"); |
| for (size_t i = 0; i < mClients.size(); ++i) { |
| wp<Client> wClient = mClients.valueAt(i); |
| if (wClient != 0) { |
| sp<Client> client = wClient.promote(); |
| if (client != 0) { |
| snprintf(buffer, SIZE, " pid: %d\n", client->pid()); |
| result.append(buffer); |
| } |
| } |
| } |
| write(fd, result.string(), result.size()); |
| return NO_ERROR; |
| } |
| |
| |
| status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| int hardwareStatus = mHardwareStatus; |
| |
| snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus); |
| result.append(buffer); |
| write(fd, result.string(), result.size()); |
| return NO_ERROR; |
| } |
| |
| status_t AudioFlinger::dumpPermissionDenial(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| snprintf(buffer, SIZE, "Permission Denial: " |
| "can't dump AudioFlinger from pid=%d, uid=%d\n", |
| IPCThreadState::self()->getCallingPid(), |
| IPCThreadState::self()->getCallingUid()); |
| result.append(buffer); |
| write(fd, result.string(), result.size()); |
| return NO_ERROR; |
| } |
| |
| static bool tryLock(Mutex& mutex) |
| { |
| bool locked = false; |
| for (int i = 0; i < kDumpLockRetries; ++i) { |
| if (mutex.tryLock() == NO_ERROR) { |
| locked = true; |
| break; |
| } |
| usleep(kDumpLockSleep); |
| } |
| return locked; |
| } |
| |
| status_t AudioFlinger::dump(int fd, const Vector<String16>& args) |
| { |
| if (checkCallingPermission(String16("android.permission.DUMP")) == false) { |
| dumpPermissionDenial(fd, args); |
| } else { |
| // get state of hardware lock |
| bool hardwareLocked = tryLock(mHardwareLock); |
| if (!hardwareLocked) { |
| String8 result(kHardwareLockedString); |
| write(fd, result.string(), result.size()); |
| } else { |
| mHardwareLock.unlock(); |
| } |
| |
| bool locked = tryLock(mLock); |
| |
| // failed to lock - AudioFlinger is probably deadlocked |
| if (!locked) { |
| String8 result(kDeadlockedString); |
| write(fd, result.string(), result.size()); |
| } |
| |
| dumpClients(fd, args); |
| dumpInternals(fd, args); |
| |
| // dump playback threads |
| for (size_t i = 0; i < mPlaybackThreads.size(); i++) { |
| mPlaybackThreads.valueAt(i)->dump(fd, args); |
| } |
| |
| // dump record threads |
| for (size_t i = 0; i < mRecordThreads.size(); i++) { |
| mRecordThreads.valueAt(i)->dump(fd, args); |
| } |
| |
| // dump all hardware devs |
| for (size_t i = 0; i < mAudioHwDevs.size(); i++) { |
| audio_hw_device_t *dev = mAudioHwDevs[i]; |
| dev->dump(dev, fd); |
| } |
| if (locked) mLock.unlock(); |
| } |
| return NO_ERROR; |
| } |
| |
| |
| // IAudioFlinger interface |
| |
| |
| sp<IAudioTrack> AudioFlinger::createTrack( |
| pid_t pid, |
| int streamType, |
| uint32_t sampleRate, |
| uint32_t format, |
| uint32_t channelMask, |
| int frameCount, |
| uint32_t flags, |
| const sp<IMemory>& sharedBuffer, |
| int output, |
| int *sessionId, |
| status_t *status) |
| { |
| sp<PlaybackThread::Track> track; |
| sp<TrackHandle> trackHandle; |
| sp<Client> client; |
| wp<Client> wclient; |
| status_t lStatus; |
| int lSessionId; |
| |
| if (streamType >= AUDIO_STREAM_CNT) { |
| LOGE("invalid stream type"); |
| lStatus = BAD_VALUE; |
| goto Exit; |
| } |
| |
| { |
| Mutex::Autolock _l(mLock); |
| PlaybackThread *thread = checkPlaybackThread_l(output); |
| PlaybackThread *effectThread = NULL; |
| if (thread == NULL) { |
| LOGE("unknown output thread"); |
| lStatus = BAD_VALUE; |
| goto Exit; |
| } |
| |
| wclient = mClients.valueFor(pid); |
| |
| if (wclient != NULL) { |
| client = wclient.promote(); |
| } else { |
| client = new Client(this, pid); |
| mClients.add(pid, client); |
| } |
| |
| LOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId); |
| if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) { |
| for (size_t i = 0; i < mPlaybackThreads.size(); i++) { |
| sp<PlaybackThread> t = mPlaybackThreads.valueAt(i); |
| if (mPlaybackThreads.keyAt(i) != output) { |
| // prevent same audio session on different output threads |
| uint32_t sessions = t->hasAudioSession(*sessionId); |
| if (sessions & PlaybackThread::TRACK_SESSION) { |
| lStatus = BAD_VALUE; |
| goto Exit; |
| } |
| // check if an effect with same session ID is waiting for a track to be created |
| if (sessions & PlaybackThread::EFFECT_SESSION) { |
| effectThread = t.get(); |
| } |
| } |
| } |
| lSessionId = *sessionId; |
| } else { |
| // if no audio session id is provided, create one here |
| lSessionId = nextUniqueId_l(); |
| if (sessionId != NULL) { |
| *sessionId = lSessionId; |
| } |
| } |
| LOGV("createTrack() lSessionId: %d", lSessionId); |
| |
| track = thread->createTrack_l(client, streamType, sampleRate, format, |
| channelMask, frameCount, sharedBuffer, lSessionId, &lStatus); |
| |
| // move effect chain to this output thread if an effect on same session was waiting |
| // for a track to be created |
| if (lStatus == NO_ERROR && effectThread != NULL) { |
| Mutex::Autolock _dl(thread->mLock); |
| Mutex::Autolock _sl(effectThread->mLock); |
| moveEffectChain_l(lSessionId, effectThread, thread, true); |
| } |
| } |
| if (lStatus == NO_ERROR) { |
| trackHandle = new TrackHandle(track); |
| } else { |
| // remove local strong reference to Client before deleting the Track so that the Client |
| // destructor is called by the TrackBase destructor with mLock held |
| client.clear(); |
| track.clear(); |
| } |
| |
| Exit: |
| if(status) { |
| *status = lStatus; |
| } |
| return trackHandle; |
| } |
| |
| uint32_t AudioFlinger::sampleRate(int output) const |
| { |
| Mutex::Autolock _l(mLock); |
| PlaybackThread *thread = checkPlaybackThread_l(output); |
| if (thread == NULL) { |
| LOGW("sampleRate() unknown thread %d", output); |
| return 0; |
| } |
| return thread->sampleRate(); |
| } |
| |
| int AudioFlinger::channelCount(int output) const |
| { |
| Mutex::Autolock _l(mLock); |
| PlaybackThread *thread = checkPlaybackThread_l(output); |
| if (thread == NULL) { |
| LOGW("channelCount() unknown thread %d", output); |
| return 0; |
| } |
| return thread->channelCount(); |
| } |
| |
| uint32_t AudioFlinger::format(int output) const |
| { |
| Mutex::Autolock _l(mLock); |
| PlaybackThread *thread = checkPlaybackThread_l(output); |
| if (thread == NULL) { |
| LOGW("format() unknown thread %d", output); |
| return 0; |
| } |
| return thread->format(); |
| } |
| |
| size_t AudioFlinger::frameCount(int output) const |
| { |
| Mutex::Autolock _l(mLock); |
| PlaybackThread *thread = checkPlaybackThread_l(output); |
| if (thread == NULL) { |
| LOGW("frameCount() unknown thread %d", output); |
| return 0; |
| } |
| return thread->frameCount(); |
| } |
| |
| uint32_t AudioFlinger::latency(int output) const |
| { |
| Mutex::Autolock _l(mLock); |
| PlaybackThread *thread = checkPlaybackThread_l(output); |
| if (thread == NULL) { |
| LOGW("latency() unknown thread %d", output); |
| return 0; |
| } |
| return thread->latency(); |
| } |
| |
| status_t AudioFlinger::setMasterVolume(float value) |
| { |
| // check calling permissions |
| if (!settingsAllowed()) { |
| return PERMISSION_DENIED; |
| } |
| |
| // when hw supports master volume, don't scale in sw mixer |
| { // scope for the lock |
| AutoMutex lock(mHardwareLock); |
| mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; |
| if (mPrimaryHardwareDev->set_master_volume(mPrimaryHardwareDev, value) == NO_ERROR) { |
| value = 1.0f; |
| } |
| mHardwareStatus = AUDIO_HW_IDLE; |
| } |
| |
| Mutex::Autolock _l(mLock); |
| mMasterVolume = value; |
| for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) |
| mPlaybackThreads.valueAt(i)->setMasterVolume(value); |
| |
| return NO_ERROR; |
| } |
| |
| status_t AudioFlinger::setMode(int mode) |
| { |
| status_t ret; |
| |
| // check calling permissions |
| if (!settingsAllowed()) { |
| return PERMISSION_DENIED; |
| } |
| if ((mode < 0) || (mode >= AUDIO_MODE_CNT)) { |
| LOGW("Illegal value: setMode(%d)", mode); |
| return BAD_VALUE; |
| } |
| |
| { // scope for the lock |
| AutoMutex lock(mHardwareLock); |
| mHardwareStatus = AUDIO_HW_SET_MODE; |
| ret = mPrimaryHardwareDev->set_mode(mPrimaryHardwareDev, mode); |
| mHardwareStatus = AUDIO_HW_IDLE; |
| } |
| |
| if (NO_ERROR == ret) { |
| Mutex::Autolock _l(mLock); |
| mMode = mode; |
| for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) |
| mPlaybackThreads.valueAt(i)->setMode(mode); |
| } |
| |
| return ret; |
| } |
| |
| status_t AudioFlinger::setMicMute(bool state) |
| { |
| // check calling permissions |
| if (!settingsAllowed()) { |
| return PERMISSION_DENIED; |
| } |
| |
| AutoMutex lock(mHardwareLock); |
| mHardwareStatus = AUDIO_HW_SET_MIC_MUTE; |
| status_t ret = mPrimaryHardwareDev->set_mic_mute(mPrimaryHardwareDev, state); |
| mHardwareStatus = AUDIO_HW_IDLE; |
| return ret; |
| } |
| |
| bool AudioFlinger::getMicMute() const |
| { |
| bool state = AUDIO_MODE_INVALID; |
| mHardwareStatus = AUDIO_HW_GET_MIC_MUTE; |
| mPrimaryHardwareDev->get_mic_mute(mPrimaryHardwareDev, &state); |
| mHardwareStatus = AUDIO_HW_IDLE; |
| return state; |
| } |
| |
| status_t AudioFlinger::setMasterMute(bool muted) |
| { |
| // check calling permissions |
| if (!settingsAllowed()) { |
| return PERMISSION_DENIED; |
| } |
| |
| Mutex::Autolock _l(mLock); |
| mMasterMute = muted; |
| for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) |
| mPlaybackThreads.valueAt(i)->setMasterMute(muted); |
| |
| return NO_ERROR; |
| } |
| |
| float AudioFlinger::masterVolume() const |
| { |
| return mMasterVolume; |
| } |
| |
| bool AudioFlinger::masterMute() const |
| { |
| return mMasterMute; |
| } |
| |
| status_t AudioFlinger::setStreamVolume(int stream, float value, int output) |
| { |
| // check calling permissions |
| if (!settingsAllowed()) { |
| return PERMISSION_DENIED; |
| } |
| |
| if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT) { |
| return BAD_VALUE; |
| } |
| |
| AutoMutex lock(mLock); |
| PlaybackThread *thread = NULL; |
| if (output) { |
| thread = checkPlaybackThread_l(output); |
| if (thread == NULL) { |
| return BAD_VALUE; |
| } |
| } |
| |
| mStreamTypes[stream].volume = value; |
| |
| if (thread == NULL) { |
| for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) { |
| mPlaybackThreads.valueAt(i)->setStreamVolume(stream, value); |
| } |
| } else { |
| thread->setStreamVolume(stream, value); |
| } |
| |
| return NO_ERROR; |
| } |
| |
| status_t AudioFlinger::setStreamMute(int stream, bool muted) |
| { |
| // check calling permissions |
| if (!settingsAllowed()) { |
| return PERMISSION_DENIED; |
| } |
| |
| if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT || |
| uint32_t(stream) == AUDIO_STREAM_ENFORCED_AUDIBLE) { |
| return BAD_VALUE; |
| } |
| |
| AutoMutex lock(mLock); |
| mStreamTypes[stream].mute = muted; |
| for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) |
| mPlaybackThreads.valueAt(i)->setStreamMute(stream, muted); |
| |
| return NO_ERROR; |
| } |
| |
| float AudioFlinger::streamVolume(int stream, int output) const |
| { |
| if (stream < 0 || uint32_t(stream) >= AUDIO_STREAM_CNT) { |
| return 0.0f; |
| } |
| |
| AutoMutex lock(mLock); |
| float volume; |
| if (output) { |
| PlaybackThread *thread = checkPlaybackThread_l(output); |
| if (thread == NULL) { |
| return 0.0f; |
| } |
| volume = thread->streamVolume(stream); |
| } else { |
| volume = mStreamTypes[stream].volume; |
| } |
| |
| return volume; |
| } |
| |
| bool AudioFlinger::streamMute(int stream) const |
| { |
| if (stream < 0 || stream >= (int)AUDIO_STREAM_CNT) { |
| return true; |
| } |
| |
| return mStreamTypes[stream].mute; |
| } |
| |
| status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) |
| { |
| status_t result; |
| |
| LOGV("setParameters(): io %d, keyvalue %s, tid %d, calling tid %d", |
| ioHandle, keyValuePairs.string(), gettid(), IPCThreadState::self()->getCallingPid()); |
| // check calling permissions |
| if (!settingsAllowed()) { |
| return PERMISSION_DENIED; |
| } |
| |
| // ioHandle == 0 means the parameters are global to the audio hardware interface |
| if (ioHandle == 0) { |
| AutoMutex lock(mHardwareLock); |
| mHardwareStatus = AUDIO_SET_PARAMETER; |
| status_t final_result = NO_ERROR; |
| for (size_t i = 0; i < mAudioHwDevs.size(); i++) { |
| audio_hw_device_t *dev = mAudioHwDevs[i]; |
| result = dev->set_parameters(dev, keyValuePairs.string()); |
| final_result = result ?: final_result; |
| } |
| mHardwareStatus = AUDIO_HW_IDLE; |
| return final_result; |
| } |
| |
| // hold a strong ref on thread in case closeOutput() or closeInput() is called |
| // and the thread is exited once the lock is released |
| sp<ThreadBase> thread; |
| { |
| Mutex::Autolock _l(mLock); |
| thread = checkPlaybackThread_l(ioHandle); |
| if (thread == NULL) { |
| thread = checkRecordThread_l(ioHandle); |
| } |
| } |
| if (thread != NULL) { |
| result = thread->setParameters(keyValuePairs); |
| return result; |
| } |
| return BAD_VALUE; |
| } |
| |
| String8 AudioFlinger::getParameters(int ioHandle, const String8& keys) |
| { |
| // LOGV("getParameters() io %d, keys %s, tid %d, calling tid %d", |
| // ioHandle, keys.string(), gettid(), IPCThreadState::self()->getCallingPid()); |
| |
| if (ioHandle == 0) { |
| String8 out_s8; |
| |
| for (size_t i = 0; i < mAudioHwDevs.size(); i++) { |
| audio_hw_device_t *dev = mAudioHwDevs[i]; |
| char *s = dev->get_parameters(dev, keys.string()); |
| out_s8 += String8(s); |
| free(s); |
| } |
| return out_s8; |
| } |
| |
| Mutex::Autolock _l(mLock); |
| |
| PlaybackThread *playbackThread = checkPlaybackThread_l(ioHandle); |
| if (playbackThread != NULL) { |
| return playbackThread->getParameters(keys); |
| } |
| RecordThread *recordThread = checkRecordThread_l(ioHandle); |
| if (recordThread != NULL) { |
| return recordThread->getParameters(keys); |
| } |
| return String8(""); |
| } |
| |
| size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, int format, int channelCount) |
| { |
| return mPrimaryHardwareDev->get_input_buffer_size(mPrimaryHardwareDev, sampleRate, format, channelCount); |
| } |
| |
| unsigned int AudioFlinger::getInputFramesLost(int ioHandle) |
| { |
| if (ioHandle == 0) { |
| return 0; |
| } |
| |
| Mutex::Autolock _l(mLock); |
| |
| RecordThread *recordThread = checkRecordThread_l(ioHandle); |
| if (recordThread != NULL) { |
| return recordThread->getInputFramesLost(); |
| } |
| return 0; |
| } |
| |
| status_t AudioFlinger::setVoiceVolume(float value) |
| { |
| // check calling permissions |
| if (!settingsAllowed()) { |
| return PERMISSION_DENIED; |
| } |
| |
| AutoMutex lock(mHardwareLock); |
| mHardwareStatus = AUDIO_SET_VOICE_VOLUME; |
| status_t ret = mPrimaryHardwareDev->set_voice_volume(mPrimaryHardwareDev, value); |
| mHardwareStatus = AUDIO_HW_IDLE; |
| |
| return ret; |
| } |
| |
| status_t AudioFlinger::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames, int output) |
| { |
| status_t status; |
| |
| Mutex::Autolock _l(mLock); |
| |
| PlaybackThread *playbackThread = checkPlaybackThread_l(output); |
| if (playbackThread != NULL) { |
| return playbackThread->getRenderPosition(halFrames, dspFrames); |
| } |
| |
| return BAD_VALUE; |
| } |
| |
| void AudioFlinger::registerClient(const sp<IAudioFlingerClient>& client) |
| { |
| |
| Mutex::Autolock _l(mLock); |
| |
| int pid = IPCThreadState::self()->getCallingPid(); |
| if (mNotificationClients.indexOfKey(pid) < 0) { |
| sp<NotificationClient> notificationClient = new NotificationClient(this, |
| client, |
| pid); |
| LOGV("registerClient() client %p, pid %d", notificationClient.get(), pid); |
| |
| mNotificationClients.add(pid, notificationClient); |
| |
| sp<IBinder> binder = client->asBinder(); |
| binder->linkToDeath(notificationClient); |
| |
| // the config change is always sent from playback or record threads to avoid deadlock |
| // with AudioSystem::gLock |
| for (size_t i = 0; i < mPlaybackThreads.size(); i++) { |
| mPlaybackThreads.valueAt(i)->sendConfigEvent(AudioSystem::OUTPUT_OPENED); |
| } |
| |
| for (size_t i = 0; i < mRecordThreads.size(); i++) { |
| mRecordThreads.valueAt(i)->sendConfigEvent(AudioSystem::INPUT_OPENED); |
| } |
| } |
| } |
| |
| void AudioFlinger::removeNotificationClient(pid_t pid) |
| { |
| Mutex::Autolock _l(mLock); |
| |
| int index = mNotificationClients.indexOfKey(pid); |
| if (index >= 0) { |
| sp <NotificationClient> client = mNotificationClients.valueFor(pid); |
| LOGV("removeNotificationClient() %p, pid %d", client.get(), pid); |
| mNotificationClients.removeItem(pid); |
| } |
| } |
| |
| // audioConfigChanged_l() must be called with AudioFlinger::mLock held |
| void AudioFlinger::audioConfigChanged_l(int event, int ioHandle, void *param2) |
| { |
| size_t size = mNotificationClients.size(); |
| for (size_t i = 0; i < size; i++) { |
| mNotificationClients.valueAt(i)->client()->ioConfigChanged(event, ioHandle, param2); |
| } |
| } |
| |
| // removeClient_l() must be called with AudioFlinger::mLock held |
| void AudioFlinger::removeClient_l(pid_t pid) |
| { |
| LOGV("removeClient_l() pid %d, tid %d, calling tid %d", pid, gettid(), IPCThreadState::self()->getCallingPid()); |
| mClients.removeItem(pid); |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| |
| AudioFlinger::ThreadBase::ThreadBase(const sp<AudioFlinger>& audioFlinger, int id) |
| : Thread(false), |
| mAudioFlinger(audioFlinger), mSampleRate(0), mFrameCount(0), mChannelCount(0), |
| mFrameSize(1), mFormat(0), mStandby(false), mId(id), mExiting(false) |
| { |
| } |
| |
| AudioFlinger::ThreadBase::~ThreadBase() |
| { |
| mParamCond.broadcast(); |
| mNewParameters.clear(); |
| } |
| |
| void AudioFlinger::ThreadBase::exit() |
| { |
| // keep a strong ref on ourself so that we wont get |
| // destroyed in the middle of requestExitAndWait() |
| sp <ThreadBase> strongMe = this; |
| |
| LOGV("ThreadBase::exit"); |
| { |
| AutoMutex lock(&mLock); |
| mExiting = true; |
| requestExit(); |
| mWaitWorkCV.signal(); |
| } |
| requestExitAndWait(); |
| } |
| |
| uint32_t AudioFlinger::ThreadBase::sampleRate() const |
| { |
| return mSampleRate; |
| } |
| |
| int AudioFlinger::ThreadBase::channelCount() const |
| { |
| return (int)mChannelCount; |
| } |
| |
| uint32_t AudioFlinger::ThreadBase::format() const |
| { |
| return mFormat; |
| } |
| |
| size_t AudioFlinger::ThreadBase::frameCount() const |
| { |
| return mFrameCount; |
| } |
| |
| status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs) |
| { |
| status_t status; |
| |
| LOGV("ThreadBase::setParameters() %s", keyValuePairs.string()); |
| Mutex::Autolock _l(mLock); |
| |
| mNewParameters.add(keyValuePairs); |
| mWaitWorkCV.signal(); |
| // wait condition with timeout in case the thread loop has exited |
| // before the request could be processed |
| if (mParamCond.waitRelative(mLock, seconds(2)) == NO_ERROR) { |
| status = mParamStatus; |
| mWaitWorkCV.signal(); |
| } else { |
| status = TIMED_OUT; |
| } |
| return status; |
| } |
| |
| void AudioFlinger::ThreadBase::sendConfigEvent(int event, int param) |
| { |
| Mutex::Autolock _l(mLock); |
| sendConfigEvent_l(event, param); |
| } |
| |
| // sendConfigEvent_l() must be called with ThreadBase::mLock held |
| void AudioFlinger::ThreadBase::sendConfigEvent_l(int event, int param) |
| { |
| ConfigEvent *configEvent = new ConfigEvent(); |
| configEvent->mEvent = event; |
| configEvent->mParam = param; |
| mConfigEvents.add(configEvent); |
| LOGV("sendConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, param); |
| mWaitWorkCV.signal(); |
| } |
| |
| void AudioFlinger::ThreadBase::processConfigEvents() |
| { |
| mLock.lock(); |
| while(!mConfigEvents.isEmpty()) { |
| LOGV("processConfigEvents() remaining events %d", mConfigEvents.size()); |
| ConfigEvent *configEvent = mConfigEvents[0]; |
| mConfigEvents.removeAt(0); |
| // release mLock before locking AudioFlinger mLock: lock order is always |
| // AudioFlinger then ThreadBase to avoid cross deadlock |
| mLock.unlock(); |
| mAudioFlinger->mLock.lock(); |
| audioConfigChanged_l(configEvent->mEvent, configEvent->mParam); |
| mAudioFlinger->mLock.unlock(); |
| delete configEvent; |
| mLock.lock(); |
| } |
| mLock.unlock(); |
| } |
| |
| status_t AudioFlinger::ThreadBase::dumpBase(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| |
| bool locked = tryLock(mLock); |
| if (!locked) { |
| snprintf(buffer, SIZE, "thread %p maybe dead locked\n", this); |
| write(fd, buffer, strlen(buffer)); |
| } |
| |
| snprintf(buffer, SIZE, "standby: %d\n", mStandby); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "Sample rate: %d\n", mSampleRate); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "Frame count: %d\n", mFrameCount); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "Channel Count: %d\n", mChannelCount); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "Channel Mask: 0x%08x\n", mChannelMask); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "Format: %d\n", mFormat); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "Frame size: %d\n", mFrameSize); |
| result.append(buffer); |
| |
| snprintf(buffer, SIZE, "\nPending setParameters commands: \n"); |
| result.append(buffer); |
| result.append(" Index Command"); |
| for (size_t i = 0; i < mNewParameters.size(); ++i) { |
| snprintf(buffer, SIZE, "\n %02d ", i); |
| result.append(buffer); |
| result.append(mNewParameters[i]); |
| } |
| |
| snprintf(buffer, SIZE, "\n\nPending config events: \n"); |
| result.append(buffer); |
| snprintf(buffer, SIZE, " Index event param\n"); |
| result.append(buffer); |
| for (size_t i = 0; i < mConfigEvents.size(); i++) { |
| snprintf(buffer, SIZE, " %02d %02d %d\n", i, mConfigEvents[i]->mEvent, mConfigEvents[i]->mParam); |
| result.append(buffer); |
| } |
| result.append("\n"); |
| |
| write(fd, result.string(), result.size()); |
| |
| if (locked) { |
| mLock.unlock(); |
| } |
| return NO_ERROR; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| |
| AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device) |
| : ThreadBase(audioFlinger, id), |
| mMixBuffer(0), mSuspended(0), mBytesWritten(0), mOutput(output), |
| mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false), |
| mDevice(device) |
| { |
| readOutputParameters(); |
| |
| mMasterVolume = mAudioFlinger->masterVolume(); |
| mMasterMute = mAudioFlinger->masterMute(); |
| |
| for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) { |
| mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream); |
| mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream); |
| } |
| } |
| |
| AudioFlinger::PlaybackThread::~PlaybackThread() |
| { |
| delete [] mMixBuffer; |
| } |
| |
| status_t AudioFlinger::PlaybackThread::dump(int fd, const Vector<String16>& args) |
| { |
| dumpInternals(fd, args); |
| dumpTracks(fd, args); |
| dumpEffectChains(fd, args); |
| return NO_ERROR; |
| } |
| |
| status_t AudioFlinger::PlaybackThread::dumpTracks(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| |
| snprintf(buffer, SIZE, "Output thread %p tracks\n", this); |
| result.append(buffer); |
| result.append(" Name Clien Typ Fmt Chn mask Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n"); |
| for (size_t i = 0; i < mTracks.size(); ++i) { |
| sp<Track> track = mTracks[i]; |
| if (track != 0) { |
| track->dump(buffer, SIZE); |
| result.append(buffer); |
| } |
| } |
| |
| snprintf(buffer, SIZE, "Output thread %p active tracks\n", this); |
| result.append(buffer); |
| result.append(" Name Clien Typ Fmt Chn mask Session Buf S M F SRate LeftV RighV Serv User Main buf Aux Buf\n"); |
| for (size_t i = 0; i < mActiveTracks.size(); ++i) { |
| wp<Track> wTrack = mActiveTracks[i]; |
| if (wTrack != 0) { |
| sp<Track> track = wTrack.promote(); |
| if (track != 0) { |
| track->dump(buffer, SIZE); |
| result.append(buffer); |
| } |
| } |
| } |
| write(fd, result.string(), result.size()); |
| return NO_ERROR; |
| } |
| |
| status_t AudioFlinger::PlaybackThread::dumpEffectChains(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| |
| snprintf(buffer, SIZE, "\n- %d Effect Chains:\n", mEffectChains.size()); |
| write(fd, buffer, strlen(buffer)); |
| |
| for (size_t i = 0; i < mEffectChains.size(); ++i) { |
| sp<EffectChain> chain = mEffectChains[i]; |
| if (chain != 0) { |
| chain->dump(fd, args); |
| } |
| } |
| return NO_ERROR; |
| } |
| |
| status_t AudioFlinger::PlaybackThread::dumpInternals(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| |
| snprintf(buffer, SIZE, "\nOutput thread %p internals\n", this); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "last write occurred (msecs): %llu\n", ns2ms(systemTime() - mLastWriteTime)); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "total writes: %d\n", mNumWrites); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "delayed writes: %d\n", mNumDelayedWrites); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "blocked in write: %d\n", mInWrite); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "suspend count: %d\n", mSuspended); |
| result.append(buffer); |
| snprintf(buffer, SIZE, "mix buffer : %p\n", mMixBuffer); |
| result.append(buffer); |
| write(fd, result.string(), result.size()); |
| |
| dumpBase(fd, args); |
| |
| return NO_ERROR; |
| } |
| |
| // Thread virtuals |
| status_t AudioFlinger::PlaybackThread::readyToRun() |
| { |
| if (mSampleRate == 0) { |
| LOGE("No working audio driver found."); |
| return NO_INIT; |
| } |
| LOGI("AudioFlinger's thread %p ready to run", this); |
| return NO_ERROR; |
| } |
| |
| void AudioFlinger::PlaybackThread::onFirstRef() |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| |
| snprintf(buffer, SIZE, "Playback Thread %p", this); |
| |
| run(buffer, ANDROID_PRIORITY_URGENT_AUDIO); |
| } |
| |
| // PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held |
| sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l( |
| const sp<AudioFlinger::Client>& client, |
| int streamType, |
| uint32_t sampleRate, |
| uint32_t format, |
| uint32_t channelMask, |
| int frameCount, |
| const sp<IMemory>& sharedBuffer, |
| int sessionId, |
| status_t *status) |
| { |
| sp<Track> track; |
| status_t lStatus; |
| |
| if (mType == DIRECT) { |
| if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) { |
| if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) { |
| LOGE("createTrack_l() Bad parameter: sampleRate %d format %d, channelMask 0x%08x \"" |
| "for output %p with format %d", |
| sampleRate, format, channelMask, mOutput, mFormat); |
| lStatus = BAD_VALUE; |
| goto Exit; |
| } |
| } |
| } else { |
| // Resampler implementation limits input sampling rate to 2 x output sampling rate. |
| if (sampleRate > mSampleRate*2) { |
| LOGE("Sample rate out of range: %d mSampleRate %d", sampleRate, mSampleRate); |
| lStatus = BAD_VALUE; |
| goto Exit; |
| } |
| } |
| |
| if (mOutput == 0) { |
| LOGE("Audio driver not initialized."); |
| lStatus = NO_INIT; |
| goto Exit; |
| } |
| |
| { // scope for mLock |
| Mutex::Autolock _l(mLock); |
| |
| // all tracks in same audio session must share the same routing strategy otherwise |
| // conflicts will happen when tracks are moved from one output to another by audio policy |
| // manager |
| uint32_t strategy = |
| AudioSystem::getStrategyForStream((audio_stream_type_t)streamType); |
| for (size_t i = 0; i < mTracks.size(); ++i) { |
| sp<Track> t = mTracks[i]; |
| if (t != 0) { |
| if (sessionId == t->sessionId() && |
| strategy != AudioSystem::getStrategyForStream((audio_stream_type_t)t->type())) { |
| lStatus = BAD_VALUE; |
| goto Exit; |
| } |
| } |
| } |
| |
| track = new Track(this, client, streamType, sampleRate, format, |
| channelMask, frameCount, sharedBuffer, sessionId); |
| if (track->getCblk() == NULL || track->name() < 0) { |
| lStatus = NO_MEMORY; |
| goto Exit; |
| } |
| mTracks.add(track); |
| |
| sp<EffectChain> chain = getEffectChain_l(sessionId); |
| if (chain != 0) { |
| LOGV("createTrack_l() setting main buffer %p", chain->inBuffer()); |
| track->setMainBuffer(chain->inBuffer()); |
| chain->setStrategy(AudioSystem::getStrategyForStream((audio_stream_type_t)track->type())); |
| chain->incTrackCnt(); |
| } |
| } |
| lStatus = NO_ERROR; |
| |
| Exit: |
| if(status) { |
| *status = lStatus; |
| } |
| return track; |
| } |
| |
| uint32_t AudioFlinger::PlaybackThread::latency() const |
| { |
| if (mOutput) { |
| return mOutput->stream->get_latency(mOutput->stream); |
| } |
| else { |
| return 0; |
| } |
| } |
| |
| status_t AudioFlinger::PlaybackThread::setMasterVolume(float value) |
| { |
| mMasterVolume = value; |
| return NO_ERROR; |
| } |
| |
| status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted) |
| { |
| mMasterMute = muted; |
| return NO_ERROR; |
| } |
| |
| float AudioFlinger::PlaybackThread::masterVolume() const |
| { |
| return mMasterVolume; |
| } |
| |
| bool AudioFlinger::PlaybackThread::masterMute() const |
| { |
| return mMasterMute; |
| } |
| |
| status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value) |
| { |
| mStreamTypes[stream].volume = value; |
| return NO_ERROR; |
| } |
| |
| status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted) |
| { |
| mStreamTypes[stream].mute = muted; |
| return NO_ERROR; |
| } |
| |
| float AudioFlinger::PlaybackThread::streamVolume(int stream) const |
| { |
| return mStreamTypes[stream].volume; |
| } |
| |
| bool AudioFlinger::PlaybackThread::streamMute(int stream) const |
| { |
| return mStreamTypes[stream].mute; |
| } |
| |
| // addTrack_l() must be called with ThreadBase::mLock held |
| status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track) |
| { |
| status_t status = ALREADY_EXISTS; |
| |
| // set retry count for buffer fill |
| track->mRetryCount = kMaxTrackStartupRetries; |
| if (mActiveTracks.indexOf(track) < 0) { |
| // the track is newly added, make sure it fills up all its |
| // buffers before playing. This is to ensure the client will |
| // effectively get the latency it requested. |
| track->mFillingUpStatus = Track::FS_FILLING; |
| track->mResetDone = false; |
| mActiveTracks.add(track); |
| if (track->mainBuffer() != mMixBuffer) { |
| sp<EffectChain> chain = getEffectChain_l(track->sessionId()); |
| if (chain != 0) { |
| LOGV("addTrack_l() starting track on chain %p for session %d", chain.get(), track->sessionId()); |
| chain->incActiveTrackCnt(); |
| } |
| } |
| |
| status = NO_ERROR; |
| } |
| |
| LOGV("mWaitWorkCV.broadcast"); |
| mWaitWorkCV.broadcast(); |
| |
| return status; |
| } |
| |
| // destroyTrack_l() must be called with ThreadBase::mLock held |
| void AudioFlinger::PlaybackThread::destroyTrack_l(const sp<Track>& track) |
| { |
| track->mState = TrackBase::TERMINATED; |
| if (mActiveTracks.indexOf(track) < 0) { |
| removeTrack_l(track); |
| } |
| } |
| |
| void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track) |
| { |
| mTracks.remove(track); |
| deleteTrackName_l(track->name()); |
| sp<EffectChain> chain = getEffectChain_l(track->sessionId()); |
| if (chain != 0) { |
| chain->decTrackCnt(); |
| } |
| } |
| |
| String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys) |
| { |
| String8 out_s8; |
| char *s; |
| |
| s = mOutput->stream->common.get_parameters(&mOutput->stream->common, keys.string()); |
| out_s8 = String8(s); |
| free(s); |
| return out_s8; |
| } |
| |
| // destroyTrack_l() must be called with AudioFlinger::mLock held |
| void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) { |
| AudioSystem::OutputDescriptor desc; |
| void *param2 = 0; |
| |
| LOGV("PlaybackThread::audioConfigChanged_l, thread %p, event %d, param %d", this, event, param); |
| |
| switch (event) { |
| case AudioSystem::OUTPUT_OPENED: |
| case AudioSystem::OUTPUT_CONFIG_CHANGED: |
| desc.channels = mChannelMask; |
| desc.samplingRate = mSampleRate; |
| desc.format = mFormat; |
| desc.frameCount = mFrameCount; |
| desc.latency = latency(); |
| param2 = &desc; |
| break; |
| |
| case AudioSystem::STREAM_CONFIG_CHANGED: |
| param2 = ¶m; |
| case AudioSystem::OUTPUT_CLOSED: |
| default: |
| break; |
| } |
| mAudioFlinger->audioConfigChanged_l(event, mId, param2); |
| } |
| |
| void AudioFlinger::PlaybackThread::readOutputParameters() |
| { |
| mSampleRate = mOutput->stream->common.get_sample_rate(&mOutput->stream->common); |
| mChannelMask = mOutput->stream->common.get_channels(&mOutput->stream->common); |
| mChannelCount = (uint16_t)popcount(mChannelMask); |
| mFormat = mOutput->stream->common.get_format(&mOutput->stream->common); |
| mFrameSize = (uint16_t)audio_stream_frame_size(&mOutput->stream->common); |
| mFrameCount = mOutput->stream->common.get_buffer_size(&mOutput->stream->common) / mFrameSize; |
| |
| // FIXME - Current mixer implementation only supports stereo output: Always |
| // Allocate a stereo buffer even if HW output is mono. |
| if (mMixBuffer != NULL) delete[] mMixBuffer; |
| mMixBuffer = new int16_t[mFrameCount * 2]; |
| memset(mMixBuffer, 0, mFrameCount * 2 * sizeof(int16_t)); |
| |
| // force reconfiguration of effect chains and engines to take new buffer size and audio |
| // parameters into account |
| // Note that mLock is not held when readOutputParameters() is called from the constructor |
| // but in this case nothing is done below as no audio sessions have effect yet so it doesn't |
| // matter. |
| // create a copy of mEffectChains as calling moveEffectChain_l() can reorder some effect chains |
| Vector< sp<EffectChain> > effectChains = mEffectChains; |
| for (size_t i = 0; i < effectChains.size(); i ++) { |
| mAudioFlinger->moveEffectChain_l(effectChains[i]->sessionId(), this, this, false); |
| } |
| } |
| |
| status_t AudioFlinger::PlaybackThread::getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames) |
| { |
| if (halFrames == 0 || dspFrames == 0) { |
| return BAD_VALUE; |
| } |
| if (mOutput == 0) { |
| return INVALID_OPERATION; |
| } |
| *halFrames = mBytesWritten / audio_stream_frame_size(&mOutput->stream->common); |
| |
| return mOutput->stream->get_render_position(mOutput->stream, dspFrames); |
| } |
| |
| uint32_t AudioFlinger::PlaybackThread::hasAudioSession(int sessionId) |
| { |
| Mutex::Autolock _l(mLock); |
| uint32_t result = 0; |
| if (getEffectChain_l(sessionId) != 0) { |
| result = EFFECT_SESSION; |
| } |
| |
| for (size_t i = 0; i < mTracks.size(); ++i) { |
| sp<Track> track = mTracks[i]; |
| if (sessionId == track->sessionId() && |
| !(track->mCblk->flags & CBLK_INVALID_MSK)) { |
| result |= TRACK_SESSION; |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| uint32_t AudioFlinger::PlaybackThread::getStrategyForSession_l(int sessionId) |
| { |
| // session AUDIO_SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that |
| // it is moved to correct output by audio policy manager when A2DP is connected or disconnected |
| if (sessionId == AUDIO_SESSION_OUTPUT_MIX) { |
| return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC); |
| } |
| for (size_t i = 0; i < mTracks.size(); i++) { |
| sp<Track> track = mTracks[i]; |
| if (sessionId == track->sessionId() && |
| !(track->mCblk->flags & CBLK_INVALID_MSK)) { |
| return AudioSystem::getStrategyForStream((audio_stream_type_t) track->type()); |
| } |
| } |
| return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC); |
| } |
| |
| sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain(int sessionId) |
| { |
| Mutex::Autolock _l(mLock); |
| return getEffectChain_l(sessionId); |
| } |
| |
| sp<AudioFlinger::EffectChain> AudioFlinger::PlaybackThread::getEffectChain_l(int sessionId) |
| { |
| sp<EffectChain> chain; |
| |
| size_t size = mEffectChains.size(); |
| for (size_t i = 0; i < size; i++) { |
| if (mEffectChains[i]->sessionId() == sessionId) { |
| chain = mEffectChains[i]; |
| break; |
| } |
| } |
| return chain; |
| } |
| |
| void AudioFlinger::PlaybackThread::setMode(uint32_t mode) |
| { |
| Mutex::Autolock _l(mLock); |
| size_t size = mEffectChains.size(); |
| for (size_t i = 0; i < size; i++) { |
| mEffectChains[i]->setMode_l(mode); |
| } |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| AudioFlinger::MixerThread::MixerThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device) |
| : PlaybackThread(audioFlinger, output, id, device), |
| mAudioMixer(0) |
| { |
| mType = PlaybackThread::MIXER; |
| mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); |
| |
| // FIXME - Current mixer implementation only supports stereo output |
| if (mChannelCount == 1) { |
| LOGE("Invalid audio hardware channel count"); |
| } |
| } |
| |
| AudioFlinger::MixerThread::~MixerThread() |
| { |
| delete mAudioMixer; |
| } |
| |
| bool AudioFlinger::MixerThread::threadLoop() |
| { |
| Vector< sp<Track> > tracksToRemove; |
| uint32_t mixerStatus = MIXER_IDLE; |
| nsecs_t standbyTime = systemTime(); |
| size_t mixBufferSize = mFrameCount * mFrameSize; |
| // FIXME: Relaxed timing because of a certain device that can't meet latency |
| // Should be reduced to 2x after the vendor fixes the driver issue |
| nsecs_t maxPeriod = seconds(mFrameCount) / mSampleRate * 3; |
| nsecs_t lastWarning = 0; |
| bool longStandbyExit = false; |
| uint32_t activeSleepTime = activeSleepTimeUs(); |
| uint32_t idleSleepTime = idleSleepTimeUs(); |
| uint32_t sleepTime = idleSleepTime; |
| Vector< sp<EffectChain> > effectChains; |
| |
| while (!exitPending()) |
| { |
| processConfigEvents(); |
| |
| mixerStatus = MIXER_IDLE; |
| { // scope for mLock |
| |
| Mutex::Autolock _l(mLock); |
| |
| if (checkForNewParameters_l()) { |
| mixBufferSize = mFrameCount * mFrameSize; |
| // FIXME: Relaxed timing because of a certain device that can't meet latency |
| // Should be reduced to 2x after the vendor fixes the driver issue |
| maxPeriod = seconds(mFrameCount) / mSampleRate * 3; |
| activeSleepTime = activeSleepTimeUs(); |
| idleSleepTime = idleSleepTimeUs(); |
| } |
| |
| const SortedVector< wp<Track> >& activeTracks = mActiveTracks; |
| |
| // put audio hardware into standby after short delay |
| if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) || |
| mSuspended) { |
| if (!mStandby) { |
| LOGV("Audio hardware entering standby, mixer %p, mSuspended %d\n", this, mSuspended); |
| mOutput->stream->common.standby(&mOutput->stream->common); |
| mStandby = true; |
| mBytesWritten = 0; |
| } |
| |
| if (!activeTracks.size() && mConfigEvents.isEmpty()) { |
| // we're about to wait, flush the binder command buffer |
| IPCThreadState::self()->flushCommands(); |
| |
| if (exitPending()) break; |
| |
| // wait until we have something to do... |
| LOGV("MixerThread %p TID %d going to sleep\n", this, gettid()); |
| mWaitWorkCV.wait(mLock); |
| LOGV("MixerThread %p TID %d waking up\n", this, gettid()); |
| |
| if (mMasterMute == false) { |
| char value[PROPERTY_VALUE_MAX]; |
| property_get("ro.audio.silent", value, "0"); |
| if (atoi(value)) { |
| LOGD("Silence is golden"); |
| setMasterMute(true); |
| } |
| } |
| |
| standbyTime = systemTime() + kStandbyTimeInNsecs; |
| sleepTime = idleSleepTime; |
| continue; |
| } |
| } |
| |
| mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove); |
| |
| // prevent any changes in effect chain list and in each effect chain |
| // during mixing and effect process as the audio buffers could be deleted |
| // or modified if an effect is created or deleted |
| lockEffectChains_l(effectChains); |
| } |
| |
| if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { |
| // mix buffers... |
| mAudioMixer->process(); |
| sleepTime = 0; |
| standbyTime = systemTime() + kStandbyTimeInNsecs; |
| //TODO: delay standby when effects have a tail |
| } else { |
| // If no tracks are ready, sleep once for the duration of an output |
| // buffer size, then write 0s to the output |
| if (sleepTime == 0) { |
| if (mixerStatus == MIXER_TRACKS_ENABLED) { |
| sleepTime = activeSleepTime; |
| } else { |
| sleepTime = idleSleepTime; |
| } |
| } else if (mBytesWritten != 0 || |
| (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)) { |
| memset (mMixBuffer, 0, mixBufferSize); |
| sleepTime = 0; |
| LOGV_IF((mBytesWritten == 0 && (mixerStatus == MIXER_TRACKS_ENABLED && longStandbyExit)), "anticipated start"); |
| } |
| // TODO add standby time extension fct of effect tail |
| } |
| |
| if (mSuspended) { |
| sleepTime = suspendSleepTimeUs(); |
| } |
| // sleepTime == 0 means we must write to audio hardware |
| if (sleepTime == 0) { |
| for (size_t i = 0; i < effectChains.size(); i ++) { |
| effectChains[i]->process_l(); |
| } |
| // enable changes in effect chain |
| unlockEffectChains(effectChains); |
| mLastWriteTime = systemTime(); |
| mInWrite = true; |
| mBytesWritten += mixBufferSize; |
| |
| int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize); |
| if (bytesWritten < 0) mBytesWritten -= mixBufferSize; |
| mNumWrites++; |
| mInWrite = false; |
| nsecs_t now = systemTime(); |
| nsecs_t delta = now - mLastWriteTime; |
| if (delta > maxPeriod) { |
| mNumDelayedWrites++; |
| if ((now - lastWarning) > kWarningThrottle) { |
| LOGW("write blocked for %llu msecs, %d delayed writes, thread %p", |
| ns2ms(delta), mNumDelayedWrites, this); |
| lastWarning = now; |
| } |
| if (mStandby) { |
| longStandbyExit = true; |
| } |
| } |
| mStandby = false; |
| } else { |
| // enable changes in effect chain |
| unlockEffectChains(effectChains); |
| usleep(sleepTime); |
| } |
| |
| // finally let go of all our tracks, without the lock held |
| // since we can't guarantee the destructors won't acquire that |
| // same lock. |
| tracksToRemove.clear(); |
| |
| // Effect chains will be actually deleted here if they were removed from |
| // mEffectChains list during mixing or effects processing |
| effectChains.clear(); |
| } |
| |
| if (!mStandby) { |
| mOutput->stream->common.standby(&mOutput->stream->common); |
| } |
| |
| LOGV("MixerThread %p exiting", this); |
| return false; |
| } |
| |
| // prepareTracks_l() must be called with ThreadBase::mLock held |
| uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track> >& activeTracks, Vector< sp<Track> > *tracksToRemove) |
| { |
| |
| uint32_t mixerStatus = MIXER_IDLE; |
| // find out which tracks need to be processed |
| size_t count = activeTracks.size(); |
| size_t mixedTracks = 0; |
| size_t tracksWithEffect = 0; |
| |
| float masterVolume = mMasterVolume; |
| bool masterMute = mMasterMute; |
| |
| if (masterMute) { |
| masterVolume = 0; |
| } |
| // Delegate master volume control to effect in output mix effect chain if needed |
| sp<EffectChain> chain = getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX); |
| if (chain != 0) { |
| uint32_t v = (uint32_t)(masterVolume * (1 << 24)); |
| chain->setVolume_l(&v, &v); |
| masterVolume = (float)((v + (1 << 23)) >> 24); |
| chain.clear(); |
| } |
| |
| for (size_t i=0 ; i<count ; i++) { |
| sp<Track> t = activeTracks[i].promote(); |
| if (t == 0) continue; |
| |
| Track* const track = t.get(); |
| audio_track_cblk_t* cblk = track->cblk(); |
| |
| // The first time a track is added we wait |
| // for all its buffers to be filled before processing it |
| mAudioMixer->setActiveTrack(track->name()); |
| if (cblk->framesReady() && track->isReady() && |
| !track->isPaused() && !track->isTerminated()) |
| { |
| //LOGV("track %d u=%08x, s=%08x [OK] on thread %p", track->name(), cblk->user, cblk->server, this); |
| |
| mixedTracks++; |
| |
| // track->mainBuffer() != mMixBuffer means there is an effect chain |
| // connected to the track |
| chain.clear(); |
| if (track->mainBuffer() != mMixBuffer) { |
| chain = getEffectChain_l(track->sessionId()); |
| // Delegate volume control to effect in track effect chain if needed |
| if (chain != 0) { |
| tracksWithEffect++; |
| } else { |
| LOGW("prepareTracks_l(): track %08x attached to effect but no chain found on session %d", |
| track->name(), track->sessionId()); |
| } |
| } |
| |
| |
| int param = AudioMixer::VOLUME; |
| if (track->mFillingUpStatus == Track::FS_FILLED) { |
| // no ramp for the first volume setting |
| track->mFillingUpStatus = Track::FS_ACTIVE; |
| if (track->mState == TrackBase::RESUMING) { |
| track->mState = TrackBase::ACTIVE; |
| param = AudioMixer::RAMP_VOLUME; |
| } |
| mAudioMixer->setParameter(AudioMixer::RESAMPLE, AudioMixer::RESET, NULL); |
| } else if (cblk->server != 0) { |
| // If the track is stopped before the first frame was mixed, |
| // do not apply ramp |
| param = AudioMixer::RAMP_VOLUME; |
| } |
| |
| // compute volume for this track |
| uint32_t vl, vr, va; |
| if (track->isMuted() || track->isPausing() || |
| mStreamTypes[track->type()].mute) { |
| vl = vr = va = 0; |
| if (track->isPausing()) { |
| track->setPaused(); |
| } |
| } else { |
| |
| // read original volumes with volume control |
| float typeVolume = mStreamTypes[track->type()].volume; |
| float v = masterVolume * typeVolume; |
| vl = (uint32_t)(v * cblk->volume[0]) << 12; |
| vr = (uint32_t)(v * cblk->volume[1]) << 12; |
| |
| va = (uint32_t)(v * cblk->sendLevel); |
| } |
| // Delegate volume control to effect in track effect chain if needed |
| if (chain != 0 && chain->setVolume_l(&vl, &vr)) { |
| // Do not ramp volume if volume is controlled by effect |
| param = AudioMixer::VOLUME; |
| track->mHasVolumeController = true; |
| } else { |
| // force no volume ramp when volume controller was just disabled or removed |
| // from effect chain to avoid volume spike |
| if (track->mHasVolumeController) { |
| param = AudioMixer::VOLUME; |
| } |
| track->mHasVolumeController = false; |
| } |
| |
| // Convert volumes from 8.24 to 4.12 format |
| int16_t left, right, aux; |
| uint32_t v_clamped = (vl + (1 << 11)) >> 12; |
| if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; |
| left = int16_t(v_clamped); |
| v_clamped = (vr + (1 << 11)) >> 12; |
| if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; |
| right = int16_t(v_clamped); |
| |
| if (va > MAX_GAIN_INT) va = MAX_GAIN_INT; |
| aux = int16_t(va); |
| |
| // XXX: these things DON'T need to be done each time |
| mAudioMixer->setBufferProvider(track); |
| mAudioMixer->enable(AudioMixer::MIXING); |
| |
| mAudioMixer->setParameter(param, AudioMixer::VOLUME0, (void *)left); |
| mAudioMixer->setParameter(param, AudioMixer::VOLUME1, (void *)right); |
| mAudioMixer->setParameter(param, AudioMixer::AUXLEVEL, (void *)aux); |
| mAudioMixer->setParameter( |
| AudioMixer::TRACK, |
| AudioMixer::FORMAT, (void *)track->format()); |
| mAudioMixer->setParameter( |
| AudioMixer::TRACK, |
| AudioMixer::CHANNEL_MASK, (void *)track->channelMask()); |
| mAudioMixer->setParameter( |
| AudioMixer::RESAMPLE, |
| AudioMixer::SAMPLE_RATE, |
| (void *)(cblk->sampleRate)); |
| mAudioMixer->setParameter( |
| AudioMixer::TRACK, |
| AudioMixer::MAIN_BUFFER, (void *)track->mainBuffer()); |
| mAudioMixer->setParameter( |
| AudioMixer::TRACK, |
| AudioMixer::AUX_BUFFER, (void *)track->auxBuffer()); |
| |
| // reset retry count |
| track->mRetryCount = kMaxTrackRetries; |
| mixerStatus = MIXER_TRACKS_READY; |
| } else { |
| //LOGV("track %d u=%08x, s=%08x [NOT READY] on thread %p", track->name(), cblk->user, cblk->server, this); |
| if (track->isStopped()) { |
| track->reset(); |
| } |
| if (track->isTerminated() || track->isStopped() || track->isPaused()) { |
| // We have consumed all the buffers of this track. |
| // Remove it from the list of active tracks. |
| tracksToRemove->add(track); |
| } else { |
| // No buffers for this track. Give it a few chances to |
| // fill a buffer, then remove it from active list. |
| if (--(track->mRetryCount) <= 0) { |
| LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this); |
| tracksToRemove->add(track); |
| // indicate to client process that the track was disabled because of underrun |
| android_atomic_or(CBLK_DISABLED_ON, &cblk->flags); |
| } else if (mixerStatus != MIXER_TRACKS_READY) { |
| mixerStatus = MIXER_TRACKS_ENABLED; |
| } |
| } |
| mAudioMixer->disable(AudioMixer::MIXING); |
| } |
| } |
| |
| // remove all the tracks that need to be... |
| count = tracksToRemove->size(); |
| if (UNLIKELY(count)) { |
| for (size_t i=0 ; i<count ; i++) { |
| const sp<Track>& track = tracksToRemove->itemAt(i); |
| mActiveTracks.remove(track); |
| if (track->mainBuffer() != mMixBuffer) { |
| chain = getEffectChain_l(track->sessionId()); |
| if (chain != 0) { |
| LOGV("stopping track on chain %p for session Id: %d", chain.get(), track->sessionId()); |
| chain->decActiveTrackCnt(); |
| } |
| } |
| if (track->isTerminated()) { |
| removeTrack_l(track); |
| } |
| } |
| } |
| |
| // mix buffer must be cleared if all tracks are connected to an |
| // effect chain as in this case the mixer will not write to |
| // mix buffer and track effects will accumulate into it |
| if (mixedTracks != 0 && mixedTracks == tracksWithEffect) { |
| memset(mMixBuffer, 0, mFrameCount * mChannelCount * sizeof(int16_t)); |
| } |
| |
| return mixerStatus; |
| } |
| |
| void AudioFlinger::MixerThread::invalidateTracks(int streamType) |
| { |
| LOGV ("MixerThread::invalidateTracks() mixer %p, streamType %d, mTracks.size %d", |
| this, streamType, mTracks.size()); |
| Mutex::Autolock _l(mLock); |
| |
| size_t size = mTracks.size(); |
| for (size_t i = 0; i < size; i++) { |
| sp<Track> t = mTracks[i]; |
| if (t->type() == streamType) { |
| android_atomic_or(CBLK_INVALID_ON, &t->mCblk->flags); |
| t->mCblk->cv.signal(); |
| } |
| } |
| } |
| |
| |
| // getTrackName_l() must be called with ThreadBase::mLock held |
| int AudioFlinger::MixerThread::getTrackName_l() |
| { |
| return mAudioMixer->getTrackName(); |
| } |
| |
| // deleteTrackName_l() must be called with ThreadBase::mLock held |
| void AudioFlinger::MixerThread::deleteTrackName_l(int name) |
| { |
| LOGV("remove track (%d) and delete from mixer", name); |
| mAudioMixer->deleteTrackName(name); |
| } |
| |
| // checkForNewParameters_l() must be called with ThreadBase::mLock held |
| bool AudioFlinger::MixerThread::checkForNewParameters_l() |
| { |
| bool reconfig = false; |
| |
| while (!mNewParameters.isEmpty()) { |
| status_t status = NO_ERROR; |
| String8 keyValuePair = mNewParameters[0]; |
| AudioParameter param = AudioParameter(keyValuePair); |
| int value; |
| |
| if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { |
| reconfig = true; |
| } |
| if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { |
| if (value != AUDIO_FORMAT_PCM_16_BIT) { |
| status = BAD_VALUE; |
| } else { |
| reconfig = true; |
| } |
| } |
| if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { |
| if (value != AUDIO_CHANNEL_OUT_STEREO) { |
| status = BAD_VALUE; |
| } else { |
| reconfig = true; |
| } |
| } |
| if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { |
| // do not accept frame count changes if tracks are open as the track buffer |
| // size depends on frame count and correct behavior would not be garantied |
| // if frame count is changed after track creation |
| if (!mTracks.isEmpty()) { |
| status = INVALID_OPERATION; |
| } else { |
| reconfig = true; |
| } |
| } |
| if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { |
| // when changing the audio output device, call addBatteryData to notify |
| // the change |
| if ((int)mDevice != value) { |
| uint32_t params = 0; |
| // check whether speaker is on |
| if (value & AUDIO_DEVICE_OUT_SPEAKER) { |
| params |= IMediaPlayerService::kBatteryDataSpeakerOn; |
| } |
| |
| int deviceWithoutSpeaker |
| = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER; |
| // check if any other device (except speaker) is on |
| if (value & deviceWithoutSpeaker ) { |
| params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn; |
| } |
| |
| if (params != 0) { |
| addBatteryData(params); |
| } |
| } |
| |
| // forward device change to effects that have requested to be |
| // aware of attached audio device. |
| mDevice = (uint32_t)value; |
| for (size_t i = 0; i < mEffectChains.size(); i++) { |
| mEffectChains[i]->setDevice_l(mDevice); |
| } |
| } |
| |
| if (status == NO_ERROR) { |
| status = mOutput->stream->common.set_parameters(&mOutput->stream->common, |
| keyValuePair.string()); |
| if (!mStandby && status == INVALID_OPERATION) { |
| mOutput->stream->common.standby(&mOutput->stream->common); |
| mStandby = true; |
| mBytesWritten = 0; |
| status = mOutput->stream->common.set_parameters(&mOutput->stream->common, |
| keyValuePair.string()); |
| } |
| if (status == NO_ERROR && reconfig) { |
| delete mAudioMixer; |
| readOutputParameters(); |
| mAudioMixer = new AudioMixer(mFrameCount, mSampleRate); |
| for (size_t i = 0; i < mTracks.size() ; i++) { |
| int name = getTrackName_l(); |
| if (name < 0) break; |
| mTracks[i]->mName = name; |
| // limit track sample rate to 2 x new output sample rate |
| if (mTracks[i]->mCblk->sampleRate > 2 * sampleRate()) { |
| mTracks[i]->mCblk->sampleRate = 2 * sampleRate(); |
| } |
| } |
| sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); |
| } |
| } |
| |
| mNewParameters.removeAt(0); |
| |
| mParamStatus = status; |
| mParamCond.signal(); |
| mWaitWorkCV.wait(mLock); |
| } |
| return reconfig; |
| } |
| |
| status_t AudioFlinger::MixerThread::dumpInternals(int fd, const Vector<String16>& args) |
| { |
| const size_t SIZE = 256; |
| char buffer[SIZE]; |
| String8 result; |
| |
| PlaybackThread::dumpInternals(fd, args); |
| |
| snprintf(buffer, SIZE, "AudioMixer tracks: %08x\n", mAudioMixer->trackNames()); |
| result.append(buffer); |
| write(fd, result.string(), result.size()); |
| return NO_ERROR; |
| } |
| |
| uint32_t AudioFlinger::MixerThread::activeSleepTimeUs() |
| { |
| return (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2; |
| } |
| |
| uint32_t AudioFlinger::MixerThread::idleSleepTimeUs() |
| { |
| return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2; |
| } |
| |
| uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs() |
| { |
| return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000); |
| } |
| |
| // ---------------------------------------------------------------------------- |
| AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device) |
| : PlaybackThread(audioFlinger, output, id, device) |
| { |
| mType = PlaybackThread::DIRECT; |
| } |
| |
| AudioFlinger::DirectOutputThread::~DirectOutputThread() |
| { |
| } |
| |
| |
| static inline int16_t clamp16(int32_t sample) |
| { |
| if ((sample>>15) ^ (sample>>31)) |
| sample = 0x7FFF ^ (sample>>31); |
| return sample; |
| } |
| |
| static inline |
| int32_t mul(int16_t in, int16_t v) |
| { |
| #if defined(__arm__) && !defined(__thumb__) |
| int32_t out; |
| asm( "smulbb %[out], %[in], %[v] \n" |
| : [out]"=r"(out) |
| : [in]"%r"(in), [v]"r"(v) |
| : ); |
| return out; |
| #else |
| return in * int32_t(v); |
| #endif |
| } |
| |
| void AudioFlinger::DirectOutputThread::applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp) |
| { |
| // Do not apply volume on compressed audio |
| if (!audio_is_linear_pcm(mFormat)) { |
| return; |
| } |
| |
| // convert to signed 16 bit before volume calculation |
| if (mFormat == AUDIO_FORMAT_PCM_8_BIT) { |
| size_t count = mFrameCount * mChannelCount; |
| uint8_t *src = (uint8_t *)mMixBuffer + count-1; |
| int16_t *dst = mMixBuffer + count-1; |
| while(count--) { |
| *dst-- = (int16_t)(*src--^0x80) << 8; |
| } |
| } |
| |
| size_t frameCount = mFrameCount; |
| int16_t *out = mMixBuffer; |
| if (ramp) { |
| if (mChannelCount == 1) { |
| int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16; |
| int32_t vlInc = d / (int32_t)frameCount; |
| int32_t vl = ((int32_t)mLeftVolShort << 16); |
| do { |
| out[0] = clamp16(mul(out[0], vl >> 16) >> 12); |
| out++; |
| vl += vlInc; |
| } while (--frameCount); |
| |
| } else { |
| int32_t d = ((int32_t)leftVol - (int32_t)mLeftVolShort) << 16; |
| int32_t vlInc = d / (int32_t)frameCount; |
| d = ((int32_t)rightVol - (int32_t)mRightVolShort) << 16; |
| int32_t vrInc = d / (int32_t)frameCount; |
| int32_t vl = ((int32_t)mLeftVolShort << 16); |
| int32_t vr = ((int32_t)mRightVolShort << 16); |
| do { |
| out[0] = clamp16(mul(out[0], vl >> 16) >> 12); |
| out[1] = clamp16(mul(out[1], vr >> 16) >> 12); |
| out += 2; |
| vl += vlInc; |
| vr += vrInc; |
| } while (--frameCount); |
| } |
| } else { |
| if (mChannelCount == 1) { |
| do { |
| out[0] = clamp16(mul(out[0], leftVol) >> 12); |
| out++; |
| } while (--frameCount); |
| } else { |
| do { |
| out[0] = clamp16(mul(out[0], leftVol) >> 12); |
| out[1] = clamp16(mul(out[1], rightVol) >> 12); |
| out += 2; |
| } while (--frameCount); |
| } |
| } |
| |
| // convert back to unsigned 8 bit after volume calculation |
| if (mFormat == AUDIO_FORMAT_PCM_8_BIT) { |
| size_t count = mFrameCount * mChannelCount; |
| int16_t *src = mMixBuffer; |
| uint8_t *dst = (uint8_t *)mMixBuffer; |
| while(count--) { |
| *dst++ = (uint8_t)(((int32_t)*src++ + (1<<7)) >> 8)^0x80; |
| } |
| } |
| |
| mLeftVolShort = leftVol; |
| mRightVolShort = rightVol; |
| } |
| |
| bool AudioFlinger::DirectOutputThread::threadLoop() |
| { |
| uint32_t mixerStatus = MIXER_IDLE; |
| sp<Track> trackToRemove; |
| sp<Track> activeTrack; |
| nsecs_t standbyTime = systemTime(); |
| int8_t *curBuf; |
| size_t mixBufferSize = mFrameCount*mFrameSize; |
| uint32_t activeSleepTime = activeSleepTimeUs(); |
| uint32_t idleSleepTime = idleSleepTimeUs(); |
| uint32_t sleepTime = idleSleepTime; |
| // use shorter standby delay as on normal output to release |
| // hardware resources as soon as possible |
| nsecs_t standbyDelay = microseconds(activeSleepTime*2); |
| |
| while (!exitPending()) |
| { |
| bool rampVolume; |
| uint16_t leftVol; |
| uint16_t rightVol; |
| Vector< sp<EffectChain> > effectChains; |
| |
| processConfigEvents(); |
| |
| mixerStatus = MIXER_IDLE; |
| |
| { // scope for the mLock |
| |
| Mutex::Autolock _l(mLock); |
| |
| if (checkForNewParameters_l()) { |
| mixBufferSize = mFrameCount*mFrameSize; |
| activeSleepTime = activeSleepTimeUs(); |
| idleSleepTime = idleSleepTimeUs(); |
| standbyDelay = microseconds(activeSleepTime*2); |
| } |
| |
| // put audio hardware into standby after short delay |
| if UNLIKELY((!mActiveTracks.size() && systemTime() > standbyTime) || |
| mSuspended) { |
| // wait until we have something to do... |
| if (!mStandby) { |
| LOGV("Audio hardware entering standby, mixer %p\n", this); |
| mOutput->stream->common.standby(&mOutput->stream->common); |
| mStandby = true; |
| mBytesWritten = 0; |
| } |
| |
| if (!mActiveTracks.size() && mConfigEvents.isEmpty()) { |
| // we're about to wait, flush the binder command buffer |
| IPCThreadState::self()->flushCommands(); |
| |
| if (exitPending()) break; |
| |
| LOGV("DirectOutputThread %p TID %d going to sleep\n", this, gettid()); |
| mWaitWorkCV.wait(mLock); |
| LOGV("DirectOutputThread %p TID %d waking up in active mode\n", this, gettid()); |
| |
| if (mMasterMute == false) { |
| char value[PROPERTY_VALUE_MAX]; |
| property_get("ro.audio.silent", value, "0"); |
| if (atoi(value)) { |
| LOGD("Silence is golden"); |
| setMasterMute(true); |
| } |
| } |
| |
| standbyTime = systemTime() + standbyDelay; |
| sleepTime = idleSleepTime; |
| continue; |
| } |
| } |
| |
| effectChains = mEffectChains; |
| |
| // find out which tracks need to be processed |
| if (mActiveTracks.size() != 0) { |
| sp<Track> t = mActiveTracks[0].promote(); |
| if (t == 0) continue; |
| |
| Track* const track = t.get(); |
| audio_track_cblk_t* cblk = track->cblk(); |
| |
| // The first time a track is added we wait |
| // for all its buffers to be filled before processing it |
| if (cblk->framesReady() && track->isReady() && |
| !track->isPaused() && !track->isTerminated()) |
| { |
| //LOGV("track %d u=%08x, s=%08x [OK]", track->name(), cblk->user, cblk->server); |
| |
| if (track->mFillingUpStatus == Track::FS_FILLED) { |
| track->mFillingUpStatus = Track::FS_ACTIVE; |
| mLeftVolFloat = mRightVolFloat = 0; |
| mLeftVolShort = mRightVolShort = 0; |
| if (track->mState == TrackBase::RESUMING) { |
| track->mState = TrackBase::ACTIVE; |
| rampVolume = true; |
| } |
| } else if (cblk->server != 0) { |
| // If the track is stopped before the first frame was mixed, |
| // do not apply ramp |
| rampVolume = true; |
| } |
| // compute volume for this track |
| float left, right; |
| if (track->isMuted() || mMasterMute || track->isPausing() || |
| mStreamTypes[track->type()].mute) { |
| left = right = 0; |
| if (track->isPausing()) { |
| track->setPaused(); |
| } |
| } else { |
| float typeVolume = mStreamTypes[track->type()].volume; |
| float v = mMasterVolume * typeVolume; |
| float v_clamped = v * cblk->volume[0]; |
| if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; |
| left = v_clamped/MAX_GAIN; |
| v_clamped = v * cblk->volume[1]; |
| if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; |
| right = v_clamped/MAX_GAIN; |
| } |
| |
| if (left != mLeftVolFloat || right != mRightVolFloat) { |
| mLeftVolFloat = left; |
| mRightVolFloat = right; |
| |
| // If audio HAL implements volume control, |
| // force software volume to nominal value |
| if (mOutput->stream->set_volume(mOutput->stream, left, right) == NO_ERROR) { |
| left = 1.0f; |
| right = 1.0f; |
| } |
| |
| // Convert volumes from float to 8.24 |
| uint32_t vl = (uint32_t)(left * (1 << 24)); |
| uint32_t vr = (uint32_t)(right * (1 << 24)); |
| |
| // Delegate volume control to effect in track effect chain if needed |
| // only one effect chain can be present on DirectOutputThread, so if |
| // there is one, the track is connected to it |
| if (!effectChains.isEmpty()) { |
| // Do not ramp volume if volume is controlled by effect |
| if(effectChains[0]->setVolume_l(&vl, &vr)) { |
| rampVolume = false; |
| } |
| } |
| |
| // Convert volumes from 8.24 to 4.12 format |
| uint32_t v_clamped = (vl + (1 << 11)) >> 12; |
| if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; |
| leftVol = (uint16_t)v_clamped; |
| v_clamped = (vr + (1 << 11)) >> 12; |
| if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; |
| rightVol = (uint16_t)v_clamped; |
| } else { |
| leftVol = mLeftVolShort; |
| rightVol = mRightVolShort; |
| rampVolume = false; |
| } |
| |
| // reset retry count |
| track->mRetryCount = kMaxTrackRetriesDirect; |
| activeTrack = t; |
| mixerStatus = MIXER_TRACKS_READY; |
| } else { |
| //LOGV("track %d u=%08x, s=%08x [NOT READY]", track->name(), cblk->user, cblk->server); |
| if (track->isStopped()) { |
| track->reset(); |
| } |
| if (track->isTerminated() || track->isStopped() || track->isPaused()) { |
| // We have consumed all the buffers of this track. |
| // Remove it from the list of active tracks. |
| trackToRemove = track; |
| } else { |
| // No buffers for this track. Give it a few chances to |
| // fill a buffer, then remove it from active list. |
| if (--(track->mRetryCount) <= 0) { |
| LOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name()); |
| trackToRemove = track; |
| } else { |
| mixerStatus = MIXER_TRACKS_ENABLED; |
| } |
| } |
| } |
| } |
| |
| // remove all the tracks that need to be... |
| if (UNLIKELY(trackToRemove != 0)) { |
| mActiveTracks.remove(trackToRemove); |
| if (!effectChains.isEmpty()) { |
| LOGV("stopping track on chain %p for session Id: %d", effectChains[0].get(), |
| trackToRemove->sessionId()); |
| effectChains[0]->decActiveTrackCnt(); |
| } |
| if (trackToRemove->isTerminated()) { |
| removeTrack_l(trackToRemove); |
| } |
| } |
| |
| lockEffectChains_l(effectChains); |
| } |
| |
| if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { |
| AudioBufferProvider::Buffer buffer; |
| size_t frameCount = mFrameCount; |
| curBuf = (int8_t *)mMixBuffer; |
| // output audio to hardware |
| while (frameCount) { |
| buffer.frameCount = frameCount; |
| activeTrack->getNextBuffer(&buffer); |
| if (UNLIKELY(buffer.raw == 0)) { |
| memset(curBuf, 0, frameCount * mFrameSize); |
| break; |
| } |
| memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize); |
| frameCount -= buffer.frameCount; |
| curBuf += buffer.frameCount * mFrameSize; |
| activeTrack->releaseBuffer(&buffer); |
| } |
| sleepTime = 0; |
| standbyTime = systemTime() + standbyDelay; |
| } else { |
| if (sleepTime == 0) { |
| if (mixerStatus == MIXER_TRACKS_ENABLED) { |
| sleepTime = activeSleepTime; |
| } else { |
| sleepTime = idleSleepTime; |
| } |
| } else if (mBytesWritten != 0 && audio_is_linear_pcm(mFormat)) { |
| memset (mMixBuffer, 0, mFrameCount * mFrameSize); |
| sleepTime = 0; |
| } |
| } |
| |
| if (mSuspended) { |
| sleepTime = suspendSleepTimeUs(); |
| } |
| // sleepTime == 0 means we must write to audio hardware |
| if (sleepTime == 0) { |
| if (mixerStatus == MIXER_TRACKS_READY) { |
| applyVolume(leftVol, rightVol, rampVolume); |
| } |
| for (size_t i = 0; i < effectChains.size(); i ++) { |
| effectChains[i]->process_l(); |
| } |
| unlockEffectChains(effectChains); |
| |
| mLastWriteTime = systemTime(); |
| mInWrite = true; |
| mBytesWritten += mixBufferSize; |
| int bytesWritten = (int)mOutput->stream->write(mOutput->stream, mMixBuffer, mixBufferSize); |
| if (bytesWritten < 0) mBytesWritten -= mixBufferSize; |
| mNumWrites++; |
| mInWrite = false; |
| mStandby = false; |
| } else { |
| unlockEffectChains(effectChains); |
| usleep(sleepTime); |
| } |
| |
| // finally let go of removed track, without the lock held |
| // since we can't guarantee the destructors won't acquire that |
| // same lock. |
| trackToRemove.clear(); |
| activeTrack.clear(); |
| |
| // Effect chains will be actually deleted here if they were removed from |
| // mEffectChains list during mixing or effects processing |
| effectChains.clear(); |
| } |
| |
| if (!mStandby) { |
| mOutput->stream->common.standby(&mOutput->stream->common); |
| } |
| |
| LOGV("DirectOutputThread %p exiting", this); |
| return false; |
| } |
| |
| // getTrackName_l() must be called with ThreadBase::mLock held |
| int AudioFlinger::DirectOutputThread::getTrackName_l() |
| { |
| return 0; |
| } |
| |
| // deleteTrackName_l() must be called with ThreadBase::mLock held |
| void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name) |
| { |
| } |
| |
| // checkForNewParameters_l() must be called with ThreadBase::mLock held |
| bool AudioFlinger::DirectOutputThread::checkForNewParameters_l() |
| { |
| bool reconfig = false; |
| |
| while (!mNewParameters.isEmpty()) { |
| status_t status = NO_ERROR; |
| String8 keyValuePair = mNewParameters[0]; |
| AudioParameter param = AudioParameter(keyValuePair); |
| int value; |
| |
| if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { |
| // do not accept frame count changes if tracks are open as the track buffer |
| // size depends on frame count and correct behavior would not be garantied |
| // if frame count is changed after track creation |
| if (!mTracks.isEmpty()) { |
| status = INVALID_OPERATION; |
| } else { |
| reconfig = true; |
| } |
| } |
| if (status == NO_ERROR) { |
| status = mOutput->stream->common.set_parameters(&mOutput->stream->common, |
| keyValuePair.string()); |
| if (!mStandby && status == INVALID_OPERATION) { |
| mOutput->stream->common.standby(&mOutput->stream->common); |
| mStandby = true; |
| mBytesWritten = 0; |
| status = mOutput->stream->common.set_parameters(&mOutput->stream->common, |
| keyValuePair.string()); |
| } |
| if (status == NO_ERROR && reconfig) { |
| readOutputParameters(); |
| sendConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); |
| } |
| } |
| |
| mNewParameters.removeAt(0); |
| |
| mParamStatus = status; |
| mParamCond.signal(); |
| mWaitWorkCV.wait(mLock); |
| } |
| return reconfig; |
| } |
| |
| uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs() |
| { |
| uint32_t time; |
| if (audio_is_linear_pcm(mFormat)) { |
| time = (uint32_t)(mOutput->stream->get_latency(mOutput->stream) * 1000) / 2; |
| } else { |
| time = 10000; |
| } |
| return time; |
| } |
| |
| uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs() |
| { |
| uint32_t time; |
| if (audio_is_linear_pcm(mFormat)) { |
| time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2; |
| } else { |
| time = 10000; |
| } |
| return time; |
| } |
| |
| uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs() |
| { |
| uint32_t time; |
| if (audio_is_linear_pcm(mFormat)) { |
| time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000); |
| } else { |
| time = 10000; |
| } |
| return time; |
| } |
| |
| |
| // ---------------------------------------------------------------------------- |
| |
| AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id) |
| : MixerThread(audioFlinger, mainThread->getOutput(), id, mainThread->device()), mWaitTimeMs(UINT_MAX) |
| { |
| mType = PlaybackThread::DUPLICATING; |
| addOutputTrack(mainThread); |
| } |
| |
| AudioFlinger::DuplicatingThread::~DuplicatingThread() |
| { |
| for (size_t i = 0; i < mOutputTracks.size(); i++) { |
| mOutputTracks[i]->destroy(); |
| } |
| mOutputTracks.clear(); |
| } |
| |
| bool AudioFlinger::DuplicatingThread::threadLoop() |
| { |
| Vector< sp<Track> > tracksToRemove; |
| uint32_t mixerStatus = MIXER_IDLE; |
| nsecs_t standbyTime = systemTime(); |
| size_t mixBufferSize = mFrameCount*mFrameSize; |
| SortedVector< sp<OutputTrack> > outputTracks; |
| uint32_t writeFrames = 0; |
| uint32_t activeSleepTime = activeSleepTimeUs(); |
| uint32_t idleSleepTime = idleSleepTimeUs(); |
| uint32_t sleepTime = idleSleepTime; |
| Vector< sp<EffectChain> > effectChains; |
| |
| while (!exitPending()) |
| { |
| processConfigEvents(); |
| |
| mixerStatus = MIXER_IDLE; |
| { // scope for the mLock |
| |
| Mutex::Autolock _l(mLock); |
| |
| if (checkForNewParameters_l()) { |
| mixBufferSize = mFrameCount*mFrameSize; |
| updateWaitTime(); |
| activeSleepTime = activeSleepTimeUs(); |
| idleSleepTime = idleSleepTimeUs(); |
| } |
| |
| const SortedVector< wp<Track> >& activeTracks = mActiveTracks; |
| |
| for (size_t i = 0; i < mOutputTracks.size(); i++) { |
| outputTracks.add(mOutputTracks[i]); |
| } |
| |
| // put audio hardware into standby after short delay |
| if UNLIKELY((!activeTracks.size() && systemTime() > standbyTime) || |
| mSuspended) { |
| if (!mStandby) { |
| for (size_t i = 0; i < outputTracks.size(); i++) { |
| outputTracks[i]->stop(); |
| } |
| mStandby = true; |
| mBytesWritten = 0; |
| } |
| |
| if (!activeTracks.size() && mConfigEvents.isEmpty()) { |
| // we're about to wait, flush the binder command buffer |
| IPCThreadState::self()->flushCommands(); |
| outputTracks.clear(); |
| |
| if (exitPending()) break; |
| |
| LOGV("DuplicatingThread %p TID %d going to sleep\n", this, gettid()); |
| mWaitWorkCV.wait(mLock); |
| LOGV("DuplicatingThread %p TID %d waking up\n", this, gettid()); |
| if (mMasterMute == false) { |
| char value[PROPERTY_VALUE_MAX]; |
| property_get("ro.audio.silent", value, "0"); |
| if (atoi(value)) { |
| LOGD("Silence is golden"); |
| setMasterMute(true); |
| } |
| } |
| |
| standbyTime = systemTime() + kStandbyTimeInNsecs; |
| sleepTime = idleSleepTime; |
| continue; |
| } |
| } |
| |
| mixerStatus = prepareTracks_l(activeTracks, &tracksToRemove); |
| |
| // prevent any changes in effect chain list and in each effect chain |
| // during mixing and effect process as the audio buffers could be deleted |
| // or modified if an effect is created or deleted |
| lockEffectChains_l(effectChains); |
| } |
| |
| if (LIKELY(mixerStatus == MIXER_TRACKS_READY)) { |
| // mix buffers... |
| if (outputsReady(outputTracks)) { |
| mAudioMixer->process(); |
| } else { |
| memset(mMixBuffer, 0, mixBufferSize); |
| } |
| sleepTime = 0; |
| writeFrames = mFrameCount; |
| } else { |
| if (sleepTime == 0) { |
| if (mixerStatus == MIXER_TRACKS_ENABLED) { |
| sleepTime = activeSleepTime; |
| } else { |
| sleepTime = idleSleepTime; |
| } |
| } else if (mBytesWritten != 0) { |
| // flush remaining overflow buffers in output tracks |
| for (size_t i = 0; i < outputTracks.size(); i++) { |
| if (outputTracks[i]->isActive()) { |
| sleepTime = 0; |
| writeFrames = 0; |
| memset(mMixBuffer, 0, mixBufferSize); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (mSuspended) { |
| sleepTime = suspendSleepTimeUs(); |
| } |
| // sleepTime == 0 means we must write to audio hardware |
| if (sleepTime == 0) { |
| for (size_t i = 0; i < effectChains.size(); i ++) { |
| effectChains[i]->process_l(); |
| } |
| // enable changes in effect chain |
| unlockEffectChains(effectChains); |
| |
| standbyTime = systemTime() + kStandbyTimeInNsecs; |
| for (size_t i = 0; i < outputTracks.size(); i++) { |
| outputTracks[i]->write(mMixBuffer, writeFrames); |
| } |
| mStandby = false; |
| mBytesWritten += mixBufferSize; |
| } else { |
| // enable changes in effect chain |
| unlockEffectChains(effectChains); |
| usleep(sleepTime); |
| } |
| |
| // finally let go of all our tracks, without the lock held |
| // since we can't guarantee the destructors won't acquire that |
| // same lock. |
| tracksToRemove.clear(); |
| outputTracks.clear(); |
| |
| // Effect chains will be actually deleted here if they were removed from |
| // mEffectChains list during mixing or effects processing |
| effectChains.clear(); |
| } |
| |
| return false; |
| } |
| |
| void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) |
| { |
| int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate(); |
| OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread, |
| this, |
| mSampleRate, |
| mFormat, |
| mChannelMask, |
| frameCount); |
| if (outputTrack->cblk() != NULL) { |
| thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f); |
| mOutputTracks.add(outputTrack); |
| LOGV("addOutputTrack() track %p, on thread %p", outputTrack, thread); |
| updateWaitTime(); |
| } |
| } |
| |
| void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread) |
| { |
| Mutex::Autolock _l(mLock); |
| for (size_t i = 0; i < mOutputTracks.size(); i++) { |
| if (mOutputTracks[i]->thread() == (ThreadBase *)thread) { |
| mOutputTracks[i]->destroy(); |
| mOutputTracks.removeAt(i); |
| updateWaitTime(); |
| return; |
| } |
| } |
| LOGV("removeOutputTrack(): unkonwn thread: %p", thread); |
| } |
| |
| void AudioFlinger::DuplicatingThread::updateWaitTime() |
| { |
| mWaitTimeMs = UINT_MAX; |
| for (size_t i = 0; i < mOutputTracks.size(); i++) { |
| sp<ThreadBase> strong = mOutputTracks[i]->thread().promote(); |
| if (strong != NULL) { |
| uint32_t waitTimeMs = (strong->frameCount() * 2 * 1000) / strong->sampleRate(); |
| if (waitTimeMs < mWaitTimeMs) { |
| mWaitTimeMs = waitTimeMs; |
| } |
| } |
| } |
| } |
| |
| |
| bool AudioFlinger::DuplicatingThread::outputsReady(SortedVector< sp<OutputTrack> > &outputTracks) |
| { |
| for (size_t i = 0; i < outputTracks.size(); i++) { |
| sp <ThreadBase> thread = outputTracks[i]->thread().promote(); |
| if (thread == 0) { |
| LOGW("DuplicatingThread::outputsReady() could not promote thread on output track %p", outputTracks[i].get()); |
| return false; |
| } |
| PlaybackThread *playbackThread = (PlaybackThread *)thread.get(); |
| if (playbackThread->standby() && !playbackThread->isSuspended()) { |
| LOGV("DuplicatingThread output track %p on thread %p Not Ready", outputTracks[i].get(), thread.get()); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs() |
| { |
| return (mWaitTimeMs * 1000) / 2; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| // TrackBase constructor must be called with AudioFlinger::mLock held |
| AudioFlinger::ThreadBase::TrackBase::TrackBase( |
| const wp<ThreadBase>& thread, |
| const sp<Client>& client, |
| uint32_t sampleRate, |
| uint32_t format, |
| uint32_t channelMask, |
| int frameCount, |
| uint32_t flags, |
| const sp<IMemory>& sharedBuffer, |
| int sessionId) |
| : RefBase(), |
| mThread(thread), |
| mClient(client), |
| mCblk(0), |
| mFrameCount(0), |
| mState(IDLE), |
| mClientTid(-1), |
| mFormat(format), |
| mFlags(flags & ~SYSTEM_FLAGS_MASK), |
| mSessionId(sessionId) |
| { |
| LOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %d", sharedBuffer->pointer(), sharedBuffer->size()); |
| |
| // LOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize); |
| size_t size = sizeof(audio_track_cblk_t); |
| uint8_t channelCount = popcount(channelMask); |
| size_t bufferSize = frameCount*channelCount*sizeof(int16_t); |
| if (sharedBuffer == 0) { |
| size += bufferSize; |
| } |
| |
| if (client != NULL) { |
| mCblkMemory = client->heap()->allocate(size); |
| if (mCblkMemory != 0) { |
| mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); |
| if (mCblk) { // construct the shared structure in-place. |
| new(mCblk) audio_track_cblk_t(); |
| // clear all buffers |
| mCblk->frameCount = frameCount; |
| mCblk->sampleRate = sampleRate; |
| mChannelCount = channelCount; |
| mChannelMask = channelMask; |
| if (sharedBuffer == 0) { |
| mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); |
| memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); |
| // Force underrun condition to avoid false underrun callback until first data is |
| // written to buffer (other flags are cleared) |
| mCblk->flags = CBLK_UNDERRUN_ON; |
| } else { |
| mBuffer = sharedBuffer->pointer(); |
| } |
| mBufferEnd = (uint8_t *)mBuffer + bufferSize; |
| } |
| } else { |
| LOGE("not enough memory for AudioTrack size=%u", size); |
| client->heap()->dump("AudioTrack"); |
| return; |
| } |
| } else { |
| mCblk = (audio_track_cblk_t *)(new uint8_t[size]); |
| if (mCblk) { // construct the shared structure in-place. |
| new(mCblk) audio_track_cblk_t(); |
| // clear all buffers |
| mCblk->frameCount = frameCount; |
| mCblk->sampleRate = sampleRate; |
| mChannelCount = channelCount; |
| mChannelMask = channelMask; |
| mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); |
| memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); |
| // Force underrun condition to avoid false underrun callback until first data is |
| // written to buffer (other flags are cleared) |
| mCblk->flags = CBLK_UNDERRUN_ON; |
| mBufferEnd = (uint8_t *)mBuffer + bufferSize; |
| } |
| } |
| } |
| |
| AudioFlinger::ThreadBase::TrackBase::~TrackBase() |
| { |
| if (mCblk) { |
| mCblk->~audio_track_cblk_t(); // destroy our shared-structure. |
| if (mClient == NULL) { |
| delete mCblk; |
| } |
| } |
| mCblkMemory.clear(); // and free the shared memory |
| if (mClient != NULL) { |
| Mutex::Autolock _l(mClient->audioFlinger()->mLock); |
| mClient.clear(); |
| } |
| } |
| |
| void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer) |
| { |
| buffer->raw = 0; |
| mFrameCount = buffer->frameCount; |
| step(); |
| buffer->frameCount = 0; |
| } |
| |
| bool AudioFlinger::ThreadBase::TrackBase::step() { |
| bool result; |
| audio_track_cblk_t* cblk = this->cblk(); |
| |
| result = cblk->stepServer(mFrameCount); |
| if (!result) { |
| LOGV("stepServer failed acquiring cblk mutex"); |
| mFlags |= STEPSERVER_FAILED; |
| } |
| return result; |
| } |
| |
| void AudioFlinger::ThreadBase::TrackBase::reset() { |
| audio_track_cblk_t* cblk = this->cblk(); |
| |
| cblk->user = 0; |
| cblk->server = 0; |
| cblk->userBase = 0; |
| cblk->serverBase = 0; |
| mFlags &= (uint32_t)(~SYSTEM_FLAGS_MASK); |
| LOGV("TrackBase::reset"); |
| } |
| |
| sp<IMemory> AudioFlinger::ThreadBase::TrackBase::getCblk() const |
| { |
| return mCblkMemory; |
| } |
| |
| int AudioFlinger::ThreadBase::TrackBase::sampleRate() const { |
| return (int)mCblk->sampleRate; |
| } |
| |
| int AudioFlinger::ThreadBase::TrackBase::channelCount() const { |
| return (const int)mChannelCount; |
| } |
| |
| uint32_t AudioFlinger::ThreadBase::TrackBase::channelMask() const { |
| return mChannelMask; |
| }<
|