Metropoli BBS
VIEWER: midas.c MODE: TEXT (ASCII)
/*      MIDAS.C
 *
 * Simple MIDAS Sound System programming interface
 *
 * Copyright 1994 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 <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include "midas.h"



/****************************************************************************\
*      Global variables:
\****************************************************************************/

SoundDevice     *SD;                    /* current Sound Device */
ModulePlayer    *MP;                    /* 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 };

    /* pointers to all Module Players: */
ModulePlayer    *midasModulePlayers[NUMMPLAYERS] =
    { &mpS3M,
      &mpMOD };

    /* Amiga Loop Emulation flags for Module Players: */
short           midasMPALE[NUMMPLAYERS] =
    { 0, 1 };






/****************************************************************************\
*      Static variables used by midasXXXX() functions:
\****************************************************************************/

static int      disableEMS;             /* should EMS usage be disabled? */
static ushort   sdNum;                  /* Sound Device number (0xFFFF for
                                           autodetect) */
static ushort   ioPort;                 /* I/O port number (0xFFFF for
                                           autodetect/default) */
static uchar    IRQ;                    /* IRQ number (0xFF for autodetect/
                                           default) */
static uchar    DMA;                    /* DMA channel number (0xFF for
                                           autodetect/default) */
static ushort   mixRate;                /* mixing rate */
static ushort   mode;                   /* forced output mode */

static int      emsInitialized;         /* is EMS heap manager initialized? */
static int      tmrInitialized;         /* is TempoTimer initialized? */
static int      sdInitialized;          /* is Sound Device initialized? */
static int      sdChOpen;               /* are Sound Device channels open? */
static int      vuInitialized;          /* are real VU-meters initialized? */
static int      mpInit;                 /* is Module Player initialized? */
static int      mpPlay;                 /* is Module Player playing? */
static int      mpInterrupt;            /* is Module Player interrupt set? */





/****************************************************************************\
*
* Function:     void midasError(char *msg)
*
* Description:  Prints an MIDAS error message to stderr, uninitializes MIDAS
*               and exits to DOS
*
* Input:        char *msg               Pointer to error message string
*
\****************************************************************************/

void midasError(char *msg)
{
    textmode(C80);
    fprintf(stderr, "MIDAS Error: %s\n", msg);
#ifdef DEBUG
    errPrintList();                     /* print error list */
#endif
    midasClose();
    exit(EXIT_FAILURE);
}




/****************************************************************************\
*
* Function:     void midasUninitError(char *msg)
*
* Description:  Prints an error message to stderr and exits to DOS without
*               uninitializing MIDAS. This function should only be used
*               from midasClose();
*
* Input:        char *msg               Pointer to error message string
*
\****************************************************************************/

void midasUninitError(char *msg)
{
    textmode(C80);
    fprintf(stderr, "FATAL: MIDAS uninitialization error: %s\n", msg);
#ifdef DEBUG
    errPrintList();                     /* print error list */
#endif
    abort();
}




/****************************************************************************\
*
* Function:     void midasDetectSD(void)
*
* Description:  Attempts to detect a Sound Device. Sets the global variable
*               SD to point to the detected Sound Device or NULL if no
*               Sound Device was detected
*
\****************************************************************************/

void midasDetectSD(void)
{
    int         dsd;
    int         dResult;
    int         error;

    SD = NULL;                          /* no Sound Device detected yet */
    dsd = 0;                            /* start from first Sound Device */

    /* search through Sound Devices until a Sound Device is detected: */
    while ( (SD == NULL) && (dsd < NUMSDEVICES) )
    {
        /* attempt to detect current SD: */
        if ( (error = (*midasSoundDevices[dsd]->Detect)(&dResult)) != OK )
            midasError(errorMsg[error]);
        if ( dResult == 1 )
        {
            sdNum = dsd;                /* Sound Device detected */
            SD = midasSoundDevices[dsd]; /* point SD to this Sound Device */
        }
        dsd++;                          /* try next Sound Device */
    }
}




