Platform library for RETRO

Dependents:   RETRO_RickGame

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Sound.cpp Source File

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 }