Playing music

open-source

#42

I suppose some sort of REAL speed test might be needed though.


#43

Btw. are you using any extra RAM buffers or just feeding the mixed samples to the PokittoLib sound buffer?


#44

Very little RAM is being used…

typedef struct{
    bool playSample;
    int soundPoint;
    const uint8_t *currentSound;
    uint32_t currentSoundSize;
    int volume;
    int speed;
    int repeat;
}sampletype;

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

The samples are pulled straight from ROM, as is the tune data.

uint8_t mixSound()
{
    int temp = 0;
    signed int ss[4];
    for(int s=0; s<4; s++){
        snd[s].soundPoint++;

        int currentPos = (snd[s].soundPoint*snd[s].speed)>>8;

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

        ss[s] = snd[s].currentSound[currentPos] -128;
        ss[s] *= snd[s].volume;
        ss[s] = ss[s]>>8;
        ss[s] *= snd[s].playSample; // will be 1 or 0, if not playing, will silence

     }
    temp = (ss[0] + ss[1] + ss[2] + ss[3])/4;
    return temp +128;
}


inline void pokSoundBufferedIRQ() {

#if POK_AUD_TRACKER
           uint32_t output = mixSound();
#else
           uint32_t output = soundbuf[soundbufindex+=Pokitto::streamon];
#endif

           if (soundbufindex==SBUFSIZE){
                soundbufindex=0;
           }

           //if (p==sizeof(beat_11025_raw)) p=0;
           //soundbuf[soundbufindex++] = output;
           //uint32_t t_on = (uint32_t)(((obj->pwm->MATCHREL0)*output)>>8); //cut out float
           //obj->pwm->MATCHREL1 = t_on;
           output *= discrete_vol_multipliers[discrete_vol];
           output >>= 8;
           dac_write(output);

           //setDRAMpoint(pixx, pixy);
}

#45

Looks very neat and simple. Is the speed parameter related to the note pitch?


#46

yes, I have a list of speeds,

const int note_speed[]={
16,17,18,19,20,21,23,24,25,27,29,30, // C, C#, D, D#, E, F, F#, G, G#, A, A#, B,
32,34,36,38,40,43,45,48,51,54,57,60,
64,68,72,76,81,85,91,96,102,108,114,121,
128,136,144,152,161,171,181,192,203,215,228,242,
256,271,287,304,323,342,362,384,406,431,456,483,
512,542,575,609,645,683,724,767,813,861,912,967,
1024,1085,1149,1218,1290,1367,1448,1534,1625,1722,1825,1933,
2048,2170,2299,2435,2580,2734,2896,3068,3251,3444,3649,3866,
4096,4339,4598,4871,5161,5467,5793,6137,6502,6889,7298,7732
};

// new playsound function
uint8_t playSound(int channel, const unsigned char *sound, int volume = 255, int speed=255, int offset=0){
    int dataOffset = 14 + offset;


    // get sound length
    uint32_t soundSize = 0;
    for(int t=0; t<4; t++){
        soundSize <<= 8;
        soundSize |= sound[t] & 0xFF;
    }

    int sampleRate = 0;
    for(int t=0; t<2; t++){
        sampleRate <<= 8;
        sampleRate |= sound[t+4] & 0xFF;
    }

    // get repeat start
    int repeatStart = 0;
    for(int t=0; t<4; t++){
        repeatStart <<= 8;
        repeatStart |= sound[t+6] & 0xFF;
    }

    // get repeat end
    int repeatEnd = 0;
    for(int t=0; t<4; t++){
        repeatEnd <<= 8;
        repeatEnd |= sound[t+10] & 0xFF;
    }

    // cheat, if there is a looping sample, make the size the length of the loop
    if(repeatEnd != 0){
        soundSize = repeatEnd;
        // also cheat for start loop
        if(repeatStart == 0){
           repeatStart = 1;
        }
    }

    float spd = (POK_AUD_FREQ / (float)sampleRate);

    Pokitto::snd[channel].currentSound = &sound[dataOffset];          // sound to play
    Pokitto::snd[channel].volume = volume;               // volume
    Pokitto::snd[channel].speed = (speed/spd);           // recalculated above
    Pokitto::snd[channel].currentSoundSize = soundSize;  // length of sound array
    Pokitto::snd[channel].soundPoint = 0;                // where the current sound is upto
    Pokitto::snd[channel].repeat = repeatStart * 256 / Pokitto::snd[channel].speed;          // repeat point
    Pokitto::snd[channel].playSample = 1;                // trigger sample playing

    return channel;
}

