BABYL OPTIONS:
Version: 5
Labels:
Note:   This is the header of an rmail file.
Note:   If you are seeing it in rmail,
Note:    it means the file has no messages in it.

0, unseen,,
*** EOOH ***
Newsgroups: rec.games.programmer
From: [email protected] (K. Lee)
Subject: ADLIB CODE and a request for help...
Organization: University of East Anglia
Date: Fri, 30 Apr 1993 11:50:54 GMT


    Greetings fellow NETFIENDS, I've just been messing with the Adlib FM
chips, coxing them into making some simple sounds. I've got some routines
working and these are enclosed below. However they are not brilliant and
I'm not sure if they work correctly. I would appreciate it if all you
"experts" would check out the code and tell me where I'm going wrong or
right. These routines were intended to be the start of some more complex
routines to play complete tunes in some standard format..say MIDI, ROL or
CMF. However I'm not really sure how to go about this..suggestions please.
What I would like is some sort of mechanism to play tunes in the background.
This is probably best done by attaching some code to an interrupt (usually
the timer), where the code reads the notes from a queue. So get cracking
on the code and post what you come up with on the network.

P.S. I know there are several commercial programs to play tunes in the
background, however the cost of these routines is ridiculous. We should
be able to get some routines up and running, and available to everyone
for free...

Have fun...

ooo     oo oooo   o           /  WEIRD BUT TRUE
 o    oo  o    o  o          /   ----------------------------------------
 o  oo   oooooooo o         /    "In 1979, Mr Liy Ming fell asleep in a
 ooo     o      o o        /   traffic jam in Tokyo and woke up two days
 o  oo   o      o oooooo  /    later in exactly the same place."
 o    oo     ------------/ ----------------------------------------------
 o      oo     Kevin A. Lee -- " I'm really mad, but I'm not mad really! "
ooo       oo   E-mail: [email protected]

----CUT HERE----------------------------------------------------------------
/* C Header File: ADLIB *****************************************************

        Author:             Kevin A. Lee

        Last Amended:       27th April, 1993

        Description:        Low-level interface to the Adlib (or compatible)
                            FM sound card. All information gleaned from
                            Jeffrey S. Lee's "Programming the Adlib/Sound
                            Blaster FM Music Chips". See Lee's document for
                            further information.
                            Compiled succesfully under Turbo C, Borland C++,
                            and Microsoft Quick C (all latest versions).

****************************************************************************/

#define MIN_REGISTER        0x01
#define MAX_REGISTER        0xF5
#define ADLIB_FM_ADDRESS    0x388       /* adlib address/status register */
#define ADLIB_FM_DATA       0x389       /* adlib data register           */

#ifndef BYTE
#define BYTE unsigned char
#endif


/*
 * FM Instrument definition for .SBI files - SoundBlaster instrument
 * - these are the important parts - we will skip the header, but since
 *   I am not sure where it starts and ends so I have had to guess.
 *   However it SEEMS! to work. Each array has two values, one for
 *   each operator.
 */
typedef struct
{
    BYTE SoundCharacteristic[2];    /* modulator frequency multiple...  */
    BYTE Level[2];                  /* modulator frequency level...     */
    BYTE AttackDecay[2];            /* modulator attack/decay...        */
    BYTE SustainRelease[2];         /* modulator sustain/release...     */
    BYTE WaveSelect[2];             /* output waveform distortion       */
    BYTE Feedback;                  /* feedback algorithm and strength  */
} FMInstrument;


/*
 * Enumerated F-Numbers (in octave 4) for the chromatic scale.
 */
enum SCALE
{
    D4b = 0x16B,    D4  = 0x181,    E4b = 0x198,    E4  = 0x1B0,
    F4  = 0x1CA,    G4b = 0x1E5,    G4  = 0x202,    A4b = 0x220,
    A4  = 0x241,    B4b = 0x263,    B4  = 0x287,    C4  = 0x2AE
};


/* function prototyping */
void WriteFM(int reg, int value);
int  ReadFM(void);
int  AdlibExists(void);
void FMReset(void);
void FMKeyOff(int voice);
void FMKeyOn(int voice, int freq, int octave);
void FMVoiceVolume(int voice, int vol);
void FMSetVoice(int voiceNum, FMInstrument *ins);
int  LoadSBI(char filename[], FMInstrument *ins);