/****************************************************************************\
*
* Function:     void midasInit(void);
*
* Description:  Initializes MIDAS Sound System
*
\****************************************************************************/

void midasInit(void)
{
    int         error, result;

    if ( !disableEMS )                  /* is EMS usage disabled? */
    {
        /* Initialize EMS Heap Manager: */
        if ( (error = emsInit(&emsInitialized)) != OK )
            midasError(errorMsg[error]);

        /* was EMS Heap Manager initialized? */
        if ( emsInitialized == 1 )
        {
            useEMS = 1;                 /* yes, use EMS memory, but do not */
            forceEMS = 0;               /* force its usage */
        }
        else
        {
            useEMS = 0;                 /* no, do not use EMS memory */
            forceEMS = 0;
        }
    }
    else
    {
        useEMS = 0;                     /* EMS disabled - do not use it */
        forceEMS = 0;
    }


    if ( sdNum == 0xFFFF )             /* has a Sound Device been selected? */
    {
        midasDetectSD();               /* attempt to detect Sound Device */
        if ( SD == NULL )
            midasError("Unable to detect Sound Device");
    }
    else
    {
        SD = midasSoundDevices[sdNum];  /* use Sound Device sdNum */

        /* Sound Device number was forced, but if no I/O port, IRQ or DMA
           number has been set, try to autodetect the values for this Sound
           Device. If detection fails, use default values */

        if ( (ioPort == 0xFFFF) && (IRQ == 0xFF) && (DMA == 0xFF) )
        {
            if ( (error = SD->Detect(&result)) != OK )
                midasError(errorMsg[error]);
            if ( result != 1 )
                midasError("Unable to detect Sound Device values");
        }
    }

    if ( ioPort != 0xFFFF )             /* has an I/O port been set? */
        SD->port = ioPort;              /* if yes, set it to Sound Device */
    if ( IRQ != 0xFF )                  /* what about IRQ number? */
        SD->IRQ = IRQ;
    if ( DMA != 0xFF )                  /* or DMA channel number */
        SD->DMA = DMA;

    /* initialize TempoTimer: */
    if ( (error = tmrInit()) != OK )
        midasError(errorMsg[error]);

    tmrInitialized = 1;                 /* TempoTimer initialized */

    /* initialize Sound Device: */
    if ( (error = SD->Init(mixRate, mode)) != OK )
        midasError(errorMsg[error]);

    sdInitialized = 1;                  /* Sound Device initialized */

#ifdef REALVUMETERS
    if ( realVU )
    {
        /* initialize real VU-meters: */
        if ( (error = vuInit()) != OK )
            midasError(errorMsg[error]);

        vuInitialized = 1;
    }
#endif
}



/****************************************************************************\
*
* Function:     void midasClose(void)
*
* Description:  Uninitializes MIDAS Sound System
*
\****************************************************************************/

void midasClose(void)
{
    int         error;

    /* if Module Player interrupt is running, remove it: */
    if ( mpInterrupt )
    {
        if ( (error = MP->RemoveInterrupt()) != OK )
            midasUninitError(errorMsg[error]);
        mpInterrupt = 0;
    }

    /* if Module Player is playing, stop it: */
    if ( mpPlay )
    {
        if ( (error = MP->StopModule()) != OK )
            midasUninitError(errorMsg[error]);
        mpPlay = 0;
    }

    /* if Module Player has been initialized, uninitialize it: */
    if ( mpInit )
    {
        if ( (error = MP->Close()) != OK )
            midasUninitError(errorMsg[error]);
        mpInit = 0;
        MP = NULL;
    }

#ifdef REALVUMETERS
    /* if real VU-meters have been initialized, uninitialize them: */
    if ( vuInitialized )
    {
        if ( (error = vuClose()) != OK )
            midasUninitError(errorMsg[error]);
        vuInitialized = 0;
    }
#endif

    /* if Sound Device channels are open, close them: */
    if ( sdChOpen )
    {
        if ( (error = SD->CloseChannels()) != OK )
            midasUninitError(errorMsg[error]);
        sdChOpen = 0;
    }

    /* if Sound Device is initialized, uninitialize it: */
    if ( sdInitialized )
    {
        if ( (error = SD->Close()) != OK )
            midasUninitError(errorMsg[error]);
        sdInitialized = 0;
        SD = NULL;
    }

    /* if TempoTimer is initialized, uninitialize it: */
    if ( tmrInitialized )
    {
        if ( (error = tmrClose()) != OK )
            midasUninitError(errorMsg[error]);
        tmrInitialized = 0;
    }

    /* if EMS Heap Manager is initialized, uninitialize it: */
    if ( emsInitialized )
    {
        if ( (error = emsClose()) != OK )
            midasUninitError(errorMsg[error]);
        emsInitialized = 0;
    }
}




