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 */
alignas(kCacheLineSize)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();
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 int32_t playerIId = PLAYER_PIID_INVALID);
98
99 // These parameters are explicitly checked in the SoundPool class
100 // so never deviate from the Java API specified values.
101 void setVolume(int32_t streamID, float leftVolume, float rightVolume);
102 void setRate(int32_t streamID, float rate);
103 void setPriority(int32_t streamID, int priority);
104 void setLoop(int32_t streamID, int loop);
105 void setPlay(int32_t streamID, const std::shared_ptr<Sound> &sound, int32_t soundID,
106 float leftVolume, float rightVolume, int32_t priority, int32_t loop, float rate);
107 void setStopTimeNs(int64_t stopTimeNs); // systemTime() clock monotonic.
108
109 // The following getters are not locked and have weak consistency.
110 // These are considered advisory only - being stale is of nuisance.
111 int32_t getPriority() const NO_THREAD_SAFETY_ANALYSIS { return mPriority; }
112 int32_t getPairPriority() const NO_THREAD_SAFETY_ANALYSIS {
113 return getPairStream()->getPriority();
114 }
115 int64_t getStopTimeNs() const NO_THREAD_SAFETY_ANALYSIS { return mStopTimeNs; }
116
117 // Can change with setPlay()
118 int32_t getStreamID() const NO_THREAD_SAFETY_ANALYSIS { return mStreamID; }
119
120 // Can change with play_l()
121 int32_t getSoundID() const NO_THREAD_SAFETY_ANALYSIS { return mSoundID; }
122
123 bool hasSound() const NO_THREAD_SAFETY_ANALYSIS { return mSound.get() != nullptr; }
124
125 // This never changes. See top of header.
126 Stream* getPairStream() const;
127
128 // Stream ID of ourselves, or the pair depending on who holds the AudioTrack
129 int getCorrespondingStreamID();
130
131 protected:
132 // AudioTrack callback interface implementation
133 class StreamCallback : public AudioTrack::IAudioTrackCallback {
134 public:
135 StreamCallback(Stream * stream, bool toggle) : mStream(stream), mToggle(toggle) {}
136 size_t onMoreData(const AudioTrack::Buffer& buffer) override;
137 void onUnderrun() override;
138 void onLoopEnd(int32_t loopsRemaining) override;
139 void onMarker(uint32_t markerPosition) override;
140 void onNewPos(uint32_t newPos) override;
141 void onBufferEnd() override;
142 void onNewIAudioTrack() override;
143 void onStreamEnd() override;
144 size_t onCanWriteMoreData(const AudioTrack::Buffer& buffer) override;
145
146 // Holding a raw ptr is technically unsafe, but, Stream objects persist
147 // through the lifetime of the StreamManager through the use of a
148 // unique_ptr<Stream[]>. Ensuring lifetime will cause us to give up
149 // locality as well as pay RefBase/sp performance cost, which we are
150 // unwilling to do. Non-owning refs to unique_ptrs are idiomatically raw
151 // ptrs, as below.
152 Stream * const mStream;
153 const bool mToggle;
154 };
155
156 sp<StreamCallback> mCallback;
157 private:
158 // garbage is used to release tracks and data outside of any lock.
159 void play_l(const std::shared_ptr<Sound>& sound, int streamID,
160 float leftVolume, float rightVolume, int priority, int loop, float rate,
161 std::vector<std::any>& garbage, int playerIId) REQUIRES(mLock);
162 void stop_l() REQUIRES(mLock);
163 void setVolume_l(float leftVolume, float rightVolume) REQUIRES(mLock);
164
165 // For use with AudioTrack callback.
166 void onBufferEnd(int toggle, int tries) NO_THREAD_SAFETY_ANALYSIS;
167
168 // StreamManager should be set on construction and not changed.
169 // release mLock before calling into StreamManager
170 StreamManager* mStreamManager = nullptr;
171
172 mutable std::mutex mLock;
173 std::atomic_int32_t mStreamID GUARDED_BY(mLock) = 0; // Valid streamIDs are always positive.
174 int mState GUARDED_BY(mLock) = IDLE;
175 std::shared_ptr<Sound> mSound GUARDED_BY(mLock); // Non-null if playing.
176 int32_t mSoundID GUARDED_BY(mLock) = 0; // SoundID associated with AudioTrack.
177 float mLeftVolume GUARDED_BY(mLock) = 0.f;
178 float mRightVolume GUARDED_BY(mLock) = 0.f;
179 int32_t mPriority GUARDED_BY(mLock) = INT32_MIN;
180 int32_t mLoop GUARDED_BY(mLock) = 0;
181 float mRate GUARDED_BY(mLock) = 0.f;
182 bool mAutoPaused GUARDED_BY(mLock) = false;
183 bool mMuted GUARDED_BY(mLock) = false;
184
185 sp<AudioTrack> mAudioTrack GUARDED_BY(mLock);
186 int mToggle GUARDED_BY(mLock) = 0;
187 int64_t mStopTimeNs GUARDED_BY(mLock) = 0; // if nonzero, time to wait for stop.
188 };
189
190 } // namespace android::soundpool
191