All MCI functions are now cleanly separated.
[wine/hacks.git] / dlls / winedos / soundblaster.c
blob72e8fa6d9f36a8f75bd38a7c0a9c674de8367a7e
1 /*
2 * Soundblaster Emulation
4 * Copyright 2002 Christian Costa
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "config.h"
23 #include "windef.h"
24 #include "dosexe.h"
25 #include "wine/debug.h"
26 #include "dsound.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(sblaster);
30 /* Board Configuration */
31 /* FIXME: Should be in a config file */
32 #define SB_IRQ 5
33 #define SB_IRQ_PRI 11
34 #define SB_DMA 1
36 /* Soundblaster state */
37 static int SampleMode; /* Mono / Stereo */
38 static int SampleRate;
39 static int SamplesCount;
40 static BYTE DSP_Command[256]; /* Store param numbers in bytes for each command */
41 static BYTE DSP_InBuffer[10]; /* Store DSP command bytes parameters from host */
42 static int InSize; /* Nb of bytes in InBuffer */
43 static BYTE DSP_OutBuffer[10]; /* Store DSP information bytes to host */
44 static int OutSize; /* Nb of bytes in InBuffer */
45 static int command; /* Current command */
46 static int end_sound_loop = 0;
47 static int dma_enable = 0;
49 /* The maximum size of a dma transfer can be 65536 */
50 #define DMATRFSIZE 1024
52 /* DMA can perform 8 or 16-bit transfer */
53 static BYTE dma_buffer[DMATRFSIZE*2];
55 /* Direct Sound buffer config */
56 #define DSBUFLEN 4096 /* FIXME: Only this value seems to work */
58 /* Direct Sound playback stuff */
59 static HMODULE hmodule;
60 typedef HRESULT (WINAPI* fnDirectSoundCreate) (LPGUID,LPDIRECTSOUND*,LPUNKNOWN);
61 fnDirectSoundCreate lpDirectSoundCreate;
62 static LPDIRECTSOUND lpdsound;
63 static LPDIRECTSOUNDBUFFER lpdsbuf;
64 static DSBUFFERDESC buf_desc;
65 static WAVEFORMATEX wav_fmt;
66 static HANDLE SB_Thread;
67 static UINT buf_off;
68 extern HWND vga_hwnd;
70 /* SB_Poll performs DMA transfers and fills the Direct Sound Buffer */
71 static DWORD CALLBACK SB_Poll( void *dummy )
73 HRESULT result;
74 LPBYTE lpbuf1 = NULL;
75 LPBYTE lpbuf2 = NULL;
76 DWORD dwsize1 = 0;
77 DWORD dwsize2 = 0;
78 DWORD dwbyteswritten1 = 0;
79 DWORD dwbyteswritten2 = 0;
80 int size;
82 /* FIXME: this loop must be improved */
83 while(!end_sound_loop)
85 Sleep(10);
87 if (dma_enable) {
88 size = DMA_Transfer(SB_DMA,min(DMATRFSIZE,SamplesCount),dma_buffer);
89 } else
90 continue;
92 result = IDirectSoundBuffer_Lock(lpdsbuf,buf_off,size,&lpbuf1,&dwsize1,&lpbuf2,&dwsize2,0);
93 if (result != DS_OK) {
94 ERR("Unable to lock sound buffer !\n");
95 continue;
98 dwbyteswritten1 = min(size,dwsize1);
99 memcpy(lpbuf1,dma_buffer,dwbyteswritten1);
100 if (size>dwsize1) {
101 dwbyteswritten2 = min(size - dwbyteswritten1,dwsize2);
102 memcpy(lpbuf2,dma_buffer+dwbyteswritten1,dwbyteswritten2);
104 buf_off = (buf_off + dwbyteswritten1 + dwbyteswritten2) % DSBUFLEN;
106 result = IDirectSoundBuffer_Unlock(lpdsbuf,lpbuf1,dwbyteswritten1,lpbuf2,dwbyteswritten2);
107 if (result!=DS_OK)
108 ERR("Unable to unlock sound buffer !\n");
110 SamplesCount -= size;
111 if (!SamplesCount) {
112 DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
113 dma_enable = 0;
116 return 0;
119 BOOL SB_Init()
121 HRESULT result;
123 if (!lpdsound) {
124 hmodule = LoadLibraryA("dsound.dll");
125 if (!hmodule) {
126 ERR("Can't load dsound.dll !\n");
127 return 0;
129 lpDirectSoundCreate = (fnDirectSoundCreate)GetProcAddress(hmodule,"DirectSoundCreate");
130 if (!lpDirectSoundCreate) {
131 /* CloseHandle(hmodule); */
132 ERR("Can't find DirectSoundCreate function !\n");
133 return 0;
135 result = (*lpDirectSoundCreate)(NULL,&lpdsound,NULL);
136 if (result != DS_OK) {
137 ERR("Unable to initialize Sound Subsystem err = %lx !\n",result);
138 return 0;
141 /* FIXME: To uncomment when :
142 - SetCooperative level is correctly implemented
143 - an always valid and non changing handle to a windows (vga_hwnd) is available
144 (this surely needs some work in vga.c)
145 result = IDirectSound_SetCooperativeLevel(lpdsound,vga_hwnd,DSSCL_EXCLUSIVE|DSSCL_PRIORITY);
146 if (result != DS_OK) {
147 ERR("Can't set cooperative level !\n");
148 return 0;
152 /* Default format */
153 wav_fmt.wFormatTag = WAVE_FORMAT_PCM;
154 wav_fmt.nChannels = 1;
155 wav_fmt.nSamplesPerSec = 22050;
156 wav_fmt.nAvgBytesPerSec = 22050;
157 wav_fmt.nBlockAlign = 1;
158 wav_fmt.wBitsPerSample = 8;
159 wav_fmt.cbSize = 0;
161 memset(&buf_desc,0,sizeof(DSBUFFERDESC));
162 buf_desc.dwSize = sizeof(DSBUFFERDESC);
163 buf_desc.dwBufferBytes = DSBUFLEN;
164 buf_desc.lpwfxFormat = &wav_fmt;
165 result = IDirectSound_CreateSoundBuffer(lpdsound,&buf_desc,&lpdsbuf,NULL);
166 if (result != DS_OK) {
167 ERR("Can't create sound buffer !\n");
168 return 0;
171 result = IDirectSoundBuffer_Play(lpdsbuf,0, 0, DSBPLAY_LOOPING);
172 if (result != DS_OK) {
173 ERR("Can't start playing !\n");
174 return 0;
177 buf_off = 0;
178 end_sound_loop = 0;
179 SB_Thread = CreateThread(NULL, 0, SB_Poll, NULL, 0, NULL);
180 TRACE("thread\n");
181 if (!SB_Thread) {
182 ERR("Can't create thread !\n");
183 return 0;
186 return 1;
189 void SB_Reset()
191 int i;
193 for(i=0;i<256;i++)
194 DSP_Command[i]=0;
196 /* Set Time Constant */
197 DSP_Command[0x40]=1;
198 /* Generate IRQ */
199 DSP_Command[0xF2]=0;
200 /* DMA DAC 8-bits */
201 DSP_Command[0x14]=2;
202 /* Generic DAC/ADC DMA (16-bit, 8-bit) */
203 for(i=0xB0;i<=0xCF;i++)
204 DSP_Command[i]=3;
205 /* DSP Indentification */
206 DSP_Command[0xE0]=1;
208 /* Clear command and input buffer */
209 command = -1;
210 InSize = 0;
212 /* Put a garbage value in the output buffer */
213 OutSize = 1;
214 if (SB_Init())
215 /* All right, let's put the magic value for autodetection */
216 DSP_OutBuffer[0] = 0xaa;
217 else
218 /* Something is wrong, put 0 to failed audetection */
219 DSP_OutBuffer[0] = 0x00;
222 /* Find a standard sampling rate for DirectSound */
223 int SB_StdSampleRate(int SampleRate)
225 if (SampleRate>((44100+48000)/2)) return 48000;
226 if (SampleRate>((32000+44100)/2)) return 44100;
227 if (SampleRate>((24000+32000)/2)) return 32000;
228 if (SampleRate>((22050+24000)/2)) return 24000;
229 if (SampleRate>((16000+22050)/2)) return 22050;
230 if (SampleRate>((12000+16000)/2)) return 16000;
231 if (SampleRate>((11025+12000)/2)) return 12000;
232 if (SampleRate>((8000+11025)/2)) return 11025;
233 return 8000;
236 void SB_ioport_out( WORD port, BYTE val )
238 switch(port)
240 /* DSP - Reset */
241 case 0x226:
242 TRACE("Resetting DSP.\n");
243 SB_Reset();
244 break;
245 /* DSP - Write Data or Command */
246 case 0x22c:
247 TRACE("val=%x\n",val);
248 if (command == -1) {
249 /* Clear input buffer and set the current command */
250 command = val;
251 InSize = 0;
253 if (InSize!=DSP_Command[command])
254 /* Fill the input buffer the command parameters if any */
255 DSP_InBuffer[InSize++]=val;
256 else {
257 /* Process command */
258 switch(command)
260 case 0x10: /* SB */
261 FIXME("Direct DAC (8-bit) - Not Implemented\n");
262 break;
263 case 0x14: /* SB */
264 SamplesCount = DSP_InBuffer[1]+(val<<8)+1;
265 TRACE("DMA DAC (8-bit) for %x samples\n",SamplesCount);
266 dma_enable = 1;
267 break;
268 case 0x20:
269 FIXME("Direct ADC (8-bit) - Not Implemented\n");
270 break;
271 case 0x24: /* SB */
272 FIXME("DMA ADC (8-bit) - Not Implemented\n");
273 break;
274 case 0x40: /* SB */
275 SampleRate = 1000000/(256-val);
276 TRACE("Set Time Constant (%d <-> %d Hz => %d Hz)\n",DSP_InBuffer[0],
277 SampleRate,SB_StdSampleRate(SampleRate));
278 SampleRate = SB_StdSampleRate(SampleRate);
279 wav_fmt.nSamplesPerSec = SampleRate;
280 wav_fmt.nAvgBytesPerSec = SampleRate;
281 IDirectSoundBuffer_SetFormat(lpdsbuf,&wav_fmt);
282 break;
283 /* case 0xBX/0xCX -> See below */
284 case 0xD0: /* SB */
285 TRACE("Halt DMA operation (8-bit)\n");
286 dma_enable = 0;
287 break;
288 case 0xD1: /* SB */
289 FIXME("Enable Speaker - Not Implemented\n");
290 break;
291 case 0xD3: /* SB */
292 FIXME("Disable Speaker - Not Implemented\n");
293 break;
294 case 0xD4: /* SB */
295 FIXME("Continue DMA operation (8-bit) - Not Implemented\n");
296 break;
297 case 0xD8: /* SB */
298 FIXME("Speaker Status - Not Implemented\n");
299 break;
300 case 0xE0: /* SB 2.0 */
301 TRACE("DSP Identification\n");
302 DSP_OutBuffer[OutSize++] = ~val;
303 break;
304 case 0xE1: /* SB */
305 FIXME("DSP Version - Not Implemented\n");
306 break;
307 case 0xF2: /* SB */
308 TRACE("IRQ Request (8-bit)\n");
309 DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
310 break;
311 default:
312 if (((command&0xF0)==0xB0)||((DSP_InBuffer[0]&0xF0)==0xC0)) {
313 /* SB16 */
314 FIXME("Generic DAC/ADC DMA (16-bit, 8-bit) - %d % d\n",command,DSP_InBuffer[1]);
315 if (command&0x02)
316 FIXME("Generic DAC/ADC fifo mode not supported\n");
317 if (command&0x04)
318 FIXME("Generic DAC/ADC autoinit dma mode not supported\n");
319 if (command&0x08)
320 FIXME("Generic DAC/ADC adc mode not supported\n");
321 switch(command>>4) {
322 case 0xB:
323 FIXME("Generic DAC/ADC 8-bit not supported\n");
324 SampleMode = 0;
325 break;
326 case 0xC:
327 FIXME("Generic DAC/ADC 16-bit not supported\n");
328 SampleMode = 1;
329 break;
330 default:
331 ERR("Generic DAC/ADC resolution unknown\n");
332 break;
334 if (DSP_InBuffer[1]&0x010)
335 FIXME("Generic DAC/ADC signed sample mode not supported\n");
336 if (DSP_InBuffer[1]&0x020)
337 FIXME("Generic DAC/ADC stereo mode not supported\n");
338 SamplesCount = DSP_InBuffer[2]+(val<<8)+1;
339 TRACE("Generic DMA for %x samples\n",SamplesCount);
340 dma_enable = 1;
342 else
343 FIXME("DSP command %x not supported\n",val);
345 /* Empty the input buffer and end the command */
346 InSize = 0;
347 command = -1;
352 BYTE SB_ioport_in( WORD port )
354 BYTE res = 0;
356 switch(port)
358 /* DSP Read Data */
359 case 0x22a:
360 /* Value in the read buffer */
361 if (OutSize)
362 res = DSP_OutBuffer[--OutSize];
363 else
364 /* return the last byte */
365 res = DSP_OutBuffer[0];
366 break;
367 /* DSP - Write Buffer Status */
368 case 0x22c:
369 /* DSP always ready for writing */
370 res = 0x00;
371 break;
372 /* DSP - Data Available Status */
373 /* DSP - IRQ Acknowledge, 8-bit */
374 case 0x22e:
375 /* DSP data availability check */
376 if (OutSize)
377 res = 0x80;
378 else
379 res = 0x00;
380 break;
382 return res;