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