Pokitto music player…. Part 2.

OK, as I managed in the past part to play a sample at the correct speed no matter what the sample rate. The next step was to play a sample and a given speed and pitch then string a bunch of them together to create a tune…

sound_player_embitz_lpc11u68

#include "Pokitto.h"
#include "HWSound.h"
#include "snd.h"
#include "tune.h"

Pokitto::Core mygame;
Pokitto::Display d;

typedef struct{
    bool playSample;                // should this sound be playing now?
    int soundPoint;                 // current position of sound
    const uint8_t *currentSound;    // currend sound array playing
    uint32_t currentSoundSize;      // length of current sound
    int volume;                     // volume of sound, best to be 63 or lower
    int speed;                      // how fast to play sound, 256 = 100%
    bool repeat;                    // 1 to repeat 0 not to
    int repeat_start;               // start point of repeat, default should be 0
    int repeat_end;                 // end point of repeate, default should be currentSoundSize
    char lastSample;                // previous sample played
} sampletype;

sampletype snd[4];                  // up to 4 sounds at once?
int oldQuart;

int currentLine=0;
int mytick=0;
int pattern=0; 

Ticker soundPlay;
Ticker musicTimer;

const float note_speed[]={
// C, C#, D, D#, E, F, F#, G, G#, A, A#, B,
16.00, 16.95, 17.96, 19.03, 20.16, 21.36, 22.62, 23.97, 25.40, 26.91, 28.51, 30.21, 
32.00, 33.90, 35.92, 38.05, 40.31, 42.71, 45.25, 47.95, 50.79, 53.82, 57.02, 60.41, 
64.00, 67.81, 71.84, 76.11, 80.64, 85.43, 90.51, 95.89, 101.60, 107.63, 114.03, 120.81, 
128.00, 135.61, 143.67, 152.21, 161.26, 170.85, 181.02, 191.78, 203.18, 215.27, 228.06, 241.63, 
256.00, 271.22, 287.34, 304.43, 322.54, 341.71, 362.03, 383.56, 406.36, 430.53, 456.13, 483.25, 
511.99, 542.44, 574.69, 608.86, 645.06, 683.43, 724.07, 767.12, 812.74, 861.06, 912.27, 966.51, 
1023.98, 1084.87, 1149.38, 1217.73, 1290.14, 1366.85, 1448.13, 1534.24, 1625.47, 1722.13, 1824.53, 1933.02, 
2047.96, 2169.74, 2298.77, 2435.46, 2580.27, 2733.71, 2896.26, 3068.48, 3250.94, 3444.25, 3649.06, 3866.05, 
4095.93, 4339.49, 4597.52, 4870.91, 5160.55, 5467.41, 5792.52, 6136.96, 6501.89, 6888.51, 7298.12, 7732.08
};

uint8_t playSound(int channel, const unsigned char *sound, uint32_t soundSize, int volume = 255, int speed=255, int repeat=0){

    // to resample current sample rate to 44100, cheap and probably wrong way
    float rate1 = 44100 / (float)sampleRates[channel];
    // speed = 256 / rate1; // playback speed is 
    speed = speed / rate1; // playback speed is 

    snd[channel].currentSound = sound;                      // sound to play
    snd[channel].currentSoundSize = (soundSize<<8)/speed;   // length of sound array adjusted for speed change
    snd[channel].volume = 63;                               // volume, best kept below 64 as louder will cause clipping when playing multiple samples
    snd[channel].speed = speed;                             // recalculated above
    snd[channel].repeat = repeat;                           // 1 to repead, 0 to not
    snd[channel].soundPoint = 0;                            // where the current sound is upto
    snd[channel].playSample = 1;                            // 1 to play this sound, 0 not to

    return channel;
}

uint8_t mixSound(int samplePos)
{
    int temp = 0;
    int ss[4];

    for(int s=0; s<4; s++){
        
        ss[s] = (snd[s].currentSound[(snd[s].soundPoint*snd[s].speed)>>8]*snd[s].volume>>8) * snd[s].playSample;

        ++snd[s].soundPoint;
        if(snd[s].soundPoint >= snd[s].currentSoundSize){
            if(snd[s].repeat){
                snd[s].soundPoint=0;
            }else{
                snd[s].playSample=0;
                snd[s].soundPoint=0;
            }
        }
     }    

    temp = (ss[0] + ss[1] + ss[2] + ss[3]) - ((ss[0] * ss[1] * ss[2] * ss[3])/256);
    
    return temp;
}


void updateAudioBuffer(){
    // when updating the audio buffer, I split the buffer into 4 and fill
    // a part that isn't being played, so there are no sync issues
    
    int quart = soundbufindex / 512;
    int sndOffset[]={1024,1536,0,512};
    
    // only update if the current playback pointer is nowhere near where we want to fill
    if(oldQuart != quart){
        oldQuart = quart;
        for(int t=0; t<=SBUFSIZE/4;){
            uint8_t sample = mixSound(t);
            soundbuf[t+sndOffset[quart]] = sample;
            t++;
        }
    }
}

void update_tune(){

    char pat = my_pattern[pattern];

    for(int t=0; t<4; t++){
        int note =       tune[pat][currentLine][t][0];
        int octave =     tune[pat][currentLine][t][1]; // no longer used
        int volume =     tune[pat][currentLine][t][2];// max volume from xm/mod was 64
        int instrument = tune[pat][currentLine][t][3];
                    
        if(note > 0){
            int speed = note_speed[(note-1)+12*octave];

            switch(instrument){
                case 1:
                    playSound(t, s_01, sizeof(s_01), volume, speed, sampleRepeat[0]);
                break;
                case 2:
                    playSound(t, s_02, sizeof(s_02), volume, speed, sampleRepeat[1]);
                break;
                case 3:
                    playSound(t, s_03, sizeof(s_03), volume, speed, sampleRepeat[2]);
                break;
                case 4:
                    playSound(t, s_04, sizeof(s_04), volume, speed, sampleRepeat[3]);
                break;
            }
                        
        }
        if(volume >= 128){
            snd[t].volume = volume-128;
        }
    }
 
    if(currentLine++==patternLength){
        currentLine=0;
        if(pattern++==sizeof(my_pattern))pattern=loopTo;
    }
}




int main ()
{
    mygame.begin();
    mygame.setFrameRate(255);

    // Init audio stream.
    pokPlayStream(); // activate stream
    mygame.sound.ampEnable(true);
    mygame.sound.playMusicStream();

    // check and update the sound buffer as often as we can
    soundPlay.attach(&updateAudioBuffer, 0.0001);
    musicTimer.attach(&update_tune, 0.055);
    
    d.color = 1;

    while (mygame.isRunning())
    {
        if (mygame.update())
        {
            
        }
    }
}


tune.h

const char my_pattern[]={0,1,2,1,2,3,4,5,6,7,8,5,9,0,10,11,18,12,13,14,15,16,17,16,14,15,16,17,16,17,19,20,19,21};
const char loopTo = 0;
const char patternLength = 63; 


const char tune[22][64][4][4]={
{
    {{0 ,0 ,128,0 },{0 ,0 ,128,0 },{0 ,0 ,128,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,4 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,4 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,4 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,3 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{1 ,5 ,160,4 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
    {{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 },{0 ,0 ,0 ,0 }},
// lots of data removed for space

};

I added a second timer to keep the music speed accurate.