/****************************************************************************\
*
* Function:     void midasSetDefaults(void)
*
* Description:  Initializes MIDAS Sound System variables to their default
*               states. MUST be the first MIDAS function called.
*
\****************************************************************************/

void midasSetDefaults(void)
{
    emsInitialized = 0;                 /* EMS heap manager is not
                                           initialized yet */
    tmrInitialized = 0;                 /* TempoTimer is not initialized */
    sdInitialized = 0;                  /* Sound Device is not initialized */
    sdChOpen = 0;                       /* Sound Device channels are not
                                           open */
    vuInitialized = 0;                  /* VU meter are not initialized */
    mpInit = 0;                         /* Module Player is not initialized */
    mpPlay = 0;                         /* Module Player is not playing */
    mpInterrupt = 0;                    /* No Module Player interrupt */


    ptTempo = 1;                        /* enable ProTracker BPM tempos */
    usePanning = 1;                     /* enable ProTracker panning cmds */
    surround = 0;                       /* disable surround to save GUS mem */
    realVU = 1;                         /* enable real VU-meters */

    disableEMS = 0;                     /* do not disable EMS usage */
    sdNum = 0x0FFFF;                    /* no Sound Device forced */
    ioPort = 0xFFFF;                    /* no I/O port forced */
    IRQ = 0xFF;                         /* no IRQ number forced */
    DMA = 0xFF;                         /* no DMA channel number forced */
    mode = 0;                           /* no output mode forced */
    mixRate = 44100;                    /* attempt to use 44100Hz mixing
                                           rate */

    SD = NULL;                          /* point SD and MP to NULL for */
    MP = NULL;                          /* safety */
}



/****************************************************************************\
*
* Function:     void midasParseOption(char *option)
*
* Description:  Parses one MIDAS command line option.
*
* Input:        char *option            Command line option string WITHOUT
*                                       the leading '-' or '/'.
*
* Recognized options:
*       -sx     Force Sound Device x (1 = GUS, 2 = PAS, 3 = WSS, 4 = SB,
*               5 = No Sound)
*       -pxxx   Force I/O port xxx (hex) for Sound Device
*       -ix     Force IRQ x for Sound Device
*       -dx     Force DMA channel x for Sound Device
*       -mxxxx  Set mixing rate to xxxx Hz
*       -oxxx   Force output mode (8 = 8-bit, 1 = 16-bit, s = stereo,
*               m = mono)
*       -e      Disable EMS usage
*       -t      Disable ProTracker BPM tempos
*       -u      Enable Surround sound
*       -v      Disable real VU-meters
*
\****************************************************************************/

