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