PokittoLib is the library needed for programming the Pokitto DIY game console (www.pokitto.com)
Dependents: YATTT sd_map_test cPong SnowDemo ... more
PokittoLib
Library for programming Pokitto hardware
How to Use
- Import this library to online compiler (see button "import" on the right hand side
- DO NOT import mbed-src anymore, a better version is now included inside PokittoLib
- Change My_settings.h according to your project
- Start coding!
POKITTO_CORE/PokittoSound.cpp
- Committer:
- Pokitto
- Date:
- 2019-12-25
- Revision:
- 71:531419862202
- Parent:
- 66:6281a40d73e6
File content as of revision 71:531419862202:
/**************************************************************************/ /*! @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" #ifdef PROJ_SDFS_STREAMING #include "SDFSDisk.h" #endif #else #include "SimSound.h" #include "PokittoSimulator.h" #endif typedef uint8_t byte; using namespace Pokitto; #ifdef PROJ_SND_DEBUG int __sx=0; #endif /** discrete hardware volume control **/ uint8_t Pokitto::discrete_vol = 0; uint8_t const Pokitto::discrete_vol_levels[8] {0,32,64,96,128,160,192,224}; uint8_t const Pokitto::discrete_vol_hw_levels[8] {0,27,64,96,36,117,127,127}; uint8_t const Pokitto::discrete_vol_multipliers[8] {0,127,127,127,192,192,255,255}; Pokitto::Core _soundc; const uint8_t *Sound::sfxDataPtr = 0; const uint8_t *Sound::sfxEndPtr = 0; uint8_t Sound::prescaler; uint16_t Sound::globalVolume; uint16_t Sound::volumeMax = VOLUME_HEADPHONE_MAX; uint8_t Sound::headPhoneLevel=1; 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) #ifdef NUM_CHANNELS #undef NUM_CHANNELS #define NUM_CHANNELS 0 #endif // NUM_CHANNELS #endif #if(NUM_CHANNELS > 0) #ifndef POK_SIM uint32_t sbyte; #else uint32_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() { Pokitto::discrete_vol++; if (discrete_vol>7) discrete_vol=7; globalVolume = discrete_vol_levels[discrete_vol]; setVolume(globalVolume); //if (globalVolume>VOLUME_HEADPHONE_MAX) setVolume(getVolume()+VOLUME_STEP*2); //else setVolume(getVolume()+VOLUME_STEP); } void Sound::volumeDown() { if (discrete_vol) Pokitto::discrete_vol--; globalVolume = discrete_vol_levels[discrete_vol]; setVolume(globalVolume); //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(); #else #ifndef POK_SIM streamon=0; #endif // POK_SIM #endif // POK_STREAM_LOOP } #endif } void Sound::begin() { #if POK_ENABLE_SOUND > 0 soundInit(); ampEnable(true); #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]; //_chanOutput[i] = _chanOutputVolume[i] = outputVolume[i] * (globalVolume) * 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) uint32_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>>headPhoneLevel)/(float)255); sbyte *= discrete_vol_multipliers[discrete_vol]; sbyte >>= 8; pwmout_write(&audiopwm,(float)(sbyte)/(float)255); } #endif output *= discrete_vol_multipliers[discrete_vol]; output >>= 8; 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);//>>headPhoneLevel; } #endif soundbyte = output;//<<headPhoneLevel; #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; //#if POK_ENABLE_SOUND > 0 discrete_vol = (volume>>5); #ifndef POK_SIM #if POK_ENABLE_SOUND > 0 setHWvolume(discrete_vol_hw_levels[discrete_vol]); //boost volume if headphonelevel #endif #endif //#endif #if POK_SHOW_VOLUME > 0 _soundc.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>MAX_WAVETYPES) 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,WSQUARE,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 > 0 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 > 0 Pokitto::ampEnable(v); #endif #endif // POK_SIM } int Sound::playMusicStream(char* filename) { return playMusicStream(filename,FILE_MODE_READONLY | FILE_MODE_BINARY); } int Sound::playMusicStream() { #if POK_STREAMING_MUSIC >0 if (currentPtr) { pokPlayStream(); return 1; } #endif // POK_STREAMING_MUSIC return 0; //no stream } void Sound::pauseMusicStream() { #if POK_ENABLE_SOUND > 0 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_READONLY | FILE_MODE_BINARY); }else{ fileRewind(); result = 0; } 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 } uint32_t Sound::getMusicStreamElapsedSec() { #if POK_STREAMING_MUSIC return streamcounter/POK_AUD_FREQ; #endif return 0; } uint32_t Sound::getMusicStreamElapsedMilliSec() { #if POK_STREAMING_MUSIC return streamcounter/(POK_AUD_FREQ/1000); #endif return 0; } void Sound::loadSampleToOsc(uint8_t os, uint8_t* data, uint32_t datasize) { OSC* o; if (os==3) o = &osc3; else if (os==2) o = &osc2; else o = &osc1; o->sample = data; o->samplelength = datasize; o->samplepos = 0; }