1 //============================================================================
2 //
3 //   SSSS    tt          lll  lll
4 //  SS  SS   tt           ll   ll
5 //  SS     tttttt  eeee   ll   ll   aaaa
6 //   SSSS    tt   ee  ee  ll   ll      aa
7 //      SS   tt   eeeeee  ll   ll   aaaaa  --  "An Atari 2600 VCS Emulator"
8 //  SS  SS   tt   ee      ll   ll  aa  aa
9 //   SSSS     ttt  eeeee llll llll  aaaaa
10 //
11 // Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
12 // and the Stella Team
13 //
14 // See the file "License.txt" for information on usage and redistribution of
15 // this file, and for a DISCLAIMER OF ALL WARRANTIES.
16 //============================================================================
17 
18 #ifdef SOUND_SUPPORT
19 
20 #ifndef SOUND_SDL2_HXX
21 #define SOUND_SDL2_HXX
22 
23 class OSystem;
24 class AudioQueue;
25 class EmulationTiming;
26 class AudioSettings;
27 class Resampler;
28 
29 #include "SDL_lib.hxx"
30 
31 #include "bspf.hxx"
32 #include "Sound.hxx"
33 
34 /**
35   This class implements the sound API for SDL.
36 
37   @author Stephen Anthony and Christian Speckner (DirtyHairy)
38 */
39 class SoundSDL2 : public Sound
40 {
41   public:
42     /**
43       Create a new sound object.  The init method must be invoked before
44       using the object.
45     */
46     SoundSDL2(OSystem& osystem, AudioSettings& audioSettings);
47 
48     /**
49       Destructor
50     */
51     ~SoundSDL2() override;
52 
53   public:
54     /**
55       Enables/disables the sound subsystem.
56 
57       @param enable  Either true or false, to enable or disable the sound system
58     */
59     void setEnabled(bool enable) override;
60 
61     /**
62       Initializes the sound device.  This must be called before any
63       calls are made to derived methods.
64     */
65     void open(shared_ptr<AudioQueue> audioQueue, EmulationTiming* emulationTiming) override;
66 
67     /**
68       Should be called to close the sound device.  Once called the sound
69       device can be started again using the open method.
70     */
71     void close() override;
72 
73     /**
74       Set the mute state of the sound object.  While muted no sound is played.
75 
76       @param state Mutes sound if true, unmute if false
77 
78       @return  The previous (old) mute state
79     */
80     bool mute(bool state) override;
81 
82     /**
83       Toggles the sound mute state.  While muted no sound is played.
84 
85       @return  The previous (old) mute state
86     */
87     bool toggleMute() override;
88 
89     /**
90       Sets the volume of the sound device to the specified level.  The
91       volume is given as a percentage from 0 to 100.  Values outside
92       this range indicate that the volume shouldn't be changed at all.
93 
94       @param percent  The new volume percentage level for the sound device
95     */
96     void setVolume(uInt32 percent) override;
97 
98     /**
99       Adjusts the volume of the sound device based on the given direction.
100 
101       @param direction  +1 indicates increase, -1 indicates decrease.
102       */
103     void adjustVolume(int direction = 1) override;
104 
105     /**
106       This method is called to provide information about the sound device.
107     */
108     string about() const override;
109 
110   protected:
111     /**
112       This method is called to query the audio devices.
113 
114       @param devices  List of device names
115     */
116     void queryHardware(VariantList& devices) override;
117 
118     /**
119       Invoked by the sound callback to process the next sound fragment.
120       The stream is 16-bits (even though the callback is 8-bits), since
121       the TIASnd class always generates signed 16-bit stereo samples.
122 
123       @param stream  Pointer to the start of the fragment
124       @param length  Length of the fragment
125     */
126     void processFragment(float* stream, uInt32 length);
127 
128   private:
129     /**
130       The actual sound device is opened only when absolutely necessary.
131       Typically this will only happen once per program run, but it can also
132       happen dynamically when changing sample rate and/or fragment size.
133     */
134     bool openDevice();
135 
136     void initResampler();
137 
138   private:
139     // Indicates if the sound device was successfully initialized
140     bool myIsInitializedFlag{false};
141 
142     // Current volume as a percentage (0 - 100)
143     uInt32 myVolume{100};
144     float myVolumeFactor{0xffff};
145 
146     // Audio specification structure
147     SDL_AudioSpec myHardwareSpec;
148 
149     uInt32 myDeviceId{0};
150 
151     SDL_AudioDeviceID myDevice{0};
152 
153     shared_ptr<AudioQueue> myAudioQueue;
154 
155     EmulationTiming* myEmulationTiming{nullptr};
156 
157     Int16* myCurrentFragment{nullptr};
158     bool myUnderrun{false};
159 
160     unique_ptr<Resampler> myResampler;
161 
162     AudioSettings& myAudioSettings;
163 
164     string myAboutString;
165 
166   private:
167     // Callback function invoked by the SDL Audio library when it needs data
168     static void callback(void* udata, uInt8* stream, int len);
169 
170     // Following constructors and assignment operators not supported
171     SoundSDL2() = delete;
172     SoundSDL2(const SoundSDL2&) = delete;
173     SoundSDL2(SoundSDL2&&) = delete;
174     SoundSDL2& operator=(const SoundSDL2&) = delete;
175     SoundSDL2& operator=(SoundSDL2&&) = delete;
176 };
177 
178 #endif
179 
180 #endif  // SOUND_SUPPORT
181