Platform library for RETRO

Dependents:   RETRO_RickGame

Committer:
Architect
Date:
Sun Mar 01 05:29:45 2015 +0000
Revision:
0:6f26c31d8573
RetroPlatform Library for RETRO gaming system

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Architect 0:6f26c31d8573 1 /*
Architect 0:6f26c31d8573 2 * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
Architect 0:6f26c31d8573 3 *
Architect 0:6f26c31d8573 4 * This file is part of the RetroPlatform Library
Architect 0:6f26c31d8573 5 *
Architect 0:6f26c31d8573 6 * The RetroPlatform Library is free software: you can redistribute it and/or modify
Architect 0:6f26c31d8573 7 * it under the terms of the GNU Lesser General Public License as published by
Architect 0:6f26c31d8573 8 * the Free Software Foundation, either version 3 of the License, or
Architect 0:6f26c31d8573 9 * (at your option) any later version.
Architect 0:6f26c31d8573 10 *
Architect 0:6f26c31d8573 11 * This program is distributed in the hope that it will be useful,
Architect 0:6f26c31d8573 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Architect 0:6f26c31d8573 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Architect 0:6f26c31d8573 14 * GNU Lesser General Public License for more details.
Architect 0:6f26c31d8573 15 *
Architect 0:6f26c31d8573 16 * You should have received a copy of the GNU Lesser General Public License
Architect 0:6f26c31d8573 17 * along with this program. If not, see <http://www.gnu.org/licenses/>
Architect 0:6f26c31d8573 18 *
Architect 0:6f26c31d8573 19 * This library is inspired by Gamebuino Library (http://gamebuino.com)
Architect 0:6f26c31d8573 20 * from Aurélien Rodot.
Architect 0:6f26c31d8573 21 */
Architect 0:6f26c31d8573 22 #include "Sound.h"
Architect 0:6f26c31d8573 23 #include "Utils.h"
Architect 0:6f26c31d8573 24
Architect 0:6f26c31d8573 25 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 26 uint8_t _rand = 1;
Architect 0:6f26c31d8573 27 const uint16_t squareWaveInstrument[] = {0x0101, 0x03F7};
Architect 0:6f26c31d8573 28 const uint16_t noiseInstrument[] = {0x0101, 0x03FF};
Architect 0:6f26c31d8573 29 const uint16_t* const defaultInstruments[] = {squareWaveInstrument,noiseInstrument};
Architect 0:6f26c31d8573 30
Architect 0:6f26c31d8573 31 #define NUM_PITCH 59
Architect 0:6f26c31d8573 32
Architect 0:6f26c31d8573 33 const uint8_t _halfPeriods[NUM_PITCH] = {
Architect 0:6f26c31d8573 34 /*268,*/253,239,225,213,201,190,179,169,159,150,142,
Architect 0:6f26c31d8573 35 134,127,119,113,106,100,95,89,84,80,75,71,67,63,60,
Architect 0:6f26c31d8573 36 56,53,50,47,45,42,40,38,36,34,32,30,28,27,25,24,22,
Architect 0:6f26c31d8573 37 21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6
Architect 0:6f26c31d8573 38 };
Architect 0:6f26c31d8573 39
Architect 0:6f26c31d8573 40 #endif
Architect 0:6f26c31d8573 41
Architect 0:6f26c31d8573 42 Sound::Sound() : speaker(P0_18)
Architect 0:6f26c31d8573 43 {
Architect 0:6f26c31d8573 44 initialize();
Architect 0:6f26c31d8573 45 }
Architect 0:6f26c31d8573 46
Architect 0:6f26c31d8573 47 void Sound::initialize()
Architect 0:6f26c31d8573 48 {
Architect 0:6f26c31d8573 49 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 50 volumeMax = VOLUME_GLOBAL_MAX;
Architect 0:6f26c31d8573 51 globalVolume = VOLUME_GLOBAL_MAX;
Architect 0:6f26c31d8573 52 prescaler = 1;
Architect 0:6f26c31d8573 53
Architect 0:6f26c31d8573 54 speaker.period_us(32);
Architect 0:6f26c31d8573 55 speaker.write(0.0);
Architect 0:6f26c31d8573 56
Architect 0:6f26c31d8573 57 for(uint8_t channel=0; channel<NUM_CHANNELS; channel++) {
Architect 0:6f26c31d8573 58 chanVolumes[channel] = VOLUME_CHANNEL_MAX;
Architect 0:6f26c31d8573 59 changeInstrumentSet(defaultInstruments, channel);
Architect 0:6f26c31d8573 60 command(CMD_INSTRUMENT, 0, 0, channel);
Architect 0:6f26c31d8573 61 }
Architect 0:6f26c31d8573 62 timer.attach_us(this, &Sound::generateOutput, 16); //62500Hz
Architect 0:6f26c31d8573 63 #endif
Architect 0:6f26c31d8573 64 }
Architect 0:6f26c31d8573 65
Architect 0:6f26c31d8573 66 void Sound::playTrack(const uint16_t* track, uint8_t channel)
Architect 0:6f26c31d8573 67 {
Architect 0:6f26c31d8573 68 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 69 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 70 return;
Architect 0:6f26c31d8573 71 stopTrack(channel);
Architect 0:6f26c31d8573 72 tracks[channel].Cursor = 0;
Architect 0:6f26c31d8573 73 tracks[channel].Data = (uint16_t*)track;
Architect 0:6f26c31d8573 74 tracks[channel].IsPlaying = true;
Architect 0:6f26c31d8573 75 #endif
Architect 0:6f26c31d8573 76 }
Architect 0:6f26c31d8573 77
Architect 0:6f26c31d8573 78 void Sound::stopTrack(uint8_t channel)
Architect 0:6f26c31d8573 79 {
Architect 0:6f26c31d8573 80 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 81 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 82 return;
Architect 0:6f26c31d8573 83 tracks[channel].IsPlaying = false;
Architect 0:6f26c31d8573 84 stopSequence(channel);
Architect 0:6f26c31d8573 85 #endif
Architect 0:6f26c31d8573 86 }
Architect 0:6f26c31d8573 87
Architect 0:6f26c31d8573 88 void Sound::updateTrack(uint8_t channel)
Architect 0:6f26c31d8573 89 {
Architect 0:6f26c31d8573 90 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 91 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 92 return;
Architect 0:6f26c31d8573 93 if(tracks[channel].IsPlaying && !sequences[channel].IsPlaying) {
Architect 0:6f26c31d8573 94 uint16_t data = tracks[channel].Data[tracks[channel].Cursor];
Architect 0:6f26c31d8573 95 if(data == 0xFFFF) {
Architect 0:6f26c31d8573 96 tracks[channel].IsPlaying = false;
Architect 0:6f26c31d8573 97 return;
Architect 0:6f26c31d8573 98 }
Architect 0:6f26c31d8573 99 uint8_t patternID = data & 0xFF;
Architect 0:6f26c31d8573 100 data >>= 8;
Architect 0:6f26c31d8573 101 patternPitch[channel] = data;
Architect 0:6f26c31d8573 102 playSequence((const uint16_t*)patternSet[channel][patternID], channel);
Architect 0:6f26c31d8573 103 tracks[channel].Cursor++;
Architect 0:6f26c31d8573 104 }
Architect 0:6f26c31d8573 105 #endif
Architect 0:6f26c31d8573 106 }
Architect 0:6f26c31d8573 107
Architect 0:6f26c31d8573 108
Architect 0:6f26c31d8573 109 void Sound::changeSequenceSet(const uint16_t* const* patterns, uint8_t channel)
Architect 0:6f26c31d8573 110 {
Architect 0:6f26c31d8573 111 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 112 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 113 return;
Architect 0:6f26c31d8573 114 patternSet[channel] = (uint16_t**)patterns;
Architect 0:6f26c31d8573 115 #endif
Architect 0:6f26c31d8573 116 }
Architect 0:6f26c31d8573 117
Architect 0:6f26c31d8573 118 void Sound::playSequence(const uint16_t* pattern, uint8_t channel)
Architect 0:6f26c31d8573 119 {
Architect 0:6f26c31d8573 120 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 121 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 122 return;
Architect 0:6f26c31d8573 123 stopSequence(channel);
Architect 0:6f26c31d8573 124 sequences[channel].Data = (uint16_t*)pattern;
Architect 0:6f26c31d8573 125 sequences[channel].Cursor = 0;
Architect 0:6f26c31d8573 126 sequences[channel].IsPlaying = true;
Architect 0:6f26c31d8573 127 noteVolume[channel] = 9;
Architect 0:6f26c31d8573 128 //reinit commands
Architect 0:6f26c31d8573 129 commands[channel].volumeSlideStepDuration = 0;
Architect 0:6f26c31d8573 130 commands[channel].arpeggioStepDuration = 0;
Architect 0:6f26c31d8573 131 commands[channel].tremoloStepDuration = 0;
Architect 0:6f26c31d8573 132 #endif
Architect 0:6f26c31d8573 133 }
Architect 0:6f26c31d8573 134
Architect 0:6f26c31d8573 135 void Sound::changeInstrumentSet(const uint16_t* const* instruments, uint8_t channel)
Architect 0:6f26c31d8573 136 {
Architect 0:6f26c31d8573 137 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 138 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 139 return;
Architect 0:6f26c31d8573 140 instrumentSet[channel] = (uint16_t**)instruments;
Architect 0:6f26c31d8573 141 #endif
Architect 0:6f26c31d8573 142 }
Architect 0:6f26c31d8573 143
Architect 0:6f26c31d8573 144 void Sound::updateSequence(uint8_t channel)
Architect 0:6f26c31d8573 145 {
Architect 0:6f26c31d8573 146 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 147 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 148 return;
Architect 0:6f26c31d8573 149 if(sequences[channel].IsPlaying) {
Architect 0:6f26c31d8573 150 if(noteDuration[channel]==0) { //if the end of the previous note is reached
Architect 0:6f26c31d8573 151
Architect 0:6f26c31d8573 152 uint16_t data = sequences[channel].Data[sequences[channel].Cursor];
Architect 0:6f26c31d8573 153
Architect 0:6f26c31d8573 154 if(data == 0) { //end of the pattern reached
Architect 0:6f26c31d8573 155 if(sequences[channel].Looping == true) {
Architect 0:6f26c31d8573 156 sequences[channel].Cursor = 0;
Architect 0:6f26c31d8573 157 data = sequences[channel].Data[sequences[channel].Cursor];
Architect 0:6f26c31d8573 158 } else {
Architect 0:6f26c31d8573 159 sequences[channel].IsPlaying = false;
Architect 0:6f26c31d8573 160 if(tracks[channel].IsPlaying) { //if this pattern is part of a track, get the next pattern
Architect 0:6f26c31d8573 161 updateTrack(channel);
Architect 0:6f26c31d8573 162 data = sequences[channel].Data[sequences[channel].Cursor];
Architect 0:6f26c31d8573 163 } else {
Architect 0:6f26c31d8573 164 stopNote(channel);
Architect 0:6f26c31d8573 165 return;
Architect 0:6f26c31d8573 166 }
Architect 0:6f26c31d8573 167 }
Architect 0:6f26c31d8573 168 }
Architect 0:6f26c31d8573 169
Architect 0:6f26c31d8573 170 while (data & 0x0001) { //read all commands and instrument changes
Architect 0:6f26c31d8573 171 data >>= 2;
Architect 0:6f26c31d8573 172 uint8_t cmd = data & 0x0F;
Architect 0:6f26c31d8573 173 data >>= 4;
Architect 0:6f26c31d8573 174 uint8_t X = data & 0x1F;
Architect 0:6f26c31d8573 175 data >>= 5;
Architect 0:6f26c31d8573 176 int8_t Y = data - 16;
Architect 0:6f26c31d8573 177 command(cmd,X,Y,channel);
Architect 0:6f26c31d8573 178 sequences[channel].Cursor++;
Architect 0:6f26c31d8573 179 data = sequences[channel].Data[sequences[channel].Cursor];
Architect 0:6f26c31d8573 180 }
Architect 0:6f26c31d8573 181 data >>= 2;
Architect 0:6f26c31d8573 182
Architect 0:6f26c31d8573 183 uint8_t pitch = data & 0x003F;
Architect 0:6f26c31d8573 184 data >>= 6;
Architect 0:6f26c31d8573 185
Architect 0:6f26c31d8573 186 uint8_t duration = data;
Architect 0:6f26c31d8573 187
Architect 0:6f26c31d8573 188 playNote(pitch, duration, channel);
Architect 0:6f26c31d8573 189
Architect 0:6f26c31d8573 190 sequences[channel].Cursor++;
Architect 0:6f26c31d8573 191 }
Architect 0:6f26c31d8573 192 }
Architect 0:6f26c31d8573 193 #endif
Architect 0:6f26c31d8573 194 }
Architect 0:6f26c31d8573 195
Architect 0:6f26c31d8573 196 void Sound::stopSequence(uint8_t channel)
Architect 0:6f26c31d8573 197 {
Architect 0:6f26c31d8573 198 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 199 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 200 return;
Architect 0:6f26c31d8573 201 stopNote(channel);
Architect 0:6f26c31d8573 202 sequences[channel].IsPlaying = false;
Architect 0:6f26c31d8573 203 #endif
Architect 0:6f26c31d8573 204 }
Architect 0:6f26c31d8573 205
Architect 0:6f26c31d8573 206 void Sound::command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t channel)
Architect 0:6f26c31d8573 207 {
Architect 0:6f26c31d8573 208 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 209 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 210 return;
Architect 0:6f26c31d8573 211
Architect 0:6f26c31d8573 212 switch(cmd) {
Architect 0:6f26c31d8573 213 case CMD_VOLUME: //volume
Architect 0:6f26c31d8573 214 X = constrain(X, 0, 10);
Architect 0:6f26c31d8573 215 noteVolume[channel] = X;
Architect 0:6f26c31d8573 216 break;
Architect 0:6f26c31d8573 217 case CMD_INSTRUMENT: //instrument
Architect 0:6f26c31d8573 218 instruments[channel].Data = (uint16_t*)instrumentSet[channel][X];
Architect 0:6f26c31d8573 219 instruments[channel].Length = instruments[channel].Data[0] & 0x00FF; //8 LSB
Architect 0:6f26c31d8573 220 instruments[channel].Length *= prescaler;
Architect 0:6f26c31d8573 221 instruments[channel].Looping = min(instruments[channel].Data[0] >> 8, instruments[channel].Length); //8 MSB - check that the loop is shorter than the instrument length
Architect 0:6f26c31d8573 222 instruments[channel].Looping *= prescaler;
Architect 0:6f26c31d8573 223 break;
Architect 0:6f26c31d8573 224 case CMD_SLIDE: //volume slide
Architect 0:6f26c31d8573 225 commands[channel].volumeSlideStepDuration = X * prescaler;
Architect 0:6f26c31d8573 226 commands[channel].volumeSlideStepSize = Y;
Architect 0:6f26c31d8573 227 break;
Architect 0:6f26c31d8573 228 case CMD_ARPEGGIO:
Architect 0:6f26c31d8573 229 commands[channel].arpeggioStepDuration = X * prescaler;
Architect 0:6f26c31d8573 230 commands[channel].arpeggioStepSize = Y;
Architect 0:6f26c31d8573 231 break;
Architect 0:6f26c31d8573 232 case CMD_TREMOLO:
Architect 0:6f26c31d8573 233 commands[channel].tremoloStepDuration = X * prescaler;
Architect 0:6f26c31d8573 234 commands[channel].tremoloStepSize = Y;
Architect 0:6f26c31d8573 235 break;
Architect 0:6f26c31d8573 236 default:
Architect 0:6f26c31d8573 237 break;
Architect 0:6f26c31d8573 238 }
Architect 0:6f26c31d8573 239 #endif
Architect 0:6f26c31d8573 240 }
Architect 0:6f26c31d8573 241
Architect 0:6f26c31d8573 242 void Sound::playNote(uint8_t pitch, uint8_t duration, uint8_t channel)
Architect 0:6f26c31d8573 243 {
Architect 0:6f26c31d8573 244 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 245 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 246 return;
Architect 0:6f26c31d8573 247
Architect 0:6f26c31d8573 248 //set note
Architect 0:6f26c31d8573 249 notePitch[channel] = pitch;
Architect 0:6f26c31d8573 250 noteDuration[channel] = duration * prescaler;
Architect 0:6f26c31d8573 251 notePlaying[channel] = true;
Architect 0:6f26c31d8573 252
Architect 0:6f26c31d8573 253 //reinit vars
Architect 0:6f26c31d8573 254 instruments[channel].NextChange = 0;
Architect 0:6f26c31d8573 255 instruments[channel].Cursor = 0;
Architect 0:6f26c31d8573 256
Architect 0:6f26c31d8573 257 commands[channel].Counter = 0;
Architect 0:6f26c31d8573 258
Architect 0:6f26c31d8573 259 _chanState[channel] = true;
Architect 0:6f26c31d8573 260 #endif
Architect 0:6f26c31d8573 261 }
Architect 0:6f26c31d8573 262
Architect 0:6f26c31d8573 263 void Sound::stopNote(uint8_t channel)
Architect 0:6f26c31d8573 264 {
Architect 0:6f26c31d8573 265 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 266 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 267 return;
Architect 0:6f26c31d8573 268 notePlaying[channel] = false;
Architect 0:6f26c31d8573 269 //counters
Architect 0:6f26c31d8573 270 noteDuration[channel] = 0;
Architect 0:6f26c31d8573 271 instruments[channel].Cursor = 0;
Architect 0:6f26c31d8573 272 commands[channel].Counter = 0;
Architect 0:6f26c31d8573 273 //output
Architect 0:6f26c31d8573 274 _chanOutput[channel] = 0;
Architect 0:6f26c31d8573 275 _chanOutputVolume[channel] = 0;
Architect 0:6f26c31d8573 276 _chanState[channel] = false;
Architect 0:6f26c31d8573 277 updateOutput();
Architect 0:6f26c31d8573 278 #endif
Architect 0:6f26c31d8573 279 }
Architect 0:6f26c31d8573 280
Architect 0:6f26c31d8573 281 void Sound::updateNote(uint8_t channel)
Architect 0:6f26c31d8573 282 {
Architect 0:6f26c31d8573 283 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 284 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 285 return;
Architect 0:6f26c31d8573 286 if (notePlaying[channel]) {
Architect 0:6f26c31d8573 287
Architect 0:6f26c31d8573 288 if(noteDuration[channel] == 0) {
Architect 0:6f26c31d8573 289 stopNote(channel);
Architect 0:6f26c31d8573 290 return;
Architect 0:6f26c31d8573 291 } else {
Architect 0:6f26c31d8573 292 noteDuration[channel]--;
Architect 0:6f26c31d8573 293 }
Architect 0:6f26c31d8573 294
Architect 0:6f26c31d8573 295 if (instruments[channel].NextChange == 0) {
Architect 0:6f26c31d8573 296
Architect 0:6f26c31d8573 297 //read the step data from the progmem and decode it
Architect 0:6f26c31d8573 298 uint16_t thisStep = instruments[channel].Data[1 + instruments[channel].Cursor];
Architect 0:6f26c31d8573 299
Architect 0:6f26c31d8573 300 stepVolume[channel] = thisStep & 0x0007;
Architect 0:6f26c31d8573 301 thisStep >>= 3;
Architect 0:6f26c31d8573 302
Architect 0:6f26c31d8573 303 uint8_t stepNoise = thisStep & 0x0001;
Architect 0:6f26c31d8573 304 thisStep >>= 1;
Architect 0:6f26c31d8573 305
Architect 0:6f26c31d8573 306 uint8_t stepDuration = thisStep & 0x003F;
Architect 0:6f26c31d8573 307 thisStep >>= 6;
Architect 0:6f26c31d8573 308
Architect 0:6f26c31d8573 309 stepPitch[channel] = thisStep;
Architect 0:6f26c31d8573 310
Architect 0:6f26c31d8573 311 //apply the step settings
Architect 0:6f26c31d8573 312 instruments[channel].NextChange = stepDuration * prescaler;
Architect 0:6f26c31d8573 313
Architect 0:6f26c31d8573 314 _chanNoise[channel] = stepNoise;
Architect 0:6f26c31d8573 315
Architect 0:6f26c31d8573 316 instruments[channel].Cursor++;
Architect 0:6f26c31d8573 317
Architect 0:6f26c31d8573 318 if (instruments[channel].Cursor >= instruments[channel].Length) {
Architect 0:6f26c31d8573 319 if (instruments[channel].Looping) {
Architect 0:6f26c31d8573 320 instruments[channel].Cursor = instruments[channel].Length - instruments[channel].Looping;
Architect 0:6f26c31d8573 321 } else {
Architect 0:6f26c31d8573 322 stopNote(channel);
Architect 0:6f26c31d8573 323 }
Architect 0:6f26c31d8573 324 }
Architect 0:6f26c31d8573 325 }
Architect 0:6f26c31d8573 326 instruments[channel].NextChange--;
Architect 0:6f26c31d8573 327
Architect 0:6f26c31d8573 328 commands[channel].Counter++;
Architect 0:6f26c31d8573 329
Architect 0:6f26c31d8573 330 outputPitch[channel] = notePitch[channel] + stepPitch[channel] + patternPitch[channel];
Architect 0:6f26c31d8573 331
Architect 0:6f26c31d8573 332 if(commands[channel].arpeggioStepDuration)
Architect 0:6f26c31d8573 333 outputPitch[channel] += commands[channel].Counter / commands[channel].arpeggioStepDuration * commands[channel].arpeggioStepSize;
Architect 0:6f26c31d8573 334
Architect 0:6f26c31d8573 335 outputPitch[channel] = outputPitch[channel] % NUM_PITCH; //wrap
Architect 0:6f26c31d8573 336
Architect 0:6f26c31d8573 337 //volume
Architect 0:6f26c31d8573 338 outputVolume[channel] = noteVolume[channel];
Architect 0:6f26c31d8573 339 if(commands[channel].volumeSlideStepDuration)
Architect 0:6f26c31d8573 340 outputVolume[channel] += commands[channel].Counter / commands[channel].volumeSlideStepDuration * commands[channel].volumeSlideStepSize;
Architect 0:6f26c31d8573 341
Architect 0:6f26c31d8573 342 if(commands[channel].tremoloStepDuration)
Architect 0:6f26c31d8573 343 outputVolume[channel] += ((commands[channel].Counter / commands[channel].tremoloStepDuration) % 2) * commands[channel].tremoloStepSize;
Architect 0:6f26c31d8573 344
Architect 0:6f26c31d8573 345 outputVolume[channel] = constrain(outputVolume[channel], 0, 9);
Architect 0:6f26c31d8573 346
Architect 0:6f26c31d8573 347 if(notePitch[channel] == 63)
Architect 0:6f26c31d8573 348 outputVolume[channel] = 0;
Architect 0:6f26c31d8573 349
Architect 0:6f26c31d8573 350 __disable_irq();
Architect 0:6f26c31d8573 351 _chanHalfPeriod[channel] = _halfPeriods[outputPitch[channel]];
Architect 0:6f26c31d8573 352 _chanOutput[channel] = _chanOutputVolume[channel] = outputVolume[channel] * globalVolume * chanVolumes[channel] * stepVolume[channel];
Architect 0:6f26c31d8573 353 __enable_irq();
Architect 0:6f26c31d8573 354 }
Architect 0:6f26c31d8573 355 #endif
Architect 0:6f26c31d8573 356 }
Architect 0:6f26c31d8573 357
Architect 0:6f26c31d8573 358 void Sound::setChannelHalfPeriod(uint8_t channel, uint8_t halfPeriod)
Architect 0:6f26c31d8573 359 {
Architect 0:6f26c31d8573 360 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 361 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 362 return;
Architect 0:6f26c31d8573 363 _chanHalfPeriod[channel] = halfPeriod;
Architect 0:6f26c31d8573 364 _chanState[channel] = false;
Architect 0:6f26c31d8573 365 _chanCount[channel] = 0;
Architect 0:6f26c31d8573 366 updateOutput();
Architect 0:6f26c31d8573 367 #endif
Architect 0:6f26c31d8573 368 }
Architect 0:6f26c31d8573 369
Architect 0:6f26c31d8573 370
Architect 0:6f26c31d8573 371 void Sound::generateOutput()
Architect 0:6f26c31d8573 372 {
Architect 0:6f26c31d8573 373 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 374 boolean outputChanged = false;
Architect 0:6f26c31d8573 375 //no for loop here, for the performance sake (this function runs 15 000 times per second...)
Architect 0:6f26c31d8573 376 //CHANNEL 0
Architect 0:6f26c31d8573 377 if (_chanOutputVolume[0]) {
Architect 0:6f26c31d8573 378 _chanCount[0]++;
Architect 0:6f26c31d8573 379 if (_chanCount[0] >= _chanHalfPeriod[0]) {
Architect 0:6f26c31d8573 380 outputChanged = true;
Architect 0:6f26c31d8573 381 _chanState[0] = !_chanState[0];
Architect 0:6f26c31d8573 382 _chanCount[0] = 0;
Architect 0:6f26c31d8573 383 if (_chanNoise[0]) {
Architect 0:6f26c31d8573 384 _rand = 67 * _rand + 71;
Architect 0:6f26c31d8573 385 _chanOutput[0] = _rand % _chanOutputVolume[0];
Architect 0:6f26c31d8573 386 }
Architect 0:6f26c31d8573 387 }
Architect 0:6f26c31d8573 388 }
Architect 0:6f26c31d8573 389
Architect 0:6f26c31d8573 390 //CHANNEL 1
Architect 0:6f26c31d8573 391 #if (NUM_CHANNELS > 1)
Architect 0:6f26c31d8573 392 if (_chanOutputVolume[1]) {
Architect 0:6f26c31d8573 393 _chanCount[1]++;
Architect 0:6f26c31d8573 394 if (_chanCount[1] >= _chanHalfPeriod[1]) {
Architect 0:6f26c31d8573 395 outputChanged = true;
Architect 0:6f26c31d8573 396 _chanState[1] = !_chanState[1];
Architect 0:6f26c31d8573 397 _chanCount[1] = 0;
Architect 0:6f26c31d8573 398 if (_chanNoise[1]) {
Architect 0:6f26c31d8573 399 _rand = 67 * _rand + 71;
Architect 0:6f26c31d8573 400 _chanOutput[1] = _rand % _chanOutputVolume[1];
Architect 0:6f26c31d8573 401 }
Architect 0:6f26c31d8573 402 }
Architect 0:6f26c31d8573 403 }
Architect 0:6f26c31d8573 404 #endif
Architect 0:6f26c31d8573 405
Architect 0:6f26c31d8573 406 //CHANNEL 2
Architect 0:6f26c31d8573 407 #if (NUM_CHANNELS > 2)
Architect 0:6f26c31d8573 408 if (_chanOutputVolume[2]) {
Architect 0:6f26c31d8573 409 _chanCount[2]++;
Architect 0:6f26c31d8573 410 if (_chanCount[2] >= _chanHalfPeriod[2]) {
Architect 0:6f26c31d8573 411 outputChanged = true;
Architect 0:6f26c31d8573 412 _chanState[2] = !_chanState[2];
Architect 0:6f26c31d8573 413 _chanCount[2] = 0;
Architect 0:6f26c31d8573 414 if (_chanNoise[2]) {
Architect 0:6f26c31d8573 415 _rand = 67 * _rand + 71;
Architect 0:6f26c31d8573 416 _chanOutput[2] = _rand % _chanOutputVolume[2];
Architect 0:6f26c31d8573 417 }
Architect 0:6f26c31d8573 418 }
Architect 0:6f26c31d8573 419 }
Architect 0:6f26c31d8573 420 #endif
Architect 0:6f26c31d8573 421
Architect 0:6f26c31d8573 422 //CHANNEL 3
Architect 0:6f26c31d8573 423 #if (NUM_CHANNELS > 3)
Architect 0:6f26c31d8573 424 if (_chanOutputVolume[3]) {
Architect 0:6f26c31d8573 425 _chanCount[3]++;
Architect 0:6f26c31d8573 426 if (_chanCount[3] >= _chanHalfPeriod[3]) {
Architect 0:6f26c31d8573 427 outputChanged = true;
Architect 0:6f26c31d8573 428 _chanState[3] = !_chanState[3];
Architect 0:6f26c31d8573 429 _chanCount[3] = 0;
Architect 0:6f26c31d8573 430 if (_chanNoise[3]) {
Architect 0:6f26c31d8573 431 _rand = 67 * _rand + 71;
Architect 0:6f26c31d8573 432 _chanOutput[3] = _rand % _chanOutputVolume[3];
Architect 0:6f26c31d8573 433 }
Architect 0:6f26c31d8573 434 }
Architect 0:6f26c31d8573 435 }
Architect 0:6f26c31d8573 436 #endif
Architect 0:6f26c31d8573 437
Architect 0:6f26c31d8573 438 if (outputChanged) {
Architect 0:6f26c31d8573 439 updateOutput();
Architect 0:6f26c31d8573 440 }
Architect 0:6f26c31d8573 441 #endif
Architect 0:6f26c31d8573 442 }
Architect 0:6f26c31d8573 443
Architect 0:6f26c31d8573 444 void Sound::updateOutput()
Architect 0:6f26c31d8573 445 {
Architect 0:6f26c31d8573 446 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 447 uint8_t output = 0;
Architect 0:6f26c31d8573 448
Architect 0:6f26c31d8573 449 //CHANNEL 0
Architect 0:6f26c31d8573 450 if (_chanState[0]) {
Architect 0:6f26c31d8573 451 output += _chanOutput[0];
Architect 0:6f26c31d8573 452 }
Architect 0:6f26c31d8573 453
Architect 0:6f26c31d8573 454 //CHANNEL 1
Architect 0:6f26c31d8573 455 #if (NUM_CHANNELS > 1)
Architect 0:6f26c31d8573 456 if (_chanState[1]) {
Architect 0:6f26c31d8573 457 output += _chanOutput[1];
Architect 0:6f26c31d8573 458 }
Architect 0:6f26c31d8573 459 #endif
Architect 0:6f26c31d8573 460
Architect 0:6f26c31d8573 461 //CHANNEL 2
Architect 0:6f26c31d8573 462 #if (NUM_CHANNELS > 2)
Architect 0:6f26c31d8573 463 if (_chanState[2]) {
Architect 0:6f26c31d8573 464 output += _chanOutput[2];
Architect 0:6f26c31d8573 465 }
Architect 0:6f26c31d8573 466 #endif
Architect 0:6f26c31d8573 467
Architect 0:6f26c31d8573 468 //CHANNEL 3
Architect 0:6f26c31d8573 469 #if (NUM_CHANNELS > 3)
Architect 0:6f26c31d8573 470 if (_chanState[3]) {
Architect 0:6f26c31d8573 471 output += _chanOutput[3];
Architect 0:6f26c31d8573 472 }
Architect 0:6f26c31d8573 473 #endif
Architect 0:6f26c31d8573 474 speaker.write(output/255.0);
Architect 0:6f26c31d8573 475 #endif
Architect 0:6f26c31d8573 476 }
Architect 0:6f26c31d8573 477
Architect 0:6f26c31d8573 478
Architect 0:6f26c31d8573 479 void Sound::setVolume(int8_t volume)
Architect 0:6f26c31d8573 480 {
Architect 0:6f26c31d8573 481 #if NUM_CHANNELS > 0
Architect 0:6f26c31d8573 482 globalVolume = volume % (volumeMax+1);
Architect 0:6f26c31d8573 483 #endif
Architect 0:6f26c31d8573 484 }
Architect 0:6f26c31d8573 485
Architect 0:6f26c31d8573 486 uint8_t Sound::getVolume()
Architect 0:6f26c31d8573 487 {
Architect 0:6f26c31d8573 488 #if NUM_CHANNELS > 0
Architect 0:6f26c31d8573 489 return globalVolume;
Architect 0:6f26c31d8573 490 #else
Architect 0:6f26c31d8573 491 return 0;
Architect 0:6f26c31d8573 492 #endif
Architect 0:6f26c31d8573 493 }
Architect 0:6f26c31d8573 494
Architect 0:6f26c31d8573 495 void Sound::setVolume(int8_t volume, uint8_t channel)
Architect 0:6f26c31d8573 496 {
Architect 0:6f26c31d8573 497 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 498 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 499 return;
Architect 0:6f26c31d8573 500 volume = (volume > VOLUME_CHANNEL_MAX) ? VOLUME_CHANNEL_MAX : volume;
Architect 0:6f26c31d8573 501 volume = (volume < 0) ? 0 : volume;
Architect 0:6f26c31d8573 502 chanVolumes[channel] = volume;
Architect 0:6f26c31d8573 503 #endif
Architect 0:6f26c31d8573 504 }
Architect 0:6f26c31d8573 505
Architect 0:6f26c31d8573 506 uint8_t Sound::getVolume(uint8_t channel)
Architect 0:6f26c31d8573 507 {
Architect 0:6f26c31d8573 508 #if(NUM_CHANNELS > 0)
Architect 0:6f26c31d8573 509 if(channel>=NUM_CHANNELS)
Architect 0:6f26c31d8573 510 return 255;
Architect 0:6f26c31d8573 511 return (chanVolumes[channel]);
Architect 0:6f26c31d8573 512 #else
Architect 0:6f26c31d8573 513 return 0;
Architect 0:6f26c31d8573 514 #endif
Architect 0:6f26c31d8573 515 }