Platform library for RETRO
Embed:
(wiki syntax)
Show/hide line numbers
Sound.cpp
00001 /* 00002 * (C) Copyright 2015 Valentin Ivanov. All rights reserved. 00003 * 00004 * This file is part of the RetroPlatform Library 00005 * 00006 * The RetroPlatform Library is free software: you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation, either version 3 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> 00018 * 00019 * This library is inspired by Gamebuino Library (http://gamebuino.com) 00020 * from Aurélien Rodot. 00021 */ 00022 #include "Sound.h" 00023 #include "Utils.h" 00024 00025 #if(NUM_CHANNELS > 0) 00026 uint8_t _rand = 1; 00027 const uint16_t squareWaveInstrument[] = {0x0101, 0x03F7}; 00028 const uint16_t noiseInstrument[] = {0x0101, 0x03FF}; 00029 const uint16_t* const defaultInstruments[] = {squareWaveInstrument,noiseInstrument}; 00030 00031 #define NUM_PITCH 59 00032 00033 const uint8_t _halfPeriods[NUM_PITCH] = { 00034 /*268,*/253,239,225,213,201,190,179,169,159,150,142, 00035 134,127,119,113,106,100,95,89,84,80,75,71,67,63,60, 00036 56,53,50,47,45,42,40,38,36,34,32,30,28,27,25,24,22, 00037 21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6 00038 }; 00039 00040 #endif 00041 00042 Sound::Sound() : speaker(P0_18) 00043 { 00044 initialize(); 00045 } 00046 00047 void Sound::initialize() 00048 { 00049 #if(NUM_CHANNELS > 0) 00050 volumeMax = VOLUME_GLOBAL_MAX; 00051 globalVolume = VOLUME_GLOBAL_MAX; 00052 prescaler = 1; 00053 00054 speaker.period_us(32); 00055 speaker.write(0.0); 00056 00057 for(uint8_t channel=0; channel<NUM_CHANNELS; channel++) { 00058 chanVolumes[channel] = VOLUME_CHANNEL_MAX; 00059 changeInstrumentSet(defaultInstruments, channel); 00060 command(CMD_INSTRUMENT, 0, 0, channel); 00061 } 00062 timer.attach_us(this, &Sound::generateOutput, 16); //62500Hz 00063 #endif 00064 } 00065 00066 void Sound::playTrack(const uint16_t* track, uint8_t channel) 00067 { 00068 #if(NUM_CHANNELS > 0) 00069 if(channel>=NUM_CHANNELS) 00070 return; 00071 stopTrack(channel); 00072 tracks[channel].Cursor = 0; 00073 tracks[channel].Data = (uint16_t*)track; 00074 tracks[channel].IsPlaying = true; 00075 #endif 00076 } 00077 00078 void Sound::stopTrack(uint8_t channel) 00079 { 00080 #if(NUM_CHANNELS > 0) 00081 if(channel>=NUM_CHANNELS) 00082 return; 00083 tracks[channel].IsPlaying = false; 00084 stopSequence(channel); 00085 #endif 00086 } 00087 00088 void Sound::updateTrack(uint8_t channel) 00089 { 00090 #if(NUM_CHANNELS > 0) 00091 if(channel>=NUM_CHANNELS) 00092 return; 00093 if(tracks[channel].IsPlaying && !sequences[channel].IsPlaying) { 00094 uint16_t data = tracks[channel].Data[tracks[channel].Cursor]; 00095 if(data == 0xFFFF) { 00096 tracks[channel].IsPlaying = false; 00097 return; 00098 } 00099 uint8_t patternID = data & 0xFF; 00100 data >>= 8; 00101 patternPitch[channel] = data; 00102 playSequence((const uint16_t*)patternSet[channel][patternID], channel); 00103 tracks[channel].Cursor++; 00104 } 00105 #endif 00106 } 00107 00108 00109 void Sound::changeSequenceSet(const uint16_t* const* patterns, uint8_t channel) 00110 { 00111 #if(NUM_CHANNELS > 0) 00112 if(channel>=NUM_CHANNELS) 00113 return; 00114 patternSet[channel] = (uint16_t**)patterns; 00115 #endif 00116 } 00117 00118 void Sound::playSequence(const uint16_t* pattern, uint8_t channel) 00119 { 00120 #if(NUM_CHANNELS > 0) 00121 if(channel>=NUM_CHANNELS) 00122 return; 00123 stopSequence(channel); 00124 sequences[channel].Data = (uint16_t*)pattern; 00125 sequences[channel].Cursor = 0; 00126 sequences[channel].IsPlaying = true; 00127 noteVolume[channel] = 9; 00128 //reinit commands 00129 commands[channel].volumeSlideStepDuration = 0; 00130 commands[channel].arpeggioStepDuration = 0; 00131 commands[channel].tremoloStepDuration = 0; 00132 #endif 00133 } 00134 00135 void Sound::changeInstrumentSet(const uint16_t* const* instruments, uint8_t channel) 00136 { 00137 #if(NUM_CHANNELS > 0) 00138 if(channel>=NUM_CHANNELS) 00139 return; 00140 instrumentSet[channel] = (uint16_t**)instruments; 00141 #endif 00142 } 00143 00144 void Sound::updateSequence(uint8_t channel) 00145 { 00146 #if(NUM_CHANNELS > 0) 00147 if(channel>=NUM_CHANNELS) 00148 return; 00149 if(sequences[channel].IsPlaying) { 00150 if(noteDuration[channel]==0) { //if the end of the previous note is reached 00151 00152 uint16_t data = sequences[channel].Data[sequences[channel].Cursor]; 00153 00154 if(data == 0) { //end of the pattern reached 00155 if(sequences[channel].Looping == true) { 00156 sequences[channel].Cursor = 0; 00157 data = sequences[channel].Data[sequences[channel].Cursor]; 00158 } else { 00159 sequences[channel].IsPlaying = false; 00160 if(tracks[channel].IsPlaying) { //if this pattern is part of a track, get the next pattern 00161 updateTrack(channel); 00162 data = sequences[channel].Data[sequences[channel].Cursor]; 00163 } else { 00164 stopNote(channel); 00165 return; 00166 } 00167 } 00168 } 00169 00170 while (data & 0x0001) { //read all commands and instrument changes 00171 data >>= 2; 00172 uint8_t cmd = data & 0x0F; 00173 data >>= 4; 00174 uint8_t X = data & 0x1F; 00175 data >>= 5; 00176 int8_t Y = data - 16; 00177 command(cmd,X,Y,channel); 00178 sequences[channel].Cursor++; 00179 data = sequences[channel].Data[sequences[channel].Cursor]; 00180 } 00181 data >>= 2; 00182 00183 uint8_t pitch = data & 0x003F; 00184 data >>= 6; 00185 00186 uint8_t duration = data; 00187 00188 playNote(pitch, duration, channel); 00189 00190 sequences[channel].Cursor++; 00191 } 00192 } 00193 #endif 00194 } 00195 00196 void Sound::stopSequence(uint8_t channel) 00197 { 00198 #if(NUM_CHANNELS > 0) 00199 if(channel>=NUM_CHANNELS) 00200 return; 00201 stopNote(channel); 00202 sequences[channel].IsPlaying = false; 00203 #endif 00204 } 00205 00206 void Sound::command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t channel) 00207 { 00208 #if(NUM_CHANNELS > 0) 00209 if(channel>=NUM_CHANNELS) 00210 return; 00211 00212 switch(cmd) { 00213 case CMD_VOLUME: //volume 00214 X = constrain(X, 0, 10); 00215 noteVolume[channel] = X; 00216 break; 00217 case CMD_INSTRUMENT: //instrument 00218 instruments[channel].Data = (uint16_t*)instrumentSet[channel][X]; 00219 instruments[channel].Length = instruments[channel].Data[0] & 0x00FF; //8 LSB 00220 instruments[channel].Length *= prescaler; 00221 instruments[channel].Looping = min(instruments[channel].Data[0] >> 8, instruments[channel].Length); //8 MSB - check that the loop is shorter than the instrument length 00222 instruments[channel].Looping *= prescaler; 00223 break; 00224 case CMD_SLIDE: //volume slide 00225 commands[channel].volumeSlideStepDuration = X * prescaler; 00226 commands[channel].volumeSlideStepSize = Y; 00227 break; 00228 case CMD_ARPEGGIO: 00229 commands[channel].arpeggioStepDuration = X * prescaler; 00230 commands[channel].arpeggioStepSize = Y; 00231 break; 00232 case CMD_TREMOLO: 00233 commands[channel].tremoloStepDuration = X * prescaler; 00234 commands[channel].tremoloStepSize = Y; 00235 break; 00236 default: 00237 break; 00238 } 00239 #endif 00240 } 00241 00242 void Sound::playNote(uint8_t pitch, uint8_t duration, uint8_t channel) 00243 { 00244 #if(NUM_CHANNELS > 0) 00245 if(channel>=NUM_CHANNELS) 00246 return; 00247 00248 //set note 00249 notePitch[channel] = pitch; 00250 noteDuration[channel] = duration * prescaler; 00251 notePlaying[channel] = true; 00252 00253 //reinit vars 00254 instruments[channel].NextChange = 0; 00255 instruments[channel].Cursor = 0; 00256 00257 commands[channel].Counter = 0; 00258 00259 _chanState[channel] = true; 00260 #endif 00261 } 00262 00263 void Sound::stopNote(uint8_t channel) 00264 { 00265 #if(NUM_CHANNELS > 0) 00266 if(channel>=NUM_CHANNELS) 00267 return; 00268 notePlaying[channel] = false; 00269 //counters 00270 noteDuration[channel] = 0; 00271 instruments[channel].Cursor = 0; 00272 commands[channel].Counter = 0; 00273 //output 00274 _chanOutput[channel] = 0; 00275 _chanOutputVolume[channel] = 0; 00276 _chanState[channel] = false; 00277 updateOutput(); 00278 #endif 00279 } 00280 00281 void Sound::updateNote(uint8_t channel) 00282 { 00283 #if(NUM_CHANNELS > 0) 00284 if(channel>=NUM_CHANNELS) 00285 return; 00286 if (notePlaying[channel]) { 00287 00288 if(noteDuration[channel] == 0) { 00289 stopNote(channel); 00290 return; 00291 } else { 00292 noteDuration[channel]--; 00293 } 00294 00295 if (instruments[channel].NextChange == 0) { 00296 00297 //read the step data from the progmem and decode it 00298 uint16_t thisStep = instruments[channel].Data[1 + instruments[channel].Cursor]; 00299 00300 stepVolume[channel] = thisStep & 0x0007; 00301 thisStep >>= 3; 00302 00303 uint8_t stepNoise = thisStep & 0x0001; 00304 thisStep >>= 1; 00305 00306 uint8_t stepDuration = thisStep & 0x003F; 00307 thisStep >>= 6; 00308 00309 stepPitch[channel] = thisStep; 00310 00311 //apply the step settings 00312 instruments[channel].NextChange = stepDuration * prescaler; 00313 00314 _chanNoise[channel] = stepNoise; 00315 00316 instruments[channel].Cursor++; 00317 00318 if (instruments[channel].Cursor >= instruments[channel].Length) { 00319 if (instruments[channel].Looping) { 00320 instruments[channel].Cursor = instruments[channel].Length - instruments[channel].Looping; 00321 } else { 00322 stopNote(channel); 00323 } 00324 } 00325 } 00326 instruments[channel].NextChange--; 00327 00328 commands[channel].Counter++; 00329 00330 outputPitch[channel] = notePitch[channel] + stepPitch[channel] + patternPitch[channel]; 00331 00332 if(commands[channel].arpeggioStepDuration) 00333 outputPitch[channel] += commands[channel].Counter / commands[channel].arpeggioStepDuration * commands[channel].arpeggioStepSize; 00334 00335 outputPitch[channel] = outputPitch[channel] % NUM_PITCH; //wrap 00336 00337 //volume 00338 outputVolume[channel] = noteVolume[channel]; 00339 if(commands[channel].volumeSlideStepDuration) 00340 outputVolume[channel] += commands[channel].Counter / commands[channel].volumeSlideStepDuration * commands[channel].volumeSlideStepSize; 00341 00342 if(commands[channel].tremoloStepDuration) 00343 outputVolume[channel] += ((commands[channel].Counter / commands[channel].tremoloStepDuration) % 2) * commands[channel].tremoloStepSize; 00344 00345 outputVolume[channel] = constrain(outputVolume[channel], 0, 9); 00346 00347 if(notePitch[channel] == 63) 00348 outputVolume[channel] = 0; 00349 00350 __disable_irq(); 00351 _chanHalfPeriod[channel] = _halfPeriods[outputPitch[channel]]; 00352 _chanOutput[channel] = _chanOutputVolume[channel] = outputVolume[channel] * globalVolume * chanVolumes[channel] * stepVolume[channel]; 00353 __enable_irq(); 00354 } 00355 #endif 00356 } 00357 00358 void Sound::setChannelHalfPeriod(uint8_t channel, uint8_t halfPeriod) 00359 { 00360 #if(NUM_CHANNELS > 0) 00361 if(channel>=NUM_CHANNELS) 00362 return; 00363 _chanHalfPeriod[channel] = halfPeriod; 00364 _chanState[channel] = false; 00365 _chanCount[channel] = 0; 00366 updateOutput(); 00367 #endif 00368 } 00369 00370 00371 void Sound::generateOutput() 00372 { 00373 #if(NUM_CHANNELS > 0) 00374 boolean outputChanged = false; 00375 //no for loop here, for the performance sake (this function runs 15 000 times per second...) 00376 //CHANNEL 0 00377 if (_chanOutputVolume[0]) { 00378 _chanCount[0]++; 00379 if (_chanCount[0] >= _chanHalfPeriod[0]) { 00380 outputChanged = true; 00381 _chanState[0] = !_chanState[0]; 00382 _chanCount[0] = 0; 00383 if (_chanNoise[0]) { 00384 _rand = 67 * _rand + 71; 00385 _chanOutput[0] = _rand % _chanOutputVolume[0]; 00386 } 00387 } 00388 } 00389 00390 //CHANNEL 1 00391 #if (NUM_CHANNELS > 1) 00392 if (_chanOutputVolume[1]) { 00393 _chanCount[1]++; 00394 if (_chanCount[1] >= _chanHalfPeriod[1]) { 00395 outputChanged = true; 00396 _chanState[1] = !_chanState[1]; 00397 _chanCount[1] = 0; 00398 if (_chanNoise[1]) { 00399 _rand = 67 * _rand + 71; 00400 _chanOutput[1] = _rand % _chanOutputVolume[1]; 00401 } 00402 } 00403 } 00404 #endif 00405 00406 //CHANNEL 2 00407 #if (NUM_CHANNELS > 2) 00408 if (_chanOutputVolume[2]) { 00409 _chanCount[2]++; 00410 if (_chanCount[2] >= _chanHalfPeriod[2]) { 00411 outputChanged = true; 00412 _chanState[2] = !_chanState[2]; 00413 _chanCount[2] = 0; 00414 if (_chanNoise[2]) { 00415 _rand = 67 * _rand + 71; 00416 _chanOutput[2] = _rand % _chanOutputVolume[2]; 00417 } 00418 } 00419 } 00420 #endif 00421 00422 //CHANNEL 3 00423 #if (NUM_CHANNELS > 3) 00424 if (_chanOutputVolume[3]) { 00425 _chanCount[3]++; 00426 if (_chanCount[3] >= _chanHalfPeriod[3]) { 00427 outputChanged = true; 00428 _chanState[3] = !_chanState[3]; 00429 _chanCount[3] = 0; 00430 if (_chanNoise[3]) { 00431 _rand = 67 * _rand + 71; 00432 _chanOutput[3] = _rand % _chanOutputVolume[3]; 00433 } 00434 } 00435 } 00436 #endif 00437 00438 if (outputChanged) { 00439 updateOutput(); 00440 } 00441 #endif 00442 } 00443 00444 void Sound::updateOutput() 00445 { 00446 #if(NUM_CHANNELS > 0) 00447 uint8_t output = 0; 00448 00449 //CHANNEL 0 00450 if (_chanState[0]) { 00451 output += _chanOutput[0]; 00452 } 00453 00454 //CHANNEL 1 00455 #if (NUM_CHANNELS > 1) 00456 if (_chanState[1]) { 00457 output += _chanOutput[1]; 00458 } 00459 #endif 00460 00461 //CHANNEL 2 00462 #if (NUM_CHANNELS > 2) 00463 if (_chanState[2]) { 00464 output += _chanOutput[2]; 00465 } 00466 #endif 00467 00468 //CHANNEL 3 00469 #if (NUM_CHANNELS > 3) 00470 if (_chanState[3]) { 00471 output += _chanOutput[3]; 00472 } 00473 #endif 00474 speaker.write(output/255.0); 00475 #endif 00476 } 00477 00478 00479 void Sound::setVolume(int8_t volume) 00480 { 00481 #if NUM_CHANNELS > 0 00482 globalVolume = volume % (volumeMax+1); 00483 #endif 00484 } 00485 00486 uint8_t Sound::getVolume() 00487 { 00488 #if NUM_CHANNELS > 0 00489 return globalVolume; 00490 #else 00491 return 0; 00492 #endif 00493 } 00494 00495 void Sound::setVolume(int8_t volume, uint8_t channel) 00496 { 00497 #if(NUM_CHANNELS > 0) 00498 if(channel>=NUM_CHANNELS) 00499 return; 00500 volume = (volume > VOLUME_CHANNEL_MAX) ? VOLUME_CHANNEL_MAX : volume; 00501 volume = (volume < 0) ? 0 : volume; 00502 chanVolumes[channel] = volume; 00503 #endif 00504 } 00505 00506 uint8_t Sound::getVolume(uint8_t channel) 00507 { 00508 #if(NUM_CHANNELS > 0) 00509 if(channel>=NUM_CHANNELS) 00510 return 255; 00511 return (chanVolumes[channel]); 00512 #else 00513 return 0; 00514 #endif 00515 }
Generated on Tue Jul 12 2022 23:21:39 by 1.7.2