1924 lines
45 KiB
C
1924 lines
45 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: PAS16.C
|
|
|
|
author: James R. Dose
|
|
date: March 27, 1994
|
|
|
|
Low level routines to support Pro AudioSpectrum and compatible
|
|
sound cards.
|
|
|
|
(c) Copyright 1994 James R. Dose. All Rights Reserved.
|
|
**********************************************************************/
|
|
|
|
#include <dos.h>
|
|
#include <conio.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "dpmi.h"
|
|
#include "dma.h"
|
|
#include "interrup.h"
|
|
#include "irq.h"
|
|
#include "pas16.h"
|
|
#include "_pas16.h"
|
|
|
|
#define USESTACK
|
|
|
|
static const int PAS_Interrupts[ PAS_MaxIrq + 1 ] =
|
|
{
|
|
INVALID, INVALID, 0xa, 0xb,
|
|
INVALID, 0xd, INVALID, 0xf,
|
|
INVALID, INVALID, 0x72, 0x73,
|
|
0x74, INVALID, INVALID, 0x77
|
|
};
|
|
|
|
static void ( interrupt far *PAS_OldInt )( void );
|
|
|
|
static int PAS_IntController1Mask;
|
|
static int PAS_IntController2Mask;
|
|
|
|
static int PAS_Installed = FALSE;
|
|
static int PAS_TranslateCode = DEFAULT_BASE;
|
|
|
|
static int PAS_OriginalPCMLeftVolume = 75;
|
|
static int PAS_OriginalPCMRightVolume = 75;
|
|
|
|
static int PAS_OriginalFMLeftVolume = 75;
|
|
static int PAS_OriginalFMRightVolume = 75;
|
|
|
|
unsigned int PAS_DMAChannel;
|
|
static int PAS_Irq;
|
|
|
|
static MVState *PAS_State = NULL;
|
|
static MVFunc *PAS_Func = NULL;
|
|
|
|
static MVState PAS_OriginalState;
|
|
static int PAS_SampleSizeConfig;
|
|
|
|
static char *PAS_DMABuffer;
|
|
static char *PAS_DMABufferEnd;
|
|
static char *PAS_CurrentDMABuffer;
|
|
static int PAS_TotalDMABufferSize;
|
|
|
|
static int PAS_TransferLength = 0;
|
|
static int PAS_MixMode = PAS_DefaultMixMode;
|
|
static unsigned PAS_SampleRate = PAS_DefaultSampleRate;
|
|
static int PAS_TimeInterval = 0;
|
|
|
|
volatile int PAS_SoundPlaying;
|
|
|
|
void ( *PAS_CallBack )( void );
|
|
|
|
// adequate stack size
|
|
#define kStackSize 2048
|
|
|
|
static unsigned short StackSelector = NULL;
|
|
static unsigned long StackPointer;
|
|
|
|
static unsigned short oldStackSelector;
|
|
static unsigned long oldStackPointer;
|
|
|
|
// This is defined because we can't create local variables in a
|
|
// function that switches stacks.
|
|
static int irqstatus;
|
|
|
|
// These declarations are necessary to use the inline assembly pragmas.
|
|
|
|
extern void GetStack(unsigned short *selptr,unsigned long *stackptr);
|
|
extern void SetStack(unsigned short selector,unsigned long stackptr);
|
|
|
|
// This function will get the current stack selector and pointer and save
|
|
// them off.
|
|
#pragma aux GetStack = \
|
|
"mov [edi],esp" \
|
|
"mov ax,ss" \
|
|
"mov [esi],ax" \
|
|
parm [esi] [edi] \
|
|
modify [eax esi edi];
|
|
|
|
// This function will set the stack selector and pointer to the specified
|
|
// values.
|
|
#pragma aux SetStack = \
|
|
"mov ss,ax" \
|
|
"mov esp,edx" \
|
|
parm [ax] [edx] \
|
|
modify [eax edx];
|
|
|
|
int PAS_ErrorCode = PAS_Ok;
|
|
|
|
#define PAS_SetErrorCode( status ) \
|
|
PAS_ErrorCode = ( status );
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_ErrorString
|
|
|
|
Returns a pointer to the error message associated with an error
|
|
number. A -1 returns a pointer the current error.
|
|
---------------------------------------------------------------------*/
|
|
|
|
char *PAS_ErrorString
|
|
(
|
|
int ErrorNumber
|
|
)
|
|
|
|
{
|
|
char *ErrorString;
|
|
|
|
switch( ErrorNumber )
|
|
{
|
|
case PAS_Warning :
|
|
case PAS_Error :
|
|
ErrorString = PAS_ErrorString( PAS_ErrorCode );
|
|
break;
|
|
|
|
case PAS_Ok :
|
|
ErrorString = "Pro AudioSpectrum ok.";
|
|
break;
|
|
|
|
case PAS_DriverNotFound :
|
|
ErrorString = "MVSOUND.SYS not loaded.";
|
|
break;
|
|
|
|
case PAS_DmaError :
|
|
ErrorString = DMA_ErrorString( DMA_Error );
|
|
break;
|
|
|
|
case PAS_InvalidIrq :
|
|
ErrorString = "Invalid Pro AudioSpectrum Irq.";
|
|
break;
|
|
|
|
case PAS_UnableToSetIrq :
|
|
ErrorString = "Unable to set Pro AudioSpectrum IRQ. Try selecting an IRQ of 7 or below.";
|
|
break;
|
|
|
|
case PAS_Dos4gwIrqError :
|
|
ErrorString = "Unsupported Pro AudioSpectrum Irq.";
|
|
break;
|
|
|
|
case PAS_NoSoundPlaying :
|
|
ErrorString = "No sound playing on Pro AudioSpectrum.";
|
|
break;
|
|
|
|
case PAS_CardNotFound :
|
|
ErrorString = "Could not find Pro AudioSpectrum.";
|
|
break;
|
|
|
|
case PAS_DPMI_Error :
|
|
ErrorString = "DPMI Error in PAS16.";
|
|
break;
|
|
|
|
case PAS_OutOfMemory :
|
|
ErrorString = "Out of conventional memory in PAS16.";
|
|
break;
|
|
|
|
default :
|
|
ErrorString = "Unknown Pro AudioSpectrum error code.";
|
|
break;
|
|
}
|
|
|
|
return( ErrorString );
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
|
|
Memory locked functions:
|
|
|
|
**********************************************************************/
|
|
|
|
|
|
#define PAS_LockStart PAS_CheckForDriver
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_CheckForDriver
|
|
|
|
Checks to see if MVSOUND.SYS is installed.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_CheckForDriver
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
union REGS regs;
|
|
unsigned result;
|
|
|
|
regs.w.ax = MV_CheckForDriver;
|
|
regs.w.bx = 0x3f3f;
|
|
|
|
#ifdef __386__
|
|
int386( MV_SoundInt, ®s, ®s );
|
|
#else
|
|
int86( MV_SoundInt, ®s, ®s );
|
|
#endif
|
|
|
|
if ( regs.w.ax != MV_CheckForDriver )
|
|
{
|
|
PAS_SetErrorCode( PAS_DriverNotFound );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
result = regs.w.bx ^ regs.w.cx ^ regs.w.dx;
|
|
if ( result != MV_Signature )
|
|
{
|
|
PAS_SetErrorCode( PAS_DriverNotFound );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_GetStateTable
|
|
|
|
Returns a pointer to the state table containing hardware state
|
|
information. The state table is necessary because the Pro Audio-
|
|
Spectrum contains only write-only registers.
|
|
---------------------------------------------------------------------*/
|
|
|
|
MVState *PAS_GetStateTable
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
union REGS regs;
|
|
MVState *ptr;
|
|
|
|
regs.w.ax = MV_GetPointerToStateTable;
|
|
|
|
#ifdef __386__
|
|
int386( MV_SoundInt, ®s, ®s );
|
|
#else
|
|
int86( MV_SoundInt, ®s, ®s );
|
|
#endif
|
|
|
|
if ( regs.w.ax != MV_Signature )
|
|
{
|
|
PAS_SetErrorCode( PAS_DriverNotFound );
|
|
return( NULL );
|
|
}
|
|
|
|
#if defined(__WATCOMC__) && defined(__FLAT__)
|
|
ptr = ( MVState * )( ( ( ( unsigned )regs.w.dx ) << 4 ) +
|
|
( ( unsigned )regs.w.bx ) );
|
|
#else
|
|
ptr = MK_FP( regs.w.dx, regs.w.bx );
|
|
#endif
|
|
|
|
return( ptr );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_GetFunctionTable
|
|
|
|
Returns a pointer to the function table containing addresses of
|
|
driver functions.
|
|
---------------------------------------------------------------------*/
|
|
|
|
MVFunc *PAS_GetFunctionTable
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
union REGS regs;
|
|
MVFunc *ptr;
|
|
|
|
regs.w.ax = MV_GetPointerToFunctionTable;
|
|
|
|
#ifdef __386__
|
|
int386( MV_SoundInt, ®s, ®s );
|
|
#else
|
|
int86( MV_SoundInt, ®s, ®s );
|
|
#endif
|
|
|
|
if ( regs.w.ax != MV_Signature )
|
|
{
|
|
PAS_SetErrorCode( PAS_DriverNotFound );
|
|
return( NULL );
|
|
}
|
|
|
|
#if defined(__WATCOMC__) && defined(__FLAT__)
|
|
ptr = ( MVFunc * )( ( ( ( unsigned )regs.w.dx ) << 4 ) +
|
|
( ( unsigned )regs.w.bx ) );
|
|
#else
|
|
ptr = MK_FP( regs.w.dx, regs.w.bx );
|
|
#endif
|
|
|
|
return( ptr );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_GetCardSettings
|
|
|
|
Returns the DMA and the IRQ channels of the sound card.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_GetCardSettings
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
union REGS regs;
|
|
int status;
|
|
|
|
regs.w.ax = MV_GetDmaIrqInt;
|
|
|
|
#ifdef __386__
|
|
int386( MV_SoundInt, ®s, ®s );
|
|
#else
|
|
int86( MV_SoundInt, ®s, ®s );
|
|
#endif
|
|
|
|
if ( regs.w.ax != MV_Signature )
|
|
{
|
|
PAS_SetErrorCode( PAS_DriverNotFound );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
PAS_DMAChannel = regs.w.bx;
|
|
PAS_Irq = regs.w.cx;
|
|
|
|
if ( PAS_Irq > PAS_MaxIrq )
|
|
{
|
|
PAS_SetErrorCode( PAS_Dos4gwIrqError );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
if ( !VALID_IRQ( PAS_Irq ) )
|
|
{
|
|
PAS_SetErrorCode( PAS_InvalidIrq );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
if ( PAS_Interrupts[ PAS_Irq ] == INVALID )
|
|
{
|
|
PAS_SetErrorCode( PAS_InvalidIrq );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
status = DMA_VerifyChannel( PAS_DMAChannel );
|
|
if ( status == DMA_Error )
|
|
{
|
|
PAS_SetErrorCode( PAS_DmaError );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_EnableInterrupt
|
|
|
|
Enables the triggering of the sound card interrupt.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_EnableInterrupt
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int mask;
|
|
int data;
|
|
unsigned flags;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
if ( PAS_Irq < 8 )
|
|
{
|
|
mask = inp( 0x21 ) & ~( 1 << PAS_Irq );
|
|
outp( 0x21, mask );
|
|
}
|
|
else
|
|
{
|
|
mask = inp( 0xA1 ) & ~( 1 << ( PAS_Irq - 8 ) );
|
|
outp( 0xA1, mask );
|
|
|
|
mask = inp( 0x21 ) & ~( 1 << 2 );
|
|
outp( 0x21, mask );
|
|
}
|
|
|
|
// Flush any pending interrupts
|
|
PAS_Write( InterruptStatus, PAS_Read( InterruptStatus ) & 0x40 );
|
|
|
|
// Enable the interrupt on the PAS
|
|
data = PAS_State->intrctlr;
|
|
data |= SampleBufferInterruptFlag;
|
|
PAS_Write( InterruptControl, data );
|
|
PAS_State->intrctlr = data;
|
|
|
|
RestoreInterrupts( flags );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_DisableInterrupt
|
|
|
|
Disables the triggering of the sound card interrupt.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_DisableInterrupt
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int mask;
|
|
int data;
|
|
unsigned flags;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
// Disable the interrupt on the PAS
|
|
data = PAS_State->intrctlr;
|
|
data &= ~( SampleRateInterruptFlag | SampleBufferInterruptFlag );
|
|
PAS_Write( InterruptControl, data );
|
|
PAS_State->intrctlr = data;
|
|
|
|
// Restore interrupt mask
|
|
if ( PAS_Irq < 8 )
|
|
{
|
|
mask = inp( 0x21 ) & ~( 1 << PAS_Irq );
|
|
mask |= PAS_IntController1Mask & ( 1 << PAS_Irq );
|
|
outp( 0x21, mask );
|
|
}
|
|
else
|
|
{
|
|
mask = inp( 0x21 ) & ~( 1 << 2 );
|
|
mask |= PAS_IntController1Mask & ( 1 << 2 );
|
|
outp( 0x21, mask );
|
|
|
|
mask = inp( 0xA1 ) & ~( 1 << ( PAS_Irq - 8 ) );
|
|
mask |= PAS_IntController2Mask & ( 1 << ( PAS_Irq - 8 ) );
|
|
outp( 0xA1, mask );
|
|
}
|
|
|
|
RestoreInterrupts( flags );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_ServiceInterrupt
|
|
|
|
Handles interrupt generated by sound card at the end of a voice
|
|
transfer. Calls the user supplied callback function.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void interrupt far PAS_ServiceInterrupt
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
#ifdef USESTACK
|
|
// save stack
|
|
GetStack( &oldStackSelector, &oldStackPointer );
|
|
|
|
// set our stack
|
|
SetStack( StackSelector, StackPointer );
|
|
#endif
|
|
|
|
irqstatus = PAS_Read( InterruptStatus );
|
|
if ( ( irqstatus & SampleBufferInterruptFlag ) == 0 )
|
|
{
|
|
#ifdef USESTACK
|
|
// restore stack
|
|
SetStack( oldStackSelector, oldStackPointer );
|
|
#endif
|
|
|
|
_chain_intr( PAS_OldInt );
|
|
}
|
|
|
|
// Clear the interrupt
|
|
irqstatus &= ~SampleBufferInterruptFlag;
|
|
PAS_Write( InterruptStatus, irqstatus );
|
|
|
|
// send EOI to Interrupt Controller
|
|
if ( PAS_Irq > 7 )
|
|
{
|
|
outp( 0xA0, 0x20 );
|
|
}
|
|
outp( 0x20, 0x20 );
|
|
|
|
|
|
// Keep track of current buffer
|
|
PAS_CurrentDMABuffer += PAS_TransferLength;
|
|
if ( PAS_CurrentDMABuffer >= PAS_DMABufferEnd )
|
|
{
|
|
PAS_CurrentDMABuffer = PAS_DMABuffer;
|
|
}
|
|
|
|
// Call the caller's callback function
|
|
if ( PAS_CallBack != NULL )
|
|
{
|
|
PAS_CallBack();
|
|
}
|
|
|
|
#ifdef USESTACK
|
|
// restore stack
|
|
SetStack( oldStackSelector, oldStackPointer );
|
|
#endif
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_Write
|
|
|
|
Writes a byte of data to the sound card.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_Write
|
|
(
|
|
int Register,
|
|
int Data
|
|
)
|
|
|
|
{
|
|
int port;
|
|
|
|
port = Register ^ PAS_TranslateCode;
|
|
outp( port, Data );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_Read
|
|
|
|
Reads a byte of data from the sound card.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_Read
|
|
(
|
|
int Register
|
|
)
|
|
|
|
{
|
|
int port;
|
|
int data;
|
|
|
|
port = Register ^ PAS_TranslateCode;
|
|
data = inp( port );
|
|
return( data );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SetSampleRateTimer
|
|
|
|
Programs the Sample Rate Timer.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_SetSampleRateTimer
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int LoByte;
|
|
int HiByte;
|
|
int data;
|
|
unsigned flags;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
// Disable the Sample Rate Timer
|
|
data = PAS_State->audiofilt;
|
|
data &= ~SampleRateTimerGateFlag;
|
|
PAS_Write( AudioFilterControl, data );
|
|
PAS_State->audiofilt = data;
|
|
|
|
// Select the Sample Rate Timer
|
|
data = SelectSampleRateTimer;
|
|
PAS_Write( LocalTimerControl, data );
|
|
PAS_State->tmrctlr = data;
|
|
|
|
LoByte = lobyte( PAS_TimeInterval );
|
|
HiByte = hibyte( PAS_TimeInterval );
|
|
|
|
// Program the Sample Rate Timer
|
|
PAS_Write( SampleRateTimer, LoByte );
|
|
PAS_Write( SampleRateTimer, HiByte );
|
|
PAS_State->samplerate = PAS_TimeInterval;
|
|
|
|
RestoreInterrupts( flags );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SetSampleBufferCount
|
|
|
|
Programs the Sample Buffer Count.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_SetSampleBufferCount
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int LoByte;
|
|
int HiByte;
|
|
int count;
|
|
int data;
|
|
unsigned flags;
|
|
|
|
flags = DisableInterrupts();
|
|
|
|
// Disable the Sample Buffer Count
|
|
data = PAS_State->audiofilt;
|
|
data &= ~SampleBufferCountGateFlag;
|
|
PAS_Write( AudioFilterControl, data );
|
|
PAS_State->audiofilt = data;
|
|
|
|
// Select the Sample Buffer Count
|
|
data = SelectSampleBufferCount;
|
|
PAS_Write( LocalTimerControl, data );
|
|
PAS_State->tmrctlr = data;
|
|
|
|
count = PAS_TransferLength;
|
|
|
|
// Check if we're using a 16-bit DMA channel
|
|
if ( PAS_DMAChannel > 3 )
|
|
{
|
|
count >>= 1;
|
|
}
|
|
|
|
LoByte = lobyte( count );
|
|
HiByte = hibyte( count );
|
|
|
|
// Program the Sample Buffer Count
|
|
PAS_Write( SampleBufferCount, LoByte );
|
|
PAS_Write( SampleBufferCount, HiByte );
|
|
PAS_State->samplecnt = count;
|
|
|
|
RestoreInterrupts( flags );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SetPlaybackRate
|
|
|
|
Sets the rate at which the digitized sound will be played in
|
|
hertz.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_SetPlaybackRate
|
|
(
|
|
unsigned rate
|
|
)
|
|
|
|
{
|
|
if ( rate < PAS_MinSamplingRate )
|
|
{
|
|
rate = PAS_MinSamplingRate;
|
|
}
|
|
|
|
if ( rate > PAS_MaxSamplingRate )
|
|
{
|
|
rate = PAS_MaxSamplingRate;
|
|
}
|
|
|
|
PAS_TimeInterval = ( unsigned )CalcTimeInterval( rate );
|
|
if ( PAS_MixMode & STEREO )
|
|
{
|
|
PAS_TimeInterval /= 2;
|
|
}
|
|
|
|
// Keep track of what the actual rate is
|
|
PAS_SampleRate = CalcSamplingRate( PAS_TimeInterval );
|
|
if ( PAS_MixMode & STEREO )
|
|
{
|
|
PAS_SampleRate /= 2;
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_GetPlaybackRate
|
|
|
|
Returns the rate at which the digitized sound will be played in
|
|
hertz.
|
|
---------------------------------------------------------------------*/
|
|
|
|
unsigned PAS_GetPlaybackRate
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
return( PAS_SampleRate );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SetMixMode
|
|
|
|
Sets the sound card to play samples in mono or stereo.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_SetMixMode
|
|
(
|
|
int mode
|
|
)
|
|
|
|
{
|
|
mode &= PAS_MaxMixMode;
|
|
|
|
// Check board revision. Revision # 0 can't play 16-bit data.
|
|
if ( ( PAS_State->intrctlr & 0xe0 ) == 0 )
|
|
{
|
|
// Force the mode to 8-bit data.
|
|
mode &= ~SIXTEEN_BIT;
|
|
}
|
|
|
|
PAS_MixMode = mode;
|
|
|
|
PAS_SetPlaybackRate( PAS_SampleRate );
|
|
|
|
return( mode );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_StopPlayback
|
|
|
|
Ends the DMA transfer of digitized sound to the sound card.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_StopPlayback
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int data;
|
|
|
|
// Don't allow anymore interrupts
|
|
PAS_DisableInterrupt();
|
|
|
|
// Stop the transfer of digital data
|
|
data = PAS_State->crosschannel;
|
|
data &= PAS_PCMStopMask;
|
|
PAS_Write( CrossChannelControl, data );
|
|
PAS_State->crosschannel = data;
|
|
|
|
// Turn off 16-bit unsigned data
|
|
data = PAS_Read( SampleSizeConfiguration );
|
|
data &= PAS_SampleSizeMask;
|
|
PAS_Write( SampleSizeConfiguration, data );
|
|
|
|
// Disable the DMA channel
|
|
DMA_EndTransfer( PAS_DMAChannel );
|
|
|
|
PAS_SoundPlaying = FALSE;
|
|
|
|
PAS_DMABuffer = NULL;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SetupDMABuffer
|
|
|
|
Programs the DMAC for sound transfer.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_SetupDMABuffer
|
|
(
|
|
char *BufferPtr,
|
|
int BufferSize,
|
|
int mode
|
|
)
|
|
|
|
{
|
|
int DmaStatus;
|
|
int data;
|
|
|
|
// Enable PAS Dma
|
|
data = PAS_State->crosschannel;
|
|
data |= PAS_DMAEnable;
|
|
PAS_Write( CrossChannelControl, data );
|
|
PAS_State->crosschannel = data;
|
|
|
|
DmaStatus = DMA_SetupTransfer( PAS_DMAChannel, BufferPtr, BufferSize, mode );
|
|
if ( DmaStatus == DMA_Error )
|
|
{
|
|
PAS_SetErrorCode( PAS_DmaError );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
PAS_DMABuffer = BufferPtr;
|
|
PAS_CurrentDMABuffer = BufferPtr;
|
|
PAS_TotalDMABufferSize = BufferSize;
|
|
PAS_DMABufferEnd = BufferPtr + BufferSize;
|
|
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_GetCurrentPos
|
|
|
|
Returns the offset within the current sound being played.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_GetCurrentPos
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
char *CurrentAddr;
|
|
int offset;
|
|
|
|
if ( !PAS_SoundPlaying )
|
|
{
|
|
PAS_SetErrorCode( PAS_NoSoundPlaying );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
CurrentAddr = DMA_GetCurrentPos( PAS_DMAChannel );
|
|
if ( CurrentAddr == NULL )
|
|
{
|
|
PAS_SetErrorCode( PAS_DmaError );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
offset = ( int )( ( ( unsigned long )CurrentAddr ) -
|
|
( ( unsigned long )PAS_CurrentDMABuffer ) );
|
|
|
|
if ( PAS_MixMode & SIXTEEN_BIT )
|
|
{
|
|
offset >>= 1;
|
|
}
|
|
|
|
if ( PAS_MixMode & STEREO )
|
|
{
|
|
offset >>= 1;
|
|
}
|
|
|
|
return( offset );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_GetFilterSetting
|
|
|
|
Returns the bit settings for the appropriate filter level.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_GetFilterSetting
|
|
(
|
|
int rate
|
|
)
|
|
|
|
{
|
|
/* CD Quality 17897hz */
|
|
if ( ( unsigned long )rate > ( unsigned long )17897L * 2 )
|
|
{
|
|
/* 00001b 20hz to 17.8khz */
|
|
return( 0x01 );
|
|
}
|
|
|
|
/* Cassette Quality 15090hz */
|
|
if ( ( unsigned long )rate > ( unsigned long )15909L * 2 )
|
|
{
|
|
/* 00010b 20hz to 15.9khz */
|
|
return( 0x02 );
|
|
}
|
|
|
|
/* FM Radio Quality 11931hz */
|
|
if ( ( unsigned long )rate > ( unsigned long )11931L * 2 )
|
|
{
|
|
/* 01001b 20hz to 11.9khz */
|
|
return( 0x09 );
|
|
}
|
|
|
|
/* AM Radio Quality 8948hz */
|
|
if ( ( unsigned long )rate > ( unsigned long )8948L * 2 )
|
|
{
|
|
/* 10001b 20hz to 8.9khz */
|
|
return( 0x11 );
|
|
}
|
|
|
|
/* Telphone Quality 5965hz */
|
|
if ( ( unsigned long )rate > ( unsigned long )5965L * 2 )
|
|
{
|
|
/* 00100b 20hz to 5.9khz */
|
|
return( 0x19 );
|
|
}
|
|
|
|
/* Male voice quality 2982hz */
|
|
/* 111001b 20hz to 2.9khz */
|
|
return( 0x04 );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_BeginTransfer
|
|
|
|
Starts playback of digitized sound on the sound card.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_BeginTransfer
|
|
(
|
|
int mode
|
|
)
|
|
|
|
{
|
|
int data;
|
|
|
|
PAS_SetSampleRateTimer();
|
|
|
|
PAS_SetSampleBufferCount();
|
|
|
|
PAS_EnableInterrupt();
|
|
|
|
// Get sample size configuration
|
|
data = PAS_Read( SampleSizeConfiguration );
|
|
|
|
// Check board revision. Revision # 0 can't play 16-bit data.
|
|
if ( PAS_State->intrctlr & 0xe0 )
|
|
{
|
|
data &= PAS_SampleSizeMask;
|
|
|
|
// set sample size bit
|
|
if ( PAS_MixMode & SIXTEEN_BIT )
|
|
{
|
|
data |= PAS_16BitSampleFlag;
|
|
}
|
|
}
|
|
|
|
// set oversampling rate
|
|
data &= PAS_OverSamplingMask;
|
|
data |= PAS_4xOverSampling;
|
|
|
|
// Set sample size configuration
|
|
PAS_Write( SampleSizeConfiguration, data );
|
|
|
|
// Get Cross channel setting
|
|
data = PAS_State->crosschannel;
|
|
data &= PAS_ChannelConnectMask;
|
|
if ( mode == RECORD )
|
|
{
|
|
data |= PAS_PCMStartADC;
|
|
}
|
|
else
|
|
{
|
|
data |= PAS_PCMStartDAC;
|
|
}
|
|
|
|
// set stereo mode bit
|
|
if ( !( PAS_MixMode & STEREO ) )
|
|
{
|
|
data |= PAS_StereoFlag;
|
|
}
|
|
|
|
PAS_Write( CrossChannelControl, data );
|
|
PAS_State->crosschannel = data;
|
|
|
|
// Get the filter appropriate filter setting
|
|
data = PAS_GetFilterSetting( PAS_SampleRate );
|
|
|
|
// Enable the Sample Rate Timer and Sample Buffer Count
|
|
data |= SampleRateTimerGateFlag | SampleBufferCountGateFlag;
|
|
|
|
if ( mode != RECORD )
|
|
{
|
|
// Enable audio (not Audio Mute)
|
|
data |= PAS_AudioMuteFlag;
|
|
}
|
|
|
|
PAS_Write( AudioFilterControl, data );
|
|
PAS_State->audiofilt = data;
|
|
|
|
PAS_SoundPlaying = TRUE;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_BeginBufferedPlayback
|
|
|
|
Begins multibuffered playback of digitized sound on the sound card.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_BeginBufferedPlayback
|
|
(
|
|
char *BufferStart,
|
|
int BufferSize,
|
|
int NumDivisions,
|
|
unsigned SampleRate,
|
|
int MixMode,
|
|
void ( *CallBackFunc )( void )
|
|
)
|
|
|
|
{
|
|
int DmaStatus;
|
|
|
|
PAS_StopPlayback();
|
|
|
|
PAS_SetMixMode( MixMode );
|
|
PAS_SetPlaybackRate( SampleRate );
|
|
|
|
PAS_TransferLength = BufferSize / NumDivisions;
|
|
PAS_SetCallBack( CallBackFunc );
|
|
|
|
DmaStatus = PAS_SetupDMABuffer( BufferStart, BufferSize, DMA_AutoInitRead );
|
|
if ( DmaStatus == PAS_Error )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
|
|
PAS_BeginTransfer( PLAYBACK );
|
|
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_BeginBufferedRecord
|
|
|
|
Begins multibuffered recording of digitized sound on the sound card.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_BeginBufferedRecord
|
|
(
|
|
char *BufferStart,
|
|
int BufferSize,
|
|
int NumDivisions,
|
|
unsigned SampleRate,
|
|
int MixMode,
|
|
void ( *CallBackFunc )( void )
|
|
)
|
|
|
|
{
|
|
int DmaStatus;
|
|
|
|
PAS_StopPlayback();
|
|
|
|
PAS_SetMixMode( MixMode );
|
|
PAS_SetPlaybackRate( SampleRate );
|
|
|
|
PAS_TransferLength = BufferSize / NumDivisions;
|
|
PAS_SetCallBack( CallBackFunc );
|
|
|
|
DmaStatus = PAS_SetupDMABuffer( BufferStart, BufferSize, DMA_AutoInitWrite );
|
|
if ( DmaStatus == PAS_Error )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
|
|
PAS_BeginTransfer( RECORD );
|
|
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_CallInt
|
|
|
|
Calls interrupt 2fh.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_CallInt( int ebx, int ecx, int edx );
|
|
#pragma aux PAS_CallInt = \
|
|
"int 2fh", \
|
|
parm [ ebx ] [ ecx ] [ edx ] modify exact [ eax ebx ecx edx esi edi ] value [ ebx ];
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_CallMVFunction
|
|
|
|
Performs a call to a real mode function.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_CallMVFunction
|
|
(
|
|
unsigned long function,
|
|
int ebx,
|
|
int ecx,
|
|
int edx
|
|
)
|
|
|
|
{
|
|
dpmi_regs callregs;
|
|
int status;
|
|
|
|
callregs.EBX = ebx;
|
|
callregs.ECX = ecx;
|
|
callregs.EDX = edx;
|
|
|
|
callregs.SS = 0;
|
|
callregs.SP = 0;
|
|
|
|
callregs.DS = 0;
|
|
callregs.ES = 0;
|
|
callregs.FS = 0;
|
|
callregs.GS = 0;
|
|
|
|
callregs.IP = function;
|
|
callregs.CS = function >> 16;
|
|
|
|
status = DPMI_CallRealModeFunction( &callregs );
|
|
if ( status != DPMI_Ok )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
|
|
return( callregs.EBX & 0xff );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SetPCMVolume
|
|
|
|
Sets the volume of digitized sound playback.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_SetPCMVolume
|
|
(
|
|
int volume
|
|
)
|
|
|
|
{
|
|
int status;
|
|
|
|
volume = max( 0, volume );
|
|
volume = min( volume, 255 );
|
|
|
|
volume *= 100;
|
|
volume /= 255;
|
|
|
|
status = PAS_CallMVFunction( PAS_Func->SetMixer, volume,
|
|
OUTPUTMIXER, L_PCM );
|
|
if ( status == PAS_Error )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
status = PAS_CallMVFunction( PAS_Func->SetMixer, volume,
|
|
OUTPUTMIXER, R_PCM );
|
|
if ( status == PAS_Error )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_GetPCMVolume
|
|
|
|
Returns the current volume of digitized sound playback.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_GetPCMVolume
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int leftvolume;
|
|
int rightvolume;
|
|
int totalvolume;
|
|
|
|
if ( PAS_Func == NULL )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
|
|
leftvolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
|
|
OUTPUTMIXER, L_PCM );
|
|
rightvolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
|
|
OUTPUTMIXER, R_PCM );
|
|
|
|
if ( ( leftvolume == PAS_Error ) || ( rightvolume == PAS_Error ) )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
|
|
leftvolume &= 0xff;
|
|
rightvolume &= 0xff;
|
|
|
|
totalvolume = ( rightvolume + leftvolume ) / 2;
|
|
totalvolume *= 255;
|
|
totalvolume /= 100;
|
|
return( totalvolume );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SetFMVolume
|
|
|
|
Sets the volume of FM sound playback.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_SetFMVolume
|
|
(
|
|
int volume
|
|
)
|
|
|
|
{
|
|
volume = max( 0, volume );
|
|
volume = min( volume, 255 );
|
|
|
|
volume *= 100;
|
|
volume /= 255;
|
|
if ( PAS_Func )
|
|
{
|
|
PAS_CallMVFunction( PAS_Func->SetMixer, volume, OUTPUTMIXER, L_FM );
|
|
PAS_CallMVFunction( PAS_Func->SetMixer, volume, OUTPUTMIXER, R_FM );
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_GetFMVolume
|
|
|
|
Returns the current volume of FM sound playback.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_GetFMVolume
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int leftvolume;
|
|
int rightvolume;
|
|
int totalvolume;
|
|
|
|
if ( PAS_Func == NULL )
|
|
{
|
|
return( 255 );
|
|
}
|
|
|
|
leftvolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
|
|
OUTPUTMIXER, L_FM ) & 0xff;
|
|
rightvolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
|
|
OUTPUTMIXER, R_FM ) & 0xff;
|
|
|
|
totalvolume = ( rightvolume + leftvolume ) / 2;
|
|
totalvolume *= 255;
|
|
totalvolume /= 100;
|
|
totalvolume = min( 255, totalvolume );
|
|
|
|
return( totalvolume );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_GetCardInfo
|
|
|
|
Returns the maximum number of bits that can represent a sample
|
|
(8 or 16) and the number of channels (1 for mono, 2 for stereo).
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_GetCardInfo
|
|
(
|
|
int *MaxSampleBits,
|
|
int *MaxChannels
|
|
)
|
|
|
|
{
|
|
int status;
|
|
|
|
if ( PAS_State == NULL )
|
|
{
|
|
status = PAS_CheckForDriver();
|
|
if ( status != PAS_Ok )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
PAS_State = PAS_GetStateTable();
|
|
if ( PAS_State == NULL )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
}
|
|
|
|
*MaxChannels = 2;
|
|
|
|
// Check board revision. Revision # 0 can't play 16-bit data.
|
|
if ( ( PAS_State->intrctlr & 0xe0 ) == 0 )
|
|
{
|
|
*MaxSampleBits = 8;
|
|
}
|
|
else
|
|
{
|
|
*MaxSampleBits = 16;
|
|
}
|
|
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SetCallBack
|
|
|
|
Specifies the user function to call at the end of a sound transfer.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_SetCallBack
|
|
(
|
|
void ( *func )( void )
|
|
)
|
|
|
|
{
|
|
PAS_CallBack = func;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_FindCard
|
|
|
|
Auto-detects the port the Pro AudioSpectrum is set for.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_FindCard
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int status;
|
|
|
|
status = PAS_TestAddress( DEFAULT_BASE );
|
|
if ( status == 0 )
|
|
{
|
|
PAS_TranslateCode = DEFAULT_BASE;
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
status = PAS_TestAddress( ALT_BASE_1 );
|
|
if ( status == 0 )
|
|
{
|
|
PAS_TranslateCode = ALT_BASE_1;
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
status = PAS_TestAddress( ALT_BASE_2 );
|
|
if ( status == 0 )
|
|
{
|
|
PAS_TranslateCode = ALT_BASE_2;
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
status = PAS_TestAddress( ALT_BASE_3 );
|
|
if ( status == 0 )
|
|
{
|
|
PAS_TranslateCode = ALT_BASE_3;
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
PAS_SetErrorCode( PAS_CardNotFound );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SaveMusicVolume
|
|
|
|
Saves the user's FM mixer settings.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_SaveMusicVolume
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int status;
|
|
int data;
|
|
|
|
if ( !PAS_Installed )
|
|
{
|
|
status = PAS_CheckForDriver();
|
|
if ( status != PAS_Ok )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
PAS_State = PAS_GetStateTable();
|
|
if ( PAS_State == NULL )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
|
|
PAS_Func = PAS_GetFunctionTable();
|
|
if ( PAS_Func == NULL )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
|
|
status = PAS_GetCardSettings();
|
|
if ( status != PAS_Ok )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
status = PAS_FindCard();
|
|
if ( status != PAS_Ok )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
// Enable PAS Sound
|
|
data = PAS_State->audiofilt;
|
|
data |= PAS_AudioMuteFlag;
|
|
|
|
PAS_Write( AudioFilterControl, data );
|
|
PAS_State->audiofilt = data;
|
|
}
|
|
|
|
status = PAS_CallMVFunction( PAS_Func->GetMixer, 0, OUTPUTMIXER, L_FM );
|
|
if ( status != PAS_Error )
|
|
{
|
|
PAS_OriginalFMLeftVolume = PAS_CallMVFunction( PAS_Func->GetMixer,
|
|
0, OUTPUTMIXER, L_FM ) & 0xff;
|
|
|
|
PAS_OriginalFMRightVolume = PAS_CallMVFunction( PAS_Func->GetMixer,
|
|
0, OUTPUTMIXER, R_FM ) & 0xff;
|
|
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
return( PAS_Warning );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_RestoreMusicVolume
|
|
|
|
Restores the user's FM mixer settings.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_RestoreMusicVolume
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
if ( PAS_Func )
|
|
{
|
|
PAS_CallMVFunction( PAS_Func->SetMixer, PAS_OriginalFMLeftVolume,
|
|
OUTPUTMIXER, L_FM );
|
|
PAS_CallMVFunction( PAS_Func->SetMixer, PAS_OriginalFMRightVolume,
|
|
OUTPUTMIXER, R_FM );
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_SaveState
|
|
|
|
Saves the original state of the PAS prior to use.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_SaveState
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
PAS_OriginalState.intrctlr = PAS_State->intrctlr;
|
|
PAS_OriginalState.audiofilt = PAS_State->audiofilt;
|
|
PAS_OriginalState.tmrctlr = PAS_State->tmrctlr;
|
|
PAS_OriginalState.samplerate = PAS_State->samplerate;
|
|
PAS_OriginalState.samplecnt = PAS_State->samplecnt;
|
|
PAS_OriginalState.crosschannel = PAS_State->crosschannel;
|
|
PAS_SampleSizeConfig = PAS_Read( SampleSizeConfiguration );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_RestoreState
|
|
|
|
Restores the original state of the PAS after use.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_RestoreState
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int LoByte;
|
|
int HiByte;
|
|
|
|
// Select the Sample Rate Timer
|
|
PAS_Write( LocalTimerControl, SelectSampleRateTimer );
|
|
PAS_State->tmrctlr = SelectSampleRateTimer;
|
|
|
|
PAS_Write( SampleRateTimer, PAS_OriginalState.samplerate );
|
|
PAS_State->samplerate = PAS_OriginalState.samplerate;
|
|
|
|
// Select the Sample Buffer Count
|
|
PAS_Write( LocalTimerControl, SelectSampleBufferCount );
|
|
PAS_State->tmrctlr = SelectSampleBufferCount;
|
|
|
|
LoByte = lobyte( PAS_OriginalState.samplecnt );
|
|
HiByte = hibyte( PAS_OriginalState.samplecnt );
|
|
PAS_Write( SampleRateTimer, LoByte );
|
|
PAS_Write( SampleRateTimer, HiByte );
|
|
PAS_State->samplecnt = PAS_OriginalState.samplecnt;
|
|
|
|
PAS_Write( CrossChannelControl, PAS_OriginalState.crosschannel );
|
|
PAS_State->crosschannel = PAS_OriginalState.crosschannel;
|
|
|
|
PAS_Write( SampleSizeConfiguration, PAS_SampleSizeConfig );
|
|
|
|
PAS_Write( InterruptControl, PAS_OriginalState.intrctlr );
|
|
PAS_State->intrctlr = PAS_OriginalState.intrctlr;
|
|
|
|
PAS_Write( AudioFilterControl, PAS_OriginalState.audiofilt );
|
|
PAS_State->audiofilt = PAS_OriginalState.audiofilt;
|
|
|
|
PAS_Write( LocalTimerControl, PAS_OriginalState.tmrctlr );
|
|
PAS_State->tmrctlr = PAS_OriginalState.tmrctlr;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_LockEnd
|
|
|
|
Used for determining the length of the functions to lock in memory.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void PAS_LockEnd
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: allocateTimerStack
|
|
|
|
Allocate a block of memory from conventional (low) memory and return
|
|
the selector (which can go directly into a segment register) of the
|
|
memory block or 0 if an error occured.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static unsigned short allocateTimerStack
|
|
(
|
|
unsigned short size
|
|
)
|
|
|
|
{
|
|
union REGS regs;
|
|
|
|
// clear all registers
|
|
memset( ®s, 0, sizeof( regs ) );
|
|
|
|
// DPMI allocate conventional memory
|
|
regs.w.ax = 0x100;
|
|
|
|
// size in paragraphs
|
|
regs.w.bx = ( size + 15 ) / 16;
|
|
|
|
int386( 0x31, ®s, ®s );
|
|
if (!regs.w.cflag)
|
|
{
|
|
// DPMI call returns selector in dx
|
|
// (ax contains real mode segment
|
|
// which is ignored here)
|
|
|
|
return( regs.w.dx );
|
|
}
|
|
|
|
// Couldn't allocate memory.
|
|
return( NULL );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: deallocateTimerStack
|
|
|
|
Deallocate a block of conventional (low) memory given a selector to
|
|
it. Assumes the block was allocated with DPMI function 0x100.
|
|
---------------------------------------------------------------------*/
|
|
|
|
static void deallocateTimerStack
|
|
(
|
|
unsigned short selector
|
|
)
|
|
|
|
{
|
|
union REGS regs;
|
|
|
|
if ( selector != NULL )
|
|
{
|
|
// clear all registers
|
|
memset( ®s, 0, sizeof( regs ) );
|
|
|
|
regs.w.ax = 0x101;
|
|
regs.w.dx = selector;
|
|
int386( 0x31, ®s, ®s );
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_Init
|
|
|
|
Initializes the sound card and prepares the module to play
|
|
digitized sounds.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_Init
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int Interrupt;
|
|
int status;
|
|
int data;
|
|
|
|
if ( PAS_Installed )
|
|
{
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
PAS_IntController1Mask = inp( 0x21 );
|
|
PAS_IntController2Mask = inp( 0xA1 );
|
|
|
|
status = PAS_CheckForDriver();
|
|
if ( status != PAS_Ok )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
PAS_State = PAS_GetStateTable();
|
|
if ( PAS_State == NULL )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
|
|
PAS_Func = PAS_GetFunctionTable();
|
|
if ( PAS_Func == NULL )
|
|
{
|
|
return( PAS_Error );
|
|
}
|
|
|
|
status = PAS_GetCardSettings();
|
|
if ( status != PAS_Ok )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
status = PAS_FindCard();
|
|
if ( status != PAS_Ok )
|
|
{
|
|
return( status );
|
|
}
|
|
|
|
PAS_SaveState();
|
|
|
|
PAS_OriginalPCMLeftVolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
|
|
OUTPUTMIXER, L_PCM ) & 0xff;
|
|
PAS_OriginalPCMRightVolume = PAS_CallMVFunction( PAS_Func->GetMixer, 0,
|
|
OUTPUTMIXER, R_PCM ) & 0xff;
|
|
|
|
PAS_SoundPlaying = FALSE;
|
|
|
|
PAS_SetCallBack( NULL );
|
|
|
|
PAS_DMABuffer = NULL;
|
|
|
|
status = PAS_LockMemory();
|
|
if ( status != PAS_Ok )
|
|
{
|
|
PAS_UnlockMemory();
|
|
return( status );
|
|
}
|
|
|
|
StackSelector = allocateTimerStack( kStackSize );
|
|
if ( StackSelector == NULL )
|
|
{
|
|
PAS_UnlockMemory();
|
|
PAS_SetErrorCode( PAS_OutOfMemory );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
// Leave a little room at top of stack just for the hell of it...
|
|
StackPointer = kStackSize - sizeof( long );
|
|
|
|
// Install our interrupt handler
|
|
Interrupt = PAS_Interrupts[ PAS_Irq ];
|
|
PAS_OldInt = _dos_getvect( Interrupt );
|
|
if ( PAS_Irq < 8 )
|
|
{
|
|
_dos_setvect( Interrupt, PAS_ServiceInterrupt );
|
|
}
|
|
else
|
|
{
|
|
status = IRQ_SetVector( Interrupt, PAS_ServiceInterrupt );
|
|
if ( status != IRQ_Ok )
|
|
{
|
|
PAS_UnlockMemory();
|
|
deallocateTimerStack( StackSelector );
|
|
StackSelector = NULL;
|
|
PAS_SetErrorCode( PAS_UnableToSetIrq );
|
|
return( PAS_Error );
|
|
}
|
|
}
|
|
|
|
// Enable PAS Sound
|
|
data = PAS_State->audiofilt;
|
|
data |= PAS_AudioMuteFlag;
|
|
|
|
PAS_Write( AudioFilterControl, data );
|
|
PAS_State->audiofilt = data;
|
|
|
|
PAS_SetPlaybackRate( PAS_DefaultSampleRate );
|
|
PAS_SetMixMode( PAS_DefaultMixMode );
|
|
|
|
PAS_Installed = TRUE;
|
|
|
|
PAS_SetErrorCode( PAS_Ok );
|
|
return( PAS_Ok );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_Shutdown
|
|
|
|
Ends transfer of sound data to the sound card and restores the
|
|
system resources used by the card.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_Shutdown
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int Interrupt;
|
|
|
|
if ( PAS_Installed )
|
|
{
|
|
// Halt the DMA transfer
|
|
PAS_StopPlayback();
|
|
|
|
// Restore the original interrupt
|
|
Interrupt = PAS_Interrupts[ PAS_Irq ];
|
|
if ( PAS_Irq >= 8 )
|
|
{
|
|
IRQ_RestoreVector( Interrupt );
|
|
}
|
|
_dos_setvect( Interrupt, PAS_OldInt );
|
|
|
|
PAS_SoundPlaying = FALSE;
|
|
|
|
PAS_DMABuffer = NULL;
|
|
|
|
PAS_SetCallBack( NULL );
|
|
|
|
PAS_CallMVFunction( PAS_Func->SetMixer, PAS_OriginalPCMLeftVolume,
|
|
OUTPUTMIXER, L_PCM );
|
|
PAS_CallMVFunction( PAS_Func->SetMixer, PAS_OriginalPCMRightVolume,
|
|
OUTPUTMIXER, R_PCM );
|
|
|
|
// DEBUG
|
|
// PAS_RestoreState();
|
|
|
|
PAS_UnlockMemory();
|
|
|
|
deallocateTimerStack( StackSelector );
|
|
StackSelector = NULL;
|
|
|
|
PAS_Installed = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_UnlockMemory
|
|
|
|
Unlocks all neccessary data.
|
|
---------------------------------------------------------------------*/
|
|
|
|
void PAS_UnlockMemory
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
DPMI_UnlockMemoryRegion( PAS_LockStart, PAS_LockEnd );
|
|
DPMI_Unlock( PAS_Interrupts );
|
|
DPMI_Unlock( PAS_OldInt );
|
|
DPMI_Unlock( PAS_IntController1Mask );
|
|
DPMI_Unlock( PAS_IntController2Mask );
|
|
DPMI_Unlock( PAS_Installed );
|
|
DPMI_Unlock( PAS_TranslateCode );
|
|
DPMI_Unlock( PAS_OriginalPCMLeftVolume );
|
|
DPMI_Unlock( PAS_OriginalPCMRightVolume );
|
|
DPMI_Unlock( PAS_OriginalFMLeftVolume );
|
|
DPMI_Unlock( PAS_OriginalFMRightVolume );
|
|
DPMI_Unlock( PAS_DMAChannel );
|
|
DPMI_Unlock( PAS_Irq );
|
|
DPMI_Unlock( PAS_State );
|
|
DPMI_Unlock( PAS_Func );
|
|
DPMI_Unlock( PAS_OriginalState );
|
|
DPMI_Unlock( PAS_SampleSizeConfig );
|
|
DPMI_Unlock( PAS_DMABuffer );
|
|
DPMI_Unlock( PAS_DMABufferEnd );
|
|
DPMI_Unlock( PAS_CurrentDMABuffer );
|
|
DPMI_Unlock( PAS_TotalDMABufferSize );
|
|
DPMI_Unlock( PAS_TransferLength );
|
|
DPMI_Unlock( PAS_MixMode );
|
|
DPMI_Unlock( PAS_SampleRate );
|
|
DPMI_Unlock( PAS_TimeInterval );
|
|
DPMI_Unlock( PAS_SoundPlaying );
|
|
DPMI_Unlock( PAS_CallBack );
|
|
DPMI_Unlock( PAS_ErrorCode );
|
|
DPMI_Unlock( irqstatus );
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------
|
|
Function: PAS_LockMemory
|
|
|
|
Locks all neccessary data.
|
|
---------------------------------------------------------------------*/
|
|
|
|
int PAS_LockMemory
|
|
(
|
|
void
|
|
)
|
|
|
|
{
|
|
int status;
|
|
|
|
status = DPMI_LockMemoryRegion( PAS_LockStart, PAS_LockEnd );
|
|
status |= DPMI_Lock( PAS_Interrupts );
|
|
status |= DPMI_Lock( PAS_OldInt );
|
|
status |= DPMI_Lock( PAS_IntController1Mask );
|
|
status |= DPMI_Lock( PAS_IntController2Mask );
|
|
status |= DPMI_Lock( PAS_Installed );
|
|
status |= DPMI_Lock( PAS_TranslateCode );
|
|
status |= DPMI_Lock( PAS_OriginalPCMLeftVolume );
|
|
status |= DPMI_Lock( PAS_OriginalPCMRightVolume );
|
|
status |= DPMI_Lock( PAS_OriginalFMLeftVolume );
|
|
status |= DPMI_Lock( PAS_OriginalFMRightVolume );
|
|
status |= DPMI_Lock( PAS_DMAChannel );
|
|
status |= DPMI_Lock( PAS_Irq );
|
|
status |= DPMI_Lock( PAS_State );
|
|
status |= DPMI_Lock( PAS_Func );
|
|
status |= DPMI_Lock( PAS_OriginalState );
|
|
status |= DPMI_Lock( PAS_SampleSizeConfig );
|
|
status |= DPMI_Lock( PAS_DMABuffer );
|
|
status |= DPMI_Lock( PAS_DMABufferEnd );
|
|
status |= DPMI_Lock( PAS_CurrentDMABuffer );
|
|
status |= DPMI_Lock( PAS_TotalDMABufferSize );
|
|
status |= DPMI_Lock( PAS_TransferLength );
|
|
status |= DPMI_Lock( PAS_MixMode );
|
|
status |= DPMI_Lock( PAS_SampleRate );
|
|
status |= DPMI_Lock( PAS_TimeInterval );
|
|
status |= DPMI_Lock( PAS_SoundPlaying );
|
|
status |= DPMI_Lock( PAS_CallBack );
|
|
status |= DPMI_Lock( PAS_ErrorCode );
|
|
status |= DPMI_Lock( irqstatus );
|
|
|
|
if ( status != DPMI_Ok )
|
|
{
|
|
PAS_UnlockMemory();
|
|
PAS_SetErrorCode( PAS_DPMI_Error );
|
|
return( PAS_Error );
|
|
}
|
|
|
|
return( PAS_Ok );
|
|
}
|