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