| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246 |
- /*
- ** Command & Conquer Red Alert(tm)
- ** Copyright 2025 Electronic Arts 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 3 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, see <http://www.gnu.org/licenses/>.
- */
- /****************************************************************************
- *
- * C O N F I D E N T I A L -- W E S T W O O D S T U D I O S
- *
- *----------------------------------------------------------------------------
- *
- * PROJECT
- * VQA player library. (32-Bit protected mode)
- *
- * FILE
- * audio.c
- *
- * DESCRIPTION
- * Audio playback and timing.
- *
- * PROGRAMMER
- * Bill Randolph
- * Denzil E. Long, Jr.
- *
- * DATE
- * June 22, 1995
- *
- *----------------------------------------------------------------------------
- *
- * PUBLIC
- * VQA_StartTimerInt - Initialize system timer interrupt.
- * VQA_StopTimerInt - Remove system timer interrupt.
- * VQA_SetTimer - Resets current time to given tick value.
- * VQA_GetTime - Return current time.
- * VQA_TimerMethod - Get timer method being used.
- * VQA_OpenAudio - Open sound system.
- * VQA_CloseAudio - Close sound system
- * VQA_StartAudio - Starts audio playback
- * VQA_StopAudio - Stop audio playback.
- * CopyAudio - Copy data from Audio Temp buf into Audio play buf.
- *
- * PRIVATE
- * TimerCallback - Timer callback routine; called by HMI
- * AutoDetect - Auto detect the sound card.
- * AudioCallback - Sound system callback.
- *
- ****************************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <malloc.h>
- #include <mem.h>
- #include <sys\timeb.h>
- #include "vqaplayp.h"
- #include <vqm32\all.h>
- #ifdef __WATCOMC__
- #pragma pack(4);
- #endif
- #if(VQAAUDIO_ON)
- #include "sos.h"
- #endif
- /*---------------------------------------------------------------------------
- * AUDIO DEFINITIONS
- *-------------------------------------------------------------------------*/
- #define HMI_SAMPLE 0x1000
- #define HMI_DEFAULT_RATE 22050
- #define HMI_UNINIT 0 /* Unitialize state. */
- #define HMI_VQAINIT 1 /* VQA initialized */
- #define HMI_APPINIT 2 /* Application initialized */
- #if(VQAAUDIO_ON)
- /*---------------------------------------------------------------------------
- * PROTOTYPES
- *-------------------------------------------------------------------------*/
- long AutoDetect(long bitsize, long channels);
- void far TimerCallback(void);
- void far cdecl AudioCallback(WORD wDriverHandle, WORD wAction,
- WORD wSampleID);
- /*---------------------------------------------------------------------------
- * GLOBAL DATA
- *-------------------------------------------------------------------------*/
- /* HMI Start Sample structure:
- *
- * lpSamplePtr - Pointer to audio buffer.
- * dwSampleSize - Size of buffer.
- * wLoopCount - Number of time to loop (-1 = infinate)
- * wChannel - Channel to play sample on.
- * wVolume - Volume level to play at.
- * wSampleID - ID for sample
- * lpCallback - Callback function pointer.
- * wSamplePort - Port to use for non-DMA
- * wSampleFlags - Flags
- * dwSampleByteLength - Total length including loops
- * dwSampleLoopPoint -
- * dwSampleLoopLength -
- * dwSamplePitchAdd - Pitch shifting component.
- * wSamplePitchFraction - Pitch shifting component.
- * wSamplePanLocation - For panning
- * wSamplePanSpeed - For panning
- * wSamplePanDirection - For panning
- * wSamplePanStart - For panning.
- * wSamplePanEnd - For panning.
- * wSampleDelayBytes - Delay part.
- * wSampleDelayRepeat - Delay part.
- * dwSampleADPCMPredicted - For compression
- * wSampleADPCMIndex - For compression
- * wSampleRootNoteMIDI - Root note for pitch shift
- * dwFiller[3] - Filler for future upgrades.
- */
- _SOS_START_SAMPLE sSOSSampleData = {
- _NULL,
- 0L,
- 0,
- _CENTER_CHANNEL,
- 0x7FFF,
- HMI_SAMPLE,
- AudioCallback,
- 0,
- 0,
- 0L,
- 0L,0L,
- 0L,0,
- 0,0,0,0,0,
- 0,0,
- 0L,0,
- 0,
- 0L,0L,0L
- };
- /* HMI Driver structure: Driver initialization structure.
- *
- * wBufferSize - Size of play buffer.
- * lpBuffer - Pointer to buffer.
- * wAllocateBuffer - Allocate buffer flag.
- * wSampleRate - Playback rate.
- * wParam -
- * dwParam -
- * lpFillHandler -
- * lpDriverMemory -
- * lpTimerMemory -
- * wTimerID -
- */
- _SOS_INIT_DRIVER sSOSInitDriver = {
- 0,
- _NULL,
- _TRUE,
- HMI_DEFAULT_RATE,
- 19,
- 0L,
- _NULL,
- _NULL,
- _NULL,
- _SOS_NORMAL_TIMER
- };
- static _SOS_CAPABILITIES DigiCaps;
- static _SOS_HARDWARE DigiHardware;
- static WORD DigiHandle = -1;
- static WORD DigiTimer = 0;
- static WORD SampleHandle;
- static long DigiInitFlag = HMI_UNINIT;
- static long PlayingFlag = 0;
- static VQAHandleP *VQAP;
- static unsigned long CurBlock = 0;
- static unsigned long NextBlock;
- static WORD VQATimer = 0;
- static long TimerMethod;
- static long TimerInitFlag = HMI_UNINIT;
- static long HMITimerFlag = HMI_UNINIT;
- static long VQATickCount = 0;
- #endif /* VQAAUDIO_ON */
- static long TickOffset = 0;
- char *HMIDevName = "<none>";
- #if(VQAAUDIO_ON)
- /****************************************************************************
- *
- * NAME
- * VQA_StartTimerInt - Initialize system timer interrupt.
- *
- * SYNOPSIS
- * Error = VQA_StartTimerInt(Init)
- *
- * long VQA_StartTimerInt(long);
- *
- * FUNCTION
- * Initialize the HMI timer system if it hasn't been already then add
- * our own timer event.
- *
- * INPUTS
- * Init - Initialize HMI timer system flag. (TRUE = Initialize)
- *
- * RESULT
- * Error - 0 if successful, -1 if error.
- *
- ****************************************************************************/
- long VQA_StartTimerInt(long init)
- {
- /* Does the caller want me to initialize the timer system? */
- if (init == 0) {
- /* If the timer system is uninitialized then it is the players
- * responsibility to do it.
- */
- if (TimerInitFlag == HMI_UNINIT) {
- sosTIMERInitSystem(_TIMER_DOS_RATE, _SOS_DEBUG_NORMAL);
- TimerInitFlag = HMI_VQAINIT;
- }
- } else {
- TimerInitFlag = HMI_APPINIT;
- }
- /* Register the VQA_TickCount timer event. */
- if (HMITimerFlag == HMI_UNINIT) {
- if (sosTIMERRegisterEvent(VQA_TIMETICKS,TimerCallback,&VQATimer) == 0) {
- HMITimerFlag = HMI_VQAINIT;
- } else {
- return (-1);
- }
- }
- return (0);
- }
- /****************************************************************************
- *
- * NAME
- * VQA_StopTimerInt - Remove system timer interrupt.
- *
- * SYNOPSIS
- * VQA_StopTimerInt()
- *
- * void VQA_StopTimerInt(void);
- *
- * FUNCTION
- * Remove our timer event from the HMI timer system. Uninitialize the
- * HMI timer system if we initialized it.
- *
- * INPUTS
- * NONE
- *
- * RESULT
- * NONE
- *
- ****************************************************************************/
- void VQA_StopTimerInt(void)
- {
- /* Remove the timer event */
- if (HMITimerFlag == HMI_VQAINIT) {
- sosTIMERRemoveEvent(VQATimer);
- }
- HMITimerFlag = HMI_UNINIT;
- if (TimerInitFlag == HMI_VQAINIT) {
- sosTIMERUnInitSystem(0);
- }
- TimerInitFlag = HMI_UNINIT;
- }
- /****************************************************************************
- *
- * NAME
- * TimerCallback - VQA timer event. (Called by HMI)
- *
- * SYNOPSIS
- * TimerCallback()
- *
- * void TimerCallback(void);
- *
- * FUNCTION
- * Our custom timer event. This is the timer event that we register with
- * HMI for system timing.
- *
- * INPUTS
- * NONE
- *
- * RESULT
- * NONE
- *
- ****************************************************************************/
- void far TimerCallback(void)
- {
- VQATickCount++;
- }
- #endif /* VQAAUDIO_ON */
- /****************************************************************************
- *
- * NAME
- * VQA_SetTimer - Resets current time to given tick value.
- *
- * SYNOPSIS
- * VQA_SetTimer(Time, Method)
- *
- * void VQA_SetTimer(long, long);
- *
- * FUNCTION
- * Sets 'TickOffset' to a value that will make the current time look like
- * the time passed in. This function allows the player to be "paused",
- * by recording the time of the pause, and then setting the timer to
- * that time. The timer method used by the player is also set. The method
- * selected is not neccesarily the method that will be used because some
- * timer methods work with only certain playback conditions. (EX: The
- * audio DMA timer method cannot be used if there is not any audio
- * playing.)
- *
- * INPUTS
- * Time - Value to set current time to.
- * Method - Timer method to use.
- *
- * RESULT
- * NONE
- *
- ****************************************************************************/
- void VQA_SetTimer(long time, long method)
- {
- unsigned long curtime;
- #if(VQAAUDIO_ON)
- /* If the client does not have a preferencee then pick a method
- * based on the state of the player.
- */
- if (method == VQA_TMETHOD_DEFAULT) {
- /* If we are playing audio, use the audio DMA position. */
- if (PlayingFlag) {
- method = VQA_TMETHOD_AUDIO;
- }
- /* Otherwise use the HMI timer if it is initialized. */
- else if (HMITimerFlag) {
- method = VQA_TMETHOD_INT;
- }
- /* If all else fails resort the the "jerky" DOS time. */
- else {
- method = VQA_TMETHOD_DOS;
- }
- } else {
- /* We cannot use the DMA position if there isn't any audio playing. */
- if (!PlayingFlag && (method == VQA_TMETHOD_AUDIO)) {
- method = VQA_TMETHOD_INT;
- }
- /* We cannot use the timer if it has not been initialized. */
- if (!HMITimerFlag && (method == VQA_TMETHOD_INT)) {
- method = VQA_TMETHOD_DOS;
- }
- }
- TimerMethod = method;
- #endif
- TickOffset = 0L;
- curtime = VQA_GetTime();
- TickOffset = (time - curtime);
- }
- /****************************************************************************
- *
- * NAME
- * VQA_GetTime - Return current time.
- *
- * SYNOPSIS
- * Time = VQA_GetTime()
- *
- * unsigned long VQA_GetTime(void);
- *
- * FUNCTION
- * This routine returns timer ticks computed one of 3 ways:
- *
- * 1) If audio is playing, the timer is based on the DMA buffer position:
- * Compute the number of audio samples that have actually been played.
- * The following internal HMI variables are used:
- *
- * _lpSOSDMAFillCount[drv_handle]: current DMA buffer position
- * _lpSOSSampleList[drv_handle][samp_handle]:
- * sampleTotalBytes: total bytes sent by HMI to the DMA buffer
- * sampleLastFill: HMI's last fill position in DMA buffer
- *
- * So, the number of samples actually played is:
- *
- * sampleTotalBytes - <DMA_diff>
- * where <DMA_diff> is how far ahead sampleLastFill is in front of
- * _lpSOSDMAFillCount: (sampleLastFill - _lpSOSDMAFillCount)
- *
- * These values are indices into a circular DMA buffer, so:
- *
- * if (sampleLastFill >= _lpSOSDMAFillCount)
- * <DMA_diff> = sampleLastFill - _lpSOSDMAFillCount
- * else
- * <DMA_diff> = (DMA_BUF_SIZE - lpSOSDMAFillCount) + sampleLastFill
- *
- * Note that, if using the stereo driver with mono data, you must
- * divide LastFill & FillCount by 2, but not TotalBytes. If using the
- * stereo driver with stereo data, you must divide all 3 variables
- * by 2.
- *
- * 2) If no audio is playing, but the timer interrupt is running,
- * VQATickCount is used as the timer
- *
- * 3) If no audio is playing & no timer interrupt is going, the DOS 18.2
- * system timer is used.
- *
- * Regardless of the method, TickOffset is used as an offset from the
- * computed time.
- *
- * INPUTS
- * NONE
- *
- * RESULT
- * Time - Time in VQA_TIMETICKS
- *
- ****************************************************************************/
- unsigned long VQA_GetTime(void)
- {
- #if(VQAAUDIO_ON)
- VQAAudio *audio;
- unsigned long bytesleft;
- unsigned long samples;
- #endif
- long ticks;
- #if(VQAAUDIO_ON)
- switch (TimerMethod) {
- /* If Audio is playing then timing is based on the audio DMA buffer
- * position.
- */
- case VQA_TMETHOD_AUDIO:
- /* Dereference commonly used data members for quicker access. */
- audio = &VQAP->VQABuf->Audio;
- /* Compute our current position in the audio track by getting the
- * bytes processed by HMI. Then adjust the bytes processed by the
- * position of the DMA fill handler, this should tell us exactly
- * where we are in the audio track.
- */
- samples = _lpSOSSampleList[DigiHandle][SampleHandle]->sampleTotalBytes;
- bytesleft = _lpSOSSampleList[DigiHandle][SampleHandle]->sampleBytesLeft;
- /* Adjust the bytes processed from the bytes left to DMA in the
- * buffer.
- */
- if (samples >= bytesleft) {
- samples -= bytesleft;
- }
- /* Calculate the number of samples by taking the total number of
- * bytes processed and divide it by the number of channels and
- * bits per sample.
- */
- samples -= (audio->NumSkipped * VQAP->Config.HMIBufSize);
- samples /= (audio->Channels * (audio->BitsPerSample >> 3));
- /* The elapsed ticks is calculated by the number of samples
- * processed times the tick resolution per second divided by the
- * sample rate.
- */
- ticks = (long)((samples * VQA_TIMETICKS) / audio->SampleRate);
- ticks += TickOffset;
- break;
- /* No audio playing, but timer interrupt is going; use VQATickCount */
- case VQA_TMETHOD_INT:
- ticks = (VQATickCount + TickOffset);
- break;
- /* No interrupts are going at all; use DOS's time */
- default:
- case VQA_TMETHOD_DOS:
- {
- struct timeb mytime;
- ftime(&mytime);
- ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm;
- ticks = ((ticks * VQA_TIMETICKS) / 1000L);
- ticks += TickOffset;
- }
- break;
- }
- #else
- {
- struct timeb mytime;
- ftime(&mytime);
- ticks = (unsigned long)mytime.time*1000L+(unsigned long)mytime.millitm;
- ticks = ((ticks * VQA_TIMETICKS) / 1000L);
- ticks += TickOffset;
- }
- #endif
- return (ticks);
- }
- /****************************************************************************
- *
- * NAME
- * VQA_TimerMethod - Get timer method being used.
- *
- * SYNOPSIS
- * Method = VQA_TimerMethod()
- *
- * long VQA_TimerMethod(void);
- *
- * FUNCTION
- * Returns the ID of the current timer method being used.
- *
- * INPUTS
- * NONE
- *
- * RESULT
- * Method - Method used for the timer.
- *
- ****************************************************************************/
- long VQA_TimerMethod(void)
- {
- #if(VQAAUDIO_ON)
- return (TimerMethod);
- #else
- return (VQA_TMETHOD_DOS);
- #endif
- }
- #if(VQAAUDIO_ON)
- /****************************************************************************
- *
- * NAME
- * VQA_OpenAudio - Open sound system.
- *
- * SYNOPSIS
- * Error = VQA_OpenAudio(VQAHandleP)
- *
- * long VQA_OpenAudio(VQAHandleP *);
- *
- * FUNCTION
- * Initialize the sound system by setting the DigiHandle to and HMI
- * driver handle. The timer must first be initialized before calling
- * this function.
- *
- * INPUTS
- * VQAHandleP - Pointer to private VQAHandle.
- *
- * RESULT
- * Error - 0 if successful, -1 if error.
- *
- ****************************************************************************/
- long VQA_OpenAudio(VQAHandleP *vqap)
- {
- VQAData *vqabuf;
- VQAAudio *audio;
- VQAConfig *config;
- VQAHeader *header;
- unsigned char *driver_path;
- WORD port;
- long rc;
- /* Return if already initialized */
- if (DigiInitFlag != HMI_UNINIT) {
- return (0);
- }
- /* Dereference data memebers for quicker access. */
- config = &vqap->Config;
- header = &vqap->Header;
- vqabuf = vqap->VQABuf;
- audio = &vqabuf->Audio;
- /* Fail if no audio buffer or DigiCard is 0 (no sound) */
- if ((audio->Buffer == NULL) || (config->DigiCard == 0)) {
- return (-1);
- }
- /* Reset the buffer position to the beginning. */
- CurBlock = 0;
- /*-------------------------------------------------------------------------
- * Compute the playback rate:
- *
- * - If Config->AudioRate is -1, use HMI_DATA_RATE scaled to the specified
- * frame rate (so the audio plays faster if we're playing faster)
- * - otherwise, use the specified rate
- *-----------------------------------------------------------------------*/
- if (config->AudioRate != -1) {
- sSOSInitDriver.wSampleRate = config->AudioRate;
- }
- else if (config->FrameRate != header->FPS) {
- sSOSInitDriver.wSampleRate = ((audio->SampleRate * config->FrameRate)
- / (unsigned long)header->FPS);
- config->AudioRate = sSOSInitDriver.wSampleRate;
- } else {
- sSOSInitDriver.wSampleRate = audio->SampleRate;
- config->AudioRate = audio->SampleRate;
- }
- /* If the application has already initialized HMI then set the
- * necessary variables. Otherwise we must setup HMI ourself.
- */
- if (config->OptionFlags & VQAOPTF_HMIINIT) {
- /* The application MUST provide the card type! */
- if (config->DigiCard == -1) {
- return (-1);
- }
- /* Init the detection system */
- driver_path = (unsigned char *)".\\";
- if ((rc = sosDIGIDetectInit(driver_path)) != 0) {
- return (rc);
- }
- /* Get the capabilities of the card being used. */
- rc = sosDIGIDetectGetCaps(config->DigiCard, &DigiCaps);
- sosDIGIDetectUnInit();
- if (rc != 0) {
- return (rc);
- }
- DigiHandle = config->DigiHandle;
- HMIDevName = "App-Specific";
- TimerInitFlag = HMI_APPINIT;
- DigiInitFlag = HMI_APPINIT;
- } else {
- /* Init the detection system */
- driver_path = (unsigned char *)".\\";
- if ((rc = sosDIGIDetectInit(driver_path)) != 0) {
- return (rc);
- }
- /*-----------------------------------------------------------------------
- * Initialize DigiHardware with port, IRQ, and DMA, and make sure
- * Config.DigiCard contains the HMI ID we want to use:
- *
- * - If Config.DigiCard is -1, auto-detect the hardware; then, do a
- * FindHardware so the GetSettings will work
- * - If Config.DigiCard is filled in, but port, IRQ or DMA is -1, use
- * FindHardware & GetSettings to get the settings
- * - If all are filled in, just use them as they are
- *---------------------------------------------------------------------*/
- /* Auto-Detect */
- if (config->DigiCard == -1) {
- /* Version 1 VQA's have only 8 bit mono audio streams. */
- if (header->Version == VQAHD_VER1) {
- config->DigiCard = AutoDetect(8, 1);
- } else {
- config->DigiCard = AutoDetect(audio->BitsPerSample, audio->Channels);
- /* Resort to 8bit mono */
- if (config->DigiCard == -1) {
- config->DigiCard = AutoDetect(8, 1);
- }
- }
- if (config->DigiCard == -1) {
- sosDIGIDetectUnInit();
- return (-1);
- }
- }
- /* Do a FindHardware & GetSettings */
- if (config->DigiPort == -1) {
- if (sosDIGIDetectFindHardware(config->DigiCard, &DigiCaps, &port)) {
- sosDIGIDetectUnInit();
- return (-1);
- }
- if (sosDIGIDetectGetSettings(&DigiHardware)) {
- sosDIGIDetectUnInit();
- return (-1);
- }
- config->DigiPort = DigiHardware.wPort;
- config->DigiIRQ = DigiHardware.wIRQ;
- config->DigiDMA = DigiHardware.wDMA;
- HMIDevName = (char *)DigiCaps.szDeviceName;
- } else {
- DigiHardware.wPort = config->DigiPort;
- DigiHardware.wIRQ = config->DigiIRQ;
- DigiHardware.wDMA = config->DigiDMA;
- HMIDevName = "App-Specific";
- }
- sosDIGIDetectUnInit();
- /* Initialize the DIGI system & driver */
- sosDIGIInitSystem(driver_path, _SOS_DEBUG_NORMAL);
- sSOSInitDriver.wBufferSize = config->HMIBufSize;
- sSOSInitDriver.lpBuffer = NULL;
- sSOSInitDriver.lpFillHandler = NULL;
- sSOSInitDriver.lpDriverMemory = NULL;
- sSOSInitDriver.lpTimerMemory = NULL;
- DigiHandle = -1;
- if ((rc = sosDIGIInitDriver(config->DigiCard, &DigiHardware,
- &sSOSInitDriver, &DigiHandle)) != 0) {
- return (rc);
- }
- /*-----------------------------------------------------------------------
- * Register the timer event
- *---------------------------------------------------------------------*/
- /* If the timer hasn't been init'd, do it now */
- if (TimerInitFlag == HMI_UNINIT) {
- sosTIMERInitSystem(_TIMER_DOS_RATE ,_SOS_DEBUG_NORMAL);
- TimerInitFlag = HMI_VQAINIT;
- }
- /* Register the event */
- rc = sosTIMERRegisterEvent(VQA_TIMETICKS, sSOSInitDriver.lpFillHandler,
- &DigiTimer);
- if (rc) {
- sosDIGIUnInitDriver(DigiHandle, _TRUE, _TRUE);
- sosDIGIUnInitSystem();
- return (rc);
- }
- config->DigiHandle = DigiHandle;
- DigiInitFlag = HMI_VQAINIT;
- }
- return (0);
- }
- /****************************************************************************
- *
- * NAME
- * AutoDetect - Auto detect the sound card.
- *
- * SYNOPSIS
- * CardID = AutoDetect(BitSize, Channels)
- *
- * long AutoDetect(long, long);
- *
- * FUNCTION
- * Autodetects the type of sound card present in the system.
- *
- * INPUTS
- * BitSize - Bits per sample size.
- * Channels - Number of desired channels.
- *
- * RESULT
- * CardID - HMI ID of the sound card found, -1 if none.
- *
- ****************************************************************************/
- long AutoDetect(long bitsize, long channels)
- {
- long device_id = -1;
- WORD port;
- long i;
- long rc;
- /* Search for an 8-bit mono device */
- if (sosDIGIDetectFindFirst(&DigiCaps, &port)) {
- return (-1);
- }
- channels--;
- i = 0;
- while (i < 6) {
- i++;
- if ((DigiCaps.wBitsPerSample == bitsize)
- && (DigiCaps.wChannels == channels)) {
- break;
- }
- if (sosDIGIDetectFindNext(&DigiCaps, &port)) {
- return (-1);
- }
- }
- /* Exit if failed to find the required device */
- if ((DigiCaps.wBitsPerSample != bitsize)
- || (DigiCaps.wChannels != channels)) {
- return (-1);
- }
- /* Stash the ID */
- device_id = DigiCaps.wDeviceID;
- /* Now that we have handled the initial pass, verify that if we found an
- * _ADLIB_GOLD_8_MONO that it is not a Logitech Sound Wave man in disguise.
- */
- if ((WORD)DigiCaps.wDeviceID == _ADLIB_GOLD_8_MONO) {
- rc = sosDIGIDetectFindNext(&DigiCaps, &port);
- while ((i < 6) && (rc == 0)) {
- i++;
- if ((DigiCaps.wBitsPerSample == 8) && (DigiCaps.wChannels == 0)) {
- break;
- }
- if ((rc = sosDIGIDetectFindNext(&DigiCaps, &port)) != 0) {
- break;
- }
- }
- /* If we don't have an error use the secondary device ID, after all,
- * anything's better than an Adlib Gold. If we do have an error or there
- * is nothing we can use then the device ID is already set to the adlib
- * gold so just let it rip.
- */
- if ((rc == 0) && ((WORD)DigiCaps.wDeviceID == _SBPRO_8_MONO)) {
- return (DigiCaps.wDeviceID);
- }
- }
- return (device_id);
- }
- /****************************************************************************
- *
- * NAME
- * VQA_CloseAudio - Close sound system
- *
- * SYNOPSIS
- * VQA_CloseAudio()
- *
- * void VQA_CloseAudio(void);
- *
- * FUNCTION
- * Removes VQA's involvement in the audio system.
- *
- * INPUTS
- * NONE
- *
- * RESULT
- * NONE
- *
- ****************************************************************************/
- void VQA_CloseAudio(void)
- {
- /* Remove the Digi event */
- if (DigiInitFlag == HMI_VQAINIT) {
- sosTIMERRemoveEvent(DigiTimer);
- }
- /* Un-init timer if necessary */
- if (TimerInitFlag == HMI_VQAINIT) {
- sosTIMERUnInitSystem(0);
- }
- TimerInitFlag = HMI_UNINIT;
- /* Remove the driver */
- if (DigiInitFlag == HMI_VQAINIT) {
- sosDIGIUnInitDriver(DigiHandle, _TRUE, _TRUE);
- sosDIGIUnInitSystem();
- }
- DigiInitFlag = HMI_UNINIT;
- if (PlayingFlag) {
- PlayingFlag = 0;
- }
- }
- /****************************************************************************
- *
- * NAME
- * VQA_StartAudio - Starts audio playback
- *
- * SYNOPSIS
- * Error = VQA_StartAudio(VQA)
- *
- * long VQA_StartAudio(VQAHandleP *);
- *
- * FUNCTION
- * Start the audio playback for the movie.
- *
- * INPUTS
- * VQA - Pointer to private VQA handle.
- *
- * RESULT
- * Error - 0 if successful, or -1 error code.
- *
- ****************************************************************************/
- long VQA_StartAudio(VQAHandleP *vqap)
- {
- VQAConfig *config;
- VQAAudio *audio;
- /* Save buffers for the callback routine */
- VQAP = vqap;
- /* Dereference commonly used data members for quicker access. */
- config = &vqap->Config;
- audio = &vqap->VQABuf->Audio;
- /* Return if already playing */
- if (audio->Flags & VQAAUDF_ISPLAYING) {
- return (-1);
- }
- /* Set my driver handle */
- if (config->DigiHandle != -1) {
- DigiHandle = config->DigiHandle;
- }
- if (DigiHandle == (WORD)-1) {
- return (-1);
- }
- /*-------------------------------------------------------------------------
- * Initialize the sample structure.
- *-----------------------------------------------------------------------*/
- memset(&sSOSSampleData, 0, sizeof(_SOS_START_SAMPLE));
- sSOSSampleData.lpSamplePtr = (unsigned char *)vqap->VQABuf->Audio.Buffer;
- sSOSSampleData.dwSampleSize = config->HMIBufSize;
- sSOSSampleData.wVolume = (config->Volume << 7);
- sSOSSampleData.wSampleID = HMI_SAMPLE;
- sSOSSampleData.lpCallback = AudioCallback;
- /* Set the channel flags for the type of data we have. */
- if (audio->Channels == 2) {
- sSOSSampleData.wChannel = _INTERLEAVED;
- } else {
- sSOSSampleData.wChannel = _CENTER_CHANNEL;
- }
- /* If the card is unable to handle stereo data the we must notify the
- * sound system to convert the stereo data to mono data during playback.
- */
- if ((audio->Channels - 1) > DigiCaps.wChannels) {
- sSOSSampleData.wSampleFlags |= _STEREOTOMONO;
- }
- /* If the card is unable to handle the sample size of the audio data
- * then we must notify the sound system to convert the audio data to
- * the proper format.
- */
- if (audio->BitsPerSample != DigiCaps.wBitsPerSample) {
- if (audio->BitsPerSample > DigiCaps.wBitsPerSample) {
- sSOSSampleData.wSampleFlags |= _TRANSLATE16TO8;
- } else {
- sSOSSampleData.wSampleFlags |= _TRANSLATE8TO16;
- }
- }
- /* Start playback */
- SampleHandle = sosDIGIStartSample(DigiHandle, &sSOSSampleData);
- audio->Flags |= VQAAUDF_ISPLAYING;
- PlayingFlag = 1;
- return (0);
- }
- /****************************************************************************
- *
- * NAME
- * VQA_StopAudio - Stop audio playback.
- *
- * SYNOPSIS
- * VQA_StopAudio(VQA)
- *
- * void VQA_StopAudio(VQAHandleP *);
- *
- * FUNCTION
- * Halts the currently playing audio stream.
- *
- * INPUTS
- * VQA - Pointer to private VQAHandle.
- *
- * RESULT
- * NONE
- *
- ****************************************************************************/
- void VQA_StopAudio(VQAHandleP *vqap)
- {
- /* Just return if not playing */
- if (vqap->VQABuf->Audio.Flags & VQAAUDF_ISPLAYING) {
- vqap->VQABuf->Audio.Flags &= ~VQAAUDF_ISPLAYING;
- sosDIGIStopSample(DigiHandle, SampleHandle);
- }
- }
- /****************************************************************************
- *
- * NAME
- * AudioCallback - Sound system callback.
- *
- * SYNOPSIS
- * AudioCallback(DriverHandle, Action, SampleID)
- *
- * void AudioCallback(WORD, WORD, WORD);
- *
- * FUNCTION
- * Our custom audio callback routine that services HMI.
- *
- * INPUTS
- * DriverHandle - HMI driver handle.
- * Action - Action taken.
- * SampleID - ID of sample.
- *
- * RESULT
- * NONE
- *
- ****************************************************************************/
- void far cdecl AudioCallback(WORD wDriverHandle,WORD wAction,WORD wSampleID)
- {
- VQAAudio *audio;
- VQAConfig *config;
- /* Dereference commonly used data members for quicker access. */
- audio = &VQAP->VQABuf->Audio;
- config = &VQAP->Config;
- /* Suppress compiler warnings */
- wDriverHandle = wDriverHandle;
- wSampleID = wSampleID;
- /* See if we're called because the buffer is empty */
- if (wAction == _SAMPLE_PROCESSED) {
- /* Compute the 'NextBlock' index */
- NextBlock = CurBlock + 1;
- if (NextBlock >= audio->NumAudBlocks) {
- NextBlock = 0;
- }
- /* See if the next block has data in it; if so, update the audio
- * buffer play position & the 'CurBlock' value.
- * If not, don't change anything and replay this block.
- */
- if (audio->IsLoaded[NextBlock] == 1) {
- /* Update this block's status to loadable (0) */
- audio->IsLoaded[CurBlock] = 0;
- /* Update position within audio buffer */
- audio->PlayPosition += config->HMIBufSize;
- CurBlock++;
- if (audio->PlayPosition >= config->AudioBufSize) {
- audio->PlayPosition = 0;
- CurBlock = 0;
- }
- } else {
- audio->NumSkipped++;
- }
- /* Start the new buffer playing */
- sSOSSampleData.lpSamplePtr = (unsigned char *)(audio->Buffer)
- + audio->PlayPosition;
- sosDIGIContinueSample(DigiHandle, SampleHandle, &sSOSSampleData);
- audio->SamplesPlayed += config->HMIBufSize;
- }
- }
- /****************************************************************************
- *
- * NAME
- * CopyAudio - Copy data from Audio Temp buffer into Audio play buffer.
- *
- * SYNOPSIS
- * Error = CopyAudio(VQA)
- *
- * long CopyAudio(VQAHandleP *);
- *
- * FUNCTION
- * This routine just copies the data in the TempBuf into the correct
- * spots in the audio play buffer. If there is no room available in the
- * audio play buffer, the routine returns VQAERR_SLEEPING, which will put
- * the whole Loader to "sleep" while it waits for a free buffer.
- *
- * If there's no data in the TempBuf to copy, the routine just returns 0.
- *
- * INPUTS
- * VQA - Pointer to private VQAHandle structure.
- *
- * RESULT
- * Error - 0 if successful or VQAERR_??? error code.
- *
- ****************************************************************************/
- long CopyAudio(VQAHandleP *vqap)
- {
- VQAAudio *audio;
- VQAConfig *config;
- long startblock;
- long endblock;
- long len1,len2;
- long i;
- /* Dereference commonly used data members for quicker access. */
- audio = &vqap->VQABuf->Audio;
- config = &vqap->Config;
- /* If audio is disabled, or if we're playing from a VOC file, or if
- * there's no Audio Buffer, or if there's no data to copy, just return 0
- */
- #if(VQAVOC_ON && VQAAUDIO_ON)
- if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (vqap->vocfh != -1)
- || (audio->Buffer == NULL) || (audio->TempBufLen == 0)) {
- #else /* VQAVOC_ON */
- if (((config->OptionFlags & VQAOPTF_AUDIO) == 0) || (audio->Buffer == NULL)
- || (audio->TempBufLen == 0)) {
- #endif /* VQAVOC_ON */
- return (0);
- }
- /* Compute start & end blocks to copy into */
- startblock = (audio->AudBufPos / config->HMIBufSize);
- endblock = (audio->AudBufPos + audio->TempBufLen) / config->HMIBufSize;
- if (endblock >= audio->NumAudBlocks) {
- endblock -= audio->NumAudBlocks;
- }
- /* If 'endblock' hasn't played yet, return VQAERR_SLEEPING */
- if (audio->IsLoaded[endblock] == 1) {
- return (VQAERR_SLEEPING);
- }
- /* Copy the data:
- *
- * - If 'startblock' < 'endblock', copy the entire buffer
- * - Otherwise, fill to the end of the buffer with part of the data, then
- * copy the rest to the beginning of the buffer
- */
- if (startblock <= endblock) {
- /* Copy data */
- memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf,
- audio->TempBufLen);
- /* Adjust current load position */
- audio->AudBufPos += audio->TempBufLen;
- /* Mark buffer as empty */
- audio->TempBufLen = 0;
- /* Set all blocks to loaded */
- for (i = startblock; i < endblock; i++) {
- audio->IsLoaded[i] = 1;
- }
- return (0);
- } else {
- /* Compute length of each piece */
- len1 = config->AudioBufSize - audio->AudBufPos;
- len2 = audio->TempBufLen - len1;
-
- /* Copy 1st piece into end of Audio Buffer */
- memcpy((audio->Buffer + audio->AudBufPos), audio->TempBuf, len1);
- /* Copy 2nd piece into start of Audio Buffer */
- memcpy(audio->Buffer, audio->TempBuf + len1, len2);
-
- /* Adjust load position */
- audio->AudBufPos = len2;
- /* Mark buffer as empty */
- audio->TempBufLen = 0;
- /* Set blocks to loaded */
- for (i = startblock; i < audio->NumAudBlocks; i++) {
- audio->IsLoaded[i] = 1;
- }
- for (i = 0; i < endblock; i++) {
- audio->IsLoaded[i] = 1;
- }
- return (0);
- }
- }
- #ifdef __WATCOMC__
- #pragma pack(1);
- #endif
- #endif /* VQAAUDIO_ON */
|