Synth with C64 like sounds. Played on two piezo buzzers with a PS/2 keyboard. Implemented on FRDM-KL46Z

Dependencies:   PS2 TSI beep2 mbed

C64Synth.cpp

Committer:
alexanderh
Date:
2014-01-24
Revision:
4:0f73a5d06177
Parent:
3:2cd5dfcad0e6

File content as of revision 4:0f73a5d06177:


/*
*   C64Synth uses a PS/2 keyboard to play C64 arps and simple tones.
*   Implemented on a FRDM KL46Z board
*
*   The keyboard is tuned in the C scale as of right now
*
*   Author: Alexander Hentschel 2014
*/

/*
*       TODO LIST
*       
*       Implemept sharp (#) notes
*       Implement scale change
*
*
*
*/




#include "mbed.h"
#include "beep2.h"
#include "TSISensor.h"
#include "PS2Keyboard.h"
#include "stdio.h"
#include "keys.h"

DigitalOut myled1(LED1);
DigitalOut myled2(LED2);

Beep arpBuzzer(PTA5);
Beep toneBuzzer(PTA12);
PS2Keyboard ps2kb(PTC8,PTC9);
TSISensor tsi;

PS2Keyboard::keyboard_event_t evt_kb;

void keyboardSynth();
void playArp(char chord);
void playNote(Beep buz,char note,float dc,float len);
void playContNote(char note,int oct,float dc);

void getNotes(char note, char* point);
float getFq(char note, bool sharp);


float toneDc = 0.5;
char lastNote;

bool arpAttack = false;
float arpDc = 0.5;
float arpSpeed = 0.02;
float arpTempovar = 0.1;
float arpSpeed2;
float arpTime = 0.025;

float masterscale=1;

int main()
{
    printf("--Program Start--");
    myled2 = !myled2;
    keyboardSynth();
}


void keyboardSynth()
{
    int arpOn = 0;
    int toneOn =0;
    char keytone = ' ';
    char playtone = ' ';
    char playchor = ' ';
    char chor;
    char test;
    int octave;


    //PS/2 keyboard test
    while(1) {

        if (ps2kb.processing(&evt_kb)) {

            //DEBUG keyboard terminalprint
            //printf("[%d]:", evt_kb.type);
           //for (int i = 0; i < evt_kb.length; i++) {
            //    printf("%02x ", evt_kb.scancode[i]);
           // }
            //printf("\n");

            //KEY PRESS PART
            if (!evt_kb.type) {

                switch(evt_kb.scancode[0]) {

                        //FUNCTION KEYS
                    case Space_key:
                        arpOn = 0;
                        toneOn = 0;
                        break;

                        //MULTICODE KEYS
                    case Mod_fn:
                        switch(evt_kb.scancode[1]) {

                            case Up_key:
                                masterscale = masterscale*2;
                                break;
                                //Tune down 1 octave
                            case Down_key:
                                masterscale = masterscale/2;
                                break;
                                //Faster arp speed
                            case Pgup_key:
                                arpTime = arpTime/1.2;
                                break;
                                //Slower arp speed
                            case Pgdown_key:
                                arpTime = arpTime*1.2;
                                break;
                                //Brighter sound
                            case Left_key:
                                toneDc = 0.98;
                                break;
                                //Square wave
                            case Right_key:
                                toneDc = 0.5;
                                break;
                                //Arp Attack
                            case End_key:
                                if (!arpAttack) {
                                    arpDc = 0;
                                    arpSpeed = 0.02;
                                } else {
                                    arpDc = 0.5;
                                }
                                arpAttack = !arpAttack;
                                break;
                        }
                        break;

                        //ARPS
                    case Z_key:
                        chor = 'a';
                        break;
                    case X_key:
                        // chor = 'b'
                        //arpOn = true;
                        break;
                    case C_key:
                        chor = 'C';
                        break;
                    case V_key:
                        chor = 'd';
                        break;
                    case B_key:
                        chor = 'e';
                        break;
                    case N_key:
                        chor = 'F';
                        break;
                    case M_key:
                        chor = 'G';
                        break;

                        // -- TONES --
                        //ROW 1
                    case A_key:
                        keytone = 'a';
                        octave = 1;
                        break;
                    case S_key:
                        keytone = 'b';
                        octave = 1;
                        break;
                    case D_key:
                        keytone = 'c';
                        octave = 1;
                        break;
                    case F_key:
                        keytone = 'd';
                        octave = 1;
                        break;
                    case G_key:
                        keytone = 'e';
                        octave = 1;
                        break;
                    case H_key:
                        keytone = 'f';
                        octave = 1;
                        break;
                    case J_key:
                        keytone = 'g';
                        octave = 1;
                        break;

                        //ROW 2
                        //TODO speaker 3
                    case Q_key:
                        keytone = 'a';
                        octave = 2;
                        break;
                    case W_key:
                        keytone = 'b';
                        octave = 2;
                        break;
                    case E_key:
                        keytone = 'c';
                        octave = 2;
                        break;
                    case R_key:
                        keytone = 'd';
                        octave = 2;
                        break;
                    case T_key:
                        keytone = 'e';
                        octave = 2;
                        break;
                    case Y_key:
                        keytone = 'f';
                        octave = 2;
                        break;
                    case U_key:
                        keytone = 'g';
                        octave = 2;
                        break;
                }

                //Set the tone that should be played. Only do this if the key is not already playing
                if(keytone != playtone) {
                    playtone = keytone;
                    toneOn++;
                }

                //Set the chord that should be played. Only do this if the key is not already playing
                if(chor != playchor) {
                    playchor = chor;
                    arpOn++;
                }


            //KEY RELEASE PART
            } else {
                test = evt_kb.scancode[1];

                //Arp key is released
                if ( test == Z_key || /*test == S_key ||*/ test == C_key || test == V_key || test == B_key || test == N_key || test == M_key) {

                    //ATTACK MODE
                    if (arpAttack && arpOn == 1) {
                        arpDc = 0;
                        arpSpeed = 0.02;
                    }
                    arpOn--;
                }
                //Tone key is released
                if ( test == Q_key || test == W_key || test == E_key || test == R_key || test == T_key || test == Y_key || test == U_key ||
                        test == A_key || test == S_key || test == D_key || test == F_key || test == G_key || test == H_key || test == J_key) {
                    toneOn--;
                }
            }
        }

        //TONE HANDLER
        if (toneOn> 0) {
            playContNote(playtone,octave,0.9);
        } else {
            keytone = ' ';
            playtone = ' ';
            lastNote = ' ';
            toneBuzzer.nobeep();
            toneOn = 0;
        }

        //CHORD/ARP HANDLER
        if (arpOn>0) {
            playArp(playchor);
        } else {
            chor = ' ';
            playchor = ' ';
            arpOn = 0;
        }
    }
}

