910 lines
24 KiB
C

/*
* $Id: mixdrv.c 1.13 1996/09/08 21:14:54 chasan released $
* 1.14 1998/10/24 18:20:53 chasan released (Mixer API)
*
* Software-based waveform synthesizer emulator driver.
*
* Copyright (C) 1995-1999 Carlos Hasan
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#define __FILTER__
#ifdef __GNUC__
#include <memory.h>
#define cdecl
#endif
#include <string.h>
#include <stdlib.h>
#include "audio.h"
#include "drivers.h"
/*
* Voice control bit fields
*/
#define VOICE_STOP 0x01
#define VOICE_16BITS 0x02
#define VOICE_LOOP 0x04
#define VOICE_BIDILOOP 0x08
#define VOICE_REVERSE 0x10
/*
* Voice pitch accurary and mixing buffer size
*/
#define ACCURACY 12
#define BUFFERSIZE 512
/*
* Waveform synthesizer voice structure
*/
typedef struct {
LPVOID lpData;
LONG dwAccum;
LONG dwFrequency;
LONG dwLoopStart;
LONG dwLoopEnd;
BYTE nVolume;
BYTE nPanning;
BYTE bControl;
BYTE bReserved;
} VOICE, *LPVOICE;
/*
* Low level voice mixing routine prototype
*/
typedef VOID (cdecl* LPFNMIXAUDIO)(LPLONG, UINT, LPVOICE);
/*
* Waveform synthesizer state structure
*/
static struct {
VOICE aVoices[AUDIO_MAX_VOICES];
UINT nVoices;
UINT wFormat;
UINT nSampleRate;
LONG dwTimerRate;
LONG dwTimerAccum;
LPBYTE lpMemory;
LPFNAUDIOTIMER lpfnAudioTimer;
LPFNMIXAUDIO lpfnMixAudioProc[2];
} Synth;
LPLONG lpVolumeTable;
LPBYTE lpFilterTable;
static VOID AIAPI UpdateVoices(LPBYTE lpData, UINT nCount);
/* low level resamplation and quantization routines */
#ifdef __ASM__
VOID cdecl QuantAudioData08(LPVOID lpBuffer, LPLONG lpData, UINT nCount);
VOID cdecl QuantAudioData16(LPVOID lpBuffer, LPLONG lpData, UINT nCount);
VOID cdecl MixAudioData08M(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice);
VOID cdecl MixAudioData08S(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice);
VOID cdecl MixAudioData08MI(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice);
VOID cdecl MixAudioData08SI(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice);
VOID cdecl MixAudioData16M(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice);
VOID cdecl MixAudioData16S(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice);
VOID cdecl MixAudioData16MI(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice);
VOID cdecl MixAudioData16SI(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice);
static VOID QuantAudioData(LPVOID lpBuffer, LPLONG lpData, UINT nCount)
{
if (Synth.wFormat & AUDIO_FORMAT_16BITS)
QuantAudioData16(lpBuffer, lpData, nCount);
else
QuantAudioData08(lpBuffer, lpData, nCount);
}
#else
typedef short SHORT;
typedef SHORT* LPSHORT;
static VOID QuantAudioData(LPVOID lpBuffer, LPLONG lpData, UINT nCount)
{
LPSHORT lpwBuffer;
LPBYTE lpbBuffer;
LONG dwSample;
if (Synth.wFormat & AUDIO_FORMAT_16BITS) {
lpwBuffer = (LPSHORT) lpBuffer;
while (nCount-- > 0) {
dwSample = *lpData++;
if (dwSample < -32768)
dwSample = -32768;
else if (dwSample > +32767)
dwSample = +32767;
*lpwBuffer++ = (SHORT) dwSample;
}
}
else {
lpbBuffer = (LPBYTE) lpBuffer;
while (nCount-- > 0) {
dwSample = *lpData++;
if (dwSample < -32768)
dwSample = -32768;
else if (dwSample > +32767)
dwSample = +32767;
*lpbBuffer++ = (BYTE) ((dwSample >> 8) + 128);
}
}
}
static VOID AIAPI
MixAudioData08M(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice)
{
register UINT count;
register DWORD accum, delta;
register LPLONG table, buf;
register LPBYTE data;
accum = lpVoice->dwAccum;
delta = lpVoice->dwFrequency;
table = lpVolumeTable + ((UINT) lpVoice->nVolume << 8);
data = lpVoice->lpData;
buf = lpBuffer;
count = nCount;
do {
*buf++ += table[data[accum >> ACCURACY]];
accum += delta;
} while (--count != 0);
lpVoice->dwAccum = accum;
}
#define min(a, b) ((a) < (b) ? (a) : (b))
static VOID AIAPI
MixAudioData08S(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice)
{
register UINT a;
register DWORD accum, delta;
register LPLONG ltable, rtable, buf;
register LPBYTE data;
accum = lpVoice->dwAccum;
delta = lpVoice->dwFrequency;
a = ((UINT) lpVoice->nVolume * lpVoice->nPanning) >> 8;
rtable = lpVolumeTable + (a << 8);
ltable = lpVolumeTable + ((lpVoice->nVolume - a) << 8);
data = lpVoice->lpData;
buf = lpBuffer;
do {
a = data[accum >> ACCURACY];
buf[0] += ltable[a];
buf[1] += rtable[a];
accum += delta;
buf += 2;
} while (--nCount != 0);
lpVoice->dwAccum = accum;
}
static VOID AIAPI
MixAudioData08MI(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice)
{
register INT a, frac;
register DWORD accum, delta;
register LPLONG table, buf;
register LPBYTE data, ftable;
/* adaptive oversampling interpolation comparison */
if (lpVoice->dwFrequency < -(1 << ACCURACY) ||
lpVoice->dwFrequency > +(1 << ACCURACY)) {
MixAudioData08M(lpBuffer, nCount, lpVoice);
return;
}
accum = lpVoice->dwAccum;
delta = lpVoice->dwFrequency;
table = lpVolumeTable + ((UINT) lpVoice->nVolume << 8);
data = lpVoice->lpData;
buf = lpBuffer;
#ifdef __FILTER__
a = (BYTE) lpVoice->bReserved;
frac = ((long) delta < 0 ? -delta : +delta) >> (ACCURACY - 5);
ftable = lpFilterTable + (frac << 8);
do {
a = (BYTE)(a + ftable[data[accum >> ACCURACY]] - ftable[a]);
*buf++ += table[a];
accum += delta;
} while (--nCount != 0);
lpVoice->bReserved = (BYTE) a;
#else
do {
register INT b;
a = (signed char) data[accum >> ACCURACY];
b = (signed char) data[(accum >> ACCURACY) + 1];
frac = (accum & ((1 << ACCURACY) - 1)) >> (ACCURACY - 5);
a = (BYTE)(a + ((frac * (b - a)) >> 5));
*buf++ += table[a];
accum += delta;
} while (--nCount != 0);
#endif
lpVoice->dwAccum = accum;
}
static VOID AIAPI
MixAudioData08SI(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice)
{
register INT a, frac;
register DWORD accum, delta;
register LPLONG buf, ltable, rtable;
register LPBYTE data, ftable;
/* adaptive oversampling interpolation comparison */
if (lpVoice->dwFrequency < -(1 << ACCURACY) ||
lpVoice->dwFrequency > +(1 << ACCURACY)) {
MixAudioData08S(lpBuffer, nCount, lpVoice);
return;
}
accum = lpVoice->dwAccum;
delta = lpVoice->dwFrequency;
a = ((UINT) lpVoice->nVolume * lpVoice->nPanning) >> 8;
rtable = lpVolumeTable + (a << 8);
ltable = lpVolumeTable + ((lpVoice->nVolume - a) << 8);
data = lpVoice->lpData;
buf = lpBuffer;
#ifdef __FILTER__
a = (BYTE) lpVoice->bReserved;
frac = ((long) delta < 0 ? -delta : +delta) >> (ACCURACY - 5);
ftable = lpFilterTable + (frac << 8);
do {
a = (BYTE)(a + ftable[data[accum >> ACCURACY]] - ftable[a]);
buf[0] += ltable[a];
buf[1] += rtable[a];
accum += delta;
buf += 2;
} while (--nCount != 0);
lpVoice->bReserved = (BYTE) a;
#else
do {
register INT b;
a = (signed char) data[accum >> ACCURACY];
b = (signed char) data[(accum >> ACCURACY) + 1];
frac = (accum & ((1 << ACCURACY) - 1)) >> (ACCURACY - 5);
a = (BYTE)(a + ((frac * (b - a)) >> 5));
buf[0] += ltable[a];
buf[1] += rtable[a];
accum += delta;
buf += 2;
} while (--nCount != 0);
#endif
lpVoice->dwAccum = accum;
}
static VOID AIAPI
MixAudioData16M(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice)
{
register UINT a, count;
register DWORD accum, delta;
register LPLONG table, buf;
register LPWORD data;
accum = lpVoice->dwAccum;
delta = lpVoice->dwFrequency;
table = lpVolumeTable + ((UINT) lpVoice->nVolume << 8);
data = lpVoice->lpData;
buf = lpBuffer;
count = nCount;
do {
a = data[accum >> ACCURACY];
*buf++ += table[a >> 8];
accum += delta;
} while (--count != 0);
lpVoice->dwAccum = accum;
}
static VOID AIAPI
MixAudioData16S(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice)
{
register UINT a;
register DWORD accum, delta;
register LPLONG ltable, rtable, buf;
register LPWORD data;
accum = lpVoice->dwAccum;
delta = lpVoice->dwFrequency;
a = ((UINT) lpVoice->nVolume * lpVoice->nPanning) >> 8;
rtable = lpVolumeTable + (a << 8);
ltable = lpVolumeTable + ((lpVoice->nVolume - a) << 8);
data = lpVoice->lpData;
buf = lpBuffer;
do {
a = data[accum >> ACCURACY];
buf[0] += ltable[a >> 8];
buf[1] += rtable[a >> 8];
accum += delta;
buf += 2;
} while (--nCount != 0);
lpVoice->dwAccum = accum;
}
static VOID AIAPI
MixAudioData16MI(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice)
{
register UINT frac, count;
register DWORD a, b, accum, delta;
register LPLONG table, buf;
register LPWORD data;
/* adaptive oversampling interpolation comparison */
if (lpVoice->dwFrequency < -(1 << ACCURACY) ||
lpVoice->dwFrequency > +(1 << ACCURACY)) {
MixAudioData16M(lpBuffer, nCount, lpVoice);
return;
}
accum = lpVoice->dwAccum;
delta = lpVoice->dwFrequency;
table = lpVolumeTable + ((UINT) lpVoice->nVolume << 8);
data = lpVoice->lpData;
buf = lpBuffer;
count = nCount;
do {
a = data[accum >> ACCURACY];
b = data[(accum >> ACCURACY) + 1];
frac = (accum & ((1 << ACCURACY) - 1)) >> (ACCURACY - 5);
a = (WORD)((SHORT)a + ((frac * ((SHORT)b - (SHORT)a)) >> 5));
*buf++ += table[a >> 8];
accum += delta;
} while (--count != 0);
lpVoice->dwAccum = accum;
}
static VOID AIAPI
MixAudioData16SI(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice)
{
register UINT frac;
register DWORD a, b, accum, delta;
register LPLONG ltable, rtable, buf;
register LPWORD data;
/* adaptive oversampling interpolation comparison */
if (lpVoice->dwFrequency < -(1 << ACCURACY) ||
lpVoice->dwFrequency > +(1 << ACCURACY)) {
MixAudioData16S(lpBuffer, nCount, lpVoice);
return;
}
accum = lpVoice->dwAccum;
delta = lpVoice->dwFrequency;
a = ((UINT) lpVoice->nVolume * lpVoice->nPanning) >> 8;
rtable = lpVolumeTable + (a << 8);
ltable = lpVolumeTable + ((lpVoice->nVolume - a) << 8);
data = lpVoice->lpData;
buf = lpBuffer;
do {
a = data[accum >> ACCURACY];
b = data[(accum >> ACCURACY) + 1];
frac = (accum & ((1 << ACCURACY) - 1)) >> (ACCURACY - 5);
a = (WORD)((SHORT)a + ((frac * ((int)(SHORT)b - (int)(SHORT)a)) >> 5));
buf[0] += ltable[a >> 8];
buf[1] += rtable[a >> 8];
accum += delta;
buf += 2;
} while (--nCount != 0);
lpVoice->dwAccum = accum;
}
#endif
static VOID MixAudioData(LPLONG lpBuffer, UINT nCount, LPVOICE lpVoice)
{
UINT nSize;
if (Synth.wFormat & AUDIO_FORMAT_STEREO)
nCount >>= 1;
while (nCount > 0 && !(lpVoice->bControl & VOICE_STOP)) {
/* check boundary conditions */
if (lpVoice->bControl & VOICE_REVERSE) {
if (lpVoice->dwAccum < lpVoice->dwLoopStart) {
if (lpVoice->bControl & VOICE_BIDILOOP) {
lpVoice->dwAccum = lpVoice->dwLoopStart +
(lpVoice->dwLoopStart - lpVoice->dwAccum);
lpVoice->bControl ^= VOICE_REVERSE;
continue;
}
else if (lpVoice->bControl & VOICE_LOOP) {
lpVoice->dwAccum = lpVoice->dwLoopEnd -
(lpVoice->dwLoopStart - lpVoice->dwAccum);
continue;
}
else {
lpVoice->bControl |= VOICE_STOP;
break;
}
}
}
else {
if (lpVoice->dwAccum > lpVoice->dwLoopEnd) {
if (lpVoice->bControl & VOICE_BIDILOOP) {
lpVoice->dwAccum = lpVoice->dwLoopEnd -
(lpVoice->dwAccum - lpVoice->dwLoopEnd);
lpVoice->bControl ^= VOICE_REVERSE;
continue;
}
else if (lpVoice->bControl & VOICE_LOOP) {
lpVoice->dwAccum = lpVoice->dwLoopStart +
(lpVoice->dwAccum - lpVoice->dwLoopEnd);
continue;
}
else {
lpVoice->bControl |= VOICE_STOP;
break;
}
}
}
/* check for overflow and clip if necessary */
nSize = nCount;
if (lpVoice->bControl & VOICE_REVERSE) {
if (nSize * lpVoice->dwFrequency >
lpVoice->dwAccum - lpVoice->dwLoopStart)
nSize = (lpVoice->dwAccum - lpVoice->dwLoopStart +
lpVoice->dwFrequency) / lpVoice->dwFrequency;
}
else {
if (nSize * lpVoice->dwFrequency >
lpVoice->dwLoopEnd - lpVoice->dwAccum)
nSize = (lpVoice->dwLoopEnd - lpVoice->dwAccum +
lpVoice->dwFrequency) / lpVoice->dwFrequency;
}
if (lpVoice->bControl & VOICE_REVERSE)
lpVoice->dwFrequency = -lpVoice->dwFrequency;
/* mixes chunk of data in a burst mode */
if (lpVoice->bControl & VOICE_16BITS) {
Synth.lpfnMixAudioProc[1] (lpBuffer, nSize, lpVoice);
}
else {
Synth.lpfnMixAudioProc[0] (lpBuffer, nSize, lpVoice);
}
if (lpVoice->bControl & VOICE_REVERSE)
lpVoice->dwFrequency = -lpVoice->dwFrequency;
/* update mixing buffer address and counter */
lpBuffer += nSize;
if (Synth.wFormat & AUDIO_FORMAT_STEREO)
lpBuffer += nSize;
nCount -= nSize;
}
}
static VOID MixAudioVoices(LPLONG lpBuffer, UINT nCount)
{
UINT nVoice, nSize;
while (nCount > 0) {
nSize = nCount;
if (Synth.lpfnAudioTimer != NULL) {
nSize = (Synth.dwTimerRate - Synth.dwTimerAccum + 64L) & ~63L;
if (nSize > nCount)
nSize = nCount;
if ((Synth.dwTimerAccum += nSize) >= Synth.dwTimerRate) {
Synth.dwTimerAccum -= Synth.dwTimerRate;
Synth.lpfnAudioTimer();
}
}
for (nVoice = 0; nVoice < Synth.nVoices; nVoice++) {
MixAudioData(lpBuffer, nSize, &Synth.aVoices[nVoice]);
}
lpBuffer += nSize;
nCount -= nSize;
}
}
/*
* High level waveform synthesizer interface
*/
static UINT AIAPI GetAudioCaps(LPAUDIOCAPS lpCaps)
{
memset(lpCaps, 0, sizeof(AUDIOCAPS));
return AUDIO_ERROR_NOTSUPPORTED;
}
static UINT AIAPI PingAudio(VOID)
{
return AUDIO_ERROR_NOTSUPPORTED;
}
static UINT AIAPI OpenAudio(LPAUDIOINFO lpInfo)
{
memset(&Synth, 0, sizeof(Synth));
Synth.wFormat = lpInfo->wFormat;
Synth.nSampleRate = lpInfo->nSampleRate;
if (Synth.wFormat & AUDIO_FORMAT_FILTER) {
Synth.lpfnMixAudioProc[0] = (Synth.wFormat & AUDIO_FORMAT_STEREO) ?
MixAudioData08SI : MixAudioData08MI;
Synth.lpfnMixAudioProc[1] = (Synth.wFormat & AUDIO_FORMAT_STEREO) ?
MixAudioData16SI : MixAudioData16MI;
}
else {
Synth.lpfnMixAudioProc[0] = (Synth.wFormat & AUDIO_FORMAT_STEREO) ?
MixAudioData08S : MixAudioData08M;
Synth.lpfnMixAudioProc[1] = (Synth.wFormat & AUDIO_FORMAT_STEREO) ?
MixAudioData16S : MixAudioData16M;
}
/* allocate volume (0-64) and filter (0-31) table */
Synth.lpMemory = malloc(sizeof(LONG) * 65 * 256 +
sizeof(BYTE) * 32 * 256 + 1023);
if (Synth.lpMemory != NULL) {
uintptr_t tmp;
tmp = ((uintptr_t)(Synth.lpMemory) + 1023) & ~1023;
lpVolumeTable = (LPLONG)(tmp);
lpFilterTable = (LPBYTE) (lpVolumeTable + 65 * 256);
ASetAudioMixerValue(AUDIO_MIXER_MASTER_VOLUME, 96);
ASetAudioCallback(UpdateVoices);
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_NOMEMORY;
}
static UINT AIAPI CloseAudio(VOID)
{
if (Synth.lpMemory != NULL)
free(Synth.lpMemory);
memset(&Synth, 0, sizeof(Synth));
return AUDIO_ERROR_NONE;
}
static UINT AIAPI SetAudioMixerValue(UINT nChannel, UINT nMixerValue)
{
LPBYTE lpFilter;
LPLONG lpVolume;
UINT nVolume, nSample;
LONG dwAccum, dwDelta;
if (Synth.lpMemory == NULL)
return AUDIO_ERROR_NOTSUPPORTED;
/* master volume must be less than 256 units */
if (nChannel != AUDIO_MIXER_MASTER_VOLUME || nMixerValue > 256)
return AUDIO_ERROR_INVALPARAM;
/* half dynamic range for mono output */
if (!(Synth.wFormat & AUDIO_FORMAT_STEREO))
nMixerValue >>= 1;
/* build volume table (0-64) */
lpVolume = lpVolumeTable;
dwDelta = 0;
for (nVolume = 0; nVolume <= 64; nVolume++, dwDelta += nMixerValue) {
dwAccum = 0;
for (nSample = 0; nSample < 128; nSample++, dwAccum += dwDelta)
*lpVolume++ = dwAccum >> 4;
dwAccum = -dwAccum;
for (nSample = 0; nSample < 128; nSample++, dwAccum += dwDelta)
*lpVolume++ = dwAccum >> 4;
}
#ifdef __FILTER__
/* build lowpass filter table (0-31) */
lpFilter = lpFilterTable;
for (nVolume = 0; nVolume < 32; nVolume++) {
dwAccum = 0;
for (nSample = 0; nSample < 128; nSample++, dwAccum += nVolume)
*lpFilter++ = dwAccum >> 5;
dwAccum = -dwAccum;
for (nSample = 0; nSample < 128; nSample++, dwAccum += nVolume)
*lpFilter++ = dwAccum >> 5;
}
#endif
return AUDIO_ERROR_NONE;
}
static UINT AIAPI OpenVoices(UINT nVoices)
{
UINT nVoice;
/*
* Initialize waveform synthesizer structure for playback
*/
if (nVoices >= 1 && nVoices <= AUDIO_MAX_VOICES) {
Synth.nVoices = nVoices;
for (nVoice = 0; nVoice < Synth.nVoices; nVoice++)
Synth.aVoices[nVoice].bControl = VOICE_STOP;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
static UINT AIAPI CloseVoices(VOID)
{
UINT nVoice;
memset(Synth.aVoices, 0, sizeof(Synth.aVoices));
for (nVoice = 0; nVoice < AUDIO_MAX_VOICES; nVoice++)
Synth.aVoices[nVoice].bControl = VOICE_STOP;
return AUDIO_ERROR_NONE;
}
static UINT AIAPI UpdateAudio(VOID)
{
return AUDIO_ERROR_NONE;
}
static LONG AIAPI GetAudioDataAvail(VOID)
{
return 0;
}
static UINT AIAPI CreateAudioData(LPAUDIOWAVE lpWave)
{
if (lpWave != NULL) {
lpWave->dwHandle = (uintptr_t) lpWave->lpData;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI DestroyAudioData(LPAUDIOWAVE lpWave)
{
if (lpWave != NULL && lpWave->dwHandle != 0) {
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI WriteAudioData(LPAUDIOWAVE lpWave, DWORD dwOffset, UINT nCount)
{
if (lpWave != NULL && lpWave->dwHandle != 0) {
/* anticlick removal work around */
if (lpWave->wFormat & AUDIO_FORMAT_LOOP) {
*(LPDWORD) (lpWave->dwHandle + lpWave->dwLoopEnd) =
*(LPDWORD) (lpWave->dwHandle + lpWave->dwLoopStart);
}
else if (dwOffset + nCount >= lpWave->dwLength) {
*(LPDWORD) (lpWave->dwHandle + lpWave->dwLength) = 0;
}
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI PrimeVoice(UINT nVoice, LPAUDIOWAVE lpWave)
{
LPVOICE lpVoice;
if (nVoice < Synth.nVoices && lpWave != NULL && lpWave->dwHandle != 0) {
lpVoice = &Synth.aVoices[nVoice];
lpVoice->lpData = (LPVOID) lpWave->dwHandle;
lpVoice->bControl = VOICE_STOP;
lpVoice->dwAccum = 0;
if (lpWave->wFormat & (AUDIO_FORMAT_LOOP | AUDIO_FORMAT_BIDILOOP)) {
lpVoice->dwLoopStart = lpWave->dwLoopStart;
lpVoice->dwLoopEnd = lpWave->dwLoopEnd;
lpVoice->bControl |= VOICE_LOOP;
if (lpWave->wFormat & AUDIO_FORMAT_BIDILOOP)
lpVoice->bControl |= VOICE_BIDILOOP;
}
else {
lpVoice->dwLoopStart = lpWave->dwLength;
lpVoice->dwLoopEnd = lpWave->dwLength;
}
if (lpWave->wFormat & AUDIO_FORMAT_16BITS) {
lpVoice->dwLoopStart >>= 1;
lpVoice->dwLoopEnd >>= 1;
lpVoice->bControl |= VOICE_16BITS;
}
lpVoice->dwAccum <<= ACCURACY;
lpVoice->dwLoopStart <<= ACCURACY;
lpVoice->dwLoopEnd <<= ACCURACY;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI StartVoice(UINT nVoice)
{
if (nVoice < Synth.nVoices) {
Synth.aVoices[nVoice].bControl &= ~VOICE_STOP;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI StopVoice(UINT nVoice)
{
if (nVoice < Synth.nVoices) {
Synth.aVoices[nVoice].bControl |= VOICE_STOP;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI SetVoicePosition(UINT nVoice, LONG dwPosition)
{
if (nVoice < Synth.nVoices) {
dwPosition <<= ACCURACY;
if (dwPosition >= 0 && dwPosition < Synth.aVoices[nVoice].dwLoopEnd) {
Synth.aVoices[nVoice].dwAccum = dwPosition;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI SetVoiceFrequency(UINT nVoice, LONG dwFrequency)
{
if (nVoice < Synth.nVoices) {
if (dwFrequency >= AUDIO_MIN_FREQUENCY &&
dwFrequency <= AUDIO_MAX_FREQUENCY) {
Synth.aVoices[nVoice].dwFrequency = ((dwFrequency << ACCURACY) +
(Synth.nSampleRate >> 1)) / Synth.nSampleRate;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI SetVoiceVolume(UINT nVoice, UINT nVolume)
{
if (nVoice < Synth.nVoices) {
if (nVolume <= AUDIO_MAX_VOLUME) {
Synth.aVoices[nVoice].nVolume = nVolume >> 1;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI SetVoicePanning(UINT nVoice, UINT nPanning)
{
if (nVoice < Synth.nVoices) {
if (nPanning <= AUDIO_MAX_PANNING) {
Synth.aVoices[nVoice].nPanning = nPanning;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI GetVoicePosition(UINT nVoice, LPLONG lpdwPosition)
{
if (nVoice < Synth.nVoices) {
if (lpdwPosition != NULL) {
*lpdwPosition = Synth.aVoices[nVoice].dwAccum >> ACCURACY;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI GetVoiceFrequency(UINT nVoice, LPLONG lpdwFrequency)
{
if (nVoice < Synth.nVoices) {
if (lpdwFrequency != NULL) {
*lpdwFrequency = (Synth.aVoices[nVoice].dwFrequency *
Synth.nSampleRate) >> ACCURACY;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI GetVoiceVolume(UINT nVoice, LPUINT lpnVolume)
{
if (nVoice < Synth.nVoices) {
if (lpnVolume != NULL) {
*lpnVolume = Synth.aVoices[nVoice].nVolume << 1;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI GetVoicePanning(UINT nVoice, LPUINT lpnPanning)
{
if (nVoice < Synth.nVoices) {
if (lpnPanning != NULL) {
*lpnPanning = Synth.aVoices[nVoice].nPanning;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI GetVoiceStatus(UINT nVoice, LPBOOL lpnStatus)
{
if (nVoice < Synth.nVoices) {
if (lpnStatus != NULL) {
*lpnStatus = (Synth.aVoices[nVoice].bControl & VOICE_STOP) != 0;
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
return AUDIO_ERROR_INVALHANDLE;
}
static UINT AIAPI SetAudioTimerProc(LPFNAUDIOTIMER lpfnAudioTimer)
{
Synth.lpfnAudioTimer = lpfnAudioTimer;
return AUDIO_ERROR_NONE;
}
static UINT AIAPI SetAudioTimerRate(UINT nBPM)
{
if (nBPM >= 0x20 && nBPM <= 0xFF) {
Synth.dwTimerRate = Synth.nSampleRate;
if (Synth.wFormat & AUDIO_FORMAT_STEREO)
Synth.dwTimerRate <<= 1;
Synth.dwTimerRate = (5 * Synth.dwTimerRate) / (2 * nBPM);
return AUDIO_ERROR_NONE;
}
return AUDIO_ERROR_INVALPARAM;
}
static VOID AIAPI UpdateVoices(LPBYTE lpData, UINT nCount)
{
static LONG aBuffer[BUFFERSIZE];
UINT nSamples;
if (Synth.wFormat & AUDIO_FORMAT_16BITS)
nCount >>= 1;
while (nCount > 0) {
if ((nSamples = nCount) > BUFFERSIZE)
nSamples = BUFFERSIZE;
memset(aBuffer, 0, nSamples << 2);
MixAudioVoices(aBuffer, nSamples);
QuantAudioData(lpData, aBuffer, nSamples);
lpData += nSamples << ((Synth.wFormat & AUDIO_FORMAT_16BITS) != 0);
nCount -= nSamples;
}
}
/*
* Waveform synthesizer public interface
*/
AUDIOSYNTHDRIVER EmuSynthDriver =
{
GetAudioCaps, PingAudio, OpenAudio, CloseAudio,
UpdateAudio, OpenVoices, CloseVoices,
SetAudioTimerProc, SetAudioTimerRate, SetAudioMixerValue,
GetAudioDataAvail, CreateAudioData, DestroyAudioData,
WriteAudioData, PrimeVoice, StartVoice, StopVoice,
SetVoicePosition, SetVoiceFrequency, SetVoiceVolume,
SetVoicePanning, GetVoicePosition, GetVoiceFrequency,
GetVoiceVolume, GetVoicePanning, GetVoiceStatus
};