• 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 #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