2132 lines
50 KiB
C
2132 lines
50 KiB
C
/*
|
|
Copyright (C) 1994-1995 Apogee Software, Ltd.
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
/**********************************************************************
|
|
module: MULTIVOC.C
|
|
|
|
author: James R. Dose
|
|
date: December 20, 1993
|
|
|
|
Routines to provide multichannel digitized sound playback for
|
|
Sound Blaster compatible sound cards.
|
|
|
|
(c) Copyright 1993 James R. Dose. All Rights Reserved.
|
|
**********************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include "dpmi.h"
|
|
#include "usrhooks.h"
|
|
#include "interrup.h"
|
|
#include "dpmi.h"
|
|
#include "ll_man.h"
|
|
#include "sndcards.h"
|
|
#include "blaster.h"
|
|
#include "sndsrc.h"
|
|
#include "pas16.h"
|
|
#include "pitch.h"
|
|
#include "multivoc.h"
|
|
#include "_multivc.h"
|
|
|
|
static signed char MV_VolumeTable[ sizeof( VOLUME_TABLE_16BIT ) ];
|
|
|
|
static VOLUME_TABLE_8BIT *MV_8BitVolumeTable = &MV_VolumeTable[ 0 ];
|
|
static VOLUME_TABLE_16BIT *MV_16BitVolumeTable = &MV_VolumeTable[ 0 ];
|
|
|
|
static Pan MV_PanTable[ MV_NumPanPositions ][ MV_MaxVolume + 1 ];
|
|
|
|
static int MV_Installed = FALSE;
|
|
static int MV_SoundCard = SoundBlaster;
|
|
static int MV_TotalVolume = MV_MaxTotalVolume;
|
|
static int MV_MaxVoices = 1;
|
|
|
|
static int MV_BufferSize = MixBufferSize;
|
|
static int MV_SampleSize = 1;
|
|
static int MV_NumberOfBuffers = NumberOfBuffers;
|
|
|
|
static int MV_MixMode = MONO_8BIT;
|
|
static int MV_Silence = SILENCE_8BIT;
|
|
static int MV_SwapLeftRight = FALSE;
|
|
|
|
static int MV_RequestedMixRate;
|
|
static int MV_MixRate;
|
|
|
|
static int MV_BufferDescriptor;
|
|
static char *MV_MixBuffer[ NumberOfBuffers ];
|
|
|
|
static VoiceNode *MV_Voices = NULL;
|
|
//static VoiceNode MV_Voices[ 16 ];
|
|
|
|
static volatile VList VoiceList = { NULL, NULL };
|
|
static volatile VList VoicePool = { NULL, NULL };
|
|
|
|
static int MV_MixPage = 0;
|
|
static int MV_PlayPage = 0;
|
|
static int MV_VoiceHandle = MV_MinVoiceHandle;
|
|
|
|
static void ( *MV_CallBackFunc )( unsigned long ) = NULL;
|
|
|
|
//char HarshClipTable[ 8 * 256 ];
|
|
//unsigned short HarshClipTable16[ 8 * 256 * 16 ];
|
|
|
|
char *HarshClipTable;
|
|
unsigned short *HarshClipTable16;
|
|
short *HarshClipTable16s;
|
|
|
|
int MV_ErrorCode = MV_Ok;
|
|
|
|
#define MV_SetErrorCode( status ) \
|
|
MV_ErrorCode = ( status );
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_ErrorString
|
|
|
|
Returns a pointer to the error message associated with an error
|
|
number. A -1 returns a pointer the current error.
|
|
---------------------------------------------------------------------*/
|
|
|
|
char *MV_ErrorString
|
|
(
|
|
int ErrorNumber
|
|
)
|
|
|
|
{
|
|
char *ErrorString;
|
|
|
|
switch( ErrorNumber )
|
|
{
|
|
case MV_Warning :
|
|
case MV_Error :
|
|
ErrorString = MV_ErrorString( MV_ErrorCode );
|
|
break;
|
|
|
|
case MV_Ok :
|
|
ErrorString = "Multivoc ok.";
|
|
break;
|
|
|
|
case MV_UnsupportedCard :
|
|
ErrorString = "Selected sound card is not supported by Multivoc.";
|
|
break;
|
|
|
|
case MV_NotInstalled :
|
|
ErrorString = "Multivoc not installed.";
|
|
break;
|
|
|
|
case MV_NoVoices :
|
|
ErrorString = "No free voices available to Multivoc.";
|
|
break;
|
|
|
|
case MV_NoMem :
|
|
ErrorString = "Out of memory in Multivoc.";
|
|
break;
|
|
|
|
case MV_VoiceNotFound :
|
|
ErrorString = "No voice with matching handle found.";
|
|
break;
|
|
|
|
case MV_BlasterError :
|
|
ErrorString = BLASTER_ErrorString( BLASTER_Error );
|
|
break;
|
|
|
|
case MV_PasError :
|
|
ErrorString = PAS_ErrorString( PAS_Error );
|
|
break;
|
|
|
|
#ifndef SOUNDSOURCE_OFF
|
|
case MV_SoundSourceError :
|
|
ErrorString = SS_ErrorString( SS_Error );
|
|
break;
|
|
#endif
|
|
|
|
case MV_DPMI_Error :
|
|
ErrorString = "DPMI Error in Multivoc.";
|
|
break;
|
|
|
|
default :
|
|
ErrorString = "Unknown Multivoc error code.";
|
|
break;
|
|
}
|
|
|
|
return( ErrorString );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
Memory locked functions:
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
#define MV_LockStart MV_Mix8bitMono
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Mix8bitMono
|
|
|
|
Mixes the sound into the buffer as an 8 bit mono sample.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_Mix8bitMono
|
|
(
|
|
VoiceNode *voice,
|
|
int buffer
|
|
)
|
|
|
|
{
|
|
// MONO8 *to;
|
|
char *to;
|
|
VOLUME8 *VolumeTable;
|
|
char *start;
|
|
char *from;
|
|
unsigned long position;
|
|
int length;
|
|
unsigned long rate;
|
|
|
|
int samp;
|
|
|
|
to = ( MONO8 * )MV_MixBuffer[ buffer ];
|
|
|
|
VolumeTable = ( VOLUME8 * )&( *MV_8BitVolumeTable )[ voice->Volume ];
|
|
|
|
start = voice->sound;
|
|
rate = voice->RateScale;
|
|
position = voice->position;
|
|
|
|
length = MixBufferSize;
|
|
|
|
// Add this voice to the mix
|
|
while( length > 0 )
|
|
{
|
|
if ( position >= voice->length )
|
|
{
|
|
voice->position = position;
|
|
MV_GetNextVOCBlock( voice );
|
|
if ( voice->Playing )
|
|
{
|
|
start = voice->sound;
|
|
rate = voice->RateScale;
|
|
position = voice->position;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// from = start + ( position >> 16 );
|
|
// *to += ( *VolumeTable )[ *from ];
|
|
from = start + ( position >> 16 );
|
|
samp = ( ( int )*to ) + ( *VolumeTable )[ *from ];
|
|
*to = HarshClipTable[ 4 * 256 + samp - 0x80 ];
|
|
|
|
to++;
|
|
position += rate;
|
|
|
|
length--;
|
|
}
|
|
|
|
voice->position = position;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Mix8bitStereo
|
|
|
|
Mixes the sound into the buffer as an 8 bit stereo sample.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_Mix8bitStereo
|
|
(
|
|
VoiceNode *voice,
|
|
int buffer
|
|
)
|
|
|
|
{
|
|
STEREO8 *to;
|
|
VOLUME8 *LeftVolumeTable;
|
|
VOLUME8 *RightVolumeTable;
|
|
char *start;
|
|
unsigned long position;
|
|
int length;
|
|
// int sample;
|
|
unsigned long rate;
|
|
|
|
char *from;
|
|
int samp;
|
|
|
|
to = ( STEREO8 * )MV_MixBuffer[ buffer ];
|
|
|
|
LeftVolumeTable = ( VOLUME8 * )&( *MV_8BitVolumeTable )[ voice->LeftVolume ];
|
|
RightVolumeTable = ( VOLUME8 * )&( *MV_8BitVolumeTable )[ voice->RightVolume ];
|
|
|
|
start = voice->sound;
|
|
rate = voice->RateScale;
|
|
position = voice->position;
|
|
|
|
length = MixBufferSize;
|
|
|
|
// Add this voice to the mix
|
|
while( length > 0 )
|
|
{
|
|
if ( position >= voice->length )
|
|
{
|
|
voice->position = position;
|
|
MV_GetNextVOCBlock( voice );
|
|
if ( voice->Playing )
|
|
{
|
|
start = voice->sound;
|
|
rate = voice->RateScale;
|
|
position = voice->position;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
// sample = ( int )*( start + ( position >> 16 ) );
|
|
// to->left += ( *LeftVolumeTable )[ sample ];
|
|
// to->right += ( *RightVolumeTable )[ sample ];
|
|
|
|
from = start + ( position >> 16 );
|
|
samp = ( ( int )to->left ) + ( *LeftVolumeTable )[ *from ];
|
|
to->left = HarshClipTable[ 4 * 256 + samp - 0x80 ];
|
|
samp = ( ( int )to->right ) + ( *RightVolumeTable )[ *from ];
|
|
to->right = HarshClipTable[ 4 * 256 + samp - 0x80 ];
|
|
|
|
to++;
|
|
position += rate;
|
|
length--;
|
|
}
|
|
|
|
voice->position = position;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Mix16bitUnsignedMono
|
|
|
|
Mixes the sound into the buffer as an 16 bit mono sample.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_Mix16bitUnsignedMono
|
|
(
|
|
VoiceNode *voice,
|
|
int buffer
|
|
)
|
|
|
|
{
|
|
// MONO16 *to;
|
|
unsigned short *to;
|
|
VOLUME16 *VolumeTable;
|
|
char *start;
|
|
char *from;
|
|
unsigned long position;
|
|
int length;
|
|
unsigned long rate;
|
|
|
|
int samp;
|
|
|
|
// to = ( MONO16 * )MV_MixBuffer[ buffer ];
|
|
to = ( unsigned short * )MV_MixBuffer[ buffer ];
|
|
|
|
VolumeTable = ( VOLUME16 * )&( *MV_16BitVolumeTable )[ voice->Volume ];
|
|
|
|
start = voice->sound;
|
|
rate = voice->RateScale;
|
|
position = voice->position;
|
|
length = MixBufferSize;
|
|
|
|
// Add this voice to the mix
|
|
while( length > 0 )
|
|
{
|
|
if ( position >= voice->length )
|
|
{
|
|
voice->position = position;
|
|
MV_GetNextVOCBlock( voice );
|
|
if ( voice->Playing )
|
|
{
|
|
start = voice->sound;
|
|
rate = voice->RateScale;
|
|
position = voice->position;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
// from = start + ( position >> 16 );
|
|
// *to += ( *VolumeTable )[ *from ];
|
|
|
|
from = start + ( position >> 16 );
|
|
samp = ( ( *to - 0x8000 ) >> 4 ) +
|
|
( *VolumeTable )[ *from ];
|
|
*to = HarshClipTable16[ 4 * 256 * 16 + samp ];// << 4;
|
|
to++;
|
|
position += rate;
|
|
length--;
|
|
}
|
|
|
|
voice->position = position;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Mix16bitUnsignedStereo
|
|
|
|
Mixes the sound into the buffer as an 16 bit stereo sample.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_Mix16bitUnsignedStereo
|
|
(
|
|
VoiceNode *voice,
|
|
int buffer
|
|
)
|
|
|
|
{
|
|
STEREO16 *to;
|
|
VOLUME16 *LeftVolumeTable;
|
|
VOLUME16 *RightVolumeTable;
|
|
char *start;
|
|
unsigned long position;
|
|
int length;
|
|
// int sample;
|
|
unsigned long rate;
|
|
|
|
char *from;
|
|
int samp;
|
|
|
|
to = ( STEREO16 * )MV_MixBuffer[ buffer ];
|
|
|
|
LeftVolumeTable = ( VOLUME16 * )&( *MV_16BitVolumeTable )[ voice->LeftVolume ];
|
|
RightVolumeTable = ( VOLUME16 * )&( *MV_16BitVolumeTable )[ voice->RightVolume ];
|
|
|
|
start = voice->sound;
|
|
rate = voice->RateScale;
|
|
position = voice->position;
|
|
|
|
length = MixBufferSize;
|
|
|
|
// Add this voice to the mix
|
|
while( length > 0 )
|
|
{
|
|
if ( position >= voice->length )
|
|
{
|
|
voice->position = position;
|
|
MV_GetNextVOCBlock( voice );
|
|
if ( voice->Playing )
|
|
{
|
|
start = voice->sound;
|
|
rate = voice->RateScale;
|
|
position = voice->position;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
// sample = ( int )*( start + ( position >> 16 ) );
|
|
// to->left += ( *LeftVolumeTable )[ sample ];
|
|
// to->right += ( *RightVolumeTable )[ sample ];
|
|
|
|
from = start + ( position >> 16 );
|
|
samp = ( ( to->left - 0x8000 ) >> 4 ) +
|
|
( *LeftVolumeTable )[ *from ];
|
|
to->left = HarshClipTable16[ 4 * 256 * 16 + samp ];// << 4;
|
|
// to->left = ( length & 127 ) << 8;
|
|
|
|
samp = ( ( to->right - 0x8000 ) >> 4 ) +
|
|
( *RightVolumeTable )[ *from ];
|
|
to->right = HarshClipTable16[ 4 * 256 * 16 + samp ];// << 4;
|
|
// to->right = ( length & 127 ) << 8;
|
|
|
|
to++;
|
|
position += rate;
|
|
|
|
length--;
|
|
}
|
|
|
|
voice->position = position;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_PrepareBuffer
|
|
|
|
Initializes the current buffer and mixes the currently active
|
|
voices.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_PrepareBuffer
|
|
(
|
|
int page
|
|
)
|
|
|
|
{
|
|
VoiceNode *voice;
|
|
|
|
// Initialize buffer
|
|
ClearBuffer_DW( MV_MixBuffer[ page ], MV_Silence, MV_BufferSize >> 2 );
|
|
|
|
voice = VoiceList.start;
|
|
while( voice != NULL )
|
|
{
|
|
switch( MV_MixMode )
|
|
{
|
|
case MONO_8BIT :
|
|
MV_Mix8bitMono( voice, page );
|
|
break;
|
|
|
|
case STEREO_8BIT :
|
|
MV_Mix8bitStereo( voice, page );
|
|
break;
|
|
|
|
case MONO_16BIT :
|
|
MV_Mix16bitUnsignedMono( voice, page );
|
|
break;
|
|
|
|
case STEREO_16BIT :
|
|
MV_Mix16bitUnsignedStereo( voice, page );
|
|
break;
|
|
}
|
|
|
|
if ( voice->Playing )
|
|
{
|
|
voice->Active[ page ] = TRUE;
|
|
}
|
|
else
|
|
{
|
|
voice->Active[ page ] = FALSE;
|
|
}
|
|
|
|
voice = voice->next;
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_DeleteDeadVoices
|
|
|
|
Removes any voices that have finished playing.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_DeleteDeadVoices
|
|
(
|
|
int page
|
|
)
|
|
|
|
{
|
|
VoiceNode *voice;
|
|
VoiceNode *next;
|
|
unsigned flags;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
voice = VoiceList.start;
|
|
while( voice != NULL )
|
|
{
|
|
next = voice->next;
|
|
|
|
// Is this voice done?
|
|
if ( !voice->Active[ page ] )
|
|
{
|
|
// Yes, move it from the play list into the free list
|
|
LL_Remove( VoiceNode, &VoiceList, voice );
|
|
LL_AddToTail( VoiceNode, &VoicePool, voice );
|
|
|
|
if ( MV_CallBackFunc )
|
|
{
|
|
MV_CallBackFunc( voice->callbackval );
|
|
}
|
|
}
|
|
|
|
voice = next;
|
|
}
|
|
|
|
RestoreInterrupts( flags );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_ServiceVoc
|
|
|
|
Starts playback of the waiting buffer and mixes the next one.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_ServiceVoc
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int ErasePage;
|
|
|
|
ErasePage = MV_PlayPage;
|
|
|
|
// Delete any voices that are done playing
|
|
MV_DeleteDeadVoices( ErasePage );
|
|
|
|
// Set which buffer is currently being played.
|
|
MV_PlayPage = MV_MixPage;
|
|
|
|
// Toggle which buffer we'll mix next
|
|
MV_MixPage++;
|
|
if ( MV_MixPage >= MV_NumberOfBuffers )
|
|
{
|
|
MV_MixPage = 0;
|
|
}
|
|
|
|
// Play any waiting voices
|
|
MV_PrepareBuffer( MV_MixPage );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetVoice
|
|
|
|
Locates the voice with the specified handle.
|
|
---------------------------------------------------------------------*/
|
|
|
|
VoiceNode *MV_GetVoice
|
|
(
|
|
int handle
|
|
)
|
|
|
|
{
|
|
VoiceNode *voice;
|
|
unsigned flags;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
voice = VoiceList.start;
|
|
|
|
while( voice != NULL )
|
|
{
|
|
if ( handle == voice->handle )
|
|
{
|
|
break;
|
|
}
|
|
|
|
voice = voice->next;
|
|
}
|
|
|
|
RestoreInterrupts( flags );
|
|
|
|
if ( voice == NULL )
|
|
{
|
|
MV_SetErrorCode( MV_VoiceNotFound );
|
|
}
|
|
|
|
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.start != NULL )
|
|
{
|
|
MV_Kill( VoiceList.start->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;
|
|
|
|
// move the voice from the play list to the free list
|
|
LL_Remove( VoiceNode, &VoiceList, voice );
|
|
LL_AddToTail( VoiceNode, &VoicePool, voice );
|
|
|
|
RestoreInterrupts( flags );
|
|
|
|
if ( MV_CallBackFunc )
|
|
{
|
|
MV_CallBackFunc( callbackval );
|
|
}
|
|
|
|
MV_SetErrorCode( MV_Ok );
|
|
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();
|
|
|
|
voice = VoiceList.start;
|
|
while( voice != NULL )
|
|
{
|
|
NumVoices++;
|
|
voice = voice->next;
|
|
}
|
|
|
|
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;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
// Check if we have any free voices
|
|
if ( VoicePool.start == NULL )
|
|
{
|
|
// check if we have a higher priority than a voice that is playing.
|
|
node = VoiceList.start;
|
|
voice = node;
|
|
while( node != NULL )
|
|
{
|
|
if ( node->priority < voice->priority )
|
|
{
|
|
voice = node;
|
|
}
|
|
|
|
node = node->next;
|
|
}
|
|
|
|
if ( priority >= voice->priority )
|
|
{
|
|
MV_Kill( voice->handle );
|
|
}
|
|
}
|
|
|
|
// Check if any voices are in the voice pool
|
|
if ( VoicePool.start == NULL )
|
|
{
|
|
// No free voices
|
|
RestoreInterrupts( flags );
|
|
return( NULL );
|
|
}
|
|
|
|
voice = VoicePool.start;
|
|
LL_Remove( VoiceNode, &VoicePool, voice );
|
|
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_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 );
|
|
}
|
|
|
|
voice->PitchScale = PITCH_GetScale( pitchoffset );
|
|
voice->RateScale = ( voice->SamplingRate *
|
|
voice->PitchScale ) / MV_MixRate;
|
|
|
|
MV_SetErrorCode( MV_Ok );
|
|
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 );
|
|
}
|
|
|
|
voice->Volume = MIX_VOLUME( vol );
|
|
|
|
if ( MV_SwapLeftRight )
|
|
{
|
|
// SBPro uses reversed panning
|
|
voice->LeftVolume = MIX_VOLUME( right );
|
|
voice->RightVolume = MIX_VOLUME( left );
|
|
}
|
|
else
|
|
{
|
|
voice->LeftVolume = MIX_VOLUME( left );
|
|
voice->RightVolume = MIX_VOLUME( right );
|
|
}
|
|
|
|
MV_SetErrorCode( MV_Ok );
|
|
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_SetMixMode
|
|
|
|
Prepares Multivoc to play stereo of mono digitized sounds.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_SetMixMode
|
|
(
|
|
int mode
|
|
)
|
|
|
|
{
|
|
if ( !MV_Installed )
|
|
{
|
|
MV_SetErrorCode( MV_NotInstalled );
|
|
return( MV_Error );
|
|
}
|
|
|
|
switch( MV_SoundCard )
|
|
{
|
|
case SoundBlaster :
|
|
case Awe32 :
|
|
MV_MixMode = BLASTER_SetMixMode( mode );
|
|
break;
|
|
|
|
case ProAudioSpectrum :
|
|
case SoundMan16 :
|
|
MV_MixMode = PAS_SetMixMode( mode );
|
|
break;
|
|
|
|
#ifndef SOUNDSOURCE_OFF
|
|
case SoundSource :
|
|
case TandySoundSource :
|
|
MV_MixMode = SS_SetMixMode( mode );
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
MV_SampleSize = sizeof( MONO8 );
|
|
MV_Silence = SILENCE_8BIT;
|
|
|
|
switch( MV_MixMode )
|
|
{
|
|
case MONO_8BIT :
|
|
MV_SampleSize = sizeof( MONO8 );
|
|
MV_Silence = SILENCE_8BIT;
|
|
break;
|
|
|
|
case STEREO_8BIT :
|
|
MV_SampleSize = sizeof( STEREO8 );
|
|
MV_Silence = SILENCE_8BIT;
|
|
break;
|
|
|
|
case MONO_16BIT :
|
|
MV_SampleSize = sizeof( MONO16 );
|
|
// if ( ( MV_SoundCard == ProAudioSpectrum ) ||
|
|
// ( MV_SoundCard == SoundMan16 ) )
|
|
// {
|
|
// MV_Silence = SILENCE_16BIT_PAS;
|
|
// }
|
|
// else
|
|
// {
|
|
MV_Silence = SILENCE_16BIT;
|
|
// }
|
|
break;
|
|
|
|
case STEREO_16BIT :
|
|
MV_SampleSize = sizeof( STEREO16 );
|
|
// if ( ( MV_SoundCard == ProAudioSpectrum ) ||
|
|
// ( MV_SoundCard == SoundMan16 ) )
|
|
// {
|
|
// MV_Silence = SILENCE_16BIT_PAS;
|
|
// }
|
|
// else
|
|
// {
|
|
MV_Silence = SILENCE_16BIT;
|
|
// }
|
|
break;
|
|
}
|
|
|
|
MV_BufferSize = MixBufferSize * MV_SampleSize;
|
|
|
|
MV_NumberOfBuffers = TotalBufferSize / MV_BufferSize;
|
|
|
|
return( MV_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_StartPlayback
|
|
|
|
Starts the sound playback engine.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_StartPlayback
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int status;
|
|
|
|
// Initialize the buffers
|
|
ClearBuffer_DW( MV_MixBuffer[ 0 ], MV_Silence, TotalBufferSize >> 2 );
|
|
|
|
// Set the mix buffer variables
|
|
MV_PlayPage = 0;
|
|
MV_MixPage = 1;
|
|
|
|
// Start playback
|
|
switch( MV_SoundCard )
|
|
{
|
|
case SoundBlaster :
|
|
case Awe32 :
|
|
status = BLASTER_BeginBufferedPlayback( MV_MixBuffer[ 0 ],
|
|
TotalBufferSize, MV_NumberOfBuffers,
|
|
MV_RequestedMixRate, MV_MixMode, MV_ServiceVoc );
|
|
|
|
if ( status != BLASTER_Ok )
|
|
{
|
|
MV_SetErrorCode( MV_BlasterError );
|
|
return( MV_Error );
|
|
}
|
|
|
|
MV_MixRate = BLASTER_GetPlaybackRate();
|
|
break;
|
|
|
|
case ProAudioSpectrum :
|
|
case SoundMan16 :
|
|
status = PAS_BeginBufferedPlayback( MV_MixBuffer[ 0 ],
|
|
TotalBufferSize, MV_NumberOfBuffers,
|
|
MV_RequestedMixRate, MV_MixMode, MV_ServiceVoc );
|
|
|
|
if ( status != PAS_Ok )
|
|
{
|
|
MV_SetErrorCode( MV_PasError );
|
|
return( MV_Error );
|
|
}
|
|
|
|
MV_MixRate = PAS_GetPlaybackRate();
|
|
break;
|
|
|
|
#ifndef SOUNDSOURCE_OFF
|
|
case SoundSource :
|
|
case TandySoundSource :
|
|
SS_BeginBufferedPlayback( MV_MixBuffer[ 0 ],
|
|
TotalBufferSize, MV_NumberOfBuffers,
|
|
MV_ServiceVoc );
|
|
MV_MixRate = SS_SampleRate;
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
return( MV_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_StopPlayback
|
|
|
|
Stops the sound playback engine.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_StopPlayback
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
// Stop sound playback
|
|
switch( MV_SoundCard )
|
|
{
|
|
case SoundBlaster :
|
|
case Awe32 :
|
|
BLASTER_StopPlayback();
|
|
break;
|
|
|
|
case ProAudioSpectrum :
|
|
case SoundMan16 :
|
|
PAS_StopPlayback();
|
|
break;
|
|
|
|
#ifndef SOUNDSOURCE_OFF
|
|
case SoundSource :
|
|
case TandySoundSource :
|
|
SS_StopPlayback();
|
|
break;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_StartRecording
|
|
|
|
Starts the sound recording engine.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_StartRecording
|
|
(
|
|
int MixRate,
|
|
void ( *function )( char *ptr, int length )
|
|
)
|
|
|
|
{
|
|
return( MV_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
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
|
|
(
|
|
int MixRate,
|
|
int vol,
|
|
int left,
|
|
int right,
|
|
void ( *function )( char **ptr, unsigned long *length )
|
|
)
|
|
|
|
{
|
|
return( MV_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_GetNextVOCBlock
|
|
|
|
Interperate the information of a VOC format sound file.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_GetNextVOCBlock
|
|
(
|
|
VoiceNode *voice
|
|
)
|
|
|
|
{
|
|
unsigned char *ptr;
|
|
int blocktype;
|
|
int lastblocktype;
|
|
unsigned long blocklength;
|
|
unsigned long samplespeed;
|
|
unsigned int tc;
|
|
int packtype;
|
|
int voicemode;
|
|
int done;
|
|
unsigned BitsPerSample;
|
|
unsigned Channels;
|
|
unsigned Format;
|
|
|
|
if ( voice->BlockLength > 0 )
|
|
{
|
|
voice->sound += 0x8000;
|
|
voice->position -= 0x80000000;
|
|
voice->length = min( voice->BlockLength, 0x8000 );
|
|
voice->BlockLength -= voice->length;
|
|
voice->length <<= 16;
|
|
return;
|
|
}
|
|
|
|
ptr = ( unsigned char * )voice->NextBlock;
|
|
|
|
voice->Playing = TRUE;
|
|
|
|
done = FALSE;
|
|
while( !done )
|
|
{
|
|
blocktype = ( int )*ptr;
|
|
blocklength = ( *( unsigned long * )( ptr + 1 ) ) & 0x00ffffff;
|
|
ptr += 4;
|
|
|
|
voicemode = 0;
|
|
lastblocktype = 0;
|
|
switch( blocktype )
|
|
{
|
|
case 0 :
|
|
// End of data
|
|
voice->Playing = FALSE;
|
|
done = TRUE;
|
|
break;
|
|
|
|
case 1 :
|
|
// Sound data block
|
|
if ( lastblocktype != 8 )
|
|
{
|
|
tc = ( unsigned int )*ptr;
|
|
packtype = *( ptr + 1 );
|
|
}
|
|
|
|
ptr += 2;
|
|
blocklength -= 2;
|
|
|
|
samplespeed = 256000000L / ( 65536 - ( tc << 8 ) );
|
|
|
|
// Skip packed or stereo data
|
|
if ( ( packtype != 0 ) || ( voicemode != 0 ) )
|
|
{
|
|
ptr += blocklength;
|
|
}
|
|
else
|
|
{
|
|
done = TRUE;
|
|
}
|
|
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
|
|
voice->LoopCount = *( unsigned short * )ptr;
|
|
ptr += blocklength;
|
|
voice->LoopStart = ptr;
|
|
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
|
|
tc = *( unsigned short * )ptr;
|
|
packtype = *( ptr + 2 );
|
|
voicemode = *( ptr + 3 );
|
|
ptr += blocklength;
|
|
break;
|
|
|
|
case 9 :
|
|
// New sound data block
|
|
samplespeed = *( unsigned long * )ptr;
|
|
BitsPerSample = ( unsigned )*( ptr + 4 );
|
|
Channels = ( unsigned )*( ptr + 5 );
|
|
Format = ( unsigned )*( unsigned short * )( ptr + 6 );
|
|
|
|
if ( ( BitsPerSample == 8 ) && ( Channels == 1 ) &&
|
|
( Format == 0 ) )
|
|
{
|
|
ptr += 12;
|
|
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;
|
|
|
|
voice->position = 0;
|
|
voice->length = min( blocklength, 0x8000 );
|
|
voice->BlockLength = blocklength - voice->length;
|
|
voice->length <<= 16;
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Play
|
|
|
|
Begin playback of sound data with the given sound levels and
|
|
priority.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_Play
|
|
(
|
|
char *ptr,
|
|
int pitchoffset,
|
|
int vol,
|
|
int left,
|
|
int right,
|
|
int priority,
|
|
unsigned long callbackval
|
|
)
|
|
|
|
{
|
|
VoiceNode *voice;
|
|
int buffer;
|
|
unsigned flags;
|
|
|
|
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->NextBlock = ptr + *( unsigned short int * )( ptr + 0x14 );
|
|
voice->LoopStart = NULL;
|
|
voice->LoopCount = 0;
|
|
voice->BlockLength = 0;
|
|
voice->PitchScale = PITCH_GetScale( pitchoffset );
|
|
|
|
MV_GetNextVOCBlock( voice );
|
|
|
|
voice->next = NULL;
|
|
voice->prev = NULL;
|
|
|
|
for( buffer = 0; buffer < NumberOfBuffers; buffer++ )
|
|
{
|
|
voice->Active[ buffer ] = TRUE;
|
|
}
|
|
|
|
voice->Volume = MIX_VOLUME( vol );
|
|
|
|
if ( MV_SwapLeftRight )
|
|
{
|
|
// SBPro uses reversed panning
|
|
voice->LeftVolume = MIX_VOLUME( right );
|
|
voice->RightVolume = MIX_VOLUME( left );
|
|
}
|
|
else
|
|
{
|
|
voice->LeftVolume = MIX_VOLUME( left );
|
|
voice->RightVolume = MIX_VOLUME( right );
|
|
}
|
|
|
|
voice->priority = priority;
|
|
voice->callbackval = callbackval;
|
|
|
|
flags = DisableInterrupts();
|
|
LL_AddToTail( VoiceNode, &VoiceList, voice );
|
|
RestoreInterrupts( flags );
|
|
|
|
MV_SetErrorCode( MV_Ok );
|
|
return( voice->handle );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Play3D
|
|
|
|
Begin playback of sound data at specified angle and distance
|
|
from listener.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_Play3D
|
|
(
|
|
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_Play( ptr, pitchoffset, mid, left, right, priority,
|
|
callbackval );
|
|
|
|
return( status );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_LockEnd
|
|
|
|
Used for determining the length of the functions to lock in memory.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void MV_LockEnd
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_CalcVolume
|
|
|
|
Create the table used to convert sound data to a specific volume
|
|
level.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_CalcVolume
|
|
(
|
|
int MaxLevel
|
|
)
|
|
|
|
{
|
|
int volume;
|
|
int val;
|
|
int level;
|
|
int rate;
|
|
int i;
|
|
unsigned flags;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
|
|
// For each volume level, create a translation table with the
|
|
// appropriate volume calculated.
|
|
rate = ( MaxLevel << 16 ) / MV_MaxVolume;
|
|
level = 0;
|
|
|
|
if ( MV_MixMode & SIXTEEN_BIT )
|
|
{
|
|
for( volume = 0; volume <= MV_MaxVolume; volume++ )
|
|
{
|
|
for( i = 0; i < 256; i++ )
|
|
{
|
|
val = i - 128;
|
|
val *= level;
|
|
// val /= MV_MaxVoices;
|
|
// val >>= 16;
|
|
val >>= 20;
|
|
( *MV_16BitVolumeTable )[ volume ][ i ] = val;
|
|
}
|
|
level += rate;
|
|
}
|
|
/*
|
|
// if ( ( MV_SoundCard == ProAudioSpectrum ) ||
|
|
// ( MV_SoundCard == SoundMan16 ) )
|
|
{
|
|
for( volume = 0; volume < 4 * 256 * 16; volume++ )
|
|
{
|
|
HarshClipTable16s[ volume ] = -0x8000;
|
|
HarshClipTable16s[ volume + 4 * 256 * 16 ] = 0xffff - 0x8000;
|
|
}
|
|
|
|
for( volume = 0; volume < 16 * 256; volume++ )
|
|
{
|
|
HarshClipTable16s[ volume + 4 * 256 * 16 - 0x800 ] =
|
|
volume * 16 - 0x8000;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*/
|
|
for( volume = 0; volume < 4 * 256 * 16; volume++ )
|
|
{
|
|
HarshClipTable16[ volume ] = 0;
|
|
HarshClipTable16[ volume + 4 * 256 * 16 ] = 0xffff;
|
|
}
|
|
|
|
for( volume = 0; volume < 16 * 256; volume++ )
|
|
{
|
|
HarshClipTable16[ volume + 4 * 256 * 16 - 0x800 ] = volume * 16;
|
|
}
|
|
// }
|
|
}
|
|
else
|
|
{
|
|
for( volume = 0; volume <= MV_MaxVolume; volume++ )
|
|
{
|
|
for( i = 0; i < 256; i++ )
|
|
{
|
|
val = i - 128;
|
|
val *= level;
|
|
// DEBUG
|
|
// val /= MV_MaxVoices;
|
|
val >>= 24;
|
|
( *MV_8BitVolumeTable )[ volume ][ i ] = val;
|
|
}
|
|
level += rate;
|
|
}
|
|
for( volume = 0; volume < 4 * 256; volume++ )
|
|
{
|
|
HarshClipTable[ volume ] = 0;
|
|
HarshClipTable[ volume + 4 * 256 ] = 255;
|
|
}
|
|
for( volume = 0; volume < 256; volume++ )
|
|
{
|
|
HarshClipTable[ volume + 4 * 256 - 128 ] = volume;
|
|
}
|
|
}
|
|
|
|
RestoreInterrupts( flags );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
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
|
|
)
|
|
|
|
{
|
|
int maxlevel;
|
|
|
|
volume = max( 0, volume );
|
|
volume = min( volume, MV_MaxTotalVolume );
|
|
|
|
MV_TotalVolume = volume;
|
|
|
|
maxlevel = ( MV_MaxLevel * volume ) / MV_MaxTotalVolume;
|
|
//printf( "maxlevel = %d\n", maxlevel );
|
|
|
|
// Calculate volume table
|
|
MV_CalcVolume( maxlevel );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
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_Init
|
|
|
|
Perform the initialization of variables and memory used by
|
|
Multivoc.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_Init
|
|
(
|
|
int soundcard,
|
|
int MixRate,
|
|
int Voices,
|
|
int MixMode
|
|
)
|
|
|
|
{
|
|
char *ptr;
|
|
int status;
|
|
int buffer;
|
|
int index;
|
|
|
|
if ( MV_Installed )
|
|
{
|
|
MV_Shutdown();
|
|
}
|
|
|
|
MV_SetErrorCode( MV_Ok );
|
|
|
|
status = MV_LockMemory();
|
|
if ( status != MV_Ok )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
status = USRHOOKS_GetMem( &MV_Voices, Voices * sizeof( VoiceNode ) );
|
|
if ( status != USRHOOKS_Ok )
|
|
{
|
|
MV_UnlockMemory();
|
|
MV_SetErrorCode( MV_NoMem );
|
|
return( MV_Error );
|
|
}
|
|
|
|
if ( MixMode & SIXTEEN_BIT )
|
|
{
|
|
status = USRHOOKS_GetMem( &HarshClipTable, sizeof( HARSH_CLIP_TABLE_16 ) );
|
|
HarshClipTable16 = ( unsigned short * )HarshClipTable;
|
|
HarshClipTable16s = ( short * )HarshClipTable;
|
|
}
|
|
else
|
|
{
|
|
status = USRHOOKS_GetMem( &HarshClipTable, sizeof( HARSH_CLIP_TABLE_8 ) );
|
|
HarshClipTable16 = ( unsigned short * )HarshClipTable;
|
|
HarshClipTable16s = ( short * )HarshClipTable;
|
|
}
|
|
|
|
if ( status != USRHOOKS_Ok )
|
|
{
|
|
USRHOOKS_FreeMem( MV_Voices );
|
|
MV_SetErrorCode( MV_NoMem );
|
|
MV_UnlockMemory();
|
|
return( MV_Error );
|
|
}
|
|
|
|
// Set number of voices before calculating volume table
|
|
MV_MaxVoices = Voices;
|
|
|
|
VoiceList.start = NULL;
|
|
VoiceList.end = NULL;
|
|
VoicePool.start = NULL;
|
|
VoicePool.end = NULL;
|
|
|
|
for( index = 0; index < Voices; index++ )
|
|
{
|
|
LL_AddToTail( VoiceNode, &VoicePool, &MV_Voices[ index ] );
|
|
}
|
|
|
|
// Allocate mix buffer within 1st megabyte
|
|
status = DPMI_GetDOSMemory( &ptr, &MV_BufferDescriptor,
|
|
2 * TotalBufferSize );
|
|
|
|
if ( status )
|
|
{
|
|
MV_SetErrorCode( MV_NoMem );
|
|
MV_UnlockMemory();
|
|
return( MV_Error );
|
|
}
|
|
|
|
MV_SwapLeftRight = FALSE;
|
|
|
|
// Initialize the sound card
|
|
switch( soundcard )
|
|
{
|
|
case SoundBlaster :
|
|
case Awe32 :
|
|
status = BLASTER_Init();
|
|
if ( status != BLASTER_Ok )
|
|
{
|
|
MV_SetErrorCode( MV_BlasterError );
|
|
}
|
|
|
|
if ( ( BLASTER_Config.Type == SBPro ) ||
|
|
( BLASTER_Config.Type == SBPro2 ) )
|
|
{
|
|
MV_SwapLeftRight = TRUE;
|
|
}
|
|
break;
|
|
|
|
case ProAudioSpectrum :
|
|
case SoundMan16 :
|
|
status = PAS_Init();
|
|
if ( status != PAS_Ok )
|
|
{
|
|
MV_SetErrorCode( MV_PasError );
|
|
}
|
|
break;
|
|
|
|
#ifndef SOUNDSOURCE_OFF
|
|
case SoundSource :
|
|
case TandySoundSource :
|
|
status = SS_Init( soundcard );
|
|
if ( status != SS_Ok )
|
|
{
|
|
MV_SetErrorCode( MV_SoundSourceError );
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default :
|
|
MV_SetErrorCode( MV_UnsupportedCard );
|
|
break;
|
|
}
|
|
|
|
if ( MV_ErrorCode != MV_Ok )
|
|
{
|
|
DPMI_FreeDOSMemory( MV_BufferDescriptor );
|
|
MV_UnlockMemory();
|
|
return( MV_Error );
|
|
}
|
|
|
|
MV_SoundCard = soundcard;
|
|
|
|
MV_Installed = TRUE;
|
|
|
|
MV_CallBackFunc = NULL;
|
|
|
|
// Set the sampling rate
|
|
MV_RequestedMixRate = MixRate;
|
|
|
|
// Set Mixer to play stereo digitized sound
|
|
MV_SetMixMode( MixMode );
|
|
|
|
// Make sure we don't cross a physical page
|
|
if ( ( ( unsigned long )ptr & 0xffff ) + TotalBufferSize > 0x10000 )
|
|
{
|
|
ptr = ( char * )( ( ( unsigned long )ptr & 0xff0000 ) + 0x10000 );
|
|
}
|
|
|
|
for( buffer = 0; buffer < MV_NumberOfBuffers; buffer++ )
|
|
{
|
|
MV_MixBuffer[ buffer ] = ptr;
|
|
ptr += MV_BufferSize;
|
|
}
|
|
|
|
// Calculate pan table
|
|
MV_CalcPanTable();
|
|
|
|
MV_SetVolume( MV_MaxTotalVolume );
|
|
|
|
// Init pitch scaler
|
|
PITCH_Init();
|
|
|
|
// Start the playback engine
|
|
status = MV_StartPlayback();
|
|
if ( status != MV_Ok )
|
|
{
|
|
// Preserve error code while we shutdown.
|
|
status = MV_ErrorCode;
|
|
MV_Shutdown();
|
|
MV_SetErrorCode( status );
|
|
return( MV_Error );
|
|
}
|
|
|
|
return( MV_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_Shutdown
|
|
|
|
Restore any resources allocated by Multivoc back to the system.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_Shutdown
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int buffer;
|
|
unsigned flags;
|
|
|
|
if ( !MV_Installed )
|
|
{
|
|
MV_SetErrorCode( MV_NotInstalled );
|
|
return( MV_Error );
|
|
}
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
MV_KillAllVoices();
|
|
|
|
MV_Installed = FALSE;
|
|
|
|
// Stop the sound playback engine
|
|
MV_StopPlayback();
|
|
|
|
// Shutdown the sound card
|
|
switch( MV_SoundCard )
|
|
{
|
|
case SoundBlaster :
|
|
case Awe32 :
|
|
BLASTER_Shutdown();
|
|
break;
|
|
|
|
case ProAudioSpectrum :
|
|
case SoundMan16 :
|
|
PAS_Shutdown();
|
|
break;
|
|
|
|
#ifndef SOUNDSOURCE_OFF
|
|
case SoundSource :
|
|
case TandySoundSource :
|
|
SS_Shutdown();
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
RestoreInterrupts( flags );
|
|
|
|
// Free any voices we allocated
|
|
USRHOOKS_FreeMem( MV_Voices );
|
|
MV_Voices = NULL;
|
|
|
|
VoiceList.start = NULL;
|
|
VoiceList.end = NULL;
|
|
VoicePool.start = NULL;
|
|
VoicePool.end = NULL;
|
|
|
|
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 );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_UnlockMemory
|
|
|
|
Unlocks all neccessary data.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void MV_UnlockMemory
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
PITCH_UnlockMemory();
|
|
|
|
DPMI_UnlockMemoryRegion( MV_LockStart, MV_LockEnd );
|
|
DPMI_Unlock( MV_VolumeTable );
|
|
DPMI_Unlock( MV_8BitVolumeTable );
|
|
DPMI_Unlock( MV_16BitVolumeTable );
|
|
DPMI_Unlock( MV_PanTable );
|
|
DPMI_Unlock( MV_Installed );
|
|
DPMI_Unlock( MV_SoundCard );
|
|
DPMI_Unlock( MV_TotalVolume );
|
|
DPMI_Unlock( MV_MaxVoices );
|
|
DPMI_Unlock( MV_BufferSize );
|
|
DPMI_Unlock( MV_SampleSize );
|
|
DPMI_Unlock( MV_NumberOfBuffers );
|
|
DPMI_Unlock( MV_MixMode );
|
|
DPMI_Unlock( MV_Silence );
|
|
DPMI_Unlock( MV_SwapLeftRight );
|
|
DPMI_Unlock( MV_RequestedMixRate );
|
|
DPMI_Unlock( MV_MixRate );
|
|
DPMI_Unlock( MV_BufferDescriptor );
|
|
DPMI_Unlock( MV_MixBuffer );
|
|
DPMI_Unlock( MV_Voices );
|
|
DPMI_Unlock( VoiceList );
|
|
DPMI_Unlock( VoicePool );
|
|
DPMI_Unlock( MV_MixPage );
|
|
DPMI_Unlock( MV_PlayPage );
|
|
DPMI_Unlock( MV_VoiceHandle );
|
|
DPMI_Unlock( MV_CallBackFunc );
|
|
DPMI_Unlock( HarshClipTable );
|
|
DPMI_Unlock( HarshClipTable16 );
|
|
DPMI_Unlock( HarshClipTable16s );
|
|
DPMI_Unlock( MV_ErrorCode );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: MV_LockMemory
|
|
|
|
Locks all neccessary data.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int MV_LockMemory
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int status;
|
|
int pitchstatus;
|
|
|
|
status = DPMI_LockMemoryRegion( MV_LockStart, MV_LockEnd );
|
|
status |= DPMI_Lock( MV_VolumeTable );
|
|
status |= DPMI_Lock( MV_8BitVolumeTable );
|
|
status |= DPMI_Lock( MV_16BitVolumeTable );
|
|
status |= DPMI_Lock( MV_PanTable );
|
|
status |= DPMI_Lock( MV_Installed );
|
|
status |= DPMI_Lock( MV_SoundCard );
|
|
status |= DPMI_Lock( MV_TotalVolume );
|
|
status |= DPMI_Lock( MV_MaxVoices );
|
|
status |= DPMI_Lock( MV_BufferSize );
|
|
status |= DPMI_Lock( MV_SampleSize );
|
|
status |= DPMI_Lock( MV_NumberOfBuffers );
|
|
status |= DPMI_Lock( MV_MixMode );
|
|
status |= DPMI_Lock( MV_Silence );
|
|
status |= DPMI_Lock( MV_SwapLeftRight );
|
|
status |= DPMI_Lock( MV_RequestedMixRate );
|
|
status |= DPMI_Lock( MV_MixRate );
|
|
status |= DPMI_Lock( MV_BufferDescriptor );
|
|
status |= DPMI_Lock( MV_MixBuffer );
|
|
status |= DPMI_Lock( MV_Voices );
|
|
status |= DPMI_Lock( VoiceList );
|
|
status |= DPMI_Lock( VoicePool );
|
|
status |= DPMI_Lock( MV_MixPage );
|
|
status |= DPMI_Lock( MV_PlayPage );
|
|
status |= DPMI_Lock( MV_VoiceHandle );
|
|
status |= DPMI_Lock( MV_CallBackFunc );
|
|
status |= DPMI_Lock( HarshClipTable );
|
|
status |= DPMI_Lock( HarshClipTable16 );
|
|
status |= DPMI_Lock( HarshClipTable16s );
|
|
status |= DPMI_Lock( MV_ErrorCode );
|
|
|
|
pitchstatus = PITCH_LockMemory();
|
|
if ( ( pitchstatus != PITCH_Ok ) || ( status != DPMI_Ok ) )
|
|
{
|
|
MV_UnlockMemory();
|
|
MV_SetErrorCode( MV_DPMI_Error );
|
|
return( MV_Error );
|
|
}
|
|
|
|
return( MV_Ok );
|
|
}
|