/**    
*
*      Plays an arpeggio of the provided chord. Arp speed can be controlled by touch slider or pgup/pgdown setting. PWM duty cycle is sweeped automatically
*
*      @param chord the chord to be played UPPER CASE is major chords, lower case is minor chords.
*
*/
void playArp(char chord)
{
    static int arpCounter;
    static char theChord[3];
    getNotes(chord, theChord);

    //keep duty cycle between 0.03 and 0.97.
    if((arpDc > 0.98 && arpSpeed >0)|| (arpDc < 0.02 && arpSpeed <0)) {
        arpSpeed = arpSpeed*-1;
    }

    arpSpeed2 = tsi.readPercentage()/15;
    //Play the correct note in the chord. Vary the duty cycle and tempo for cool C64 arp effect
    playNote(arpBuzzer,theChord[arpCounter],arpDc,arpTime+arpTempovar*arpSpeed+arpSpeed2);

    arpDc = arpDc+arpSpeed;

    //Reset the notes
    arpCounter++;
    if (arpCounter == 3) {
        arpCounter= 0;
    }
}

/**     
*
*      Plays a note on the provided buzzer
*
*      @param buz the buzzer to play the note on,
*      @param note the note to be played
*      @param dc the duty cycle value
*      @param len the length of the note
*
*/
void playNote(Beep buz, char note,float dc,float len)
{
    buz.beep(getFq(note,false),len,dc);
    myled1 = !myled1;
    myled2 = !myled2;
    if (note=='w') {
        wait(0.05);
    }
    wait (len); //wait while the note plays.
}


/**     -- PlayContNote --
*
*      Plays a continuous note on the second buzzer
*
*      @param note the note to be played
*      @param oct the octave
*      @param dc the duty cycle of the note
*/
void playContNote(char note,int oct,float dc)
{
    static float fq;

    if(lastNote != note) {
        fq = getFq(note,false);
        toneBuzzer.beepNoStop(fq*oct/2,toneDc);
        myled1 = !myled1;
        myled2 = !myled2;
    } else {
        toneBuzzer.changeDc(toneDc);
    }

    //wait (len); //wait while the note plays.
    lastNote = note;
}

/**     
*
*      Gets the notes in a chord
*
*      @param chord the chord
*      @param notes the array to save the notes in
*
*/

void getNotes(char chord, char* notes)
{
    if (chord=='a') {
        notes[0] = 'a';
        notes[1] = 'c';
        notes[2] = 'e';
    }
    if (chord=='C') {
        notes[0] = 'g';
        notes[1] = 'c';
        notes[2] = 'e';
    }
    if (chord=='F') {
        notes[0] = 'a';
        notes[1] = 'c';
        notes[2] = 'f';
    }
    if (chord=='G') {
        notes[0] = 'g';
        notes[1] = 'b';
        notes[2] = 'd';
    }
    if (chord=='d') {
        notes[0] = 'a';
        notes[1] = 'd';
        notes[2] = 'f';
    }
    if (chord=='e') {
        notes[0] = 'g';
        notes[1] = 'b';
        notes[2] = 'e';
    }
}


/**     
*
*      Gets the frequency of the note
*
*      @param note the note
*
*      @return the frequency as float
*/
float getFq(char note, bool sharp)
{
    if(!sharp ) {
        if (note=='a') {
            return 880*masterscale;
        }
        if (note=='b') {
            return 987.76*masterscale;
        }
        if (note=='c') {
            return 1046.5*masterscale;
        }
        if (note=='d') {
            return 1174.66*masterscale;
        }
        if (note=='e') {
            return 1318.51*masterscale;
        }
        if (note=='f') {
            return 1396.91*masterscale;
        }
        if (note=='g') {
            return 1567.98*masterscale;
        }

        //Sharps #
    } else {
        if (note=='f') {
            return 1479.98*masterscale;
        }
    }
    return 0;
}