• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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