/* 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: SNDSRC.C author: James R. Dose date: March 26, 1994 Low level routines to support the Disney Sound Source. (c) Copyright 1994 James R. Dose. All Rights Reserved. **********************************************************************/ #define STEREO 1 #define SIXTEEN_BIT 2 #define MONO_8BIT 0 #define STEREO_8BIT ( STEREO ) #define MONO_16BIT ( SIXTEEN_BIT ) #define STEREO_16BIT ( STEREO | SIXTEEN_BIT ) #include #include #include #include "dpmi.h" #include "task_man.h" #include "sndcards.h" #include "user.h" #include "sndsrc.h" #define TRUE ( 1 == 1 ) #define FALSE ( !TRUE ) static int SS_Installed = FALSE; static int SS_Port = SS_DefaultPort; static int SS_OffCommand = 0xc; static char *SS_BufferStart; static char *SS_BufferEnd; static char *SS_CurrentBuffer; static int SS_BufferNum = 0; static int SS_NumBuffers = 0; static int SS_TotalBufferSize = 0; static int SS_TransferLength = 0; static int SS_CurrentLength = 0; static char *SS_SoundPtr; volatile int SS_SoundPlaying; static task *SS_Timer; void ( *SS_CallBack )( void ); int SS_ErrorCode = SS_Ok; #define SS_SetErrorCode( status ) \ SS_ErrorCode = ( status ); /*--------------------------------------------------------------------- Function: SS_ErrorString Returns a pointer to the error message associated with an error number. A -1 returns a pointer the current error. ---------------------------------------------------------------------*/ char *SS_ErrorString ( int ErrorNumber ) { char *ErrorString; switch( ErrorNumber ) { case SS_Error : ErrorString = SS_ErrorString( SS_ErrorCode ); break; case SS_Ok : ErrorString = "Sound Source ok."; break; case SS_NotFound : ErrorString = "Could not detect Sound Source."; break; case SS_NoSoundPlaying : ErrorString = "No sound playing in SndSrc."; break; case SS_DPMI_Error : ErrorString = "DPMI Error in SndSrc."; break; default : ErrorString = "Unknown Sound Source error code."; break; } return( ErrorString ); } /********************************************************************** Memory locked functions: **********************************************************************/ #define SS_LockStart SS_ServiceInterrupt /*--------------------------------------------------------------------- Function: SS_ServiceInterrupt Handles interrupt generated by sound card at the end of a voice transfer. Calls the user supplied callback function. ---------------------------------------------------------------------*/ static void SS_ServiceInterrupt ( task *Task ) { int port = SS_Port; int count; count = 0; while( ( inp( port + 1 ) & 0x40 ) == 0 ) { outp( port, *SS_SoundPtr++ ); outp( port + 2, SS_OffCommand ); outp( port + 2, 4 ); SS_CurrentLength--; if ( SS_CurrentLength == 0 ) { // Keep track of current buffer SS_CurrentBuffer += SS_TransferLength; SS_BufferNum++; if ( SS_BufferNum >= SS_NumBuffers ) { SS_BufferNum = 0; SS_CurrentBuffer = SS_BufferStart; } SS_CurrentLength = SS_TransferLength; SS_SoundPtr = SS_CurrentBuffer; // Call the caller's callback function if ( SS_CallBack != NULL ) { SS_CallBack(); } } count++; // Only do at most 14 samples per tick if ( count > 13 ) { break; } } } /*--------------------------------------------------------------------- Function: SS_StopPlayback Ends the transfer of digitized sound to the Sound Source. ---------------------------------------------------------------------*/ void SS_StopPlayback ( void ) { if ( SS_SoundPlaying ) { TS_Terminate( SS_Timer ); outp( SS_Port, 0x80 ); outp( SS_Port + 2, SS_OffCommand ); outp( SS_Port + 2, 4 ); SS_SoundPlaying = FALSE; SS_BufferStart = NULL; } } /*--------------------------------------------------------------------- Function: SS_GetCurrentPos Returns the offset within the current sound being played. ---------------------------------------------------------------------*/ int SS_GetCurrentPos ( void ) { int offset; if ( !SS_SoundPlaying ) { SS_SetErrorCode( SS_NoSoundPlaying ); return( SS_Warning ); } offset = ( int )( ( ( unsigned long )SS_SoundPtr ) - ( ( unsigned long )SS_CurrentBuffer ) ); return( offset ); } /*--------------------------------------------------------------------- Function: SS_LockEnd Used for determining the length of the functions to lock in memory. ---------------------------------------------------------------------*/ static void SS_LockEnd ( void ) { } /*--------------------------------------------------------------------- Function: SS_BeginBufferedPlayback Begins multibuffered playback of digitized sound on the Sound Source. ---------------------------------------------------------------------*/ int SS_BeginBufferedPlayback ( char *BufferStart, int BufferSize, int NumDivisions, void ( *CallBackFunc )( void ) ) { if ( SS_SoundPlaying ) { SS_StopPlayback(); } SS_SetCallBack( CallBackFunc ); SS_BufferStart = BufferStart; SS_CurrentBuffer = BufferStart; SS_SoundPtr = BufferStart; SS_TotalBufferSize = BufferSize; SS_BufferEnd = BufferStart + BufferSize; SS_TransferLength = BufferSize / NumDivisions; SS_CurrentLength = SS_TransferLength; SS_BufferNum = 0; SS_NumBuffers = NumDivisions; SS_SoundPlaying = TRUE; // SS_Timer = TS_ScheduleTask( SS_ServiceInterrupt, 438, 1, NULL ); SS_Timer = TS_ScheduleTask( SS_ServiceInterrupt, 510, 1, NULL ); TS_Dispatch(); return( SS_Ok ); } /*--------------------------------------------------------------------- Function: SS_GetPlaybackRate Returns the rate at which the digitized sound will be played in hertz. ---------------------------------------------------------------------*/ int SS_GetPlaybackRate ( void ) { return( SS_SampleRate ); } /*--------------------------------------------------------------------- Function: SS_SetMixMode Sets the sound card to play samples in mono or stereo. ---------------------------------------------------------------------*/ int SS_SetMixMode ( int mode ) { mode = MONO_8BIT; return( mode ); } /*--------------------------------------------------------------------- Function: SS_SetPort Selects which port to use to write to the Sound Source. ---------------------------------------------------------------------*/ int SS_SetPort ( int port ) { if ( SS_Installed ) { SS_Shutdown(); } SS_Port = port; return( SS_Ok ); } /*--------------------------------------------------------------------- Function: SS_SetCallBack Specifies the user function to call at the end of a sound transfer. ---------------------------------------------------------------------*/ void SS_SetCallBack ( void ( *func )( void ) ) { SS_CallBack = func; } /*--------------------------------------------------------------------- Function: SS_TestTimer Used as a delay in SS_TestSoundSource. ---------------------------------------------------------------------*/ void SS_TestTimer ( task *Task ) { ( *( int * )( Task->data ) )++; } /*--------------------------------------------------------------------- Function: SS_TestSoundSource Detect if the Sound Source is located at the specified port. ---------------------------------------------------------------------*/ int SS_TestSoundSource ( int port ) { int present; task *timer; volatile int ticks; int i; present = FALSE; timer = TS_ScheduleTask( SS_TestTimer, 140, 1, &ticks ); TS_Dispatch(); outp( port + 2, 4 ); ticks = 0; while( ticks < 4 ) { // Do nothing for a while } TS_Terminate( timer ); if ( ( inp( port + 1 ) & 0x40 ) == 0 ) { for( i = 32; i > 0; i-- ) { outp( port, 0x80 ); outp( port + 2, SS_OffCommand ); outp( port + 2, 4 ); } if ( inp( port + 1 ) & 0x40 ) { present = TRUE; } } outp( port + 2, SS_OffCommand ); return( present ); } /*--------------------------------------------------------------------- Function: SS_DetectSoundSource Detects which port the Sound Source is located. ---------------------------------------------------------------------*/ int SS_DetectSoundSource ( void ) { if ( USER_CheckParameter( SELECT_SOUNDSOURCE_PORT1 ) ) { SS_Port = SS_Port1; return( TRUE ); } if ( USER_CheckParameter( SELECT_SOUNDSOURCE_PORT2 ) ) { SS_Port = SS_Port2; return( TRUE ); } if ( USER_CheckParameter( SELECT_SOUNDSOURCE_PORT3 ) ) { SS_Port = SS_Port3; return( TRUE ); } if ( SS_TestSoundSource( SS_Port1 ) ) { SS_Port = SS_Port1; return( TRUE ); } if ( SS_TestSoundSource( SS_Port2 ) ) { SS_Port = SS_Port2; return( TRUE ); } if ( SS_TestSoundSource( SS_Port3 ) ) { SS_Port = SS_Port3; return( TRUE ); } return( FALSE ); } /*--------------------------------------------------------------------- Function: SS_Init Initializes the Sound Source prepares the module to play digitized sounds. ---------------------------------------------------------------------*/ int SS_Init ( int soundcard ) { int status; if ( SS_Installed ) { SS_Shutdown(); } if ( ( soundcard == TandySoundSource ) || ( USER_CheckParameter( SELECT_TANDY_SOUNDSOURCE ) ) ) { // Tandy SS_OffCommand = 0x0e; } else { // Disney SS_OffCommand = 0x0c; } status = SS_DetectSoundSource(); if ( !status ) { SS_SetErrorCode( SS_NotFound ); return( SS_Warning ); } status = SS_LockMemory(); if ( status != SS_Ok ) { SS_UnlockMemory(); return( status ); } status = SS_Ok; outp( SS_Port + 2, 4 ); SS_SoundPlaying = FALSE; SS_SetCallBack( NULL ); SS_BufferStart = NULL; SS_Installed = TRUE; SS_SetErrorCode( status ); return( status ); } /*--------------------------------------------------------------------- Function: SS_Shutdown Ends transfer of sound data to the Sound Source. ---------------------------------------------------------------------*/ void SS_Shutdown ( void ) { // Halt the transfer SS_StopPlayback(); outp( SS_Port + 2, SS_OffCommand ); SS_SoundPlaying = FALSE; SS_BufferStart = NULL; SS_SetCallBack( NULL ); SS_UnlockMemory(); SS_Installed = FALSE; } /*--------------------------------------------------------------------- Function: SS_UnlockMemory Unlocks all neccessary data. ---------------------------------------------------------------------*/ void SS_UnlockMemory ( void ) { DPMI_UnlockMemoryRegion( SS_LockStart, SS_LockEnd ); DPMI_Unlock( SS_Installed ); DPMI_Unlock( SS_Port ); DPMI_Unlock( SS_OffCommand ); DPMI_Unlock( SS_BufferStart ); DPMI_Unlock( SS_BufferEnd ); DPMI_Unlock( SS_CurrentBuffer ); DPMI_Unlock( SS_BufferNum ); DPMI_Unlock( SS_NumBuffers ); DPMI_Unlock( SS_TotalBufferSize ); DPMI_Unlock( SS_TransferLength ); DPMI_Unlock( SS_CurrentLength ); DPMI_Unlock( SS_SoundPtr ); DPMI_Unlock( SS_SoundPlaying ); DPMI_Unlock( SS_Timer ); DPMI_Unlock( SS_CallBack ); DPMI_Unlock( SS_ErrorCode ); } /*--------------------------------------------------------------------- Function: SS_LockMemory Locks all neccessary data. ---------------------------------------------------------------------*/ int SS_LockMemory ( void ) { int status; status = DPMI_LockMemoryRegion( SS_LockStart, SS_LockEnd ); status |= DPMI_Lock( SS_Installed ); status |= DPMI_Lock( SS_Port ); status |= DPMI_Lock( SS_OffCommand ); status |= DPMI_Lock( SS_BufferStart ); status |= DPMI_Lock( SS_BufferEnd ); status |= DPMI_Lock( SS_CurrentBuffer ); status |= DPMI_Lock( SS_BufferNum ); status |= DPMI_Lock( SS_NumBuffers ); status |= DPMI_Lock( SS_TotalBufferSize ); status |= DPMI_Lock( SS_TransferLength ); status |= DPMI_Lock( SS_CurrentLength ); status |= DPMI_Lock( SS_SoundPtr ); status |= DPMI_Lock( SS_SoundPlaying ); status |= DPMI_Lock( SS_Timer ); status |= DPMI_Lock( SS_CallBack ); status |= DPMI_Lock( SS_ErrorCode ); if ( status != DPMI_Ok ) { SS_UnlockMemory(); SS_SetErrorCode( SS_DPMI_Error ); return( SS_Error ); } return( SS_Ok ); }