----CUT HERE-----------------------------------------------------------------
/* C Source File: ADLIB *****************************************************

        Author:             Kevin A. Lee

        Last Amended:       27th March, 1993

        Description:        Low-level interface to the Adlib (or compatible)
                            FM sound card. All information gleaned from
                            Jeffrey S. Lee's "Programming the Adlib/Sound
                            Blaster FM Music Chips". See Lee's document for
                            further information.
                            Compiled succesfully under Turbo C, Borland C++,
                            and Microsoft Quick C (all latest versions).

****************************************************************************/

#include 
#include 
#include 
#include 
#include "adlib.h"

/* comment out this line if you don't want to test the routines */
#define TEST

/* local function */
void Wait(clock_t wait);



/* Function: WriteFM ********************************************************
 *
 *      Parameters:         reg - which FM register to write to.
 *                          value - value to write.
 *
 *      Description:        writes a value to the specified register and
 *                          waits for the "official" recommended periods.
 *
 */
void WriteFM(int reg, int value)
{
int i;

    outp(ADLIB_FM_ADDRESS, (BYTE)reg);              /* set up the register  */
    for (i = 0; i < 6; i++) inp(ADLIB_FM_ADDRESS);  /* wait 12 cycles       */
    outp(ADLIB_FM_DATA, (BYTE)value);               /* write out the value  */
    for (i = 0; i < 35; i++) inp(ADLIB_FM_ADDRESS); /* wait 84 cycles       */
} /* End of WriteFM */



/* Function: ReadFM *********************************************************
 *
 *      Returns:            the value in the status register.
 *
 *      Description:        return a value in the status register.
 *
 */
int ReadFM(void)
{
    return (inp(ADLIB_FM_ADDRESS));
} /* End of ReadFM */



/* Function: AdlibExists ****************************************************
 *
 *      Returns:            1 (true) if an Adlib compatible sound card
 *                          is present, else 0 (false).
 *
 *      Description:        determines whether an Adlib (or compatible)
 *                          sound card is present.
 *
 */
int AdlibExists(void)
{
int stat1, stat2;

    WriteFM(0x04, 0x60);            /* reset both timers        */
    WriteFM(0x04, 0x80);            /* enable timer interrupts  */
    stat1 = ReadFM();               /* read status register     */
    WriteFM(0x02, 0xFF);
    WriteFM(0x04, 0x21);            /* start timer 1            */
    Wait(80);                       /* could do something useful*/
    stat2 = ReadFM();               /* read status register     */
    WriteFM(0x04, 0x60);            /* reset both timers        */
    WriteFM(0x04, 0x80);            /* enable timer interrupts  */

    if (((stat1 & 0xE0) == 0x00) && ((stat2 & 0xE0) == 0xC0)) return (1);
    return (0);
} /* End of AdlibExists */



/* Function: FMResest *******************************************************
 *
 *      Description:        quick and dirty sound card reset (zeros all
 *                          registers).
 *
 */
void FMReset(void)
{
int i;

    /* zero all registers */
    for (i = MIN_REGISTER; i < MAX_REGISTER+1; i++) WriteFM(i, 0);

    /* allow FM chips to control the waveform of each operator */
    WriteFM(0x01, 0x20);

    /* set rhythm enabled (6 melodic voices, 5 percussive) */
    WriteFM(0xBD, 0x20);
} /* End of FMReset */



/* Function: FMKeyOff *******************************************************
 *
 *      Parameters:         voice - which voice to turn off.
 *
 *      Description:        turns off the specified voice.
 *
 */
void FMKeyOff(int voice)
{
int regNum;

    /* turn voice off */
    regNum = 0xB0 + voice % 11;
    WriteFM(regNum, 0);
} /* End of FMKeyOff */



/* Function: FMKeyOff *******************************************************
 *
 *      Parameters:         voice - which voice to turn on.
 *                          freq - its frequency (note).
 *                          octave - its octave.
 *
 *      Description:        turns on a voice of specfied frequency and
 *                          octave.
 *
 */
void FMKeyOn(int voice, int freq, int octave)
{
int regNum, tmp;

    regNum = 0xA0 + voice % 11;
    WriteFM(regNum, freq & 0xff);
    regNum = 0xB0 + voice % 11;
    tmp = (freq >> 8) | (octave << 2) | 0x20;
    WriteFM(regNum, tmp);
} /* End of FMKeyOn */


