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