mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-16 09:21:12 +00:00
169e0c824b
git-svn-id: https://svn.eduke32.com/eduke32@398 1a8010ca-5511-0410-912e-c29ae57300e0
2693 lines
65 KiB
C
2693 lines
65 KiB
C
/*
|
|
Copyright (C) 1994-1995 Apogee Software, Ltd.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
Modifications for JonoF's port by Jonathon Fowler (jonof@edgenetwk.com)
|
|
*/
|
|
/**********************************************************************
|
|
module: MULTIVOC.C
|
|
|
|
author: James R. Dose
|
|
date: December 20, 1993
|
|
|
|
Routines to provide multichannel digitized sound playback for
|
|
Sound Blaster compatible sound cards.
|
|
|
|
(c) Copyright 1993 James R. Dose. All Rights Reserved.
|
|
**********************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifndef _MSC_VER
|
|
#include <unistd.h>
|
|
#endif
|
|
#ifdef _WIN32
|
|
#include "dsoundout.h"
|
|
#else
|
|
#include "dsl.h"
|
|
#endif
|
|
#include "compat.h"
|
|
#include "baselayer.h"
|
|
#include "usrhooks.h"
|
|
#include "linklist.h"
|
|
#include "pitch.h"
|
|
#include "multivoc.h"
|
|
#include "_multivc.h"
|
|
|
|
#define STEREO 1
|
|
#define SIXTEEN_BIT 2
|
|
|
|
#define MONO_8BIT 0
|
|
#define STEREO_8BIT (STEREO)
|
|
#define MONO_16BIT (SIXTEEN_BIT)
|
|
#define STEREO_16BIT (STEREO | SIXTEEN_BIT)
|
|
|
|
#define IS_QUIET(ptr) ((void *)(ptr) == (void *)&MV_VolumeTable[ 0 ])
|
|
|
|
static int MV_ReverbLevel;
|
|
static int MV_ReverbDelay;
|
|
static VOLUME16 *MV_ReverbTable = NULL;
|
|
|
|
//static signed short MV_VolumeTable[ MV_MaxVolume + 1 ][ 256 ];
|
|
static signed short MV_VolumeTable[ 63 + 1 ][ 256 ];
|
|
|
|
//static Pan MV_PanTable[ MV_NumPanPositions ][ MV_MaxVolume + 1 ];
|
|
static Pan MV_PanTable[ MV_NumPanPositions ][ 63 + 1 ];
|
|
|
|
static int MV_Installed = FALSE;
|
|
static int MV_SoundCard = -1;
|
|
static int MV_TotalVolume = MV_MaxTotalVolume;
|
|
static int MV_MaxVoices = 1;
|
|
static int MV_Recording;
|
|
|
|
static int MV_BufferSize = 0;
|
|
static int MV_BufferLength;
|
|
|
|
static int MV_NumberOfBuffers = NumberOfBuffers;
|
|
|
|
static int MV_MixMode = MONO_8BIT;
|
|
static int MV_Channels = 1;
|
|
static int MV_Bits = 8;
|
|
|
|
static int MV_Silence = SILENCE_8BIT;
|
|
static int MV_SwapLeftRight = FALSE;
|
|
|
|
static int MV_RequestedMixRate;
|
|
static int MV_MixRate;
|
|
|
|
static int MV_BuffShift;
|
|
|
|
static int MV_TotalMemory;
|
|
|
|
static int MV_BufferEmpty[ NumberOfBuffers ];
|
|
char *MV_MixBuffer[ NumberOfBuffers + 1 ];
|
|
static char *MV_MixBufferPtr = NULL;
|
|
|
|
static VoiceNode *MV_Voices = NULL;
|
|
|
|
static VoiceNode VoiceList;
|
|
static VoiceNode VoicePool;
|
|
|
|
int MV_MixPage = 0;
|
|
static int MV_VoiceHandle = MV_MinVoiceHandle;
|
|
|
|
static void(*MV_CallBackFunc)(unsigned long) = NULL;
|
|
static void(*MV_RecordFunc)(char *ptr, int length) = NULL;
|
|
static void(*MV_MixFunction)(VoiceNode *voice, int buffer);
|
|
|
|
static int MV_MaxVolume = 63;
|
|
|
|
char *MV_HarshClipTable;
|
|
char *MV_MixDestination;
|
|
short *MV_LeftVolume;
|
|
short *MV_RightVolume;
|
|
int MV_SampleSize = 1;
|
|
int MV_RightChannelOffset;
|
|
|
|
unsigned long MV_MixPosition;
|
|
|
|
int MV_ErrorCode = MV_Ok;
|
|
|
|
#define MV_SetErrorCode(status) \
|
|
MV_ErrorCode = (status);
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_ErrorString
|
|
|
|
Returns a pointer to the error message associated with an error
|
|
number. A -1 returns a pointer the current error.
|
|
---------------------------------------------------------------------*/
|
|
|
|
char *MV_ErrorString(int ErrorNumber)
|
|
{
|
|
char *ErrorString;
|
|
|
|
switch (ErrorNumber)
|
|
{
|
|
case MV_Warning :
|
|
case MV_Error :
|
|
ErrorString = MV_ErrorString(MV_ErrorCode);
|
|
break;
|
|
|
|
case MV_Ok :
|
|
ErrorString = "Multivoc ok.";
|
|
break;
|
|
|
|
case MV_UnsupportedCard :
|
|
ErrorString = "Selected sound card is not supported by Multivoc.";
|
|
break;
|
|
|
|
case MV_NotInstalled :
|
|
ErrorString = "Multivoc not installed.";
|
|
break;
|
|
|
|
case MV_NoVoices :
|
|
ErrorString = "No free voices available to Multivoc.";
|
|
break;
|
|
|
|
case MV_NoMem :
|
|
ErrorString = "Out of memory in Multivoc.";
|
|
break;
|
|
|
|
case MV_VoiceNotFound :
|
|
ErrorString = "No voice with matching handle found.";
|
|
break;
|
|
|
|
case MV_BlasterError :
|
|
#if defined(_WIN32)
|
|
ErrorString = DSOUND_ErrorString(DSOUND_ErrorCode);
|
|
#else
|
|
ErrorString = DSL_ErrorString(DSL_ErrorCode);
|
|
#endif
|
|
break;
|
|
|
|
|
|
case MV_DPMI_Error :
|
|
ErrorString = "DPMI Error in Multivoc.";
|
|
break;
|
|
|
|
case MV_InvalidVOCFile :
|
|
ErrorString = "Invalid VOC file passed in to Multivoc.";
|
|
break;
|
|
|
|
case MV_InvalidWAVFile :
|
|
ErrorString = "Invalid WAV file passed in to Multivoc.";
|
|
break;
|
|
|
|
case MV_InvalidMixMode :
|
|
ErrorString = "Invalid mix mode request in Multivoc.";
|
|
break;
|
|
|
|
case MV_IrqFailure :
|
|
ErrorString = "Playback failed, possibly due to an invalid or conflicting IRQ.";
|
|
break;
|
|
|
|
case MV_DMAFailure :
|
|
ErrorString = "Playback failed, possibly due to an invalid or conflicting DMA channel.";
|
|
break;
|
|
|
|
case MV_DMA16Failure :
|
|
ErrorString = "Playback failed, possibly due to an invalid or conflicting DMA channel. \n"
|
|
"Make sure the 16-bit DMA channel is correct.";
|
|
break;
|
|
|
|
case MV_NullRecordFunction :
|
|
ErrorString = "Null record function passed to MV_StartRecording.";
|
|
break;
|
|
|
|
default :
|
|
ErrorString = "Unknown Multivoc error code.";
|
|
break;
|
|
}
|
|
|
|
return(ErrorString);
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
Memory locked functions:
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetBufferSize
|
|
|
|
Returns the buffer size for the given samplerate.
|
|
---------------------------------------------------------------------*/
|
|
#define BASEBUFSZ (512+128)
|
|
static unsigned MV_GetBufferSize(unsigned samplerate)
|
|
{
|
|
static unsigned lastsr = 0, lastbufsz = 0;
|
|
|
|
if (samplerate == lastsr) return lastbufsz;
|
|
|
|
lastsr = samplerate;
|
|
lastbufsz = (samplerate*BASEBUFSZ/22050)&(~15);
|
|
|
|
return lastbufsz;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Mix
|
|
|
|
Mixes the sound into the buffer.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void MV_Mix(VoiceNode *voice, int buffer)
|
|
{
|
|
char *start;
|
|
int length;
|
|
long voclength;
|
|
unsigned long position;
|
|
unsigned long rate;
|
|
unsigned long FixedPointBufferSize;
|
|
|
|
if ((voice->length == 0) && (voice->GetSound(voice) != KeepPlaying))
|
|
{
|
|
return;
|
|
}
|
|
|
|
length = MixBufferSize;
|
|
FixedPointBufferSize = voice->FixedPointBufferSize;
|
|
|
|
MV_MixDestination = MV_MixBuffer[ buffer ];
|
|
MV_LeftVolume = voice->LeftVolume;
|
|
MV_RightVolume = voice->RightVolume;
|
|
|
|
if ((MV_Channels == 2) && (IS_QUIET(MV_LeftVolume)))
|
|
{
|
|
MV_LeftVolume = MV_RightVolume;
|
|
MV_MixDestination += MV_RightChannelOffset;
|
|
}
|
|
|
|
// Add this voice to the mix
|
|
while (length > 0)
|
|
{
|
|
start = voice->sound;
|
|
rate = voice->RateScale;
|
|
position = voice->position;
|
|
|
|
// Check if the last sample in this buffer would be
|
|
// beyond the length of the sample block
|
|
if ((position + FixedPointBufferSize) >= voice->length)
|
|
{
|
|
if (position < voice->length)
|
|
{
|
|
voclength = (voice->length - position + rate - 1) / rate;
|
|
}
|
|
else
|
|
{
|
|
voice->GetSound(voice);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
voclength = length;
|
|
}
|
|
|
|
voice->mix(position, rate, start, voclength);
|
|
|
|
if (voclength & 1)
|
|
{
|
|
MV_MixPosition += rate;
|
|
voclength -= 1;
|
|
}
|
|
voice->position = MV_MixPosition;
|
|
|
|
length -= voclength;
|
|
|
|
if (voice->position >= voice->length)
|
|
{
|
|
// Get the next block of sound
|
|
if (voice->GetSound(voice) != KeepPlaying)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (length > 0)
|
|
{
|
|
// Get the position of the last sample in the buffer
|
|
FixedPointBufferSize = voice->RateScale * (length - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PlayVoice
|
|
|
|
Adds a voice to the play list.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_PlayVoice(VoiceNode *voice)
|
|
{
|
|
unsigned flags;
|
|
|
|
flags = DisableInterrupts();
|
|
LL_SortedInsertion(&VoiceList, voice, prev, next, VoiceNode, priority);
|
|
|
|
RestoreInterrupts(flags);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_StopVoice
|
|
|
|
Removes the voice from the play list and adds it to the free list.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_StopVoice(VoiceNode *voice)
|
|
{
|
|
unsigned int flags;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
// move the voice from the play list to the free list
|
|
LL_Remove(voice, next, prev);
|
|
LL_Add(&VoicePool, voice, next, prev);
|
|
|
|
RestoreInterrupts(flags);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_ServiceVoc
|
|
|
|
Starts playback of the waiting buffer and mixes the next one.
|
|
---------------------------------------------------------------------*/
|
|
|
|
// static int backcolor = 1;
|
|
|
|
int MV_ServiceVoc(int buffer)
|
|
{
|
|
VoiceNode *voice;
|
|
VoiceNode *next;
|
|
|
|
// Get the currently playing buffer
|
|
#if defined(_WIN32)
|
|
MV_MixPage = buffer;
|
|
#endif
|
|
|
|
// Toggle which buffer we'll mix next
|
|
MV_MixPage++;
|
|
if (MV_MixPage >= MV_NumberOfBuffers)
|
|
{
|
|
MV_MixPage -= MV_NumberOfBuffers;
|
|
}
|
|
|
|
if (MV_ReverbLevel == 0)
|
|
{
|
|
// Initialize buffer
|
|
//Commented out so that the buffer is always cleared.
|
|
//This is so the guys at Echo Speech can mix into the
|
|
//buffer even when no sounds are playing.
|
|
//if (!MV_BufferEmpty[ MV_MixPage ])
|
|
{
|
|
ClearBuffer_DW(MV_MixBuffer[ MV_MixPage ], MV_Silence, MV_BufferSize >> 2);
|
|
MV_BufferEmpty[ MV_MixPage ] = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
char *end;
|
|
char *source;
|
|
char *dest;
|
|
int count;
|
|
int length;
|
|
|
|
end = MV_MixBuffer[ 0 ] + MV_BufferLength;
|
|
dest = MV_MixBuffer[ MV_MixPage ];
|
|
source = MV_MixBuffer[ MV_MixPage ] - MV_ReverbDelay;
|
|
if (source < MV_MixBuffer[ 0 ])
|
|
{
|
|
source += MV_BufferLength;
|
|
}
|
|
|
|
length = MV_BufferSize;
|
|
while (length > 0)
|
|
{
|
|
count = length;
|
|
if (source + count > end)
|
|
{
|
|
count = end - source;
|
|
}
|
|
|
|
if (MV_Bits == 16)
|
|
{
|
|
if (MV_ReverbTable != NULL)
|
|
{
|
|
MV_16BitReverb(source, dest, MV_ReverbTable, count / 2);
|
|
}
|
|
else
|
|
{
|
|
MV_16BitReverbFast(source, dest, count / 2, MV_ReverbLevel);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (MV_ReverbTable != NULL)
|
|
{
|
|
MV_8BitReverb((signed char *)source, (signed char *)dest, MV_ReverbTable, count);
|
|
}
|
|
else
|
|
{
|
|
MV_8BitReverbFast((signed char *)source, (signed char *)dest, count, MV_ReverbLevel);
|
|
}
|
|
}
|
|
|
|
// if we go through the loop again, it means that we've wrapped around the buffer
|
|
source = MV_MixBuffer[ 0 ];
|
|
dest += count;
|
|
length -= count;
|
|
}
|
|
}
|
|
|
|
// Play any waiting voices
|
|
for (voice = VoiceList.next; voice != &VoiceList; voice = next)
|
|
{
|
|
// if ((voice < &MV_Voices[ 0 ]) || (voice > &MV_Voices[ 8 ]))
|
|
// {
|
|
// SetBorderColor(backcolor++);
|
|
// break;
|
|
// }
|
|
|
|
MV_BufferEmpty[ MV_MixPage ] = FALSE;
|
|
|
|
MV_MixFunction(voice, MV_MixPage);
|
|
|
|
next = voice->next;
|
|
|
|
// Is this voice done?
|
|
if (!voice->Playing)
|
|
{
|
|
MV_StopVoice(voice);
|
|
|
|
if (MV_CallBackFunc)
|
|
{
|
|
MV_CallBackFunc(voice->callbackval);
|
|
}
|
|
}
|
|
}
|
|
|
|
return MV_MixPage;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetNextVOCBlock
|
|
|
|
Interperate the information of a VOC format sound file.
|
|
---------------------------------------------------------------------*/
|
|
|
|
playbackstatus MV_GetNextVOCBlock(VoiceNode *voice)
|
|
{
|
|
unsigned char *ptr;
|
|
int blocktype;
|
|
int lastblocktype;
|
|
unsigned long blocklength = 0;
|
|
unsigned long samplespeed = 0;
|
|
unsigned int tc = 0;
|
|
int packtype;
|
|
int voicemode;
|
|
int done;
|
|
unsigned BitsPerSample;
|
|
unsigned Channels;
|
|
unsigned Format;
|
|
|
|
if (voice->BlockLength > 0)
|
|
{
|
|
voice->position -= voice->length;
|
|
voice->sound += voice->length >> 16;
|
|
if (voice->bits == 16)
|
|
{
|
|
voice->sound += voice->length >> 16;
|
|
}
|
|
voice->length = min(voice->BlockLength, 0x8000);
|
|
voice->BlockLength -= voice->length;
|
|
voice->length <<= 16;
|
|
return(KeepPlaying);
|
|
}
|
|
|
|
if ((voice->length > 0) && (voice->LoopEnd != NULL) && (voice->LoopStart != NULL))
|
|
{
|
|
voice->BlockLength = voice->LoopSize;
|
|
voice->sound = voice->LoopStart;
|
|
voice->position = 0;
|
|
voice->length = min(voice->BlockLength, 0x8000);
|
|
voice->BlockLength -= voice->length;
|
|
voice->length <<= 16;
|
|
return(KeepPlaying);
|
|
}
|
|
|
|
ptr = (unsigned char *)voice->NextBlock;
|
|
|
|
voice->Playing = TRUE;
|
|
|
|
voicemode = 0;
|
|
lastblocktype = 0;
|
|
packtype = 0;
|
|
|
|
done = FALSE;
|
|
while (!done)
|
|
{
|
|
// Stop playing if we get a NULL pointer
|
|
if (ptr == NULL)
|
|
{
|
|
voice->Playing = FALSE;
|
|
done = TRUE;
|
|
break;
|
|
}
|
|
|
|
blocktype = (int)*ptr;
|
|
blocklength = (*(unsigned long *)(ptr + 1)) & 0x00ffffff;
|
|
ptr += 4;
|
|
|
|
switch (blocktype)
|
|
{
|
|
case 0 :
|
|
// End of data
|
|
if ((voice->LoopStart == NULL) || ((unsigned long *)voice->LoopStart >= (unsigned long *)(ptr - 4)))
|
|
{
|
|
voice->Playing = FALSE;
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
voice->BlockLength = (char*)(ptr - 4) - voice->LoopStart;
|
|
voice->sound = voice->LoopStart;
|
|
voice->position = 0;
|
|
voice->length = min(voice->BlockLength, 0x8000);
|
|
voice->BlockLength -= voice->length;
|
|
voice->length <<= 16;
|
|
return(KeepPlaying);
|
|
}
|
|
break;
|
|
|
|
case 1 :
|
|
// Sound data block
|
|
voice->bits = 8;
|
|
if (lastblocktype != 8)
|
|
{
|
|
tc = (unsigned int)*ptr << 8;
|
|
packtype = *(ptr + 1);
|
|
}
|
|
|
|
ptr += 2;
|
|
blocklength -= 2;
|
|
|
|
samplespeed = 256000000L / (65536 - tc);
|
|
|
|
// Skip packed or stereo data
|
|
if ((packtype != 0) || (voicemode != 0))
|
|
{
|
|
ptr += blocklength;
|
|
}
|
|
else
|
|
{
|
|
done = TRUE;
|
|
}
|
|
voicemode = 0;
|
|
break;
|
|
|
|
case 2 :
|
|
// Sound continuation block
|
|
samplespeed = voice->SamplingRate;
|
|
done = TRUE;
|
|
break;
|
|
|
|
case 3 :
|
|
// Silence
|
|
// Not implimented.
|
|
ptr += blocklength;
|
|
break;
|
|
|
|
case 4 :
|
|
// Marker
|
|
// Not implimented.
|
|
ptr += blocklength;
|
|
break;
|
|
|
|
case 5 :
|
|
// ASCII string
|
|
// Not implimented.
|
|
ptr += blocklength;
|
|
break;
|
|
|
|
case 6 :
|
|
// Repeat begin
|
|
if (voice->LoopEnd == NULL)
|
|
{
|
|
voice->LoopCount = *(unsigned short *)ptr;
|
|
voice->LoopStart = (char *)(ptr + blocklength);
|
|
}
|
|
ptr += blocklength;
|
|
break;
|
|
|
|
case 7 :
|
|
// Repeat end
|
|
ptr += blocklength;
|
|
if (lastblocktype == 6)
|
|
{
|
|
voice->LoopCount = 0;
|
|
}
|
|
else
|
|
{
|
|
if ((voice->LoopCount > 0) && (voice->LoopStart != NULL))
|
|
{
|
|
ptr = (unsigned char *)voice->LoopStart;
|
|
if (voice->LoopCount < 0xffff)
|
|
{
|
|
voice->LoopCount--;
|
|
if (voice->LoopCount == 0)
|
|
{
|
|
voice->LoopStart = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 8 :
|
|
// Extended block
|
|
voice->bits = 8;
|
|
tc = *(unsigned short *)ptr;
|
|
packtype = *(ptr + 2);
|
|
voicemode = *(ptr + 3);
|
|
ptr += blocklength;
|
|
break;
|
|
|
|
case 9 :
|
|
// New sound data block
|
|
samplespeed = *(unsigned long *)ptr;
|
|
BitsPerSample = (unsigned)*(ptr + 4);
|
|
Channels = (unsigned)*(ptr + 5);
|
|
Format = (unsigned)*(unsigned short *)(ptr + 6);
|
|
|
|
if ((BitsPerSample == 8) && (Channels == 1) && (Format == VOC_8BIT))
|
|
{
|
|
ptr += 12;
|
|
blocklength -= 12;
|
|
voice->bits = 8;
|
|
done = TRUE;
|
|
}
|
|
else if ((BitsPerSample == 16) && (Channels == 1) && (Format == VOC_16BIT))
|
|
{
|
|
ptr += 12;
|
|
blocklength -= 12;
|
|
voice->bits = 16;
|
|
done = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ptr += blocklength;
|
|
}
|
|
break;
|
|
|
|
default :
|
|
// Unknown data. Probably not a VOC file.
|
|
voice->Playing = FALSE;
|
|
done = TRUE;
|
|
break;
|
|
}
|
|
|
|
lastblocktype = blocktype;
|
|
}
|
|
|
|
if (voice->Playing)
|
|
{
|
|
voice->NextBlock = (char *)(ptr + blocklength);
|
|
voice->sound = (char *)ptr;
|
|
|
|
voice->SamplingRate = samplespeed;
|
|
voice->RateScale = (voice->SamplingRate * voice->PitchScale) / MV_MixRate;
|
|
|
|
// Multiply by MixBufferSize - 1
|
|
voice->FixedPointBufferSize = (voice->RateScale * MixBufferSize) -
|
|
voice->RateScale;
|
|
|
|
if (voice->LoopEnd != NULL)
|
|
{
|
|
if (blocklength > (unsigned long)voice->LoopEnd)
|
|
{
|
|
blocklength = (unsigned long)voice->LoopEnd;
|
|
}
|
|
else
|
|
{
|
|
voice->LoopEnd = (char *)blocklength;
|
|
}
|
|
|
|
voice->LoopStart = voice->sound + (unsigned long)voice->LoopStart;
|
|
voice->LoopEnd = voice->sound + (unsigned long)voice->LoopEnd;
|
|
voice->LoopSize = voice->LoopEnd - voice->LoopStart;
|
|
}
|
|
|
|
if (voice->bits == 16)
|
|
{
|
|
blocklength /= 2;
|
|
}
|
|
|
|
voice->position = 0;
|
|
voice->length = min(blocklength, 0x8000);
|
|
voice->BlockLength = blocklength - voice->length;
|
|
voice->length <<= 16;
|
|
|
|
MV_SetVoiceMixMode(voice);
|
|
|
|
return(KeepPlaying);
|
|
}
|
|
|
|
return(NoMoreData);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetNextDemandFeedBlock
|
|
|
|
Controls playback of demand fed data.
|
|
---------------------------------------------------------------------*/
|
|
|
|
playbackstatus MV_GetNextDemandFeedBlock(VoiceNode *voice)
|
|
{
|
|
if (voice->BlockLength > 0)
|
|
{
|
|
voice->position -= voice->length;
|
|
voice->sound += voice->length >> 16;
|
|
voice->length = min(voice->BlockLength, 0x8000);
|
|
voice->BlockLength -= voice->length;
|
|
voice->length <<= 16;
|
|
|
|
return(KeepPlaying);
|
|
}
|
|
|
|
if (voice->DemandFeed == NULL)
|
|
{
|
|
return(NoMoreData);
|
|
}
|
|
|
|
voice->position = 0;
|
|
(voice->DemandFeed)(&voice->sound, &voice->BlockLength);
|
|
voice->length = min(voice->BlockLength, 0x8000);
|
|
voice->BlockLength -= voice->length;
|
|
voice->length <<= 16;
|
|
|
|
if ((voice->length > 0) && (voice->sound != NULL))
|
|
{
|
|
return(KeepPlaying);
|
|
}
|
|
return(NoMoreData);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetNextRawBlock
|
|
|
|
Controls playback of demand fed data.
|
|
---------------------------------------------------------------------*/
|
|
|
|
playbackstatus MV_GetNextRawBlock(VoiceNode *voice)
|
|
{
|
|
if (voice->BlockLength <= 0)
|
|
{
|
|
if (voice->LoopStart == NULL)
|
|
{
|
|
voice->Playing = FALSE;
|
|
return(NoMoreData);
|
|
}
|
|
|
|
voice->BlockLength = voice->LoopSize;
|
|
voice->NextBlock = voice->LoopStart;
|
|
voice->length = 0;
|
|
voice->position = 0;
|
|
}
|
|
|
|
voice->sound = voice->NextBlock;
|
|
voice->position -= voice->length;
|
|
voice->length = min(voice->BlockLength, 0x8000);
|
|
voice->NextBlock += voice->length;
|
|
if (voice->bits == 16)
|
|
{
|
|
voice->NextBlock += voice->length;
|
|
}
|
|
voice->BlockLength -= voice->length;
|
|
voice->length <<= 16;
|
|
|
|
return(KeepPlaying);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetNextWAVBlock
|
|
|
|
Controls playback of demand fed data.
|
|
---------------------------------------------------------------------*/
|
|
|
|
playbackstatus MV_GetNextWAVBlock(VoiceNode *voice)
|
|
{
|
|
if (voice->BlockLength <= 0)
|
|
{
|
|
if (voice->LoopStart == NULL)
|
|
{
|
|
voice->Playing = FALSE;
|
|
return(NoMoreData);
|
|
}
|
|
|
|
voice->BlockLength = voice->LoopSize;
|
|
voice->NextBlock = voice->LoopStart;
|
|
voice->length = 0;
|
|
voice->position = 0;
|
|
}
|
|
|
|
voice->sound = voice->NextBlock;
|
|
voice->position -= voice->length;
|
|
voice->length = min(voice->BlockLength, 0x8000);
|
|
voice->NextBlock += voice->length;
|
|
if (voice->bits == 16)
|
|
{
|
|
voice->NextBlock += voice->length;
|
|
}
|
|
voice->BlockLength -= voice->length;
|
|
voice->length <<= 16;
|
|
|
|
return(KeepPlaying);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_ServiceRecord
|
|
|
|
Starts recording of the waiting buffer.
|
|
---------------------------------------------------------------------*/
|
|
#if 0
|
|
static void MV_ServiceRecord(void)
|
|
{
|
|
if (MV_RecordFunc)
|
|
{
|
|
MV_RecordFunc(MV_MixBuffer[ 0 ] + MV_MixPage * MixBufferSize, MixBufferSize);
|
|
}
|
|
|
|
// Toggle which buffer we'll mix next
|
|
MV_MixPage++;
|
|
if (MV_MixPage >= NumberOfBuffers)
|
|
{
|
|
MV_MixPage = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetVoice
|
|
|
|
Locates the voice with the specified handle.
|
|
---------------------------------------------------------------------*/
|
|
|
|
VoiceNode *MV_GetVoice(int handle)
|
|
{
|
|
VoiceNode *voice;
|
|
unsigned flags;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
for (voice = VoiceList.next; voice != &VoiceList; voice = voice->next)
|
|
{
|
|
if (handle == voice->handle)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
RestoreInterrupts(flags);
|
|
|
|
if (voice == &VoiceList)
|
|
{
|
|
MV_SetErrorCode(MV_VoiceNotFound);
|
|
voice = NULL;
|
|
}
|
|
|
|
return(voice);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_VoicePlaying
|
|
|
|
Checks if the voice associated with the specified handle is
|
|
playing.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_VoicePlaying(int handle)
|
|
{
|
|
VoiceNode *voice;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(FALSE);
|
|
}
|
|
|
|
voice = MV_GetVoice(handle);
|
|
|
|
if (voice == NULL)
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_KillAllVoices
|
|
|
|
Stops output of all currently active voices.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_KillAllVoices(void)
|
|
{
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
// Remove all the voices from the list
|
|
while (VoiceList.next != &VoiceList)
|
|
{
|
|
MV_Kill(VoiceList.next->handle);
|
|
}
|
|
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Kill
|
|
|
|
Stops output of the voice associated with the specified handle.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_Kill(int handle)
|
|
{
|
|
VoiceNode *voice;
|
|
unsigned flags;
|
|
unsigned long callbackval;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
voice = MV_GetVoice(handle);
|
|
if (voice == NULL)
|
|
{
|
|
RestoreInterrupts(flags);
|
|
MV_SetErrorCode(MV_VoiceNotFound);
|
|
return(MV_Error);
|
|
}
|
|
|
|
callbackval = voice->callbackval;
|
|
|
|
MV_StopVoice(voice);
|
|
|
|
RestoreInterrupts(flags);
|
|
|
|
if (MV_CallBackFunc)
|
|
{
|
|
MV_CallBackFunc(callbackval);
|
|
}
|
|
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_VoicesPlaying
|
|
|
|
Determines the number of currently active voices.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_VoicesPlaying(void)
|
|
{
|
|
VoiceNode *voice;
|
|
int NumVoices = 0;
|
|
unsigned flags;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(0);
|
|
}
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
for (voice = VoiceList.next; voice != &VoiceList; voice = voice->next)
|
|
{
|
|
NumVoices++;
|
|
}
|
|
|
|
RestoreInterrupts(flags);
|
|
|
|
return(NumVoices);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_AllocVoice
|
|
|
|
Retrieve an inactive or lower priority voice for output.
|
|
---------------------------------------------------------------------*/
|
|
|
|
VoiceNode *MV_AllocVoice(int priority)
|
|
{
|
|
VoiceNode *voice;
|
|
VoiceNode *node;
|
|
unsigned flags;
|
|
|
|
//return(NULL);
|
|
if (MV_Recording)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
// Check if we have any free voices
|
|
if (LL_Empty(&VoicePool, next, prev))
|
|
{
|
|
// check if we have a higher priority than a voice that is playing.
|
|
voice = VoiceList.next;
|
|
for (node = voice->next; node != &VoiceList; node = node->next)
|
|
{
|
|
if (node->priority < voice->priority)
|
|
{
|
|
voice = node;
|
|
}
|
|
}
|
|
|
|
if (priority >= voice->priority)
|
|
{
|
|
MV_Kill(voice->handle);
|
|
}
|
|
}
|
|
|
|
// Check if any voices are in the voice pool
|
|
if (LL_Empty(&VoicePool, next, prev))
|
|
{
|
|
// No free voices
|
|
RestoreInterrupts(flags);
|
|
return(NULL);
|
|
}
|
|
|
|
voice = VoicePool.next;
|
|
LL_Remove(voice, next, prev);
|
|
RestoreInterrupts(flags);
|
|
|
|
// Find a free voice handle
|
|
do
|
|
{
|
|
MV_VoiceHandle++;
|
|
if (MV_VoiceHandle < MV_MinVoiceHandle)
|
|
{
|
|
MV_VoiceHandle = MV_MinVoiceHandle;
|
|
}
|
|
}
|
|
while (MV_VoicePlaying(MV_VoiceHandle));
|
|
|
|
voice->handle = MV_VoiceHandle;
|
|
|
|
return(voice);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_VoiceAvailable
|
|
|
|
Checks if a voice can be play at the specified priority.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_VoiceAvailable(int priority)
|
|
{
|
|
VoiceNode *voice;
|
|
VoiceNode *node;
|
|
unsigned flags;
|
|
|
|
// Check if we have any free voices
|
|
if (!LL_Empty(&VoicePool, next, prev))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
// check if we have a higher priority than a voice that is playing.
|
|
voice = VoiceList.next;
|
|
for (node = VoiceList.next; node != &VoiceList; node = node->next)
|
|
{
|
|
if (node->priority < voice->priority)
|
|
{
|
|
voice = node;
|
|
}
|
|
}
|
|
|
|
RestoreInterrupts(flags);
|
|
|
|
if ((voice != &VoiceList) && (priority >= voice->priority))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetVoicePitch
|
|
|
|
Sets the pitch for the specified voice.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_SetVoicePitch(VoiceNode *voice, unsigned long rate, int pitchoffset)
|
|
{
|
|
voice->SamplingRate = rate;
|
|
voice->PitchScale = PITCH_GetScale(pitchoffset);
|
|
voice->RateScale = (rate * voice->PitchScale) / MV_MixRate;
|
|
|
|
// Multiply by MixBufferSize - 1
|
|
voice->FixedPointBufferSize = (voice->RateScale * MixBufferSize) -
|
|
voice->RateScale;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetPitch
|
|
|
|
Sets the pitch for the voice associated with the specified handle.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_SetPitch(int handle, int pitchoffset)
|
|
{
|
|
VoiceNode *voice;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
voice = MV_GetVoice(handle);
|
|
if (voice == NULL)
|
|
{
|
|
MV_SetErrorCode(MV_VoiceNotFound);
|
|
return(MV_Error);
|
|
}
|
|
|
|
MV_SetVoicePitch(voice, voice->SamplingRate, pitchoffset);
|
|
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetFrequency
|
|
|
|
Sets the frequency for the voice associated with the specified handle.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_SetFrequency(int handle, int frequency)
|
|
{
|
|
VoiceNode *voice;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
voice = MV_GetVoice(handle);
|
|
if (voice == NULL)
|
|
{
|
|
MV_SetErrorCode(MV_VoiceNotFound);
|
|
return(MV_Error);
|
|
}
|
|
|
|
MV_SetVoicePitch(voice, frequency, 0);
|
|
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetVolumeTable
|
|
|
|
Returns a pointer to the volume table associated with the specified
|
|
volume.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static short *MV_GetVolumeTable(int vol)
|
|
{
|
|
int volume;
|
|
short *table;
|
|
|
|
volume = MIX_VOLUME(vol);
|
|
|
|
table = (short*)&MV_VolumeTable[ volume ];
|
|
|
|
return(table);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetVoiceMixMode
|
|
|
|
Selects which method should be used to mix the voice.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void MV_SetVoiceMixMode(VoiceNode *voice)
|
|
{
|
|
unsigned flags;
|
|
int test;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
test = T_DEFAULT;
|
|
if (MV_Bits == 8)
|
|
{
|
|
test |= T_8BITS;
|
|
}
|
|
|
|
if (voice->bits == 16)
|
|
{
|
|
test |= T_16BITSOURCE;
|
|
}
|
|
|
|
if (MV_Channels == 1)
|
|
{
|
|
test |= T_MONO;
|
|
}
|
|
else
|
|
{
|
|
if (IS_QUIET(voice->RightVolume))
|
|
{
|
|
test |= T_RIGHTQUIET;
|
|
}
|
|
else if (IS_QUIET(voice->LeftVolume))
|
|
{
|
|
test |= T_LEFTQUIET;
|
|
}
|
|
}
|
|
|
|
// Default case
|
|
voice->mix = MV_Mix8BitMono;
|
|
|
|
switch (test)
|
|
{
|
|
case T_8BITS | T_MONO | T_16BITSOURCE :
|
|
voice->mix = MV_Mix8BitMono16;
|
|
break;
|
|
|
|
case T_8BITS | T_MONO :
|
|
voice->mix = MV_Mix8BitMono;
|
|
break;
|
|
|
|
case T_8BITS | T_16BITSOURCE | T_LEFTQUIET :
|
|
MV_LeftVolume = MV_RightVolume;
|
|
voice->mix = MV_Mix8BitMono16;
|
|
break;
|
|
|
|
case T_8BITS | T_LEFTQUIET :
|
|
MV_LeftVolume = MV_RightVolume;
|
|
voice->mix = MV_Mix8BitMono;
|
|
break;
|
|
|
|
case T_8BITS | T_16BITSOURCE | T_RIGHTQUIET :
|
|
voice->mix = MV_Mix8BitMono16;
|
|
break;
|
|
|
|
case T_8BITS | T_RIGHTQUIET :
|
|
voice->mix = MV_Mix8BitMono;
|
|
break;
|
|
|
|
case T_8BITS | T_16BITSOURCE :
|
|
voice->mix = MV_Mix8BitStereo16;
|
|
break;
|
|
|
|
case T_8BITS :
|
|
voice->mix = MV_Mix8BitStereo;
|
|
break;
|
|
|
|
case T_MONO | T_16BITSOURCE :
|
|
voice->mix = MV_Mix16BitMono16;
|
|
break;
|
|
|
|
case T_MONO :
|
|
voice->mix = MV_Mix16BitMono;
|
|
break;
|
|
|
|
case T_16BITSOURCE | T_LEFTQUIET :
|
|
MV_LeftVolume = MV_RightVolume;
|
|
voice->mix = MV_Mix16BitMono16;
|
|
break;
|
|
|
|
case T_LEFTQUIET :
|
|
MV_LeftVolume = MV_RightVolume;
|
|
voice->mix = MV_Mix16BitMono;
|
|
break;
|
|
|
|
case T_16BITSOURCE | T_RIGHTQUIET :
|
|
voice->mix = MV_Mix16BitMono16;
|
|
break;
|
|
|
|
case T_RIGHTQUIET :
|
|
voice->mix = MV_Mix16BitMono;
|
|
break;
|
|
|
|
case T_16BITSOURCE :
|
|
voice->mix = MV_Mix16BitStereo16;
|
|
break;
|
|
|
|
case T_SIXTEENBIT_STEREO :
|
|
voice->mix = MV_Mix16BitStereo;
|
|
break;
|
|
|
|
default :
|
|
voice->mix = MV_Mix8BitMono;
|
|
}
|
|
|
|
RestoreInterrupts(flags);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetVoiceVolume
|
|
|
|
Sets the stereo and mono volume level of the voice associated
|
|
with the specified handle.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_SetVoiceVolume(VoiceNode *voice, int vol, int left, int right)
|
|
{
|
|
if (MV_Channels == 1)
|
|
{
|
|
left = vol;
|
|
right = vol;
|
|
}
|
|
|
|
if (MV_SwapLeftRight)
|
|
{
|
|
// SBPro uses reversed panning
|
|
voice->LeftVolume = MV_GetVolumeTable(right);
|
|
voice->RightVolume = MV_GetVolumeTable(left);
|
|
}
|
|
else
|
|
{
|
|
voice->LeftVolume = MV_GetVolumeTable(left);
|
|
voice->RightVolume = MV_GetVolumeTable(right);
|
|
}
|
|
|
|
MV_SetVoiceMixMode(voice);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_EndLooping
|
|
|
|
Stops the voice associated with the specified handle from looping
|
|
without stoping the sound.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_EndLooping(int handle)
|
|
{
|
|
VoiceNode *voice;
|
|
unsigned flags;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
voice = MV_GetVoice(handle);
|
|
if (voice == NULL)
|
|
{
|
|
RestoreInterrupts(flags);
|
|
MV_SetErrorCode(MV_VoiceNotFound);
|
|
return(MV_Warning);
|
|
}
|
|
|
|
voice->LoopCount = 0;
|
|
voice->LoopStart = NULL;
|
|
voice->LoopEnd = NULL;
|
|
|
|
RestoreInterrupts(flags);
|
|
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetPan
|
|
|
|
Sets the stereo and mono volume level of the voice associated
|
|
with the specified handle.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_SetPan(int handle, int vol, int left, int right)
|
|
{
|
|
VoiceNode *voice;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
voice = MV_GetVoice(handle);
|
|
if (voice == NULL)
|
|
{
|
|
MV_SetErrorCode(MV_VoiceNotFound);
|
|
return(MV_Warning);
|
|
}
|
|
|
|
MV_SetVoiceVolume(voice, vol, left, right);
|
|
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Pan3D
|
|
|
|
Set the angle and distance from the listener of the voice associated
|
|
with the specified handle.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_Pan3D(int handle, int angle, int distance)
|
|
{
|
|
int left;
|
|
int right;
|
|
int mid;
|
|
int volume;
|
|
int status;
|
|
|
|
if (distance < 0)
|
|
{
|
|
distance = -distance;
|
|
angle += MV_NumPanPositions / 2;
|
|
}
|
|
|
|
volume = MIX_VOLUME(distance);
|
|
|
|
// Ensure angle is within 0 - 31
|
|
angle &= MV_MaxPanPosition;
|
|
|
|
left = MV_PanTable[ angle ][ volume ].left;
|
|
right = MV_PanTable[ angle ][ volume ].right;
|
|
mid = max(0, 255 - distance);
|
|
|
|
status = MV_SetPan(handle, mid, left, right);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetReverb
|
|
|
|
Sets the level of reverb to add to mix.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_SetReverb(int reverb)
|
|
{
|
|
MV_ReverbLevel = MIX_VOLUME(reverb);
|
|
MV_ReverbTable = &MV_VolumeTable[ MV_ReverbLevel ];
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetFastReverb
|
|
|
|
Sets the level of reverb to add to mix.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_SetFastReverb(int reverb)
|
|
{
|
|
MV_ReverbLevel = max(0, min(16, reverb));
|
|
MV_ReverbTable = NULL;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetMaxReverbDelay
|
|
|
|
Returns the maximum delay time for reverb.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_GetMaxReverbDelay(void)
|
|
{
|
|
int maxdelay;
|
|
|
|
maxdelay = MixBufferSize * MV_NumberOfBuffers;
|
|
|
|
return maxdelay;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetReverbDelay
|
|
|
|
Returns the current delay time for reverb.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_GetReverbDelay(void)
|
|
{
|
|
return MV_ReverbDelay / MV_SampleSize;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetReverbDelay
|
|
|
|
Sets the delay level of reverb to add to mix.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_SetReverbDelay(int delay)
|
|
{
|
|
int maxdelay;
|
|
|
|
maxdelay = MV_GetMaxReverbDelay();
|
|
MV_ReverbDelay = max((signed)MixBufferSize, min(delay, maxdelay));
|
|
MV_ReverbDelay *= MV_SampleSize;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetMixMode
|
|
|
|
Prepares Multivoc to play stereo of mono digitized sounds.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_SetMixMode(int numchannels, int samplebits)
|
|
{
|
|
int mode;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
mode = 0;
|
|
if (numchannels == 2)
|
|
{
|
|
mode |= STEREO;
|
|
}
|
|
if (samplebits == 16)
|
|
{
|
|
mode |= SIXTEEN_BIT;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
MV_MixMode = DSOUND_SetMixMode(mode);
|
|
#else
|
|
MV_MixMode = mode;
|
|
#endif
|
|
|
|
MV_Channels = 1;
|
|
if (MV_MixMode & STEREO)
|
|
{
|
|
MV_Channels = 2;
|
|
}
|
|
|
|
MV_Bits = 8;
|
|
if (MV_MixMode & SIXTEEN_BIT)
|
|
{
|
|
MV_Bits = 16;
|
|
}
|
|
|
|
MV_BuffShift = 7 + MV_Channels;
|
|
MV_SampleSize = sizeof(MONO8) * MV_Channels;
|
|
|
|
if (MV_Bits == 8)
|
|
{
|
|
MV_Silence = SILENCE_8BIT;
|
|
}
|
|
else
|
|
{
|
|
MV_Silence = SILENCE_16BIT;
|
|
MV_BuffShift += 1;
|
|
MV_SampleSize *= 2;
|
|
}
|
|
|
|
MV_BufferSize = MixBufferSize * MV_SampleSize;
|
|
MV_NumberOfBuffers = TotalBufferSize / MV_BufferSize;
|
|
MV_BufferLength = TotalBufferSize;
|
|
|
|
MV_RightChannelOffset = MV_SampleSize / 2;
|
|
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_StartPlayback
|
|
|
|
Starts the sound playback engine.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_StartPlayback(void)
|
|
{
|
|
int status;
|
|
int buffer;
|
|
|
|
// Initialize the buffers
|
|
ClearBuffer_DW(MV_MixBuffer[ 0 ], MV_Silence, TotalBufferSize >> 2);
|
|
for (buffer = 0; buffer < MV_NumberOfBuffers; buffer++)
|
|
{
|
|
MV_BufferEmpty[ buffer ] = TRUE;
|
|
}
|
|
|
|
// Set the mix buffer variables
|
|
MV_MixPage = 1;
|
|
|
|
MV_MixFunction = MV_Mix;
|
|
|
|
#if defined(_WIN32)
|
|
MV_MixRate = MV_RequestedMixRate;
|
|
|
|
// Start playback
|
|
status = DSOUND_BeginBufferedPlayback(MV_MixBuffer[ 0 ], MV_ServiceVoc, TotalBufferSize, MV_NumberOfBuffers);
|
|
if (status != DSOUND_Ok)
|
|
{
|
|
MV_SetErrorCode(MV_BlasterError);
|
|
return(MV_Error);
|
|
}
|
|
#else
|
|
status = DSL_BeginBufferedPlayback(MV_MixBuffer[ 0 ], TotalBufferSize, MV_NumberOfBuffers, MV_RequestedMixRate, MV_MixMode, (void *)MV_ServiceVoc);
|
|
|
|
if (status != DSL_Ok)
|
|
{
|
|
MV_SetErrorCode(MV_BlasterError);
|
|
return(MV_Error);
|
|
}
|
|
|
|
MV_MixRate = DSL_GetPlaybackRate();
|
|
#endif
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_StopPlayback
|
|
|
|
Stops the sound playback engine.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_StopPlayback(void)
|
|
{
|
|
VoiceNode *voice;
|
|
VoiceNode *next;
|
|
unsigned flags;
|
|
|
|
// Stop sound playback
|
|
#if defined(_WIN32)
|
|
DSOUND_StopPlayback();
|
|
#else
|
|
DSL_StopPlayback();
|
|
#endif
|
|
|
|
// Make sure all callbacks are done.
|
|
flags = DisableInterrupts();
|
|
|
|
for (voice = VoiceList.next; voice != &VoiceList; voice = next)
|
|
{
|
|
next = voice->next;
|
|
|
|
MV_StopVoice(voice);
|
|
|
|
if (MV_CallBackFunc)
|
|
{
|
|
MV_CallBackFunc(voice->callbackval);
|
|
}
|
|
}
|
|
|
|
RestoreInterrupts(flags);
|
|
}
|
|
|
|
|
|
#if 0
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_StartRecording
|
|
|
|
Starts the sound recording engine.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_StartRecording(int MixRate, void(*function)(char *ptr, int length))
|
|
{
|
|
int status;
|
|
|
|
switch (MV_SoundCard)
|
|
{
|
|
case SoundBlaster :
|
|
break;
|
|
|
|
default :
|
|
MV_SetErrorCode(MV_UnsupportedCard);
|
|
return(MV_Error);
|
|
break;
|
|
}
|
|
|
|
if (function == NULL)
|
|
{
|
|
MV_SetErrorCode(MV_NullRecordFunction);
|
|
return(MV_Error);
|
|
}
|
|
|
|
MV_StopPlayback();
|
|
|
|
// Initialize the buffers
|
|
ClearBuffer_DW(MV_MixBuffer[ 0 ], SILENCE_8BIT, TotalBufferSize >> 2);
|
|
|
|
// Set the mix buffer variables
|
|
MV_MixPage = 0;
|
|
|
|
MV_RecordFunc = function;
|
|
|
|
// Start playback
|
|
switch (MV_SoundCard)
|
|
{
|
|
case SoundBlaster :
|
|
status = BLASTER_BeginBufferedRecord(MV_MixBuffer[ 0 ], TotalBufferSize, NumberOfBuffers, MixRate, MONO_8BIT, MV_ServiceRecord);
|
|
|
|
if (status != BLASTER_Ok)
|
|
{
|
|
MV_SetErrorCode(MV_BlasterError);
|
|
return(MV_Error);
|
|
}
|
|
break;
|
|
}
|
|
|
|
MV_Recording = TRUE;
|
|
return(MV_Ok);
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_StopRecord
|
|
|
|
Stops the sound record engine.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_StopRecord(void)
|
|
{
|
|
// Stop sound playback
|
|
switch (MV_SoundCard)
|
|
{
|
|
case SoundBlaster :
|
|
BLASTER_StopPlayback();
|
|
break;
|
|
}
|
|
|
|
MV_Recording = FALSE;
|
|
MV_StartPlayback();
|
|
}
|
|
#endif
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_StartDemandFeedPlayback
|
|
|
|
Plays a digitized sound from a user controlled buffering system.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_StartDemandFeedPlayback(void(*function)(char **ptr, unsigned long *length), int rate, int pitchoffset, int vol, int left, int right, int priority, unsigned long callbackval)
|
|
{
|
|
VoiceNode *voice;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
// Request a voice from the voice pool
|
|
voice = MV_AllocVoice(priority);
|
|
if (voice == NULL)
|
|
{
|
|
MV_SetErrorCode(MV_NoVoices);
|
|
return(MV_Error);
|
|
}
|
|
|
|
voice->wavetype = DemandFeed;
|
|
voice->bits = 8;
|
|
voice->GetSound = MV_GetNextDemandFeedBlock;
|
|
voice->NextBlock = NULL;
|
|
voice->DemandFeed = function;
|
|
voice->LoopStart = NULL;
|
|
voice->LoopCount = 0;
|
|
voice->BlockLength = 0;
|
|
voice->position = 0;
|
|
voice->sound = NULL;
|
|
voice->length = 0;
|
|
voice->BlockLength = 0;
|
|
voice->Playing = TRUE;
|
|
voice->next = NULL;
|
|
voice->prev = NULL;
|
|
voice->priority = priority;
|
|
voice->callbackval = callbackval;
|
|
|
|
MV_SetVoicePitch(voice, rate, pitchoffset);
|
|
MV_SetVoiceVolume(voice, vol, left, right);
|
|
MV_PlayVoice(voice);
|
|
|
|
return(voice->handle);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PlayRaw
|
|
|
|
Begin playback of sound data with the given sound levels and
|
|
priority.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_PlayRaw(char *ptr, unsigned long length, unsigned rate, int pitchoffset, int vol, int left, int right, int priority, unsigned long callbackval)
|
|
{
|
|
int status;
|
|
|
|
status = MV_PlayLoopedRaw(ptr, length, NULL, NULL, rate, pitchoffset, vol, left, right, priority, callbackval);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PlayLoopedRaw
|
|
|
|
Begin playback of sound data with the given sound levels and
|
|
priority.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_PlayLoopedRaw(char *ptr, long length, char *loopstart, char *loopend, unsigned rate, int pitchoffset, int vol, int left, int right, int priority, unsigned long callbackval)
|
|
{
|
|
VoiceNode *voice;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
// Request a voice from the voice pool
|
|
voice = MV_AllocVoice(priority);
|
|
if (voice == NULL)
|
|
{
|
|
MV_SetErrorCode(MV_NoVoices);
|
|
return(MV_Error);
|
|
}
|
|
|
|
voice->wavetype = Raw;
|
|
voice->bits = 8;
|
|
voice->GetSound = MV_GetNextRawBlock;
|
|
voice->Playing = TRUE;
|
|
voice->NextBlock = ptr;
|
|
voice->position = 0;
|
|
voice->BlockLength = length;
|
|
voice->length = 0;
|
|
voice->next = NULL;
|
|
voice->prev = NULL;
|
|
voice->priority = priority;
|
|
voice->callbackval = callbackval;
|
|
voice->LoopStart = loopstart;
|
|
voice->LoopEnd = loopend;
|
|
voice->LoopSize = (voice->LoopEnd - voice->LoopStart) + 1;
|
|
|
|
MV_SetVoicePitch(voice, rate, pitchoffset);
|
|
MV_SetVoiceVolume(voice, vol, left, right);
|
|
MV_PlayVoice(voice);
|
|
|
|
return(voice->handle);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PlayWAV
|
|
|
|
Begin playback of sound data with the given sound levels and
|
|
priority.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_PlayWAV(char *ptr, int pitchoffset, int vol, int left, int right, int priority, unsigned long callbackval)
|
|
{
|
|
int status;
|
|
|
|
status = MV_PlayLoopedWAV(ptr, -1, -1, pitchoffset, vol, left, right, priority, callbackval);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PlayWAV3D
|
|
|
|
Begin playback of sound data at specified angle and distance
|
|
from listener.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_PlayWAV3D(char *ptr, int pitchoffset, int angle, int distance, int priority, unsigned long callbackval)
|
|
{
|
|
int left;
|
|
int right;
|
|
int mid;
|
|
int volume;
|
|
int status;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
if (distance < 0)
|
|
{
|
|
distance = -distance;
|
|
angle += MV_NumPanPositions / 2;
|
|
}
|
|
|
|
volume = MIX_VOLUME(distance);
|
|
|
|
// Ensure angle is within 0 - 31
|
|
angle &= MV_MaxPanPosition;
|
|
|
|
left = MV_PanTable[ angle ][ volume ].left;
|
|
right = MV_PanTable[ angle ][ volume ].right;
|
|
mid = max(0, 255 - distance);
|
|
|
|
status = MV_PlayWAV(ptr, pitchoffset, mid, left, right, priority, callbackval);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PlayLoopedWAV
|
|
|
|
Begin playback of sound data with the given sound levels and
|
|
priority.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_PlayLoopedWAV(char *ptr, long loopstart, long loopend, int pitchoffset, int vol, int left, int right, int priority, unsigned long callbackval)
|
|
{
|
|
riff_header *riff;
|
|
format_header *format;
|
|
data_header *data;
|
|
VoiceNode *voice;
|
|
int length;
|
|
int absloopend;
|
|
int absloopstart;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
riff = (riff_header *)ptr;
|
|
|
|
if ((strncmp(riff->RIFF, "RIFF", 4) != 0) || (strncmp(riff->WAVE, "WAVE", 4) != 0) || (strncmp(riff->fmt, "fmt ", 4) != 0))
|
|
{
|
|
MV_SetErrorCode(MV_InvalidWAVFile);
|
|
return(MV_Error);
|
|
}
|
|
|
|
format = (format_header *)(riff + 1);
|
|
data = (data_header *)(((char *)format) + riff->format_size);
|
|
|
|
// Check if it's PCM data.
|
|
if (format->wFormatTag != 1)
|
|
{
|
|
MV_SetErrorCode(MV_InvalidWAVFile);
|
|
return(MV_Error);
|
|
}
|
|
|
|
if (format->nChannels != 1)
|
|
{
|
|
MV_SetErrorCode(MV_InvalidWAVFile);
|
|
return(MV_Error);
|
|
}
|
|
|
|
if ((format->nBitsPerSample != 8) && (format->nBitsPerSample != 16))
|
|
{
|
|
MV_SetErrorCode(MV_InvalidWAVFile);
|
|
return(MV_Error);
|
|
}
|
|
|
|
if (strncmp((char *)data->DATA, "data", 4) != 0)
|
|
{
|
|
MV_SetErrorCode(MV_InvalidWAVFile);
|
|
return(MV_Error);
|
|
}
|
|
|
|
// Request a voice from the voice pool
|
|
voice = MV_AllocVoice(priority);
|
|
if (voice == NULL)
|
|
{
|
|
MV_SetErrorCode(MV_NoVoices);
|
|
return(MV_Error);
|
|
}
|
|
|
|
voice->wavetype = WAV;
|
|
voice->bits = format->nBitsPerSample;
|
|
voice->GetSound = MV_GetNextWAVBlock;
|
|
|
|
length = data->size;
|
|
absloopstart = loopstart;
|
|
absloopend = loopend;
|
|
if (voice->bits == 16)
|
|
{
|
|
loopstart *= 2;
|
|
data->size &= ~1;
|
|
loopend *= 2;
|
|
length /= 2;
|
|
}
|
|
|
|
loopend = min(loopend, (signed)data->size);
|
|
absloopend = min(absloopend, length);
|
|
|
|
voice->Playing = TRUE;
|
|
voice->DemandFeed = NULL;
|
|
voice->LoopStart = NULL;
|
|
voice->LoopCount = 0;
|
|
voice->position = 0;
|
|
voice->length = 0;
|
|
voice->BlockLength = absloopend;
|
|
voice->NextBlock = (char *)(data + 1);
|
|
voice->next = NULL;
|
|
voice->prev = NULL;
|
|
voice->priority = priority;
|
|
voice->callbackval = callbackval;
|
|
voice->LoopStart = voice->NextBlock + loopstart;
|
|
voice->LoopEnd = voice->NextBlock + loopend;
|
|
voice->LoopSize = absloopend - absloopstart;
|
|
|
|
if ((loopstart >= (signed)data->size) || (loopstart < 0))
|
|
{
|
|
voice->LoopStart = NULL;
|
|
voice->LoopEnd = NULL;
|
|
voice->BlockLength = length;
|
|
}
|
|
|
|
MV_SetVoicePitch(voice, format->nSamplesPerSec, pitchoffset);
|
|
MV_SetVoiceVolume(voice, vol, left, right);
|
|
MV_PlayVoice(voice);
|
|
|
|
return(voice->handle);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PlayVOC3D
|
|
|
|
Begin playback of sound data at specified angle and distance
|
|
from listener.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_PlayVOC3D(char *ptr, int pitchoffset, int angle, int distance, int priority, unsigned long callbackval)
|
|
{
|
|
int left;
|
|
int right;
|
|
int mid;
|
|
int volume;
|
|
int status;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
if (distance < 0)
|
|
{
|
|
distance = -distance;
|
|
angle += MV_NumPanPositions / 2;
|
|
}
|
|
|
|
volume = MIX_VOLUME(distance);
|
|
|
|
// Ensure angle is within 0 - 31
|
|
angle &= MV_MaxPanPosition;
|
|
|
|
left = MV_PanTable[ angle ][ volume ].left;
|
|
right = MV_PanTable[ angle ][ volume ].right;
|
|
mid = max(0, 255 - distance);
|
|
|
|
status = MV_PlayVOC(ptr, pitchoffset, mid, left, right, priority, callbackval);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PlayVOC
|
|
|
|
Begin playback of sound data with the given sound levels and
|
|
priority.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_PlayVOC(char *ptr, int pitchoffset, int vol, int left, int right, int priority, unsigned long callbackval)
|
|
{
|
|
int status;
|
|
|
|
status = MV_PlayLoopedVOC(ptr, -1, -1, pitchoffset, vol, left, right, priority, callbackval);
|
|
|
|
return(status);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PlayLoopedVOC
|
|
|
|
Begin playback of sound data with the given sound levels and
|
|
priority.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_PlayLoopedVOC(char *ptr, long loopstart, long loopend, int pitchoffset, int vol, int left, int right, int priority, unsigned long callbackval)
|
|
{
|
|
VoiceNode *voice;
|
|
int status;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
MV_SetErrorCode(MV_NotInstalled);
|
|
return(MV_Error);
|
|
}
|
|
|
|
// Make sure it's a valid VOC file.
|
|
status = strncmp(ptr, "Creative Voice File", 19);
|
|
if (status != 0)
|
|
{
|
|
MV_SetErrorCode(MV_InvalidVOCFile);
|
|
return(MV_Error);
|
|
}
|
|
|
|
// Request a voice from the voice pool
|
|
voice = MV_AllocVoice(priority);
|
|
if (voice == NULL)
|
|
{
|
|
MV_SetErrorCode(MV_NoVoices);
|
|
return(MV_Error);
|
|
}
|
|
|
|
voice->wavetype = VOC;
|
|
voice->bits = 8;
|
|
voice->GetSound = MV_GetNextVOCBlock;
|
|
voice->NextBlock = ptr + *(unsigned short int *)(ptr + 0x14);
|
|
voice->DemandFeed = NULL;
|
|
voice->LoopStart = NULL;
|
|
voice->LoopCount = 0;
|
|
voice->BlockLength = 0;
|
|
voice->PitchScale = PITCH_GetScale(pitchoffset);
|
|
voice->length = 0;
|
|
voice->next = NULL;
|
|
voice->prev = NULL;
|
|
voice->priority = priority;
|
|
voice->callbackval = callbackval;
|
|
voice->LoopStart = (char *)loopstart;
|
|
voice->LoopEnd = (char *)loopend;
|
|
voice->LoopSize = loopend - loopstart + 1;
|
|
|
|
if (loopstart < 0)
|
|
{
|
|
voice->LoopStart = NULL;
|
|
voice->LoopEnd = NULL;
|
|
}
|
|
|
|
MV_SetVoiceVolume(voice, vol, left, right);
|
|
MV_PlayVoice(voice);
|
|
|
|
return(voice->handle);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_CreateVolumeTable
|
|
|
|
Create the table used to convert sound data to a specific volume
|
|
level.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_CreateVolumeTable(int index, int volume, int MaxVolume)
|
|
{
|
|
int val;
|
|
int level;
|
|
int i;
|
|
|
|
level = (volume * MaxVolume) / MV_MaxTotalVolume;
|
|
if (MV_Bits == 16)
|
|
{
|
|
for (i = 0; i < 65536; i += 256)
|
|
{
|
|
val = i - 0x8000;
|
|
val *= level;
|
|
val /= MV_MaxVolume;
|
|
MV_VolumeTable[ index ][ i / 256 ] = val;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
val = i - 0x80;
|
|
val *= level;
|
|
val /= MV_MaxVolume;
|
|
MV_VolumeTable[ volume ][ i ] = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_CalcVolume
|
|
|
|
Create the table used to convert sound data to a specific volume
|
|
level.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_CalcVolume(int MaxVolume)
|
|
{
|
|
int volume;
|
|
|
|
for (volume = 0; volume < 128; volume++)
|
|
{
|
|
MV_HarshClipTable[ volume ] = 0;
|
|
MV_HarshClipTable[ volume + 384 ] = 255;
|
|
}
|
|
for (volume = 0; volume < 256; volume++)
|
|
{
|
|
MV_HarshClipTable[ volume + 128 ] = volume;
|
|
}
|
|
|
|
// For each volume level, create a translation table with the
|
|
// appropriate volume calculated.
|
|
for (volume = 0; volume <= MV_MaxVolume; volume++)
|
|
{
|
|
MV_CreateVolumeTable(volume, volume, MaxVolume);
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_CalcPanTable
|
|
|
|
Create the table used to determine the stereo volume level of
|
|
a sound located at a specific angle and distance from the listener.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_CalcPanTable(void)
|
|
{
|
|
int level;
|
|
int angle;
|
|
int distance;
|
|
int HalfAngle;
|
|
int ramp;
|
|
|
|
HalfAngle = (MV_NumPanPositions / 2);
|
|
|
|
for (distance = 0; distance <= MV_MaxVolume; distance++)
|
|
{
|
|
level = (255 * (MV_MaxVolume - distance)) / MV_MaxVolume;
|
|
for (angle = 0; angle <= HalfAngle / 2; angle++)
|
|
{
|
|
ramp = level - ((level * angle) /
|
|
(MV_NumPanPositions / 4));
|
|
|
|
MV_PanTable[ angle ][ distance ].left = ramp;
|
|
MV_PanTable[ HalfAngle - angle ][ distance ].left = ramp;
|
|
MV_PanTable[ HalfAngle + angle ][ distance ].left = level;
|
|
MV_PanTable[ MV_MaxPanPosition - angle ][ distance ].left = level;
|
|
|
|
MV_PanTable[ angle ][ distance ].right = level;
|
|
MV_PanTable[ HalfAngle - angle ][ distance ].right = level;
|
|
MV_PanTable[ HalfAngle + angle ][ distance ].right = ramp;
|
|
MV_PanTable[ MV_MaxPanPosition - angle ][ distance ].right = ramp;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetVolume
|
|
|
|
Sets the volume of digitized sound playback.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_SetVolume(int volume)
|
|
{
|
|
volume = max(0, volume);
|
|
volume = min(volume, MV_MaxTotalVolume);
|
|
|
|
MV_TotalVolume = volume;
|
|
|
|
// Calculate volume table
|
|
MV_CalcVolume(volume);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetVolume
|
|
|
|
Returns the volume of digitized sound playback.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_GetVolume(void)
|
|
{
|
|
return(MV_TotalVolume);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetCallBack
|
|
|
|
Set the function to call when a voice stops.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_SetCallBack(void(*function)(unsigned long))
|
|
{
|
|
MV_CallBackFunc = function;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_SetReverseStereo
|
|
|
|
Set the orientation of the left and right channels.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_SetReverseStereo(int setting)
|
|
{
|
|
MV_SwapLeftRight = setting;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetReverseStereo
|
|
|
|
Returns the orientation of the left and right channels.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_GetReverseStereo(void)
|
|
{
|
|
return(MV_SwapLeftRight);
|
|
}
|
|
|
|
#if 0
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_TestPlayback
|
|
|
|
Checks if playback has started.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_TestPlayback(void)
|
|
{
|
|
unsigned flags;
|
|
long time;
|
|
int start;
|
|
int status;
|
|
int pos;
|
|
|
|
flags = DisableInterrupts();
|
|
_enable();
|
|
|
|
status = MV_Error;
|
|
start = MV_MixPage;
|
|
time = clock() + CLOCKS_PER_SEC * 2;
|
|
|
|
while (clock() < time)
|
|
{
|
|
if (MV_MixPage != start)
|
|
{
|
|
status = MV_Ok;
|
|
}
|
|
}
|
|
|
|
RestoreInterrupts(flags);
|
|
|
|
if (status != MV_Ok)
|
|
{
|
|
// Just in case an error doesn't get reported
|
|
MV_SetErrorCode(MV_DMAFailure);
|
|
|
|
switch (MV_SoundCard)
|
|
{
|
|
case SoundBlaster :
|
|
pos = BLASTER_GetCurrentPos();
|
|
break;
|
|
|
|
default :
|
|
MV_SetErrorCode(MV_UnsupportedCard);
|
|
pos = -2;
|
|
break;
|
|
}
|
|
|
|
if (pos > 0)
|
|
{
|
|
MV_SetErrorCode(MV_IrqFailure);
|
|
}
|
|
else if (pos == 0)
|
|
{
|
|
if (MV_Bits == 16)
|
|
{
|
|
MV_SetErrorCode(MV_DMA16Failure);
|
|
}
|
|
else
|
|
{
|
|
MV_SetErrorCode(MV_DMAFailure);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(status);
|
|
}
|
|
#endif
|
|
|
|
int USRHOOKS_GetMem(void **ptr, unsigned long size)
|
|
{
|
|
*ptr = malloc(size);
|
|
|
|
if (*ptr == NULL)
|
|
return(USRHOOKS_Error);
|
|
|
|
return(USRHOOKS_Ok);
|
|
|
|
}
|
|
|
|
int USRHOOKS_FreeMem(void *ptr)
|
|
{
|
|
free(ptr);
|
|
return(USRHOOKS_Ok);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Init
|
|
|
|
Perform the initialization of variables and memory used by
|
|
Multivoc.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_Init(int soundcard, int MixRate, int Voices, int numchannels, int samplebits)
|
|
{
|
|
char *ptr;
|
|
int status;
|
|
int buffer;
|
|
int index;
|
|
|
|
if (MV_Installed)
|
|
{
|
|
MV_Shutdown();
|
|
}
|
|
|
|
initprintf("Initializing MultiVoc...\n");
|
|
|
|
MV_SetErrorCode(MV_Ok);
|
|
|
|
MV_TotalMemory = Voices * sizeof(VoiceNode) + sizeof(HARSH_CLIP_TABLE_8);
|
|
status = USRHOOKS_GetMem((void **)&ptr, MV_TotalMemory);
|
|
if (status != USRHOOKS_Ok)
|
|
{
|
|
MV_SetErrorCode(MV_NoMem);
|
|
return(MV_Error);
|
|
}
|
|
|
|
MV_Voices = (VoiceNode *)ptr;
|
|
MV_HarshClipTable = ptr + (MV_TotalMemory - sizeof(HARSH_CLIP_TABLE_8));
|
|
|
|
// Set number of voices before calculating volume table
|
|
MV_MaxVoices = Voices;
|
|
|
|
initprintf(" - Maximum voices: %d\n", MV_MaxVoices);
|
|
|
|
LL_Reset(&VoiceList, next, prev);
|
|
LL_Reset(&VoicePool, next, prev);
|
|
|
|
for (index = 0; index < Voices; index++)
|
|
{
|
|
LL_Add(&VoicePool, &MV_Voices[ index ], next, prev);
|
|
}
|
|
|
|
// Set the sampling rate
|
|
MV_RequestedMixRate = MixRate;
|
|
|
|
initprintf(" - Using %d byte mixing buffers\n", MixBufferSize);
|
|
|
|
// Allocate mix buffer within 1st megabyte
|
|
ptr = (char *)malloc(TotalBufferSize + 8); // FIXME: temporarily fixes bounds error somewhere...
|
|
if (!ptr)
|
|
{
|
|
USRHOOKS_FreeMem(MV_Voices);
|
|
MV_Voices = NULL;
|
|
MV_TotalMemory = 0;
|
|
|
|
MV_SetErrorCode(MV_NoMem);
|
|
return(MV_Error);
|
|
}
|
|
|
|
MV_MixBufferPtr = ptr;
|
|
|
|
MV_SetReverseStereo(FALSE);
|
|
|
|
// Initialize the sound card
|
|
#if defined(_WIN32)
|
|
status = DSOUND_Init(soundcard, MixRate, numchannels, samplebits, TotalBufferSize);
|
|
if (status != DSOUND_Ok)
|
|
{
|
|
MV_SetErrorCode(MV_BlasterError);
|
|
}
|
|
#else
|
|
status = DSL_Init();
|
|
if (status != DSL_Ok)
|
|
{
|
|
MV_SetErrorCode(MV_BlasterError);
|
|
}
|
|
#endif
|
|
if (MV_ErrorCode != MV_Ok)
|
|
{
|
|
status = MV_ErrorCode;
|
|
|
|
USRHOOKS_FreeMem(MV_Voices);
|
|
MV_Voices = NULL;
|
|
MV_TotalMemory = 0;
|
|
|
|
free(ptr);
|
|
|
|
MV_SetErrorCode(status);
|
|
return(MV_Error);
|
|
}
|
|
|
|
MV_SoundCard = soundcard;
|
|
MV_Installed = TRUE;
|
|
MV_CallBackFunc = NULL;
|
|
MV_RecordFunc = NULL;
|
|
MV_Recording = FALSE;
|
|
MV_ReverbLevel = 0;
|
|
MV_ReverbTable = NULL;
|
|
|
|
// Set Mixer to play stereo digitized sound
|
|
MV_SetMixMode(numchannels, samplebits);
|
|
MV_ReverbDelay = MV_BufferSize * 3;
|
|
|
|
//MV_MixBuffer[ MV_NumberOfBuffers ] = ptr;
|
|
for (buffer = 0; buffer < MV_NumberOfBuffers; buffer++)
|
|
{
|
|
MV_MixBuffer[ buffer ] = ptr;
|
|
ptr += MV_BufferSize;
|
|
}
|
|
|
|
// Calculate pan table
|
|
MV_CalcPanTable();
|
|
|
|
MV_SetVolume(MV_MaxTotalVolume);
|
|
|
|
// Start the playback engine
|
|
status = MV_StartPlayback();
|
|
if (status != MV_Ok)
|
|
{
|
|
// Preserve error code while we shutdown.
|
|
status = MV_ErrorCode;
|
|
MV_Shutdown();
|
|
MV_SetErrorCode(status);
|
|
return(MV_Error);
|
|
}
|
|
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Shutdown
|
|
|
|
Restore any resources allocated by Multivoc back to the system.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_Shutdown(void)
|
|
{
|
|
int buffer;
|
|
unsigned flags;
|
|
|
|
if (!MV_Installed)
|
|
{
|
|
return(MV_Ok);
|
|
}
|
|
|
|
initprintf("Uninitializing MultiVoc...\n");
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
MV_KillAllVoices();
|
|
|
|
MV_Installed = FALSE;
|
|
|
|
// Stop the sound recording engine
|
|
if (MV_Recording)
|
|
{
|
|
//MV_StopRecord();
|
|
}
|
|
|
|
// Stop the sound playback engine
|
|
MV_StopPlayback();
|
|
|
|
// Shutdown the sound card
|
|
#if defined(_WIN32)
|
|
DSOUND_Shutdown();
|
|
#else
|
|
DSL_Shutdown();
|
|
#endif
|
|
|
|
RestoreInterrupts(flags);
|
|
|
|
// Free any voices we allocated
|
|
USRHOOKS_FreeMem(MV_Voices);
|
|
MV_Voices = NULL;
|
|
MV_TotalMemory = 0;
|
|
|
|
LL_Reset(&VoiceList, next, prev);
|
|
LL_Reset(&VoicePool, next, prev);
|
|
|
|
MV_MaxVoices = 1;
|
|
|
|
// Release the descriptor from our mix buffer
|
|
if (MV_MixBufferPtr) free(MV_MixBufferPtr);
|
|
MV_MixBufferPtr = NULL;
|
|
|
|
for (buffer = 0; buffer < NumberOfBuffers; buffer++)
|
|
{
|
|
MV_MixBuffer[ buffer ] = NULL;
|
|
}
|
|
|
|
return(MV_Ok);
|
|
}
|
|
|
|
|