/* Function: FMVoiceVolume **************************************************
 *
 *      Parameters:         voice - which voice to set volume of
 *                          vol - new volume value (experiment).
 *
 *      Description:        sets the volume of a voice to the specified
 *                          value in the range (0-63)?
 *
 */
void FMVoiceVolume(int voice, int vol)
{
int regNum;

    regNum = 0x40 + voice % 11;
    WriteFM(regNum, vol);
} /* End of FMVoiceVolume */



/* Function: FMSetVoice *****************************************************
 *
 *      Parameters:         voiceNum - which voice to set.
 *                          ins - instrument to set voice.
 *
 *      Description:        sets the instrument of a voice.
 *
 */
void FMSetVoice(int voiceNum, FMInstrument *ins)
{
int opCellNum, cellOffset, i;

    voiceNum %= 11;
    cellOffset = voiceNum % 3 + ((voiceNum / 3) << 3);

    /* set sound characteristic */
    opCellNum = 0x20 + (char)cellOffset;
    WriteFM(opCellNum, ins->SoundCharacteristic[0]);
    opCellNum += 3;
    WriteFM(opCellNum, ins->SoundCharacteristic[1]);

    /* set level/output */
    opCellNum = 0x40 + (char)cellOffset;
    WriteFM(opCellNum, ins->Level[0]);
    opCellNum += 3;
    WriteFM(opCellNum, ins->Level[1]);

    /* set Attack/Decay */
    opCellNum = 0x60 + (char)cellOffset;
    WriteFM(opCellNum, ins->AttackDecay[0]);
    opCellNum += 3;
    WriteFM(opCellNum, ins->AttackDecay[1]);

    /* set Sustain/Release */
    opCellNum = 0x80 + (char)cellOffset;
    WriteFM(opCellNum, ins->SustainRelease[0]);
    opCellNum += 3;
    WriteFM(opCellNum, ins->SustainRelease[1]);

    /* set Wave Select */
    opCellNum = 0xE0 + (char)cellOffset;
    WriteFM(opCellNum, ins->WaveSelect[0]);
    opCellNum += 3;
    WriteFM(opCellNum, ins->WaveSelect[1]);

    /* set Feedback/Selectivity */
    opCellNum = (BYTE)0xC0 + (BYTE)voiceNum;
    WriteFM(opCellNum, ins->Feedback);
} /* End of FMSetVoice */



/* Function: LoadSBI ********************************************************
 *
 *      Parameters:         fileName - name of .SBI file.
 *                          ins - variable to place data in.
 *
 *      Description:        loads a .SBI into the instrument structure.
 *
 */
int LoadSBI(char fileName[], FMInstrument *ins)
{
int i;
FILE *fp;
size_t structSize = sizeof(FMInstrument);

    if ((fp = fopen(fileName, "rb")) == NULL) return (0);

    /* skip the header - or do we? */
    for (i = 0; i < 36; i++) fgetc(fp);

    /* read the data */
    fread(ins, structSize, 1, fp);

    fclose(fp);
    return (1);
} /* End of LoadSBI */



/* Function: Wait **********************************************************
 *
 *      Parameters:     wait - time in microseconds
 *
 *      Description:    pauses for a specified number of microseconds.
 *
 */
void Wait(clock_t wait)
{
clock_t goal;

    if (!wait) return;

    goal = wait + clock();
    while ((goal > clock()) && !kbhit()) ;
} /* End of Wait */



/* test of the routines */
#ifdef TEST
main()
{
enum SCALE test[] = { D4, E4, F4, G4, A4, B4, C4 };
static FMInstrument testInst =
{
    0x21, 0x31, 0x4F, 0x00,
    0xF1, 0xF2, 0x11, 0x11,
    0x00, 0x00, 0x06
};
int i;

    printf("\nChecking for Adlib sound card...");
    if (!AdlibExists())
    {
        printf("not found.\n.");
        exit(1);
    }
    printf("found.\n");
    printf("Now playing tune...\n");

    FMReset();
    FMSetVoice(0, &testInst);
    for (i = 0; i < 7; i++)
    {
        FMKeyOn(0, test[i], 4);
        Wait(25);
        FMKeyOff(0);
        Wait(1);
    }
    FMReset();
}
#endif
----CUT HERE-----------------------------------------------------------------