/* ======================================================================== */
/*  The routines and data in this file (notes.c) are dedicated to the       */
/*  public domain via the Creative Commons CC0 v1.0 license by its author,  */
/*  Joseph Zbiciak.                                                         */
/*                                                                          */
/*          https://creativecommons.org/publicdomain/zero/1.0/              */
/* ======================================================================== */

#include <stdio.h>
#include <math.h>

/* 0 = C1, 84 = C8 */
double note_freq(int note)
{
    double freq;

    note -= 45;     /* Make A4 = 0.  A4 is 440Hz */
    freq = 440.0 * pow(2.0, (double)note / 12.0);

    return freq;
}

double ntsc_period(double freq)
{
    return 3579545.0 / 32.0 / freq;
}

double pal_period(double freq)
{
    return 4000000.0 / 32.0 / freq;
}

const char *note_names[12] =
{
    "C%d",
    "C#%d / Db%d",
    "D%d",
    "D#%d / Eb%d",
    "E%d",
    "F%d",
    "F#%d / Gb%d",
    "G%d",
    "G#%d / Ab%d",
    "A%d",
    "A#%d / Bb%d",
    "B%d",
};

const char *note_name(int note)
{
    int octave;
    static char buf[10];

    if (note < 0)
    {
        buf[0] = 0;
        return buf;
    }

    octave = 1 + note / 12;
    note  %= 12;

    sprintf(buf, note_names[note], octave, octave);
    return buf;
}

const char *note_labels[12][2] =
{
    {   "C%d",  NULL    },
    {   "Cs%d","Db%d"   },
    {   "D%d",  NULL    },
    {   "Ds%d","Eb%d"   },
    {   "E%d",  NULL    },
    {   "F%d",  NULL    },
    {   "Fs%d","Gb%d"   },
    {   "G%d",  NULL    },
    {   "Gs%d","Ab%d"   },
    {   "A%d",  NULL    },
    {   "As%d","Bb%d"   },
    {   "B%d",  NULL    },
};

const char *note_label(int note, int sf)
{
    int octave;
    static char buf[10];

    if (note < 0 || sf < 0 || sf > 1)
        return NULL;

    octave = 1 + note / 12;
    note  %= 12;

    if (note_labels[note][sf] == NULL)
        return NULL;

    sprintf(buf, note_labels[note][sf], octave);
    return buf;
}


int main(void)
{
    int i, pi;
    double p, f, fr, err;
    const char *lbl, *name;

    printf(";; ======================================================"
            "================== ;;\n");
    printf(";;  This file was autogenerated.\n");
    printf(";;\n");
    printf(";;  The data in this file is dedicated to the public domain\n");
    printf(";;  via the Creative Commons CC0 v1.0 license by the author\n");
    printf(";;  of the program that generated this file.\n");
    printf(";;\n");
    printf(";;     https://creativecommons.org/publicdomain/zero/1.0/\n");
    printf(";;\n");
    printf(";; You can compare the computed frequencies versus the ideal\n");
    printf(";; values using this chart online:\n");
    printf(";;\n");
    printf(";;     http://www.phy.mtu.edu/~suits/notefreqs.html\n");
    printf(";; ======================================================"
            "================== ;;\n");
    printf("\n\n");

    printf("NOTES.NTSC  PROC\n");
    for (i = 0; i < 85; i++)
    {
        p  = ntsc_period(f = note_freq(i));
        pi = (int)floor(p + 0.5);
        fr = ntsc_period(pi);

        err = 100.0 * ((fr / f) - 1.0); /* relative error */

        if ((lbl = note_label(i, 1)) != NULL)
            printf("@@%s\n", lbl);

        lbl  = note_label(i, 0);
        name = note_name(i);

        printf(
            "@@%-4s      DECLE   $%.4X   ; %-10s %7.2f Hz (%+1.3f%% err)\n",
            lbl, pi, name, fr, err);
    }
    printf("            ENDP\n");

    printf("\n\n");

    printf("NOTES.PAL   PROC\n");
    for (i = 0; i < 85; i++)
    {
        p  = pal_period(f = note_freq(i));
        pi = (int)floor(p + 0.5);
        fr = pal_period(pi);

        err = 100.0 * ((fr / f) - 1.0); /* relative error */

        if ((lbl = note_label(i, 1)) != NULL)
            printf("@@%s\n", lbl);

        lbl  = note_label(i, 0);
        name = note_name(i);

        printf(
            "@@%-4s      DECLE   $%.4X   ; %-10s %7.2f Hz (%+1.3f%% err)\n",
            lbl, pi, name, fr, err);
    }
    printf("            ENDP\n");
    
    printf(";; End of note data\n");

    return 0;
}
