1
/* OS/2 RealTime DART Engine for mpg123 (C) 1998 Samuel Audet <guardia@cam.org> */
12
#ifndef MPG123_INCLUDED
16
/* complementary audio parameters */
17
int numbuffers = 32; /* total audio buffers, _bare_ minimum = 4 (cuz of prio boost check) */
18
int lockdevice = FALSE;
20
char *boostprio = NULL;
21
char *normalprio = NULL;
22
unsigned char boostclass = 3, normalclass = 2;
23
signed char boostdelta = 0, normaldelta = 31;
24
unsigned char mmerror[160] = {0};
28
static ULONG ulMCIBuffers;
30
static MCI_AMP_OPEN_PARMS maop = {0};
31
static MCI_MIXSETUP_PARMS mmp = {0};
32
static MCI_BUFFER_PARMS mbp = {0};
33
static MCI_GENERIC_PARMS mgp = {0};
34
static MCI_SET_PARMS msp = {0};
35
static MCI_STATUS_PARMS mstatp = {0};
36
static MCI_MIX_BUFFER *MixBuffers = NULL;
40
MCI_MIX_BUFFER *NextBuffer;
44
BUFFERINFO *bufferinfo = NULL;
47
static HEV dataplayed = 0;
48
static ULONG resetcount;
49
static BOOL paused = FALSE;
51
static MCI_MIX_BUFFER *tobefilled, *playingbuffer = NULL, playedbuffer;
52
static void *pBufferplayed;
54
static BOOL nomoredata,nobuffermode,justflushed;
56
static TIB *mainthread; /* thread info to set thread priority */
61
static LONG APIENTRY DARTEvent(ULONG ulStatus, MCI_MIX_BUFFER *PlayedBuffer, ULONG ulFlags)
65
case MIX_STREAM_ERROR | MIX_WRITE_COMPLETE: /* error occur in device */
67
if ( ulStatus == ERROR_DEVICE_UNDERRUN)
68
/* Write buffers to rekick off the amp mixer. */
69
mmp.pmixWrite( mmp.ulMixHandle,
74
case MIX_WRITE_COMPLETE: /* for playback */
76
playingbuffer = ((BUFFERINFO *) PlayedBuffer->ulUserParm)->NextBuffer;
78
/* the next three lines are only useful to audio_playing_samples() */
79
playedbuffer = *PlayedBuffer;
80
playedbuffer.pBuffer = pBufferplayed;
81
memcpy(playedbuffer.pBuffer, PlayedBuffer->pBuffer, PlayedBuffer->ulBufferLength);
83
/* just too bad, the decoder fell behind... here we just keep the
84
buffer to be filled in front of the playing one so that when the
85
decoder kicks back in, we'll hear it in at the right time */
86
if(tobefilled == playingbuffer)
88
tobefilled = ((BUFFERINFO *) playingbuffer->ulUserParm)->NextBuffer;
93
playingframe = ((BUFFERINFO *) playingbuffer->ulUserParm)->frameNum;
95
/* if we're about to be short of decoder's data
96
(2nd ahead buffer not filled), let's boost its priority! */
97
if(tobefilled == ( (BUFFERINFO *) ((BUFFERINFO *) playingbuffer->ulUserParm)->NextBuffer->ulUserParm)->NextBuffer)
98
DosSetPriority(PRTYS_THREAD,boostclass,boostdelta,mainthread->tib_ptib2->tib2_ultid);
101
/* empty the played buffer in case it doesn't get filled back */
102
memset(PlayedBuffer->pBuffer,0,PlayedBuffer->ulBufferLength);
104
DosPostEventSem(dataplayed);
106
mmp.pmixWrite( mmp.ulMixHandle,
107
PlayedBuffer /* will contain new data */,
115
} /* end DARTEvent */
118
static void MciError(ULONG ulError)
120
unsigned char buffer[128];
123
rc = mciGetErrorString(ulError, buffer, sizeof(buffer));
125
if (rc == MCIERR_SUCCESS)
126
sprintf(mmerror,"MCI Error %d: %s\n",ULONG_LOWD(ulError),buffer);
128
sprintf(mmerror,"MCI Error %d: Cannot query error message.\n",ULONG_LOWD(rc));
130
fprintf(stderr,"%s",mmerror);
133
int audio_set_volume(struct audio_info_struct *ai, USHORT setvolume)
135
if(setvolume > 100) setvolume = 100;
136
volume = setvolume; /* useful when device is closed and reopened */
139
memset(&msp,0,sizeof(msp));
140
msp.ulAudio = MCI_SET_AUDIO_ALL;
141
msp.ulLevel = setvolume;
143
mciSendCommand(maop.usDeviceID, MCI_SET,
144
MCI_WAIT | MCI_SET_AUDIO | MCI_SET_VOLUME,
150
int audio_pause(struct audio_info_struct *ai, int pause)
155
mciSendCommand(maop.usDeviceID, MCI_PAUSE,
159
mciSendCommand(maop.usDeviceID, MCI_RESUME,
166
int audio_open(struct audio_info_struct *ai)
174
if(maop.usDeviceID) return (maop.usDeviceID);
178
if(!ai->device) ai->device = "0";
180
if(ai->rate < 0) ai->rate = 44100;
181
if(ai->channels < 0) ai->channels = 2;
182
if(ai->format < 0) ai->format = AUDIO_FORMAT_SIGNED_16;
184
if(ai->format == AUDIO_FORMAT_SIGNED_16)
186
else if(ai->format == AUDIO_FORMAT_UNSIGNED_8)
190
/* open the mixer device */
191
memset (&maop, 0, sizeof(maop));
193
maop.pszDeviceType = (PSZ) MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX, atoi(ai->device));
195
openflags = MCI_WAIT | MCI_OPEN_TYPE_ID;
196
if(!lockdevice) openflags |= MCI_OPEN_SHAREABLE;
198
rc = mciSendCommand(0,
204
if (ULONG_LOWD(rc) != MCIERR_SUCCESS)
211
/* volume in ai->gain ?? */
213
/* Set the MCI_MIXSETUP_PARMS data structure to match the audio stream. */
215
memset(&mmp, 0, sizeof(mmp));
217
mmp.ulBitsPerSample = bits;
218
mmp.ulFormatTag = MCI_WAVE_FORMAT_PCM;
219
mmp.ulSamplesPerSec = ai->rate;
220
mmp.ulChannels = ai->channels;
222
/* Setup the mixer for playback of wave data */
223
mmp.ulFormatMode = MCI_PLAY;
224
mmp.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
225
mmp.pmixEvent = DARTEvent;
227
rc = mciSendCommand( maop.usDeviceID,
229
MCI_WAIT | MCI_MIXSETUP_INIT,
233
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
240
volume = audio_set_volume(ai,volume);
242
/* Set up the BufferParms data structure and allocate
243
* device buffers from the Amp-Mixer */
245
memset(&mbp, 0, sizeof(mbp));
248
if(numbuffers < 5) numbuffers = 5;
249
if(numbuffers > 200) numbuffers = 200;
250
MixBuffers = calloc(numbuffers, sizeof(*MixBuffers));
251
bufferinfo = calloc(numbuffers, sizeof(*bufferinfo));
253
ulMCIBuffers = numbuffers;
254
mbp.ulNumBuffers = ulMCIBuffers;
255
/* mbp.ulBufferSize = mmp.ulBufferSize; */
256
/* I don't like this... they must be smaller than 64KB or else the
257
engine needs major rewrite */
258
mbp.ulBufferSize = audiobufsize;
259
mbp.pBufList = MixBuffers;
261
rc = mciSendCommand( maop.usDeviceID,
263
MCI_WAIT | MCI_ALLOCATE_MEMORY,
267
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
274
pBufferplayed = playedbuffer.pBuffer = calloc(1,audiobufsize);
276
ulMCIBuffers = mbp.ulNumBuffers; /* never know! */
278
/* Fill all device buffers with zeros and set linked list */
280
for(i = 0; i < ulMCIBuffers; i++)
282
MixBuffers[i].ulFlags = 0;
283
MixBuffers[i].ulBufferLength = mbp.ulBufferSize;
284
memset(MixBuffers[i].pBuffer, 0, MixBuffers[i].ulBufferLength);
286
MixBuffers[i].ulUserParm = (ULONG) &bufferinfo[i];
287
bufferinfo[i].NextBuffer = &MixBuffers[i+1];
290
bufferinfo[i-1].NextBuffer = &MixBuffers[0];
292
/* Create a semaphore to know when data has been played by the DART thread */
293
DosCreateEventSem(NULL,&dataplayed,0,FALSE);
295
playingbuffer = &MixBuffers[0];
296
tobefilled = &MixBuffers[1];
299
nobuffermode = FALSE;
304
temp = alloca(strlen(boostprio)+1);
305
strcpy(temp,boostprio);
307
boostdelta = atoi(temp+1);
309
boostclass = atoi(temp);
311
if(boostclass > 4) boostdelta = 3;
312
if(boostdelta > 31) boostdelta = 31;
313
if(boostdelta < -31) boostdelta = -31;
318
temp = alloca(strlen(normalprio)+1);
319
strcpy(temp,normalprio);
321
normaldelta = atoi(temp+1);
323
normalclass = atoi(temp);
325
if(normalclass > 4) normaldelta = 3;
326
if(normaldelta > 31) normaldelta = 31;
327
if(normaldelta < -31) normaldelta = -31;
330
DosGetInfoBlocks(&mainthread,&ppib); /* ppib not needed, but makes some DOSCALLS.DLL crash */
331
DosSetPriority(PRTYS_THREAD,boostclass,boostdelta,mainthread->tib_ptib2->tib2_ultid);
333
/* Write buffers to kick off the amp mixer. see DARTEvent() */
334
rc = mmp.pmixWrite( mmp.ulMixHandle,
338
return maop.usDeviceID;
341
int audio_play_samples(struct audio_info_struct *ai,unsigned char *buf,int len)
343
/* if we're too quick, let's wait */
346
MCI_MIX_BUFFER *temp = playingbuffer;
349
(tobefilled != (temp = ((BUFFERINFO *) temp->ulUserParm)->NextBuffer)) &&
350
(tobefilled != (temp = ((BUFFERINFO *) temp->ulUserParm)->NextBuffer)) &&
351
(tobefilled != (temp = ((BUFFERINFO *) temp->ulUserParm)->NextBuffer)) )
353
DosResetEventSem(dataplayed,&resetcount);
354
DosWaitEventSem(dataplayed, -1);
355
temp = playingbuffer;
359
while(tobefilled == playingbuffer)
361
DosResetEventSem(dataplayed,&resetcount);
362
DosWaitEventSem(dataplayed, -1);
371
memcpy(tobefilled->pBuffer, buf, len);
372
tobefilled->ulBufferLength = len;
373
// ((BUFFERINFO *) tobefilled->ulUserParm)->frameNum = fr->frameNum;
375
/* if we're out of the water (3rd ahead buffer filled),
376
let's reduce our priority */
377
if(tobefilled == ( (BUFFERINFO *) ( (BUFFERINFO *) ((BUFFERINFO *) playingbuffer->ulUserParm)->NextBuffer->ulUserParm)->NextBuffer->ulUserParm)->NextBuffer)
378
DosSetPriority(PRTYS_THREAD,normalclass,normaldelta,mainthread->tib_ptib2->tib2_ultid);
380
tobefilled = ((BUFFERINFO *) tobefilled->ulUserParm)->NextBuffer;
386
int audio_playing_samples(struct audio_info_struct *ai,unsigned char *buf,int len)
388
if(len > audiobufsize || !playingbuffer) return -1;
390
if(mmp.ulBitsPerSample == 16)
391
ai->format = AUDIO_FORMAT_SIGNED_16;
392
else if(mmp.ulBitsPerSample == 8)
393
ai->format = AUDIO_FORMAT_UNSIGNED_8;
396
ai->rate = mmp.ulSamplesPerSec;
397
ai->channels = mmp.ulChannels;
404
mstatp.ulItem = MCI_STATUS_POSITION;
406
rc = mciSendCommand( maop.usDeviceID,
408
MCI_STATUS_ITEM | MCI_WAIT,
412
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
419
/* this is hypocrite...
420
DART returns the value in ulReturn instead of ulValue,
421
also it returns in milliseconds and not MMTIME... arg */
423
upto = (mstatp.ulReturn-playedbuffer.ulTime) * mmp.ulSamplesPerSec / 1000;
424
upto *= mmp.ulChannels * (mmp.ulBitsPerSample>>3);
426
/* if a timing problem occurs, let's at least not crash */
427
if(upto > playingbuffer->ulBufferLength) upto = playingbuffer->ulBufferLength;
430
memcpy(buf,(char *) (playingbuffer->pBuffer)+upto-len, len);
433
memcpy(buf,(char *) playedbuffer.pBuffer+playedbuffer.ulBufferLength-(len-upto),len-upto);
434
memcpy(buf+(len-upto),playingbuffer->pBuffer,upto);
441
int audio_nobuffermode(struct audio_info_struct *ai, int setnobuffermode)
443
nobuffermode = setnobuffermode;
447
int audio_trash_buffers(struct audio_info_struct *ai)
453
/* Fill all device buffers with zeros */
454
for(i = 0; i < ulMCIBuffers; i++)
455
memset(MixBuffers[i].pBuffer, 0, MixBuffers[i].ulBufferLength);
457
tobefilled = ((BUFFERINFO *) playingbuffer->ulUserParm)->NextBuffer;
463
int audio_close(struct audio_info_struct *ai)
472
DosResetEventSem(dataplayed,&resetcount);
473
DosWaitEventSem(dataplayed, -1);
476
playingbuffer = NULL;
478
DosCloseEventSem(dataplayed);
483
rc = mciSendCommand( maop.usDeviceID,
485
MCI_WAIT | MCI_DEALLOCATE_MEMORY,
489
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
500
memset(&mbp, 0, sizeof(mbp));
502
rc = mciSendCommand( maop.usDeviceID,
508
if ( ULONG_LOWD(rc) != MCIERR_SUCCESS )
514
memset(&maop, 0, sizeof(maop));
520
* get formats for specific channel/rate parameters
522
int audio_get_formats(struct audio_info_struct *ai)
526
MCI_MIXSETUP_PARMS mmptemp = {0};
528
mmp.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
529
mmp.pmixEvent = DARTEvent;
531
mmptemp.ulFormatMode = MCI_PLAY;
532
mmptemp.ulSamplesPerSec = ai->rate;
533
mmptemp.ulChannels = ai->channels;
535
mmptemp.ulFormatTag = MCI_WAVE_FORMAT_PCM;
536
mmptemp.ulBitsPerSample = 16;
537
rc = mciSendCommand( maop.usDeviceID,
539
MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
542
if((ULONG_LOWD(rc) == MCIERR_SUCCESS) && (rc != 0x4000)) /* undocumented */
543
fmts = fmts | AUDIO_FORMAT_SIGNED_16;
545
mmptemp.ulFormatTag = MCI_WAVE_FORMAT_PCM;
546
mmptemp.ulBitsPerSample = 8;
547
rc = mciSendCommand( maop.usDeviceID,
549
MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
552
if((ULONG_LOWD(rc) == MCIERR_SUCCESS) && (rc != 0x4000)) /* undocumented */
553
fmts = fmts | AUDIO_FORMAT_UNSIGNED_8;
555
mmptemp.ulFormatTag = MCI_WAVE_FORMAT_ALAW;
556
mmptemp.ulBitsPerSample = 8;
557
rc = mciSendCommand( maop.usDeviceID,
559
MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
562
if((ULONG_LOWD(rc) == MCIERR_SUCCESS) && (rc != 0x4000)) /* undocumented */
563
fmts = fmts | AUDIO_FORMAT_ALAW_8;
565
mmptemp.ulFormatTag = MCI_WAVE_FORMAT_MULAW;
566
mmptemp.ulBitsPerSample = 8;
567
rc = mciSendCommand( maop.usDeviceID,
569
MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
572
if((ULONG_LOWD(rc) == MCIERR_SUCCESS) && (rc != 0x4000)) /* undocumented */
573
fmts = fmts | AUDIO_FORMAT_ULAW_8;
578
int audio_rate_best_match(struct audio_info_struct *ai)
583
int audio_get_devices(char *info, int deviceid)
586
MCI_SYSINFO_PARMS mip;
590
MCI_SYSINFO_LOGDEVICE mid;
592
mip.pszReturn = buffer;
593
mip.ulRetSize = sizeof(buffer);
594
mip.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
595
mip.ulNumber = deviceid;
599
MCI_WAIT | MCI_SYSINFO_INSTALLNAME,
603
mip.ulItem = MCI_SYSINFO_QUERY_DRIVER;
604
mip.pSysInfoParm = ∣
605
strcpy(mid.szInstallName,buffer);
609
MCI_WAIT | MCI_SYSINFO_ITEM,
613
strcpy(info,mid.szProductInfo);
620
mip.pszReturn = buffer;
621
mip.ulRetSize = sizeof(buffer);
622
mip.usDeviceType = MCI_DEVTYPE_AUDIO_AMPMIX;
626
MCI_WAIT | MCI_SYSINFO_QUANTITY,
630
number = atoi(mip.pszReturn);