I created this from a list of note frequencies I found online and recalculated them into ‘precentages’ out of 255 (instead of 100) then used a multiply and divide to remove the need for floats.

later today, I’ll update a music player example and upload it to show whats going on.


#47

As the mixSound() is called very often it might be worthwhile to do a separate if-branch for the silence (playSample==0) at the very beginning of the for block. In “silent” sample case you could just store zero to the ss[] buffer and increase and check the soundPoint counter, and skip the rest of the calculations.


#48

Moved the discussion over here from another thread…


#49

Btw. I cannot find your PR in PokittoLib GitHub


#50

I suppose this can handle sound effects too? I mean e.g. 3 channels for music and 1 channel for effects?

Eventually, we should support 4-bit samples too but lets do first things first.

Also, you might not know Jonne’s fix to the DAC audio(?). I do not know if it affects to your audio system but for streaming audio, it was essential. The fix is here in my PR ( Pokitto::dac_write() change only): https://github.com/pokitto/PokittoLib/pull/76/commits/531e507fd352121acd17c7b673d672b9c1cd4c9a


#51

It will be about 2 months ago I think.

[edit] maybe not.
I’m trying to add to a new clone of pokittolib, but now I’m having trouble with mixMode screen mode.
MusicPlayer\main.cpp|223|undefined reference to `Pokitto::Display::scanType’|
Which is odd, because that has a) been working for ages and b) is defined, I can see it in the various source files.


#52

I think I managed a PR. I hate github.


#53

Would you be open to some code improvement suggestions?

Also, is there a GitHub link for the code as it currently stands?


#54

You mean change it all to a form that I can no longer understand myself?


#55

For anyone interested, I’m using the following javascript tool to convert 8bit .wav files to those used by my code.

To use simply (choose File) -> (Convert) -> (copy code)
The (Resample) button will shorten the sample by removing every second byte. Don’t bother with that.


<html>
	<title>Wav to Pokitto</title>
	<style type="text/css">
		
		#progressbarOuter{
			display: block;
			border: 2px solid gray;
			color: gray;
			background-color: white;
			border-radius: 8px;
			font-size: 20px;
			font-weight: bold;
			width:204px;
			height:40px;
			position:relative;
		}

		#progressbarOuter span{
			display:block;
			text-align:center;
			width:100%;
			position:absolute;
			z-index:2;
			top:0;
			left:0;
			line-height:36px;
		}
		#progressbarInner{
			border: 0px solid gray;
			color: #000;
			background-color: #88f;
			border-radius: 3px;
			font-size: 20px;
			font-weight: bold;
			width:0px;
			height:36px;
			text-align: center;
			position:relative;
			z-index:1;
			padding:2px;
		}
		
		.custom-file-input {
			display: block;
		}
		.custom-file-input input {
			visibility: hidden;
			width: 100px;
		}
		.custom-file-input:before {
			width:148px;
			content: 'Choose File';
			display: block;
			border: 2px solid gray;
			color: gray;
			background-color: white;
			padding: 8px 12px 8px 44px;
			border-radius: 8px;
			font-size: 20px;
			font-weight: bold;
			background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJMYXllcl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCINCgkgdmlld0JveD0iMCAwIDUxMiA1MTIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDUxMiA1MTI7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxwYXRoIHN0eWxlPSJmaWxsOiNFQ0VERUY7IiBkPSJNMTA2LjMyNCw4LjAxN2MtOS40NDYsMC0xNy4xMDIsNy42NTYtMTcuMTAyLDE3LjEwMnY0NjEuNzYyYzAsOS40NDYsNy42NTYsMTcuMTAyLDE3LjEwMiwxNy4xMDINCgloMzc2LjI1MWM5LjQ0NiwwLDE3LjEwMi03LjY1NiwxNy4xMDItMTcuMTAyVjg0Ljk3N2wtNzYuOTYtNzYuOTZIMTA2LjMyNHoiLz4NCjxwYXRoIHN0eWxlPSJmaWxsOiNEOURDREY7IiBkPSJNNDM5LjgxOCw4NC45NzdoNTkuODU4bC03Ni45Ni03Ni45NnY1OS44NThDNDIyLjcxNSw3Ny4zMTksNDMwLjM3Myw4NC45NzcsNDM5LjgxOCw4NC45Nzd6Ii8+DQo8cGF0aCBzdHlsZT0iZmlsbDojQzZDQUNGOyIgZD0iTTM5Ny4wNjMsMTk2LjE0Mkg4OS4yMjJWNDIuMjIxaDMwNy44NDFjNC43MjIsMCw4LjU1MSwzLjgyOSw4LjU1MSw4LjU1MXYxMzYuODE4DQoJQzQwNS42MTQsMTkyLjMxMyw0MDEuNzg1LDE5Ni4xNDIsMzk3LjA2MywxOTYuMTQyeiIvPg0KPHBvbHlnb24gc3R5bGU9ImZpbGw6I0Y3OUY0RDsiIHBvaW50cz0iMzg4LjUxMiwxNzkuMDQgMTIuMjYxLDE3OS4wNCA4MC42NywxMTAuNjMgMTIuMjYxLDQyLjIyMSAzODguNTEyLDQyLjIyMSAiLz4NCjxwYXRoIGQ9Ik00OTkuNjc3LDQyNi40ODljNC40MjgsMCw4LjAxNy0zLjU4OSw4LjAxNy04LjAxN1Y4NC45NzdjMC0yLjEtMC44NjItNC4xODQtMi4zNDctNS42NjhsLTc2Ljk2LTc2Ljk2DQoJQzQyNi44OTksMC44NjMsNDI0LjgxNywwLDQyMi43MTYsMEgxMDYuMzI0QzkyLjQ3MywwLDgxLjIwNSwxMS4yNjgsODEuMjA1LDI1LjExOXY5LjA4NkgxMi4yNjFjLTYuOTg3LDAtMTAuNjE1LDguNzM4LTUuNjY5LDEzLjY4NQ0KCWw2Mi43NDEsNjIuNzQxTDYuNTkyLDE3My4zNzFjLTQuOTQ2LDQuOTQ3LTEuMzE5LDEzLjY4NSw1LjY2OSwxMy42ODVoNjguOTQ0djI5OS44MjVjMCwxMy44NTEsMTEuMjY4LDI1LjExOSwyNS4xMTksMjUuMTE5aDM3Ni4yNTENCgljMTMuODUxLDAsMjUuMTE5LTExLjI2OCwyNS4xMTktMjUuMTE5di0zNC4yMDVjMC00LjQyNy0zLjU4OC04LjAxNy04LjAxNy04LjAxN3MtOC4wMTcsMy41ODktOC4wMTcsOC4wMTd2MzQuMjA1DQoJYzAsNS4wMS00LjA3Niw5LjA4Ni05LjA4Niw5LjA4NkgxMDYuMzI0Yy01LjAxLDAtOS4wODYtNC4wNzYtOS4wODYtOS4wODZWMTg3LjA1Nmg1MS44NDFjNC40MjgsMCw4LjAxNy0zLjU4OSw4LjAxNy04LjAxNw0KCXMtMy41ODgtOC4wMTctOC4wMTctOC4wMTdIMzEuNjE1bDU0LjcyNC01NC43MjRjMy4xMzEtMy4xMzEsMy4xMzEtOC4yMDcsMC0xMS4zMzdMMzEuNjE1LDUwLjIzOGgzNDguODh2MTIwLjc4NUgxODMuMjg0DQoJYy00LjQyOCwwLTguMDE3LDMuNTg5LTguMDE3LDguMDE3czMuNTg4LDguMDE3LDguMDE3LDguMDE3aDIwNS4yMjhjNC40MjgsMCw4LjAxNy0zLjU4OSw4LjAxNy04LjAxN1Y0Mi4yMjENCgljMC00LjQyNy0zLjU4OC04LjAxNy04LjAxNy04LjAxN0g5Ny4yMzh2LTkuMDg2YzAtNS4wMSw0LjA3Ni05LjA4Niw5LjA4Ni05LjA4Nkg0MTQuN3Y1MS44NDFjMCwxMy44NTEsMTEuMjY4LDI1LjExOSwyNS4xMTksMjUuMTE5DQoJaDUxLjg0MXYzMjUuNDc4QzQ5MS42Niw0MjIuOSw0OTUuMjQ4LDQyNi40ODksNDk5LjY3Nyw0MjYuNDg5eiBNNDM5LjgxOSw3Ni45NmMtNS4wMSwwLTkuMDg2LTQuMDc2LTkuMDg2LTkuMDg2VjI3LjM3bDQ5LjU4OSw0OS41OQ0KCUg0MzkuODE5eiIvPg0KPHBhdGggZD0iTTMwMywxNDQuMzAxYzMuNDUxLDAsNi41MTUtMi4yMDgsNy42MDUtNS40ODFsMTcuMTAyLTUxLjMwN2MxLjM4MS00LjE0NC0wLjkyNi04Ljc2LTUuMDctMTAuMTQxDQoJYy00LjE0NS0xLjM4MS04Ljc1OSwwLjkyNy0xMC4xNDEsNS4wNzFMMzAzLDExMC45MzNsLTkuNDk3LTI4LjQ5Yy0xLjM4Mi00LjE0NS01Ljk5NS02LjQ1My0xMC4xNDEtNS4wNzENCgljLTQuMTQ0LDEuMzgyLTYuNDUxLDUuOTk1LTUuMDcsMTAuMTQxbDE3LjEwMiw1MS4zMDdDMjk2LjQ4NSwxNDIuMDkyLDI5OS41NDksMTQ0LjMwMSwzMDMsMTQ0LjMwMXoiLz4NCjxwYXRoIGQ9Ik0yNDIuMTk2LDEzOC44MTlsOS40OTctMjguNDlsOS40OTcsMjguNDljMS40MzksNC4zMTUsNi4zMzMsNi41ODIsMTAuNTYzLDQuOTE1YzMuOTA2LTEuNTM5LDUuOTczLTYuMDA5LDQuNjQ4LTkuOTg2DQoJbC0xNy4xMDItNTEuMzA3Yy0xLjA3OC0zLjIzNy00LjE5NC01LjQ4MS03LjYwNS01LjQ4MWMtMy40MTIsMC02LjUyNywyLjI0NS03LjYwNSw1LjQ4MWwtMTcuMTAyLDUxLjMwNw0KCWMtMS4zODEsNC4xNDQsMC45MjYsOC43Niw1LjA3LDEwLjE0MUMyMzYuMiwxNDUuMjcsMjQwLjgxNCwxNDIuOTYyLDI0Mi4xOTYsMTM4LjgxOXoiLz4NCjxwYXRoIGQ9Ik0xNzMuMzUyLDEzOS44NjlsOS45MzItMTkuODYzbDkuOTMyLDE5Ljg2M2MzLjEzMSw2LjI2MywxMi41NjUsNS41ODMsMTQuNzc1LTEuMDVsMTcuMTAyLTUxLjMwNw0KCWMxLjQtNC4yMDEtMC44NjktOC43NC01LjA3LTEwLjE0MWMtNC4yMDQtMS4zOTktOC43NDEsMC44Ny0xMC4xNDEsNS4wNzFsLTEwLjk4MiwzMi45NDVsLTguNDQ2LTE2Ljg5NA0KCWMtMi44OTctNS43OTMtMTEuNDQ0LTUuNzkzLTE0LjM0LDBsLTguNDQ2LDE2Ljg5NGwtMTAuOTgyLTMyLjk0NWMtMS40MDEtNC4yMDEtNS45MzktNi40NzItMTAuMTQxLTUuMDcxDQoJYy00LjIwMSwxLjQtNi40Nyw1Ljk0LTUuMDcsMTAuMTQxbDE3LjEwMiw1MS4zMDdDMTYwLjc4NiwxNDUuNDUyLDE3MC4yMjEsMTQ2LjEzLDE3My4zNTIsMTM5Ljg2OXoiLz4NCjxwYXRoIGQ9Ik0xNDEuMDYzLDM3NS43MTZjMCw0LjQyNywzLjU4OCw4LjAxNyw4LjAxNyw4LjAxN2g0Mi43NTZjMi41MjMsMCw0LjktMS4xODgsNi40MTMtMy4yMDdsMTUuMTk1LTIwLjI2MWwxMy4zMjIsNTkuOTQ2DQoJYzAuODE3LDMuNjc1LDQuMTk4LDYuMzM0LDcuOTU5LDYuMjc3YzMuNzYtMC4wNTgsNy4wNTMtMi44NDQsNy43NDUtNi41MzhsMTcuMDk3LTkxLjE4NGwxOC40MTMsMTE2LjYxDQoJYzAuNjE2LDMuODk5LDQuMTAzLDYuODI0LDguMDQ4LDYuNzY1YzMuOTQzLTAuMDU5LDcuMzM3LTMuMTEyLDcuODI1LTcuMDIybDE3Ljg2OC0xNDIuOTQybDE3LjUzNSwxMzQuNDM1DQoJYzAuNDk4LDMuODIzLDMuNzcxLDYuODE0LDcuNjIzLDYuOTcyYzMuODUsMC4xNTgsNy4zNTctMi41NjMsOC4xNjUtNi4zM2wxNy4xNy04MC4xMjZsOS44NjEsNTQuMjI4DQoJYzAuNjAxLDMuMzA0LDMuMjksNS45NDEsNi42MDQsNi40NzhjMy40MjMsMC41NTUsNi45MDMtMS4yMjQsOC40NTQtNC4zMjdsMTQuODg3LTI5Ljc3M2gzNy44YzQuNDI4LDAsOC4wMTctMy41ODksOC4wMTctOC4wMTcNCgljMC00LjQyNy0zLjU4OC04LjAxNy04LjAxNy04LjAxN2gtNDIuNzU2Yy0zLjAzNywwLTUuODEzLDEuNzE2LTcuMTcsNC40MzJsLTYuMzQ3LDEyLjY5M2wtMTIuODAxLTcwLjQwMQ0KCWMtMC42ODEtMy43NDYtNC4wMjEtNi41NDctNy44MjUtNi41ODJjLTMuNzU3LTAuMDM0LTcuMTE1LDIuNjY2LTcuOTAxLDYuMzM2bC0xNS44OSw3NC4xNTlMMzE5LjUsMjM3Ljg2MQ0KCWMtMC41MTUtMy45NDctMy45NzktNi45ODUtNy45Ni02Ljk4Yy0zLjk4OCwwLjAwNS03LjQ0OSwzLjA2Ni03Ljk0NCw3LjAyM2wtMTguNjIxLDE0OC45NzRsLTE2LjgxMy0xMDYuNDc1DQoJYy0wLjYwOC0zLjg1My0zLjkwMy02LjcwOS03LjgwNC02Ljc2NWMtMy44OTUtMC4wNTctNy4yNzYsMi43MDQtNy45OTQsNi41MzhsLTE4LjQ0MSw5OC4zNDZsLTguNjExLTM4Ljc0OA0KCWMtMS40NTUtNi41NDMtMTAuMjA3LTguNDQ0LTE0LjIzOS0zLjA3MUwxODcuODI1LDM2Ny43aC0zOC43NDdDMTQ0LjY1MSwzNjcuNjk5LDE0MS4wNjMsMzcxLjI4OSwxNDEuMDYzLDM3NS43MTZ6Ii8+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8L3N2Zz4NCg==');
			background-size: 30px auto; background-position: 3px center;  background-repeat:no-repeat;
		}
		
		.button {
			display: block;
			width:208px;
			border: 2px solid gray;
			color: gray;
			background-color: white;
			padding: 8px 44px 8px 44px;
			border-radius: 8px;
			font-size: 20px;
			font-weight: bold;
		}

		*:focus {
			outline: none;
		}
	
	</style>

<script>

	// wav file format info from - https://sites.google.com/site/musicgapi/technical-documents/wav-file-format

	var inArray;
	var reSample;
	
	function init() {
		document.getElementById('files').addEventListener('change', handleFileSelect, false);
		reSample = 0;
	}
		
	function decToHex(buffer) {
		var s = '', h = '0123456789ABCDEF';
		s = h[buffer >> 4] + h[buffer & 15];
		return '0x' + s;
	}
		
	function DoConversion() {	
		// This is where your converter goes
		if (inArray.length<2) {return;}
		
		// check wave header
		if( !(inArray[0]=='R' && inArray[1]=='I' && inArray[2]=='F' && inArray[3]=='F' )){
			//	not wav file.
			alert('Not a valid .wav file.');
			return;
		}
		
		// check sample format, must be PCM
		if(inArray[20].charCodeAt() != 1){
			alert('Not PCM format' + 'Is is ' + inArray[20].charCodeAt() );
			return;
		}
		
		// check channels, must be MONO
		if(inArray[22].charCodeAt() != 1){
			alert('Not MONO' );
			return;
		}

		bitRate = 0;
		// samples, must be 8bit
		if(inArray[34].charCodeAt() == 8){
			bitRate = 8;
		}

		if(inArray[34].charCodeAt() == 16){
//			bitRate = 16;
			alert('Bit Rate = ' + bitRate + 'it must be 8-bit!');
			return;
		}

		
		txt = document.getElementById("theOutput");
		
		//var fileLength = inArray.length - 44;
		
			// 41,42,43,44
			fileLength =  inArray[43].charCodeAt() << 24;
			fileLength += inArray[42].charCodeAt() << 16;
			fileLength += inArray[41].charCodeAt() << 8;
			fileLength += inArray[40].charCodeAt();
		
		fileLength = ((fileLength+1) >> 1)<<1;
		
		txt.value = 'const unsigned char ';
		txt.value += files.value.substr(files.value.lastIndexOf('\\') + 1).split('.')[0];

		// look for 'smpl' data
		smplOffset = 44 + fileLength;
		magicString =  String.fromCharCode(inArray[smplOffset  ].charCodeAt());
		magicString += String.fromCharCode(inArray[smplOffset+1].charCodeAt());
		magicString += String.fromCharCode(inArray[smplOffset+2].charCodeAt());
		magicString += String.fromCharCode(inArray[smplOffset+3].charCodeAt());

		if(reSample == 0){
			txt.value += '[' + (fileLength + 14);
		}else{
			txt.value += '[' + (Math.floor(fileLength/2) + 6);
		}
		txt.value += ']={\n';

		txt.value += '	';

		fl = fileLength;
		if(reSample!=0) fl=Math.floor(fl/2);
		txt.value += decToHex((fl >> 24) & 255) + ', ';
		txt.value += decToHex((fl >> 16) & 255) + ', ';
		txt.value += decToHex((fl >> 8) & 255) + ', ';
		txt.value += decToHex(fl & 255) + ', ';

		txt.value += '// sample length = ' + fl + ' bytes.\n	';

		
		//sRate = parseInt(document.getElementById("samplerate").value, 10);
		//txt.value += decToHex(inArray[27].charCodeAt()) + ', ';
		//txt.value += decToHex(inArray[26].charCodeAt()) + ', ';
		
		//sRate = inArray[27].charCodeAt();
		//sRate = (sRate << 8) + inArray[26].charCodeAt();
		sRate = inArray[25].charCodeAt();
		sRate = (sRate << 8) + inArray[24].charCodeAt();

		if(reSample == 0){
			txt.value += decToHex(inArray[25].charCodeAt()) + ', ';
			txt.value += decToHex(inArray[24].charCodeAt()) + ', ';
		}else{
			sRate = Math.floor(sRate/2);
			txt.value += decToHex( (sRate >> 8)&255 ) + ', ';
			txt.value += decToHex( sRate &255 ) + ', ';
		}

		txt.value += '// sample rate = ' + sRate + 'Hz\n';

		// add loop data here
		alert('File Length = '+fileLength+'\n'+'smpl? = ' + magicString);
		if(magicString.includes('smp')){
			txt.value += '	// loop found\n';
			numLoops =  inArray[smplOffset+39].charCodeAt() << 24;
			numLoops += inArray[smplOffset+38].charCodeAt() << 16;
			numLoops += inArray[smplOffset+37].charCodeAt() << 8;
			numLoops += inArray[smplOffset+36].charCodeAt();
			//txt.value += '	// number of loops = ' + numLoops + '\n';
			
			loopStart =  inArray[smplOffset+55].charCodeAt() << 24;
			loopStart += inArray[smplOffset+54].charCodeAt() << 16;
			loopStart += inArray[smplOffset+53].charCodeAt() << 8;
			loopStart += inArray[smplOffset+52].charCodeAt();
			//txt.value += '	// start of loop = ' + loopStart + '\n';

			fl = loopStart;
			if(reSample!=0) fl=Math.floor(fl/2);
			txt.value += '	';
			txt.value += decToHex((fl >> 24) & 255) + ', ';
			txt.value += decToHex((fl >> 16) & 255) + ', ';
			txt.value += decToHex((fl >> 8) & 255) + ', ';
			txt.value += decToHex(fl & 255) + ', ';
			txt.value += '// loop start = ' + fl + '\n';

			loopEnd =  inArray[smplOffset+59].charCodeAt() << 24;
			loopEnd += inArray[smplOffset+58].charCodeAt() << 16;
			loopEnd += inArray[smplOffset+57].charCodeAt() << 8;
			loopEnd += inArray[smplOffset+56].charCodeAt();
			//txt.value += '	// end of loop = ' + loopEnd + '\n';

			fl = loopEnd;
			if(reSample!=0) fl=Math.floor(fl/2);
			txt.value += '	';
			txt.value += decToHex((fl >> 24) & 255) + ', ';
			txt.value += decToHex((fl >> 16) & 255) + ', ';
			txt.value += decToHex((fl >> 8) & 255) + ', ';
			txt.value += decToHex(fl & 255) + ', ';
			txt.value += '// loop end = ' + fl + '\n';
		
		}else{
			txt.value += '	// loop not found\n';
			txt.value += '	0x00, 0x00, 0x00, 0x00,	// no start\n';
			txt.value += '	0x00, 0x00, 0x00, 0x00,	// no finish\n';
		
		}





		txt.value += '	// sample data\n';
	
		txt.value += '	';

		var counter = 0;
		if(bitRate == 8){
		
			 fileLength+=44;
		
			// use an asyc loop so that the UI doesn't freeze.
			(function loop(i){
				
				var next_i = i+256;
				if(next_i > fileLength+1) next_i = fileLength+1;
				
				while( i < next_i){
					txt.value += decToHex(inArray[i-1].charCodeAt()) + ', ';
					if(++counter % 16 == 0 ) txt.value += '\n	';
					++i;
					if(reSample != 0){ ++i; }
				}
				var container = document.getElementById("progressbarInner");
				container.style.width = (200/fileLength) * i;
				
				container = document.getElementById("percentage");
				container.innerHTML = Math.floor((100/fileLength) * i) + '%';
				
				if(i<fileLength+1){
					setTimeout(function(){ loop(i) },1); 
				} else {
					txt.value += '\n};\n\n';
				}
			}(45)); // start copying at byte 44
		} // 8bit
				
	}

	function handleFileSelect(evt) {
		// This takes the file selected (and should be easily convertable to handle many files, but sod that for now!!) 
		// and plonks the binarydata into inArray.
		
		var files = evt.target.files
		for (var i = 0, f; f = files[i]; i++) {
			var reader = new FileReader()
			
			reader.onload = (function(theFile) {
					// (2) - This is next, and is what happens once the file (below) has loaded.
				return function(e) {
					inArray=e.target.result;	// This plonks the array into the array buffer variable thing
				}
			})(f)
				// (1) - This says "Read the file", and happens first
			reader.readAsBinaryString(f)
		}
	}
	
	function copyCode(){
		txt = document.getElementById("theOutput");
		
		txt.select();
        try{
			return document.execCommand("copy");  // Security exception may be thrown by some browsers.
        } catch (ex) {
            console.warn("Copy to clipboard failed.", ex);
            return false;
        }
		
	}
	
	function doReSample(){
		reSample = 1;
		DoConversion();
	}

</script>

<body onload="init()">

<h1>Wav to Pokitto converter 1.0</h1>

<span>Input file must be 8-bit mono PCM .wav</span><br/><br/>

<label class="custom-file-input">
	<input id="files" type="file" accept=".wav">
</label>

<button onclick="reSample=0; DoConversion();return false;" class="button">Convert</button><br/>

<button onclick="doReSample(); return false;" class="button">Resample</button><br/>

<div id="progressbarOuter"><span id="percentage">0%</span><div id="progressbarInner"></div></div><br/>

<button onclick="copyCode();return false;" class="button">Copy Code</button><br/>

<textarea id="theOutput" name="theOutput" rows="25" cols="120"></textarea>

<br/><br/>
<div>Code by <a href="https://twitter.com/Spinal_Cord" title="@Spinal_Cord">@Spinal_Cord</a>, <a href="https://twitter.com/DiziHelen" title="@DiziHelen">@DiziHelen</a> and <a href="https://twitter.com/Jayenkai" title="@Jayenkai">@Jayenkai</a></div>
<div>Icons made by <a href="https://www.freepik.com/" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" 			    title="Flaticon">www.flaticon.com</a> is licensed by <a href="http://creativecommons.org/licenses/by/3.0/" 			    title="Creative Commons BY 3.0" target="_blank">CC 3.0 BY</a></div>
</body></html>


#56

Are you using a modified version of the pokitto library?

Pokitto::snd and Pokitto::Display::scanType don’t seem to exist.

I can explain every single change I make.

If anything they should make the code slightly easier to understand because I’d be getting rid of some of the low-level stuff in favour of some equivalent structs.


#57

I think simple changes that improve clarity are good, like adding structs or enums.

Adding templates are frightening for some people.


#58

That’s what the main focus would be.

E.g. instead of all the byte copying in playSound, a Sound struct can be used.

People seem to forget that you don’t have to store your data as arrays of bytes, you can just use structs.

Templates really don’t deserve the level of FUD people like to spread.

There’s some really ridiculous examples that do crazy things,
but those kinds of uses are either very special cases (like Boost.Spirit) or just people showing off for the sake of it.

The most common uses are pretty easy to understand.
Most people can understand the idea of std::min(a, b) and std::size(array) even if they don’t know how they work internally.

Examples

For example, std::min is no more complex than defining a min macro:

template< typename Type >
const Type & min(const Type & a, const Type & b)
{
	return (a < b) ? a : b;
}

And you use it in exactly the same way, min(a, b).

Occaisionally the two types don’t match and you have to specify which type to use, e.g. min<int>(a, b), but that’s fairly rare and not too difficult to overcome.

And making a function adapt to arrays of different sizes…

template< typename Type, std::size_t size >
void print(const Type (&array)[size])
{
	for(std::size_t index = 0; index < size; ++index)
		Pokitto::Display::println(array[index]);
}

Looks odd at first, but it’s easy to use, just print(array) and the compiler infers Type and size for you.

The compiler is smart enough to figure out that if a variable is defined as int array[5] then Type becomes int and size becomes 5 - a process known as template substitution.

Template parameters are a lot like function parameters,
the only real difference is that they can only change at compile time.

Ultimately templates are just C++'s implementation of generic programming and compile-time pattern matching.
Languages like C# and Java also feature generic programming.


#59

Yup, I did do a PR with the changes. But as always, Not sure if it was done correctly as I have trouble understanding the exact process with github.


#60

I can see where you’re introducing snd.
I can replace that with a local snd for now.

As for scanType, it seems that was introduced a while ago and I haven’t updated my library in a while.


Even with a fully updated library I’m getting:
main.cpp:267: undefined reference to 'Pokitto::Display::scanType'.


#61

I assume your setup doesn’t have the mixmode example?
Just change the screen mode to something else in the settings.h and don’t use my screen junk. It’s only there because I like that screen mode.