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