1 /**
2 * @file driver_fmod.cpp
3 * FMOD Studio low-level audio plugin. @ingroup dsfmod
4 *
5 * @authors Copyright © 2011-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
6 *
7 * @par License
8 * GPL: http://www.gnu.org/licenses/gpl.html (with exception granted to allow
9 * linking against FMOD Studio)
10 *
11 * <small>This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version. This program is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17 * Public License for more details. You should have received a copy of the GNU
18 * General Public License along with this program; if not, see:
19 * http://www.gnu.org/licenses
20 *
21 * <b>Special Exception to GPLv2:</b>
22 * Linking the Doomsday Audio Plugin for FMOD Studio (audio_fmod) statically or
23 * dynamically with other modules is making a combined work based on
24 * audio_fmod. Thus, the terms and conditions of the GNU General Public License
25 * cover the whole combination. In addition, <i>as a special exception</i>, the
26 * copyright holders of audio_fmod give you permission to combine audio_fmod
27 * with free software programs or libraries that are released under the GNU
28 * LGPL and with code included in the standard release of "FMOD Studio Programmer's
29 * API" under the "FMOD Studio Programmer's API" license (or modified versions of
30 * such code, with unchanged license). You may copy and distribute such a
31 * system following the terms of the GNU GPL for audio_fmod and the licenses of
32 * the other code concerned, provided that you include the source code of that
33 * other code when and as the GNU GPL requires distribution of source code.
34 * (Note that people who make modified versions of audio_fmod are not obligated
35 * to grant this special exception for their modified versions; it is their
36 * choice whether to do so. The GNU General Public License gives permission to
37 * release a modified version without this exception; this exception also makes
38 * it possible to release a modified version which carries forward this
39 * exception.) </small>
40 */
41
42 #include "driver_fmod.h"
43 #include "api_audiod.h"
44 #include "api_audiod_sfx.h"
45 #include <stdio.h>
46 #include <fmod.h>
47 #include <fmod_errors.h>
48 #include <fmod.hpp>
49
50 #include "doomsday.h"
51 #include <de/c_wrapper.h>
52 #include <de/ArrayValue>
53 #include <de/Config>
54 #include <de/DictionaryValue>
55 #include <de/LogBuffer>
56 #include <de/ScriptSystem>
57 #include <de/TextValue>
58
59 FMOD::System *fmodSystem = 0;
60
61 struct Driver
62 {
63 de::String name;
64 FMOD_GUID guid;
65 int systemRate;
66 FMOD_SPEAKERMODE speakerMode;
67 int speakerModeChannels;
68 };
69 static QVector<Driver> fmodDrivers;
70
speakerModeText(FMOD_SPEAKERMODE mode)71 static const char *speakerModeText(FMOD_SPEAKERMODE mode)
72 {
73 switch (mode)
74 {
75 case FMOD_SPEAKERMODE_DEFAULT: return "Default";
76 case FMOD_SPEAKERMODE_RAW: return "Raw";
77 case FMOD_SPEAKERMODE_MONO: return "Mono";
78 case FMOD_SPEAKERMODE_STEREO: return "Stereo";
79 case FMOD_SPEAKERMODE_QUAD: return "Quad";
80 case FMOD_SPEAKERMODE_SURROUND: return "Surround";
81 case FMOD_SPEAKERMODE_5POINT1: return "5.1";
82 case FMOD_SPEAKERMODE_7POINT1: return "7.1";
83 default: break;
84 }
85 return "";
86 }
87
88 /**
89 * Initialize the FMOD Studio low-level sound driver.
90 */
DS_Init(void)91 int DS_Init(void)
92 {
93 using namespace de;
94
95 if (fmodSystem)
96 {
97 return true; // Already initialized.
98 }
99
100 // Create the FMOD audio system.
101 FMOD_RESULT result;
102 if ((result = FMOD::System_Create(&fmodSystem)) != FMOD_OK)
103 {
104 LOGDEV_AUDIO_ERROR("FMOD::System_Create failed (%d) %s") << result << FMOD_ErrorString(result);
105 fmodSystem = 0;
106 return false;
107 }
108
109 // Print the credit required by FMOD license.
110 LOG_AUDIO_NOTE("FMOD by Firelight Technologies Pty Ltd");
111
112 // Check what kind of drivers are available.
113 {
114 int numDrivers = 0;
115 fmodSystem->getNumDrivers(&numDrivers);
116 fmodDrivers.resize(numDrivers);
117 std::unique_ptr<de::ArrayValue> names(new de::ArrayValue);
118 for (int i = 0; i < numDrivers; ++i)
119 {
120 auto &drv = fmodDrivers[i];
121 char nameBuf[512];
122 zap(nameBuf);
123 fmodSystem->getDriverInfo(i, nameBuf, sizeof(nameBuf),
124 &drv.guid, &drv.systemRate,
125 &drv.speakerMode, &drv.speakerModeChannels);
126 drv.name = String::format("%s (%s)", nameBuf, speakerModeText(drv.speakerMode));
127 names->add(TextValue(drv.name));
128
129 LOG_AUDIO_MSG("FMOD driver %i: \"%s\" Rate:%iHz Mode:%s Channels:%i")
130 << i << drv.name
131 << drv.systemRate
132 << speakerModeText(drv.speakerMode)
133 << drv.speakerModeChannels;
134 }
135 ScriptSystem::get()["Audio"]["outputs"].value<DictionaryValue>()
136 .add(new TextValue("fmod"), names.release());
137 }
138
139 // Select the configured driver.
140 {
141 int configuredDriverIndex = Config::get().geti("audio.output", 0);
142 if (configuredDriverIndex < fmodDrivers.size())
143 {
144 result = fmodSystem->setDriver(configuredDriverIndex);
145 if (result != FMOD_OK)
146 {
147 LOG_AUDIO_ERROR("Failed to select FMOD audio driver: %s")
148 << fmodDrivers[configuredDriverIndex].name;
149 }
150 }
151 }
152
153 #if 0
154 #ifdef WIN32
155 {
156 // Figure out the system's configured default speaker mode.
157 FMOD_SPEAKERMODE speakerMode;
158 result = fmodSystem->getDriverCaps(0, 0, 0, &speakerMode);
159 if (result == FMOD_OK)
160 {
161 fmodSystem->setSpeakerMode(speakerMode);
162 }
163 }
164 #endif
165
166 de::String const speakerMode = de::Config::get().gets("audio.fmod.speakerMode", "");
167 if (speakerMode == "5.1")
168 {
169 fmodSystem->setSpeakerMode(FMOD_SPEAKERMODE_5POINT1);
170 }
171 else if (speakerMode == "7.1")
172 {
173 fmodSystem->setSpeakerMode(FMOD_SPEAKERMODE_7POINT1);
174 }
175 else if (speakerMode == "prologic")
176 {
177 fmodSystem->setSpeakerMode(FMOD_SPEAKERMODE_SRS5_1_MATRIX);
178 }
179
180 // Manual overrides.
181 if (CommandLine_Exists("-speaker51"))
182 {
183 fmodSystem->setSpeakerMode(FMOD_SPEAKERMODE_5POINT1);
184 }
185 if (CommandLine_Exists("-speaker71"))
186 {
187 fmodSystem->setSpeakerMode(FMOD_SPEAKERMODE_7POINT1);
188 }
189 if (CommandLine_Exists("-speakerprologic"))
190 {
191 fmodSystem->setSpeakerMode(FMOD_SPEAKERMODE_SRS5_1_MATRIX);
192 }
193 #endif
194
195 // Initialize FMOD.
196 if ((result = fmodSystem->init(
197 50, FMOD_INIT_NORMAL | FMOD_INIT_3D_RIGHTHANDED | FMOD_INIT_CHANNEL_LOWPASS, 0)) !=
198 FMOD_OK)
199 {
200 LOGDEV_AUDIO_ERROR("FMOD init failed: (%d) %s") << result << FMOD_ErrorString(result);
201 fmodSystem->release();
202 fmodSystem = 0;
203 return false;
204 }
205
206 // Options.
207 FMOD_ADVANCEDSETTINGS settings;
208 de::zap(settings);
209 settings.cbSize = sizeof(settings);
210 settings.HRTFMaxAngle = 360;
211 settings.HRTFMinAngle = 180;
212 settings.HRTFFreq = 11000;
213 fmodSystem->setAdvancedSettings(&settings);
214
215 #ifdef _DEBUG
216 int numPlugins = 0;
217 fmodSystem->getNumPlugins(FMOD_PLUGINTYPE_CODEC, &numPlugins);
218 DSFMOD_TRACE("Plugins loaded: " << numPlugins);
219 for (int i = 0; i < numPlugins; i++)
220 {
221 unsigned int handle;
222 fmodSystem->getPluginHandle(FMOD_PLUGINTYPE_CODEC, i, &handle);
223
224 FMOD_PLUGINTYPE pType;
225 char pName[100];
226 unsigned int pVer = 0;
227 fmodSystem->getPluginInfo(handle, &pType, pName, sizeof(pName), &pVer);
228
229 DSFMOD_TRACE("Plugin " << i << ", handle " << handle << ": type " << pType
230 << ", name:'" << pName << "', ver:" << pVer);
231 }
232 #endif
233
234 LOGDEV_AUDIO_VERBOSE("[FMOD] Initialized");
235 return true;
236 }
237
238 /**
239 * Shut everything down.
240 */
DS_Shutdown(void)241 void DS_Shutdown(void)
242 {
243 DMFmod_Music_Shutdown();
244 //DMFmod_CDAudio_Shutdown();
245
246 DSFMOD_TRACE("DS_Shutdown.");
247 fmodSystem->release();
248 fmodSystem = 0;
249 }
250
251 /**
252 * The Event function is called to tell the driver about certain critical
253 * events like the beginning and end of an update cycle.
254 */
DS_Event(int type)255 void DS_Event(int type)
256 {
257 if (!fmodSystem) return;
258
259 if (type == SFXEV_END)
260 {
261 // End of frame, do an update.
262 fmodSystem->update();
263 }
264 }
265
DS_Set(int prop,const void * ptr)266 int DS_Set(int prop, const void* ptr)
267 {
268 if (!fmodSystem) return false;
269
270 switch (prop)
271 {
272 case AUDIOP_SOUNDFONT_FILENAME: {
273 const char* path = reinterpret_cast<const char*>(ptr);
274 DSFMOD_TRACE("DS_Set: Soundfont = " << path);
275 if (!path || !strlen(path))
276 {
277 // Use the default.
278 path = 0;
279 }
280 DMFmod_Music_SetSoundFont(path);
281 return true; }
282
283 default:
284 DSFMOD_TRACE("DS_Set: Unknown property " << prop);
285 return false;
286 }
287 }
288
289 /**
290 * Declares the type of the plugin so the engine knows how to treat it. Called
291 * automatically when the plugin is loaded.
292 */
deng_LibraryType(void)293 DENG_ENTRYPOINT const char* deng_LibraryType(void)
294 {
295 return "deng-plugin/audio";
296 }
297
298 #if defined (DENG_STATIC_LINK)
299
staticlib_audio_fmod_symbol(char const * name)300 DENG_EXTERN_C void *staticlib_audio_fmod_symbol(char const *name)
301 {
302 DENG_SYMBOL_PTR(name, deng_LibraryType)
303 DENG_SYMBOL_PTR(name, DS_Init)
304 DENG_SYMBOL_PTR(name, DS_Shutdown)
305 DENG_SYMBOL_PTR(name, DS_Event)
306 DENG_SYMBOL_PTR(name, DS_Set)
307 DENG_SYMBOL_PTR(name, DS_SFX_Init)
308 DENG_SYMBOL_PTR(name, DS_SFX_CreateBuffer)
309 DENG_SYMBOL_PTR(name, DS_SFX_DestroyBuffer)
310 DENG_SYMBOL_PTR(name, DS_SFX_Load)
311 DENG_SYMBOL_PTR(name, DS_SFX_Reset)
312 DENG_SYMBOL_PTR(name, DS_SFX_Play)
313 DENG_SYMBOL_PTR(name, DS_SFX_Stop)
314 DENG_SYMBOL_PTR(name, DS_SFX_Refresh)
315 DENG_SYMBOL_PTR(name, DS_SFX_Set)
316 DENG_SYMBOL_PTR(name, DS_SFX_Setv)
317 DENG_SYMBOL_PTR(name, DS_SFX_Listener)
318 DENG_SYMBOL_PTR(name, DS_SFX_Listenerv)
319 DENG_SYMBOL_PTR(name, DS_SFX_Getv)
320 DENG_SYMBOL_PTR(name, DM_Music_Init)
321 DENG_SYMBOL_PTR(name, DM_Music_Update)
322 DENG_SYMBOL_PTR(name, DM_Music_Get)
323 DENG_SYMBOL_PTR(name, DM_Music_Set)
324 DENG_SYMBOL_PTR(name, DM_Music_Pause)
325 DENG_SYMBOL_PTR(name, DM_Music_Stop)
326 DENG_SYMBOL_PTR(name, DM_Music_SongBuffer)
327 DENG_SYMBOL_PTR(name, DM_Music_Play)
328 DENG_SYMBOL_PTR(name, DM_Music_PlayFile)
329 qWarning() << name << "not found in audio_fmod";
330 return nullptr;
331 }
332
333 #else
334
335 DENG_DECLARE_API(Con);
336
337 DENG_API_EXCHANGE(
338 DENG_GET_API(DE_API_CONSOLE, Con);
339 )
340
341 #endif
342