From a8f828a066773a59fdf646299359d35ae0e845af Mon Sep 17 00:00:00 2001 From: wyatt8740 Date: Mon, 6 Jan 2014 12:02:16 -0500 Subject: Removed accidental upload of copyrighted map files --- WinQuake/snd_gus.c | 2586 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1293 insertions(+), 1293 deletions(-) (limited to 'WinQuake/snd_gus.c') diff --git a/WinQuake/snd_gus.c b/WinQuake/snd_gus.c index dd1376f..7c567ca 100644 --- a/WinQuake/snd_gus.c +++ b/WinQuake/snd_gus.c @@ -1,1293 +1,1293 @@ -/* -Copyright (C) 1996-1997 Id Software, Inc. - -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. - -*/ -//============================================================================= -// Routines for GUS support in QUAKE -// -// Author(s): Jayeson Lee-Steere -//============================================================================= - -#include "quakedef.h" -#include "dosisms.h" - -//============================================================================= -// Author(s): Jayeson Lee-Steere - -#define INI_STRING_SIZE 0x100 - -FILE *ini_fopen(const char *filename, const char *modes); -int ini_fclose(FILE *f); -void ini_fgets(FILE *f, const char *section, const char *field, char *s); - -// Routines for reading from .INI files -// The read routines are fairly efficient. -// -// Author(s): Jayeson Lee-Steere - -#define MAX_SECTION_WIDTH 20 -#define MAX_FIELD_WIDTH 20 - -#define NUM_SECTION_BUFFERS 10 -#define NUM_FIELD_BUFFERS 20 - -struct section_buffer -{ - long offset; - char name[MAX_SECTION_WIDTH+1]; -}; - -struct field_buffer -{ - long offset; - int section; - char name[MAX_FIELD_WIDTH+1]; -}; - -static FILE *current_file=NULL; -static int current_section; - -static int current_section_buffer=0; -static int current_field_buffer=0; - -static struct section_buffer section_buffers[NUM_SECTION_BUFFERS]; -static struct field_buffer field_buffers[NUM_FIELD_BUFFERS]; -//*************************************************************************** -// Internal routines -//*************************************************************************** -static char toupper(char c) -{ - if (c>='a' && c<='z') - c-=('a'-'A'); - return(c); -} - -static void reset_buffer(FILE *f) -{ - int i; - - for (i=0;i0 && out[i-1]==' ') - i--; - // Null terminate the output string. - out[i]=0; -} - -// Extracts the field name from a field line -// e.g. in="sooty=life be in it" gives out="sooty" -static void get_field_name(char *out, char *in) -{ - int i=0; - - // Skip leading spaces - while (in[0]==' ') - in++; - // Copy name to output string - while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0) - { - if (iNUM_SECTION_BUFFERS) - current_section_buffer=0; - // Delete any field buffers that correspond to this section - for (i=0;iNUM_FIELD_BUFFERS) - current_field_buffer=0; - // Set buffer information - strcpy(field_buffers[current_field_buffer].name,field); - field_buffers[current_field_buffer].section=section; - field_buffers[current_field_buffer].offset=offset; -} - -// Identical to fgets except the string is trucated at the first ';', -// carriage return or line feed. -static char *stripped_fgets(char *s, int n, FILE *f) -{ - int i=0; - - if (fgets(s,n,f)==NULL) - return(NULL); - - while (s[i]!=';' && s[i]!=13 && s[i]!=10 && s[i]!=0) - i++; - s[i]=0; - - return(s); -} - -//*************************************************************************** -// Externally accessable routines -//*************************************************************************** -// Opens an .INI file. Works like fopen -FILE *ini_fopen(const char *filename, const char *modes) -{ - return(fopen(filename,modes)); -} - -// Closes a .INI file. Works like fclose -int ini_fclose(FILE *f) -{ - if (f==current_file) - reset_buffer(NULL); - return(fclose(f)); -} - -// Puts "field" from "section" from .ini file "f" into "s". -// If "section" does not exist or "field" does not exist in -// section then s=""; -void ini_fgets(FILE *f, const char *section, const char *field, char *s) -{ - int i; - long start_pos,string_start_pos; - char ts[INI_STRING_SIZE*2]; - - if (f!=current_file) - reset_buffer(f); - - // Default to "Not found" - s[0]=0; - - // See if section is in buffer - for (i=0;i>1) & 0x0001FFFF) | (Address & 0x000C0000L) ); -} - -void ClearGf1Ints(void) -{ - int i; - - SetGf18(DMA_CONTROL,0x00); - SetGf18(ADLIB_CONTROL,0x00); - SetGf18(RECORD_CONTROL,0x00); - - GetGf18(DMA_CONTROL); - GetGf18(RECORD_CONTROL); - for (i=0;i<32;i++); - GetGf18(GET_IRQV); -} - - -//============================================================================= -// Get Interwave (UltraSound PnP) configuration if any -//============================================================================= -static qboolean GUS_GetIWData(void) -{ - char *Interwave,s[INI_STRING_SIZE]; - FILE *IwFile; - int CodecBase,CodecDma,i; - - Interwave=getenv("INTERWAVE"); - if (Interwave==NULL) - return(false); - - // Open IW.INI - IwFile=ini_fopen(Interwave,"rt"); - if (IwFile==NULL) - return(false); - - // Read codec base and codec DMA - ini_fgets(IwFile,"setup 0","CodecBase",s); - sscanf(s,"%X",&CodecBase); - ini_fgets(IwFile,"setup 0","DMA2",s); - sscanf(s,"%i",&CodecDma); - - ini_fclose(IwFile); - - // Make sure numbers OK - if (CodecBase==0 || CodecDma==0) - return(false); - - CodecRegisterSelect=CodecBase; - CodecData=CodecBase+1; - CodecStatus=CodecBase+2; - DmaChannel=CodecDma; - - // Make sure there is a CODEC at the CODEC base - - // Clear any pending IRQs - dos_inportb(CodecStatus); - dos_outportb(CodecStatus,0); - - // Wait for 'INIT' bit to clear - for (i=0;i<0xFFFF;i++) - if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) - break; - if (i==0xFFFF) - return(false); - - // Get chip revision - can not be zero - dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); - if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) - return(false); - if ((dos_inportb(CodecData) & 0x0F) == 0) - return(false); - - HaveCodec=1; - Con_Printf("Sound Card is UltraSound PnP\n"); - return(true); -} - -//============================================================================= -// Get UltraSound MAX configuration if any -//============================================================================= -static qboolean GUS_GetMAXData(void) -{ - char *Ultrasnd,*Ultra16; - int i; - int GusBase,Dma1,Dma2,Irq1,Irq2; - int CodecBase,CodecDma,CodecIrq,CodecType; - BYTE MaxVal; - - Ultrasnd=getenv("ULTRASND"); - Ultra16=getenv("ULTRA16"); - if (Ultrasnd==NULL || Ultra16==NULL) - return(false); - - sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2); - sscanf(Ultra16,"%x,%i,%i,%i",&CodecBase,&CodecDma,&CodecIrq,&CodecType); - - if (CodecType==0 && CodecDma!=0) - DmaChannel=CodecDma & 0x07; - else - DmaChannel=Dma2 & 0x07; - - // Make sure there is a GUS at GUS base - dos_outportb(GusBase+0x08,0x55); - if (dos_inportb(GusBase+0x0A)!=0x55) - return(false); - dos_outportb(GusBase+0x08,0xAA); - if (dos_inportb(GusBase+0x0A)!=0xAA) - return(false); - - // Program CODEC control register - MaxVal=((CodecBase & 0xF0)>>4) | 0x40; - if (Dma1 > 3) - MaxVal|=0x10; - if (Dma2 > 3) - MaxVal|=0x20; - dos_outportb(GusBase+0x106,MaxVal); - - CodecRegisterSelect=CodecBase; - CodecData=CodecBase+1; - CodecStatus=CodecBase+2; - - // Make sure there is a CODEC at the CODEC base - - // Clear any pending IRQs - dos_inportb(CodecStatus); - dos_outportb(CodecStatus,0); - - // Wait for 'INIT' bit to clear - for (i=0;i<0xFFFF;i++) - if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) - break; - if (i==0xFFFF) - return(false); - - // Get chip revision - can not be zero - dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); - if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) - return(false); - if ((dos_inportb(CodecData) & 0x0F) == 0) - return(false); - - HaveCodec=1; - Con_Printf("Sound Card is UltraSound MAX\n"); - return(true); -} - -//============================================================================= -// Get regular UltraSound configuration if any -//============================================================================= -static qboolean GUS_GetGUSData(void) -{ - char *Ultrasnd; - int GusBase,Dma1,Dma2,Irq1,Irq2,i; - - Ultrasnd=getenv("ULTRASND"); - if (Ultrasnd==NULL) - return(false); - - sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2); - - DmaChannel=Dma1 & 0x07; - - // Make sure there is a GUS at GUS base - dos_outportb(GusBase+0x08,0x55); - if (dos_inportb(GusBase+0x0A)!=0x55) - return(false); - dos_outportb(GusBase+0x08,0xAA); - if (dos_inportb(GusBase+0x0A)!=0xAA) - return(false); - - Gf1TimerControl = GusBase+0x008; - Gf1PageRegister = GusBase+0x102; - Gf1RegisterSelect = GusBase+0x103; - Gf1DataLow = GusBase+0x104; - Gf1DataHigh = GusBase+0x105; - - // Reset the GUS - SetGf18(MASTER_RESET,0x00); - Gf1Delay(); - Gf1Delay(); - SetGf18(MASTER_RESET,0x01); - Gf1Delay(); - Gf1Delay(); - - // Set to max (32) voices - SetGf18(SET_VOICES,0xDF); - - // Clear any pending IRQ's - ClearGf1Ints(); - - // Set all registers to known values - for (i=0;i<32;i++) - { - dos_outportb(Gf1PageRegister,i); - SetGf18(SET_CONTROL,0x03); - SetGf18(SET_VOLUME_CONTROL,0x03); - Gf1Delay(); - SetGf18(SET_CONTROL,0x03); - SetGf18(SET_VOLUME_CONTROL,0x03); - SetGf116(SET_START_HIGH,0); - SetGf116(SET_START_LOW,0); - SetGf116(SET_END_HIGH,0); - SetGf116(SET_END_LOW,0); - SetGf116(SET_ACC_HIGH,0); - SetGf116(SET_ACC_LOW,0); - SetGf18(SET_VOLUME_RATE,63); - SetGf18(SET_VOLUME_START,5); - SetGf18(SET_VOLUME_END,251); - SetGf116(SET_VOLUME,5<<8); - } - - // Clear any pending IRQ's - ClearGf1Ints(); - - // Enable DAC etc. - SetGf18(MASTER_RESET,0x07); - - // Enable line output so we can hear something - dos_outportb(GusBase,0x08); - - HaveCodec=0; - Con_Printf("Sound Card is UltraSound\n"); - return(true); -} - - -//============================================================================= -// Programs the DMA controller to start DMAing in Auto-init mode -//============================================================================= -static void GUS_StartDMA(BYTE DmaChannel,short *dma_buffer,int count) -{ - int mode; - int RealAddr; - - RealAddr = ptr2real(dma_buffer); - - if (DmaChannel <= 3) - { - ModeReg = 0x0B; - DisableReg = 0x0A; - ClearReg = 0x0E; - } - else - { - ModeReg = 0xD6; - DisableReg = 0xD4; - ClearReg = 0xDC; - } - CountReg=CountRegs[DmaChannel]; - AddrReg=AddrRegs[DmaChannel]; - - dos_outportb(DisableReg, DmaChannel | 4); // disable channel - - // set mode- see "undocumented pc", p.876 - mode = (1<<6) // single-cycle - +(0<<5) // address increment - +(1<<4) // auto-init dma - +(2<<2) // read - +(DmaChannel & 0x03); // channel # - dos_outportb(ModeReg, mode); - - // set page - dos_outportb(PageRegs[DmaChannel], RealAddr >> 16); - - if (DmaChannel <= 3) - { // address is in bytes - dos_outportb(0x0C, 0); // prepare to send 16-bit value - dos_outportb(AddrReg, RealAddr & 0xff); - dos_outportb(AddrReg, (RealAddr>>8) & 0xff); - - dos_outportb(0x0C, 0); // prepare to send 16-bit value - dos_outportb(CountReg, (count-1) & 0xff); - dos_outportb(CountReg, (count-1) >> 8); - } - else - { // address is in words - dos_outportb(0xD8, 0); // prepare to send 16-bit value - dos_outportb(AddrReg, (RealAddr>>1) & 0xff); - dos_outportb(AddrReg, (RealAddr>>9) & 0xff); - - dos_outportb(0xD8, 0); // prepare to send 16-bit value - dos_outportb(CountReg, ((count>>1)-1) & 0xff); - dos_outportb(CountReg, ((count>>1)-1) >> 8); - } - - dos_outportb(ClearReg, 0); // clear write mask - dos_outportb(DisableReg, DmaChannel & ~4); -} - -//============================================================================= -// Starts the CODEC playing -//============================================================================= -static void GUS_StartCODEC(int count,BYTE FSVal) -{ - int i,j; - - // Clear any pending IRQs - dos_inportb(CodecStatus); - dos_outportb(CodecStatus,0); - - // Set mode to 2 - dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); - dos_outportb(CodecData,0xC0); - - // Stop any playback or capture which may be happening - dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); - dos_outportb(CodecData,dos_inportb(CodecData) & 0xFC); - - // Set FS - dos_outportb(CodecRegisterSelect,CODEC_FS_FORMAT | 0x40); - dos_outportb(CodecData,FSVal | 0x50); // Or in stereo and 16 bit bits - - // Wait a bit - for (i=0;i<10;i++) - dos_inportb(CodecData); - - // Routine 1 to counter CODEC bug - wait for init bit to clear and then a - // bit longer (i=min loop count, j=timeout - for (i=0,j=0;i<1000 && j<0x7FFFF;j++) - if ((dos_inportb(CodecRegisterSelect) & 0x80)==0) - i++; - - // Routine 2 to counter CODEC bug - this is from Forte's code. For me it - // does not seem to cure the problem, but is added security - // Waits till we can modify index register - for (j=0;j<0x7FFFF;j++) - { - dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); - if (dos_inportb(CodecRegisterSelect)==(CODEC_INTERFACE_CONFIG | 0x40)) - break; - } - - // Perform ACAL - dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); - dos_outportb(CodecData,0x08); - - // Clear MCE bit - this makes ACAL happen - dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); - - // Wait for ACAL to finish - for (j=0;j<0x7FFFF;j++) - { - if ((dos_inportb(CodecRegisterSelect) & 0x80) != 0) - continue; - dos_outportb(CodecRegisterSelect,CODEC_ERROR_STATUS_AND_INIT); - if ((dos_inportb(CodecData) & 0x20) == 0) - break; - } - - // Clear ACAL bit - dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); - dos_outportb(CodecData,0x00); - dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); - - // Set some other junk - dos_outportb(CodecRegisterSelect,CODEC_LOOPBACK_CONTROL); - dos_outportb(CodecData,0x00); - dos_outportb(CodecRegisterSelect,CODEC_PIN_CONTROL); - dos_outportb(CodecData,0x08); // IRQ is disabled in PIN control - - // Set count (it doesn't really matter what value we stuff in here - dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_LOWER_BASE_COUNT); - dos_outportb(CodecData,count & 0xFF); - dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_UPPER_BASE_COUNT); - dos_outportb(CodecData,count >> 8); - - // Start playback - dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); - dos_outportb(CodecData,0x01); -} - -//============================================================================= -// Starts the GF1 playing -//============================================================================= -static void GUS_StartGf1(int count,BYTE Voices) -{ - DWORD StartAddressL,EndAddressL,StartAddressR,EndAddressR; - - // Set number of voices to give us the sampling rate we want - SetGf18(SET_VOICES,0xC0 | (Voices-1)); - - // Figure out addresses - StartAddressL=ConvertTo16(0); - EndAddressL=ConvertTo16(count-2-2); - StartAddressR=ConvertTo16(2); - EndAddressR=ConvertTo16(count-2); - - // Set left voice addresses - dos_outportb(Gf1PageRegister,0); - SetGf116(SET_START_LOW,StartAddressL<<9); - SetGf116(SET_START_HIGH,StartAddressL>>7); - SetGf116(SET_ACC_LOW,StartAddressL<<9); - SetGf116(SET_ACC_HIGH,StartAddressL>>7); - SetGf116(SET_END_LOW,EndAddressL<<9); - SetGf116(SET_END_HIGH,EndAddressL>>7); - // Set balance to full left - SetGf18(SET_BALANCE,0); - // Set volume to full - SetGf116(SET_VOLUME,0xFFF0); - // Set FC to 2 (so we play every second sample) - SetGf116(SET_FREQUENCY,0x0800); - - // Set right voice addresses - dos_outportb(Gf1PageRegister,1); - SetGf116(SET_START_LOW,StartAddressR<<9); - SetGf116(SET_START_HIGH,StartAddressR>>7); - SetGf116(SET_ACC_LOW,StartAddressR<<9); - SetGf116(SET_ACC_HIGH,StartAddressR>>7); - SetGf116(SET_END_LOW,EndAddressR<<9); - SetGf116(SET_END_HIGH,EndAddressR>>7); - // Set balance to full right - SetGf18(SET_BALANCE,15); - // Set volume to full - SetGf116(SET_VOLUME,0xFFF0); - // Set FC to 2 (so we play every second sample) - SetGf116(SET_FREQUENCY,0x0800); - - // Start voices - dos_outportb(Gf1PageRegister,0); - SetGf18(SET_CONTROL,0x0C); - dos_outportb(Gf1PageRegister,1); - SetGf18(SET_CONTROL,0x0C); - Gf1Delay(); - dos_outportb(Gf1PageRegister,0); - SetGf18(SET_CONTROL,0x0C); - dos_outportb(Gf1PageRegister,1); - SetGf18(SET_CONTROL,0x0C); -} - - -//============================================================================= -// Figures out what kind of UltraSound we have, if any, and starts it playing -//============================================================================= -qboolean GUS_Init(void) -{ - int rc; - int RealAddr; - BYTE FSVal,Voices; - struct CodecRateStruct *CodecRate; - struct Gf1RateStruct *Gf1Rate; - - // See what kind of UltraSound we have, if any - if (GUS_GetIWData()==false) - if (GUS_GetMAXData()==false) - if (GUS_GetGUSData()==false) - return(false); - - shm = &sn; - - if (HaveCodec) - { - // do 11khz sampling rate unless command line parameter wants different - shm->speed = 11025; - FSVal = 0x03; - rc = COM_CheckParm("-sspeed"); - if (rc) - { - shm->speed = Q_atoi(com_argv[rc+1]); - - // Make sure rate not too high - if (shm->speed>48000) - shm->speed=48000; - - // Adjust speed to match one of the possible CODEC rates - for (CodecRate=CodecRates;CodecRate->Rate!=0;CodecRate++) - { - if (shm->speed <= CodecRate->Rate) - { - shm->speed=CodecRate->Rate; - FSVal=CodecRate->FSVal; - break; - } - } - } - - - // Always do 16 bit stereo - shm->channels = 2; - shm->samplebits = 16; - - // allocate buffer twice the size we need so we can get aligned buffer - dma_buffer = dos_getmemory(BUFFER_SIZE*2); - if (dma_buffer==NULL) - { - Con_Printf("Couldn't allocate sound dma buffer"); - return false; - } - - RealAddr = ptr2real(dma_buffer); - RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1); - dma_buffer = (short *) real2ptr(RealAddr); - - // Zero off DMA buffer - memset(dma_buffer, 0, BUFFER_SIZE); - - shm->soundalive = true; - shm->splitbuffer = false; - - shm->samplepos = 0; - shm->submission_chunk = 1; - shm->buffer = (unsigned char *) dma_buffer; - shm->samples = BUFFER_SIZE/(shm->samplebits/8); - - GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); - GUS_StartCODEC(BUFFER_SIZE,FSVal); - } - else - { - // do 19khz sampling rate unless command line parameter wants different - shm->speed = 19293; - Voices=32; - rc = COM_CheckParm("-sspeed"); - if (rc) - { - shm->speed = Q_atoi(com_argv[rc+1]); - - // Make sure rate not too high - if (shm->speed>44100) - shm->speed=44100; - - // Adjust speed to match one of the possible GF1 rates - for (Gf1Rate=Gf1Rates;Gf1Rate->Rate!=0;Gf1Rate++) - { - if (shm->speed <= Gf1Rate->Rate) - { - shm->speed=Gf1Rate->Rate; - Voices=Gf1Rate->Voices; - break; - } - } - } - - // Always do 16 bit stereo - shm->channels = 2; - shm->samplebits = 16; - - // allocate buffer twice the size we need so we can get aligned buffer - dma_buffer = dos_getmemory(BUFFER_SIZE*2); - if (dma_buffer==NULL) - { - Con_Printf("Couldn't allocate sound dma buffer"); - return false; - } - - RealAddr = ptr2real(dma_buffer); - RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1); - dma_buffer = (short *) real2ptr(RealAddr); - - // Zero off DMA buffer - memset(dma_buffer, 0, BUFFER_SIZE); - - shm->soundalive = true; - shm->splitbuffer = false; - - shm->samplepos = 0; - shm->submission_chunk = 1; - shm->buffer = (unsigned char *) dma_buffer; - shm->samples = BUFFER_SIZE/(shm->samplebits/8); - - GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); - SetGf116(SET_DMA_ADDRESS,0x0000); - if (DmaChannel<=3) - SetGf18(DMA_CONTROL,0x41); - else - SetGf18(DMA_CONTROL,0x45); - GUS_StartGf1(BUFFER_SIZE,Voices); - } - return(true); -} - -//============================================================================= -// Returns the current playback position -//============================================================================= -int GUS_GetDMAPos(void) -{ - int count; - - if (HaveCodec) - { - // clear 16-bit reg flip-flop - // load the current dma count register - if (DmaChannel < 4) - { - dos_outportb(0x0C, 0); - count = dos_inportb(CountReg); - count += dos_inportb(CountReg) << 8; - if (shm->samplebits == 16) - count /= 2; - count = shm->samples - (count+1); - } - else - { - dos_outportb(0xD8, 0); - count = dos_inportb(CountReg); - count += dos_inportb(CountReg) << 8; - if (shm->samplebits == 8) - count *= 2; - count = shm->samples - (count+1); - } - - } - else - { - // Read current position from GF1 - dos_outportb(Gf1PageRegister,0); - count=(GetGf116(GET_ACC_HIGH)<<7) & 0xFFFF; - // See which half of buffer we are in. Note that since this is 16 bit - // data we are playing, position is in 16 bit samples - if (GetGf18(DMA_CONTROL) & 0x40) - { - GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); - SetGf116(SET_DMA_ADDRESS,0x0000); - if (DmaChannel<=3) - SetGf18(DMA_CONTROL,0x41); - else - SetGf18(DMA_CONTROL,0x45); - } - } - - shm->samplepos = count & (shm->samples-1); - return(shm->samplepos); -} - -//============================================================================= -// Stops the UltraSound playback -//============================================================================= -void GUS_Shutdown (void) -{ - if (HaveCodec) - { - // Stop CODEC - dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); - dos_outportb(CodecData,0x01); - } - else - { - // Stop Voices - dos_outportb(Gf1PageRegister,0); - SetGf18(SET_CONTROL,0x03); - dos_outportb(Gf1PageRegister,1); - SetGf18(SET_CONTROL,0x03); - Gf1Delay(); - dos_outportb(Gf1PageRegister,0); - SetGf18(SET_CONTROL,0x03); - dos_outportb(Gf1PageRegister,1); - SetGf18(SET_CONTROL,0x03); - - // Stop any DMA - SetGf18(DMA_CONTROL,0x00); - GetGf18(DMA_CONTROL); - } - - dos_outportb(DisableReg, DmaChannel | 4); // disable dma channel -} +/* +Copyright (C) 1996-1997 Id Software, Inc. + +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. + +*/ +//============================================================================= +// Routines for GUS support in QUAKE +// +// Author(s): Jayeson Lee-Steere +//============================================================================= + +#include "quakedef.h" +#include "dosisms.h" + +//============================================================================= +// Author(s): Jayeson Lee-Steere + +#define INI_STRING_SIZE 0x100 + +FILE *ini_fopen(const char *filename, const char *modes); +int ini_fclose(FILE *f); +void ini_fgets(FILE *f, const char *section, const char *field, char *s); + +// Routines for reading from .INI files +// The read routines are fairly efficient. +// +// Author(s): Jayeson Lee-Steere + +#define MAX_SECTION_WIDTH 20 +#define MAX_FIELD_WIDTH 20 + +#define NUM_SECTION_BUFFERS 10 +#define NUM_FIELD_BUFFERS 20 + +struct section_buffer +{ + long offset; + char name[MAX_SECTION_WIDTH+1]; +}; + +struct field_buffer +{ + long offset; + int section; + char name[MAX_FIELD_WIDTH+1]; +}; + +static FILE *current_file=NULL; +static int current_section; + +static int current_section_buffer=0; +static int current_field_buffer=0; + +static struct section_buffer section_buffers[NUM_SECTION_BUFFERS]; +static struct field_buffer field_buffers[NUM_FIELD_BUFFERS]; +//*************************************************************************** +// Internal routines +//*************************************************************************** +static char toupper(char c) +{ + if (c>='a' && c<='z') + c-=('a'-'A'); + return(c); +} + +static void reset_buffer(FILE *f) +{ + int i; + + for (i=0;i0 && out[i-1]==' ') + i--; + // Null terminate the output string. + out[i]=0; +} + +// Extracts the field name from a field line +// e.g. in="sooty=life be in it" gives out="sooty" +static void get_field_name(char *out, char *in) +{ + int i=0; + + // Skip leading spaces + while (in[0]==' ') + in++; + // Copy name to output string + while (in[0]!='=' && in[0]!=13 && in[0]!=10 && in[0]!=0) + { + if (iNUM_SECTION_BUFFERS) + current_section_buffer=0; + // Delete any field buffers that correspond to this section + for (i=0;iNUM_FIELD_BUFFERS) + current_field_buffer=0; + // Set buffer information + strcpy(field_buffers[current_field_buffer].name,field); + field_buffers[current_field_buffer].section=section; + field_buffers[current_field_buffer].offset=offset; +} + +// Identical to fgets except the string is trucated at the first ';', +// carriage return or line feed. +static char *stripped_fgets(char *s, int n, FILE *f) +{ + int i=0; + + if (fgets(s,n,f)==NULL) + return(NULL); + + while (s[i]!=';' && s[i]!=13 && s[i]!=10 && s[i]!=0) + i++; + s[i]=0; + + return(s); +} + +//*************************************************************************** +// Externally accessable routines +//*************************************************************************** +// Opens an .INI file. Works like fopen +FILE *ini_fopen(const char *filename, const char *modes) +{ + return(fopen(filename,modes)); +} + +// Closes a .INI file. Works like fclose +int ini_fclose(FILE *f) +{ + if (f==current_file) + reset_buffer(NULL); + return(fclose(f)); +} + +// Puts "field" from "section" from .ini file "f" into "s". +// If "section" does not exist or "field" does not exist in +// section then s=""; +void ini_fgets(FILE *f, const char *section, const char *field, char *s) +{ + int i; + long start_pos,string_start_pos; + char ts[INI_STRING_SIZE*2]; + + if (f!=current_file) + reset_buffer(f); + + // Default to "Not found" + s[0]=0; + + // See if section is in buffer + for (i=0;i>1) & 0x0001FFFF) | (Address & 0x000C0000L) ); +} + +void ClearGf1Ints(void) +{ + int i; + + SetGf18(DMA_CONTROL,0x00); + SetGf18(ADLIB_CONTROL,0x00); + SetGf18(RECORD_CONTROL,0x00); + + GetGf18(DMA_CONTROL); + GetGf18(RECORD_CONTROL); + for (i=0;i<32;i++); + GetGf18(GET_IRQV); +} + + +//============================================================================= +// Get Interwave (UltraSound PnP) configuration if any +//============================================================================= +static qboolean GUS_GetIWData(void) +{ + char *Interwave,s[INI_STRING_SIZE]; + FILE *IwFile; + int CodecBase,CodecDma,i; + + Interwave=getenv("INTERWAVE"); + if (Interwave==NULL) + return(false); + + // Open IW.INI + IwFile=ini_fopen(Interwave,"rt"); + if (IwFile==NULL) + return(false); + + // Read codec base and codec DMA + ini_fgets(IwFile,"setup 0","CodecBase",s); + sscanf(s,"%X",&CodecBase); + ini_fgets(IwFile,"setup 0","DMA2",s); + sscanf(s,"%i",&CodecDma); + + ini_fclose(IwFile); + + // Make sure numbers OK + if (CodecBase==0 || CodecDma==0) + return(false); + + CodecRegisterSelect=CodecBase; + CodecData=CodecBase+1; + CodecStatus=CodecBase+2; + DmaChannel=CodecDma; + + // Make sure there is a CODEC at the CODEC base + + // Clear any pending IRQs + dos_inportb(CodecStatus); + dos_outportb(CodecStatus,0); + + // Wait for 'INIT' bit to clear + for (i=0;i<0xFFFF;i++) + if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) + break; + if (i==0xFFFF) + return(false); + + // Get chip revision - can not be zero + dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); + if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) + return(false); + if ((dos_inportb(CodecData) & 0x0F) == 0) + return(false); + + HaveCodec=1; + Con_Printf("Sound Card is UltraSound PnP\n"); + return(true); +} + +//============================================================================= +// Get UltraSound MAX configuration if any +//============================================================================= +static qboolean GUS_GetMAXData(void) +{ + char *Ultrasnd,*Ultra16; + int i; + int GusBase,Dma1,Dma2,Irq1,Irq2; + int CodecBase,CodecDma,CodecIrq,CodecType; + BYTE MaxVal; + + Ultrasnd=getenv("ULTRASND"); + Ultra16=getenv("ULTRA16"); + if (Ultrasnd==NULL || Ultra16==NULL) + return(false); + + sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2); + sscanf(Ultra16,"%x,%i,%i,%i",&CodecBase,&CodecDma,&CodecIrq,&CodecType); + + if (CodecType==0 && CodecDma!=0) + DmaChannel=CodecDma & 0x07; + else + DmaChannel=Dma2 & 0x07; + + // Make sure there is a GUS at GUS base + dos_outportb(GusBase+0x08,0x55); + if (dos_inportb(GusBase+0x0A)!=0x55) + return(false); + dos_outportb(GusBase+0x08,0xAA); + if (dos_inportb(GusBase+0x0A)!=0xAA) + return(false); + + // Program CODEC control register + MaxVal=((CodecBase & 0xF0)>>4) | 0x40; + if (Dma1 > 3) + MaxVal|=0x10; + if (Dma2 > 3) + MaxVal|=0x20; + dos_outportb(GusBase+0x106,MaxVal); + + CodecRegisterSelect=CodecBase; + CodecData=CodecBase+1; + CodecStatus=CodecBase+2; + + // Make sure there is a CODEC at the CODEC base + + // Clear any pending IRQs + dos_inportb(CodecStatus); + dos_outportb(CodecStatus,0); + + // Wait for 'INIT' bit to clear + for (i=0;i<0xFFFF;i++) + if ((dos_inportb(CodecRegisterSelect) & 0x80) == 0) + break; + if (i==0xFFFF) + return(false); + + // Get chip revision - can not be zero + dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); + if ((dos_inportb(CodecRegisterSelect) & 0x7F) != CODEC_MODE_AND_ID) + return(false); + if ((dos_inportb(CodecData) & 0x0F) == 0) + return(false); + + HaveCodec=1; + Con_Printf("Sound Card is UltraSound MAX\n"); + return(true); +} + +//============================================================================= +// Get regular UltraSound configuration if any +//============================================================================= +static qboolean GUS_GetGUSData(void) +{ + char *Ultrasnd; + int GusBase,Dma1,Dma2,Irq1,Irq2,i; + + Ultrasnd=getenv("ULTRASND"); + if (Ultrasnd==NULL) + return(false); + + sscanf(Ultrasnd,"%x,%i,%i,%i,%i",&GusBase,&Dma1,&Dma2,&Irq1,&Irq2); + + DmaChannel=Dma1 & 0x07; + + // Make sure there is a GUS at GUS base + dos_outportb(GusBase+0x08,0x55); + if (dos_inportb(GusBase+0x0A)!=0x55) + return(false); + dos_outportb(GusBase+0x08,0xAA); + if (dos_inportb(GusBase+0x0A)!=0xAA) + return(false); + + Gf1TimerControl = GusBase+0x008; + Gf1PageRegister = GusBase+0x102; + Gf1RegisterSelect = GusBase+0x103; + Gf1DataLow = GusBase+0x104; + Gf1DataHigh = GusBase+0x105; + + // Reset the GUS + SetGf18(MASTER_RESET,0x00); + Gf1Delay(); + Gf1Delay(); + SetGf18(MASTER_RESET,0x01); + Gf1Delay(); + Gf1Delay(); + + // Set to max (32) voices + SetGf18(SET_VOICES,0xDF); + + // Clear any pending IRQ's + ClearGf1Ints(); + + // Set all registers to known values + for (i=0;i<32;i++) + { + dos_outportb(Gf1PageRegister,i); + SetGf18(SET_CONTROL,0x03); + SetGf18(SET_VOLUME_CONTROL,0x03); + Gf1Delay(); + SetGf18(SET_CONTROL,0x03); + SetGf18(SET_VOLUME_CONTROL,0x03); + SetGf116(SET_START_HIGH,0); + SetGf116(SET_START_LOW,0); + SetGf116(SET_END_HIGH,0); + SetGf116(SET_END_LOW,0); + SetGf116(SET_ACC_HIGH,0); + SetGf116(SET_ACC_LOW,0); + SetGf18(SET_VOLUME_RATE,63); + SetGf18(SET_VOLUME_START,5); + SetGf18(SET_VOLUME_END,251); + SetGf116(SET_VOLUME,5<<8); + } + + // Clear any pending IRQ's + ClearGf1Ints(); + + // Enable DAC etc. + SetGf18(MASTER_RESET,0x07); + + // Enable line output so we can hear something + dos_outportb(GusBase,0x08); + + HaveCodec=0; + Con_Printf("Sound Card is UltraSound\n"); + return(true); +} + + +//============================================================================= +// Programs the DMA controller to start DMAing in Auto-init mode +//============================================================================= +static void GUS_StartDMA(BYTE DmaChannel,short *dma_buffer,int count) +{ + int mode; + int RealAddr; + + RealAddr = ptr2real(dma_buffer); + + if (DmaChannel <= 3) + { + ModeReg = 0x0B; + DisableReg = 0x0A; + ClearReg = 0x0E; + } + else + { + ModeReg = 0xD6; + DisableReg = 0xD4; + ClearReg = 0xDC; + } + CountReg=CountRegs[DmaChannel]; + AddrReg=AddrRegs[DmaChannel]; + + dos_outportb(DisableReg, DmaChannel | 4); // disable channel + + // set mode- see "undocumented pc", p.876 + mode = (1<<6) // single-cycle + +(0<<5) // address increment + +(1<<4) // auto-init dma + +(2<<2) // read + +(DmaChannel & 0x03); // channel # + dos_outportb(ModeReg, mode); + + // set page + dos_outportb(PageRegs[DmaChannel], RealAddr >> 16); + + if (DmaChannel <= 3) + { // address is in bytes + dos_outportb(0x0C, 0); // prepare to send 16-bit value + dos_outportb(AddrReg, RealAddr & 0xff); + dos_outportb(AddrReg, (RealAddr>>8) & 0xff); + + dos_outportb(0x0C, 0); // prepare to send 16-bit value + dos_outportb(CountReg, (count-1) & 0xff); + dos_outportb(CountReg, (count-1) >> 8); + } + else + { // address is in words + dos_outportb(0xD8, 0); // prepare to send 16-bit value + dos_outportb(AddrReg, (RealAddr>>1) & 0xff); + dos_outportb(AddrReg, (RealAddr>>9) & 0xff); + + dos_outportb(0xD8, 0); // prepare to send 16-bit value + dos_outportb(CountReg, ((count>>1)-1) & 0xff); + dos_outportb(CountReg, ((count>>1)-1) >> 8); + } + + dos_outportb(ClearReg, 0); // clear write mask + dos_outportb(DisableReg, DmaChannel & ~4); +} + +//============================================================================= +// Starts the CODEC playing +//============================================================================= +static void GUS_StartCODEC(int count,BYTE FSVal) +{ + int i,j; + + // Clear any pending IRQs + dos_inportb(CodecStatus); + dos_outportb(CodecStatus,0); + + // Set mode to 2 + dos_outportb(CodecRegisterSelect,CODEC_MODE_AND_ID); + dos_outportb(CodecData,0xC0); + + // Stop any playback or capture which may be happening + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + dos_outportb(CodecData,dos_inportb(CodecData) & 0xFC); + + // Set FS + dos_outportb(CodecRegisterSelect,CODEC_FS_FORMAT | 0x40); + dos_outportb(CodecData,FSVal | 0x50); // Or in stereo and 16 bit bits + + // Wait a bit + for (i=0;i<10;i++) + dos_inportb(CodecData); + + // Routine 1 to counter CODEC bug - wait for init bit to clear and then a + // bit longer (i=min loop count, j=timeout + for (i=0,j=0;i<1000 && j<0x7FFFF;j++) + if ((dos_inportb(CodecRegisterSelect) & 0x80)==0) + i++; + + // Routine 2 to counter CODEC bug - this is from Forte's code. For me it + // does not seem to cure the problem, but is added security + // Waits till we can modify index register + for (j=0;j<0x7FFFF;j++) + { + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); + if (dos_inportb(CodecRegisterSelect)==(CODEC_INTERFACE_CONFIG | 0x40)) + break; + } + + // Perform ACAL + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); + dos_outportb(CodecData,0x08); + + // Clear MCE bit - this makes ACAL happen + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + + // Wait for ACAL to finish + for (j=0;j<0x7FFFF;j++) + { + if ((dos_inportb(CodecRegisterSelect) & 0x80) != 0) + continue; + dos_outportb(CodecRegisterSelect,CODEC_ERROR_STATUS_AND_INIT); + if ((dos_inportb(CodecData) & 0x20) == 0) + break; + } + + // Clear ACAL bit + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG | 0x40); + dos_outportb(CodecData,0x00); + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + + // Set some other junk + dos_outportb(CodecRegisterSelect,CODEC_LOOPBACK_CONTROL); + dos_outportb(CodecData,0x00); + dos_outportb(CodecRegisterSelect,CODEC_PIN_CONTROL); + dos_outportb(CodecData,0x08); // IRQ is disabled in PIN control + + // Set count (it doesn't really matter what value we stuff in here + dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_LOWER_BASE_COUNT); + dos_outportb(CodecData,count & 0xFF); + dos_outportb(CodecRegisterSelect,CODEC_PLAYBACK_UPPER_BASE_COUNT); + dos_outportb(CodecData,count >> 8); + + // Start playback + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + dos_outportb(CodecData,0x01); +} + +//============================================================================= +// Starts the GF1 playing +//============================================================================= +static void GUS_StartGf1(int count,BYTE Voices) +{ + DWORD StartAddressL,EndAddressL,StartAddressR,EndAddressR; + + // Set number of voices to give us the sampling rate we want + SetGf18(SET_VOICES,0xC0 | (Voices-1)); + + // Figure out addresses + StartAddressL=ConvertTo16(0); + EndAddressL=ConvertTo16(count-2-2); + StartAddressR=ConvertTo16(2); + EndAddressR=ConvertTo16(count-2); + + // Set left voice addresses + dos_outportb(Gf1PageRegister,0); + SetGf116(SET_START_LOW,StartAddressL<<9); + SetGf116(SET_START_HIGH,StartAddressL>>7); + SetGf116(SET_ACC_LOW,StartAddressL<<9); + SetGf116(SET_ACC_HIGH,StartAddressL>>7); + SetGf116(SET_END_LOW,EndAddressL<<9); + SetGf116(SET_END_HIGH,EndAddressL>>7); + // Set balance to full left + SetGf18(SET_BALANCE,0); + // Set volume to full + SetGf116(SET_VOLUME,0xFFF0); + // Set FC to 2 (so we play every second sample) + SetGf116(SET_FREQUENCY,0x0800); + + // Set right voice addresses + dos_outportb(Gf1PageRegister,1); + SetGf116(SET_START_LOW,StartAddressR<<9); + SetGf116(SET_START_HIGH,StartAddressR>>7); + SetGf116(SET_ACC_LOW,StartAddressR<<9); + SetGf116(SET_ACC_HIGH,StartAddressR>>7); + SetGf116(SET_END_LOW,EndAddressR<<9); + SetGf116(SET_END_HIGH,EndAddressR>>7); + // Set balance to full right + SetGf18(SET_BALANCE,15); + // Set volume to full + SetGf116(SET_VOLUME,0xFFF0); + // Set FC to 2 (so we play every second sample) + SetGf116(SET_FREQUENCY,0x0800); + + // Start voices + dos_outportb(Gf1PageRegister,0); + SetGf18(SET_CONTROL,0x0C); + dos_outportb(Gf1PageRegister,1); + SetGf18(SET_CONTROL,0x0C); + Gf1Delay(); + dos_outportb(Gf1PageRegister,0); + SetGf18(SET_CONTROL,0x0C); + dos_outportb(Gf1PageRegister,1); + SetGf18(SET_CONTROL,0x0C); +} + + +//============================================================================= +// Figures out what kind of UltraSound we have, if any, and starts it playing +//============================================================================= +qboolean GUS_Init(void) +{ + int rc; + int RealAddr; + BYTE FSVal,Voices; + struct CodecRateStruct *CodecRate; + struct Gf1RateStruct *Gf1Rate; + + // See what kind of UltraSound we have, if any + if (GUS_GetIWData()==false) + if (GUS_GetMAXData()==false) + if (GUS_GetGUSData()==false) + return(false); + + shm = &sn; + + if (HaveCodec) + { + // do 11khz sampling rate unless command line parameter wants different + shm->speed = 11025; + FSVal = 0x03; + rc = COM_CheckParm("-sspeed"); + if (rc) + { + shm->speed = Q_atoi(com_argv[rc+1]); + + // Make sure rate not too high + if (shm->speed>48000) + shm->speed=48000; + + // Adjust speed to match one of the possible CODEC rates + for (CodecRate=CodecRates;CodecRate->Rate!=0;CodecRate++) + { + if (shm->speed <= CodecRate->Rate) + { + shm->speed=CodecRate->Rate; + FSVal=CodecRate->FSVal; + break; + } + } + } + + + // Always do 16 bit stereo + shm->channels = 2; + shm->samplebits = 16; + + // allocate buffer twice the size we need so we can get aligned buffer + dma_buffer = dos_getmemory(BUFFER_SIZE*2); + if (dma_buffer==NULL) + { + Con_Printf("Couldn't allocate sound dma buffer"); + return false; + } + + RealAddr = ptr2real(dma_buffer); + RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1); + dma_buffer = (short *) real2ptr(RealAddr); + + // Zero off DMA buffer + memset(dma_buffer, 0, BUFFER_SIZE); + + shm->soundalive = true; + shm->splitbuffer = false; + + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) dma_buffer; + shm->samples = BUFFER_SIZE/(shm->samplebits/8); + + GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); + GUS_StartCODEC(BUFFER_SIZE,FSVal); + } + else + { + // do 19khz sampling rate unless command line parameter wants different + shm->speed = 19293; + Voices=32; + rc = COM_CheckParm("-sspeed"); + if (rc) + { + shm->speed = Q_atoi(com_argv[rc+1]); + + // Make sure rate not too high + if (shm->speed>44100) + shm->speed=44100; + + // Adjust speed to match one of the possible GF1 rates + for (Gf1Rate=Gf1Rates;Gf1Rate->Rate!=0;Gf1Rate++) + { + if (shm->speed <= Gf1Rate->Rate) + { + shm->speed=Gf1Rate->Rate; + Voices=Gf1Rate->Voices; + break; + } + } + } + + // Always do 16 bit stereo + shm->channels = 2; + shm->samplebits = 16; + + // allocate buffer twice the size we need so we can get aligned buffer + dma_buffer = dos_getmemory(BUFFER_SIZE*2); + if (dma_buffer==NULL) + { + Con_Printf("Couldn't allocate sound dma buffer"); + return false; + } + + RealAddr = ptr2real(dma_buffer); + RealAddr = (RealAddr + BUFFER_SIZE) & ~(BUFFER_SIZE-1); + dma_buffer = (short *) real2ptr(RealAddr); + + // Zero off DMA buffer + memset(dma_buffer, 0, BUFFER_SIZE); + + shm->soundalive = true; + shm->splitbuffer = false; + + shm->samplepos = 0; + shm->submission_chunk = 1; + shm->buffer = (unsigned char *) dma_buffer; + shm->samples = BUFFER_SIZE/(shm->samplebits/8); + + GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); + SetGf116(SET_DMA_ADDRESS,0x0000); + if (DmaChannel<=3) + SetGf18(DMA_CONTROL,0x41); + else + SetGf18(DMA_CONTROL,0x45); + GUS_StartGf1(BUFFER_SIZE,Voices); + } + return(true); +} + +//============================================================================= +// Returns the current playback position +//============================================================================= +int GUS_GetDMAPos(void) +{ + int count; + + if (HaveCodec) + { + // clear 16-bit reg flip-flop + // load the current dma count register + if (DmaChannel < 4) + { + dos_outportb(0x0C, 0); + count = dos_inportb(CountReg); + count += dos_inportb(CountReg) << 8; + if (shm->samplebits == 16) + count /= 2; + count = shm->samples - (count+1); + } + else + { + dos_outportb(0xD8, 0); + count = dos_inportb(CountReg); + count += dos_inportb(CountReg) << 8; + if (shm->samplebits == 8) + count *= 2; + count = shm->samples - (count+1); + } + + } + else + { + // Read current position from GF1 + dos_outportb(Gf1PageRegister,0); + count=(GetGf116(GET_ACC_HIGH)<<7) & 0xFFFF; + // See which half of buffer we are in. Note that since this is 16 bit + // data we are playing, position is in 16 bit samples + if (GetGf18(DMA_CONTROL) & 0x40) + { + GUS_StartDMA(DmaChannel,dma_buffer,BUFFER_SIZE); + SetGf116(SET_DMA_ADDRESS,0x0000); + if (DmaChannel<=3) + SetGf18(DMA_CONTROL,0x41); + else + SetGf18(DMA_CONTROL,0x45); + } + } + + shm->samplepos = count & (shm->samples-1); + return(shm->samplepos); +} + +//============================================================================= +// Stops the UltraSound playback +//============================================================================= +void GUS_Shutdown (void) +{ + if (HaveCodec) + { + // Stop CODEC + dos_outportb(CodecRegisterSelect,CODEC_INTERFACE_CONFIG); + dos_outportb(CodecData,0x01); + } + else + { + // Stop Voices + dos_outportb(Gf1PageRegister,0); + SetGf18(SET_CONTROL,0x03); + dos_outportb(Gf1PageRegister,1); + SetGf18(SET_CONTROL,0x03); + Gf1Delay(); + dos_outportb(Gf1PageRegister,0); + SetGf18(SET_CONTROL,0x03); + dos_outportb(Gf1PageRegister,1); + SetGf18(SET_CONTROL,0x03); + + // Stop any DMA + SetGf18(DMA_CONTROL,0x00); + GetGf18(DMA_CONTROL); + } + + dos_outportb(DisableReg, DmaChannel | 4); // disable dma channel +} -- cgit v1.2.3