1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "SoundPool::Stream"
19 #include <utils/Log.h>
20
21 #include "Stream.h"
22
23 #include "StreamManager.h"
24
25 namespace android::soundpool {
26
~Stream()27 Stream::~Stream()
28 {
29 ALOGV("%s(%p)", __func__, this);
30 }
31
autoPause()32 void Stream::autoPause()
33 {
34 std::lock_guard lock(mLock);
35 if (mState == PLAYING) {
36 ALOGV("%s: track streamID: %d", __func__, (int)mStreamID);
37 mState = PAUSED;
38 mAutoPaused = true;
39 if (mAudioTrack != nullptr) {
40 mAudioTrack->pause();
41 }
42 }
43 }
44
autoResume()45 void Stream::autoResume()
46 {
47 std::lock_guard lock(mLock);
48 if (mAutoPaused) {
49 if (mState == PAUSED) {
50 ALOGV("%s: track streamID: %d", __func__, (int)mStreamID);
51 mState = PLAYING;
52 if (mAudioTrack != nullptr) {
53 mAudioTrack->start();
54 }
55 }
56 mAutoPaused = false; // New for R: always reset autopause (consistent with API spec).
57 }
58 }
59
mute(bool muting)60 void Stream::mute(bool muting)
61 {
62 std::lock_guard lock(mLock);
63 mMuted = muting;
64 if (mAudioTrack != nullptr) {
65 if (mMuted) {
66 mAudioTrack->setVolume(0.0f, 0.0f);
67 } else {
68 mAudioTrack->setVolume(mLeftVolume, mRightVolume);
69 }
70 }
71 }
72
pause(int32_t streamID)73 void Stream::pause(int32_t streamID)
74 {
75 std::lock_guard lock(mLock);
76 if (streamID == mStreamID) {
77 if (mState == PLAYING) {
78 ALOGV("%s: track streamID: %d", __func__, streamID);
79 mState = PAUSED;
80 if (mAudioTrack != nullptr) {
81 mAudioTrack->pause();
82 }
83 }
84 }
85 }
86
resume(int32_t streamID)87 void Stream::resume(int32_t streamID)
88 {
89 std::lock_guard lock(mLock);
90 if (streamID == mStreamID) {
91 if (mState == PAUSED) {
92 ALOGV("%s: track streamID: %d", __func__, streamID);
93 mState = PLAYING;
94 if (mAudioTrack != nullptr) {
95 mAudioTrack->start();
96 }
97 mAutoPaused = false; // TODO: is this right? (ambiguous per spec), move outside?
98 }
99 }
100 }
101
setRate(int32_t streamID,float rate)102 void Stream::setRate(int32_t streamID, float rate)
103 {
104 std::lock_guard lock(mLock);
105 if (streamID == mStreamID) {
106 mRate = rate;
107 if (mAudioTrack != nullptr && mSound != nullptr) {
108 const auto sampleRate = (uint32_t)lround(double(mSound->getSampleRate()) * rate);
109 mAudioTrack->setSampleRate(sampleRate);
110 }
111 }
112 }
113
setVolume_l(float leftVolume,float rightVolume)114 void Stream::setVolume_l(float leftVolume, float rightVolume)
115 {
116 mLeftVolume = leftVolume;
117 mRightVolume = rightVolume;
118 if (mAudioTrack != nullptr && !mMuted) {
119 mAudioTrack->setVolume(leftVolume, rightVolume);
120 }
121 }
122
setVolume(int32_t streamID,float leftVolume,float rightVolume)123 void Stream::setVolume(int32_t streamID, float leftVolume, float rightVolume)
124 {
125 std::lock_guard lock(mLock);
126 if (streamID == mStreamID) {
127 setVolume_l(leftVolume, rightVolume);
128 }
129 }
130
setPriority(int32_t streamID,int32_t priority)131 void Stream::setPriority(int32_t streamID, int32_t priority)
132 {
133 std::lock_guard lock(mLock);
134 if (streamID == mStreamID) {
135 mPriority = priority;
136 }
137 }
138
setLoop(int32_t streamID,int32_t loop)139 void Stream::setLoop(int32_t streamID, int32_t loop)
140 {
141 std::lock_guard lock(mLock);
142 if (streamID == mStreamID) {
143 if (mAudioTrack != nullptr && mSound != nullptr) {
144 const uint32_t loopEnd = mSound->getSizeInBytes() / mSound->getChannelCount() /
145 (mSound->getFormat() == AUDIO_FORMAT_PCM_16_BIT
146 ? sizeof(int16_t) : sizeof(uint8_t));
147 mAudioTrack->setLoop(0, loopEnd, loop);
148 }
149 mLoop = loop;
150 }
151 }
152
setPlay(int32_t streamID,const std::shared_ptr<Sound> & sound,int32_t soundID,float leftVolume,float rightVolume,int32_t priority,int32_t loop,float rate)153 void Stream::setPlay(
154 int32_t streamID, const std::shared_ptr<Sound> &sound, int32_t soundID,
155 float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate)
156 {
157 std::lock_guard lock(mLock);
158 // We must be idle, or we must be repurposing a pending Stream.
159 LOG_ALWAYS_FATAL_IF(mState != IDLE && mAudioTrack != nullptr, "State %d must be IDLE", mState);
160 mSound = sound;
161 mSoundID = soundID;
162 mLeftVolume = leftVolume;
163 mRightVolume = rightVolume;
164 mPriority = priority;
165 mLoop = loop;
166 mRate = rate;
167 mState = PLAYING;
168 mAutoPaused = false; // New for R (consistent with Java API spec).
169 mStreamID = streamID; // prefer this to be the last, as it is an atomic sync point
170 }
171
setStopTimeNs(int64_t stopTimeNs)172 void Stream::setStopTimeNs(int64_t stopTimeNs)
173 {
174 std::lock_guard lock(mLock);
175 mStopTimeNs = stopTimeNs;
176 }
177
requestStop(int32_t streamID)178 bool Stream::requestStop(int32_t streamID)
179 {
180 std::lock_guard lock(mLock);
181 if (streamID == mStreamID) {
182 ALOGV("%s: track streamID: %d", __func__, streamID);
183 if (mAudioTrack != nullptr) {
184 if (mState == PLAYING && !mMuted && (mLeftVolume != 0.f || mRightVolume != 0.f)) {
185 setVolume_l(0.f, 0.f);
186 mStopTimeNs = systemTime() + kStopWaitTimeNs;
187 } else {
188 mStopTimeNs = systemTime();
189 }
190 return true; // must be queued on the restart list.
191 }
192 stop_l();
193 }
194 return false;
195 }
196
stop()197 void Stream::stop()
198 {
199 std::lock_guard lock(mLock);
200 stop_l();
201 }
202
stop_l()203 void Stream::stop_l()
204 {
205 if (mState != IDLE) {
206 ALOGV("%s: track(%p) streamID: %d", __func__, mAudioTrack.get(), (int)mStreamID);
207 if (mAudioTrack != nullptr) {
208 mAudioTrack->stop();
209 }
210 mSound.reset();
211 mState = IDLE;
212 }
213 }
214
clearAudioTrack()215 void Stream::clearAudioTrack()
216 {
217 sp<AudioTrack> release; // release outside of lock.
218 std::lock_guard lock(mLock);
219 // This will invoke the destructor which waits for the AudioTrack thread to join,
220 // and is currently the only safe way to ensure there are no callbacks afterwards.
221 release = mAudioTrack; // or std::swap if we had move semantics.
222 mAudioTrack.clear();
223 }
224
getPairStream() const225 Stream* Stream::getPairStream() const
226 {
227 return mStreamManager->getPairStream(this);
228 }
229
playPairStream()230 Stream* Stream::playPairStream() {
231 Stream* pairStream = getPairStream();
232 LOG_ALWAYS_FATAL_IF(pairStream == nullptr, "No pair stream!");
233 sp<AudioTrack> releaseTracks[2];
234 {
235 ALOGV("%s: track streamID: %d", __func__, (int)getStreamID());
236 // TODO: Do we really want to force a simultaneous synchronization between
237 // the stream and its pair?
238
239 // note locking order - the paired stream is obtained before the queued stream.
240 // we can invert the locking order, but it is slightly more optimal to do it this way.
241 std::lock_guard lockp(pairStream->mLock);
242 if (pairStream->mSound == nullptr) {
243 return nullptr; // no pair sound
244 }
245 {
246 std::lock_guard lock(mLock);
247 LOG_ALWAYS_FATAL_IF(mState != IDLE, "State: %d must be IDLE", mState);
248 // TODO: do we want a specific set() here?
249 pairStream->mAudioTrack = mAudioTrack;
250 pairStream->mSoundID = mSoundID; // optimization to reuse AudioTrack.
251 pairStream->mToggle = mToggle;
252 pairStream->mAutoPaused = mAutoPaused; // save autopause state
253 pairStream->mMuted = mMuted;
254 mAudioTrack.clear(); // the pair owns the audiotrack.
255 mSound.reset();
256 mSoundID = 0;
257 }
258 // TODO: do we need a specific play_l() anymore?
259 const int pairState = pairStream->mState;
260 pairStream->play_l(pairStream->mSound, pairStream->mStreamID,
261 pairStream->mLeftVolume, pairStream->mRightVolume, pairStream->mPriority,
262 pairStream->mLoop, pairStream->mRate, releaseTracks);
263 if (pairStream->mState == IDLE) {
264 return nullptr; // AudioTrack error
265 }
266 if (pairState == PAUSED) { // reestablish pause
267 pairStream->mState = PAUSED;
268 pairStream->mAudioTrack->pause();
269 }
270 }
271 // release tracks outside of Stream lock
272 return pairStream;
273 }
274
play_l(const std::shared_ptr<Sound> & sound,int32_t nextStreamID,float leftVolume,float rightVolume,int32_t priority,int32_t loop,float rate,sp<AudioTrack> releaseTracks[2])275 void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID,
276 float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate,
277 sp<AudioTrack> releaseTracks[2])
278 {
279 // These tracks are released without the lock.
280 sp<AudioTrack> &oldTrack = releaseTracks[0];
281 sp<AudioTrack> &newTrack = releaseTracks[1];
282 status_t status = NO_ERROR;
283
284 {
285 ALOGV("%s(%p)(soundID=%d, streamID=%d, leftVolume=%f, rightVolume=%f,"
286 " priority=%d, loop=%d, rate=%f)",
287 __func__, this, sound->getSoundID(), nextStreamID, leftVolume, rightVolume,
288 priority, loop, rate);
289
290 // initialize track
291 const audio_stream_type_t streamType =
292 AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
293 const int32_t channelCount = sound->getChannelCount();
294 const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
295 size_t frameCount = 0;
296
297 if (loop) {
298 const audio_format_t format = sound->getFormat();
299 const size_t frameSize = audio_is_linear_pcm(format)
300 ? channelCount * audio_bytes_per_sample(format) : 1;
301 frameCount = sound->getSizeInBytes() / frameSize;
302 }
303
304 // check if the existing track has the same sound id.
305 if (mAudioTrack != nullptr && mSoundID == sound->getSoundID()) {
306 // the sample rate may fail to change if the audio track is a fast track.
307 if (mAudioTrack->setSampleRate(sampleRate) == NO_ERROR) {
308 newTrack = mAudioTrack;
309 ALOGV("%s: reusing track %p for sound %d",
310 __func__, mAudioTrack.get(), sound->getSoundID());
311 }
312 }
313 if (newTrack == nullptr) {
314 // mToggle toggles each time a track is started on a given stream.
315 // The toggle is concatenated with the Stream address and passed to AudioTrack
316 // as callback user data. This enables the detection of callbacks received from the old
317 // audio track while the new one is being started and avoids processing them with
318 // wrong audio audio buffer size (mAudioBufferSize)
319 auto toggle = mToggle ^ 1;
320 void* userData = (void*)((uintptr_t)this | toggle);
321 audio_channel_mask_t soundChannelMask = sound->getChannelMask();
322 // When sound contains a valid channel mask, use it as is.
323 // Otherwise, use stream count to calculate channel mask.
324 audio_channel_mask_t channelMask = soundChannelMask != AUDIO_CHANNEL_NONE
325 ? soundChannelMask : audio_channel_out_mask_from_count(channelCount);
326
327 // do not create a new audio track if current track is compatible with sound parameters
328
329 newTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
330 channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
331 staticCallback, userData,
332 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
333 AudioTrack::TRANSFER_DEFAULT,
334 nullptr /*offloadInfo*/, -1 /*uid*/, -1 /*pid*/,
335 mStreamManager->getAttributes());
336 // Set caller name so it can be logged in destructor.
337 // MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_SOUNDPOOL
338 newTrack->setCallerName("soundpool");
339 oldTrack = mAudioTrack;
340 status = newTrack->initCheck();
341 if (status != NO_ERROR) {
342 ALOGE("%s: error creating AudioTrack", __func__);
343 // newTrack goes out of scope, so reference count drops to zero
344 goto exit;
345 }
346 // From now on, AudioTrack callbacks received with previous toggle value will be ignored.
347 mToggle = toggle;
348 mAudioTrack = newTrack;
349 ALOGV("%s: using new track %p for sound %d",
350 __func__, newTrack.get(), sound->getSoundID());
351 }
352 if (mMuted) {
353 newTrack->setVolume(0.0f, 0.0f);
354 } else {
355 newTrack->setVolume(leftVolume, rightVolume);
356 }
357 newTrack->setLoop(0, frameCount, loop);
358 mAudioTrack->start();
359 mSound = sound;
360 mSoundID = sound->getSoundID();
361 mPriority = priority;
362 mLoop = loop;
363 mLeftVolume = leftVolume;
364 mRightVolume = rightVolume;
365 mRate = rate;
366 mState = PLAYING;
367 mStopTimeNs = 0;
368 mStreamID = nextStreamID; // prefer this to be the last, as it is an atomic sync point
369 }
370
371 exit:
372 ALOGV("%s: delete oldTrack %p", __func__, oldTrack.get());
373 if (status != NO_ERROR) {
374 // TODO: should we consider keeping the soundID if the old track is OK?
375 // Do not attempt to restart this track (should we remove the stream id?)
376 mState = IDLE;
377 mSoundID = 0;
378 mSound.reset();
379 mAudioTrack.clear(); // actual release from releaseTracks[]
380 }
381 }
382
383 /* static */
staticCallback(int event,void * user,void * info)384 void Stream::staticCallback(int event, void* user, void* info)
385 {
386 const auto userAsInt = (uintptr_t)user;
387 auto stream = reinterpret_cast<Stream*>(userAsInt & ~1);
388 stream->callback(event, info, int(userAsInt & 1), 0 /* tries */);
389 }
390
callback(int event,void * info,int toggle,int tries)391 void Stream::callback(int event, void* info, int toggle, int tries)
392 {
393 int32_t activeStreamIDToRestart = 0;
394 {
395 std::unique_lock lock(mLock);
396 ALOGV("%s track(%p) streamID %d", __func__, mAudioTrack.get(), (int)mStreamID);
397
398 if (mAudioTrack == nullptr) {
399 // The AudioTrack is either with this stream or its pair.
400 // if this swaps a few times, the toggle is bound to be wrong, so we fail then.
401 //
402 // TODO: Modify AudioTrack callbacks to avoid the hacky toggle and retry
403 // logic here.
404 if (tries < 3) {
405 lock.unlock();
406 ALOGV("%s streamID %d going to pair stream", __func__, (int)mStreamID);
407 getPairStream()->callback(event, info, toggle, tries + 1);
408 } else {
409 ALOGW("%s streamID %d cannot find track", __func__, (int)mStreamID);
410 }
411 return;
412 }
413 if (mToggle != toggle) {
414 ALOGD("%s streamID %d wrong toggle", __func__, (int)mStreamID);
415 return;
416 }
417 switch (event) {
418 case AudioTrack::EVENT_MORE_DATA:
419 ALOGW("%s streamID %d Invalid EVENT_MORE_DATA for static track",
420 __func__, (int)mStreamID);
421 break;
422 case AudioTrack::EVENT_UNDERRUN:
423 ALOGW("%s streamID %d Invalid EVENT_UNDERRUN for static track",
424 __func__, (int)mStreamID);
425 break;
426 case AudioTrack::EVENT_BUFFER_END:
427 ALOGV("%s streamID %d EVENT_BUFFER_END", __func__, (int)mStreamID);
428 if (mState != IDLE) {
429 activeStreamIDToRestart = mStreamID;
430 mStopTimeNs = systemTime();
431 }
432 break;
433 case AudioTrack::EVENT_LOOP_END:
434 ALOGV("%s streamID %d EVENT_LOOP_END", __func__, (int)mStreamID);
435 break;
436 case AudioTrack::EVENT_NEW_IAUDIOTRACK:
437 ALOGV("%s streamID %d NEW_IAUDIOTRACK", __func__, (int)mStreamID);
438 break;
439 default:
440 ALOGW("%s streamID %d Invalid event %d", __func__, (int)mStreamID, event);
441 break;
442 }
443 } // lock ends here. This is on the callback thread, no need to be precise.
444 if (activeStreamIDToRestart > 0) {
445 // Restart only if a particular streamID is still current and active.
446 ALOGV("%s: moveToRestartQueue %d", __func__, activeStreamIDToRestart);
447 mStreamManager->moveToRestartQueue(this, activeStreamIDToRestart);
448 }
449 }
450
dump() const451 void Stream::dump() const
452 {
453 // TODO: consider std::try_lock() - ok for now for ALOGV.
454 ALOGV("mPairStream=%p, mState=%d, mStreamID=%d, mSoundID=%d, mPriority=%d, mLoop=%d",
455 getPairStream(), mState, (int)getStreamID(), getSoundID(), mPriority, mLoop);
456 }
457
458 } // namespace android::soundpool
459