void midasParseOption(char *option)
{
    int         c;
    char        *opt;

    opt = &option[1];
    switch ( option[0] )
    {
        /* -sx     Force Sound Device x */
        case 's':
            sdNum = atoi(opt) - 1;
            if ( sdNum >= NUMSDEVICES )
                midasError("Illegal Sound Device number");
            break;

        /* -pxxx   Force I/O port xxx (hex) for Sound Device */
        case 'p':
            sscanf(opt, "%X", &ioPort);
            break;

        /* -ix     Force IRQ x for Sound Device */
        case 'i':
            IRQ = atoi(opt);
            break;

        /* -dx     Force DMA channel x for Sound Device */
        case 'd':
            DMA = atoi(opt);
            break;

        /* -mxxxx  Set mixing rate to xxxx Hz */
        case 'm':
            mixRate = atol(opt);
            if ( mixRate < 1 )
                midasError("Invalid mixing rate");
            break;

        /* -e      Disable EMS usage */
        case 'e':
            disableEMS = 1;
            break;

        /* -t      Disable ProTracker BPM tempos */
        case 't':
            ptTempo = 0;
            break;

        /* -u      Enable Surround sound */
        case 'u':
            surround = 1;
            break;

        /* -oxxx   Force output mode */
        case 'o':
            for ( c = 0; c < strlen(opt); c++ )
            {
                switch( opt[c] )
                {
                    /* Output mode '8' - 8-bit */
                    case '8':
                        mode |= sd8bit;
                        mode &= 0xFFFF ^ sd16bit;
                        break;

                    /* Output mode '1' - 16-bit */
                    case '1':
                        mode |= sd16bit;
                        mode &= 0xFFFF ^ sd8bit;
                        break;

                    /* Output mode 'm' - mono */
                    case 'm':
                        mode |= sdMono;
                        mode &= 0xFFFF ^ sdStereo;
                        break;

                    /* Output mode 's' - stereo */
                    case 's':
                        mode |= sdStereo;
                        mode &= 0xFFFF ^ sdMono;
                        break;

                    default:
                        midasError("Invalid output mode character");
                        break;
                }
            }
            break;

        /* -v      Disable real VU-meters */
        case 'v':
            realVU = 0;
            break;

        default:
            midasError("Unknown option character");
            break;
    }
}




/****************************************************************************\
*
* Function:     void midasParseOptions(int optCount, char **options)
*
* Description:  Parses MIDAS command line options and sets MIDAS variables
*               accordingly.
*
* Input:        int optCount            Number of options
*               char **options          Pointer to an array of pointers to
*                                       option strings.
*
* Also '/' is recognized as a option delimiter.
*
\****************************************************************************/

void midasParseOptions(int optCount, char **options)
{
    int         i;

    for ( i = 0; i < optCount; i++ )
    {
        if ( ( options[i][0] == '-' ) || ( options[i][0] == '/' )  )
            midasParseOption(&options[i][1]);
        else
            midasError("Invalid command line option");
    }
}




/****************************************************************************\
*
* Function:     void midasParseEnvironment(void)
*
* Description:  Parses the MIDAS environment string, which has same format
*               as the command line options.
*
\****************************************************************************/

void midasParseEnvironment(void)
{
    char        *envs, *midasenv, *opt;
    int         spos, slen, stopparse, error;

    /* try to get pointer to MIDAS environment string: */
    envs = getenv("MIDAS");

    if ( envs != NULL )
    {
        slen = strlen(envs);
        /* allocate memory for a copy of the environment string: */
        if ( (error = memAlloc(slen+1, (void**) &midasenv)) != OK )
            midasError(errorMsg[error]);

        /* copy environment string to midasenv: */
        strcpy(midasenv, envs);

        spos = 0;                       /* search position = 0 */
        opt = NULL;                     /* current option string = NULL */
        stopparse = 0;

        /* parse the whole environment string: */
        while ( !stopparse )
        {
            switch ( midasenv[spos] )
            {
                case ' ':
                    /* current character is space - change it to '\0' and
                       parse this option string if it exists*/
                    midasenv[spos] = 0;
                    if ( opt != NULL )
                        midasParseOption(opt);

                    opt = NULL;         /* no option string */
                    spos++;             /* next character */
                    break;

                case 0:
                    /* Current character is '\0' - end. Parse option string
                       if it exists and stop parsing. */
                    if ( (opt != NULL) && (*opt != 0) )
                        midasParseOption(opt);
                    stopparse = 1;
                    break;

                case '-':
                case '/':
                    /* Current character is '-' or '/' - option string starts
                       from next character */
                    spos++;
                    opt = &midasenv[spos];
                    break;

                default:
                    /* some normal character - continue parsing from next
                       character */
                    spos++;
            }
        }

        if ( (error = memFree(midasenv)) != OK )
            midasError(errorMsg[error]);
    }
}



