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 #pragma once 18 19 #include "Sound.h" 20 21 #include <any> 22 #include <android-base/thread_annotations.h> 23 #include <audio_utils/clock.h> 24 #include <media/AudioTrack.h> 25 26 namespace android::soundpool { 27 28 // This is the amount of time to wait after stop is called when stealing an 29 // AudioTrack to allow the sound to ramp down. If this is 0, glitches 30 // may occur when stealing an AudioTrack. 31 inline constexpr int64_t kStopWaitTimeNs = 20 * NANOS_PER_MILLISECOND; 32 33 inline constexpr size_t kCacheLineSize = 64; /* std::hardware_constructive_interference_size */ 34 35 class StreamManager; // forward decl 36 37 /** 38 * A Stream is associated with a StreamID exposed to the app to play a Sound. 39 * 40 * The Stream uses monitor locking strategy on mLock. 41 * https://en.wikipedia.org/wiki/Monitor_(synchronization) 42 * 43 * where public methods are guarded by a lock (as needed) 44 * 45 * For Java equivalent APIs, see 46 * https://developer.android.com/reference/android/media/SoundPool 47 * 48 * Streams are paired by the StreamManager, so one stream in the pair may be "stopping" 49 * while the other stream of the pair has been prepared to run 50 * (and the streamID returned to the app) pending its pair to be stopped. 51 * The pair of a Stream may be obtained by calling getPairStream(), 52 * where this->getPairStream()->getPairStream() == this; (pair is a commutative relationship). 53 * 54 * playPairStream() and getPairPriority() access the paired stream. 55 * See also StreamManager.h for details of physical layout implications of paired streams. 56 */ 57 class alignas(kCacheLineSize) Stream { 58 public: 59 enum state { IDLE, PAUSED, PLAYING }; 60 // The PAUSED, PLAYING state directly corresponds to the AudioTrack state of an active Stream. 61 // 62 // The IDLE state indicates an inactive Stream. An IDLE Stream may have a non-nullptr 63 // AudioTrack, which may be recycled for use if the SoundID matches the next Stream playback. 64 // 65 // PAUSED -> PLAYING through resume() (see also autoResume()) 66 // PLAYING -> PAUSED through pause() (see also autoPause()) 67 // 68 // IDLE is the initial state of a Stream and also when a stream becomes inactive. 69 // {PAUSED, PLAYING} -> IDLE through stop() (or if the Sound finishes playing) 70 // IDLE -> PLAYING through play(). (there is no way to start a Stream in paused mode). 71 72 ~Stream(); setStreamManager(StreamManager * streamManager)73 void setStreamManager(StreamManager* streamManager) { // non-nullptr 74 mStreamManager = streamManager; // set in StreamManager constructor, not changed 75 } 76 77 // The following methods are monitor locked by mLock. 78 // 79 // For methods taking a streamID: 80 // if the streamID matches the Stream's mStreamID, then method proceeds 81 // else the command is ignored with no effect. 82 83 // returns true if the stream needs to be explicitly stopped. 84 bool requestStop(int32_t streamID); 85 void stop(); // explicit stop(), typically called from the worker thread. 86 void clearAudioTrack(); 87 void pause(int32_t streamID); 88 void autoPause(); // see the Java SoundPool.autoPause documentation for details. 89 void resume(int32_t streamID); 90 void autoResume(); 91 void mute(bool muting); 92 void dump() const NO_THREAD_SAFETY_ANALYSIS; // disable for ALOGV (see func for details). 93 94 // returns the pair stream if successful, nullptr otherwise. 95 // garbage is used to release tracks and data outside of any lock. 96 Stream* playPairStream(std::vector<std::any>& garbage); 97 98 // These parameters are explicitly checked in the SoundPool class 99 // so never deviate from the Java API specified values. 100 void setVolume(int32_t streamID, float leftVolume, float rightVolume); 101 void setRate(int32_t streamID, float rate); 102 void setPriority(int32_t streamID, int priority); 103 void setLoop(int32_t streamID, int loop); 104 void setPlay(int32_t streamID, const std::shared_ptr<Sound> &sound, int32_t soundID, 105 float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate); 106 void setStopTimeNs(int64_t stopTimeNs); // systemTime() clock monotonic. 107 108 // The following getters are not locked and have weak consistency. 109 // These are considered advisory only - being stale is of nuisance. getPriority()110 int32_t getPriority() const NO_THREAD_SAFETY_ANALYSIS { return mPriority; } getPairPriority()111 int32_t getPairPriority() const NO_THREAD_SAFETY_ANALYSIS { 112 return getPairStream()->getPriority(); 113 } getStopTimeNs()114 int64_t getStopTimeNs() const NO_THREAD_SAFETY_ANALYSIS { return mStopTimeNs; } 115 116 // Can change with setPlay() getStreamID()117 int32_t getStreamID() const NO_THREAD_SAFETY_ANALYSIS { return mStreamID; } 118 119 // Can change with play_l() getSoundID()120 int32_t getSoundID() const NO_THREAD_SAFETY_ANALYSIS { return mSoundID; } 121 hasSound()122 bool hasSound() const NO_THREAD_SAFETY_ANALYSIS { return mSound.get() != nullptr; } 123 124 // This never changes. See top of header. 125 Stream* getPairStream() const; 126 127 // Stream ID of ourselves, or the pair depending on who holds the AudioTrack 128 int getCorrespondingStreamID(); 129 130 protected: 131 // AudioTrack callback interface implementation 132 class StreamCallback : public AudioTrack::IAudioTrackCallback { 133 public: StreamCallback(Stream * stream,bool toggle)134 StreamCallback(Stream * stream, bool toggle) : mStream(stream), mToggle(toggle) {} 135 size_t onMoreData(const AudioTrack::Buffer& buffer) override; 136 void onUnderrun() override; 137 void onLoopEnd(int32_t loopsRemaining) override; 138 void onMarker(uint32_t markerPosition) override; 139 void onNewPos(uint32_t newPos) override; 140 void onBufferEnd() override; 141 void onNewIAudioTrack() override; 142 void onStreamEnd() override; 143 size_t onCanWriteMoreData(const AudioTrack::Buffer& buffer) override; 144 145 // Holding a raw ptr is technically unsafe, but, Stream objects persist 146 // through the lifetime of the StreamManager through the use of a 147 // unique_ptr<Stream[]>. Ensuring lifetime will cause us to give up 148 // locality as well as pay RefBase/sp performance cost, which we are 149 // unwilling to do. Non-owning refs to unique_ptrs are idiomatically raw 150 // ptrs, as below. 151 Stream * const mStream; 152 const bool mToggle; 153 }; 154 155 sp<StreamCallback> mCallback; 156 private: 157 // garbage is used to release tracks and data outside of any lock. 158 void play_l(const std::shared_ptr<Sound>& sound, int streamID, 159 float leftVolume, float rightVolume, int priority, int loop, float rate, 160 std::vector<std::any>& garbage) REQUIRES(mLock); 161 void stop_l() REQUIRES(mLock); 162 void setVolume_l(float leftVolume, float rightVolume) REQUIRES(mLock); 163 164 // For use with AudioTrack callback. 165 void onBufferEnd(int toggle, int tries) NO_THREAD_SAFETY_ANALYSIS; 166 167 // StreamManager should be set on construction and not changed. 168 // release mLock before calling into StreamManager 169 StreamManager* mStreamManager = nullptr; 170 171 mutable std::mutex mLock; 172 std::atomic_int32_t mStreamID GUARDED_BY(mLock) = 0; // Valid streamIDs are always positive. 173 int mState GUARDED_BY(mLock) = IDLE; 174 std::shared_ptr<Sound> mSound GUARDED_BY(mLock); // Non-null if playing. 175 int32_t mSoundID GUARDED_BY(mLock) = 0; // SoundID associated with AudioTrack. 176 float mLeftVolume GUARDED_BY(mLock) = 0.f; 177 float mRightVolume GUARDED_BY(mLock) = 0.f; 178 int32_t mPriority GUARDED_BY(mLock) = INT32_MIN; 179 int32_t mLoop GUARDED_BY(mLock) = 0; 180 float mRate GUARDED_BY(mLock) = 0.f; 181 bool mAutoPaused GUARDED_BY(mLock) = false; 182 bool mMuted GUARDED_BY(mLock) = false; 183 184 sp<AudioTrack> mAudioTrack GUARDED_BY(mLock); 185 int mToggle GUARDED_BY(mLock) = 0; 186 int64_t mStopTimeNs GUARDED_BY(mLock) = 0; // if nonzero, time to wait for stop. 187 }; 188 189 } // namespace android::soundpool 190