aboutsummaryrefslogtreecommitdiff
path: root/games/eduke32/files/patch-source__jaudiolib__unixvoc.c
diff options
context:
space:
mode:
Diffstat (limited to 'games/eduke32/files/patch-source__jaudiolib__unixvoc.c')
-rw-r--r--games/eduke32/files/patch-source__jaudiolib__unixvoc.c2880
1 files changed, 2880 insertions, 0 deletions
diff --git a/games/eduke32/files/patch-source__jaudiolib__unixvoc.c b/games/eduke32/files/patch-source__jaudiolib__unixvoc.c
new file mode 100644
index 000000000000..eb475a196596
--- /dev/null
+++ b/games/eduke32/files/patch-source__jaudiolib__unixvoc.c
@@ -0,0 +1,2880 @@
+--- ./source/jaudiolib/unixvoc.c.orig Wed Aug 2 00:35:30 2006
++++ ./source/jaudiolib/unixvoc.c Wed Aug 2 00:35:30 2006
+@@ -0,0 +1,2877 @@
++/*
++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.
++
++ou should have received a copy of the GNU General Public License
++long with this program; if not, write to the Free Software
++Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++
++*/
++/**********************************************************************
++ 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 <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++
++#include "util.h"
++#include "dpmi.h"
++#include "usrhooks.h"
++#include "interrup.h"
++#include "dma.h"
++#include "linklist.h"
++#include "dsl.h"
++
++#include "pitch.h"
++#include "multivoc.h"
++#include "_multivc.h"
++#include "debugio.h"
++
++// platform.h is from the build engine, but I need the byteswapping macros... --ryan.
++#include "platform.h"
++
++#define RoundFixed( fixedval, bits ) \
++ ( \
++ ( \
++ (fixedval) + ( 1 << ( (bits) - 1 ) )\
++ ) >> (bits) \
++ )
++
++#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 = MixBufferSize;
++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_DMAChannel = -1;
++static int MV_BuffShift;
++
++static int MV_TotalMemory;
++
++static int MV_BufferDescriptor;
++static int MV_BufferEmpty[ NumberOfBuffers ];
++char *MV_MixBuffer[ NumberOfBuffers + 1 ];
++
++static VoiceNode *MV_Voices = NULL;
++
++static volatile VoiceNode VoiceList;
++static volatile VoiceNode VoicePool;
++
++/*static*/ 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_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:
++
++**********************************************************************/
++
++
++#define MV_LockStart MV_Mix
++
++
++/*---------------------------------------------------------------------
++ 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 != NULL ) &&
++ ( 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 flags;
++
++ flags = DisableInterrupts();
++
++ // move the voice from the play list to the free list
++ LL_Remove( voice, next, prev );
++ LL_Add( (VoiceNode *)&VoicePool, voice, next, prev );
++
++ RestoreInterrupts( flags );
++ }
++
++
++/*---------------------------------------------------------------------
++ Function: MV_ServiceVoc
++
++ Starts playback of the waiting buffer and mixes the next one.
++---------------------------------------------------------------------*/
++
++// static int backcolor = 1;
++
++static int MV_ServiceVoc(int dummy_arg)
++ {
++ VoiceNode *voice;
++ VoiceNode *next;
++ char *buffer;
++
++ // 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, (const VOLUME16 *)MV_ReverbTable, count / 2 );
++ else
++ MV_16BitReverbFast( source, dest, count / 2, MV_ReverbLevel );
++ }
++ else
++ {
++ if ( MV_ReverbTable != NULL )
++ MV_8BitReverb( source, dest, (const VOLUME16 *)MV_ReverbTable, count );
++ else
++ MV_8BitReverbFast( source, 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;
++
++ if (MV_MixFunction != NULL)
++ 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 );
++ }
++ }
++ }
++ }
++
++
++int leftpage = -1;
++int rightpage = -1;
++
++void MV_ServiceGus( char **ptr, unsigned long *length )
++ {
++ if ( leftpage == MV_MixPage )
++ {
++ MV_ServiceVoc(0);
++ }
++
++ leftpage = MV_MixPage;
++
++ *ptr = MV_MixBuffer[ MV_MixPage ];
++ *length = MV_BufferSize;
++ }
++
++void MV_ServiceRightGus( char **ptr, unsigned long *length )
++ {
++ if ( rightpage == MV_MixPage )
++ {
++ MV_ServiceVoc(0);
++ }
++
++ rightpage = MV_MixPage;
++
++ *ptr = MV_MixBuffer[ MV_MixPage ] + MV_RightChannelOffset;
++ *length = MV_BufferSize;
++ }
++
++/*---------------------------------------------------------------------
++ Function: MV_GetNextVOCBlock
++
++ Interperate the information of a VOC format sound file.
++---------------------------------------------------------------------*/
++static __inline unsigned int get_le32(void *p0)
++{
++ //unsigned char *p = p0;
++ //return p[0] | (p[1]<<8) | (p[2]<<16) | (p[3]<<24);
++ unsigned int val = *((unsigned int *) p0);
++ return(BUILDSWAP_INTEL32(val));
++}
++
++static __inline unsigned int get_le16(void *p0)
++{
++ //unsigned char *p = p0;
++ //return p[0] | (p[1]<<8);
++ unsigned short val = *((unsigned short *) p0);
++ return( (unsigned int) (BUILDSWAP_INTEL16(val)) );
++}
++
++playbackstatus MV_GetNextVOCBlock
++ (
++ VoiceNode *voice
++ )
++
++ {
++ unsigned char *ptr;
++ int blocktype=0;
++ int lastblocktype=0;
++ unsigned long blocklength=0l;
++ unsigned long samplespeed=0l;
++ unsigned int tc=0;
++ int packtype=0;
++ int voicemode=0;
++ int done=0;
++ 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;
++ }
++
++ {
++ unsigned tmp = get_le32(ptr);
++ blocktype = tmp&255;
++ blocklength = tmp>>8;
++ }
++ ptr += 4;
++
++ switch( blocktype )
++ {
++ case 0 :
++ // End of data
++ if ( ( voice->LoopStart == NULL ) ||
++ ( (unsigned char *)voice->LoopStart >= ( ptr - 4 ) ) )
++ {
++ voice->Playing = FALSE;
++ done = TRUE;
++ }
++ else
++ {
++ voice->BlockLength = ( ptr - 4 ) - (unsigned char *)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 = get_le16(ptr);
++ voice->LoopStart = 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 = voice->LoopStart;
++ if ( voice->LoopCount < 0xffff )
++ {
++ voice->LoopCount--;
++ if ( voice->LoopCount == 0 )
++ {
++ voice->LoopStart = NULL;
++ }
++ }
++ }
++ }
++ break;
++
++ case 8 :
++ // Extended block
++ voice->bits = 8;
++ tc = get_le16(ptr);
++ packtype = *( ptr + 2 );
++ voicemode = *( ptr + 3 );
++ ptr += blocklength;
++ break;
++
++ case 9 :
++ // New sound data block
++ samplespeed = get_le32(ptr);
++ BitsPerSample = ptr[4];
++ Channels = ptr[5];
++ Format = get_le16(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 = ptr + blocklength;
++ voice->sound = 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.
++---------------------------------------------------------------------*/
++
++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;
++ }
++ }
++
++
++/*---------------------------------------------------------------------
++ 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 );
++
++ // SBF - should this return null?
++ return 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( 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;
++ }
++
++ MV_MixMode = mode;
++
++ 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;
++
++ status = DSL_BeginBufferedPlayback( MV_MixBuffer[ 0 ],
++ TotalBufferSize, MV_NumberOfBuffers,
++ MV_RequestedMixRate, MV_MixMode, MV_ServiceVoc );
++
++ if ( status != DSL_Ok )
++ {
++ MV_SetErrorCode( MV_BlasterError );
++ return( MV_Error );
++ }
++
++ MV_MixRate = DSL_GetPlaybackRate();
++
++ return( MV_Ok );
++ }
++
++
++/*---------------------------------------------------------------------
++ Function: MV_StopPlayback
++
++ Stops the sound playback engine.
++---------------------------------------------------------------------*/
++
++void MV_StopPlayback
++ (
++ void
++ )
++
++ {
++ VoiceNode *voice;
++ VoiceNode *next;
++ unsigned flags;
++
++ DSL_StopPlayback();
++
++ // 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 );
++ }
++
++
++/*---------------------------------------------------------------------
++ Function: MV_StartRecording
++
++ Starts the sound recording engine.
++---------------------------------------------------------------------*/
++
++int MV_StartRecording
++ (
++ int MixRate,
++ void ( *function )( char *ptr, int length )
++ )
++
++ {
++ MV_SetErrorCode( MV_UnsupportedCard );
++ return( MV_Error );
++ }
++
++
++/*---------------------------------------------------------------------
++ Function: MV_StopRecord
++
++ Stops the sound record engine.
++---------------------------------------------------------------------*/
++
++void MV_StopRecord
++ (
++ void
++ )
++
++ {
++ }
++
++
++/*---------------------------------------------------------------------
++ 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( 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, (long)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 >= (long)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;
++ unsigned short nextpos;
++
++ 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;
++
++ nextpos = *( unsigned short * )( ptr + 0x14 );
++ voice->NextBlock = ptr + BUILDSWAP_INTEL16(nextpos);
++
++ 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_LockEnd
++
++ Used for determining the length of the functions to lock in memory.
++---------------------------------------------------------------------*/
++
++static void MV_LockEnd
++ (
++ void
++ )
++
++ {
++ }
++
++
++/*---------------------------------------------------------------------
++ 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 );
++ }
++
++
++/*---------------------------------------------------------------------
++ Function: MV_TestPlayback
++
++ Checks if playback has started.
++---------------------------------------------------------------------*/
++
++int MV_TestPlayback(void)
++ {
++ return MV_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();
++ }
++
++ 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;
++
++ LL_Reset( (VoiceNode *)&VoiceList, next, prev );
++ LL_Reset( (VoiceNode *)&VoicePool, next, prev );
++
++ for( index = 0; index < Voices; index++ )
++ {
++ LL_Add( (VoiceNode *)&VoicePool, &MV_Voices[ index ], next, prev );
++ }
++
++ // Allocate mix buffer within 1st megabyte
++ status = DPMI_GetDOSMemory( ( void ** )&ptr, &MV_BufferDescriptor,
++ 2 * TotalBufferSize );
++
++ if ( status )
++ {
++ USRHOOKS_FreeMem( MV_Voices );
++ MV_Voices = NULL;
++ MV_TotalMemory = 0;
++
++ MV_SetErrorCode( MV_NoMem );
++ return( MV_Error );
++ }
++
++ MV_SetReverseStereo( FALSE );
++
++ // Initialize the sound card
++ status = DSL_Init();
++ if ( status != DSL_Ok )
++ {
++ MV_SetErrorCode( MV_BlasterError );
++ }
++
++ if ( MV_ErrorCode != MV_Ok )
++ {
++ status = MV_ErrorCode;
++
++ USRHOOKS_FreeMem( MV_Voices );
++ MV_Voices = NULL;
++ MV_TotalMemory = 0;
++
++ DPMI_FreeDOSMemory( MV_BufferDescriptor );
++
++ 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 the sampling rate
++ MV_RequestedMixRate = MixRate;
++
++ // 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 );
++ }
++
++ if ( MV_TestPlayback() != MV_Ok )
++ {
++ 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 );
++ }
++
++ 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
++ DSL_Shutdown();
++
++ RestoreInterrupts( flags );
++
++ // Free any voices we allocated
++ USRHOOKS_FreeMem( MV_Voices );
++ MV_Voices = NULL;
++ MV_TotalMemory = 0;
++
++ LL_Reset( (VoiceNode *)&VoiceList, next, prev );
++ LL_Reset( (VoiceNode *)&VoicePool, next, prev );
++
++ MV_MaxVoices = 1;
++
++ // Release the descriptor from our mix buffer
++ DPMI_FreeDOSMemory( MV_BufferDescriptor );
++ for( buffer = 0; buffer < NumberOfBuffers; buffer++ )
++ {
++ MV_MixBuffer[ buffer ] = NULL;
++ }
++
++ return( MV_Ok );
++ }