/****************************************************************************\
*
* Function:     mpModule *midasPlayModule(char *fileName, int numEffectChns)
*
* Description:  Loads a module into memory, points MP to the correct Module
*               Player and starts playing it.
*
* Input:        char *fileName          Pointer to module file name
*               int numEffectChns       Number of channels to open for sound
*                                       effects.
*
* 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("TUNE.MOD", 3), you can use channels 0-2 for
*               sound effects.
*
\****************************************************************************/

mpModule *midasPlayModule(char *fileName, int numEffectChns)
{
    uchar       *header;
    fileHandle  f;
    mpModule    *module;
    short       numChans;
    int         error, mpNum, recognized;

    if ( (error = memAlloc(MPHDRSIZE, (void**) &header)) != OK )
        midasError(errorMsg[error]);

    if ( (error = fileOpen(fileName, fileOpenRead, &f)) != OK )
        midasError(errorMsg[error]);

    /* read MPHDRSIZE bytes of module header: */
    if ( (error = fileRead(f, header, MPHDRSIZE)) != OK )
        midasError(errorMsg[error]);

    if ( (error = fileClose(f)) != OK )
        midasError(errorMsg[error]);

    /* Search through all Module Players to find one that recognizes
       file header: */
    mpNum = 0; MP = NULL;
    while ( (mpNum < NUMMPLAYERS) && (MP == NULL) )
    {
        if ( (error = midasModulePlayers[mpNum]->Identify(header,
            &recognized)) != OK )
            midasError(errorMsg[error]);
        if ( recognized )
        {
            MP = midasModulePlayers[mpNum];
            ALE = midasMPALE[mpNum];
        }
        mpNum++;
    }

    if ( MP == NULL )
        midasError("Unknown module format");

    /* deallocate module header: */
    if ( (error = memFree(header)) != OK )
        midasError(errorMsg[error]);

    /* initialize module player: */
    if ( (error = MP->Init(SD)) != OK )
        midasError(errorMsg[error]);
    mpInit = 1;

    /* load module: */
    if ( (error = MP->LoadModule(fileName, SD, (mpModule**) &module)) != OK )
        midasError(errorMsg[error]);

    numChans = module->numChans;

    /* open Sound Device channels: */
    if ( (error = SD->OpenChannels(numChans + numEffectChns)) != OK )
        midasError(errorMsg[error]);
    sdChOpen = 1;

    /* Start playing the module using Sound Device channels (numEffectChns) -
       (numEffectChns+numChans-1) and looping the whole song: */
    if ( (error = MP->PlayModule(module, numEffectChns, numChans, 0, 32767))
        != OK )
        midasError(errorMsg[error]);
    mpPlay = 1;

    /* start playing using the timer: */
    if ( (error = MP->SetInterrupt()) != OK )
        midasError(errorMsg[error]);

    return module;
}




/****************************************************************************\
*
* Function:     void midasStopModule(mpModule *module)
*
* Description:  Stops playing a module, deallocates it and uninitializes
*               the Module Player. Also closes _all_ Sound Device channels,
*               including those opened for effects.
*
\****************************************************************************/

void midasStopModule(mpModule *module)
{
    int         error;

    /* remove Module Player interrupt: */
    if ( (error = MP->RemoveInterrupt()) != OK )
        midasError(errorMsg[error]);
    mpInterrupt = 0;

    /* stop playing the module: */
    if ( (error = MP->StopModule()) != OK )
        midasError(errorMsg[error]);
    mpPlay = 0;

    /* deallocate module: */
    if ( (error = MP->FreeModule(module, SD)) != OK )
        midasError(errorMsg[error]);

    /* uninitialize Module Player: */
    if ( (error = MP->Close()) != OK )
        midasError(errorMsg[error]);
    mpInit = 0;
    MP = NULL;                          /* point MP to NULL for safety */

    /* close Sound Device channels: */
    if ( (error = SD->CloseChannels()) != OK )
        midasError(errorMsg[error]);
    sdChOpen = 0;
}
[ RETURN TO DIRECTORY ]