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