Etterna 0.75.1
Loading...
Searching...
No Matches
RageSound.h
1/* RageSound - High-level sound object. */
2
3#ifndef RAGE_SOUND_H
4#define RAGE_SOUND_H
5
6#include "RageSoundPosMap.h"
7#include "RageUtil/Misc/RageThreads.h"
8#include "RageUtil/Misc/RageTimer.h"
9#include "Etterna/Models/Lua/LuaReference.h"
10#include "fft.h"
11
12class RageSoundReader;
13struct lua_State;
14
15struct Butter
16{
17 RageTimer tm;
18 RageTimer hwTime;
19 RageTimer syncTime;
20 float hwPosition;
21 float syncPosition;
22 float acc;
23};
24
25/* Driver interface for sounds: this is what drivers see. */
27{
28 public:
29 virtual ~RageSoundBase() = default;
30 virtual void SoundIsFinishedPlaying() = 0;
31 virtual auto GetDataToPlay(float* buffer,
32 int size,
33 int64_t& iStreamFrame,
34 int& got_bytes) -> int = 0;
35 virtual void CommitPlayingPosition(int64_t iFrameno,
36 int64_t iPosition,
37 int iBytesRead) = 0;
38 [[nodiscard]] virtual auto GetStartTime() const -> RageTimer
39 {
40 return RageZeroTimer;
41 }
42 [[nodiscard]] virtual auto GetLoadedFilePath() const -> std::string = 0;
43};
44
51{
53
54 // The amount of data to play (or loop):
55 float m_StartSecond{ 0 };
56 float m_LengthSeconds{ -1 };
57
58 // Number of seconds to spend fading in.
59 float m_fFadeInSeconds{ 0 };
60
61 // Number of seconds to spend fading out.
62 float m_fFadeOutSeconds{ 0 };
63
64 float m_Volume{ 1.0F };
65 float m_fAttractVolume{ 1.0F }; // multiplies with m_Volume
66
67 /* Number of samples input and output when changing speed.
68 * Currently, this is either 1/1, 5/4 or 4/5. */
69 float m_fPitch{ 1.0F };
70 float m_fSpeed{ 1.0F };
71
72 // Accurate Sync (for now only useful for MP3s)
73 bool m_bAccurateSync{ false };
74
75 /* Optional driver feature: time to actually start playing sounds.
76 * If zero, or if not supported, the sound will start immediately. */
77 RageTimer m_StartTime;
78
81 {
86 M_AUTO
88 } StopMode{
89 M_AUTO
90 };
91
92 // "is a sound that should be played even during attract"
93 bool m_bIsCriticalSound{ false };
94
95 // represents sounds caused by presses
96 bool m_bIsAction{ false };
97
98 // sounds (music) playing in the background
99 bool m_bIsBGM{ false };
100};
101
103{
105
106 /* If true, speed and pitch changes will be supported for this sound, at a
107 * small memory penalty if not used. */
108 bool m_bSupportRateChanging{ false };
109
110 // If true, panning will be supported for this sound.
111 bool m_bSupportPan{ false };
112};
113
114template <class T>
116{
117 public:
118 typedef T value_type;
119 MufftAllocator() noexcept {};
120
121 T* allocate(size_t n) { return static_cast<T*>(mufft_alloc(n * sizeof(T))); }
122 void deallocate(T* p, size_t n) { mufft_free(p); }
123
124 template<typename U>
125 MufftAllocator(const MufftAllocator<U>& other) throw(){};
126};
127
128struct cfloat
129{
130 float real;
131 float imag;
132};
133
135{
136 public:
137 RageSound();
138 ~RageSound() override;
139 RageSound(const RageSound& cpy);
140 auto operator=(const RageSound& cpy) -> RageSound&;
141
142 /* If bPrecache == true, we'll preload the entire file into memory if
143 * small enough. If this is done, a large number of copies of the sound
144 * can be played without much performance penalty. This is useful for
145 * efficiently playing keyed sounds, and for rapidly-repeating sound
146 * effects, such as the music wheel.
147 *
148 * If cache == false, we'll always stream the sound on demand, which
149 * makes loads much faster.
150 *
151 * If the file failed to load, false is returned, Error() is set
152 * and a null sample will be loaded. This makes failed loads nonfatal;
153 * they can be ignored most of the time, so we continue to work if a file
154 * is broken or missing.
155 */
156 auto Load(const std::string& sFile,
157 bool bPrecache,
158 const RageSoundLoadParams* pParams = nullptr) -> bool;
159
160 /* Using this version means the "don't care" about caching. Currently,
161 * this always will not cache the sound; this may become a preference. */
162 auto Load(const std::string& sFile) -> bool;
163
164 /* Load a RageSoundReader that you've set up yourself. Sample rate
165 * conversion will be set up only if needed. Doesn't fail. */
166 void LoadSoundReader(RageSoundReader* pSound);
167
168 // Get the loaded RageSoundReader. While playing, only properties can be
169 // set.
170 auto GetSoundReader() -> RageSoundReader* { return m_pSource; }
171
172 void Unload();
173 auto IsLoaded() const -> bool;
174 void DeleteSelfWhenFinishedPlaying();
175
176 void StartPlaying(float fGiven = 0, bool forcedTime = false);
177 void StopPlaying();
178
179 auto GetError() const -> std::string { return m_sError; }
180
181 void Play(bool is_action, const RageSoundParams* params = nullptr);
182 void PlayCopy(bool is_action,
183 const RageSoundParams* pParams = nullptr) const;
184 void Stop();
185
186 /* Cleanly pause or unpause the sound. If the sound wasn't already playing,
187 * return true and do nothing. */
188 auto Pause(bool bPause) -> bool;
189 bool m_bPaused{ false };
190
191 auto GetLengthSeconds() -> float;
192 auto GetPositionSeconds(bool* approximate = nullptr,
193 RageTimer* Timestamp = nullptr) -> float;
194 auto GetLoadedFilePath() const -> std::string override
195 {
196 return m_sFilePath;
197 }
198 auto IsPlaying() const -> bool { return m_bPlaying; }
199
200 auto GetPlaybackRate() const -> float;
201 auto GetStartTime() const -> RageTimer override;
202 void SetParams(const RageSoundParams& p);
203 auto GetParams() const -> const RageSoundParams& { return m_Param; }
204 auto SetProperty(const std::string& sProperty, float fValue) -> bool;
205 void SetStopModeFromString(const std::string& sStopMode);
206 void SetPositionSeconds(float fGiven);
207 void SetIsAction(bool b);
208
209 void SetPlayBackCallback(const std::shared_ptr<LuaReference>& f,
210 unsigned int bufSize = 1024);
211 std::atomic<bool> pendingPlayBackCall{ false };
212 void ExecutePlayBackCallback(Lua* L);
213
214 // Lua
215 virtual void PushSelf(lua_State* L);
216
217 private:
218 mutable RageMutex m_Mutex;
219
220 RageSoundReader* m_pSource;
221
222 // We keep track of sound blocks we've sent out recently through
223 // GetDataToPlay.
224 pos_map_queue m_HardwareToStreamMap;
225 pos_map_queue m_StreamToSourceMap;
226
227 std::string m_sFilePath;
228
229 void ApplyParams();
230 RageSoundParams m_Param;
231
232 /* Current position of the output sound, in frames. If < 0, nothing will
233 * play until it becomes positive. */
234 int64_t m_iStreamFrame;
235
236 // For all operations related to sound play callbacks
237 std::mutex recentSamplesMutex;
238 unsigned int recentPCMSamplesBufferSize{ 1024 };
239 std::shared_ptr<LuaReference> soundPlayCallback;
240 std::vector<float, MufftAllocator<float>> recentPCMSamples;
241 std::vector<cfloat, MufftAllocator<cfloat>> fftBuffer;
242 mufft_plan_1d *fftPlan{ nullptr };
243
244 /* Hack: When we stop a playing sound, we can't ask the driver the position
245 * (we're not playing); and we can't seek back to the current playing
246 * position when we stop (too slow), but we want to be able to report the
247 * position we were at when we stopped without jumping to the last position
248 * we buffered. Keep track of the position after a seek or stop, so we can
249 * return a sane position when stopped, and when playing but pos_map hasn't
250 * yet been filled. */
251 int m_iStoppedSourceFrame{ 0 };
252 bool m_bPlaying{ false };
253 bool m_bDeleteWhenFinished{ false };
254
255 std::string m_sError;
256
257 Butter m_Pasteurizer{};
258
259 auto GetSourceFrameFromHardwareFrame(int64_t iHardwareFrame,
260 bool* bApproximate = nullptr) const
261 -> int;
262
263 auto SetPositionFrames(int frames = -1) -> bool;
264 auto GetStopMode() const -> RageSoundParams::StopMode_t; // resolves M_AUTO
265
266 void SoundIsFinishedPlaying() override; // called by sound drivers
267
268 public:
269 // These functions are called only by sound drivers.
270
271 /* Returns the number of bytes actually put into pBuffer. If 0 is returned,
272 * it signals the stream to stop; once it's flushed, SoundStopped will be
273 * called. Until then, SOUNDMAN->GetPosition can still be called; the sound
274 * is still playing. */
275 auto GetDataToPlay(float* pBuffer,
276 int iSize,
277 int64_t& iStreamFrame,
278 int& iBytesRead) -> int override;
279 void CommitPlayingPosition(int64_t iHardwareFrame,
280 int64_t iStreamFrame,
281 int iGotFrames) override;
282};
283
284#endif
Definition RageSound.h:116
Definition RageThreads.h:232
Definition RageSound.h:27
Definition RageSoundReader.h:7
Definition RageSound.h:135
Definition RageTimer.h:9
Definition RageSoundPosMap.h:8
Definition RageSound.h:16
Definition RageSound.h:103
The parameters to play a sound.
Definition RageSound.h:51
enum RageSoundParams::StopMode_t M_AUTO
How does the sound stop itself, if it does?
StopMode_t
How does the sound stop itself, if it does?
Definition RageSound.h:81
@ M_CONTINUE
Definition RageSound.h:84
@ M_LOOP
Definition RageSound.h:83
@ M_STOP
Definition RageSound.h:82
Definition RageSound.h:129