Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of PokittoLib by
POKITTO_CORE/PokittoSound.cpp
- Committer:
- Pokitto
- Date:
- 2017-09-19
- Revision:
- 4:ecf2fe370c1c
- Parent:
- 2:968589ca3484
File content as of revision 4:ecf2fe370c1c:
/**************************************************************************/
/*!
@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
}
