1
2 /*
3 * The Real SoundTracker - dsound (output) driver.
4 *
5 * Copyright (C) 1999 Tammo Hinrichs
6 * Copyright (C) 2000 Fabian Giesen
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 */
22
23 #include <config.h>
24
25 #if defined(_WIN32)
26
27 #include <glib/gi18n.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "driver.h"
33 #include "errors.h"
34 #include "gui-subs.h"
35 #include "mixer.h"
36 #include "preferences.h"
37 #include "winpipe.h"
38
39 #include <dsound.h>
40 #include <windows.h>
41 #include <windowsx.h>
42
43 typedef HRESULT(WINAPI* dscfunc)(LPGUID, LPDIRECTSOUND*, LPUNKNOWN);
44
45 typedef struct {
46 GtkWidget* configwidget;
47
48 int out_sock, out_bits, out_channels, out_rate;
49 int samples_per_frame;
50 int mf;
51
52 void* buf;
53 int fakepipe[2];
54 gpointer polltag;
55
56 long buflen;
57 signed long playpos, lastpos;
58
59 HINSTANCE hdsinst;
60 HWND winhwnd;
61 HANDLE thread;
62 dscfunc DSCreate;
63
64 LPDIRECTSOUND lpds;
65 DSCAPS dscaps;
66
67 WAVEFORMATEX pbformat, sbformat;
68 WAVEFORMATEX* wbformat;
69 DSBUFFERDESC pbdesc, sbdesc;
70 LPDIRECTSOUNDBUFFER pbuffer, sbuffer, wbuffer;
71 DSBCAPS pbcaps;
72
73 int locked;
74 void *lockbuf, *lockbuf2;
75 DWORD locklen, locklen2;
76
77 char* tempbuf;
78 int writeprim;
79 int sampshift;
80
81 double outtime;
82 double playtime;
83 } dsound_driver;
84
85 static int thread;
86
87 static int
dsound_get_buffer_pos(void * dp)88 dsound_get_buffer_pos(void* dp)
89 {
90 dsound_driver* d = dp;
91 int pc;
92
93 IDirectSoundBuffer_GetCurrentPosition(d->wbuffer, (DWORD*)&pc, 0);
94 return pc;
95 }
96
97 static void
dsound_poll_ready_playing(gpointer data,gint source,GdkInputCondition condition)98 dsound_poll_ready_playing(gpointer data,
99 gint source,
100 GdkInputCondition condition)
101 {
102 dsound_driver* const d = data;
103 int bufpos, delta, pos, lockok = 0, len1, len2;
104 char bla;
105
106 bufpos = dsound_get_buffer_pos(data);
107 delta = ((bufpos >= d->lastpos) ? 0 : d->buflen) + bufpos - d->lastpos;
108
109 pos = (bufpos + delta) % d->buflen;
110 d->playpos += delta;
111
112 while (!lockok) {
113 switch (IDirectSoundBuffer_Lock(d->wbuffer, d->lastpos, delta, &d->lockbuf, &d->locklen, &d->lockbuf2, &d->locklen2, 0)) {
114 case DS_OK:
115 lockok = 1;
116 break;
117 case DSERR_BUFFERLOST:
118 if
119 FAILED(IDirectSoundBuffer_Restore(d->wbuffer))
120 lockok = 2;
121 break;
122 default:
123 lockok = 2;
124 }
125 }
126
127 if (lockok == 1) {
128 audio_mix(d->lockbuf, d->locklen >> d->sampshift, d->out_rate, d->mf);
129 if (d->locklen2)
130 audio_mix(d->lockbuf2, d->locklen2 >> d->sampshift, d->out_rate, d->mf);
131 }
132
133 IDirectSoundBuffer_Unlock(d->wbuffer, d->lockbuf, d->locklen, d->lockbuf2, d->locklen2);
134 d->lastpos = bufpos;
135 }
136
137 static DWORD WINAPI
dsound_thread(LPVOID dp)138 dsound_thread(LPVOID dp)
139 {
140 dsound_driver* const d = dp;
141
142 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
143
144 while (thread) {
145 dsound_poll_ready_playing(d, 0, 0);
146 Sleep(25); /* sonst zieht das entschieden zuviel rechenzeit */
147 }
148
149 return 0;
150 }
151
152 static void
dsound_make_config_widgets(dsound_driver * d)153 dsound_make_config_widgets(dsound_driver* d)
154 {
155 GtkWidget *thing, *mainbox;
156
157 d->configwidget = mainbox = gtk_vbox_new(FALSE, 2);
158
159 thing = gtk_label_new(_("in beta state. aber ryg rockt."));
160 gtk_box_pack_start(GTK_BOX(mainbox), thing, FALSE, TRUE, 0);
161 gtk_widget_show(thing);
162 }
163
164 static GtkWidget*
dsound_getwidget(void * dp)165 dsound_getwidget(void* dp)
166 {
167 dsound_driver* const d = dp;
168
169 return d->configwidget;
170 }
171
172 static void*
dsound_new(void)173 dsound_new(void)
174 {
175 dsound_driver* d = g_new(dsound_driver, 1);
176
177 d->out_bits = 16;
178 d->out_rate = 44100;
179 d->out_channels = 2;
180 d->pbuffer = 0;
181 d->sbuffer = 0;
182 d->wbuffer = 0;
183 d->writeprim = 0;
184
185 dsound_make_config_widgets(d);
186
187 return d;
188 }
189
190 static void
dsound_destroy(void * dp)191 dsound_destroy(void* dp)
192 {
193 dsound_driver* const d = dp;
194
195 gtk_widget_destroy(d->configwidget);
196 g_free(dp);
197 }
198
199 static void
dsound_release(void * dp)200 dsound_release(void* dp)
201 {
202 dsound_driver* const d = dp;
203
204 thread = 0;
205 WaitForSingleObject(d->thread, 1000);
206
207 if (d->sbuffer) {
208 IDirectSoundBuffer_Stop(d->sbuffer);
209 IDirectSoundBuffer_Release(d->sbuffer);
210 }
211
212 if (d->tempbuf)
213 g_free(d->tempbuf);
214
215 d->sbuffer = 0;
216 d->tempbuf = 0;
217
218 IDirectSound_Release(d->lpds);
219 }
220
221 static gboolean
dsound_play(void * dp)222 dsound_play(void* dp)
223 {
224 dsound_driver* d = dp;
225 int len;
226
227 memset(&d->pbformat, 0, sizeof(WAVEFORMATEX));
228 d->pbformat.wFormatTag = WAVE_FORMAT_PCM;
229 d->pbformat.nChannels = d->out_channels;
230 d->pbformat.nSamplesPerSec = d->out_rate;
231 d->pbformat.wBitsPerSample = d->out_bits;
232 d->pbformat.nBlockAlign = d->pbformat.wBitsPerSample / 8 * d->pbformat.nChannels;
233 d->pbformat.nAvgBytesPerSec = d->pbformat.nSamplesPerSec * d->pbformat.nBlockAlign;
234 if
235 FAILED(IDirectSoundBuffer_SetFormat(d->pbuffer, &d->pbformat))
236 {
237 if (d->writeprim) {
238 IDirectSound_SetCooperativeLevel(d->lpds, d->winhwnd, DSSCL_PRIORITY);
239 d->writeprim = 0;
240 }
241 }
242
243 IDirectSoundBuffer_GetFormat(d->pbuffer, &d->pbformat, sizeof(d->pbformat), 0);
244
245 len = (d->out_bits / 8 * d->out_channels * d->out_rate) / 5; // 200ms buf default
246
247 if (d->writeprim) {
248 d->pbcaps.dwSize = sizeof(d->pbcaps);
249 IDirectSoundBuffer_GetCaps(d->pbuffer, &d->pbcaps);
250 len = d->pbcaps.dwBufferBytes;
251 d->wbuffer = d->pbuffer;
252 d->wbformat = &d->pbformat;
253 } else {
254 len = (len < DSBSIZE_MIN) ? DSBSIZE_MIN : (len > DSBSIZE_MAX) ? DSBSIZE_MAX : len;
255
256 memset(&d->sbformat, 0, sizeof(WAVEFORMATEX));
257 d->sbformat.wFormatTag = WAVE_FORMAT_PCM;
258 d->sbformat.nChannels = d->out_channels;
259 d->sbformat.nSamplesPerSec = d->out_rate;
260 d->sbformat.wBitsPerSample = d->out_bits;
261 d->sbformat.nBlockAlign = d->sbformat.wBitsPerSample / 8 * d->sbformat.nChannels;
262 d->sbformat.nAvgBytesPerSec = d->sbformat.nSamplesPerSec * d->sbformat.nBlockAlign;
263
264 memset(&d->sbdesc, 0, sizeof(DSBUFFERDESC));
265 d->sbdesc.dwSize = sizeof(DSBUFFERDESC);
266 d->sbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_STICKYFOCUS;
267 d->sbdesc.dwBufferBytes = len;
268 d->sbdesc.lpwfxFormat = &d->sbformat;
269
270 if
271 FAILED(IDirectSound_CreateSoundBuffer(d->lpds, &d->sbdesc, &d->sbuffer, 0))
272 return FALSE;
273
274 d->wbuffer = d->sbuffer;
275 d->wbformat = &d->sbformat;
276 }
277
278 d->buflen = len;
279
280 if
281 FAILED(IDirectSoundBuffer_Lock(d->wbuffer, 0, 0, &d->lockbuf, &d->locklen, 0, 0, DSBLOCK_ENTIREBUFFER))
282 {
283 IDirectSoundBuffer_Unlock(d->wbuffer, d->lockbuf, 0, 0, 0);
284
285 if (d->writeprim) {
286 d->writeprim = 0;
287 IDirectSound_SetCooperativeLevel(d->lpds, d->winhwnd, DSSCL_PRIORITY);
288 return dsound_play(dp);
289 } else {
290 IDirectSoundBuffer_Release(d->sbuffer);
291 return FALSE;
292 }
293 }
294 else {
295 memset(d->lockbuf, 0, d->locklen);
296 IDirectSoundBuffer_Unlock(d->wbuffer, d->lockbuf, 0, 0, 0);
297 }
298
299 if
300 FAILED(IDirectSoundBuffer_Play(d->wbuffer, 0, 0, DSBPLAY_LOOPING))
301 {
302 if (d->writeprim) {
303 d->writeprim = 0;
304 IDirectSound_SetCooperativeLevel(d->lpds, d->winhwnd, DSSCL_PRIORITY);
305 return dsound_play(dp);
306 } else {
307 IDirectSoundBuffer_Release(d->sbuffer);
308 return FALSE;
309 }
310 }
311
312 d->tempbuf = g_new(char, len);
313 memset(d->tempbuf, 0, len);
314 d->buf = d->tempbuf;
315
316 d->locked = 0;
317 d->playpos = -d->buflen;
318 d->lastpos = 0;
319
320 return TRUE;
321 }
322
323 static gboolean
dsound_open(void * dp)324 dsound_open(void* dp)
325 {
326 dsound_driver* const d = dp;
327 int out_format;
328 DWORD tid;
329 char bla;
330
331 d->winhwnd = GetForegroundWindow();
332 d->writeprim = 0;
333
334 d->hdsinst = LoadLibrary("dsound.dll");
335 if (!d->hdsinst)
336 return FALSE;
337
338 d->DSCreate = (dscfunc)GetProcAddress(d->hdsinst, "DirectSoundCreate");
339 if (!d->DSCreate)
340 return FALSE;
341
342 if
343 FAILED(d->DSCreate(0, &d->lpds, 0))
344 return FALSE;
345
346 d->dscaps.dwSize = sizeof(DSCAPS);
347 if
348 FAILED(IDirectSound_GetCaps(d->lpds, &d->dscaps))
349 return FALSE;
350
351 if
352 FAILED(IDirectSound_SetCooperativeLevel(d->lpds, d->winhwnd, d->writeprim ? DSSCL_WRITEPRIMARY : DSSCL_PRIORITY))
353 {
354 d->writeprim = 0;
355 if
356 FAILED(IDirectSound_SetCooperativeLevel(d->lpds, d->winhwnd, DSSCL_PRIORITY))
357 return FALSE;
358 }
359
360 memset(&d->pbdesc, 0, sizeof(DSBUFFERDESC));
361 d->pbdesc.dwSize = sizeof(DSBUFFERDESC);
362 d->pbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
363 if
364 FAILED(IDirectSound_CreateSoundBuffer(d->lpds, &d->pbdesc, &d->pbuffer, 0))
365 return FALSE;
366
367 if (!dsound_play(dp))
368 return FALSE;
369
370 d->sampshift = (d->out_bits == 16) ? 1 : 0;
371 d->sampshift += (d->out_channels == 2) ? 1 : 0;
372
373 d->mf = (d->out_bits == 16) ? ST_MIXER_FORMAT_S16_LE : ST_MIXER_FORMAT_U8;
374 d->mf |= (d->out_channels == 2) ? ST_MIXER_FORMAT_STEREO : 0;
375 // d->polltag=audio_poll_add(d->fakepipe[0], GDK_INPUT_READ, dsound_poll_ready_playing, d);
376
377 /* now kick the "polling" (which sucks coz of pipe-faking) */
378
379 thread = 1;
380 d->thread = CreateThread(0, 8 * 1024, dsound_thread, d, 0, &tid);
381
382 return TRUE;
383 }
384
385 static double
dsound_get_play_time(void * dp)386 dsound_get_play_time(void* dp)
387 {
388 dsound_driver* const d = dp;
389
390 return (double)d->playpos / (double)d->wbformat->nAvgBytesPerSec;
391 }
392
393 static inline int
dsound_get_play_rate(void * d)394 dsound_get_play_rate(void* d)
395 {
396 dsound_driver* const dp = d;
397 return dp->out_rate;
398 }
399
400 static gboolean
dsound_loadsettings(void * dp,const gchar * f)401 dsound_loadsettings(void* dp,
402 const gchar* f)
403 {
404 // dsound_driver * const d = dp;
405
406 return TRUE;
407 }
408
409 static gboolean
dsound_savesettings(void * dp,const gchar * f)410 dsound_savesettings(void* dp,
411 const gchar* f)
412 {
413 // dsound_driver * const d = dp;
414
415 return TRUE;
416 }
417
418 st_driver driver_out_dsound = {
419 "DirectSound Output",
420
421 dsound_new,
422 dsound_destroy,
423
424 dsound_open,
425 dsound_release,
426
427 dsound_getwidget,
428 dsound_loadsettings,
429 dsound_savesettings,
430
431 NULL,
432 NULL,
433
434 dsound_get_play_time,
435 dsound_get_play_rate
436 };
437
438 #endif /* defined(_WIN32) */
439