#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;
}
