1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
|
//-----------------------------------------------------------------------------
// Name: soundtrack.h
// Developer: Wolfire Games LLC
// Description:
// License: Read below
//-----------------------------------------------------------------------------
//
// Copyright 2022 Wolfire Games LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//-----------------------------------------------------------------------------
#pragma once
#include <Sound/al_audio.h>
#include <Sound/Loader/base_loader.h>
#include <Sound/high_res_buffer_segment.h>
#include <Sound/AudioFilters/transition_mixer.h>
#include <Sound/AudioFilters/limiter_audio_filter.h>
#include <XML/Parsers/musicxmlparser.h>
#include <Sound/buffer_segment.h>
#include <Internal/filesystem.h>
#include <Internal/referencecounter.h>
#include <map>
#include <set>
#include <deque>
class Soundtrack;
extern bool g_sound_enable_layered_soundtrack_limiter;
class PlayedInterface
{
public:
PlayedInterface( Soundtrack* _owner );
virtual ~PlayedInterface() {};
virtual void update( HighResBufferSegment* buffer ) = 0;
virtual int64_t GetPCMPos() = 0;
virtual void SetPCMPos( int64_t pos ) = 0;
virtual int64_t GetPCMCount() = 0;
virtual int SampleRate() = 0;
virtual int Channels() = 0;
protected:
Soundtrack* owner;
};
class PlayedSongInterface : public PlayedInterface
{
public:
PlayedSongInterface( Soundtrack* _owner );
PlayedSongInterface( Soundtrack* _owner, const MusicXMLParser::Song& song);
virtual ~PlayedSongInterface() {};
virtual void Rewind() = 0;
virtual bool IsAtEnd() = 0;
virtual void SetGain(float v) = 0;
virtual float GetGain() = 0;
const std::string GetSongName() const;
const std::string GetSongType() const;
const MusicXMLParser::Song& GetSong() const;
protected:
MusicXMLParser::Song song;
};
typedef ReferenceCounter<PlayedSongInterface> rc_PlayedSongInterface;
class Soundtrack : public audioStreamer
{
private:
class PlayedSegment : public PlayedInterface
{
public:
PlayedSegment( Soundtrack* _owner );
PlayedSegment( Soundtrack* _owner, const MusicXMLParser::Segment& segment, bool overlapped_transition );
virtual ~PlayedSegment() {};
void update( HighResBufferSegment* buffer );
const MusicXMLParser::Segment& GetSegment();
bool IsAtEnd();
void Rewind();
int64_t GetPCMPos();
void SetPCMPos( int64_t pos );
int64_t GetPCMCount();
int SampleRate();
int Channels();
const std::string GetSegmentName() const;
private:
rc_baseLoader data;
MusicXMLParser::Segment segment;
public:
bool overlapped_transition;
bool started;
};
class PlayedSegmentedSong : public PlayedSongInterface
{
public:
PlayedSegmentedSong( Soundtrack* _owner, const MusicXMLParser::Song& _song );
virtual ~PlayedSegmentedSong( );
void update( HighResBufferSegment* buffer );
bool QueueSegment( const MusicXMLParser::Segment& nextSeg );
bool TransitionIntoSegment( const MusicXMLParser::Segment& newSeg );
bool SetSegment( const MusicXMLParser::Segment& nextSeg );
bool IsAtEnd();
void Rewind();
int SampleRate();
int Channels();
void SetGain(float v);
float GetGain();
const std::string GetSegmentName() const;
void SetPCMPos( int64_t c );
int64_t GetPCMPos();
int64_t GetPCMCount();
friend bool operator==( const Soundtrack::PlayedSegmentedSong &lhs, const Soundtrack::PlayedSegmentedSong &rhs );
private:
PlayedSegment currentSegment;
std::deque<PlayedSegment> segmentQueue;
TransitionMixer mixer;
};
class PlayedSingleSong : public PlayedSongInterface
{
public:
PlayedSingleSong( Soundtrack* _owner );
PlayedSingleSong( Soundtrack* _owner, const MusicXMLParser::Song& song );
virtual ~PlayedSingleSong() {};
void update( HighResBufferSegment* buffer );
bool IsAtEnd();
void Rewind();
int64_t GetPCMPos();
void SetPCMPos( int64_t pos );
int64_t GetPCMCount();
void SetGain(float v);
float GetGain();
int SampleRate();
int Channels();
private:
rc_baseLoader data;
public:
bool started;
int current_volume_step;
float volume_change_per_second;
float target_gain;
float from_gain;
float current_gain;
};
class PlayedLayeredSong : public PlayedSongInterface
{
public:
PlayedLayeredSong( Soundtrack* _owner );
PlayedLayeredSong( Soundtrack* _owner, const MusicXMLParser::Song& song );
virtual ~PlayedLayeredSong();
void update( HighResBufferSegment* buffer );
bool IsAtEnd();
void Rewind();
int64_t GetPCMPos();
void SetPCMPos( int64_t pos );
int64_t GetPCMCount();
void SetGain(float v);
float GetGain();
int SampleRate();
int Channels();
//Specialized
bool SetLayerGain(const std::string& name, float gain);
float GetLayerGain(const std::string& name);
std::vector<std::string> GetLayerNames() const;
const std::map<std::string,float> GetLayerGains();
private:
std::vector<rc_PlayedSongInterface> subsongs;
int64_t pcm_count;
int64_t pcm_pos;
int sample_rate;
int channels;
bool started;
};
class TransitionPlayer : public PlayedInterface
{
public:
TransitionPlayer(Soundtrack* _owner);
virtual ~TransitionPlayer();
void update( HighResBufferSegment* buffer );
void SetTransitionPeriod( float sec );
bool TransitionToSong( const MusicXMLParser::Song& nextSong );
bool SetSong( const MusicXMLParser::Song& nextSong );
bool QueueSegment( const std::string& segment_name );
bool TransitionIntoSegment( const std::string& segment_name );
bool SetSegment( const std::string& segment_name );
const std::string GetSegmentName( ) const;
bool SetLayerGain( const std::string& layer, float gain );
float GetLayerGain( const std::string& layer );
std::vector<std::string> GetLayerNames() const;
const std::map<std::string,float> GetLayerGains();
void SetPCMPos( int64_t c );
int64_t GetPCMPos();
int64_t GetPCMCount();
virtual int SampleRate();
virtual int Channels();
const std::string GetSongName() const;
const std::string GetSongType() const;
const std::string GetNextSongName() const;
const std::string GetNextSongType() const;
private:
std::map<std::string, int64_t> stored_pcm_pos;
bool playing;
TransitionMixer mixer;
rc_PlayedSongInterface currentSong;
std::deque<rc_PlayedSongInterface> songQueue;
};
friend bool operator==( const Soundtrack::PlayedSegmentedSong &lhs, const Soundtrack::PlayedSegmentedSong &rhs );
friend bool operator!=( const Soundtrack::PlayedSegmentedSong &lhs, const Soundtrack::PlayedSegmentedSong &rhs );
std::map<std::string,MusicXMLParser::Music> music;
LimiterAudioFilter limiter;
TransitionPlayer transitionPlayer;
int current_volume_step;
float volume_change_per_second;
float volume_start;
float volume_target;
void PostProcessVolume(HighResBufferSegment* buffer);
public:
void Dispose();
//Audiostreamer API
/// Request to fill a buffer with data
virtual void update(rc_alAudioBuffer buffer);
/// Return the number of buffers this streaming class requires (usually 2)
virtual unsigned long required_buffers();
/// Stop the current stream (reset to empty)
virtual void Stop();
//AudioEmitter API
//
/// Set to false to allow the emitter to time out (useful only on non-looping sounds)
virtual bool KeepPlaying();
/// if true is returned, this will be a relative-to-listener sound
virtual bool GetPosition(vec3 &p);
virtual void GetDirection(vec3 &p);
virtual const vec3& GetVelocity();
virtual const vec3 GetPosition();
virtual const vec3 GetOcclusionPosition();
/// Indicate the priority of this effect to make room for newer/higher priority effects
virtual unsigned char GetPriority();
virtual void SetVolume(float vol);
virtual bool IsTransient();
public: //Control API
Soundtrack( float volume );
virtual ~Soundtrack();
void AddMusic( const Path& file );
void RemoveMusic( const Path& file );
//Set what segment to play after current is finished
bool QueueSegment( const std::string& name );
bool TransitionIntoSegment( const std::string& name );
//Abruptly change segment
bool SetSegment( const std::string& name );
const std::string GetSegment( ) const;
const std::map<std::string,float> GetLayerGains();
const std::vector<std::string> GetLayerNames() const;
const std::string GetSongName() const;
const std::string GetSongType() const;
const std::string GetNextSongName() const;
const std::string GetNextSongType() const;
const MusicXMLParser::Song GetSong(const std::string& name);
//Make a soft transition to a different song.
bool TransitionToSong( const std::string& name );
//Abruptly change song.
bool SetSong( const std::string& name );
void SetLayerGain(const std::string& layer, float v);
};
inline bool operator==( const Soundtrack::PlayedSegmentedSong &lhs, const Soundtrack::PlayedSegmentedSong &rhs )
{
return lhs.song == rhs.song;
}
inline bool operator!=( const Soundtrack::PlayedSegmentedSong &lhs, const Soundtrack::PlayedSegmentedSong &rhs )
{
return !(lhs == rhs);
}
|