/* 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. */ /********************************************************************** file: GUSWAVE.C author: James R. Dose date: March 23, 1994 Digitized sound playback routines for the Gravis Ultrasound. (c) Copyright 1994 James R. Dose. All Rights Reserved. **********************************************************************/ #include #include #include #include #include #include #include "debugio.h" #include "interrup.h" #include "ll_man.h" #include "pitch.h" #include "user.h" #include "multivoc.h" #include "_guswave.h" #include "newgf1.h" #include "gusmidi.h" #include "guswave.h" #define ATR_INDEX 0x3c0 #define STATUS_REGISTER_1 0x3da #define SetBorderColor(color) \ { \ inp (STATUS_REGISTER_1); \ outp (ATR_INDEX,0x31); \ outp (ATR_INDEX,color); \ } static const int GUSWAVE_PanTable[ 32 ] = { 8, 9, 10, 11, 11, 12, 13, 14, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7 }; static voicelist VoiceList; static voicelist VoicePool; static voicestatus VoiceStatus[ MAX_VOICES ]; //static VoiceNode GUSWAVE_Voices[ VOICES ]; static int GUSWAVE_VoiceHandle = GUSWAVE_MinVoiceHandle; static int GUSWAVE_MaxVoices = VOICES; //static int GUSWAVE_Installed = FALSE; static void ( *GUSWAVE_CallBackFunc )( unsigned long ) = NULL; // current volume for dig audio - from 0 to 4095 static int GUSWAVE_Volume = MAX_VOLUME; static int GUSWAVE_SwapLeftRight = FALSE; static int GUS_Debug = FALSE; extern int GUSMIDI_Installed; int GUSWAVE_ErrorCode = GUSWAVE_Ok; #define GUSWAVE_SetErrorCode( status ) \ GUSWAVE_ErrorCode = ( status ); /*--------------------------------------------------------------------- Function: GUSWAVE_ErrorString Returns a pointer to the error message associated with an error number. A -1 returns a pointer the current error. ---------------------------------------------------------------------*/ char *GUSWAVE_ErrorString ( int ErrorNumber ) { char *ErrorString; switch( ErrorNumber ) { case GUSWAVE_Warning : case GUSWAVE_Error : ErrorString = GUSWAVE_ErrorString( GUSWAVE_ErrorCode ); break; case GUSWAVE_Ok : ErrorString = "GUSWAVE ok."; break; case GUSWAVE_GUSError : ErrorString = GUS_ErrorString( GUS_Error ); break; case GUSWAVE_NotInstalled : ErrorString = "GUSWAVE not installed."; break; case GUSWAVE_NoVoices : ErrorString = "No free voices available to GUSWAVE."; break; case GUSWAVE_UltraNoMem : ErrorString = "Not enough Ultrasound memory available for GUSWAVE."; break; case GUSWAVE_UltraNoMemMIDI : ErrorString = "Not enough Ultrasound memory available for GUSWAVE. " "Try initializing Sound FX before Music."; break; case GUSWAVE_VoiceNotFound : ErrorString = "No voice with matching handle found."; break; case GUSWAVE_InvalidVOCFile : ErrorString = "Invalid VOC file passed in to GUSWAVE."; break; case GUSWAVE_InvalidWAVFile : ErrorString = "Invalid WAV file passed in to GUSWAVE."; break; default : ErrorString = "Unknown GUSWAVE error code."; break; } return( ErrorString ); } /*--------------------------------------------------------------------- Function: GUSWAVE_CallBack GF1 callback service routine. ---------------------------------------------------------------------*/ char GUS_Silence8[ 1024 ] = //256 ] = { 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 }; //unsigned short GUS_Silence16[ 128 ] = unsigned short GUS_Silence16[ 512 ] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; static int LOADDS GUSWAVE_CallBack ( int reason, int voice, unsigned char **buf, unsigned long *size ) { VoiceNode *Voice; playbackstatus status; // this function is called from an interrupt // remember not to make any DOS or BIOS calls from here // also don't call any C library functions unless you are sure that // they are reentrant // restore our DS register if ( VoiceStatus[ voice ].playing == FALSE ) { return( DIG_DONE ); } if ( reason == DIG_MORE_DATA ) { // SetBorderColor(16); Voice = VoiceStatus[ voice ].Voice; if ( ( Voice != NULL ) && ( Voice->Playing ) ) /* { *buf = ( unsigned char * )GUS_Silence16; *size = 1024; SetBorderColor(0); return( DIG_MORE_DATA ); } */ { status = Voice->GetSound( Voice ); if ( status != SoundDone ) { if ( ( Voice->sound == NULL ) || ( status == NoMoreData ) ) { if ( Voice->bits == 8 ) { *buf = GUS_Silence8; } else { *buf = ( unsigned char * )GUS_Silence16; } *size = 256; } else { *buf = Voice->sound; *size = Voice->length; } return( DIG_MORE_DATA ); } } // SetBorderColor(16); return( DIG_DONE ); } if ( reason == DIG_DONE ) { Voice = VoiceStatus[ voice ].Voice; VoiceStatus[ voice ].playing = FALSE; if ( Voice != NULL ) { Voice->Active = FALSE; Voice->Playing = FALSE; // I'm commenting this out because a -1 could cause a crash if it // is sent to the GF1 code. This shouldn't be necessary since // Active should be false when GF1voice is -1, but this is just // a precaution. Adjust the pan on the wrong voice is a lot // more pleasant than a crash! // Voice->GF1voice = -1; LL_Remove( VoiceNode, &VoiceList, Voice ); LL_AddToTail( VoiceNode, &VoicePool, Voice ); } if ( GUSWAVE_CallBackFunc ) { GUSWAVE_CallBackFunc( Voice->callbackval ); } } return( DIG_DONE ); } /*--------------------------------------------------------------------- Function: GUSWAVE_DebugCallBack GF1 callback service routine with debugging info. ---------------------------------------------------------------------*/ static int LOADDS GUSWAVE_DebugCallBack ( int reason, int voice, unsigned char **buf, unsigned long *size ) { VoiceNode *Voice; // this function is called from an interrupt // remember not to make any DOS or BIOS calls from here // also don't call any C library functions unless you are sure that // they are reentrant // restore our DS register if ( VoiceStatus[ voice ].playing == FALSE ) { // DB_printf( "GUS Voice %d not playing.\n", voice ); DB_printf( "GUS Voice " ); DB_PrintNum( voice ); DB_printf( " not playing.\n" ); return( DIG_DONE ); } if ( reason == DIG_MORE_DATA ) { Voice = VoiceStatus[ voice ].Voice; // DB_printf( "Voice %d : More data -- ", Voice ); DB_printf( "Voice " ); DB_PrintNum( voice ); DB_printf( " : More data -- " ); if ( Voice != NULL ) { if ( Voice->Playing ) { GUSWAVE_GetNextVOCBlock( Voice ); if ( Voice->Playing ) { // DB_printf( "More data -- size = %u blocklength = %u\n", // Voice->length, Voice->BlockLength ); DB_printf( "More data -- size = " ); DB_PrintNum( Voice->length ); DB_printf( " blocklength = " ); DB_PrintNum( Voice->BlockLength ); DB_printf( "\n" ); *buf = Voice->sound; *size = Voice->length; return( DIG_MORE_DATA ); } else { DB_printf( "Voice done.\n" ); } } else { DB_printf( "Voice not active.\n" ); } } else { DB_printf( " NULL Voice\n" ); } return( DIG_DONE ); } if ( reason == DIG_DONE ) { VoiceStatus[ voice ].playing = FALSE; Voice = VoiceStatus[ voice ].Voice; // DB_printf( "Voice %d : Done -- ", Voice ); DB_printf( "Voice " ); DB_PrintNum( voice ); DB_printf( " : Done -- " ); if ( Voice != NULL ) { DB_printf( "Ok\n" ); Voice->Active = FALSE; Voice->Playing = FALSE; // I'm commenting this out because a -1 could cause a crash if it // is sent to the GF1 code. This shouldn't be necessary since // Active should be false when GF1voice is -1, but this is just // a precaution. Adjust the pan on the wrong voice is a lot // more pleasant than a crash! // Voice->GF1voice = -1; LL_Remove( VoiceNode, &VoiceList, Voice ); LL_AddToTail( VoiceNode, &VoicePool, Voice ); } else { DB_printf( "Null voice\n" ); } if ( GUSWAVE_CallBackFunc ) { GUSWAVE_CallBackFunc( Voice->callbackval ); } } return( DIG_DONE ); } /*--------------------------------------------------------------------- Function: GUSWAVE_GetVoice Locates the voice with the specified handle. ---------------------------------------------------------------------*/ static VoiceNode *GUSWAVE_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 ) { GUSWAVE_SetErrorCode( GUSWAVE_VoiceNotFound ); } return( voice ); } /*--------------------------------------------------------------------- Function: GUSWAVE_VoicePlaying Checks if the voice associated with the specified handle is playing. ---------------------------------------------------------------------*/ int GUSWAVE_VoicePlaying ( int handle ) { VoiceNode *voice; voice = GUSWAVE_GetVoice( handle ); if ( voice != NULL ) { return( voice->Active ); } return( FALSE ); } /*--------------------------------------------------------------------- Function: GUSWAVE_VoicesPlaying Determines the number of currently active voices. ---------------------------------------------------------------------*/ int GUSWAVE_VoicesPlaying ( void ) { int index; int NumVoices = 0; unsigned flags; flags = DisableInterrupts(); for( index = 0; index < GUSWAVE_MaxVoices; index++ ) { if ( GUSWAVE_Voices[ index ].Active ) { NumVoices++; } } RestoreInterrupts( flags ); if ( GUS_Debug ) { DB_printf( "Number of voices = %d.\n", NumVoices ); } return( NumVoices ); } /*--------------------------------------------------------------------- Function: GUSWAVE_Kill Stops output of the voice associated with the specified handle. ---------------------------------------------------------------------*/ int GUSWAVE_Kill ( int handle ) { VoiceNode *voice; unsigned flags; flags = DisableInterrupts(); voice = GUSWAVE_GetVoice( handle ); if ( voice == NULL ) { RestoreInterrupts( flags ); GUSWAVE_SetErrorCode( GUSWAVE_VoiceNotFound ); if ( GUS_Debug ) { DB_printf( "Could not find voice to kill.\n" ); } return( GUSWAVE_Warning ); } RestoreInterrupts( flags ); if ( !GUS_Debug ) { if ( voice->Active ) { gf1_stop_digital( voice->GF1voice ); } } else { DB_printf( "Kill - GUS Voice %d ", voice->GF1voice ); if ( voice->Active ) { DB_printf( "active\n" ); gf1_stop_digital( voice->GF1voice ); } else { DB_printf( "inactive\n" ); } } // RestoreInterrupts( flags ); return( GUSWAVE_Ok ); } /*--------------------------------------------------------------------- Function: GUSWAVE_KillAllVoices Stops output of all currently active voices. ---------------------------------------------------------------------*/ int GUSWAVE_KillAllVoices ( void ) { int i; unsigned flags; if ( !GUSWAVE_Installed ) { return( GUSWAVE_Ok ); } if ( GUS_Debug ) { DB_printf( "Kill All Voices\n" ); } flags = DisableInterrupts(); // Remove all the voices from the list for( i = 0; i < GUSWAVE_MaxVoices; i++ ) { if ( GUSWAVE_Voices[ i ].Active ) { // GUSWAVE_Kill( GUSWAVE_Voices[ i ].handle ); gf1_stop_digital( GUSWAVE_Voices[ i ].GF1voice ); } } for( i = 0; i < MAX_VOICES; i++ ) { VoiceStatus[ i ].playing = FALSE; VoiceStatus[ i ].Voice = NULL; } VoicePool.start = NULL; VoicePool.end = NULL; VoiceList.start = NULL; VoiceList.end = NULL; for( i = 0; i < GUSWAVE_MaxVoices; i++ ) { GUSWAVE_Voices[ i ].Active = FALSE; if ( GUSWAVE_Voices[ i ].mem != NULL ) { LL_AddToTail( VoiceNode, &VoicePool, &GUSWAVE_Voices[ i ] ); } } RestoreInterrupts( flags ); return( GUSWAVE_Ok ); } /*--------------------------------------------------------------------- Function: GUSWAVE_SetPitch Sets the pitch for the voice associated with the specified handle. ---------------------------------------------------------------------*/ int GUSWAVE_SetPitch ( int handle, int pitchoffset ) { VoiceNode *voice; unsigned flags; flags = DisableInterrupts(); voice = GUSWAVE_GetVoice( handle ); if ( voice == NULL ) { RestoreInterrupts( flags ); GUSWAVE_SetErrorCode( GUSWAVE_VoiceNotFound ); return( GUSWAVE_Warning ); } if ( voice->Active ) { voice->PitchScale = PITCH_GetScale( pitchoffset ); voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) >> 16; gf1_dig_set_freq( voice->GF1voice, voice->RateScale ); } RestoreInterrupts( flags ); return( GUSWAVE_Ok ); } /*--------------------------------------------------------------------- Function: GUSWAVE_SetPan3D Sets the pan position of the voice with the specified handle. ---------------------------------------------------------------------*/ int GUSWAVE_SetPan3D ( int handle, int angle, int distance ) { VoiceNode *voice; int pan; unsigned flags; flags = DisableInterrupts(); voice = GUSWAVE_GetVoice( handle ); if ( voice == NULL ) { RestoreInterrupts( flags ); GUSWAVE_SetErrorCode( GUSWAVE_VoiceNotFound ); return( GUSWAVE_Warning ); } if ( voice->Active ) { angle &= 31; pan = GUSWAVE_PanTable[ angle ]; if ( GUSWAVE_SwapLeftRight ) { pan = 15 - pan; } distance = max( 0, distance ); distance = min( 255, distance ); voice->Volume = 255 - distance; voice->Pan = pan; gf1_dig_set_pan( voice->GF1voice, pan ); gf1_dig_set_vol( voice->GF1voice, GUSWAVE_Volume - distance * 4 ); } RestoreInterrupts( flags ); return( GUSWAVE_Ok ); } /*--------------------------------------------------------------------- Function: GUSWAVE_SetVolume Sets the total volume of the digitized sounds. ---------------------------------------------------------------------*/ void GUSWAVE_SetVolume ( int volume ) { int i; volume = max( 0, volume ); volume = min( 255, volume ); GUSWAVE_Volume = MAX_VOLUME - ( 255 - volume ) * 4; for( i = 0; i < GUSWAVE_MaxVoices; i++ ) { if ( GUSWAVE_Voices[ i ].Active ) { gf1_dig_set_vol( GUSWAVE_Voices[ i ].GF1voice, GUSWAVE_Volume - ( 255 - GUSWAVE_Voices[ i ].Volume ) * 4 ); } } } /*--------------------------------------------------------------------- Function: GUSWAVE_GetVolume Returns the total volume of the digitized sounds. ---------------------------------------------------------------------*/ int GUSWAVE_GetVolume ( void ) { return( 255 - ( ( MAX_VOLUME - GUSWAVE_Volume ) / 4 ) ); } /*--------------------------------------------------------------------- Function: GUSWAVE_AllocVoice Retrieve an inactive or lower priority voice for output. ---------------------------------------------------------------------*/ static VoiceNode *GUSWAVE_AllocVoice ( int priority ) { VoiceNode *voice; VoiceNode *node; unsigned flags; // If we don't have any free voices, check if we have a higher // priority than one that is playing. if ( GUSWAVE_VoicesPlaying() >= GUSWAVE_MaxVoices ) { flags = DisableInterrupts(); node = VoiceList.start; voice = node; while( node != NULL ) { if ( node->priority < voice->priority ) { voice = node; } node = node->next; } RestoreInterrupts( flags ); if ( priority >= voice->priority ) { GUSWAVE_Kill( voice->handle ); } } // Check if any voices are in the voice pool flags = DisableInterrupts(); voice = VoicePool.start; if ( voice != NULL ) { LL_Remove( VoiceNode, &VoicePool, voice ); } RestoreInterrupts( flags ); if ( voice != NULL ) { do { GUSWAVE_VoiceHandle++; if ( GUSWAVE_VoiceHandle < GUSWAVE_MinVoiceHandle ) { GUSWAVE_VoiceHandle = GUSWAVE_MinVoiceHandle; } } while( GUSWAVE_VoicePlaying( GUSWAVE_VoiceHandle ) ); voice->handle = GUSWAVE_VoiceHandle; } return( voice ); } /*--------------------------------------------------------------------- Function: GUSWAVE_VoiceAvailable Checks if a voice can be play at the specified priority. ---------------------------------------------------------------------*/ int GUSWAVE_VoiceAvailable ( int priority ) { VoiceNode *voice; VoiceNode *node; unsigned flags; if ( GUSWAVE_VoicesPlaying() < GUSWAVE_MaxVoices ) { return( TRUE ); } flags = DisableInterrupts(); node = VoiceList.start; voice = node; while( node != NULL ) { if ( node->priority < voice->priority ) { voice = node; } node = node->next; } RestoreInterrupts( flags ); if ( priority >= voice->priority ) { return( TRUE ); } return( FALSE ); } /*--------------------------------------------------------------------- Function: GUSWAVE_GetNextVOCBlock Interperate the information of a VOC format sound file. ---------------------------------------------------------------------*/ playbackstatus GUSWAVE_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 += MAX_BLOCK_LENGTH; voice->length = min( voice->BlockLength, MAX_BLOCK_LENGTH ); voice->BlockLength -= voice->length; return( KeepPlaying ); } ptr = ( unsigned char * )voice->NextBlock; voice->Playing = TRUE; voicemode = 0; lastblocktype = 0; packtype = 0; done = FALSE; while( !done ) { // Stop playing if we get a NULL pointer if ( ptr == NULL ) { voice->Playing = FALSE; done = TRUE; break; } blocktype = ( int )*ptr; blocklength = ( *( unsigned long * )( ptr + 1 ) ) & 0x00ffffff; ptr += 4; switch( blocktype ) { case 0 : // End of data voice->Playing = FALSE; done = TRUE; 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 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 voice->bits = 8; tc = *( unsigned short * )ptr; packtype = *( ptr + 2 ); voicemode = *( ptr + 3 ); ptr += blocklength; break; case 9 : // New sound data block samplespeed = *( unsigned long * )ptr; BitsPerSample = ( unsigned )*( ptr + 4 ); Channels = ( unsigned )*( ptr + 5 ); Format = ( unsigned )*( unsigned short * )( ptr + 6 ); if ( ( BitsPerSample == 8 ) && ( Channels == 1 ) && ( Format == VOC_8BIT ) ) { ptr += 12; blocklength -= 12; voice->bits = 8; done = TRUE; } else if ( ( BitsPerSample == 16 ) && ( Channels == 1 ) && ( Format == VOC_16BIT ) ) { ptr += 12; blocklength -= 12; voice->bits = 16; done = TRUE; } else { ptr += blocklength; } break; default : // Unknown data. Probably not a VOC file. voice->Playing = FALSE; done = TRUE; break; } lastblocktype = blocktype; } if ( voice->Playing ) { voice->NextBlock = ptr + blocklength; voice->sound = ptr; voice->SamplingRate = samplespeed; voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) >> 16; voice->length = min( blocklength, MAX_BLOCK_LENGTH ); voice->BlockLength = blocklength - voice->length; return( KeepPlaying ); } return( SoundDone ); } /*--------------------------------------------------------------------- Function: GUSWAVE_GetNextWAVBlock Controls playback of demand fed data. ---------------------------------------------------------------------*/ playbackstatus GUSWAVE_GetNextWAVBlock ( VoiceNode *voice ) { if ( voice->BlockLength <= 0 ) { if ( voice->LoopStart == NULL ) { voice->Playing = FALSE; return( SoundDone ); } voice->BlockLength = voice->LoopSize; voice->NextBlock = voice->LoopStart; voice->length = 0; } voice->sound = voice->NextBlock; voice->length = min( voice->BlockLength, 0x8000 ); voice->NextBlock += voice->length; voice->BlockLength -= voice->length; return( KeepPlaying ); } /*--------------------------------------------------------------------- Function: GUSWAVE_GetNextDemandFeedBlock Controls playback of demand fed data. ---------------------------------------------------------------------*/ playbackstatus GUSWAVE_GetNextDemandFeedBlock ( VoiceNode *voice ) { if ( voice->BlockLength > 0 ) { voice->sound += voice->length; voice->length = min( voice->BlockLength, 0x8000 ); voice->BlockLength -= voice->length; return( KeepPlaying ); } if ( voice->DemandFeed == NULL ) { return( SoundDone ); } ( voice->DemandFeed )( &voice->sound, &voice->BlockLength ); // voice->sound = GUS_Silence16; // voice->BlockLength = 256; voice->length = min( voice->BlockLength, 0x8000 ); voice->BlockLength -= voice->length; if ( ( voice->length > 0 ) && ( voice->sound != NULL ) ) { return( KeepPlaying ); } return( NoMoreData ); } /*--------------------------------------------------------------------- Function: GUSWAVE_Play Begins playback of digitized sound. ---------------------------------------------------------------------*/ int GUSWAVE_Play ( VoiceNode *voice, int angle, int volume, int channels ) { int VoiceNumber; int type; int pan; unsigned flags; int ( *servicefunction )( int reason, int voice, unsigned char **buf, unsigned long *size ); type = 0; if ( channels != 1 ) { type |= TYPE_STEREO; } if ( voice->bits == 8 ) { type |= TYPE_8BIT; type |= TYPE_INVERT_MSB; } voice->GF1voice = -1; angle &= 31; pan = GUSWAVE_PanTable[ angle ]; if ( GUSWAVE_SwapLeftRight ) { pan = 15 - pan; } voice->Pan = pan; volume = max( 0, volume ); volume = min( 255, volume ); voice->Volume = volume; if ( !GUS_Debug ) { servicefunction = GUSWAVE_CallBack; } else { servicefunction = GUSWAVE_DebugCallBack; } VoiceNumber = gf1_play_digital( 0, voice->sound, voice->length, voice->mem, GUSWAVE_Volume - ( 255 - volume ) * 4, pan, voice->RateScale, type, &GUS_HoldBuffer, servicefunction ); if ( VoiceNumber == NO_MORE_VOICES ) { if ( GUS_Debug ) { DB_printf( "Out of voices.\n" ); } flags = DisableInterrupts(); LL_AddToTail( VoiceNode, &VoicePool, voice ); RestoreInterrupts( flags ); GUSWAVE_SetErrorCode( GUSWAVE_NoVoices ); return( GUSWAVE_Warning ); } flags = DisableInterrupts(); voice->GF1voice = VoiceNumber; voice->Active = TRUE; LL_AddToTail( VoiceNode, &VoiceList, voice ); VoiceStatus[ VoiceNumber ].playing = TRUE; VoiceStatus[ VoiceNumber ].Voice = voice; if ( GUS_Debug ) { DB_printf( "GUS voice %d playing\n", VoiceNumber ); } RestoreInterrupts( flags ); return( voice->handle ); } /*--------------------------------------------------------------------- Function: GUSWAVE_PlayVOC Begins playback of digitized sound. ---------------------------------------------------------------------*/ int GUSWAVE_PlayVOC ( char *sample, int pitchoffset, int angle, int volume, int priority, unsigned long callbackval ) { int handle; int status; playbackstatus soundstatus; VoiceNode *voice; unsigned flags; // Make sure it's a valid VOC file. status = strncmp( sample, "Creative Voice File", 19 ); if ( status != 0 ) { // Tell multivoc that we had a bad VOC file MV_ErrorCode = MV_InvalidVOCFile; GUSWAVE_SetErrorCode( GUSWAVE_InvalidVOCFile ); return( GUSWAVE_Error ); } // Request a voice from the voice pool voice = GUSWAVE_AllocVoice( priority ); if ( voice == NULL ) { if ( GUS_Debug ) { DB_printf( "No more voices. Skipping sound.\n" ); } GUSWAVE_SetErrorCode( GUSWAVE_NoVoices ); return( GUSWAVE_Warning ); } voice->NextBlock = sample + *( unsigned short int * )( sample + 0x14 ); voice->LoopStart = NULL; voice->LoopCount = 0; voice->BlockLength = 0; voice->PitchScale = PITCH_GetScale( pitchoffset ); voice->wavetype = VOC; voice->bits = 8; voice->GetSound = GUSWAVE_GetNextVOCBlock; voice->length = 0; voice->next = NULL; voice->prev = NULL; voice->priority = priority; voice->callbackval = callbackval; soundstatus = GUSWAVE_GetNextVOCBlock( voice ); if ( soundstatus == SoundDone ) { flags = DisableInterrupts(); LL_AddToTail( VoiceNode, &VoicePool, voice ); RestoreInterrupts( flags ); if ( GUS_Debug ) { DB_printf( "Voice ended before playback.\n" ); } // Tell multivoc that we had a bad VOC file MV_ErrorCode = MV_InvalidVOCFile; GUSWAVE_SetErrorCode( GUSWAVE_InvalidVOCFile ); return( GUSWAVE_Error ); } handle = GUSWAVE_Play( voice, angle, volume, 1 ); return( handle ); } /*--------------------------------------------------------------------- Function: GUSWAVE_PlayWAV Begins playback of digitized sound. ---------------------------------------------------------------------*/ int GUSWAVE_PlayWAV ( char *sample, int pitchoffset, int angle, int volume, int priority, unsigned long callbackval ) { VoiceNode *voice; int handle; int channels; int bits; int length; riff_header *riff; format_header *format; data_header *data; riff = ( riff_header * )sample; if ( ( strncmp( riff->RIFF, "RIFF", 4 ) != 0 ) || ( strncmp( riff->WAVE, "WAVE", 4 ) != 0 ) || ( strncmp( riff->fmt, "fmt ", 4) != 0 ) ) { GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile ); return( GUSWAVE_Error ); } format = ( format_header * )( riff + 1 ); data = ( data_header * )( ( ( char * )format ) + riff->format_size ); if ( format->wFormatTag != 1 ) { GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile ); return( GUSWAVE_Error ); } channels = format->nChannels; if ( ( channels != 1 ) && ( channels != 2 ) ) { GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile ); return( GUSWAVE_Error ); } bits = format->nBitsPerSample; if ( ( bits != 8 ) && ( bits != 16 ) ) { GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile ); return( GUSWAVE_Error ); } if ( strncmp( data->DATA, "data", 4 ) != 0 ) { GUSWAVE_SetErrorCode( GUSWAVE_InvalidWAVFile ); return( GUSWAVE_Error ); } // Request a voice from the voice pool voice = GUSWAVE_AllocVoice( priority ); if ( voice == NULL ) { if ( GUS_Debug ) { DB_printf( "No more voices. Skipping sound.\n" ); } GUSWAVE_SetErrorCode( GUSWAVE_NoVoices ); return( GUSWAVE_Warning ); } voice->wavetype = WAV; voice->bits = bits; voice->GetSound = GUSWAVE_GetNextWAVBlock; length = data->size; voice->Playing = TRUE; voice->DemandFeed = NULL; voice->LoopStart = NULL; voice->LoopCount = 0; voice->length = min( length, 0x8000 ); voice->BlockLength = length - voice->length;// min( loopend + 1, data->size ); voice->sound = ( char * )( data + 1 ); voice->NextBlock = voice->sound + voice->length; voice->next = NULL; voice->prev = NULL; voice->priority = priority; voice->callbackval = callbackval; voice->LoopStart = NULL;// voice->NextBlock + loopstart; voice->LoopEnd = NULL;//voice->NextBlock + min( loopend, data->size - 1 ); voice->LoopSize = 0;//( voice->LoopEnd - voice->LoopStart ) + 1; voice->PitchScale = PITCH_GetScale( pitchoffset ); voice->SamplingRate = format->nSamplesPerSec; voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) >> 16; handle = GUSWAVE_Play( voice, angle, volume, channels ); return( handle ); } /*--------------------------------------------------------------------- Function: GUSWAVE_StartDemandFeedPlayback Begins playback of digitized sound. ---------------------------------------------------------------------*/ int GUSWAVE_StartDemandFeedPlayback ( void ( *function )( char **ptr, unsigned long *length ), int channels, int bits, int rate, int pitchoffset, int angle, int volume, int priority, unsigned long callbackval ) { VoiceNode *voice; int handle; // Request a voice from the voice pool voice = GUSWAVE_AllocVoice( priority ); if ( voice == NULL ) { if ( GUS_Debug ) { DB_printf( "No more voices. Skipping sound.\n" ); } GUSWAVE_SetErrorCode( GUSWAVE_NoVoices ); return( GUSWAVE_Warning ); } voice->wavetype = DemandFeed; voice->bits = bits; voice->GetSound = GUSWAVE_GetNextDemandFeedBlock; voice->Playing = TRUE; voice->DemandFeed = function; voice->LoopStart = NULL; voice->LoopCount = 0; voice->BlockLength = 0; voice->length = 256; voice->sound = ( bits == 8 ) ? GUS_Silence8 : ( char * )GUS_Silence16; voice->NextBlock = NULL; voice->next = NULL; voice->prev = NULL; voice->priority = priority; voice->callbackval = callbackval; voice->PitchScale = PITCH_GetScale( pitchoffset ); voice->SamplingRate = rate; voice->RateScale = ( voice->SamplingRate * voice->PitchScale ) >> 16; handle = GUSWAVE_Play( voice, angle, volume, channels ); return( handle ); } /*--------------------------------------------------------------------- Function: GUSWAVE_SetReverseStereo Set the orientation of the left and right channels. ---------------------------------------------------------------------*/ void GUSWAVE_SetReverseStereo ( int setting ) { GUSWAVE_SwapLeftRight = setting; } /*--------------------------------------------------------------------- Function: GUSWAVE_GetReverseStereo Returns the orientation of the left and right channels. ---------------------------------------------------------------------*/ int GUSWAVE_GetReverseStereo ( void ) { return( GUSWAVE_SwapLeftRight ); } /*--------------------------------------------------------------------- Function: GUSWAVE_InitVoices Begins playback of digitized sound. ---------------------------------------------------------------------*/ static int GUSWAVE_InitVoices ( void ) { int i; for( i = 0; i < MAX_VOICES; i++ ) { VoiceStatus[ i ].playing = FALSE; VoiceStatus[ i ].Voice = NULL; } VoicePool.start = NULL; VoicePool.end = NULL; VoiceList.start = NULL; VoiceList.end = NULL; for( i = 0; i < VOICES; i++ ) { GUSWAVE_Voices[ i ].num = -1; GUSWAVE_Voices[ i ].Active = FALSE; GUSWAVE_Voices[ i ].GF1voice = -1; GUSWAVE_Voices[ i ].mem = NULL; } for( i = 0; i < VOICES; i++ ) { GUSWAVE_Voices[ i ].num = i; GUSWAVE_Voices[ i ].Active = FALSE; GUSWAVE_Voices[ i ].GF1voice = 0; GUSWAVE_Voices[ i ].mem = gf1_malloc( GF1BSIZE ); if ( GUSWAVE_Voices[ i ].mem == NULL ) { GUSWAVE_MaxVoices = i; if ( i < 1 ) { if ( GUSMIDI_Installed ) { GUSWAVE_SetErrorCode( GUSWAVE_UltraNoMemMIDI ); } else { GUSWAVE_SetErrorCode( GUSWAVE_UltraNoMem ); } return( GUSWAVE_Error ); } break; } LL_AddToTail( VoiceNode, &VoicePool, &GUSWAVE_Voices[ i ] ); } return( GUSWAVE_Ok ); } /*--------------------------------------------------------------------- Function: GUSWAVE_SetCallBack Set the function to call when a voice stops. ---------------------------------------------------------------------*/ void GUSWAVE_SetCallBack ( void ( *function )( unsigned long ) ) { GUSWAVE_CallBackFunc = function; } /*--------------------------------------------------------------------- Function: GUSWAVE_Init Initializes the Gravis Ultrasound for digitized sound playback. ---------------------------------------------------------------------*/ int GUSWAVE_Init ( int numvoices ) { int status; if ( GUSWAVE_Installed ) { GUSWAVE_Shutdown(); } GUSWAVE_SetErrorCode( GUSWAVE_Ok ); status = GUS_Init(); if ( status != GUS_Ok ) { GUSWAVE_SetErrorCode( GUSWAVE_GUSError ); return( GUSWAVE_Error ); } GUS_Debug = USER_CheckParameter( "DEBUGGUS" ); GUSWAVE_MaxVoices = min( numvoices, VOICES ); GUSWAVE_MaxVoices = max( GUSWAVE_MaxVoices, 0 ); status = GUSWAVE_InitVoices(); if ( status != GUSWAVE_Ok ) { GUS_Shutdown(); return( status ); } GUSWAVE_SetReverseStereo( FALSE ); GUSWAVE_CallBackFunc = NULL; GUSWAVE_Installed = TRUE; return( GUSWAVE_Ok ); }