/* MIDAS.C * * Simplified MIDAS Sound System API * * Copyright 1995 Petteri Kangaslampi and Jarno Paananen * * This file is part of the MIDAS Sound System, and may only be * used, modified and distributed under the terms of the MIDAS * Sound System license, LICENSE.TXT. By continuing to use, * modify or distribute this file you indicate that you have * read the license and understand and accept it fully. */ #include #include #include #include "midas.h" /****************************************************************************\ * Global variables: \****************************************************************************/ SoundDevice *midasSD; /* current Sound Device */ ModulePlayer *midasMP; /* current Module Player */ SoundDevice *midasSoundDevices[NUMSDEVICES] = { &GUS, /* array of pointers to all Sound */ &PAS, /* Devices, in numbering and */ &WSS, /* detection order - GUS is SD #1 */ &SB, /* and will be detected first */ &NSND }; int midasDisableEMS; /* 1 if EMS usage is disabled (default 0) */ int midasSDNumber; /* Sound Device number (-1 for autodetect, default -1) */ int midasSDPort; /* Sound Device I/O port number (-1 for autodetect or SD default, default -1) */ int midasSDIRQ; /* Sound Device IRQ number (-1 for autodetect or SD default, default -1) */ int midasSDDMA; /* Sound Device DMA channel number (-1 for autodetect or SD default, default -1) */ int midasSDCard; /* Sound Device sound card type (-1 for autodetect or SD default, default -1) */ unsigned midasMixRate; /* Sound Device mixing rate */ unsigned midasOutputMode; /* Sound Device output mode force bits, default 0 (SD default) */ int midasAmplification; /* Forced amplification level or -1 for SD default (default -1) */ int midasChannels; /* number of channels open or 0 if no channels have been opened using midasOpenChannels() */ int midasPlayerNum; /* timer music player number */ int midasEMSInit; /* is EMS heap manager initialized? */ int midasTMRInit; /* is TempoTimer initialized? */ int midasTMRPlay; /* is sound being played with timer?*/ int midasSDInit; /* is Sound Device initialized? */ int midasSDChans; /* are Sound Device channels open? */ int midasMPInit; /* is Module Player initialized? */ int midasMPPlay; /* is Module Player playing? */ int midasTMRMusic; /* is music being player with timer?*/ /****************************************************************************\ * * Function: void midasError(int errNum) * * Description: Prints a MIDAS error message to stderr and exits to DOS * * Input: int errNum MIDAS error code * \****************************************************************************/ void CALLING midasError(int errNum) { char errmsg[60]; midasClose(); /* uninitialize MIDAS Sound System */ mStrCopy(&errmsg[0], "MIDAS Error: "); mStrAppend(&errmsg[0], errorMsg[errNum]); errErrorExit(&errmsg[0]); /* print error message */ } /****************************************************************************\ * * Function: void midasUninitError(int errNum) * * Description: Prints an error message to stderr and exits to DOS without * uninitializing MIDAS. This function should only be used * from midasClose(); * * Input: int errNum MIDAS error code * \****************************************************************************/ void CALLING midasUninitError(int errNum) { char errmsg[85]; mStrCopy(&errmsg[0], "FATAL MIDAS uninitialization failure: "); mStrAppend(&errmsg[0], errorMsg[errNum]); errErrorExit(&errmsg[0]); /* print error message */ } /****************************************************************************\ * * Function: void midasDetectSD(void) * * Description: Attempts to detect a Sound Device. Sets the global variable * midasSD to point to the detected Sound Device or NULL if no * Sound Device was detected * \****************************************************************************/ void CALLING midasDetectSD(void) { int dsd; int dResult; int error; midasSD = NULL; /* no Sound Device detected yet */ midasSDNumber = -1; dsd = 0; /* start from first Sound Device */ /* search through Sound Devices until a Sound Device is detected: */ while ( (midasSD == NULL) && (dsd < NUMSDEVICES) ) { /* attempt to detect current SD: */ if ( (error = (*midasSoundDevices[dsd]->Detect)(&dResult)) != OK ) midasError(error); if ( dResult == 1 ) { midasSDNumber = dsd; /* Sound Device detected */ /* point midasSD to this Sound Device: */ midasSD = midasSoundDevices[dsd]; } dsd++; /* try next Sound Device */ } } /****************************************************************************\ * * Function: void midasInit(void); * * Description: Initializes MIDAS Sound System * \****************************************************************************/ void CALLING midasInit(void) { int error, result; #ifndef NOEMS if ( !midasDisableEMS ) /* is EMS usage disabled? */ { /* Initialize EMS Heap Manager: */ if ( (error = emsInit(&midasEMSInit)) != OK ) midasError(error); /* was EMS Heap Manager initialized? */ if ( midasEMSInit == 1 ) { useEMS = 1; /* yes, use EMS memory */ } else { useEMS = 0; /* no, do not use EMS memory */ } } else #endif { midasEMSInit = 0; useEMS = 0; /* EMS disabled - do not use it */ } if ( midasSDNumber == -1 ) /* has a Sound Device been selected? */ { midasDetectSD(); /* attempt to detect Sound Device */ if ( midasSD == NULL ) midasError(errSDFailure); } else { /* use selected Sound Device: */ midasSD = midasSoundDevices[midasSDNumber]; /* Sound Device number was forced, but if no I/O port, IRQ, DMA or sound card type has been set, try to autodetect the values for this Sound Device. If detection fails, use default values: */ if ( (midasSDPort == -1) && (midasSDIRQ == -1) && (midasSDDMA == -1) && (midasSDCard == -1) ) if ( (error = midasSD->Detect(&result)) != OK ) midasError(error); } if ( midasSDPort != -1 ) /* has an I/O port been selected? */ midasSD->port = midasSDPort; /* if yes, set it to Sound Device */ if ( midasSDIRQ != -1 ) /* SD IRQ number? */ midasSD->IRQ = midasSDIRQ; /* if yes, set it to Sound Device */ if ( midasSDDMA != -1 ) /* SD DMA channel number? */ midasSD->DMA = midasSDDMA; if ( midasSDCard != -1 ) /* sound card type? */ midasSD->cardType = midasSDCard; /* initialize TempoTimer: */ if ( (error = tmrInit()) != OK ) midasError(error); midasTMRInit = 1; /* TempoTimer initialized */ /* initialize Sound Device: */ if ( (error = midasSD->Init(midasMixRate, midasOutputMode)) != OK ) midasError(error); midasSDInit = 1; /* Sound Device initialized */ /* start playing sound using the timer: */ if ( (error = tmrPlaySD(midasSD)) != OK ) midasError(error); midasTMRPlay = 1; } /****************************************************************************\ * * Function: void midasClose(void) * * Description: Uninitializes MIDAS Sound System * \****************************************************************************/ void CALLING midasClose(void) { int error; /* if music is being played with timer, stop it: */ if ( midasTMRMusic ) { if ( (error = midasMP->SetUpdRateFunct(NULL)) != OK ) midasUninitError(error); if ( (error = tmrStopMusic(midasPlayerNum)) != OK ) midasUninitError(error); midasTMRMusic = 0; } /* if Module Player is playing, stop it: */ if ( midasMPPlay ) { if ( (error = midasMP->StopModule()) != OK ) midasUninitError(error); midasMPPlay = 0; } /* if Module Player has been initialized, uninitialize it: */ if ( midasMPInit ) { if ( (error = midasMP->Close()) != OK ) midasUninitError(error); midasMPInit = 0; midasMP = NULL; } /* if Sound Device channels are open, close them: */ if ( midasSDChans ) { if ( (error = midasSD->CloseChannels()) != OK ) midasUninitError(error); midasSDChans = 0; midasChannels = 0; } /* if sound is being played, stop it: */ if ( midasTMRPlay ) { if ( (error = tmrStopSD()) != OK ) midasUninitError(error); midasTMRPlay = 0; } /* if Sound Device is initialized, uninitialize it: */ if ( midasSDInit ) { if ( (error = midasSD->Close()) != OK ) midasUninitError(error); midasSDInit = 0; midasSD = NULL; } /* if TempoTimer is initialized, uninitialize it: */ if ( midasTMRInit ) { if ( (error = tmrClose()) != OK ) midasUninitError(error); midasTMRInit = 0; } #ifndef NOEMS /* if EMS Heap Manager is initialized, uninitialize it: */ if ( midasEMSInit ) { if ( (error = emsClose()) != OK ) midasUninitError(error); midasEMSInit = 0; } #endif } /****************************************************************************\ * * Function: void midasSetDefaults(void) * * Description: Initializes MIDAS Sound System variables to their default * states. MUST be the first MIDAS function called. * \****************************************************************************/ void CALLING midasSetDefaults(void) { midasEMSInit = 0; /* EMS heap manager is not initialized yet */ midasTMRInit = 0; /* TempoTimer is not initialized */ midasTMRPlay = 0; /* Sound is not being played */ midasSDInit = 0; /* Sound Device is not initialized */ midasSDChans = 0; /* Sound Device channels are not open */ midasMPInit = 0; /* Module Player is not initialized */ midasMPPlay = 0; /* Module Player is not playing */ midasTMRMusic = 0; /* Music is not being played with timer */ midasChannels = 0; /* No channels opened */ ptTempo = 1; /* enable ProTracker BPM tempos */ usePanning = 1; /* enable ProTracker panning cmds */ surround = 0; /* disable surround to save GUS mem */ extendedOctaves = 0; /* Disable extended octaves */ useVDS = 1; /* use VDS if found */ midasDisableEMS = 0; /* do not disable EMS usage */ midasSDNumber = -1; /* no Sound Device forced */ midasSDPort = -1; /* no I/O port forced */ midasSDIRQ = -1; /* no IRQ number forced */ midasSDDMA = -1; /* no DMA channel number forced */ midasSDCard = -1; /* no sound card type forced */ midasOutputMode = 0; /* no output mode forced */ midasMixRate = 44100; /* attempt to use 44100Hz mixing rate */ midasAmplification = -1; /* use default amplification level */ midasSD = NULL; /* point midasSD and midasMP to */ midasMP = NULL; /* NULL for safety */ } #ifndef NOLOADERS /****************************************************************************\ * * Function: mpModule *midasLoadModule(char *fileName, ModulePlayer *MP, * int (*SaveSampleInfo)(ushort sdInstHandle, uchar *sample, * ushort slength, ushort loopStart, ushort loopEnd ); * * Description: Loads a module file into memory. * * Input: char *fileName Pointer to module file name * ModulePlayer *MP Pointer to the Module Player which * will be used for loading the module * int (*SaveSampleInfo)() Pointer to sample information saving * function. sdInstHandle = Sound Device * instrument handle, sample = pointer to * sample data, slength = sample length, * loopStart = sample loop start, * loopEnd = sample loop end. The * function must return a MIDAS error * code. NULL if no such function is * used. * * Returns: Pointer to the loaded module structure * * Notes: The only practical use at this point for SaveSampleInfo() are * the real VU-meters. To load a module and add the prepare the * VU meter information use: * module = midasLoadModule(fileName, MP, &vuPrepare); * Note that the definition of SaveSampleInfo matches exactly * the prototype of vuPrepare(). * \****************************************************************************/ mpModule * CALLING midasLoadModule(char *fileName, ModulePlayer *MP, int CALLING (*SaveSampleInfo)(ushort sdInstHandle, uchar *sample, ushort slength, ushort loopStart, ushort loopEnd)) { mpModule *module; int error; /* load module: */ if ( (error = MP->LoadModule(fileName, midasSD, SaveSampleInfo, (mpModule**) &module)) != OK ) midasError(error); return module; } /****************************************************************************\ * * Function: void midasFreeModule(mpModule *module); * * Description: Deallocates a module from memory * * Input: mpModule *module Pointer to module to be deallocated * \****************************************************************************/ void CALLING midasFreeModule(mpModule *module) { ModulePlayer *MP; int error; MP = module->MP; if ( (error = MP->FreeModule(module, midasSD)) != OK ) midasError(error); } #endif /****************************************************************************\ * * Function: void midasOpenChannels(int numChans); * * Description: Opens Sound Device channels for sound and music output. * * Input: int numChans Number of channels to open * * Notes: Channels opened with this function can be used for sound * playing, and modules played with midasPlayModule() will be * played through the last of these channels. This function is * provided so that the same number of channels can be open * the whole time throughout the execution of the program, * keeping the volume level constant. Note that you must ensure * that you open enough channels for all modules, otherwise * midasPlayModule() will fail. * \****************************************************************************/ void CALLING midasOpenChannels(int numChans) { int error; midasChannels = numChans; /* open Sound Device channels: */ if ( (error = midasSD->OpenChannels(numChans)) != OK ) midasError(error); midasSDChans = 1; /* set amplification level if forced: */ if ( midasAmplification != -1 ) { if ( (error = midasSD->SetAmplification(midasAmplification)) != OK ) midasError(error); } } /****************************************************************************\ * * Function: void midasCloseChannels(void); * * Description: Closes Sound Device channels opened with midasOpenChannels(). * Do NOT call this function unless you have opened the sound * channels used yourself with midasOpenChannels(). * \****************************************************************************/ void CALLING midasCloseChannels(void) { int error; /* Close Sound Device channels: */ if ( (error = midasSD->CloseChannels()) != OK ) midasError(error); midasSDChans = 0; midasChannels = 0; } /****************************************************************************\ * * Function: midasPlayModule(mpModule *module, int numEffectChns) * * Description: Loads a module into memory, points midasMP to the correct * Module Player and starts playing it. * * Input: mpModule *module Module loaded with midasLoadModule() * int numEffectChns Number of channels to open for sound * effects. Ignored if sound channels * have already been opened with * midasOpenChannels(). * * Returns: Pointer to module structure. This function can not fail, * as it will call midasError() to handle all error cases. * * Notes: The Sound Device channels available for sound effects are the * _first_ numEffectChns channels. So, for example, if you use * midasPlayModule(module, 3), you can use channels 0-2 for sound * effects. If you already have opened channels with * midasOpenChannels(), the module will be played with the last * possible channels, so that the first channels will be * available for sound effects. Note that if not enough channels * are open this function will fail. * \****************************************************************************/ void CALLING midasPlayModule(mpModule *module, int numEffectChns) { short numChans; int error; int firstChannel; midasMP = module->MP; /* initialize module player: */ if ( (error = midasMP->Init(midasSD)) != OK ) midasError(error); midasMPInit = 1; numChans = module->numChans; /* Open Sound Device channels if not already open: */ if ( midasChannels == 0 ) { if ( (error = midasSD->OpenChannels(numChans + numEffectChns)) != OK ) midasError(error); midasSDChans = 1; firstChannel = numEffectChns; /* set amplification level if forced: */ if ( midasAmplification != -1 ) { if ( (error = midasSD->SetAmplification(midasAmplification)) != OK ) midasError(error); } } else { if ( midasChannels < numChans ) midasError(errNoChannels); firstChannel = midasChannels - numChans; } /* Start playing the module using the last Sound Device channels and looping the whole song: */ if ( (error = midasMP->PlayModule(module, firstChannel, numChans, 0, 32767)) != OK ) midasError(error); midasMPPlay = 1; /* start playing using the timer: */ if ( (error = tmrPlayMusic((void*) midasMP->Play, &midasPlayerNum)) != OK ) midasError(error); if ( (error = midasMP->SetUpdRateFunct(&tmrSetUpdRate)) != OK ) midasError(error); midasTMRMusic = 1; } /****************************************************************************\ * * Function: void midasStopModule(mpModule *module) * * Input: mpModule *module the module which is being played * * Description: Stops playing a module and uninitializes the Module Player. * If sound channels were NOT opened through midasOpenChannels(), * but by letting midasPlayModule() open them, they will be * closed. Sound channels opened with midasOpenChannels() are NOT * closed and must be closed separately. * \****************************************************************************/ void CALLING midasStopModule(mpModule *module) { int error, i; midasMP = module->MP; /* Stop playing music with timer: */ if ( (error = midasMP->SetUpdRateFunct(NULL)) != OK ) midasError(error); if ( (error = tmrStopMusic(midasPlayerNum)) != OK ) midasError(error); midasTMRMusic = 0; /* stop playing the module: */ if ( (error = midasMP->StopModule()) != OK ) midasError(error); midasMPPlay = 0; /* uninitialize Module Player: */ if ( (error = midasMP->Close()) != OK ) midasError(error); midasMPInit = 0; midasMP = NULL; /* point midasMP to NULL for safety */ /* If Sound Device channels were not opened with midasOpenChannels(), close them: */ if ( midasChannels == 0 ) { if ( (error = midasSD->CloseChannels()) != OK ) midasError(error); midasSDChans = 0; } else { /* Sound Device channels were originally opened with midasOpenChannels(). Now stop sounds from the channels used by the Module Player: */ for ( i = (midasChannels - module->numChans); i < midasChannels; i++ ) { if ( (error = midasSD->StopSound(i)) != OK ) midasError(error); if ( (error = midasSD->SetVolume(i, 0)) != OK ) midasError(error); } } }