freebsd-ports/games/jfsw/files/patch-jfsw_src_20051009-source-jaudiolib-unixvoc.c
Ganael LAPLANCHE 867d8ef619 - Fix build on recent FreeBSD (10, 11)
- Add LICENSE and update LEGAL accordingly
- Update USES
- Use NO_WRKSUBDIR/BUILD_WRKSRC instead of WRKSRC
- Enable staging (and always stage documentation)
- Rework patches: split them and convert them to UNIX file format (use dos2unix)
2014-01-13 08:20:30 +00:00

2880 lines
67 KiB
C

--- jfsw_src_20051009/source/jaudiolib/unixvoc.c.orig 1970-01-01 01:00:00.000000000 +0100
+++ jfsw_src_20051009/source/jaudiolib/unixvoc.c 2005-10-10 15:02:08.000000000 +0200
@@ -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 );
+ }