1 /* FluidSynth - A Software Synthesizer
2 *
3 * Copyright (C) 2003 Peter Hanappe and others.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public License
7 * as published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA
19 */
20
21
22 #define INITGUID
23
24 #include "fluidsynth_priv.h"
25 #include "fluid_synth.h"
26 #include "fluid_sys.h"
27 #include "fluid_adriver.h"
28 #include "fluid_settings.h"
29 #include <mmsystem.h>
30 #include <dsound.h>
31
32 fluid_audio_driver_t*
33 new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth);
34
35 int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* data);
36 DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter);
37
38 HWND fluid_win32_get_window(void);
39 char* fluid_win32_error(HRESULT hr);
40
41
42 #define FLUID_HINSTANCE ((HINSTANCE)fluid_get_hinstance())
43
44 typedef struct {
45 fluid_audio_driver_t driver;
46 LPDIRECTSOUND direct_sound;
47 LPDIRECTSOUNDBUFFER prim_buffer;
48 LPDIRECTSOUNDBUFFER sec_buffer;
49 WAVEFORMATEX* format;
50 HANDLE thread;
51 DWORD threadID;
52 fluid_synth_t* synth;
53 fluid_audio_callback_t write;
54 int cont;
55 DWORD buffer_byte_size;
56 DWORD queue_byte_size;
57 DWORD frame_size;
58 } fluid_dsound_audio_driver_t;
59
60 typedef struct {
61 LPGUID devGUID;
62 char* devname;
63 } fluid_dsound_devsel_t;
64
65 BOOL CALLBACK
fluid_dsound_enum_callback(LPGUID guid,LPCTSTR description,LPCTSTR module,LPVOID context)66 fluid_dsound_enum_callback(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context)
67 {
68 fluid_settings_t* settings = (fluid_settings_t*) context;
69 fluid_settings_add_option(settings, "audio.dsound.device", (const char *)description);
70
71 return TRUE;
72 }
73
74 BOOL CALLBACK
fluid_dsound_enum_callback2(LPGUID guid,LPCTSTR description,LPCTSTR module,LPVOID context)75 fluid_dsound_enum_callback2(LPGUID guid, LPCTSTR description, LPCTSTR module, LPVOID context)
76 {
77 fluid_dsound_devsel_t* devsel = (fluid_dsound_devsel_t*) context;
78 FLUID_LOG(FLUID_DBG, "Testing audio device: %s", description);
79 if (strcasecmp(devsel->devname, description) == 0) {
80 devsel->devGUID = FLUID_NEW(GUID);
81 if(devsel->devGUID) {
82 memcpy(devsel->devGUID, guid, sizeof(GUID));
83 FLUID_LOG(FLUID_DBG, "Selected audio device GUID: %p", devsel->devGUID);
84 }
85 }
86 return TRUE;
87 }
88
fluid_dsound_audio_driver_settings(fluid_settings_t * settings)89 void fluid_dsound_audio_driver_settings(fluid_settings_t* settings)
90 {
91 fluid_settings_register_str(settings, "audio.dsound.device", "default", 0, NULL, NULL);
92 fluid_settings_add_option(settings, "audio.dsound.device", "default");
93 DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback, settings);
94 }
95
96
97 /*
98 * new_fluid_dsound_audio_driver
99 */
100 fluid_audio_driver_t*
new_fluid_dsound_audio_driver(fluid_settings_t * settings,fluid_synth_t * synth)101 new_fluid_dsound_audio_driver(fluid_settings_t* settings, fluid_synth_t* synth)
102 {
103 HRESULT hr;
104 DSBUFFERDESC desc;
105 fluid_dsound_audio_driver_t* dev = NULL;
106 DSCAPS caps;
107 char *buf1;
108 DWORD bytes1;
109 double sample_rate;
110 int periods, period_size;
111 fluid_dsound_devsel_t devsel;
112
113 /* check if the globals are initialized */
114 if (FLUID_HINSTANCE == NULL) {
115 FLUID_LOG(FLUID_ERR, "FluidSynth hinstance not set, which is needed for DirectSound");
116 return NULL;
117 }
118
119 /*
120 if (fluid_wnd == NULL) {
121 if (fluid_win32_create_window() != 0) {
122 FLUID_LOG(FLUID_ERR, "Couldn't create window needed for DirectSound");
123 return NULL;
124 }
125 }
126 */
127 /* create and clear the driver data */
128 dev = FLUID_NEW(fluid_dsound_audio_driver_t);
129 if (dev == NULL) {
130 FLUID_LOG(FLUID_ERR, "Out of memory");
131 return NULL;
132 }
133 FLUID_MEMSET(dev, 0, sizeof(fluid_dsound_audio_driver_t));
134
135 dev->synth = synth;
136 dev->cont = 1;
137
138 fluid_settings_getnum(settings, "synth.sample-rate", &sample_rate);
139 fluid_settings_getint(settings, "audio.periods", &periods);
140 fluid_settings_getint(settings, "audio.period-size", &period_size);
141
142 /* check the format */
143 if (!fluid_settings_str_equal(settings, "audio.sample-format", "16bits")) {
144 FLUID_LOG(FLUID_ERR, "Unhandled sample format");
145 goto error_recovery;
146 }
147
148 dev->frame_size = 2 * sizeof(short);
149 dev->buffer_byte_size = period_size * dev->frame_size;
150 dev->queue_byte_size = periods * dev->buffer_byte_size;
151 dev->write = fluid_synth_write_s16;
152
153 /* create and initialize the buffer format */
154 dev->format = (WAVEFORMATEX*) FLUID_MALLOC(sizeof(WAVEFORMATEX));
155 if (dev->format == NULL) {
156 FLUID_LOG(FLUID_ERR, "Out of memory");
157 goto error_recovery;
158 }
159 ZeroMemory(dev->format, sizeof(WAVEFORMATEX));
160
161 dev->format->wFormatTag = WAVE_FORMAT_PCM;
162 dev->format->nChannels = 2;
163 dev->format->wBitsPerSample = 16;
164 dev->format->nSamplesPerSec = (DWORD) sample_rate;
165 dev->format->nBlockAlign = (WORD) dev->frame_size;
166 dev->format->nAvgBytesPerSec = dev->format->nSamplesPerSec * dev->frame_size;
167 dev->format->cbSize = 0;
168
169 devsel.devGUID = NULL;
170 /* get the selected device name. if none is specified, use NULL for the default device. */
171 if(fluid_settings_dupstr(settings, "audio.dsound.device", &devsel.devname) /* ++ alloc device name */
172 && devsel.devname && strlen (devsel.devname) > 0) {
173 /* look for the GUID of the selected device */
174 DirectSoundEnumerate((LPDSENUMCALLBACK) fluid_dsound_enum_callback2, (void *)&devsel);
175 }
176
177 if (devsel.devname) FLUID_FREE (devsel.devname); /* -- free device name */
178
179 /* open DirectSound */
180 hr = DirectSoundCreate(devsel.devGUID, &dev->direct_sound, NULL);
181 if (hr != DS_OK) {
182 FLUID_LOG(FLUID_ERR, "Failed to create the DirectSound object");
183 goto error_recovery;
184 }
185
186 hr = IDirectSound_SetCooperativeLevel(dev->direct_sound, fluid_win32_get_window(), DSSCL_PRIORITY);
187 if (hr != DS_OK) {
188 FLUID_LOG(FLUID_ERR, "Failed to set the cooperative level");
189 goto error_recovery;
190 }
191
192 caps.dwSize = sizeof(caps);
193 hr = IDirectSound_GetCaps(dev->direct_sound, &caps);
194 if (hr != DS_OK) {
195 FLUID_LOG(FLUID_ERR, "Failed to query the device capacities");
196 goto error_recovery;
197 }
198
199 /* create primary buffer */
200
201 ZeroMemory(&desc, sizeof(DSBUFFERDESC));
202 desc.dwSize = sizeof(DSBUFFERDESC);
203 desc.dwFlags = DSBCAPS_PRIMARYBUFFER;
204
205 if (caps.dwFreeHwMixingStreamingBuffers > 0) {
206 desc.dwFlags |= DSBCAPS_LOCHARDWARE;
207 }
208
209 hr = IDirectSound_CreateSoundBuffer(dev->direct_sound, &desc, &dev->prim_buffer, NULL);
210 if (hr != DS_OK) {
211 FLUID_LOG(FLUID_ERR, "Failed to allocate the primary buffer");
212 goto error_recovery;
213 }
214
215 /* set the primary sound buffer to this format. if it fails, just
216 print a warning. */
217 hr = IDirectSoundBuffer_SetFormat(dev->prim_buffer, dev->format);
218 if (hr != DS_OK) {
219 FLUID_LOG(FLUID_WARN, "Can't set format of primary sound buffer", fluid_win32_error(hr));
220 }
221
222 /* initialize the buffer description */
223
224 ZeroMemory(&desc, sizeof(DSBUFFERDESC));
225 desc.dwSize = sizeof(DSBUFFERDESC);
226 desc.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2;
227 desc.lpwfxFormat = dev->format;
228 desc.dwBufferBytes = dev->queue_byte_size;
229 desc.dwReserved = 0;
230
231 if (caps.dwFreeHwMixingStreamingBuffers > 0) {
232 desc.dwFlags |= DSBCAPS_LOCHARDWARE;
233 }
234
235 /* create the secondary sound buffer */
236
237 hr = IDirectSound_CreateSoundBuffer(dev->direct_sound, &desc, &dev->sec_buffer, NULL);
238 if (hr != DS_OK) {
239 FLUID_LOG(FLUID_ERR, "dsound: Can't create sound buffer: %s", fluid_win32_error(hr));
240 goto error_recovery;
241 }
242
243
244 /* Lock */
245 hr = IDirectSoundBuffer_Lock(dev->sec_buffer, 0, 0, (void*) &buf1, &bytes1, 0, 0, DSBLOCK_ENTIREBUFFER);
246
247 if ((hr != DS_OK) || (buf1 == NULL)) {
248 FLUID_LOG(FLUID_PANIC, "Failed to lock the audio buffer. Exiting.");
249 goto error_recovery;
250 }
251
252 /* fill the buffer with silence */
253 memset(buf1, 0, bytes1);
254
255 /* Unlock */
256 IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, 0, 0);
257
258
259 /* start the audio thread */
260 dev->thread = CreateThread(NULL, 0, &fluid_dsound_audio_run, (LPVOID) dev, 0, &dev->threadID);
261 if (dev->thread == NULL) {
262 goto error_recovery;
263 }
264
265 return (fluid_audio_driver_t*) dev;
266
267 error_recovery:
268 delete_fluid_dsound_audio_driver((fluid_audio_driver_t*) dev);
269 return NULL;
270 }
271
272
delete_fluid_dsound_audio_driver(fluid_audio_driver_t * d)273 int delete_fluid_dsound_audio_driver(fluid_audio_driver_t* d)
274 {
275 fluid_dsound_audio_driver_t* dev = (fluid_dsound_audio_driver_t*) d;
276
277 if (dev == NULL) {
278 return FLUID_OK;
279 }
280
281 /* tell the audio thread to stop its loop */
282 dev->cont = 0;
283
284 /* wait till the audio thread exits */
285 if (dev->thread != 0) {
286 if (WaitForSingleObject(dev->thread, 2000) != WAIT_OBJECT_0) {
287 /* on error kill the thread mercilessly */
288 FLUID_LOG(FLUID_DBG, "Couldn't join the audio thread. killing it.");
289 TerminateThread(dev->thread, 0);
290 }
291 }
292
293 /* release all the allocated ressources */
294
295 if (dev->format != NULL) {
296 FLUID_FREE(dev->format);
297 }
298
299 if (dev->sec_buffer != NULL) {
300 IDirectSoundBuffer_Stop(dev->sec_buffer);
301 IDirectSoundBuffer_Release(dev->sec_buffer);
302 }
303 if (dev->prim_buffer != NULL) {
304 IDirectSoundBuffer_Release(dev->prim_buffer);
305 }
306 if (dev->direct_sound != NULL) {
307 IDirectSound_Release(dev->direct_sound);
308 }
309
310 FLUID_FREE(dev);
311
312 // fluid_win32_destroy_window();
313
314 return 0;
315 }
316
fluid_dsound_audio_run(LPVOID lpParameter)317 DWORD WINAPI fluid_dsound_audio_run(LPVOID lpParameter)
318 {
319 fluid_dsound_audio_driver_t* dev = (fluid_dsound_audio_driver_t*) lpParameter;
320 short *buf1, *buf2;
321 DWORD bytes1, bytes2;
322 DWORD cur_position, frames, play_position, write_position, bytes;
323 HRESULT res;
324
325 cur_position = 0;
326
327 /* boost the priority of the audio thread */
328 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
329
330 IDirectSoundBuffer_Play(dev->sec_buffer, 0, 0, DSBPLAY_LOOPING);
331
332 while (dev->cont) {
333
334 IDirectSoundBuffer_GetCurrentPosition(dev->sec_buffer, &play_position, &write_position);
335
336 if (cur_position <= play_position) {
337 bytes = play_position - cur_position;
338 } else if ((play_position < cur_position) && (write_position <= cur_position)) {
339 bytes = dev->queue_byte_size + play_position - cur_position;
340 } else {
341 bytes = 0;
342 }
343
344 if (bytes >= dev->buffer_byte_size) {
345
346 /* Lock */
347 res = IDirectSoundBuffer_Lock(dev->sec_buffer, cur_position, bytes, (void*) &buf1, &bytes1, (void*) &buf2, &bytes2, 0);
348
349 if ((res != DS_OK) || (buf1 == NULL)) {
350 FLUID_LOG(FLUID_PANIC, "Failed to lock the audio buffer. System lockup might follow. Exiting.");
351 ExitProcess(0);
352 }
353
354 /* fill the first part of the buffer */
355 if (bytes1 > 0) {
356 frames = bytes1 / dev->frame_size;
357 dev->write(dev->synth, frames, buf1, 0, 2, buf1, 1, 2);
358 cur_position += frames * dev->frame_size;
359 }
360
361 /* fill the second part of the buffer */
362 if ((buf2 != NULL) && (bytes2 > 0)) {
363 frames = bytes2 / dev->frame_size;
364 dev->write(dev->synth, frames, buf2, 0, 2, buf2, 1, 2);
365 cur_position += frames * dev->frame_size;
366 }
367
368 /* Unlock */
369 IDirectSoundBuffer_Unlock(dev->sec_buffer, buf1, bytes1, buf2, bytes2);
370
371 if (cur_position >= dev->queue_byte_size) {
372 cur_position -= dev->queue_byte_size;
373 }
374
375 } else {
376 Sleep(1);
377 }
378 }
379
380 ExitThread(0);
381 return 0; /* never reached */
382 }
383
384
fluid_win32_error(HRESULT hr)385 char* fluid_win32_error(HRESULT hr) {
386 char *s = "Don't know why";
387 switch (hr) {
388 case E_NOINTERFACE: s = "No such interface"; break;
389 case DSERR_GENERIC: s = "Generic error"; break;
390 case DSERR_ALLOCATED: s = "Required resources already allocated"; break;
391 case DSERR_BADFORMAT: s = "The format is not supported"; break;
392 case DSERR_INVALIDPARAM: s = "Invalid parameter"; break;
393 case DSERR_NOAGGREGATION: s = "No aggregation"; break;
394 case DSERR_OUTOFMEMORY: s = "Out of memory"; break;
395 case DSERR_UNINITIALIZED: s = "Uninitialized"; break;
396 case DSERR_UNSUPPORTED: s = "Function not supported"; break;
397 }
398 return s;
399 }
400