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