SoundChannel::~SoundChannel() { LOGV("SoundChannel destructor"); if (mAudioTrack) { LOGV("stop track"); mAudioTrack->stop(); delete mAudioTrack; } clearNextEvent(); mSample.clear(); }
SoundChannel::~SoundChannel() { ALOGV("SoundChannel destructor %p", this); { Mutex::Autolock lock(&mLock); clearNextEvent(); doStop_l(); } // do not call AudioTrack destructor with mLock held as it will wait for the AudioTrack // callback thread to exit which may need to execute process() and acquire the mLock. mAudioTrack.clear(); }
// call with sound pool lock held void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume, float rightVolume, int priority, int loop, float rate) { sp<AudioTrack> oldTrack; sp<AudioTrack> newTrack; status_t status; { // scope for the lock Mutex::Autolock lock(&mLock); ALOGV("SoundChannel::play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f," " priority=%d, loop=%d, rate=%f", this, sample->sampleID(), nextChannelID, leftVolume, rightVolume, priority, loop, rate); // if not idle, this voice is being stolen if (mState != IDLE) { ALOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID); mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate); stop_l(); return; } // initialize track size_t afFrameCount; uint32_t afSampleRate; audio_stream_type_t streamType = mSoundPool->streamType(); if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { afFrameCount = kDefaultFrameCount; } if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { afSampleRate = kDefaultSampleRate; } int numChannels = sample->numChannels(); uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5); uint32_t totalFrames = (kDefaultBufferCount * afFrameCount * sampleRate) / afSampleRate; uint32_t bufferFrames = (totalFrames + (kDefaultBufferCount - 1)) / kDefaultBufferCount; uint32_t frameCount = 0; if (loop) { frameCount = sample->size()/numChannels/ ((sample->format() == AUDIO_FORMAT_PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); } #ifndef USE_SHARED_MEM_BUFFER // Ensure minimum audio buffer size in case of short looped sample if(frameCount < totalFrames) { frameCount = totalFrames; } #endif // mToggle toggles each time a track is started on a given channel. // The toggle is concatenated with the SoundChannel address and passed to AudioTrack // as callback user data. This enables the detection of callbacks received from the old // audio track while the new one is being started and avoids processing them with // wrong audio audio buffer size (mAudioBufferSize) unsigned long toggle = mToggle ^ 1; void *userData = (void *)((unsigned long)this | toggle); uint32_t channels = (numChannels == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO; // do not create a new audio track if current track is compatible with sample parameters #ifdef USE_SHARED_MEM_BUFFER newTrack = new AudioTrack(streamType, sampleRate, sample->format(), channels, sample->getIMemory(), AUDIO_OUTPUT_FLAG_FAST, callback, userData); #else newTrack = new AudioTrack(streamType, sampleRate, sample->format(), channels, frameCount, AUDIO_OUTPUT_FLAG_FAST, callback, userData, bufferFrames); #endif oldTrack = mAudioTrack; status = newTrack->initCheck(); if (status != NO_ERROR) { ALOGE("Error creating AudioTrack"); goto exit; } ALOGV("setVolume %p", newTrack.get()); newTrack->setVolume(leftVolume, rightVolume); newTrack->setLoop(0, frameCount, loop); // From now on, AudioTrack callbacks received with previous toggle value will be ignored. mToggle = toggle; mAudioTrack = newTrack; mPos = 0; mSample = sample; mChannelID = nextChannelID; mPriority = priority; mLoop = loop; mLeftVolume = leftVolume; mRightVolume = rightVolume; mNumChannels = numChannels; mRate = rate; clearNextEvent(); mState = PLAYING; mAudioTrack->start(); mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize(); } exit: ALOGV("delete oldTrack %p", oldTrack.get()); if (status != NO_ERROR) { mAudioTrack.clear(); } }
void SoundChannel::play(const sp<Sample>& sample, int nextChannelID, float leftVolume, float rightVolume, int priority, int loop, float rate) { AudioTrack* oldTrack; LOGV("play %p: sampleID=%d, channelID=%d, leftVolume=%f, rightVolume=%f, priority=%d, loop=%d, rate=%f", this, sample->sampleID(), nextChannelID, leftVolume, rightVolume, priority, loop, rate); // if not idle, this voice is being stolen if (mState != IDLE) { LOGV("channel %d stolen - event queued for channel %d", channelID(), nextChannelID); mNextEvent.set(sample, nextChannelID, leftVolume, rightVolume, priority, loop, rate); stop(); return; } // initialize track int afFrameCount; int afSampleRate; int streamType = mSoundPool->streamType(); if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) { afFrameCount = kDefaultFrameCount; } if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) { afSampleRate = kDefaultSampleRate; } int numChannels = sample->numChannels(); uint32_t sampleRate = uint32_t(float(sample->sampleRate()) * rate + 0.5); uint32_t bufferFrames = (afFrameCount * sampleRate) / afSampleRate; uint32_t frameCount = 0; if (loop) { frameCount = sample->size()/numChannels/((sample->format() == AudioSystem::PCM_16_BIT) ? sizeof(int16_t) : sizeof(uint8_t)); } #ifndef USE_SHARED_MEM_BUFFER // Ensure minimum audio buffer size in case of short looped sample if(frameCount < kDefaultBufferCount * bufferFrames) { frameCount = kDefaultBufferCount * bufferFrames; } #endif AudioTrack* newTrack; // mToggle toggles each time a track is started on a given channel. // The toggle is concatenated with the SoundChannel address and passed to AudioTrack // as callback user data. This enables the detection of callbacks received from the old // audio track while the new one is being started and avoids processing them with // wrong audio audio buffer size (mAudioBufferSize) unsigned long toggle = mToggle ^ 1; void *userData = (void *)((unsigned long)this | toggle); #ifdef USE_SHARED_MEM_BUFFER newTrack = new AudioTrack(streamType, sampleRate, sample->format(), numChannels, sample->getIMemory(), 0, callback, userData); #else newTrack = new AudioTrack(streamType, sampleRate, sample->format(), numChannels, frameCount, 0, callback, userData, bufferFrames); #endif if (newTrack->initCheck() != NO_ERROR) { LOGE("Error creating AudioTrack"); delete newTrack; return; } LOGV("setVolume %p", newTrack); newTrack->setVolume(leftVolume, rightVolume); newTrack->setLoop(0, frameCount, loop); { Mutex::Autolock lock(&mLock); // From now on, AudioTrack callbacks recevieved with previous toggle value will be ignored. mToggle = toggle; oldTrack = mAudioTrack; mAudioTrack = newTrack; mPos = 0; mSample = sample; mChannelID = nextChannelID; mPriority = priority; mLoop = loop; mLeftVolume = leftVolume; mRightVolume = rightVolume; mNumChannels = numChannels; mRate = rate; clearNextEvent(); mState = PLAYING; mAudioTrack->start(); mAudioBufferSize = newTrack->frameCount()*newTrack->frameSize(); } LOGV("delete oldTrack %p", oldTrack); delete oldTrack; }