xref: /aosp_15_r20/frameworks/base/media/jni/soundpool/Stream.h (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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