source: trunk/src/dsound/dart.cpp@ 5553

Last change on this file since 5553 was 5553, checked in by phaller, 24 years ago

Fix crashes in PowerDVD 3

File size: 14.4 KB
Line 
1/* $Id: dart.cpp,v 1.5 2001-04-20 13:22:37 phaller Exp $ */
2/*
3 * Dart Interface..
4 *
5 * Kevin Langman
6 *
7 * Project Odin Software License can be found in LICENSE.TXT
8 *
9 */
10
11#define INCL_DOS
12#define INCL_OS2MM
13#include <os2wrap.h>
14#include <os2mewrap.h>
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19
20#define DART_DSOUND
21#include "dart.h"
22#include "dsmixer.h"
23
24#include <misc.h>
25
26static MCI_MIXSETUP_PARMS *MixSetup_Global;
27static long lLastBuff;
28static char *pDSoundBuff;
29static BOOL fIsPlaying = FALSE;
30
31USHORT usDeviceID; /* Amp Mixer device id */
32
33/* TODO: scrap this variable! */
34MCI_MIX_BUFFER *pMixBuffers; /* Device buffers */
35
36MCI_MIXSETUP_PARMS MixSetupParms; /* Mixer parameters */
37MCI_BUFFER_PARMS BufferParms; /* Device buffer parms */
38ULONG ulNumDartBuffs; /* # of DART buffers */
39
40#define ULONG_LOWD(ul) (*(USHORT *)((ULONG *)(&ul))) /* Low Word */
41
42//******************************************************************************
43//******************************************************************************
44LONG APIENTRY OS2_Dart_Update(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags)
45{
46 ULONG rc;
47
48 if( ( (ulFlags == MIX_WRITE_COMPLETE) ||
49 ((ulFlags == (MIX_WRITE_COMPLETE | MIX_STREAM_ERROR))&&
50 (ulStatus == ERROR_DEVICE_UNDERRUN)) )
51 )
52 {
53 // wrap last buffer index
54 lLastBuff++;
55 if (lLastBuff >= ulNumDartBuffs)
56 {
57 lLastBuff = 0;
58 }
59
60 if( fIsPlaying == FALSE /*&& lLastBuff == 0*/ )
61 {
62 mciSendCommand(usDeviceID, MCI_STOP, MCI_WAIT, NULL, 0);
63 return TRUE;
64 }
65
66 /* Now mix sound from all playing secondary SoundBuffers into the primary buffer */
67 MixCallback(BUFFER_SIZE/ulNumDartBuffs);
68
69 /* Fill The next buff from the DSound Buffer */
70 memcpy( pMixBuffers[lLastBuff].pBuffer, &pDSoundBuff[lLastBuff*(BUFFER_SIZE/ulNumDartBuffs)], BUFFER_SIZE/ulNumDartBuffs );
71
72 /* Send the NEXT Buffer to Dart for playing! */
73 rc = MixSetup_Global->pmixWrite(MixSetup_Global->ulMixHandle, &pMixBuffers[lLastBuff], 1 );
74 }
75
76 return TRUE;
77}
78
79
80long Dart_Open_Device(USHORT *pusDeviceID, void **vpMixBuffer, void **vpMixSetup,
81 void **vpBuffParms, void **ppvBuffer)
82{
83
84 MCI_AMP_OPEN_PARMS AmpOpenParms;
85 ULONG rc, ulNew;
86 LONG lAdd = 5;
87 short device = 0;
88
89 dprintf(("DSOUND-DART: Dart_Open_Device"));
90
91 /* TODO: remove eventually... */
92 DosSetRelMaxFH(&lAdd, &ulNew);
93
94 ulNumDartBuffs = BUFFER_SIZE * 44100 / (2 * 2 * 2048 * 22050);
95
96 pMixBuffers = (MCI_MIX_BUFFER*)malloc(sizeof(MCI_MIX_BUFFER) * ulNumDartBuffs);
97
98 *vpMixBuffer = pMixBuffers;
99 *vpMixSetup = &MixSetupParms;
100 *vpBuffParms = &BufferParms;
101
102 lLastBuff = 0;
103
104 /* Is there a way to avoid the use of the MixSetup_Global ????? */
105 MixSetup_Global = &MixSetupParms;
106 /****************************************************************/
107
108 // If the DSOUND_DEVICE is set then use that number as the device for DART.
109 // this will allow people with many sound cards to use the card of their choice
110 // for an instance of a DSOUND enabled app!
111 if (getenv("DSOUND_DEVICE") != NULL) {
112 device = atoi(getenv("DSOUND_DEVICE"));
113 }
114
115 /* Open the AmpMix Device and start processing the buffer! */
116 memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS));
117 AmpOpenParms.usDeviceID = 0;
118 //AmpOpenParms.pszDeviceType = (PSZ)MCI_DEVTYPE_AUDIO_AMPMIX;
119 AmpOpenParms.pszDeviceType = (PSZ)MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX, (USHORT)device);
120
121 rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE_ID, (PVOID)&AmpOpenParms, 0);
122 if (rc != MCIERR_SUCCESS) {
123 dprintf(("DSOUND-DART: MCI_OPEN %d", rc));
124 return DSERR_GENERIC;
125 }
126 *pusDeviceID = AmpOpenParms.usDeviceID;
127 usDeviceID = AmpOpenParms.usDeviceID;
128
129 /* setup playback parameters */
130 memset(&MixSetupParms, 0, sizeof(MCI_MIXSETUP_PARMS));
131 MixSetupParms.ulBitsPerSample = 16;
132 MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM;
133 MixSetupParms.ulSamplesPerSec = 22500;
134 MixSetupParms.ulChannels = 2;
135 MixSetupParms.ulFormatMode = MCI_PLAY;
136 MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
137 MixSetupParms.pmixEvent = OS2_Dart_Update;
138
139 rc = mciSendCommand(usDeviceID, MCI_MIXSETUP, MCI_WAIT | MCI_MIXSETUP_INIT,
140 (PVOID)&MixSetupParms, 0);
141 if (rc != MCIERR_SUCCESS) {
142 dprintf(("DSOUND-DART: MCI_MIXSETUP (Constructor) %d", rc));
143 return DSERR_GENERIC;
144 }
145
146 /* Create the Audio Buffer */
147 // OK... Since DSound only uses 1 buffer and uses the GetPosition API to
148 // figure out where it can and can't write. I have emulating this by
149 // using many smaller buffers and do locking by tracking what buffer that is
150 // currently playing. This maybe less CPU friendly then other methods but
151 // it's the best my little brain could come up with!!
152
153 MixSetupParms.ulBufferSize = BUFFER_SIZE / ulNumDartBuffs;
154
155 BufferParms.ulNumBuffers = ulNumDartBuffs;
156 BufferParms.ulBufferSize = MixSetupParms.ulBufferSize;
157 BufferParms.pBufList = pMixBuffers;
158
159 rc = mciSendCommand(usDeviceID, MCI_BUFFER, MCI_WAIT | MCI_ALLOCATE_MEMORY,
160 (PVOID)&BufferParms, 0);
161 if ( ULONG_LOWD(rc) != MCIERR_SUCCESS ) {
162 dprintf(("DSOUND-DART: MCI_BUFFER (Alloc) %d", rc));
163 mciSendCommand(*pusDeviceID, MCI_CLOSE, MCI_WAIT, NULL, 0);
164 return DSERR_OUTOFMEMORY;
165 }
166
167 /* Clear the Buffer */
168 // Set initial values to 32767 to avoid clicks on start of playback.
169 for (device = 0; device < ulNumDartBuffs; device++) {
170 memset(pMixBuffers[device].pBuffer, 32767, BUFFER_SIZE/ulNumDartBuffs);
171 }
172
173 // Allocate memory for the DSound "Holder" buffer.
174 // TODO: Move this to the Constructor for OS2PrimBuff
175 // so that the Deconstructor can be used to free the memory!
176 *(char**)ppvBuffer = (char*)malloc(BUFFER_SIZE);
177 if (*ppvBuffer == NULL) {
178 return DSERR_OUTOFMEMORY;
179 }
180 pDSoundBuff = (char*)(*ppvBuffer);
181
182 dprintf(("DSOUND-DART: Dart_Open_Device Exiting"));
183
184 return DS_OK;
185}
186
187long Dart_Close_Device(USHORT usDeviceID, void *vpMixBuffer, void *vpMixSetup,
188 void *vpBuffParms )
189{
190 MCI_MIX_BUFFER *MixBuffer;
191 MCI_MIXSETUP_PARMS *MixSetup;
192 MCI_BUFFER_PARMS *BufferParms;
193 ULONG rc;
194
195 dprintf(("DSOUND-DART: Dart_Close_Device"));
196
197 MixBuffer = (MCI_MIX_BUFFER*)vpMixBuffer;
198 MixSetup = (MCI_MIXSETUP_PARMS*)vpMixSetup;
199 BufferParms = (MCI_BUFFER_PARMS*)vpBuffParms;
200
201 rc = mciSendCommand(usDeviceID, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, BufferParms, 0);
202 if (rc != MCIERR_SUCCESS) {
203 dprintf(("DSOUND-DART: MCI_BUFFER (Close) %d", rc));
204 }
205 rc = mciSendCommand(usDeviceID, MCI_CLOSE, MCI_WAIT, NULL, 0);
206 if (rc != MCIERR_SUCCESS) {
207 dprintf(("DSOUND-DART: MCI_CLOSE (Close) %d", rc));
208 }
209
210 dprintf(("DSOUND-DART: Dart_Close_Device returning DS_OK"));
211 return DS_OK;
212}
213
214
215long Dart_GetPosition(USHORT usDeviceID, LONG *pulPosition)
216{
217 dprintf(("DSOUND-DART: Dart_GetPosition"));
218
219 *pulPosition = (lLastBuff * (BUFFER_SIZE/ulNumDartBuffs)) + (BUFFER_SIZE/ulNumDartBuffs);
220 if (*pulPosition > BUFFER_SIZE)
221 *pulPosition = 0;
222
223 dprintf(("DSOUND-DART: Returning %d", *pulPosition));
224
225 return DS_OK;
226}
227
228long Dart_SetFormat(USHORT *pusDeviceID, void *vpMixSetup, void *vpBufferParms, void **vpMixBuffer, LONG lBPS, LONG lSPS, LONG lChannels )
229{
230 ULONG rc;
231 MCI_MIXSETUP_PARMS *MixSetup;
232 MCI_BUFFER_PARMS *BufferParms;
233 MCI_AMP_OPEN_PARMS AmpOpenParms;
234 short device = 0;
235
236 /* Recalculate the number of DART buffers based on the new data rate */
237 /* Note: the factor 2048 means a 2K buffer, 1024 = 4K, 512 = 8K and so on */
238 ulNumDartBuffs = BUFFER_SIZE * 44100 / ((lBPS / 8) * lChannels * 2048 * lSPS);
239
240 /* Reallocate the MCI_MIX_BUFFER array */
241 free(pMixBuffers);
242 pMixBuffers = (MCI_MIX_BUFFER*)malloc(sizeof(MCI_MIX_BUFFER) * ulNumDartBuffs);
243
244 MixSetup = (MCI_MIXSETUP_PARMS*)vpMixSetup;
245 BufferParms = (MCI_BUFFER_PARMS*)vpBufferParms;
246 *vpMixBuffer = pMixBuffers;
247
248
249 dprintf(("DSOUND-DART: Dart_SetFormat"));
250
251 // If the DSOUND_DEVICE is set then use that number as the device for DART.
252 // this will allow people with many sound cards to use the card of there choice
253 // for an instance of a DSOUND enabled app!
254 if (getenv("DSOUND_DEVICE") != NULL) {
255 device = atoi(getenv( "DSOUND_DEVICE" ));
256 }
257
258 /* Dealloc to avoid the 5511 error */
259 rc = mciSendCommand(*pusDeviceID, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY,
260 BufferParms, 0);
261 if (rc != MCIERR_SUCCESS) {
262 dprintf(("DSOUND-DART: MCI_DEALLOCATE_MEMORY (SetFormat) %d", rc));
263 return DSERR_GENERIC;
264 }
265
266 rc = mciSendCommand(*pusDeviceID, MCI_CLOSE, MCI_WAIT, NULL, 0);
267 if (rc != MCIERR_SUCCESS) {
268 dprintf(("DSOUND-DART: MCI_CLOSE (SetFormat) %d", rc));
269 return(DSERR_GENERIC);
270 }
271
272 /* Reopen the MixAmp Device and start processing the buffer! */
273 memset(&AmpOpenParms,0,sizeof(MCI_AMP_OPEN_PARMS));
274 AmpOpenParms.usDeviceID = 0;
275 AmpOpenParms.pszDeviceType = (PSZ)MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX, (USHORT)device);
276
277 rc = mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_TYPE_ID, (PVOID)&AmpOpenParms, 0);
278 if (rc != MCIERR_SUCCESS) {
279 dprintf(("DSOUND-DART: MCI_OPEN %d", rc));
280 return DSERR_GENERIC;
281 }
282 *pusDeviceID = AmpOpenParms.usDeviceID;
283
284 /* setup playback parameters */
285 memset(MixSetup, 0, sizeof(MCI_MIXSETUP_PARMS));
286 MixSetup->ulBitsPerSample = lBPS;
287 MixSetup->ulFormatTag = MCI_WAVE_FORMAT_PCM;
288 MixSetup->ulSamplesPerSec = lSPS;
289 MixSetup->ulChannels = lChannels;
290 MixSetup->ulFormatMode = MCI_PLAY;
291 MixSetup->ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
292 MixSetup->pmixEvent = OS2_Dart_Update;
293
294 rc = mciSendCommand(*pusDeviceID, MCI_MIXSETUP, MCI_WAIT | MCI_MIXSETUP_INIT,
295 (PVOID)MixSetup, 0);
296 if (rc != MCIERR_SUCCESS) {
297 dprintf(("DSOUND-DART: MCI_MIXSETUP (SetFormat) %d", rc));
298 return DSERR_GENERIC;
299 }
300
301 memset(BufferParms, 0, sizeof(MCI_BUFFER_PARMS));
302 memset(pMixBuffers, 0, sizeof(MCI_MIX_BUFFER) * ulNumDartBuffs);
303 BufferParms->ulStructLength = sizeof(MCI_BUFFER_PARMS);
304 BufferParms->ulNumBuffers = ulNumDartBuffs;
305 BufferParms->ulBufferSize = BUFFER_SIZE/ulNumDartBuffs;
306 BufferParms->pBufList = pMixBuffers;
307 pMixBuffers->pBuffer = NULL;
308
309 rc = mciSendCommand(*pusDeviceID, MCI_BUFFER, MCI_WAIT | MCI_ALLOCATE_MEMORY,
310 (PVOID)BufferParms, 0);
311 if (rc != MCIERR_SUCCESS) {
312 dprintf(("DSOUND-DART: MCI_BUFFER_ALLOCATE_MEMORY (SetFormat) %d", rc));
313 mciSendCommand(*pusDeviceID, MCI_CLOSE, MCI_WAIT, NULL, 0);
314 memset(pMixBuffers, 0, sizeof(MCI_MIX_BUFFER) * ulNumDartBuffs);
315 return DSERR_OUTOFMEMORY;
316 }
317
318 /* Clear the Buffer */
319 // If the data is 8bit then set values to 127
320 // If the data is 16bit then set values to 32767
321 // Doing this will avoid the clicks at the beging of playback! :)
322 for (int i=0; i<ulNumDartBuffs; i++) {
323 memset(pMixBuffers[i].pBuffer, lBPS == 8 ? 127 : 32767, BUFFER_SIZE / ulNumDartBuffs);
324 }
325
326 lLastBuff = 0; /* we have to reset this, the number of buffers probably changed! */
327
328 /* If the primary buffer was playing, we have to restart it!! */
329 if (fIsPlaying) {
330 dprintf(("DSOUND-DART: Restarting playback!!!!"));
331
332 /* Mix the first buffer before playing */
333 MixCallback(BUFFER_SIZE/ulNumDartBuffs);
334 memcpy(pMixBuffers[lLastBuff].pBuffer, &pDSoundBuff[lLastBuff*(BUFFER_SIZE/ulNumDartBuffs)], BUFFER_SIZE/ulNumDartBuffs);
335
336 USHORT sel = RestoreOS2FS();
337 /* Note: the call to pmixWrite trashes the FS selector, we have to save */
338 /* and then restore FS!!! Otherwise exception handling will be broken. */
339 MixSetupParms.pmixWrite(MixSetupParms.ulMixHandle, pMixBuffers, 2);
340 SetFS(sel);
341 fIsPlaying = TRUE;
342 }
343
344 return DS_OK;
345}
346
347
348long Dart_Stop(USHORT usDeviceID)
349{
350 dprintf(("DSOUND-DART: Dart_Stop"));
351
352 if (!fIsPlaying)
353 return DS_OK;
354
355 fIsPlaying = FALSE;
356
357 // The OS2_Dart_Update function is now used to send the MCI_STOP!!
358 // Doing this fixes a bug where after a Dart_Stop call the sound would
359 // continue to loop because the OS2_Dart_Update would send the next
360 // buffer causing Dart to start again..
361
362 //rc = mciSendCommand(usDeviceID, MCI_STOP, MCI_WAIT, NULL, 0);
363 //if (rc != MCIERR_SUCCESS) {
364 // { FILE *dbf; dbf=fopen("log.log", "a"); fprintf( dbf, "Error in MCI_STOP...\n"); fclose(dbf); }
365 // dprintf(("DSOUND-DART: MCI_PAUSE %d", rc));
366 // return DSERR_GENERIC;
367 //}
368
369
370 return DS_OK;
371}
372
373long Dart_Play(USHORT usDeviceID, void *vpMixSetup, void *vpMixBuffer, long playing)
374{
375 ULONG rc;
376 MCI_MIXSETUP_PARMS *MixSetup;
377 MCI_MIX_BUFFER *MixBuffer;
378
379 MixSetup = (MCI_MIXSETUP_PARMS*)vpMixSetup;
380 MixBuffer = (MCI_MIX_BUFFER*)vpMixBuffer;
381
382 dprintf(("DSOUND-DART: Dart_Play"));
383
384 if (playing == TRUE)
385 {
386 rc = mciSendCommand(usDeviceID, MCI_RESUME, MCI_WAIT, NULL, 0);
387 if (rc != MCIERR_SUCCESS)
388 {
389 dprintf(("DSOUND-DART: MCI_RESUME %d", rc));
390 return DSERR_GENERIC;
391 }
392 }
393 else
394 { //if (playing==FALSE)
395 dprintf(("DSOUND-DART: Playback started!!!!"));
396
397 // verify if buffers have actually been allocated
398 // PH 2001-04-20 Power DVD 3 seens to come in here
399 // with ulNumDartBuffs == 0 and also pmixWrite==NULL.
400 // However pmixEvent is != NULL. So I assume (!)
401 // this might fix the cause.
402 //
403 // Does fix the crash actually. I suspect DART cannot
404 // deliver mixing functions for the requested sound format:
405 // 16-Bit, 4-channel?, 48kHz.
406 if( (MixSetup->pmixWrite != NULL) &&
407 (MixSetup->ulNumBuffers > 0) )
408 {
409
410 /* Mix the first buffer before playing */
411 MixCallback(BUFFER_SIZE/ulNumDartBuffs);
412 memcpy(MixBuffer[lLastBuff].pBuffer, &pDSoundBuff[lLastBuff*(BUFFER_SIZE/ulNumDartBuffs)], BUFFER_SIZE/ulNumDartBuffs);
413
414 USHORT sel = RestoreOS2FS();
415 /* Note: the call to pmixWrite trashes the FS selector, we have to save */
416 /* and then restore FS!!! Otherwise exception handling will be broken. */
417 MixSetupParms.pmixWrite(MixSetupParms.ulMixHandle, MixBuffer, 2);
418 SetFS(sel);
419 fIsPlaying = TRUE;
420 }
421 }
422
423 return DS_OK;
424}
Note: See TracBrowser for help on using the repository browser.