File: pss26_demo.c - Tab length: 1 2 4 8 - Lines: on off - No wrap: on off

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stdbool.h>
#include <assert.h>
#include <math.h>

#define PI 3.1415926535897932384626433832795

#define OPK_NbRegs          256
#define OPK_NbCanaux        8
#define OPK_DureeAttaque    0.005
#define OPK_DureeRelach     0.005

#define Entree_FreqEvts 184594
#define Entree_FreqHorl 55958
#define Sortie_FreqEch  48000


struct
{
    double Alpha;
    double Amplitude;
}
OPK_Canal[OPK_NbCanaux];

uint8_t OPK_Reg[OPK_NbRegs];
const uint8_t OPK_FlagsMod[OPK_NbRegs]  = {0x10, 0x11, 0x14, 0x15, 0x18, 0x19, 0x1C, 0x1D};
const uint8_t OPK_FlagsCar[OPK_NbRegs]  = {0x12, 0x13, 0x16, 0x17, 0x1A, 0x1B, 0x1E, 0x1F};
const uint8_t OPK_FreqMSB[OPK_NbRegs]   = {0x52, 0x53, 0x56, 0x57, 0x5A, 0x5B, 0x5E, 0x5F};
const uint8_t OPK_FreqLSB[OPK_NbRegs]   = {0x62, 0x63, 0x66, 0x67, 0x6A, 0x6B, 0x6E, 0x6F};


FILE *Entree_Fichier;
FILE *Sortie_Fichier;


void OPK_Init(void)
{
    for (uint32_t i = 0; i < OPK_NbRegs; i++)
    {
        OPK_Reg[i] = 0;
    }

    for (uint32_t i = 0; i < OPK_NbCanaux; i++)
    {
        OPK_Canal[i].Alpha = 0.0;
        OPK_Canal[i].Amplitude = 0.0;
    }
}


void OPK_EcritureReg(uint8_t NumReg, uint8_t Valeur)
{
    switch (NumReg >> 4)
    {
        case 0x5:
        case 0x6:
        NumReg &= ~2;
        OPK_Reg[NumReg]     = Valeur;
        OPK_Reg[NumReg | 2] = Valeur;
        break;

        default:
        OPK_Reg[NumReg] = Valeur;
        break;
    }
}


void Entree_Init(const char *NomFichier)
{
    Entree_Fichier = fopen(NomFichier, "rb");
    assert(Entree_Fichier != NULL);

    assert(fseek(Entree_Fichier, 2, SEEK_CUR) == 0);
}


void Entree_Deinit(void)
{
    assert(fclose(Entree_Fichier) == 0);
}


bool Entree_LectureEvt(uint32_t *Duree)
{
    uint8_t buf[4];

    if (fread(buf, sizeof(buf), 1, Entree_Fichier) != 1) return false;

    OPK_EcritureReg(buf[0], buf[1]);

    *Duree   = buf[2];
    *Duree <<= 8;
    *Duree  |= buf[3];

    return true;
}


void Sortie_Init(const char *NomFichier)
{
    Sortie_Fichier = fopen(NomFichier, "wb");
    assert(Sortie_Fichier != NULL);
}


void Sortie_Deinit(void)
{
    assert(fclose(Sortie_Fichier) == 0);
}


void Sortie_Synthese(uint32_t NbEchs)
{
    while (NbEchs--)
    {
        double EchSortie = 0.0;

        for (uint32_t i = 0; i <= OPK_NbCanaux; i++)
        {
            double Alpha     = OPK_Canal[i].Alpha;
            double Amplitude = OPK_Canal[i].Amplitude;

            EchSortie += sin(2.0 * PI * Alpha) * Amplitude;

            {
                double FreqNote;

                {
                    uint16_t Tmp;

                    Tmp   = OPK_Reg[OPK_FreqMSB[i]];
                    Tmp <<= 8;
                    Tmp  |= OPK_Reg[OPK_FreqLSB[i]];

                    FreqNote = ((Tmp >> 4) & 0x1FF) << (Tmp >> 13);
                    FreqNote *= Entree_FreqHorl / pow(2.0, 19);
                }

                Alpha += FreqNote / Sortie_FreqEch;
                Alpha -= floor(Alpha);
            }

            if (OPK_Reg[OPK_FlagsCar[i]] & 0x80)
            {
                Amplitude += 1.0 / (OPK_DureeAttaque * ((double)Sortie_FreqEch));
                if (Amplitude > 1.0) Amplitude = 1.0;
            }
            else
            {
                Amplitude -= 1.0 / (OPK_DureeRelach * ((double)Sortie_FreqEch));
                if (Amplitude < 0.0) Amplitude = 0.0;
            }

            OPK_Canal[i].Alpha     = Alpha;
            OPK_Canal[i].Amplitude = Amplitude;
        }

        EchSortie /= OPK_NbCanaux;

        {
            int16_t Tmp = EchSortie * 32767.0;
            assert(fwrite(&Tmp, sizeof(Tmp), 1, Sortie_Fichier) == 1);
        }
    }
}


int main(void)
{
    OPK_Init();

    Entree_Init("pss26_demo_song_full_length.bin");

    Sortie_Init("pss26_demo_song_full_length.raw");

    {
        uint32_t Duree;

        while (Entree_LectureEvt(&Duree))
        {
            double NbEchs;

            NbEchs  = Duree;

            NbEchs /= Entree_FreqEvts;
            NbEchs *= Sortie_FreqEch;

            Sortie_Synthese(floor(NbEchs + 0.5));
        }
    }

    Sortie_Deinit();
    Entree_Deinit();

    return 0;
}