1 /* MikMod sound library
2 (c) 1998, 1999, 2000 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 output on OS/2 MMPM/2 using direct audio (DART)
26
27 ==============================================================================*/
28
29 /*
30
31 Written by Miodrag Vallat <miod@mikmod.org>
32 Lots of improvements (basically made usable...) by Andrew Zabolotny
33 <bit@eltech.ru>
34
35 */
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include "mikmod_internals.h"
42
43 #ifdef DRV_DART
44
45 #define INCL_DOS
46 #define INCL_OS2MM
47 #include <os2.h>
48 /* Prevent a warning: PPFN redefined */
49 #define PPFN _PPFN
50 #include <os2me.h>
51 #undef PPFN
52
53 #include <stdlib.h>
54 #include <string.h>
55
56 #define MAX_BUFFERCOUNT 8
57
58 static MCI_MIX_BUFFER MixBuffers[MAX_BUFFERCOUNT];
59 static MCI_MIXSETUP_PARMS MixSetupParms;
60 static MCI_BUFFER_PARMS BufferParms;
61 static ULONG DeviceIndex = 0; /* use default waveaudio device */
62 static ULONG BufferSize = (ULONG)-1; /* autodetect buffer size */
63 static ULONG BufferCount = 2; /* use two buffers */
64 static ULONG DeviceID = 0;
65
Dart_CommandLine(const CHAR * cmdline)66 static void Dart_CommandLine(const CHAR *cmdline)
67 {
68 char *ptr;
69 int buf;
70
71 if ((ptr = MD_GetAtom("buffer", cmdline, 0)) != NULL) {
72 buf = atoi(ptr);
73 if (buf >= 12 && buf <= 16)
74 BufferSize = 1 << buf;
75 MikMod_free(ptr);
76 }
77
78 if ((ptr = MD_GetAtom("count", cmdline, 0)) != NULL) {
79 buf = atoi(ptr);
80 if (buf >= 2 && buf <= MAX_BUFFERCOUNT)
81 BufferCount = buf;
82 MikMod_free(ptr);
83 }
84
85 if ((ptr = MD_GetAtom("device", cmdline, 0)) != NULL) {
86 buf = atoi(ptr);
87 if (buf >= 0 && buf <= 8)
88 DeviceIndex = buf;
89 MikMod_free(ptr);
90 }
91 }
92
93 /* Buffer update thread (created and called by DART)
94 This is a high-priority thread used to compute and update the audio stream,
95 automatically created by the DART subsystem. We compute the next audio
96 buffer and feed it to the waveaudio device. */
Dart_UpdateBuffers(ULONG ulStatus,PMCI_MIX_BUFFER pBuffer,ULONG ulFlags)97 static LONG APIENTRY Dart_UpdateBuffers(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags)
98 {
99 /* sanity check */
100 if (!pBuffer)
101 return TRUE;
102
103 /* if we have finished a buffer, we're ready to play a new one */
104 if ((ulFlags == MIX_WRITE_COMPLETE) ||
105 ((ulFlags == (MIX_WRITE_COMPLETE | MIX_STREAM_ERROR)) &&
106 (ulStatus == ERROR_DEVICE_UNDERRUN))) {
107 /* refill this audio buffer and feed it again */
108 MUTEX_LOCK(vars);
109 if (Player_Paused_internal())
110 pBuffer->ulBufferLength =
111 VC_SilenceBytes(pBuffer->pBuffer, BufferSize);
112 else
113 pBuffer->ulBufferLength =
114 VC_WriteBytes(pBuffer->pBuffer, BufferSize);
115 MUTEX_UNLOCK(vars);
116 MixSetupParms.pmixWrite(MixSetupParms.ulMixHandle, pBuffer, 1);
117 }
118 return TRUE;
119 }
120
Dart_IsPresent(void)121 static BOOL Dart_IsPresent(void)
122 {
123 MCI_AMP_OPEN_PARMS AmpOpenParms;
124 MCI_GENERIC_PARMS GenericParms;
125
126 memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS));
127 AmpOpenParms.pszDeviceType = (PSZ) MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX,
128 (USHORT) DeviceIndex);
129
130 if (mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID,
131 (PVOID) &AmpOpenParms, 0) != MCIERR_SUCCESS)
132 return 0;
133
134 mciSendCommand(AmpOpenParms.usDeviceID, MCI_CLOSE, MCI_WAIT,
135 (PVOID) &GenericParms, 0);
136 return 1;
137 }
138
Dart_Init(void)139 static int Dart_Init(void)
140 {
141 MCI_AMP_OPEN_PARMS AmpOpenParms;
142 MCI_GENERIC_PARMS GenericParms;
143
144 MixBuffers[0].pBuffer = NULL; /* marker */
145 DeviceID = 0;
146 memset(&GenericParms, 0, sizeof(MCI_GENERIC_PARMS));
147
148 /* open AMP device */
149 memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS));
150 AmpOpenParms.usDeviceID = 0;
151 AmpOpenParms.pszDeviceType = (PSZ) MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX,
152 (USHORT) DeviceIndex);
153
154
155 if (mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID,
156 (PVOID) & AmpOpenParms, 0) != MCIERR_SUCCESS) {
157 _mm_errno = MMERR_OPENING_AUDIO;
158 return 1;
159 }
160
161 DeviceID = AmpOpenParms.usDeviceID;
162
163 /* setup playback parameters */
164 memset(&MixSetupParms, 0, sizeof(MCI_MIXSETUP_PARMS));
165 MixSetupParms.ulBitsPerSample = (md_mode & DMODE_16BITS) ? 16 : 8;
166 MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM;
167 MixSetupParms.ulSamplesPerSec = md_mixfreq;
168 MixSetupParms.ulChannels = (md_mode & DMODE_STEREO) ? 2 : 1;
169 MixSetupParms.ulFormatMode = MCI_PLAY;
170 MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
171 MixSetupParms.pmixEvent = Dart_UpdateBuffers;
172
173 if (mciSendCommand(DeviceID, MCI_MIXSETUP,
174 MCI_WAIT | MCI_MIXSETUP_INIT,
175 (PVOID) & MixSetupParms, 0) != MCIERR_SUCCESS) {
176 _mm_errno = MMERR_OS2_MIXSETUP;
177 return 1;
178 }
179
180 /* Compute audio buffer size now if necessary */
181 if (BufferSize == (ULONG)-1) {
182 /* DART suggested buffer size is somewhat too big. We compute a size
183 for circa 1/4" of playback. */
184 int bit;
185
186 BufferSize = md_mixfreq >> 2;
187 if (md_mode & DMODE_STEREO)
188 BufferSize <<= 1;
189 if (md_mode & DMODE_16BITS)
190 BufferSize <<= 1;
191 for (bit = 15; bit >= 12; bit--)
192 if (BufferSize & (1 << bit))
193 break;
194 BufferSize = (1 << bit);
195 }
196 /* make sure buffer is not greater than 64 Kb, as DART can't handle this
197 situation. */
198 if (BufferSize > 65536)
199 BufferSize = 65536;
200
201 BufferParms.ulStructLength = sizeof(BufferParms);
202 BufferParms.ulNumBuffers = BufferCount;
203 BufferParms.ulBufferSize = BufferSize;
204 BufferParms.pBufList = MixBuffers;
205
206 if (mciSendCommand(DeviceID, MCI_BUFFER,
207 MCI_WAIT | MCI_ALLOCATE_MEMORY,
208 (PVOID) &BufferParms, 0) != MCIERR_SUCCESS) {
209 _mm_errno = MMERR_OUT_OF_MEMORY;
210 return 1;
211 }
212
213 return VC_Init();
214 }
215
Dart_Exit(void)216 static void Dart_Exit(void)
217 {
218 MCI_GENERIC_PARMS GenericParms;
219
220 if (MixBuffers[0].pBuffer) {
221 mciSendCommand(DeviceID, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY,
222 &BufferParms, 0);
223 MixBuffers[0].pBuffer = NULL;
224 }
225 if (DeviceID) {
226 mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, (PVOID) &GenericParms, 0);
227 DeviceID = 0;
228 }
229 VC_Exit();
230 }
231
Dart_PlayStart(void)232 static int Dart_PlayStart(void)
233 {
234 MCI_GENERIC_PARMS GenericParms;
235 int i;
236
237 if (VC_PlayStart())
238 return 1;
239
240 /* silence buffers */
241 for (i = 0; i < BufferCount; i++) {
242 VC_SilenceBytes(MixBuffers[i].pBuffer, BufferSize);
243 MixBuffers[i].ulBufferLength = BufferSize;
244 }
245
246 /* grab exclusive rights to device instance (not entire device) */
247 GenericParms.hwndCallback = 0; /* Not needed, so set to 0 */
248 if (mciSendCommand(DeviceID, MCI_ACQUIREDEVICE, MCI_EXCLUSIVE_INSTANCE,
249 (PVOID) &GenericParms, 0) != MCIERR_SUCCESS) {
250 mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, (PVOID) &GenericParms, 0);
251 _mm_errno = MMERR_OPENING_AUDIO;
252 return 1;
253 }
254
255 /* wake DART up */
256 MixSetupParms.pmixWrite(MixSetupParms.ulMixHandle, MixBuffers, BufferCount);
257
258 return 0;
259 }
260
Dart_Update(void)261 static void Dart_Update(void)
262 {
263 /* Nothing to do: everything is done in DART_UpdateBuffers() */
264 }
265
Dart_PlayStop(void)266 static void Dart_PlayStop(void)
267 {
268 MCI_GENERIC_PARMS GenericParms;
269
270 GenericParms.hwndCallback = 0;
271 mciSendCommand(DeviceID, MCI_STOP, MCI_WAIT, (PVOID) &GenericParms, 0);
272
273 VC_PlayStop();
274 }
275
276 MIKMODAPI MDRIVER drv_dart = {
277 NULL,
278 "Direct audio (DART)",
279 "OS/2 DART driver v1.2",
280 0, 255,
281 "dart",
282 "device:r:0,8,0:Waveaudio device index to use (0 - default)\n"
283 "buffer:r:12,16:Audio buffer log2 size\n"
284 "count:r:2,8,2:Number of audio buffers\n",
285 Dart_CommandLine,
286 Dart_IsPresent,
287 VC_SampleLoad,
288 VC_SampleUnload,
289 VC_SampleSpace,
290 VC_SampleLength,
291 Dart_Init,
292 Dart_Exit,
293 NULL,
294 VC_SetNumVoices,
295 Dart_PlayStart,
296 Dart_PlayStop,
297 Dart_Update,
298 NULL,
299 VC_VoiceSetVolume,
300 VC_VoiceGetVolume,
301 VC_VoiceSetFrequency,
302 VC_VoiceGetFrequency,
303 VC_VoiceSetPanning,
304 VC_VoiceGetPanning,
305 VC_VoicePlay,
306 VC_VoiceStop,
307 VC_VoiceStopped,
308 VC_VoiceGetPosition,
309 VC_VoiceRealVolume
310 };
311
312 #else
313
314 MISSING(drv_dart);
315
316 #endif
317
318 /* ex:set ts=4: */
319