Release 2.9.
[wine.git] / dlls / krnl386.exe16 / soundblaster.c
blob8939bb3e878bb8c121351d341a662bdfc27e07b8
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "config.h"
23 #include <stdarg.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "dosexe.h"
28 #include "wine/debug.h"
29 #include "wingdi.h"
30 #include "mmsystem.h"
31 #include "dsound.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(sblaster);
35 /* Board Configuration */
36 /* FIXME: Should be in a config file */
37 #define SB_IRQ 5
38 #define SB_IRQ_PRI 11
39 #define SB_DMA 1
41 /* Soundblaster state */
42 static int SampleMode; /* Mono / Stereo */
43 static int SampleRate;
44 static int SamplesCount;
45 static BYTE DSP_Command[256]; /* Store param numbers in bytes for each command */
46 static BYTE DSP_InBuffer[10]; /* Store DSP command bytes parameters from host */
47 static int InSize; /* Nb of bytes in InBuffer */
48 static BYTE DSP_OutBuffer[10]; /* Store DSP information bytes to host */
49 static int OutSize; /* Nb of bytes in InBuffer */
50 static int command; /* Current command */
51 static BOOL end_sound_loop = FALSE;
52 static BOOL dma_enable = FALSE;
54 /* The maximum size of a dma transfer can be 65536 */
55 #define DMATRFSIZE 1024
57 /* DMA can perform 8 or 16-bit transfer */
58 static BYTE dma_buffer[DMATRFSIZE*2];
60 /* Direct Sound buffer config */
61 #define DSBUFLEN 4096 /* FIXME: Only this value seems to work */
63 /* Direct Sound playback stuff */
64 static LPDIRECTSOUND lpdsound;
65 static LPDIRECTSOUNDBUFFER lpdsbuf;
66 static DSBUFFERDESC buf_desc;
67 static WAVEFORMATEX wav_fmt;
68 static HANDLE SB_Thread;
69 static UINT buf_off;
70 extern HWND vga_hwnd;
72 /* SB_Poll performs DMA transfers and fills the Direct Sound Buffer */
73 static DWORD CALLBACK SB_Poll( void *dummy )
75 HRESULT result;
76 LPBYTE lpbuf1 = NULL;
77 LPBYTE lpbuf2 = NULL;
78 DWORD dwsize1 = 0;
79 DWORD dwsize2 = 0;
80 DWORD dwbyteswritten1 = 0;
81 DWORD dwbyteswritten2 = 0;
82 int size;
84 /* FIXME: this loop must be improved */
85 while(!end_sound_loop)
87 Sleep(10);
89 if (dma_enable) {
90 size = DMA_Transfer(SB_DMA,min(DMATRFSIZE,SamplesCount),dma_buffer);
91 } else
92 continue;
94 result = IDirectSoundBuffer_Lock(lpdsbuf,buf_off,size,(LPVOID *)&lpbuf1,&dwsize1,(LPVOID *)&lpbuf2,&dwsize2,0);
95 if (result != DS_OK) {
96 ERR("Unable to lock sound buffer !\n");
97 continue;
100 dwbyteswritten1 = min(size,dwsize1);
101 memcpy(lpbuf1,dma_buffer,dwbyteswritten1);
102 if (size>dwsize1) {
103 dwbyteswritten2 = min(size - dwbyteswritten1,dwsize2);
104 memcpy(lpbuf2,dma_buffer+dwbyteswritten1,dwbyteswritten2);
106 buf_off = (buf_off + dwbyteswritten1 + dwbyteswritten2) % DSBUFLEN;
108 result = IDirectSoundBuffer_Unlock(lpdsbuf,lpbuf1,dwbyteswritten1,lpbuf2,dwbyteswritten2);
109 if (result!=DS_OK)
110 ERR("Unable to unlock sound buffer !\n");
112 SamplesCount -= size;
113 if (!SamplesCount) {
114 DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
115 dma_enable = FALSE;
118 return 0;
121 static BOOL SB_Init(void)
123 HRESULT result;
125 if (!lpdsound) {
126 result = DirectSoundCreate(NULL,&lpdsound,NULL);
127 if (result != DS_OK) {
128 ERR("Unable to initialize Sound Subsystem err = %x !\n",result);
129 return FALSE;
132 /* FIXME: To uncomment when :
133 - SetCooperative level is correctly implemented
134 - an always valid and non changing handle to a windows (vga_hwnd) is available
135 (this surely needs some work in vga.c)
136 result = IDirectSound_SetCooperativeLevel(lpdsound,vga_hwnd,DSSCL_EXCLUSIVE|DSSCL_PRIORITY);
137 if (result != DS_OK) {
138 ERR("Can't set cooperative level !\n");
139 return FALSE;
143 /* Default format */
144 wav_fmt.wFormatTag = WAVE_FORMAT_PCM;
145 wav_fmt.nChannels = 1;
146 wav_fmt.nSamplesPerSec = 22050;
147 wav_fmt.nAvgBytesPerSec = 22050;
148 wav_fmt.nBlockAlign = 1;
149 wav_fmt.wBitsPerSample = 8;
150 wav_fmt.cbSize = 0;
152 memset(&buf_desc,0,sizeof(DSBUFFERDESC));
153 buf_desc.dwSize = sizeof(DSBUFFERDESC);
154 buf_desc.dwBufferBytes = DSBUFLEN;
155 buf_desc.lpwfxFormat = &wav_fmt;
156 result = IDirectSound_CreateSoundBuffer(lpdsound,&buf_desc,&lpdsbuf,NULL);
157 if (result != DS_OK) {
158 ERR("Can't create sound buffer !\n");
159 return FALSE;
162 result = IDirectSoundBuffer_Play(lpdsbuf,0, 0, DSBPLAY_LOOPING);
163 if (result != DS_OK) {
164 ERR("Can't start playing !\n");
165 return FALSE;
168 buf_off = 0;
169 end_sound_loop = FALSE;
170 SB_Thread = CreateThread(NULL, 0, SB_Poll, NULL, 0, NULL);
171 TRACE("thread\n");
172 if (!SB_Thread) {
173 ERR("Can't create thread !\n");
174 return FALSE;
177 return TRUE;
180 static void SB_Reset(void)
182 int i;
184 for(i=0;i<256;i++)
185 DSP_Command[i]=0;
187 /* Set Time Constant */
188 DSP_Command[0x40]=1;
189 /* Generate IRQ */
190 DSP_Command[0xF2]=0;
191 /* DMA DAC 8-bits */
192 DSP_Command[0x14]=2;
193 /* Generic DAC/ADC DMA (16-bit, 8-bit) */
194 for(i=0xB0;i<=0xCF;i++)
195 DSP_Command[i]=3;
196 /* DSP Identification */
197 DSP_Command[0xE0]=1;
199 /* Clear command and input buffer */
200 command = -1;
201 InSize = 0;
203 /* Put a garbage value in the output buffer */
204 OutSize = 1;
205 if (SB_Init())
206 /* All right, let's put the magic value for autodetection */
207 DSP_OutBuffer[0] = 0xaa;
208 else
209 /* Something is wrong, put 0 to failed autodetection */
210 DSP_OutBuffer[0] = 0x00;
213 /* Find a standard sampling rate for DirectSound */
214 static int SB_StdSampleRate(int SampleRate)
216 if (SampleRate>((44100+48000)/2)) return 48000;
217 if (SampleRate>((32000+44100)/2)) return 44100;
218 if (SampleRate>((24000+32000)/2)) return 32000;
219 if (SampleRate>((22050+24000)/2)) return 24000;
220 if (SampleRate>((16000+22050)/2)) return 22050;
221 if (SampleRate>((12000+16000)/2)) return 16000;
222 if (SampleRate>((11025+12000)/2)) return 12000;
223 if (SampleRate>((8000+11025)/2)) return 11025;
224 return 8000;
227 void SB_ioport_out( WORD port, BYTE val )
229 switch(port)
231 /* DSP - Reset */
232 case 0x226:
233 TRACE("Resetting DSP.\n");
234 SB_Reset();
235 break;
236 /* DSP - Write Data or Command */
237 case 0x22c:
238 TRACE("val=%x\n",val);
239 if (command == -1) {
240 /* Clear input buffer and set the current command */
241 command = val;
242 InSize = 0;
244 if (InSize!=DSP_Command[command])
245 /* Fill the input buffer the command parameters if any */
246 DSP_InBuffer[InSize++]=val;
247 else {
248 /* Process command */
249 switch(command)
251 case 0x10: /* SB */
252 FIXME("Direct DAC (8-bit) - Not Implemented\n");
253 break;
254 case 0x14: /* SB */
255 SamplesCount = DSP_InBuffer[1]+(val<<8)+1;
256 TRACE("DMA DAC (8-bit) for %x samples\n",SamplesCount);
257 dma_enable = TRUE;
258 break;
259 case 0x20:
260 FIXME("Direct ADC (8-bit) - Not Implemented\n");
261 break;
262 case 0x24: /* SB */
263 FIXME("DMA ADC (8-bit) - Not Implemented\n");
264 break;
265 case 0x40: /* SB */
266 SampleRate = 1000000/(256-val);
267 TRACE("Set Time Constant (%d <-> %d Hz => %d Hz)\n",DSP_InBuffer[0],
268 SampleRate,SB_StdSampleRate(SampleRate));
269 SampleRate = SB_StdSampleRate(SampleRate);
270 wav_fmt.nSamplesPerSec = SampleRate;
271 wav_fmt.nAvgBytesPerSec = SampleRate;
272 IDirectSoundBuffer_SetFormat(lpdsbuf,&wav_fmt);
273 break;
274 /* case 0xBX/0xCX -> See below */
275 case 0xD0: /* SB */
276 TRACE("Halt DMA operation (8-bit)\n");
277 dma_enable = FALSE;
278 break;
279 case 0xD1: /* SB */
280 FIXME("Enable Speaker - Not Implemented\n");
281 break;
282 case 0xD3: /* SB */
283 FIXME("Disable Speaker - Not Implemented\n");
284 break;
285 case 0xD4: /* SB */
286 FIXME("Continue DMA operation (8-bit) - Not Implemented\n");
287 break;
288 case 0xD8: /* SB */
289 FIXME("Speaker Status - Not Implemented\n");
290 break;
291 case 0xE0: /* SB 2.0 */
292 TRACE("DSP Identification\n");
293 DSP_OutBuffer[OutSize++] = ~val;
294 break;
295 case 0xE1: /* SB */
296 TRACE("DSP Version\n");
297 OutSize=2;
298 DSP_OutBuffer[0]=0; /* returns version 1.0 */
299 DSP_OutBuffer[1]=1;
300 break;
301 case 0xF2: /* SB */
302 TRACE("IRQ Request (8-bit)\n");
303 DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
304 break;
305 default:
306 if (((command&0xF0)==0xB0)||((DSP_InBuffer[0]&0xF0)==0xC0)) {
307 /* SB16 */
308 FIXME("Generic DAC/ADC DMA (16-bit, 8-bit) - %d % d\n",command,DSP_InBuffer[1]);
309 if (command&0x02)
310 FIXME("Generic DAC/ADC fifo mode not supported\n");
311 if (command&0x04)
312 FIXME("Generic DAC/ADC autoinit dma mode not supported\n");
313 if (command&0x08)
314 FIXME("Generic DAC/ADC adc mode not supported\n");
315 switch(command>>4) {
316 case 0xB:
317 FIXME("Generic DAC/ADC 8-bit not supported\n");
318 SampleMode = 0;
319 break;
320 case 0xC:
321 FIXME("Generic DAC/ADC 16-bit not supported\n");
322 SampleMode = 1;
323 break;
324 default:
325 ERR("Generic DAC/ADC resolution unknown\n");
326 break;
328 if (DSP_InBuffer[1]&0x010)
329 FIXME("Generic DAC/ADC signed sample mode not supported\n");
330 if (DSP_InBuffer[1]&0x020)
331 FIXME("Generic DAC/ADC stereo mode not supported\n");
332 SamplesCount = DSP_InBuffer[2]+(val<<8)+1;
333 TRACE("Generic DMA for %x samples\n",SamplesCount);
334 dma_enable = TRUE;
336 else
337 FIXME("DSP command %x not supported\n",val);
339 /* Empty the input buffer and end the command */
340 InSize = 0;
341 command = -1;
346 BYTE SB_ioport_in( WORD port )
348 BYTE res = 0;
350 switch(port)
352 /* DSP Read Data */
353 case 0x22a:
354 /* Value in the read buffer */
355 if (OutSize)
356 res = DSP_OutBuffer[--OutSize];
357 else
358 /* return the last byte */
359 res = DSP_OutBuffer[0];
360 break;
361 /* DSP - Write Buffer Status */
362 case 0x22c:
363 /* DSP always ready for writing */
364 res = 0x00;
365 break;
366 /* DSP - Data Available Status */
367 /* DSP - IRQ Acknowledge, 8-bit */
368 case 0x22e:
369 /* DSP data availability check */
370 if (OutSize)
371 res = 0x80;
372 else
373 res = 0x00;
374 break;
376 return res;