winmm: Use proper structure where possible to fix command string parsing on 64-bit.
[wine] / dlls / krnl386.exe16 / soundblaster.c
1 /*
2  * Soundblaster Emulation
3  *
4  * Copyright 2002 Christian Costa
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include "config.h"
22
23 #include <stdarg.h>
24
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"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(sblaster);
34
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
40
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 int end_sound_loop = 0;
52 static int dma_enable = 0;
53
54 /* The maximum size of a dma transfer can be 65536 */
55 #define DMATRFSIZE 1024
56
57 /* DMA can perform 8 or 16-bit transfer */
58 static BYTE dma_buffer[DMATRFSIZE*2];
59
60 /* Direct Sound buffer config */
61 #define DSBUFLEN 4096 /* FIXME: Only this value seems to work */
62
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;
71
72 /* SB_Poll performs DMA transfers and fills the Direct Sound Buffer */
73 static DWORD CALLBACK SB_Poll( void *dummy )
74 {
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;
83
84     /* FIXME: this loop must be improved */
85     while(!end_sound_loop)
86     {
87         Sleep(10);
88
89         if (dma_enable) {
90             size = DMA_Transfer(SB_DMA,min(DMATRFSIZE,SamplesCount),dma_buffer);
91         } else
92             continue;
93
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;
98         }
99
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);
105         }
106         buf_off = (buf_off + dwbyteswritten1 + dwbyteswritten2) % DSBUFLEN;
107
108         result = IDirectSoundBuffer_Unlock(lpdsbuf,lpbuf1,dwbyteswritten1,lpbuf2,dwbyteswritten2);
109         if (result!=DS_OK)
110             ERR("Unable to unlock sound buffer !\n");
111
112         SamplesCount -= size;
113         if (!SamplesCount) {
114             DOSVM_QueueEvent(SB_IRQ,SB_IRQ_PRI,NULL,NULL);
115             dma_enable = 0;
116         }
117     }
118     return 0;
119 }
120
121 static BOOL SB_Init(void)
122 {
123     HRESULT result;
124
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 0;
130         }
131
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 0;
140         }
141         */
142
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;
151
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 0;
160         }
161
162         result = IDirectSoundBuffer_Play(lpdsbuf,0, 0, DSBPLAY_LOOPING);
163         if (result != DS_OK) {
164             ERR("Can't start playing !\n");
165             return 0;
166         }
167
168         buf_off = 0;
169         end_sound_loop = 0;
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 0;
175         }
176     }
177     return 1;
178 }
179
180 static void SB_Reset(void)
181 {
182     int i;
183
184     for(i=0;i<256;i++)
185         DSP_Command[i]=0;
186
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 Indentification */
197     DSP_Command[0xE0]=1;
198
199     /* Clear command and input buffer */
200     command = -1;
201     InSize = 0;
202
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;
211 }
212
213 /* Find a standard sampling rate for DirectSound */
214 static int SB_StdSampleRate(int SampleRate)
215 {
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;
225 }
226
227 void SB_ioport_out( WORD port, BYTE val )
228 {
229     switch(port)
230     {
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;
243         }
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)
250             {
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 = 1;
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 = 0;
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;
327                     }
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 = 1;
335                 }
336                 else
337                     FIXME("DSP command %x not supported\n",val);
338             }
339             /* Empty the input buffer and end the command */
340             InSize = 0;
341             command = -1;
342         }
343     }
344 }
345
346 BYTE SB_ioport_in( WORD port )
347 {
348     BYTE res = 0;
349
350     switch(port)
351     {
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;
375     }
376     return res;
377 }