PokittoLib is the library needed for programming the Pokitto DIY game console (www.pokitto.com)

Revision:
6:72f87b7c7400
Child:
11:aa12eb46aa02
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/POKITTO_CORE/PokittoSound.cpp	Wed Oct 11 20:35:52 2017 +0000
@@ -0,0 +1,913 @@
+/**************************************************************************/
+/*!
+    @file     PokittoSound.cpp
+    @author   Jonne Valola
+
+    @section LICENSE
+
+    Software License Agreement (BSD License)
+
+    Copyright (c) 2016, Jonne Valola
+    All rights reserved.
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+    3. Neither the name of the copyright holders nor the
+    names of its contributors may be used to endorse or promote products
+    derived from this software without specific prior written permission.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
+    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
+    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/**************************************************************************/
+
+/*
+ * NOTE:
+ * API of the Pokitto Sound library is partially identical to the Gamebuino Sound API.
+ * Due to the difference in architecture (ARM Cortex-M0+ in mbed environment vs. 8-bit AVR)
+ * large parts are not identical. Most functions were rewritten, with only API remaining.
+ * We want to give attribution to the original author's project:
+ *
+ * License for Gamebuino-identical code:
+ *
+ * (C) Copyright 2014 Aur�lien Rodot. All rights reserved.
+ *
+ * This file is part of the Gamebuino Library (http://gamebuino.com)
+ *
+ * The Gamebuino Library is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+#include "PokittoSound.h"
+#include "Pokitto_settings.h"
+#include "PokittoCore.h"
+#include "Synth.h"
+
+#ifndef POK_SIM
+#include "HWSound.h"
+#else
+#include "SimSound.h"
+#include "PokittoSimulator.h"
+#endif
+
+typedef uint8_t byte;
+
+using namespace Pokitto;
+
+Pokitto::Core c;
+
+uint8_t Sound::prescaler;
+uint16_t Sound::globalVolume;
+uint16_t Sound::volumeMax = VOLUME_HEADPHONE_MAX;
+
+bool Sound::trackIsPlaying[NUM_CHANNELS];
+bool Sound::patternIsPlaying[NUM_CHANNELS];
+uint8_t Sound::outputPitch[NUM_CHANNELS];
+int8_t Sound::outputVolume[NUM_CHANNELS];
+
+uint16_t *Sound::trackData[NUM_CHANNELS];
+uint8_t Sound::trackCursor[NUM_CHANNELS];
+uint16_t **Sound::patternSet[NUM_CHANNELS];
+int8_t Sound::patternPitch[NUM_CHANNELS];
+
+// pattern data
+uint16_t *Sound::patternData[NUM_CHANNELS];
+uint16_t **Sound::instrumentSet[NUM_CHANNELS];
+bool Sound::patternLooping[NUM_CHANNELS];
+uint16_t Sound::patternCursor[NUM_CHANNELS];
+
+// note data
+uint8_t Sound::notePitch[NUM_CHANNELS];
+uint8_t Sound::noteDuration[NUM_CHANNELS];
+int8_t Sound::noteVolume[NUM_CHANNELS];
+bool Sound::notePlaying[NUM_CHANNELS];
+
+// commands data
+int8_t Sound::commandsCounter[NUM_CHANNELS];
+int8_t Sound::volumeSlideStepDuration[NUM_CHANNELS];
+int8_t Sound::volumeSlideStepSize[NUM_CHANNELS];
+uint8_t Sound::arpeggioStepDuration[NUM_CHANNELS];
+int8_t Sound::arpeggioStepSize[NUM_CHANNELS];
+uint8_t Sound::tremoloStepDuration[NUM_CHANNELS];
+int8_t Sound::tremoloStepSize[NUM_CHANNELS];
+
+// instrument data
+uint16_t *Sound::instrumentData[NUM_CHANNELS];
+uint8_t Sound::instrumentLength[NUM_CHANNELS]; //number of steps in the instrument
+uint8_t Sound::instrumentLooping[NUM_CHANNELS]; //how many steps to loop on when the last step of the instrument is reached
+uint16_t Sound::instrumentCursor[NUM_CHANNELS]; //which step is being played
+uint8_t Sound::instrumentNextChange[NUM_CHANNELS]; //how many frames before the next step
+
+//current step data
+int8_t Sound::stepVolume[NUM_CHANNELS];
+uint8_t Sound::stepPitch[NUM_CHANNELS];
+uint8_t Sound::chanVolumes[NUM_CHANNELS];
+
+#if (POK_ENABLE_SOUND < 1)
+ #define NUM_CHANNELS 0
+#endif
+
+#if(NUM_CHANNELS > 0)
+    #ifndef POK_SIM
+        uint8_t sbyte;
+    #else
+    uint8_t sbyte;
+    float pwm1;
+    #endif // POK_SIM
+
+//declare these variables globally for faster access
+uint8_t _rand = 1;
+uint8_t _chanCount[NUM_CHANNELS]; //counts until the next change of the waveform
+bool _chanState[NUM_CHANNELS]; //if the waveform is currently high or low
+uint8_t _chanHalfPeriod[NUM_CHANNELS]; //duration of half the period of the waveform
+uint8_t _chanOutputVolume[NUM_CHANNELS]; //amplitude of the outputted waveform
+uint8_t _chanOutput[NUM_CHANNELS]; //current value of the outputted waveform
+bool _chanNoise[NUM_CHANNELS]; //if a random value should be added to the waveform to generate noise
+
+#if POK_GBSOUND > 0
+const uint16_t squareWaveInstrument[]  = {0x0101, 0x03F7};
+const uint16_t noiseInstrument[]  = {0x0101, 0x03FF};
+const uint16_t* const defaultInstruments[]  = {squareWaveInstrument,noiseInstrument};
+
+const uint16_t playOKPattern[]  = {0x0005,0x138,0x168,0x0000};
+const uint16_t playCancelPattern[]  = {0x0005,0x168,0x138,0x0000};
+const uint16_t playTickP[]  = {0x0045,0x168,0x0000};
+#endif
+#if(EXTENDED_NOTE_RANGE > 0)
+//extended note range
+#define NUM_PITCH 59
+const uint8_t _halfPeriods[NUM_PITCH] = {246,232,219,207,195,184,174,164,155,146,138,130,123,116,110,104,98,92,87,82,78,73,69,65,62,58,55,52,49,46,44,41,39,37,35,33,31,29,28,26,25,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6};
+#else
+//regular note range
+#define NUM_PITCH 36
+const uint8_t _halfPeriods[NUM_PITCH] = {246,232,219,207,195,184,174,164,155,146,138,130,123,116,110,104,98,92,87,82,78,73,69,65,62,58,55,52,49,46,44,41,39,37,35,33};
+#endif
+
+#endif
+
+void Pokitto::audio_IRQ() {
+    #if POK_STREAMING_MUSIC > 0
+        #if POK_STREAMFREQ_HALVE > 0
+        streamstep = 1-streamstep;
+        #else
+        streamstep=1;
+        #endif
+
+        streamstep &= streamon; //check if stream is on
+		
+        if(streamvol && streamstep) {
+            uint8_t output = (*currentPtr++);
+            sbyte = output;
+        } else {
+            sbyte = 0; // duty cycle
+        }
+
+        if (currentPtr >= endPtr)
+        {
+        currentBuffer++;
+        if (currentBuffer==4) currentBuffer=0;
+        currentPtr = buffers[currentBuffer];
+        endPtr = currentPtr + BUFFER_SIZE;
+        }
+
+    #endif // POK_STREAMING_MUSIC
+    #if (NUM_CHANNELS > 0)
+	Sound::generateOutput();
+    #endif
+}
+
+void Sound::volumeUp() {
+    if (globalVolume>VOLUME_HEADPHONE_MAX) setVolume(getVolume()+VOLUME_STEP*2);
+    else setVolume(getVolume()+VOLUME_STEP);
+}
+
+void Sound::volumeDown() {
+    if (globalVolume>VOLUME_HEADPHONE_MAX) setVolume(getVolume()-VOLUME_STEP*4);
+    else setVolume(getVolume()-VOLUME_STEP);
+}
+
+
+void Sound::setMaxVol(int16_t v) {
+    if (v < 0) v = 0; //prevent nasty wraparound
+    if (v > VOLUME_SPEAKER_MAX) {
+            v = VOLUME_SPEAKER_MAX;
+    }
+    volumeMax = v;
+    setVolume(globalVolume);
+}
+
+uint16_t Sound::getMaxVol() {
+    return volumeMax;
+}
+
+void Sound::updateStream() {
+    #if POK_STREAMING_MUSIC
+    if (oldBuffer != currentBuffer) {
+        if (currentBuffer==0) fileReadBytes(&buffers[3][0],BUFFER_SIZE);
+        else if (currentBuffer==1) fileReadBytes(&buffers[0][0],BUFFER_SIZE);
+        else if (currentBuffer==2) fileReadBytes(&buffers[1][0],BUFFER_SIZE);
+        else fileReadBytes(&buffers[2][0],BUFFER_SIZE);
+        oldBuffer = currentBuffer;
+        streamcounter += BUFFER_SIZE;
+    }
+
+    #ifndef POK_SIM
+    if ( streamcounter > fs.fsize - (BUFFER_SIZE)) {
+    #else
+    if ( streamcounter > getFileLength() - (BUFFER_SIZE)) {
+    #endif
+        streamcounter=0;
+        #if POK_STREAM_LOOP > 0
+        fileRewind();
+        #endif
+        #ifndef POK_SIM
+        streamon=0;
+        #endif // POK_SIM
+    }
+    #endif
+}
+
+void Sound::begin() {
+#if POK_ENABLE_SOUND > 1
+soundInit();
+#endif
+#if (NUM_CHANNELS > 0)
+#if POK_ENABLE_SOUND > 0
+#if POK_GBSOUND > 0
+	prescaler = 1;
+	for(byte i=0; i<NUM_CHANNELS; i++){
+		chanVolumes[i] = VOLUME_CHANNEL_MAX;
+		changeInstrumentSet(defaultInstruments, i); //load default instruments. #0:square wave, #1: noise
+		command(CMD_INSTRUMENT, 0, 0, i); //set the default instrument to square wave
+	}
+#endif // POK_GBSOUND
+#endif //POK_ENABLE_SOUND
+#endif
+}
+
+void Sound::playTrack(const uint16_t* track, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	stopTrack(channel);
+	trackCursor[channel] = 0;
+	trackData[channel] = (uint16_t*)track;
+	trackIsPlaying[channel] = true;
+#endif
+}
+
+void Sound::stopTrack(uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	trackIsPlaying[channel] = false;
+	stopPattern(channel);
+#endif
+}
+
+void Sound::stopTrack(){
+#if(NUM_CHANNELS > 0)
+	for(uint8_t i=0; i<NUM_CHANNELS; i++){
+		stopTrack(i);
+	}
+#endif
+}
+
+void Sound::updateTrack(uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	if(trackIsPlaying[channel] && !patternIsPlaying[channel]){
+		uint16_t data = pgm_read_word(trackData[channel] + trackCursor[channel]);
+		if(data == 0xFFFF){ //en of the track
+			trackIsPlaying[channel] = false;
+			return;
+		}
+		uint8_t patternID = data & 0xFF;
+		data >>= 8;
+		patternPitch[channel] = data;
+		playPattern((const uint16_t*)pgm_read_word(&(patternSet[channel][patternID])), channel);
+		trackCursor[channel] ++;
+	}
+#endif
+}
+
+void Sound::updateTrack(){
+#if(NUM_CHANNELS > 0)
+	for (uint8_t i=0; i<NUM_CHANNELS; i++){
+		updateTrack(i);
+	}
+#endif
+}
+
+void Sound::changePatternSet(const uint16_t* const* patterns, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	patternSet[channel] = (uint16_t**)patterns;
+#endif
+}
+
+void Sound::playPattern(const uint16_t* pattern, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	stopPattern(channel);
+	patternData[channel] = (uint16_t*)pattern;
+	patternCursor[channel] = 0;
+	patternIsPlaying[channel] = true;
+	noteVolume[channel] = 9;
+	//reinit commands
+	volumeSlideStepDuration[channel] = 0;
+	arpeggioStepDuration[channel] = 0;
+	tremoloStepDuration[channel] = 0;
+#endif
+}
+
+void Sound::updatePattern(){
+#if(NUM_CHANNELS > 0)
+	for (uint8_t i=0; i<NUM_CHANNELS; i++){
+		updatePattern(i);
+	}
+#endif
+}
+
+void Sound::changeInstrumentSet(const uint16_t* const* instruments, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	instrumentSet[channel] = (uint16_t**)instruments;
+#endif
+}
+
+void Sound::updatePattern(uint8_t i){
+#if(NUM_CHANNELS > 0)
+	if(i>=NUM_CHANNELS)
+		return;
+	if(patternIsPlaying[i]){
+		if(noteDuration[i]==0){//if the end of the previous note is reached
+
+			uint16_t data = pgm_read_word(patternCursor[i] + patternData[i]);
+
+			if(data == 0){ //end of the pattern reached
+				if(patternLooping[i] == true){
+					patternCursor[i] = 0;
+					data = pgm_read_word(patternCursor[i] + patternData[i]);
+				}
+				else{
+					patternIsPlaying[i] = false;
+					if(trackIsPlaying[i]){ //if this pattern is part of a track, get the next pattern
+						updateTrack(i);
+						data = pgm_read_word(patternCursor[i] + patternData[i]);
+					} else {
+						stopNote(i);
+						//Serial.print("pattern end\n");
+						return;
+					}
+				}
+			}
+
+			while (data & 0x0001){ //read all commands and instrument changes
+				data >>= 2;
+				//Serial.print("\ncmd\t");
+				uint8_t cmd = data & 0x0F;
+				data >>= 4;
+				uint8_t X = data & 0x1F;
+				data >>= 5;
+				int8_t Y = data - 16;
+				command(cmd,X,Y,i);
+				patternCursor[i]++;
+				data = pgm_read_word(patternCursor[i] + patternData[i]);
+			}
+			data >>= 2;
+
+			uint8_t pitch = data & 0x003F;
+			data >>= 6;
+
+			uint8_t duration = data;
+			if(pitch != 63){
+			}
+
+			playNote(pitch, duration, i);
+
+			patternCursor[i]++;
+		}
+	}
+#endif
+}
+
+void Sound::stopPattern(uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	stopNote(channel);
+	patternIsPlaying[channel] = false;
+#endif
+}
+
+void Sound::stopPattern(){
+#if(NUM_CHANNELS > 0)
+	for(uint8_t i=0; i<NUM_CHANNELS; i++){
+		stopPattern(i);
+	}
+#endif
+}
+
+void Sound::command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t i){
+#if(NUM_CHANNELS > 0)
+	if(i>=NUM_CHANNELS)
+		return;
+	switch(cmd){
+	case CMD_VOLUME: //volume
+	    X = constrain((int8_t)X, 0, 10);
+		noteVolume[i] = X;
+		break;
+	case CMD_INSTRUMENT: //instrument
+		instrumentData[i] = (uint16_t*)pgm_read_word(&(instrumentSet[i][X]));
+		instrumentLength[i] = pgm_read_word(&(instrumentData[i][0])) & 0x00FF; //8 LSB
+		instrumentLength[i] *= prescaler;
+		instrumentLooping[i] = min2((pgm_read_word(&(instrumentData[i][0])) >> 8), instrumentLength[i]); //8 MSB - check that the loop is shorter than the instrument length
+		instrumentLooping[i] *= prescaler;
+		break;
+	case CMD_SLIDE: //volume slide
+		volumeSlideStepDuration[i] = X * prescaler;
+		volumeSlideStepSize[i] = Y;
+		break;
+	case CMD_ARPEGGIO:
+		arpeggioStepDuration[i] = X * prescaler;
+		arpeggioStepSize[i] = Y;
+		break;
+	case CMD_TREMOLO:
+		tremoloStepDuration[i] = X * prescaler;
+		tremoloStepSize[i] = Y;
+		break;
+	default:
+		break;
+	}
+#endif
+}
+
+void Sound::playNote(uint8_t pitch, uint8_t duration, uint8_t channel){
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	//set note
+	notePitch[channel] = pitch;
+	noteDuration[channel] = duration * prescaler;
+	//reinit vars
+	instrumentNextChange[channel] = 0;
+	instrumentCursor[channel] = 0;
+	notePlaying[channel] = true;
+	_chanState[channel] = true;
+	commandsCounter[channel] = 0;
+#endif
+}
+
+void Sound::stopNote(uint8_t channel) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	notePlaying[channel] = false;
+	//counters
+	noteDuration[channel] = 0;
+	instrumentCursor[channel] = 0;
+	commandsCounter[channel] = 0;
+	//output
+	_chanOutput[channel] = 0;
+	_chanOutputVolume[channel] = 0;
+	_chanState[channel] = false;
+	updateOutput();
+#endif
+}
+
+void Sound::stopNote() {
+#if(NUM_CHANNELS > 0)
+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) {
+		stopNote(channel);
+	}
+#endif
+}
+
+void Sound::updateNote() {
+#if(NUM_CHANNELS > 0)
+	for (uint8_t i = 0; i < NUM_CHANNELS; i++) {
+		updateNote(i);
+	}
+#endif
+}
+
+void Sound::updateNote(uint8_t i) {
+#if(NUM_CHANNELS > 0)
+	if(i>=NUM_CHANNELS)
+		return;
+	if (notePlaying[i]) {
+
+		if(noteDuration[i] == 0){
+			stopNote(i);
+			//Serial.println("note end");
+			return;
+		} else {
+			noteDuration[i]--;
+		}
+
+		if (instrumentNextChange[i] == 0) {
+
+			//read the step data from the progmem and decode it
+			uint16_t thisStep = pgm_read_word(&(instrumentData[i][1 + instrumentCursor[i]]));
+
+			stepVolume[i] = thisStep & 0x0007;
+			thisStep >>= 3;
+
+			uint8_t stepNoise = thisStep & 0x0001;
+			thisStep >>= 1;
+
+			uint8_t stepDuration = thisStep & 0x003F;
+			thisStep >>= 6;
+
+			stepPitch[i] = thisStep;
+
+			//apply the step settings
+			instrumentNextChange[i] = stepDuration * prescaler;
+
+			_chanNoise[i] = stepNoise;
+
+
+			instrumentCursor[i]++;
+
+			if (instrumentCursor[i] >= instrumentLength[i]) {
+				if (instrumentLooping[i]) {
+					instrumentCursor[i] = instrumentLength[i] - instrumentLooping[i];
+				} else {
+					stopNote(i);
+				}
+			}
+		}
+		instrumentNextChange[i]--;
+
+		commandsCounter[i]++;
+
+		//UPDATE VALUES
+		//pitch
+		outputPitch[i] = notePitch[i] + stepPitch[i] + patternPitch[i];
+		if(arpeggioStepDuration[i]){
+		  outputPitch[i] += commandsCounter[i] / arpeggioStepDuration[i] * arpeggioStepSize[i];
+		}
+		outputPitch[i] = (outputPitch[i] + NUM_PITCH) % NUM_PITCH; //wrap
+		//volume
+		outputVolume[i] = noteVolume[i];
+		if(volumeSlideStepDuration[i]){
+		  outputVolume[i] += commandsCounter[i] / volumeSlideStepDuration[i] * volumeSlideStepSize[i];
+		}
+		if(tremoloStepDuration[i]){
+			outputVolume[i] += ((commandsCounter[i]/tremoloStepDuration[i]) % 2) * tremoloStepSize[i];
+		}
+		outputVolume[i] = constrain(outputVolume[i], 0, 9);
+		if(notePitch[i] == 63){
+			outputVolume[i] = 0;
+		}
+		// jonnehw noInterrupts();
+		_chanHalfPeriod[i] = pgm_read_byte(_halfPeriods + outputPitch[i]);
+		_chanOutput[i] = _chanOutputVolume[i] = outputVolume[i] * (globalVolume>>GLOBVOL_SHIFT) * chanVolumes[i] * stepVolume[i];
+		//Serial.println(outputVolume[i]);
+		// jonnehw interrupts();
+	}
+#endif
+}
+
+void Sound::setChannelHalfPeriod(uint8_t channel, uint8_t halfPeriod) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	_chanHalfPeriod[channel] = halfPeriod;
+	_chanState[channel] = false;
+	_chanCount[channel] = 0;
+	updateOutput();
+#endif
+}
+
+
+void Sound::generateOutput() {
+#if(NUM_CHANNELS > 0)
+	bool outputChanged = false;
+	//no for loop here, for the performance sake (this function runs 15 000 times per second...)
+	//CHANNEL 0
+	if (_chanOutputVolume[0]) {
+		_chanCount[0]++;
+		if (_chanCount[0] >= _chanHalfPeriod[0]) {
+			outputChanged = true;
+			_chanState[0] = !_chanState[0];
+			_chanCount[0] = 0;
+			if (_chanNoise[0]) {
+				_rand = 67 * _rand + 71;
+				_chanOutput[0] = _rand % _chanOutputVolume[0];
+			}
+		}
+	}
+
+
+	//CHANNEL 1
+	#if (NUM_CHANNELS > 1)
+	if (_chanOutputVolume[1]) {
+		_chanCount[1]++;
+		if (_chanCount[1] >= _chanHalfPeriod[1]) {
+			outputChanged = true;
+			_chanState[1] = !_chanState[1];
+			_chanCount[1] = 0;
+			if (_chanNoise[1]) {
+				_rand = 67 * _rand + 71;
+				_chanOutput[1] = _rand % _chanOutputVolume[1];
+			}
+		}
+	}
+	#endif
+
+	//CHANNEL 2
+	#if (NUM_CHANNELS > 2)
+	if (_chanOutputVolume[2]) {
+		_chanCount[2]++;
+		if (_chanCount[2] >= _chanHalfPeriod[2]) {
+			outputChanged = true;
+			_chanState[2] = !_chanState[2];
+			_chanCount[2] = 0;
+			if (_chanNoise[2]) {
+				_rand = 67 * _rand + 71;
+				_chanOutput[2] = _rand % _chanOutputVolume[2];
+			}
+		}
+	}
+	#endif
+
+	//CHANNEL 3
+	#if (NUM_CHANNELS > 3)
+	if (_chanOutputVolume[3]) {
+		_chanCount[3]++;
+		if (_chanCount[3] >= _chanHalfPeriod[3]) {
+			outputChanged = true;
+			_chanState[3] = !_chanState[3];
+			_chanCount[3] = 0;
+			if (_chanNoise[3]) {
+				_rand = 67 * _rand + 71;
+				_chanOutput[3] = _rand % _chanOutputVolume[3];
+			}
+		}
+	}
+	#endif
+
+    #if POK_STREAMING_MUSIC
+        if (streamstep) {
+            outputChanged=true;
+        }
+    #endif
+
+	if (outputChanged) {
+		updateOutput();
+	}
+#endif
+}
+
+void Sound::updateOutput() {
+#if(NUM_CHANNELS > 0)
+	uint8_t output = 0;
+
+	//CHANNEL 0
+	if (_chanState[0]) {
+		output += _chanOutput[0];
+	}
+
+	//CHANNEL 1
+	#if (NUM_CHANNELS > 1)
+	if (_chanState[1]) {
+		output += _chanOutput[1];
+	}
+	#endif
+
+	//CHANNEL 2
+	#if (NUM_CHANNELS > 2)
+	if (_chanState[2]) {
+		output += _chanOutput[2];
+	}
+	#endif
+
+	//CHANNEL 3
+	#if (NUM_CHANNELS > 3)
+	if (_chanState[3]) {
+		output += _chanOutput[3];
+	}
+	#endif
+
+    #ifndef POK_SIM
+    #if POK_ENABLE_SOUND
+    /** HARDWARE **/
+
+        #if POK_STREAMING_MUSIC
+            if (streamstep) {
+                pwmout_write(&audiopwm,(float)sbyte/(float)255);
+            }
+        #endif
+            dac_write((uint8_t)output); //direct hardware mixing baby !
+    soundbyte = output;
+    #endif //POK_ENABLE_SOUND
+    #else
+    /** SIMULATOR **/
+        #if POK_STREAMING_MUSIC
+            if (streamstep) {
+                uint16_t o = output + sbyte;
+                output = o/2;
+            }
+        #endif
+        soundbyte = output;
+    #endif // POK_SIM
+#endif
+}
+
+void Sound::setPatternLooping(bool loop, uint8_t channel) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	patternLooping[channel] = loop;
+#endif
+}
+
+void Sound::playOK(){
+    #if POK_GBSOUND
+#if(NUM_CHANNELS > 0)
+	playPattern(playOKPattern,0);
+#endif
+#endif // POK_GBSOUND
+}
+
+void Sound::playCancel(){
+#if POK_GBSOUND
+#if(NUM_CHANNELS > 0)
+	playPattern(playCancelPattern,0);
+#endif
+#endif
+}
+
+void Sound::playTick(){
+#if POK_GBSOUND
+#if(NUM_CHANNELS > 0)
+	playPattern(playTickP,0);
+#endif
+#endif // POK_GBSOUND
+}
+
+void Sound::setVolume(int16_t volume) {
+//#if NUM_CHANNELS > 0
+	if (volume<0) volume = 0;
+	if (volume>volumeMax) volume = volumeMax;
+	globalVolume = volume; // % (volumeMax+1);
+	#ifndef POK_SIM
+	volume = (volume / 2)-10;
+	if (volume<0) volume = 0;
+	#if POK_ENABLE_SOUND > 1
+	setHWvolume(volume);
+	#endif
+	#endif
+	#if POK_SHOW_VOLUME > 0
+	c.volbar_visible = VOLUMEBAR_TIMEOUT;
+	#endif
+//#endif
+}
+
+uint16_t Sound::getVolume() {
+//#if NUM_CHANNELS > 0
+	return globalVolume;
+//#else
+//	return 0;
+//#endif
+}
+
+void Sound::setVolume(int8_t volume, uint8_t channel) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return;
+	volume = (volume > VOLUME_CHANNEL_MAX) ? VOLUME_CHANNEL_MAX : volume;
+	volume = (volume < 0) ? 0 : volume;
+	chanVolumes[channel] = volume;
+#endif
+}
+
+uint8_t Sound::getVolume(uint8_t channel) {
+#if(NUM_CHANNELS > 0)
+	if(channel>=NUM_CHANNELS)
+		return 255;
+	return (chanVolumes[channel]);
+#else
+	return 0;
+#endif
+}
+
+void Sound::playTone(uint8_t os, int frq, uint8_t amp, uint8_t wav,uint8_t arpmode)
+{
+    if (wav>5) wav=0;
+    if (arpmode>MAX_ARPMODE) arpmode=MAX_ARPMODE;
+    if (os==1) setOSC(&osc1,1,wav,1,0,0,frq,amp,0,0,0,0,0,0,arpmode,0,0);
+    else if (os==2) setOSC(&osc2,1,wav,1,0,0,frq,amp,0,0,0,0,0,0,arpmode,0,0);
+    else if (os==3) setOSC(&osc3,1,wav,1,0,0,frq,amp,0,0,0,0,0,0,arpmode,0,0);
+}
+
+void Sound::playTone(uint8_t os, uint16_t frq, uint8_t volume, uint32_t duration)
+{
+    if (os==1) setOSC(&osc1,1,WSAW,frq,volume,duration);
+    else if (os==2) setOSC(&osc2,1,WTRI,frq,volume,duration);
+    else if (os==3) setOSC(&osc3,1,WTRI,frq,volume,duration);
+}
+
+uint8_t Sound::ampIsOn()
+{
+    #ifdef POK_SIM
+    return core.ampIsOn();
+    #else
+    #if POK_ENABLE_SOUND > 1
+    return Pokitto::ampIsOn();
+    #endif
+    #endif // POK_SIM
+    return 0;
+}
+
+void Sound::ampEnable(uint8_t v) {
+    #ifdef POK_SIM
+    core.ampEnable(v);
+    #else
+    #if POK_ENABLE_SOUND > 1
+    Pokitto::ampEnable(v);
+    #endif
+    #endif // POK_SIM
+
+}
+
+int Sound::playMusicStream(char* filename)
+{
+    return playMusicStream(filename,0);
+}
+
+int Sound::playMusicStream()
+{
+    #if POK_STREAMING_MUSIC >0
+    if (currentPtr) {
+            pokPlayStream();
+            return 1;
+    } else return 0; //no stream
+    #endif // POK_STREAMING_MUSIC
+}
+
+void Sound::pauseMusicStream() {
+    #if POK_ENABLE_SOUND > 1
+    pokPauseStream();
+    #endif
+}
+
+int Sound::playMusicStream(char* filename, uint8_t options)
+{
+    #if POK_STREAMING_MUSIC
+        uint8_t result;
+        result = pokInitSD();
+        if (!isThisFileOpen(filename)) {
+            fileClose(); // close any open files
+            result = fileOpen(filename,FILE_MODE_OVERWRITE | FILE_MODE_BINARY);
+        }
+
+        if (result) {
+                currentPtr = 0; // mark that no stream is available
+                return 0; // opening music file failed
+        }
+
+        fileReadBytes(&buffers[0][0],BUFFER_SIZE);
+        fileReadBytes(&buffers[1][0],BUFFER_SIZE);
+        fileReadBytes(&buffers[2][0],BUFFER_SIZE);
+        fileReadBytes(&buffers[3][0],BUFFER_SIZE);
+        currentBuffer = 0;
+        currentPtr = buffers[currentBuffer];
+        endPtr = currentPtr + BUFFER_SIZE;
+
+        //streaming = STR_PLAYING|options;
+
+        if (!options) pokPlayStream(); // activate stream
+    #endif //POK_STREAMING_MUSIC
+    return 1; // opening music file succeeded
+}
+