Platform library for RETRO
Revision 0:6f26c31d8573, committed 2015-03-01
- Comitter:
- Architect
- Date:
- Sun Mar 01 05:29:45 2015 +0000
- Commit message:
- RetroPlatform Library for RETRO gaming system
Changed in this revision
diff -r 000000000000 -r 6f26c31d8573 Display.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Display.cpp Sun Mar 01 05:29:45 2015 +0000 @@ -0,0 +1,142 @@ +/* + * (C) Copyright 2015 Valentin Ivanov. All rights reserved. + * + * This file is part of the RetroPlatform Library + * + * The RetroPlatform 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/> + * + * This library is inspired by Gamebuino Library (http://gamebuino.com) + * from Aurélien Rodot. + */ +#include "mbed.h" +#include "Display.h" + +Display::Display( PinName backlightPin, PinName resetPin, PinName dsPin, + PinName mosiPin, PinName misoPin, PinName clkPin, PinName csPin, PanelColorFilter colorFilter ) + : LCD_ST7735(backlightPin, resetPin, dsPin, mosiPin, misoPin, clkPin, csPin, colorFilter ) +{ +} + + +void Display::drawBitmapIndexed(uint8_t x, uint8_t y, const uint8_t *pbmp) +{ + drawBitmapIndexed(x, y, pbmp[0], pbmp[1], pbmp+2); +} + +void Display::drawBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *pbmp) +{ + clip(x, y, w, h); + int bytes = w * h / 8; + beginBatchCommand(CMD_RAMWR); + + while(bytes--) { +// writeBatchData(_pPalette[*pbmp>>7&0x1]); +// writeBatchData(_pPalette[*pbmp>>6&0x1]); +// writeBatchData(_pPalette[*pbmp>>5&0x1]); +// writeBatchData(_pPalette[*pbmp>>4&0x1]); +// writeBatchData(_pPalette[*pbmp>>3&0x1]); +// writeBatchData(_pPalette[*pbmp>>2&0x1]); +// writeBatchData(_pPalette[*pbmp>>1&0x1]); +// writeBatchData(_pPalette[(*pbmp++)&0x1]); + } + endBatchCommand(); +} + +void Display::drawBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *pbmp, const uint16_t *palette, bool mirrored) +{ + clip(x, y, w, h); + int pixels = w * h; + beginBatchCommand(CMD_RAMWR); + + if( mirrored ) { + for( int iy =0; iy < h; iy++ ) { + const uint8_t * p = pbmp + (w>>1)*iy + (w>>1)-1; + for( int ix =0; ix < w; ) { + writeBatchData(palette[(*p)&0x0F]); + writeBatchData(palette[((*p)>>4)&0x0F]); + ix+=2; + p--; + } + } + } else { + while(pixels) { + writeBatchData(palette[((*pbmp)>>4)&0x0F]); + writeBatchData(palette[(*pbmp)&0x0F]); + pixels-=2; + pbmp++; + } + } + endBatchCommand(); +} + +//void Display::drawBitmapTransparent(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *pbmp, const uint16_t *palette, uint8_t color, bool mirrored ) +//{ +// clip(x, y, w, h); +// int pixels = w * h; +// beginBatchCommand(CMD_RAMWR); +// +// if( mirrored ) { +// for( int iy =0; iy < h; iy++ ) { +// const uint8_t * p = pbmp + w*iy + w-1; +// for( int ix =0; ix < w; ix++ ) { +// { +// writeBatchData(palette[*p--]); +// } +// } +// } +// } else { +// while(pixels--) { +// writeBatchData(palette[*pbmp++]); +// } +// } +// endBatchCommand(); +//} + +void Display::drawBitmapIndexed(uint8_t x, uint8_t y, int w, int h, int src_x, int src_y, int srcWidth, int srcHeight, const uint8_t *pbmp, const uint16_t *palette) +{ + clip(x, y, srcWidth, srcHeight); + beginBatchCommand(CMD_RAMWR); + const uint8_t *p = pbmp + src_x + (src_y * w); + for(int iy = 0; iy < srcHeight; ++iy) { + for(int ix = 0; ix < srcWidth; ++ix) { + writeBatchData(palette[*(p + ix)]); + } + p += w; + } + endBatchCommand(); +} + +void Display::eraseBitmapIndexed(uint8_t x, uint8_t y, const uint8_t *pbmp) +{ + eraseBitmapIndexed(x, y, pbmp[0], pbmp[1], pbmp+2); +} + +void Display::eraseBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *pbmp) +{ + clip(x, y, w, h); + int bytes = w * h / 8; + beginBatchCommand(CMD_RAMWR); + + while(bytes--) { +// writeBatchData(_pPalette[0]); +// writeBatchData(_pPalette[0]); +// writeBatchData(_pPalette[0]); +// writeBatchData(_pPalette[0]); +// writeBatchData(_pPalette[0]); +// writeBatchData(_pPalette[0]); +// writeBatchData(_pPalette[0]); +// writeBatchData(_pPalette[0]); + } + endBatchCommand(); +} \ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Display.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Display.h Sun Mar 01 05:29:45 2015 +0000 @@ -0,0 +1,54 @@ +/* + * (C) Copyright 2015 Valentin Ivanov. All rights reserved. + * + * This file is part of the RetroPlatform Library + * + * The RetroPlatform 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/> + * + * This library is inspired by Gamebuino Library (http://gamebuino.com) + * from Aurélien Rodot. + */ +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "LCD_ST7735.h" + + +class Display : public LCD_ST7735 +{ + +public: + Display( + PinName backlightPin, + PinName resetPin, + PinName dsPin, + PinName mosiPin, + PinName misoPin, + PinName clkPin, + PinName csPin, + PanelColorFilter colorFilter = BGR + ); + + void drawBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *pbmp); + void drawBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *pbmp, const uint16_t *palette, bool mirrored = false); + //void drawBitmapTransparent(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *pbmp, const uint16_t *palette, uint8_t color, bool mirrored = false); + void drawBitmapIndexed(uint8_t x, uint8_t y, int w, int h, int src_x, int src_y, int srcWidth, int srcHeight, const uint8_t *pbmp, const uint16_t *palette); + + void drawBitmapIndexed(uint8_t x, uint8_t y, const uint8_t *pbmp); + void eraseBitmapIndexed(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *pbmp); + void eraseBitmapIndexed(uint8_t x, uint8_t y, const uint8_t *pbmp); +}; + + +#endif //DISPLAY_H \ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Retro.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Retro.cpp Sun Mar 01 05:29:45 2015 +0000 @@ -0,0 +1,179 @@ +/* + * (C) Copyright 2015 Valentin Ivanov. All rights reserved. + * + * This file is part of the RetroPlatform Library + * + * The RetroPlatform 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/> + * + * This library is inspired by Gamebuino Library (http://gamebuino.com) + * from Aurélien Rodot. + */ +#include "Retro.h" +#include "us_ticker_api.h" + +const uint16_t OKSequence[] = {0x0005,0x138,0x168,0x0000}; +const uint16_t CancelSequence[] = {0x0005,0x168,0x138,0x0000}; +const uint16_t TickSequence[] = {0x0045,0x168,0x0000}; + +DigitalIn Retro::pin[NUM_BTN] = { + DigitalIn(P0_14, PullUp), //left + DigitalIn(P0_11, PullUp), //right + DigitalIn(P0_12, PullUp), //down + DigitalIn(P0_13, PullUp), //up + DigitalIn(P0_16, PullUp), //robot + DigitalIn(P0_1, PullUp) //ship +}; + +Retro::Retro(): display( + P0_19, + P0_20, + P0_7, + P0_21, + P0_22, + P1_15, + P0_2, + LCD_ST7735::RGB), leftEye(P0_9,false), rightEye(P0_8,false) +{ + initialize(); +} + + +void Retro::initialize() +{ + timePerFrame = 50000; + frameStartUs = us_ticker_read(); + + display.setOrientation(LCD_ST7735::Rotate270, false); + display.setForegroundColor(Color565::White); + display.setBackgroundColor(Color565::Black); + + sound.initialize(); + sound.setVolume(1); + + setFrameRate(20); +} + +void Retro::setFrameRate(uint8_t fps) +{ + timePerFrame = 1000000 / fps; + sound.prescaler = fps / 20; + sound.prescaler = max(1, sound.prescaler); +} + +bool once = false; + +bool Retro::update() +{ + uint32_t current_time = us_ticker_read(); + frameDurationUs = current_time - frameStartUs; + + if( frameDurationUs > timePerFrame ) { + frameStartUs = current_time; + + + once = false; + + readButtons(); + return true; + } else { + if( !once ) { + once = true; + sound.update(); + + } + return false; + } +} + +void Retro::playOK() +{ + sound.playSequence(OKSequence,0); +} + +void Retro::playCancel() +{ + sound.playSequence(CancelSequence,0); +} + +void Retro::playTick() +{ + sound.playSequence(TickSequence,0); +} + +bool Retro::collideCheck( int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) +{ + return !( x2 >= x1+w1 || x1 >= x2+w2 || y2 >= y1+h1 || y1 >= y2 + h2 ); +} + +//Buttons +void Retro::readButtons() +{ + for (uint8_t thisButton = 0; thisButton < NUM_BTN; thisButton++) { + if ( pin[thisButton].read() == 0 ) { + _state[thisButton]++; + } else { + if (_state[thisButton] == 0) + continue; + if (_state[thisButton] == 0xFF) + _state[thisButton] = 0; + else + _state[thisButton] = 0xFF; + } + } +} + +bool Retro::pressed(uint8_t button) +{ + if (_state[button] == 1) + return true; + else + return false; +} + +bool Retro::released(uint8_t button) +{ + if (_state[button] == 0xFF) + return true; + else + return false; +} + +bool Retro::held(uint8_t button, uint8_t time) +{ + if(_state[button] == (time+1)) + return true; + else + return false; +} + +bool Retro::repeat(uint8_t button, uint8_t period) +{ + if (period <= 1) { + if ((_state[button] != 0xFF) && (_state[button])) + return true; + } else { + if ((_state[button] != 0xFF) && ((_state[button] % period) == 1)) + return true; + } + return false; +} + +uint8_t Retro::timeHeld(uint8_t button) +{ + if(_state[button] != 0xFF) + return _state[button]; + else + return 0; + +} \ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Retro.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Retro.h Sun Mar 01 05:29:45 2015 +0000 @@ -0,0 +1,87 @@ +/* + * (C) Copyright 2015 Valentin Ivanov. All rights reserved. + * + * This file is part of the RetroPlatform Library + * + * The RetroPlatform 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/> + * + * This library is inspired by Gamebuino Library (http://gamebuino.com) + * from Aurélien Rodot. + */ +#ifndef __RETRO_H__ +#define __RETRO_H__ + +#include "mbed.h" +#include "Sound.h" +#include "Display.h" +#include "Color565.h" + +#define LCDWIDTH 160 +#define LCDHEIGHT 128 + +#define BTN_LEFT 0 +#define BTN_RIGHT 1 +#define BTN_DOWN 2 +#define BTN_UP 3 +#define BTN_ROBOT 4 +#define BTN_SHIP 5 + +#define NUM_BTN 6 + +class Retro +{ +private: + + uint32_t frameStartUs, frameEndUs; + +private: + static DigitalIn pin[NUM_BTN]; + uint8_t _state[NUM_BTN]; + +public: + uint32_t timePerFrame; + uint32_t frameDurationUs; + + Sound sound; + Display display; + + DigitalOut leftEye; + DigitalOut rightEye; + +public: + Retro(); + void initialize(); + bool update(); + + void setFrameRate(uint8_t fps); + static bool collideCheck( int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2); + + void playOK(); + void playCancel(); + void playTick(); + + void readButtons(); + bool pressed(uint8_t button); + bool released(uint8_t button); + bool held(uint8_t button, uint8_t time); + bool repeat(uint8_t button, uint8_t period); + uint8_t timeHeld(uint8_t button); + +}; + + +uint8_t max(uint8_t val1, uint8_t val2 ); +uint8_t min(uint8_t val1, uint8_t val2 ); + +#endif //__RETRO_H__ \ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Sound.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sound.cpp Sun Mar 01 05:29:45 2015 +0000 @@ -0,0 +1,515 @@ +/* + * (C) Copyright 2015 Valentin Ivanov. All rights reserved. + * + * This file is part of the RetroPlatform Library + * + * The RetroPlatform 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/> + * + * This library is inspired by Gamebuino Library (http://gamebuino.com) + * from Aurélien Rodot. + */ +#include "Sound.h" +#include "Utils.h" + +#if(NUM_CHANNELS > 0) +uint8_t _rand = 1; +const uint16_t squareWaveInstrument[] = {0x0101, 0x03F7}; +const uint16_t noiseInstrument[] = {0x0101, 0x03FF}; +const uint16_t* const defaultInstruments[] = {squareWaveInstrument,noiseInstrument}; + +#define NUM_PITCH 59 + +const uint8_t _halfPeriods[NUM_PITCH] = { + /*268,*/253,239,225,213,201,190,179,169,159,150,142, + 134,127,119,113,106,100,95,89,84,80,75,71,67,63,60, + 56,53,50,47,45,42,40,38,36,34,32,30,28,27,25,24,22, + 21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6 +}; + +#endif + +Sound::Sound() : speaker(P0_18) +{ + initialize(); +} + +void Sound::initialize() +{ +#if(NUM_CHANNELS > 0) + volumeMax = VOLUME_GLOBAL_MAX; + globalVolume = VOLUME_GLOBAL_MAX; + prescaler = 1; + + speaker.period_us(32); + speaker.write(0.0); + + for(uint8_t channel=0; channel<NUM_CHANNELS; channel++) { + chanVolumes[channel] = VOLUME_CHANNEL_MAX; + changeInstrumentSet(defaultInstruments, channel); + command(CMD_INSTRUMENT, 0, 0, channel); + } + timer.attach_us(this, &Sound::generateOutput, 16); //62500Hz +#endif +} + +void Sound::playTrack(const uint16_t* track, uint8_t channel) +{ +#if(NUM_CHANNELS > 0) + if(channel>=NUM_CHANNELS) + return; + stopTrack(channel); + tracks[channel].Cursor = 0; + tracks[channel].Data = (uint16_t*)track; + tracks[channel].IsPlaying = true; +#endif +} + +void Sound::stopTrack(uint8_t channel) +{ +#if(NUM_CHANNELS > 0) + if(channel>=NUM_CHANNELS) + return; + tracks[channel].IsPlaying = false; + stopSequence(channel); +#endif +} + +void Sound::updateTrack(uint8_t channel) +{ +#if(NUM_CHANNELS > 0) + if(channel>=NUM_CHANNELS) + return; + if(tracks[channel].IsPlaying && !sequences[channel].IsPlaying) { + uint16_t data = tracks[channel].Data[tracks[channel].Cursor]; + if(data == 0xFFFF) { + tracks[channel].IsPlaying = false; + return; + } + uint8_t patternID = data & 0xFF; + data >>= 8; + patternPitch[channel] = data; + playSequence((const uint16_t*)patternSet[channel][patternID], channel); + tracks[channel].Cursor++; + } +#endif +} + + +void Sound::changeSequenceSet(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::playSequence(const uint16_t* pattern, uint8_t channel) +{ +#if(NUM_CHANNELS > 0) + if(channel>=NUM_CHANNELS) + return; + stopSequence(channel); + sequences[channel].Data = (uint16_t*)pattern; + sequences[channel].Cursor = 0; + sequences[channel].IsPlaying = true; + noteVolume[channel] = 9; + //reinit commands + commands[channel].volumeSlideStepDuration = 0; + commands[channel].arpeggioStepDuration = 0; + commands[channel].tremoloStepDuration = 0; +#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::updateSequence(uint8_t channel) +{ +#if(NUM_CHANNELS > 0) + if(channel>=NUM_CHANNELS) + return; + if(sequences[channel].IsPlaying) { + if(noteDuration[channel]==0) { //if the end of the previous note is reached + + uint16_t data = sequences[channel].Data[sequences[channel].Cursor]; + + if(data == 0) { //end of the pattern reached + if(sequences[channel].Looping == true) { + sequences[channel].Cursor = 0; + data = sequences[channel].Data[sequences[channel].Cursor]; + } else { + sequences[channel].IsPlaying = false; + if(tracks[channel].IsPlaying) { //if this pattern is part of a track, get the next pattern + updateTrack(channel); + data = sequences[channel].Data[sequences[channel].Cursor]; + } else { + stopNote(channel); + return; + } + } + } + + while (data & 0x0001) { //read all commands and instrument changes + data >>= 2; + uint8_t cmd = data & 0x0F; + data >>= 4; + uint8_t X = data & 0x1F; + data >>= 5; + int8_t Y = data - 16; + command(cmd,X,Y,channel); + sequences[channel].Cursor++; + data = sequences[channel].Data[sequences[channel].Cursor]; + } + data >>= 2; + + uint8_t pitch = data & 0x003F; + data >>= 6; + + uint8_t duration = data; + + playNote(pitch, duration, channel); + + sequences[channel].Cursor++; + } + } +#endif +} + +void Sound::stopSequence(uint8_t channel) +{ +#if(NUM_CHANNELS > 0) + if(channel>=NUM_CHANNELS) + return; + stopNote(channel); + sequences[channel].IsPlaying = false; +#endif +} + +void Sound::command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t channel) +{ +#if(NUM_CHANNELS > 0) + if(channel>=NUM_CHANNELS) + return; + + switch(cmd) { + case CMD_VOLUME: //volume + X = constrain(X, 0, 10); + noteVolume[channel] = X; + break; + case CMD_INSTRUMENT: //instrument + instruments[channel].Data = (uint16_t*)instrumentSet[channel][X]; + instruments[channel].Length = instruments[channel].Data[0] & 0x00FF; //8 LSB + instruments[channel].Length *= prescaler; + instruments[channel].Looping = min(instruments[channel].Data[0] >> 8, instruments[channel].Length); //8 MSB - check that the loop is shorter than the instrument length + instruments[channel].Looping *= prescaler; + break; + case CMD_SLIDE: //volume slide + commands[channel].volumeSlideStepDuration = X * prescaler; + commands[channel].volumeSlideStepSize = Y; + break; + case CMD_ARPEGGIO: + commands[channel].arpeggioStepDuration = X * prescaler; + commands[channel].arpeggioStepSize = Y; + break; + case CMD_TREMOLO: + commands[channel].tremoloStepDuration = X * prescaler; + commands[channel].tremoloStepSize = 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; + notePlaying[channel] = true; + + //reinit vars + instruments[channel].NextChange = 0; + instruments[channel].Cursor = 0; + + commands[channel].Counter = 0; + + _chanState[channel] = true; +#endif +} + +void Sound::stopNote(uint8_t channel) +{ +#if(NUM_CHANNELS > 0) + if(channel>=NUM_CHANNELS) + return; + notePlaying[channel] = false; + //counters + noteDuration[channel] = 0; + instruments[channel].Cursor = 0; + commands[channel].Counter = 0; + //output + _chanOutput[channel] = 0; + _chanOutputVolume[channel] = 0; + _chanState[channel] = false; + updateOutput(); +#endif +} + +void Sound::updateNote(uint8_t channel) +{ +#if(NUM_CHANNELS > 0) + if(channel>=NUM_CHANNELS) + return; + if (notePlaying[channel]) { + + if(noteDuration[channel] == 0) { + stopNote(channel); + return; + } else { + noteDuration[channel]--; + } + + if (instruments[channel].NextChange == 0) { + + //read the step data from the progmem and decode it + uint16_t thisStep = instruments[channel].Data[1 + instruments[channel].Cursor]; + + stepVolume[channel] = thisStep & 0x0007; + thisStep >>= 3; + + uint8_t stepNoise = thisStep & 0x0001; + thisStep >>= 1; + + uint8_t stepDuration = thisStep & 0x003F; + thisStep >>= 6; + + stepPitch[channel] = thisStep; + + //apply the step settings + instruments[channel].NextChange = stepDuration * prescaler; + + _chanNoise[channel] = stepNoise; + + instruments[channel].Cursor++; + + if (instruments[channel].Cursor >= instruments[channel].Length) { + if (instruments[channel].Looping) { + instruments[channel].Cursor = instruments[channel].Length - instruments[channel].Looping; + } else { + stopNote(channel); + } + } + } + instruments[channel].NextChange--; + + commands[channel].Counter++; + + outputPitch[channel] = notePitch[channel] + stepPitch[channel] + patternPitch[channel]; + + if(commands[channel].arpeggioStepDuration) + outputPitch[channel] += commands[channel].Counter / commands[channel].arpeggioStepDuration * commands[channel].arpeggioStepSize; + + outputPitch[channel] = outputPitch[channel] % NUM_PITCH; //wrap + + //volume + outputVolume[channel] = noteVolume[channel]; + if(commands[channel].volumeSlideStepDuration) + outputVolume[channel] += commands[channel].Counter / commands[channel].volumeSlideStepDuration * commands[channel].volumeSlideStepSize; + + if(commands[channel].tremoloStepDuration) + outputVolume[channel] += ((commands[channel].Counter / commands[channel].tremoloStepDuration) % 2) * commands[channel].tremoloStepSize; + + outputVolume[channel] = constrain(outputVolume[channel], 0, 9); + + if(notePitch[channel] == 63) + outputVolume[channel] = 0; + + __disable_irq(); + _chanHalfPeriod[channel] = _halfPeriods[outputPitch[channel]]; + _chanOutput[channel] = _chanOutputVolume[channel] = outputVolume[channel] * globalVolume * chanVolumes[channel] * stepVolume[channel]; + __enable_irq(); + } +#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) + boolean 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 (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 + speaker.write(output/255.0); +#endif +} + + +void Sound::setVolume(int8_t volume) +{ +#if NUM_CHANNELS > 0 + globalVolume = volume % (volumeMax+1); +#endif +} + +uint8_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 +} \ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Sound.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Sound.h Sun Mar 01 05:29:45 2015 +0000 @@ -0,0 +1,224 @@ +/* + * (C) Copyright 2015 Valentin Ivanov. All rights reserved. + * + * This file is part of the RetroPlatform Library + * + * The RetroPlatform 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/> + * + * This library is inspired by Gamebuino Library (http://gamebuino.com) + * from Aurélien Rodot. + */ +#ifndef SOUND_H +#define SOUND_H + +#include <mbed.h> + +//commands +#define CMD_VOLUME 0 +#define CMD_INSTRUMENT 1 +#define CMD_SLIDE 2 +#define CMD_ARPEGGIO 3 +#define CMD_TREMOLO 4 + + +#define NUM_CHANNELS 3 //number of sound channels (no more than 4) + +#define VOLUME_GLOBAL_MAX 1 + +//7=instrument volume 9=note volume +#define VOLUME_CHANNEL_MAX 255/NUM_CHANNELS/VOLUME_GLOBAL_MAX/7/9 + +#define boolean bool + +//A set of sequences +typedef struct { + uint16_t *Data; + uint8_t Cursor; + bool IsPlaying; +} Track; + +//A sequence of commands and notes +typedef struct { + uint16_t *Data; + bool Looping; + uint16_t Cursor; + bool IsPlaying; +} Sequence; + +//A note of selected pitch, duration and volume +typedef struct { + uint8_t Pitch; + uint8_t Duration; + int8_t Volume; + bool IsPlaying; +} Note; + +typedef struct { + int8_t Counter; + int8_t volumeSlideStepDuration; + int8_t volumeSlideStepSize; + uint8_t arpeggioStepDuration; + int8_t arpeggioStepSize; + uint8_t tremoloStepDuration; + int8_t tremoloStepSize; + +} Command; + +typedef struct { + uint16_t **SequenceSet; + uint16_t **InstrumentSet; + + uint8_t Volume; + uint8_t Count; + bool State; + uint8_t HalfPeriod; + uint8_t OutputVolume; + uint8_t Output; + bool Noise; + + + + int8_t SequencePitch; +} AudioChannel; + +typedef struct { + uint16_t *Data; + uint8_t Length; + uint8_t Looping; + uint16_t Cursor; + uint8_t NextChange; +} Instrument; + +class Sound { + + Ticker timer; + PwmOut speaker; + + Track tracks[NUM_CHANNELS]; + Sequence sequences[NUM_CHANNELS]; + Command commands[NUM_CHANNELS]; + Instrument instruments[NUM_CHANNELS]; + +public: + Sound(); + void initialize(); + + void update() + { + updateTracks(); + updateSequences(); + updateNotes(); + } + + void playTrack(const uint16_t* track, uint8_t channel); + void updateTrack(uint8_t channel); + void stopTrack(uint8_t channel); + void stopTracks() { + for(uint8_t channel=0; channel<NUM_CHANNELS; channel++) + stopTrack(channel); + } + void updateTracks() { + for(uint8_t channel=0; channel<NUM_CHANNELS; channel++) + updateTrack(channel); + } + + void changeSequenceSet(const uint16_t* const* patterns, uint8_t channel); + + bool isTrackPlaying(uint8_t channel) + { + return tracks[channel].IsPlaying; + } + + void playSequence(const uint16_t* pattern, uint8_t channel); + void changeInstrumentSet(const uint16_t* const* instruments, uint8_t channel); + void updateSequence(uint8_t channel); + void stopSequence(uint8_t channel); + void stopSequences() { + for(uint8_t channel=0; channel<NUM_CHANNELS; channel++) + stopSequence(channel); + } + void updateSequences() { + for(uint8_t channel=0; channel<NUM_CHANNELS; channel++) + updateSequence(channel); + } + + + void playNote(uint8_t pitch, uint8_t duration, uint8_t channel); + void updateNote(uint8_t i); + void stopNote(uint8_t channel); + void updateNotes() { + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) + updateNote(channel); + } + void stopNotes() { + for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) + stopNote(channel); + } + + void command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t channel); + + uint8_t outputPitch[NUM_CHANNELS]; + int8_t outputVolume[NUM_CHANNELS]; + + void setVolume(int8_t volume); + uint8_t getVolume(); + void setVolume(int8_t volume, uint8_t channel); + uint8_t getVolume(uint8_t channel); + + + uint8_t prescaler; + + void setChannelHalfPeriod(uint8_t channel, uint8_t halfPeriod); + + + uint8_t globalVolume; + uint8_t volumeMax; + +#if (NUM_CHANNELS > 0) + //tracks data + uint16_t **patternSet[NUM_CHANNELS]; + int8_t patternPitch[NUM_CHANNELS]; + + // pattern data + uint16_t **instrumentSet[NUM_CHANNELS]; + + // note data + uint8_t notePitch[NUM_CHANNELS]; + uint8_t noteDuration[NUM_CHANNELS]; + int8_t noteVolume[NUM_CHANNELS]; + boolean notePlaying[NUM_CHANNELS]; + + + //current step data + int8_t stepVolume[NUM_CHANNELS]; + uint8_t stepPitch[NUM_CHANNELS]; + + uint8_t chanVolumes[NUM_CHANNELS]; + + + uint8_t _chanCount[NUM_CHANNELS]; //counts until the next change of the waveform + boolean _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 + boolean _chanNoise[NUM_CHANNELS]; //if a random value should be added to the waveform to generate noise + +#endif + void updateOutput(); + +private: + void generateOutput(); +}; + +#endif /* SOUND_H */ \ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Utils.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils.cpp Sun Mar 01 05:29:45 2015 +0000 @@ -0,0 +1,82 @@ +/* + * (C) Copyright 2015 Valentin Ivanov. All rights reserved. + * + * This file is part of the RetroPlatform Library + * + * The RetroPlatform 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/> + * + * This library is inspired by Gamebuino Library (http://gamebuino.com) + * from Aurélien Rodot. + */ +#include "mbed.h" +#include "Utils.h" + +uint8_t constrain(uint8_t val, uint8_t minimum, uint8_t maximum ) +{ + if( val < minimum ) + return minimum; + if( val > maximum ) + return maximum; + + return val; +} + +uint8_t max(uint8_t val1, uint8_t val2 ) +{ + if( val1 < val2 ) + return val2; + + return val1; +} + +uint8_t min(uint8_t val1, uint8_t val2 ) +{ + if( val1 < val2 ) + return val1; + + return val2; +} + +#define IAP_LOCATION 0x1fff1ff1 +typedef void (*IAP_call)(unsigned int [], unsigned int []); + +IAP_call iap_entry = reinterpret_cast<IAP_call>(IAP_LOCATION); +unsigned int IAP_command[ 5 ]; +unsigned int IAP_result[ 5 ]; + +int write_eeprom( char *source_addr, char *target_addr, int size ) +{ + IAP_command[ 0 ] = 61; + IAP_command[ 1 ] = (unsigned int)target_addr; // Destination EEPROM address where data bytes are to be written. This address should be a 256 byte boundary. + IAP_command[ 2 ] = (unsigned int)source_addr; // Source RAM address from which data bytes are to be read. This address should be a word boundary. + IAP_command[ 3 ] = size; // Number of bytes to be written. Should be 256 | 512 | 1024 | 4096. + IAP_command[ 4 ] = SystemCoreClock / 1000; // CPU Clock Frequency (CCLK) in kHz. + + iap_entry( IAP_command, IAP_result ); + + return ( (int)IAP_result[ 0 ] ); +} + +int read_eeprom( char *source_addr, char *target_addr, int size ) +{ + IAP_command[ 0 ] = 62; + IAP_command[ 1 ] = (unsigned int)source_addr; // Source EEPROM address from which data bytes are to be read. This address should be a word boundary. + IAP_command[ 2 ] = (unsigned int)target_addr; // Destination RAM address where data bytes are to be written. This address should be a 256 byte boundary. + IAP_command[ 3 ] = size; // Number of bytes to be written. Should be 256 | 512 | 1024 | 4096. + IAP_command[ 4 ] = SystemCoreClock / 1000; // CPU Clock Frequency (CCLK) in kHz. + + iap_entry( IAP_command, IAP_result ); + + return ( (int)IAP_result[ 0 ] ); +} \ No newline at end of file
diff -r 000000000000 -r 6f26c31d8573 Utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Utils.h Sun Mar 01 05:29:45 2015 +0000 @@ -0,0 +1,32 @@ +/* + * (C) Copyright 2015 Valentin Ivanov. All rights reserved. + * + * This file is part of the RetroPlatform Library + * + * The RetroPlatform 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/> + * + * This library is inspired by Gamebuino Library (http://gamebuino.com) + * from Aurélien Rodot. + */ +#ifndef __UTILS_H__ +#define __UTILS_H__ + +uint8_t constrain(uint8_t val, uint8_t minimum, uint8_t maximum ); +uint8_t max(uint8_t val1, uint8_t val2 ); +uint8_t min(uint8_t val1, uint8_t val2 ); + +int write_eeprom( char *source_addr, char *target_addr, int size ); +int read_eeprom( char *source_addr, char *target_addr, int size ); + +#endif //__UTILS_H__ \ No newline at end of file