-
Notifications
You must be signed in to change notification settings - Fork 4
/
AudioSystem.cpp
342 lines (313 loc) · 7.73 KB
/
AudioSystem.cpp
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
// Ahmed S. Tolba 2015-2018
#include "AudioSystem.h"
#include <SDL/SDL_log.h>
#include <fmod_studio.hpp>
#include <fmod_errors.h>
#include <vector>
unsigned int AudioSystem::sNextID = 0;
AudioSystem::AudioSystem(Game* game)
:mGame(game)
,mSystem(nullptr)
,mLowLevelSystem(nullptr)
{
}
AudioSystem::~AudioSystem()
{
}
bool AudioSystem::Initialize()
{
// Initialize debug logging
FMOD::Debug_Initialize(
FMOD_DEBUG_LEVEL_ERROR, // Log only errors
FMOD_DEBUG_MODE_TTY // Output to stdout
);
// Create FMOD studio system object
FMOD_RESULT result;
result = FMOD::Studio::System::create(&mSystem);
if (result != FMOD_OK)
{
SDL_Log("Failed to create FMOD system: %s", FMOD_ErrorString(result));
return false;
}
// Initialize FMOD studio system
result = mSystem->initialize(
512, // Max number of concurrent sounds
FMOD_STUDIO_INIT_NORMAL, // Use default settings
FMOD_INIT_NORMAL, // Use default settings
nullptr // Usually null
);
if (result != FMOD_OK)
{
SDL_Log("Failed to initialize FMOD system: %s", FMOD_ErrorString(result));
return false;
}
// Save the low-level system pointer
mSystem->getLowLevelSystem(&mLowLevelSystem);
// Load the master banks (strings first)
LoadBank("Assets/Master Bank.strings.bank");
LoadBank("Assets/Master Bank.bank");
return true;
}
void AudioSystem::Shutdown()
{
// Unload all banks
UnloadAllBanks();
// Shutdown FMOD system
if (mSystem)
{
mSystem->release();
}
}
void AudioSystem::LoadBank(const std::string& name)
{
// Prevent double-loading
if (mBanks.find(name) != mBanks.end())
{
return;
}
// Try to load bank
FMOD::Studio::Bank* bank = nullptr;
FMOD_RESULT result = mSystem->loadBankFile(
name.c_str(), // File name of bank
FMOD_STUDIO_LOAD_BANK_NORMAL, // Normal loading
&bank // Save pointer to bank
);
const int maxPathLength = 512;
if (result == FMOD_OK)
{
// Add bank to map
mBanks.emplace(name, bank);
// Load all non-streaming sample data
bank->loadSampleData();
// Get the number of events in this bank
int numEvents = 0;
bank->getEventCount(&numEvents);
if (numEvents > 0)
{
// Get list of event descriptions in this bank
std::vector<FMOD::Studio::EventDescription*> events(numEvents);
bank->getEventList(events.data(), numEvents, &numEvents);
char eventName[maxPathLength];
for (int i = 0; i < numEvents; i++)
{
FMOD::Studio::EventDescription* e = events[i];
// Get the path of this event (like event:/Explosion2D)
e->getPath(eventName, maxPathLength, nullptr);
// Add to event map
mEvents.emplace(eventName, e);
}
}
// Get the number of buses in this bank
int numBuses = 0;
bank->getBusCount(&numBuses);
if (numBuses > 0)
{
// Get list of buses in this bank
std::vector<FMOD::Studio::Bus*> buses(numBuses);
bank->getBusList(buses.data(), numBuses, &numBuses);
char busName[512];
for (int i = 0; i < numBuses; i++)
{
FMOD::Studio::Bus* bus = buses[i];
// Get the path of this bus (like bus:/SFX)
bus->getPath(busName, 512, nullptr);
// Add to buses map
mBuses.emplace(busName, bus);
}
}
}
}
void AudioSystem::UnloadBank(const std::string& name)
{
// Ignore if not loaded
auto iter = mBanks.find(name);
if (iter == mBanks.end())
{
return;
}
// First we need to remove all events from this bank
FMOD::Studio::Bank* bank = iter->second;
int numEvents = 0;
bank->getEventCount(&numEvents);
if (numEvents > 0)
{
// Get event descriptions for this bank
std::vector<FMOD::Studio::EventDescription*> events(numEvents);
// Get list of events
bank->getEventList(events.data(), numEvents, &numEvents);
char eventName[512];
for (int i = 0; i < numEvents; i++)
{
FMOD::Studio::EventDescription* e = events[i];
// Get the path of this event
e->getPath(eventName, 512, nullptr);
// Remove this event
auto eventi = mEvents.find(eventName);
if (eventi != mEvents.end())
{
mEvents.erase(eventi);
}
}
}
// Get the number of buses in this bank
int numBuses = 0;
bank->getBusCount(&numBuses);
if (numBuses > 0)
{
// Get list of buses in this bank
std::vector<FMOD::Studio::Bus*> buses(numBuses);
bank->getBusList(buses.data(), numBuses, &numBuses);
char busName[512];
for (int i = 0; i < numBuses; i++)
{
FMOD::Studio::Bus* bus = buses[i];
// Get the path of this bus (like bus:/SFX)
bus->getPath(busName, 512, nullptr);
// Remove this bus
auto busi = mBuses.find(busName);
if (busi != mBuses.end())
{
mBuses.erase(busi);
}
}
}
// Unload sample data and bank
bank->unloadSampleData();
bank->unload();
// Remove from banks map
mBanks.erase(iter);
}
void AudioSystem::UnloadAllBanks()
{
for (auto& iter : mBanks)
{
// Unload the sample data, then the bank itself
iter.second->unloadSampleData();
iter.second->unload();
}
mBanks.clear();
// No banks means no events
mEvents.clear();
}
SoundEvent AudioSystem::PlayEvent(const std::string& name)
{
unsigned int retID = 0;
auto iter = mEvents.find(name);
if (iter != mEvents.end())
{
// Create instance of event
FMOD::Studio::EventInstance* event = nullptr;
iter->second->createInstance(&event);
if (event)
{
// Start the event instance
event->start();
// Get the next id, and add to map
sNextID++;
retID = sNextID;
mEventInstances.emplace(retID, event);
}
}
return SoundEvent(this, retID);
}
void AudioSystem::Update(float deltaTime)
{
// Find any stopped event instances
std::vector<unsigned int> done;
for (auto& iter : mEventInstances)
{
FMOD::Studio::EventInstance* e = iter.second;
// Get the state of this event
FMOD_STUDIO_PLAYBACK_STATE state;
e->getPlaybackState(&state);
if (state == FMOD_STUDIO_PLAYBACK_STOPPED)
{
// Release the event and add id to done
e->release();
done.emplace_back(iter.first);
}
}
// Remove done event instances from map
for (auto id : done)
{
mEventInstances.erase(id);
}
// Update FMOD
mSystem->update();
}
namespace
{
FMOD_VECTOR VecToFMOD(const Vector3& in)
{
// Convert from our coordinates (+x forward, +y right, +z up)
// to FMOD (+z forward, +x right, +y up)
FMOD_VECTOR v;
v.x = in.y;
v.y = in.z;
v.z = in.x;
return v;
}
}
void AudioSystem::SetListener(const Matrix4& viewMatrix)
{
// Invert the view matrix to get the correct vectors
Matrix4 invView = viewMatrix;
invView.Invert();
FMOD_3D_ATTRIBUTES listener;
// Set position, forward, up
listener.position = VecToFMOD(invView.GetTranslation());
// In the inverted view, third row is forward
listener.forward = VecToFMOD(invView.GetZAxis());
// In the inverted view, second row is up
listener.up = VecToFMOD(invView.GetYAxis());
// Set velocity to zero (fix if using Doppler effect)
listener.velocity = {0.0f, 0.0f, 0.0f};
// Send to FMOD
mSystem->setListenerAttributes(0, &listener);
}
float AudioSystem::GetBusVolume(const std::string& name) const
{
float retVal = 0.0f;
const auto iter = mBuses.find(name);
if (iter != mBuses.end())
{
iter->second->getVolume(&retVal);
}
return retVal;
}
bool AudioSystem::GetBusPaused(const std::string & name) const
{
bool retVal = false;
const auto iter = mBuses.find(name);
if (iter != mBuses.end())
{
iter->second->getPaused(&retVal);
}
return retVal;
}
void AudioSystem::SetBusVolume(const std::string& name, float volume)
{
auto iter = mBuses.find(name);
if (iter != mBuses.end())
{
iter->second->setVolume(volume);
}
}
void AudioSystem::SetBusPaused(const std::string & name, bool pause)
{
auto iter = mBuses.find(name);
if (iter != mBuses.end())
{
iter->second->setPaused(pause);
}
}
FMOD::Studio::EventInstance* AudioSystem::GetEventInstance(unsigned int id)
{
FMOD::Studio::EventInstance* event = nullptr;
auto iter = mEventInstances.find(id);
if (iter != mEventInstances.end())
{
event = iter->second;
}
return event;
}