319 lines
8.2 KiB
C
Executable File
319 lines
8.2 KiB
C
Executable File
/* MikMod sound library
|
|
(c) 1998, 1999, 2000 Miodrag Vallat and others - see file AUTHORS for
|
|
complete list.
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Library General Public License as
|
|
published by the Free Software Foundation; either version 2 of
|
|
the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
02111-1307, USA.
|
|
*/
|
|
|
|
/*==============================================================================
|
|
|
|
$Id: drv_dart.c,v 1.2 2004/01/31 22:39:40 raph Exp $
|
|
|
|
Driver for output on OS/2 MMPM/2 using direct audio (DART)
|
|
|
|
==============================================================================*/
|
|
|
|
/*
|
|
|
|
Written by Miodrag Vallat <miod@mikmod.org>
|
|
Lots of improvements (basically made usable...) by Andrew Zabolotny
|
|
<bit@eltech.ru>
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "mikmod_internals.h"
|
|
|
|
#ifdef DRV_DART
|
|
|
|
#define INCL_DOS
|
|
#define INCL_OS2MM
|
|
#include <os2.h>
|
|
/* Prevent a warning: PPFN redefined */
|
|
#define PPFN _PPFN
|
|
#include <os2me.h>
|
|
#undef PPFN
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define MAX_BUFFERCOUNT 8
|
|
|
|
static MCI_MIX_BUFFER MixBuffers[MAX_BUFFERCOUNT];
|
|
static MCI_MIXSETUP_PARMS MixSetupParms;
|
|
static MCI_BUFFER_PARMS BufferParms;
|
|
static ULONG DeviceIndex = 0; /* use default waveaudio device */
|
|
static ULONG BufferSize = (ULONG)-1; /* autodetect buffer size */
|
|
static ULONG BufferCount = 2; /* use two buffers */
|
|
static ULONG DeviceID = 0;
|
|
|
|
static void Dart_CommandLine(CHAR *cmdline)
|
|
{
|
|
char *ptr;
|
|
int buf;
|
|
|
|
if ((ptr = MD_GetAtom("buffer", cmdline, 0))) {
|
|
buf = atoi(ptr);
|
|
if (buf >= 12 && buf <= 16)
|
|
BufferSize = 1 << buf;
|
|
free(ptr);
|
|
}
|
|
|
|
if ((ptr = MD_GetAtom("count", cmdline, 0))) {
|
|
buf = atoi(ptr);
|
|
if (buf >= 2 && buf <= MAX_BUFFERCOUNT)
|
|
BufferCount = buf;
|
|
free(ptr);
|
|
}
|
|
|
|
if ((ptr = MD_GetAtom("device", cmdline, 0))) {
|
|
buf = atoi(ptr);
|
|
if (buf >= 0 && buf <= 8)
|
|
DeviceIndex = buf;
|
|
free(ptr);
|
|
}
|
|
}
|
|
|
|
/* Buffer update thread (created and called by DART)
|
|
This is a high-priority thread used to compute and update the audio stream,
|
|
automatically created by the DART subsystem. We compute the next audio
|
|
buffer and feed it to the waveaudio device. */
|
|
static LONG APIENTRY Dart_UpdateBuffers(ULONG ulStatus, PMCI_MIX_BUFFER pBuffer, ULONG ulFlags)
|
|
{
|
|
/* sanity check */
|
|
if (!pBuffer)
|
|
return TRUE;
|
|
|
|
/* if we have finished a buffer, we're ready to play a new one */
|
|
if ((ulFlags == MIX_WRITE_COMPLETE) ||
|
|
((ulFlags == (MIX_WRITE_COMPLETE | MIX_STREAM_ERROR)) &&
|
|
(ulStatus == ERROR_DEVICE_UNDERRUN))) {
|
|
/* refill this audio buffer and feed it again */
|
|
MUTEX_LOCK(vars);
|
|
if (Player_Paused_internal())
|
|
pBuffer->ulBufferLength =
|
|
VC_SilenceBytes(pBuffer->pBuffer, BufferSize);
|
|
else
|
|
pBuffer->ulBufferLength =
|
|
VC_WriteBytes(pBuffer->pBuffer, BufferSize);
|
|
MUTEX_UNLOCK(vars);
|
|
MixSetupParms.pmixWrite(MixSetupParms.ulMixHandle, pBuffer, 1);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL Dart_IsPresent(void)
|
|
{
|
|
MCI_AMP_OPEN_PARMS AmpOpenParms;
|
|
MCI_GENERIC_PARMS GenericParms;
|
|
|
|
memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS));
|
|
AmpOpenParms.pszDeviceType = (PSZ) MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX,
|
|
(USHORT) DeviceIndex);
|
|
|
|
if (mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID,
|
|
(PVOID) &AmpOpenParms, 0) != MCIERR_SUCCESS)
|
|
return 0;
|
|
|
|
mciSendCommand(AmpOpenParms.usDeviceID, MCI_CLOSE, MCI_WAIT,
|
|
(PVOID) &GenericParms, 0);
|
|
return 1;
|
|
}
|
|
|
|
static BOOL Dart_Init(void)
|
|
{
|
|
MCI_AMP_OPEN_PARMS AmpOpenParms;
|
|
MCI_GENERIC_PARMS GenericParms;
|
|
|
|
MixBuffers[0].pBuffer = NULL; /* marker */
|
|
DeviceID = 0;
|
|
memset(&GenericParms, 0, sizeof(MCI_GENERIC_PARMS));
|
|
|
|
/* open AMP device */
|
|
memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS));
|
|
AmpOpenParms.usDeviceID = 0;
|
|
AmpOpenParms.pszDeviceType = (PSZ) MAKEULONG(MCI_DEVTYPE_AUDIO_AMPMIX,
|
|
(USHORT) DeviceIndex);
|
|
|
|
|
|
if (mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_SHAREABLE | MCI_OPEN_TYPE_ID,
|
|
(PVOID) & AmpOpenParms, 0) != MCIERR_SUCCESS) {
|
|
_mm_errno = MMERR_OPENING_AUDIO;
|
|
return 1;
|
|
}
|
|
|
|
DeviceID = AmpOpenParms.usDeviceID;
|
|
|
|
/* setup playback parameters */
|
|
memset(&MixSetupParms, 0, sizeof(MCI_MIXSETUP_PARMS));
|
|
MixSetupParms.ulBitsPerSample = (md_mode & DMODE_16BITS) ? 16 : 8;
|
|
MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM;
|
|
MixSetupParms.ulSamplesPerSec = md_mixfreq;
|
|
MixSetupParms.ulChannels = (md_mode & DMODE_STEREO) ? 2 : 1;
|
|
MixSetupParms.ulFormatMode = MCI_PLAY;
|
|
MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
|
|
MixSetupParms.pmixEvent = Dart_UpdateBuffers;
|
|
|
|
if (mciSendCommand(DeviceID, MCI_MIXSETUP,
|
|
MCI_WAIT | MCI_MIXSETUP_INIT,
|
|
(PVOID) & MixSetupParms, 0) != MCIERR_SUCCESS) {
|
|
_mm_errno = MMERR_OS2_MIXSETUP;
|
|
return 1;
|
|
}
|
|
|
|
/* Compute audio buffer size now if necessary */
|
|
if (BufferSize == (ULONG)-1) {
|
|
/* DART suggested buffer size is somewhat too big. We compute a size
|
|
for circa 1/4" of playback. */
|
|
int bit;
|
|
|
|
BufferSize = md_mixfreq >> 2;
|
|
if (md_mode & DMODE_STEREO)
|
|
BufferSize <<= 1;
|
|
if (md_mode & DMODE_16BITS)
|
|
BufferSize <<= 1;
|
|
for (bit = 15; bit >= 12; bit--)
|
|
if (BufferSize & (1 << bit))
|
|
break;
|
|
BufferSize = (1 << bit);
|
|
}
|
|
/* make sure buffer is not greater than 64 Kb, as DART can't handle this
|
|
situation. */
|
|
if (BufferSize > 65536)
|
|
BufferSize = 65536;
|
|
|
|
BufferParms.ulStructLength = sizeof(BufferParms);
|
|
BufferParms.ulNumBuffers = BufferCount;
|
|
BufferParms.ulBufferSize = BufferSize;
|
|
BufferParms.pBufList = MixBuffers;
|
|
|
|
if (mciSendCommand(DeviceID, MCI_BUFFER,
|
|
MCI_WAIT | MCI_ALLOCATE_MEMORY,
|
|
(PVOID) &BufferParms, 0) != MCIERR_SUCCESS) {
|
|
_mm_errno = MMERR_OUT_OF_MEMORY;
|
|
return 1;
|
|
}
|
|
|
|
return VC_Init();
|
|
}
|
|
|
|
static void Dart_Exit(void)
|
|
{
|
|
MCI_GENERIC_PARMS GenericParms;
|
|
|
|
VC_Exit();
|
|
if (MixBuffers[0].pBuffer) {
|
|
mciSendCommand(DeviceID, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY,
|
|
&BufferParms, 0);
|
|
MixBuffers[0].pBuffer = NULL;
|
|
}
|
|
if (DeviceID) {
|
|
mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, (PVOID) &GenericParms, 0);
|
|
DeviceID = 0;
|
|
}
|
|
}
|
|
|
|
static BOOL Dart_PlayStart(void)
|
|
{
|
|
MCI_GENERIC_PARMS GenericParms;
|
|
int i;
|
|
|
|
if (VC_PlayStart())
|
|
return 1;
|
|
|
|
/* silence buffers */
|
|
for (i = 0; i < BufferCount; i++) {
|
|
VC_SilenceBytes(MixBuffers[i].pBuffer, BufferSize);
|
|
MixBuffers[i].ulBufferLength = BufferSize;
|
|
}
|
|
|
|
/* grab exclusive rights to device instance (not entire device) */
|
|
GenericParms.hwndCallback = 0; /* Not needed, so set to 0 */
|
|
if (mciSendCommand(DeviceID, MCI_ACQUIREDEVICE, MCI_EXCLUSIVE_INSTANCE,
|
|
(PVOID) &GenericParms, 0) != MCIERR_SUCCESS) {
|
|
mciSendCommand(DeviceID, MCI_CLOSE, MCI_WAIT, (PVOID) &GenericParms, 0);
|
|
_mm_errno = MMERR_OPENING_AUDIO;
|
|
return 1;
|
|
}
|
|
|
|
/* wake DART up */
|
|
MixSetupParms.pmixWrite(MixSetupParms.ulMixHandle, MixBuffers, BufferCount);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void Dart_Update(void)
|
|
{
|
|
/* Nothing to do: everything is done in DART_UpdateBuffers() */
|
|
}
|
|
|
|
static void Dart_PlayStop(void)
|
|
{
|
|
MCI_GENERIC_PARMS GenericParms;
|
|
|
|
GenericParms.hwndCallback = 0;
|
|
mciSendCommand(DeviceID, MCI_STOP, MCI_WAIT, (PVOID) &GenericParms, 0);
|
|
|
|
VC_PlayStop();
|
|
}
|
|
|
|
MIKMODAPI MDRIVER drv_dart = {
|
|
NULL,
|
|
"Direct audio (DART)",
|
|
"OS/2 DART driver v1.2",
|
|
0, 255,
|
|
"dart",
|
|
"device:r:0,8,0:Waveaudio device index to use (0 - default)\n"
|
|
"buffer:r:12,16:Audio buffer log2 size\n"
|
|
"count:r:2,8,2:Number of audio buffers\n",
|
|
Dart_CommandLine,
|
|
Dart_IsPresent,
|
|
VC_SampleLoad,
|
|
VC_SampleUnload,
|
|
VC_SampleSpace,
|
|
VC_SampleLength,
|
|
Dart_Init,
|
|
Dart_Exit,
|
|
NULL,
|
|
VC_SetNumVoices,
|
|
Dart_PlayStart,
|
|
Dart_PlayStop,
|
|
Dart_Update,
|
|
NULL,
|
|
VC_VoiceSetVolume,
|
|
VC_VoiceGetVolume,
|
|
VC_VoiceSetFrequency,
|
|
VC_VoiceGetFrequency,
|
|
VC_VoiceSetPanning,
|
|
VC_VoiceGetPanning,
|
|
VC_VoicePlay,
|
|
VC_VoiceStop,
|
|
VC_VoiceStopped,
|
|
VC_VoiceGetPosition,
|
|
VC_VoiceRealVolume
|
|
};
|
|
|
|
#else
|
|
|
|
MISSING(drv_dart);
|
|
|
|
#endif
|
|
|
|
/* ex:set ts=4: */
|