a0c65c67fab9708c5998e81581809bbf525599d4
[dosdemo] / libs / mikmod / drivers / drv_sb.c
1 /*      MikMod sound library
2         (c) 1998, 1999 Miodrag Vallat and others - see file AUTHORS for
3         complete list.
4
5         This library is free software; you can redistribute it and/or modify
6         it under the terms of the GNU Library General Public License as
7         published by the Free Software Foundation; either version 2 of
8         the License, or (at your option) any later version.
9
10         This program is distributed in the hope that it will be useful,
11         but WITHOUT ANY WARRANTY; without even the implied warranty of
12         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13         GNU Library General Public License for more details.
14
15         You should have received a copy of the GNU Library General Public
16         License along with this library; if not, write to the Free Software
17         Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18         02111-1307, USA.
19 */
20
21 /*==============================================================================
22
23   $Id$
24
25   Driver for SoundBlaster/Pro/16/AWE32 under DOS
26
27 ==============================================================================*/
28
29 /*
30
31         Written by Andrew Zabolotny <bit@eltech.ru>
32
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #ifdef DRV_SB
40
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44 #ifdef HAVE_FCNTL_H
45 #include <fcntl.h>
46 #endif
47
48 #include "mikmod_internals.h"
49
50 #include "dossb.h"
51
52 static void SB_CommandLine(const CHAR *cmdline)
53 {
54         char *ptr, *end;
55
56         if ((ptr=MD_GetAtom("port",cmdline,0)) != NULL) {
57                 sb.port = strtol(ptr, &end, 16);
58                 MikMod_free(ptr);
59         }
60         if ((ptr=MD_GetAtom("irq",cmdline,0)) != NULL) {
61                 sb.irq = strtol(ptr, &end, 10);
62                 MikMod_free(ptr);
63         }
64         if ((ptr=MD_GetAtom("dma",cmdline,0)) != NULL) {
65                 sb.dma8 = strtol(ptr, &end, 10);
66                 MikMod_free(ptr);
67         }
68         if ((ptr=MD_GetAtom("hidma",cmdline,0)) != NULL) {
69                 sb.dma16 = strtol(ptr, &end, 10);
70                 MikMod_free(ptr);
71         }
72 }
73
74 static BOOL SB_IsThere(void)
75 {
76         return sb_detect();
77 }
78
79 static int SB_Init(void)
80 {
81         if (!sb_open()) {
82                 _mm_errno = MMERR_INVALID_DEVICE;
83                 return 1;
84         }
85
86         /* Adjust md_mode according to sound card capabilities */
87         if (!(sb.caps & SBMODE_STEREO))
88                 md_mode &= ~DMODE_STEREO;
89         if (!(sb.caps & SBMODE_16BITS))
90                 md_mode &= ~DMODE_16BITS;
91
92         if (md_mixfreq < 4000)
93                 md_mixfreq = 4000;
94         if (md_mode & DMODE_STEREO) {
95                 if (md_mixfreq > sb.maxfreq_stereo)
96                         md_mixfreq = sb.maxfreq_stereo;
97         } else {
98                 if (md_mixfreq > sb.maxfreq_mono)
99                         md_mixfreq = sb.maxfreq_mono;
100         }
101
102         return VC_Init();
103 }
104
105 static void SB_Exit(void)
106 {
107         VC_Exit();
108         sb_close();
109 }
110
111 /* The last buffer byte filled with sound */
112 static unsigned int buff_tail = 0;
113
114 static void SB_Callback(void)
115 {
116         unsigned int dma_size, dma_pos;
117         ULONG (*mixer)(SBYTE *buf, ULONG todo);
118
119         sb_query_dma(&dma_size, &dma_pos);
120         /* There isn't much sense in filling less than 256 bytes */
121         dma_pos &= ~255;
122
123         /* If nothing to mix, quit */
124         if (buff_tail == dma_pos)
125                 return;
126
127         if (Player_Paused_internal())
128                 mixer = VC_SilenceBytes;
129         else
130                 mixer = VC_WriteBytes;
131
132         /* If DMA pointer still didn't wrapped around ... */
133         if (dma_pos > buff_tail) {
134                 buff_tail += mixer ((SBYTE *)(sb.dma_buff->linear + buff_tail), dma_pos - buff_tail);
135                 /* If we arrived right to the DMA buffer end, jump to the beginning */
136                 if (buff_tail >= dma_size)
137                         buff_tail = 0;
138         } else {
139                 /* If wrapped around, fill first to the end of buffer */
140                 mixer ((SBYTE *)(sb.dma_buff->linear + buff_tail), dma_size - buff_tail);
141                 /* Now fill from buffer beginning to current DMA pointer */
142                 buff_tail = mixer ((SBYTE *)sb.dma_buff->linear, dma_pos);
143         }
144 }
145
146 static void SB_Update(void)
147 {
148         /* Do nothing: the real update is done during SB interrupts */
149 }
150
151 static int SB_PlayStart (void)
152 {
153         if (VC_PlayStart())
154                 return 1;
155
156         /* Enable speaker output */
157         sb_output(TRUE);
158
159         /* Set our routine to be called during SB IRQs */
160         buff_tail = 0;
161         sb.timer_callback = SB_Callback;
162
163         /* Start cyclic DMA transfer */
164         if (!sb_start_dma(((md_mode & DMODE_16BITS) ? SBMODE_16BITS | SBMODE_SIGNED : 0) |
165                 ((md_mode & DMODE_STEREO) ? SBMODE_STEREO : 0), md_mixfreq))
166         {
167                 _mm_errno = MMERR_DOSSB_STARTDMA;
168                 return 1;
169         }
170
171         return 0;
172 }
173
174 static int SB_Reset(void)
175 {
176         sb_reset();
177         VC_Exit();
178         return VC_Init();
179 }
180
181 static void SB_PlayStop(void)
182 {
183         sb.timer_callback = NULL;
184         sb_output(FALSE);
185         sb_stop_dma();
186         VC_PlayStop();
187 }
188
189 MDRIVER drv_sb =
190 {
191         NULL,
192         "Sound Blaster",
193         "Sound Blaster Orig/2.0/Pro/16 v1.0",
194         0, 255,
195         "sb",
196         "port:c:220,230,240,250,260,270,280,32C,530,604,E80,F40,220:Sound Blaster base I/O port\n"
197         "irq:c:2,3,5,7,10,5:Sound Blaster IRQ\n"
198         "dma:c:0,1,3,1:Sound Blaster 8 bit DMA channel\n"
199         "hidma:c:5,6,7,5:Sound Blaster 16 bit DMA channel (SB16/AWE32 only)\n",
200
201         SB_CommandLine,
202         SB_IsThere,
203         VC_SampleLoad,
204         VC_SampleUnload,
205         VC_SampleSpace,
206         VC_SampleLength,
207         SB_Init,
208         SB_Exit,
209         SB_Reset,
210         VC_SetNumVoices,
211         SB_PlayStart,
212         SB_PlayStop,
213         SB_Update,
214         NULL,
215         VC_VoiceSetVolume,
216         VC_VoiceGetVolume,
217         VC_VoiceSetFrequency,
218         VC_VoiceGetFrequency,
219         VC_VoiceSetPanning,
220         VC_VoiceGetPanning,
221         VC_VoicePlay,
222         VC_VoiceStop,
223         VC_VoiceStopped,
224         VC_VoiceGetPosition,
225         VC_VoiceRealVolume
226 };
227
228 #else /* DRV_SB */
229
230 #include "mikmod_internals.h"
231 MISSING(drv_sb);
232
233 #endif
234
235 /* ex:set ts=4: */