#define delay(x) Sleep(x) const //List of FNumbers uint16_t FNr[12] = { /*C*/ 343, /*C#/Db*/ 363, /*D*/ 385, /*D#/Eb*/ 408, /*E*/ 432, /*F*/ 458, /*F#/Gb*/ 485, /*G*/ 514, /*G#/Ab*/ 544, /*A*/ 577, /*A#/Bb*/ 611, /*B*/ 647 }; //Flat Whole Sharp} #define C 0 #define CSharp 1 #define DFlat 1 #define D 2 #define DSharp 3 #define EFlat 3 #define E 4 #define F 5 #define FSharp 6 #define GFlat 6 #define G 7 #define GSharp 8 #define AFlat 8 #define A 9 #define ASharp 10 #define BFlat 10 #define B 11 struct Operator { uint8_t //Tremolo Tremolo, //Vibrato Vibrato, //Envelop generator type EGType, //KSR KSR, //Frequency multiplier Multi, //Key scaling level KSL, //Volume TotalLevel, //Shape of the wave envelop Attack, Decay, Sustain, Release, //Type of wave WaveShape; }; struct Instrument { struct Operator Modulator, Carrier; uint8_t //Feedback strength Feedback, //Synthesis type SynType; }; struct Instrument Flute, Piano, Harp; uint8_t CurNote, CurBlock; /************************************************************************** ** Writes a value to a specified index register on the FM card ** **************************************************************************/ void WriteFM(uint8_t Register, uint8_t Value) { OPL3Write(0, Register); OPL3Write(1, Value); // unsigned char Counter; //Select register // outportb(0x388, Register); //Wait for card to accept value // for (Counter = 1; Counter < 25; Counter++) { inportb(0x388); } //Send value // outportb(0x389, Value); //Wait for card to accept value // for (Counter = 1; Counter < 100; Counter++) { inportb(0x388); } } /************************************************************************** ** Checks for the presence of an FM card ** **************************************************************************/ uint8_t FMInstalled() { // unsigned char A, B; // WriteFM(1, 0); // WriteFM(4, 0x60); // WriteFM(4, 0x80); // A = inportb(0x388); // A = OPL3Read(0); // WriteFM(2, 0xFF); // WriteFM(4, 0x21); // B = inportb(0x388); // OPL3TimerOver(0); // B = OPL3Read(0); // WriteFM(4, 0x60); // WriteFM(4, 0x80); // if ((A & 0xE0) == 0 && (B & 0xE0) == 0xC0) { return (1); // } // else { // return (0); // } } /************************************************************************** ** Activates a voice on the FM card ** *************************************************************************** ** Voice selects one of the 9 FM voices ** ** FNumber selects the note to be played ** ** Block selects the octave for the specified note ** **************************************************************************/ void NoteOn(uint8_t Voice, uint8_t Note, uint8_t Block) { WriteFM(0xA0 + Voice, FNr[Note] & 0xFF); WriteFM(0xB0 + Voice, (FNr[Note] >> 8) + (Block << 2) + 32); } /************************************************************************** ** Deactivates a voice on the FM card ** *************************************************************************** ** Make sure to give the same values for Note and Block or this will ** ** sound very odd. ** **************************************************************************/ void NoteOff(uint8_t Voice, uint8_t Note, uint8_t Block) { WriteFM(0xA0 + Voice, FNr[Note] & 0xFF); WriteFM(0xB0 + Voice, (FNr[Note] >> 8) + (Block << 2)); } /************************************************************************** ** Sets instrument settings for a voice on the FM card ** **************************************************************************/ const static unsigned char OpAdr[9] = { 0, 1, 2, 8, 9, 10, 16, 17, 18 }; //Addresses of the operators used to form voice data void SetInstrument(uint8_t Voice, struct Instrument *Instr) { uint8_t Value; //Set up voice modulator Value = Instr->Modulator.Tremolo << 8 | Instr->Modulator.Vibrato << 7 | Instr->Modulator.EGType << 6 | Instr->Modulator.KSR << 5 | Instr->Modulator.Multi; WriteFM(0x20 + OpAdr[Voice], Value); Value = Instr->Modulator.KSL << 7 | Instr->Modulator.TotalLevel; WriteFM(0x40 + OpAdr[Voice], Value); Value = Instr->Modulator.Attack << 4 | Instr->Modulator.Decay; WriteFM(0x60 + OpAdr[Voice], Value); Value = Instr->Modulator.Sustain << 4 | Instr->Modulator.Release; WriteFM(0x80 + OpAdr[Voice], Value); WriteFM(0xE0 + OpAdr[Voice], Instr->Modulator.WaveShape); Value = Instr->Feedback << 1 | Instr->SynType; WriteFM(0xC0 + OpAdr[Voice], Value); //Set up voice carrier Value = Instr->Carrier.Tremolo << 8 | Instr->Carrier.Vibrato << 7 | Instr->Carrier.EGType << 6 | Instr->Carrier.KSR << 5 | Instr->Carrier.Multi; WriteFM(0x23 + OpAdr[Voice], Value); Value = Instr->Carrier.KSL << 7 | Instr->Carrier.TotalLevel; WriteFM(0x43 + OpAdr[Voice], Value); Value = Instr->Carrier.Attack << 4 | Instr->Carrier.Decay; WriteFM(0x63 + OpAdr[Voice], Value); Value = Instr->Carrier.Sustain << 4 | Instr->Carrier.Release; WriteFM(0x83 + OpAdr[Voice], Value); WriteFM(0xE3 + OpAdr[Voice], Instr->Carrier.WaveShape); } void test() { // clrscr(); again: printf("Demonstration of programming a FM sound card [AdLib, SB and compatibles]\n"); printf("--------------------------------------------------------------------------\n"); //Check to see if an FM sound card is present /* if (FMInstalled()) { printf("AdLib/SB or compatible FM sound card found.\n"); } else { //If not, end the programme printf("AdLib/SB or compatible FM sound card not found.\n"); return; } */ //Set up voices 0 - 3 to a "harp" - note, only the values other than 0 are set Harp.Modulator.Attack = 15; Harp.Modulator.Decay = 5; Harp.Modulator.Sustain = 8; Harp.Modulator.Release = 5; Harp.Modulator.Multi = 2; Harp.Modulator.TotalLevel = 41; Harp.Carrier.Attack = 15; Harp.Carrier.Decay = 2; Harp.Carrier.Release = 3; Harp.Carrier.Multi = 1; Harp.Carrier.KSL = 2; Harp.Carrier.TotalLevel = 3; SetInstrument(0, &Harp); SetInstrument(1, &Harp); SetInstrument(2, &Harp); //Play C chord using Harp printf("Playing C chord.\n"); NoteOn(0, C, 4); NoteOn(1, E, 4); NoteOn(2, G, 4); delay(1000); //Wait 1 second NoteOff(0, C, 4); NoteOff(1, E, 4); NoteOff(2, G, 4); //Wait a while delay(500); //Set up voice 3 to a "piano" - this doesn't really sound like a piano, //but that's what they call it! Piano.Modulator.Attack = 15; Piano.Modulator.Decay = 1; Piano.Modulator.Sustain = 10; Piano.Modulator.Release = 3; Piano.Modulator.Multi = 1; Piano.Modulator.KSL = 1; Piano.Modulator.TotalLevel = 16; Piano.Carrier.Attack = 13; Piano.Carrier.Decay = 2; Piano.Carrier.Sustain = 8; Piano.Carrier.Release = 4; Piano.Carrier.Multi = 1; Piano.Carrier.TotalLevel = 0; Piano.Carrier.KSR = 1; Piano.Feedback = 3; SetInstrument(3, &Piano); //Play all notes in octave 4 for .1 seconds using piano [voice 3] printf("Playing all notes in octave four.\n"); CurBlock = 4; for (CurNote = 0; CurNote < 12; CurNote++) { NoteOn(3, CurNote, CurBlock); delay(100); //Wait .1 seconds NoteOff(3, CurNote, CurBlock); } //Wait a while delay(500); //Set up voice 4 to a "flute" Flute.Modulator.Attack = 6; Flute.Modulator.Decay = 14; Flute.Modulator.Sustain = 7; Flute.Modulator.Release = 15; Flute.Modulator.Vibrato = 1; Flute.Modulator.Tremolo = 1; Flute.Modulator.EGType = 1; Flute.Modulator.KSL = 3; Flute.Modulator.TotalLevel = 44; Flute.Carrier.Attack = 6; Flute.Carrier.Decay = 5; Flute.Carrier.Sustain = 13; Flute.Carrier.Release = 10; Flute.Carrier.Vibrato = 1; Flute.Carrier.EGType = 1; Flute.Carrier.Multi = 1; Flute.Carrier.TotalLevel = 0; Flute.Feedback = 7; Flute.SynType = 3; SetInstrument(4, &Flute); //Play short tune using Flute [voice 4] as primary, Harp [voice 0] as secondary voice printf("Playing short tune.\n"); NoteOn(4, C, 5); NoteOn(0, C, 3); delay(250); NoteOff(4, C, 5); NoteOn(4, E, 5); delay(250); NoteOff(4, E, 5); NoteOff(0, C, 3); NoteOn(4, C, 5); NoteOn(0, G, 3); delay(250); NoteOff(4, C, 5); NoteOn(4, G, 4); delay(250); NoteOff(4, G, 4); NoteOff(0, G, 3); NoteOn(4, E, 4); NoteOn(0, C, 4); delay(250); NoteOff(4, E, 4); NoteOn(4, G, 4); delay(250); NoteOff(4, G, 4); NoteOff(0, C, 4); NoteOn(4, E, 4); NoteOn(0, G, 3); delay(250); NoteOff(4, E, 4); NoteOn(4, D, 4); delay(250); NoteOff(4, D, 4); NoteOff(0, G, 3); NoteOn(4, C, 4); NoteOn(0, C, 3); delay(2000); NoteOff(4, C, 4); NoteOff(0, C, 3); goto again; }