1 /* dxsound.c: DirectX sound I/O
2    Copyright (c) 2003-2004 Marek Januszewski, Philip Kendall
3 
4    $Id: dxsound.c 3752 2008-08-20 00:50:02Z specu $
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20    Author contact information:
21 
22    E-mail: philip-fuse@shadowmagic.org.uk
23 
24 */
25 
26 #include <config.h>
27 
28 #include <windows.h>
29 #include <mmsystem.h>
30 #include <dsound.h>
31 
32 #include "settings.h"
33 #include "sound.h"
34 #include "ui/ui.h"
35 
36 /* same as for SDL Sound */
37 #define MAX_AUDIO_BUFFER 8192*5
38 
39 static LPDIRECTSOUND lpDS; /* DirectSound object */
40 static LPDIRECTSOUNDBUFFER lpDSBuffer; /* sound buffer */
41 
42 static DWORD nextpos; /* next position in circular buffer */
43 
44 static int sixteenbit;
45 
46 int
sound_lowlevel_init(const char * device,int * freqptr,int * stereoptr)47 sound_lowlevel_init( const char *device, int *freqptr, int *stereoptr )
48 {
49 
50   WAVEFORMATEX pcmwf; /* waveformat struct */
51   DSBUFFERDESC dsbd; /* buffer description */
52 
53   /* Initialize COM */
54   CoInitialize(NULL);
55 
56   /* create DirectSound object */
57   if( CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
58 			&IID_IDirectSound, (void**)&lpDS ) != DS_OK ) {
59     settings_current.sound = 0;
60     ui_error( UI_ERROR_ERROR, "Couldn't create DirectSound object.");
61     CoUninitialize();
62     return 1;
63   }
64 
65   /* initialize it */
66   if( IDirectSound_Initialize( lpDS, NULL ) != DS_OK ) {
67     settings_current.sound = 0;
68     ui_error( UI_ERROR_ERROR, "Couldn't initialize DirectSound." );
69     CoUninitialize();
70     return 1;
71   }
72 
73   /* set normal cooperative level */
74   if( IDirectSound_SetCooperativeLevel( lpDS, GetDesktopWindow(),
75 					DSSCL_NORMAL ) != DS_OK ) {
76     settings_current.sound = 0;
77     ui_error( UI_ERROR_ERROR, "Couldn't set DirectSound cooperation level." );
78     IDirectSound_Release( lpDS );
79     CoUninitialize();
80     return 1;
81   }
82 
83   /* create wave format description */
84   memset( &pcmwf, 0, sizeof( WAVEFORMATEX ) );
85 
86   pcmwf.cbSize = 0;
87   if( settings_current.sound_force_8bit ) {
88     pcmwf.wBitsPerSample = 8;
89     sixteenbit = 0;
90   } else {
91     pcmwf.wBitsPerSample = 16;
92     sixteenbit = 1;
93   }
94   pcmwf.nChannels = *stereoptr ? 2 : 1;
95   pcmwf.nBlockAlign = pcmwf.nChannels * ( sixteenbit ? 2 : 1 );
96   pcmwf.nSamplesPerSec = *freqptr;
97 
98   pcmwf.nAvgBytesPerSec = pcmwf.nSamplesPerSec * pcmwf.nBlockAlign;
99 
100   pcmwf.wFormatTag = WAVE_FORMAT_PCM;
101 
102   /* create sound buffer description */
103   memset( &dsbd, 0, sizeof( DSBUFFERDESC ) );
104   dsbd.dwBufferBytes = MAX_AUDIO_BUFFER;
105 
106   dsbd.dwFlags = DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLVOLUME |
107                  DSBCAPS_CTRLFREQUENCY | DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE;
108 
109   dsbd.dwSize = sizeof( DSBUFFERDESC );
110   dsbd.lpwfxFormat = &pcmwf;
111 
112   /* attempt to create the buffer */
113   if( IDirectSound_CreateSoundBuffer( lpDS, &dsbd, &lpDSBuffer, NULL )
114       != DS_OK ) {
115     settings_current.sound = 0;
116     ui_error( UI_ERROR_ERROR, "Couldn't create DirectSound buffer." );
117     IDirectSound_Release( lpDS );
118     CoUninitialize();
119     return 1;
120   }
121 
122   /* play buffer */
123   if( IDirectSoundBuffer_Play( lpDSBuffer, 0, 0, DSBPLAY_LOOPING ) != DS_OK ) {
124     settings_current.sound = 0;
125     ui_error( UI_ERROR_ERROR, "Couldn't play sound." );
126     IDirectSoundBuffer_Release( lpDSBuffer );
127     IDirectSound_Release( lpDS );
128     CoUninitialize();
129     return 1;
130   }
131 
132   nextpos = 0;
133 
134   return 0;
135 }
136 
137 void
sound_lowlevel_end(void)138 sound_lowlevel_end( void )
139 {
140   if( IDirectSoundBuffer_Stop( lpDSBuffer ) != DS_OK ) {
141     settings_current.sound = 0;
142     ui_error( UI_ERROR_ERROR, "Couldn't stop sound." );
143   }
144 
145   IDirectSoundBuffer_Release( lpDSBuffer );
146   IDirectSound_Release( lpDS );
147   CoUninitialize();
148 }
149 
150 /* Copying data to the buffer */
151 void
sound_lowlevel_frame(libspectrum_signed_word * data,int len)152 sound_lowlevel_frame( libspectrum_signed_word *data, int len )
153 {
154   HRESULT hres;
155   int i1, i2;
156   int lsb = 1;
157 
158   /* two pair because of circular buffer */
159   UCHAR *ucbuffer1, *ucbuffer2;
160   DWORD length1, length2;
161   DWORD playcursor;
162   long cursordiff;
163 
164   if( sixteenbit )
165     len *= 2;
166 
167   while( len ) {
168     while( 1 ) {
169       IDirectSoundBuffer_GetCurrentPosition( lpDSBuffer, &playcursor, NULL );
170 
171       cursordiff = (long)nextpos - (long)playcursor;
172 
173       if( playcursor > nextpos )
174         cursordiff += MAX_AUDIO_BUFFER;
175 
176       if( cursordiff < len * 6 )
177         break;
178 
179       Sleep(10);
180     }
181 
182     /* lock the buffer */
183     hres = IDirectSoundBuffer_Lock( lpDSBuffer, nextpos, (DWORD)len,
184                                   (void **)&ucbuffer1, &length1,
185                                   (void **)&ucbuffer2, &length2,
186                                   0 );
187     if( hres != DS_OK ) return; /* couldn't get a lock on the buffer */
188 
189     /* write to the first part of buffer */
190     for( i1 = 0; i1 < length1 && len > 0; i1++ ) {
191       if( sixteenbit ) {
192         if( lsb ) {
193           ucbuffer1[ i1 ] = *data & 0xff;
194         } else {
195           ucbuffer1[ i1 ] = *data >> 8;
196           data++;
197         }
198         lsb = !lsb;
199       } else {
200         ucbuffer1[ i1 ] = ( ( *data++ ) >> 8 ) ^ 0x80;
201       }
202       nextpos++;
203       len--;
204     }
205 
206     if( ucbuffer2 ) {
207       /* write to the second part of buffer */
208       for( i2 = 0; i2 < length2 && len > 0; i2++ ) {
209         if( sixteenbit ) {
210           if( lsb ) {
211             ucbuffer2[ i2 ] = *data & 0xff;
212           } else {
213             ucbuffer2[ i2 ] = *data >> 8;
214             data++;
215           }
216           lsb = !lsb;
217         } else {
218           ucbuffer2[ i2 ] = ( ( *data++ ) >> 8 ) ^ 0x80;
219         }
220         nextpos++;
221         len--;
222       }
223     } else {
224       i2 = 0;
225     }
226 
227     if( nextpos >= MAX_AUDIO_BUFFER ) nextpos -= MAX_AUDIO_BUFFER;
228 
229     /* unlock the buffer */
230     IDirectSoundBuffer_Unlock( lpDSBuffer, ucbuffer1, i1, ucbuffer2, i2 );
231   }
232 }
233