├── .gitignore ├── Audio Synthesis For Music.pdf ├── Audio Synthesis For Music.pptx ├── MusicSynth ├── AudioEffects.h ├── AudioUtils.h ├── DemoAdditive.cpp ├── DemoBLWaveForms.cpp ├── DemoClipping.cpp ├── DemoDelay.cpp ├── DemoDrum.cpp ├── DemoDucking.cpp ├── DemoEnvelopes.cpp ├── DemoFMSynth.cpp ├── DemoFiltering.cpp ├── DemoFlange.cpp ├── DemoList.h ├── DemoMgr.cpp ├── DemoMgr.h ├── DemoMixing.cpp ├── DemoPopping.cpp ├── DemoReverb.cpp ├── DemoSine.cpp ├── DemoStereo.cpp ├── DemoTremVib.cpp ├── DemoWaveForms.cpp ├── Main.cpp ├── MusicSynth.exe ├── MusicSynth.sln ├── MusicSynth.vcxproj ├── MusicSynth.vcxproj.filters ├── PortAudio │ ├── Bin │ │ └── portaudio.lib │ └── include │ │ ├── pa_asio.h │ │ ├── pa_jack.h │ │ ├── pa_linux_alsa.h │ │ ├── pa_mac_core.h │ │ ├── pa_win_ds.h │ │ ├── pa_win_wasapi.h │ │ ├── pa_win_waveformat.h │ │ ├── pa_win_wdmks.h │ │ ├── pa_win_wmme.h │ │ └── portaudio.h ├── SampleList.h ├── Samples.cpp ├── Samples.h ├── Samples │ ├── clap.wav │ ├── cymbal.wav │ ├── dreams.wav │ ├── kick.wav │ ├── legend1.wav │ ├── legend2.wav │ ├── oakenfold.wav │ ├── pvd.wav │ └── ting.wav ├── WavFile.cpp ├── WavFile.h └── portaudio.dll └── Structure.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # other things we want to ignore 2 | *.sdf 3 | *.suo 4 | *.vcxproj.user 5 | *.ipch 6 | *.opensdf 7 | *.ilk 8 | *.pdb 9 | **/Debug/*.* 10 | **/Release/*.* -------------------------------------------------------------------------------- /Audio Synthesis For Music.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/Audio Synthesis For Music.pdf -------------------------------------------------------------------------------- /Audio Synthesis For Music.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/Audio Synthesis For Music.pptx -------------------------------------------------------------------------------- /MusicSynth/AudioEffects.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/AudioEffects.h -------------------------------------------------------------------------------- /MusicSynth/AudioUtils.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/AudioUtils.h -------------------------------------------------------------------------------- /MusicSynth/DemoAdditive.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoAdditive.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | #include "AudioEffects.h" 10 | #include 11 | 12 | namespace DemoAdditive { 13 | 14 | struct SNote { 15 | SNote(float frequency) 16 | : m_frequency(frequency) 17 | , m_age(0) 18 | , m_dead(false) 19 | , m_releaseAge(0) 20 | , m_phase(0.0f) {} 21 | 22 | float m_frequency; 23 | size_t m_age; 24 | bool m_dead; 25 | size_t m_releaseAge; 26 | float m_phase; 27 | }; 28 | 29 | std::vector g_notes; 30 | std::mutex g_notesMutex; 31 | 32 | //-------------------------------------------------------------------------------------------------- 33 | void OnInit() { } 34 | 35 | //-------------------------------------------------------------------------------------------------- 36 | void OnExit() { } 37 | 38 | //-------------------------------------------------------------------------------------------------- 39 | inline float GenerateNoteSample (SNote& note, float sampleRate) { 40 | 41 | float c_decayTime = 1.5f; 42 | size_t c_numHarmonics = 10; 43 | 44 | // calculate our age in seconds and advance our age in samples, by 1 sample 45 | float ageInSeconds = float(note.m_age) / sampleRate; 46 | ++note.m_age; 47 | 48 | // handle the note dieing 49 | if (ageInSeconds > c_decayTime) 50 | { 51 | note.m_dead = true; 52 | return 0.0; 53 | } 54 | 55 | // add our harmonics together 56 | float ret = 0.0f; 57 | for (size_t index = 1; index <= c_numHarmonics; ++index) { 58 | float envelope = Envelope4Pt( 59 | ageInSeconds * float(index), 60 | 0.0f, 0.0f, 61 | c_decayTime*0.05f, 1.0f, 62 | c_decayTime*0.10f, 0.5f, 63 | c_decayTime, 0.0f 64 | ); 65 | float phase = std::fmodf(note.m_phase * float(index) , 1.0f); 66 | //ret += SineWave(phase) * envelope; 67 | ret += SawWaveBandLimited(phase, 5) * envelope; 68 | } 69 | 70 | // advance phase 71 | note.m_phase = std::fmodf(note.m_phase + note.m_frequency / sampleRate, 1.0f); 72 | 73 | // return the value 74 | return ret; 75 | } 76 | 77 | //-------------------------------------------------------------------------------------------------- 78 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 79 | 80 | // get a lock on our notes vector 81 | std::lock_guard guard(g_notesMutex); 82 | 83 | // for every sample in our output buffer 84 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 85 | 86 | // add up all notes to get the final value. 87 | float value = 0.0f; 88 | std::for_each( 89 | g_notes.begin(), 90 | g_notes.end(), 91 | [&value, sampleRate](SNote& note) { 92 | value += GenerateNoteSample(note, sampleRate); 93 | } 94 | ); 95 | 96 | // copy the value to all audio channels 97 | for (size_t channel = 0; channel < numChannels; ++channel) 98 | outputBuffer[channel] = value; 99 | } 100 | 101 | // remove notes that have died 102 | auto iter = std::remove_if( 103 | g_notes.begin(), 104 | g_notes.end(), 105 | [] (const SNote& note) { 106 | return note.m_dead; 107 | } 108 | ); 109 | 110 | if (iter != g_notes.end()) 111 | g_notes.erase(iter); 112 | } 113 | 114 | //-------------------------------------------------------------------------------------------------- 115 | void OnKey (char key, bool pressed) { 116 | 117 | // nothing to do on key release 118 | if (!pressed) 119 | return; 120 | 121 | // figure out what frequency to play 122 | float frequency = 0.0f; 123 | switch (key) { 124 | // QWERTY row 125 | case 'Q': frequency = NoteToFrequency(3, 0); break; 126 | case 'W': frequency = NoteToFrequency(3, 1); break; 127 | case 'E': frequency = NoteToFrequency(3, 2); break; 128 | case 'R': frequency = NoteToFrequency(3, 3); break; 129 | case 'T': frequency = NoteToFrequency(3, 4); break; 130 | case 'Y': frequency = NoteToFrequency(3, 5); break; 131 | case 'U': frequency = NoteToFrequency(3, 6); break; 132 | case 'I': frequency = NoteToFrequency(3, 7); break; 133 | case 'O': frequency = NoteToFrequency(3, 8); break; 134 | case 'P': frequency = NoteToFrequency(3, 9); break; 135 | case -37: frequency = NoteToFrequency(3, 10); break; 136 | 137 | // ASDF row 138 | case 'A': frequency = NoteToFrequency(2, 0); break; 139 | case 'S': frequency = NoteToFrequency(2, 1); break; 140 | case 'D': frequency = NoteToFrequency(2, 2); break; 141 | case 'F': frequency = NoteToFrequency(2, 3); break; 142 | case 'G': frequency = NoteToFrequency(2, 4); break; 143 | case 'H': frequency = NoteToFrequency(2, 5); break; 144 | case 'J': frequency = NoteToFrequency(2, 6); break; 145 | case 'K': frequency = NoteToFrequency(2, 7); break; 146 | case 'L': frequency = NoteToFrequency(2, 8); break; 147 | case -70: frequency = NoteToFrequency(2, 9); break; 148 | case -34: frequency = NoteToFrequency(2, 10); break; 149 | 150 | // ZXCV row 151 | case 'Z': frequency = NoteToFrequency(1, 0); break; 152 | case 'X': frequency = NoteToFrequency(1, 1); break; 153 | case 'C': frequency = NoteToFrequency(1, 2); break; 154 | case 'V': frequency = NoteToFrequency(1, 3); break; 155 | case 'B': frequency = NoteToFrequency(1, 4); break; 156 | case 'N': frequency = NoteToFrequency(1, 5); break; 157 | case 'M': frequency = NoteToFrequency(1, 6); break; 158 | case -68: frequency = NoteToFrequency(1, 7); break; 159 | case -66: frequency = NoteToFrequency(1, 8); break; 160 | case -65: frequency = NoteToFrequency(1, 9); break; 161 | case -95: frequency = NoteToFrequency(1, 10); break; // right shift 162 | 163 | // left shift = low freq 164 | case 16: frequency = NoteToFrequency(0, 5); break; 165 | 166 | // left shift = low freq 167 | case -94: frequency = NoteToFrequency(0, 0); break; 168 | 169 | default: { 170 | return; 171 | } 172 | } 173 | 174 | // get a lock on our notes vector and add the new note 175 | std::lock_guard guard(g_notesMutex); 176 | g_notes.push_back(SNote(frequency)); 177 | } 178 | 179 | //-------------------------------------------------------------------------------------------------- 180 | void OnEnterDemo () { 181 | printf("Letter keys to play notes.\r\nleft shift / control is super low frequency.\r\n"); 182 | printf("\r\nInstructions:\r\n"); 183 | printf("Play notes. Explain how they are made\r\n"); 184 | 185 | // clear all the notes out 186 | std::lock_guard guard(g_notesMutex); 187 | g_notes.clear(); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /MusicSynth/DemoBLWaveForms.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoBLWaveForms.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | #include 10 | 11 | namespace DemoBLWaveForms { 12 | 13 | enum EWaveForm { 14 | e_waveSine, 15 | e_waveSaw, 16 | e_waveSquare, 17 | e_waveTriangle 18 | }; 19 | 20 | const char* WaveFormToString (EWaveForm waveForm) { 21 | switch (waveForm) { 22 | case e_waveSine: return "Sine"; 23 | case e_waveSaw: return "Bandlimited Saw"; 24 | case e_waveSquare: return "Bandlimited Square"; 25 | case e_waveTriangle: return "Bandlimited Triangle"; 26 | } 27 | return "???"; 28 | } 29 | 30 | struct SNote { 31 | SNote(float frequency, EWaveForm waveForm) 32 | : m_frequency(frequency) 33 | , m_waveForm(waveForm) 34 | , m_age(0) 35 | , m_dead(false) 36 | , m_wantsKeyRelease(false) 37 | , m_releaseAge(0) {} 38 | 39 | float m_frequency; 40 | EWaveForm m_waveForm; 41 | size_t m_age; 42 | bool m_dead; 43 | bool m_wantsKeyRelease; 44 | size_t m_releaseAge; 45 | }; 46 | 47 | std::vector g_notes; 48 | std::mutex g_notesMutex; 49 | EWaveForm g_currentWaveForm; 50 | 51 | //-------------------------------------------------------------------------------------------------- 52 | void OnInit() { } 53 | 54 | //-------------------------------------------------------------------------------------------------- 55 | void OnExit() { } 56 | 57 | //-------------------------------------------------------------------------------------------------- 58 | inline float GenerateEnvelope (SNote& note, float ageInSeconds, float sampleRate) { 59 | 60 | // this just puts a short envelope on the beginning and end of the note and kills the note 61 | // when the release envelope is done. 62 | 63 | float envelope = 0.0f; 64 | 65 | static const float c_envelopeTime = 0.1f; 66 | 67 | // if the key isn't yet released 68 | if (note.m_releaseAge == 0) { 69 | // release the key if it wants to be released and has done the intro envelope 70 | if (note.m_wantsKeyRelease && ageInSeconds > c_envelopeTime) { 71 | note.m_releaseAge = note.m_age; 72 | } 73 | // else do the intro envelope 74 | else { 75 | envelope = Envelope2Pt( 76 | ageInSeconds, 77 | 0.0f, 0.0f, 78 | c_envelopeTime, 1.0f 79 | ); 80 | } 81 | } 82 | 83 | // if the key has been released, apply the outro envelope 84 | if (note.m_releaseAge != 0) { 85 | 86 | float releaseAgeInSeconds = float(note.m_releaseAge) / sampleRate; 87 | 88 | float secondsInRelease = ageInSeconds - releaseAgeInSeconds; 89 | 90 | envelope = Envelope2Pt( 91 | secondsInRelease, 92 | 0.0f, 1.0f, 93 | c_envelopeTime, 0.0f 94 | ); 95 | 96 | // kill the note when the release is done 97 | if (secondsInRelease > c_envelopeTime) 98 | note.m_dead = true; 99 | } 100 | 101 | return envelope; 102 | } 103 | 104 | //-------------------------------------------------------------------------------------------------- 105 | inline float GenerateNoteSample (SNote& note, float sampleRate) { 106 | 107 | // calculate our age in seconds and advance our age in samples, by 1 sample 108 | float ageInSeconds = float(note.m_age) / sampleRate; 109 | ++note.m_age; 110 | 111 | // generate the envelope value for our note 112 | // decrease note volume a bit, because the volume adjustments don't seem to be quite enough 113 | float envelope = GenerateEnvelope(note, ageInSeconds, sampleRate) * 0.8f; 114 | 115 | // generate the audio sample value for the current time. 116 | // Note that it is ok that we are basing audio samples on age instead of phase, because the 117 | // frequency never changes and we envelope the front and back to avoid popping. 118 | float phase = std::fmodf(ageInSeconds * note.m_frequency, 1.0f); 119 | switch (note.m_waveForm) { 120 | case e_waveSine: return SineWave(phase) * envelope; 121 | case e_waveSaw: return SawWaveBandLimited(phase, 10) * envelope; 122 | case e_waveSquare: return SquareWaveBandLimited(phase, 10) * envelope; 123 | case e_waveTriangle:return TriangleWaveBandLimited(phase, 10) * envelope; 124 | } 125 | 126 | return 0.0f; 127 | } 128 | 129 | //-------------------------------------------------------------------------------------------------- 130 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 131 | 132 | // get a lock on our notes vector 133 | std::lock_guard guard(g_notesMutex); 134 | 135 | // for every sample in our output buffer 136 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 137 | 138 | // add up all notes to get the final value. 139 | float value = 0.0f; 140 | std::for_each( 141 | g_notes.begin(), 142 | g_notes.end(), 143 | [&value, sampleRate](SNote& note) { 144 | value += GenerateNoteSample(note, sampleRate); 145 | } 146 | ); 147 | 148 | // copy the value to all audio channels 149 | for (size_t channel = 0; channel < numChannels; ++channel) 150 | outputBuffer[channel] = value; 151 | } 152 | 153 | // remove notes that have died 154 | auto iter = std::remove_if( 155 | g_notes.begin(), 156 | g_notes.end(), 157 | [] (const SNote& note) { 158 | return note.m_dead; 159 | } 160 | ); 161 | 162 | if (iter != g_notes.end()) 163 | g_notes.erase(iter); 164 | } 165 | 166 | //-------------------------------------------------------------------------------------------------- 167 | void StopNote (float frequency) { 168 | 169 | // get a lock on our notes vector 170 | std::lock_guard guard(g_notesMutex); 171 | 172 | // Any note that is this frequency should note that it wants to enter released state. 173 | std::for_each( 174 | g_notes.begin(), 175 | g_notes.end(), 176 | [frequency] (SNote& note) { 177 | if (note.m_frequency == frequency) { 178 | note.m_wantsKeyRelease = true; 179 | } 180 | } 181 | ); 182 | } 183 | 184 | //-------------------------------------------------------------------------------------------------- 185 | void ReportParams() { 186 | printf("Instrument: %s\r\n", WaveFormToString(g_currentWaveForm)); 187 | } 188 | 189 | //-------------------------------------------------------------------------------------------------- 190 | void OnKey (char key, bool pressed) { 191 | 192 | // pressing numbers switches instruments 193 | if (pressed) { 194 | switch (key) 195 | { 196 | case '1': g_currentWaveForm = e_waveSine; ReportParams(); return; 197 | case '2': g_currentWaveForm = e_waveSaw; ReportParams(); return; 198 | case '3': g_currentWaveForm = e_waveSquare; ReportParams(); return; 199 | case '4': g_currentWaveForm = e_waveTriangle; ReportParams(); return; 200 | } 201 | } 202 | 203 | // figure out what frequency to play 204 | float frequency = 0.0f; 205 | switch (key) { 206 | 207 | // QWERTY row 208 | case 'Q': frequency = NoteToFrequency(3, 0); break; 209 | case 'W': frequency = NoteToFrequency(3, 1); break; 210 | case 'E': frequency = NoteToFrequency(3, 2); break; 211 | case 'R': frequency = NoteToFrequency(3, 3); break; 212 | case 'T': frequency = NoteToFrequency(3, 4); break; 213 | case 'Y': frequency = NoteToFrequency(3, 5); break; 214 | case 'U': frequency = NoteToFrequency(3, 6); break; 215 | case 'I': frequency = NoteToFrequency(3, 7); break; 216 | case 'O': frequency = NoteToFrequency(3, 8); break; 217 | case 'P': frequency = NoteToFrequency(3, 9); break; 218 | case -37: frequency = NoteToFrequency(3, 10); break; 219 | 220 | // ASDF row 221 | case 'A': frequency = NoteToFrequency(2, 0); break; 222 | case 'S': frequency = NoteToFrequency(2, 1); break; 223 | case 'D': frequency = NoteToFrequency(2, 2); break; 224 | case 'F': frequency = NoteToFrequency(2, 3); break; 225 | case 'G': frequency = NoteToFrequency(2, 4); break; 226 | case 'H': frequency = NoteToFrequency(2, 5); break; 227 | case 'J': frequency = NoteToFrequency(2, 6); break; 228 | case 'K': frequency = NoteToFrequency(2, 7); break; 229 | case 'L': frequency = NoteToFrequency(2, 8); break; 230 | case -70: frequency = NoteToFrequency(2, 9); break; 231 | case -34: frequency = NoteToFrequency(2, 10); break; 232 | 233 | // ZXCV row 234 | case 'Z': frequency = NoteToFrequency(1, 0); break; 235 | case 'X': frequency = NoteToFrequency(1, 1); break; 236 | case 'C': frequency = NoteToFrequency(1, 2); break; 237 | case 'V': frequency = NoteToFrequency(1, 3); break; 238 | case 'B': frequency = NoteToFrequency(1, 4); break; 239 | case 'N': frequency = NoteToFrequency(1, 5); break; 240 | case 'M': frequency = NoteToFrequency(1, 6); break; 241 | case -68: frequency = NoteToFrequency(1, 7); break; 242 | case -66: frequency = NoteToFrequency(1, 8); break; 243 | case -65: frequency = NoteToFrequency(1, 9); break; 244 | case -95: frequency = NoteToFrequency(1, 10); break; // right shift 245 | 246 | // left shift = low freq 247 | case 16: frequency = NoteToFrequency(0, 5); break; 248 | 249 | // left shift = low freq 250 | case -94: frequency = NoteToFrequency(0, 0); break; 251 | 252 | default: { 253 | return; 254 | } 255 | } 256 | 257 | 258 | // if releasing a note, we need to find and kill the flute note of the same frequency 259 | if (!pressed) { 260 | StopNote(frequency); 261 | return; 262 | } 263 | 264 | // get a lock on our notes vector and add the new note 265 | std::lock_guard guard(g_notesMutex); 266 | g_notes.push_back(SNote(frequency, g_currentWaveForm)); 267 | } 268 | 269 | //-------------------------------------------------------------------------------------------------- 270 | void OnEnterDemo () { 271 | g_currentWaveForm = e_waveSine; 272 | printf("Letter keys to play notes.\r\nleft shift / control is super low frequency.\r\n"); 273 | printf("1 = Sine\r\n"); 274 | printf("2 = Band Limited Saw\r\n"); 275 | printf("3 = Band Limited Square\r\n"); 276 | printf("4 = Band Limited Triangle\r\n"); 277 | printf("\r\nInstructions:\r\n"); 278 | printf("Play diff instruments. Mention smoother sounds.\r\n"); 279 | 280 | // clear all the notes out 281 | std::lock_guard guard(g_notesMutex); 282 | g_notes.clear(); 283 | } 284 | } 285 | -------------------------------------------------------------------------------- /MusicSynth/DemoClipping.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoClipping.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | 10 | namespace DemoClipping { 11 | 12 | float g_frequency = 0.0f; 13 | float g_volumeAmplifier = 1.0f; 14 | 15 | enum EVoiceState { 16 | e_stopped, 17 | e_wantStart, 18 | e_started 19 | }; 20 | 21 | EVoiceState g_voiceState = e_stopped; 22 | 23 | //-------------------------------------------------------------------------------------------------- 24 | void OnInit() { } 25 | 26 | //-------------------------------------------------------------------------------------------------- 27 | void OnExit() { } 28 | 29 | //-------------------------------------------------------------------------------------------------- 30 | float SampleAudioSample(size_t age, SWavFile& sample, float sampleRate) { 31 | 32 | // handle the note dieing when it is done 33 | size_t sampleIndex = age*sample.m_numChannels; 34 | if (sampleIndex >= sample.m_numSamples) { 35 | g_voiceState = e_stopped; 36 | return 0.0f; 37 | } 38 | 39 | // calculate and apply an envelope to the sound samples 40 | float ageInSeconds = float(age) / sampleRate; 41 | float envelope = Envelope4Pt( 42 | ageInSeconds, 43 | 0.0f, 0.0f, 44 | 0.1f, 1.0f, 45 | sample.m_lengthSeconds - 0.1f, 1.0f, 46 | sample.m_lengthSeconds, 0.0f 47 | ); 48 | 49 | // return the sample value multiplied by the envelope 50 | return sample.m_samples[sampleIndex] * envelope; 51 | } 52 | 53 | //-------------------------------------------------------------------------------------------------- 54 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 55 | static float phase = 0.0f; 56 | 57 | // handle the voice starting 58 | static size_t voiceStarted = 0; 59 | EVoiceState voiceState = g_voiceState; 60 | if (voiceState == e_wantStart) { 61 | g_voiceState = e_started; 62 | voiceState = e_started; 63 | voiceStarted = CDemoMgr::GetSampleClock(); 64 | } 65 | 66 | // calculate how much our phase should change each sample 67 | float phaseAdvance = g_frequency / sampleRate; 68 | 69 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 70 | 71 | // get the sine wave amplitude for this phase (angle) 72 | float value = SineWave(phase) * g_volumeAmplifier; 73 | 74 | // sample the voice if we should 75 | if (voiceState == e_started) 76 | value += SampleAudioSample(CDemoMgr::GetSampleClock() - voiceStarted + sample, g_sample_legend2, sampleRate) * g_volumeAmplifier; 77 | 78 | // advance the phase, making sure to stay within 0 and 1 79 | phase += phaseAdvance; 80 | phase = std::fmod(phase, 1.0f); 81 | 82 | // copy the value to all audio channels 83 | for (size_t channel = 0; channel < numChannels; ++channel) 84 | outputBuffer[channel] = value; 85 | } 86 | } 87 | 88 | //-------------------------------------------------------------------------------------------------- 89 | void OnKey (char key, bool pressed) { 90 | 91 | // only listen to key down events 92 | if (!pressed) 93 | return; 94 | 95 | // left alt for toggling voice 96 | if (key == -92) { 97 | if (g_voiceState == e_stopped) 98 | g_voiceState = e_wantStart; 99 | else 100 | g_voiceState = e_stopped; 101 | } 102 | 103 | switch (key) { 104 | // number row 105 | case '1': g_volumeAmplifier = 1.0f; break; 106 | case '2': g_volumeAmplifier = 2.0f; break; 107 | case '3': g_volumeAmplifier = 3.0f; break; 108 | case '4': g_volumeAmplifier = 4.0f; break; 109 | case '5': g_volumeAmplifier = 5.0f; break; 110 | case '6': g_volumeAmplifier = 6.0f; break; 111 | case '7': g_volumeAmplifier = 7.0f; break; 112 | case '8': g_volumeAmplifier = 8.0f; break; 113 | case '9': g_volumeAmplifier = 9.0f; break; 114 | case '0': g_volumeAmplifier = 20.0f; break; 115 | 116 | // QWERTY row 117 | case 'Q': g_frequency = NoteToFrequency(3, 0); break; 118 | case 'W': g_frequency = NoteToFrequency(3, 1); break; 119 | case 'E': g_frequency = NoteToFrequency(3, 2); break; 120 | case 'R': g_frequency = NoteToFrequency(3, 3); break; 121 | case 'T': g_frequency = NoteToFrequency(3, 4); break; 122 | case 'Y': g_frequency = NoteToFrequency(3, 5); break; 123 | case 'U': g_frequency = NoteToFrequency(3, 6); break; 124 | case 'I': g_frequency = NoteToFrequency(3, 7); break; 125 | case 'O': g_frequency = NoteToFrequency(3, 8); break; 126 | case 'P': g_frequency = NoteToFrequency(3, 9); break; 127 | case -37: g_frequency = NoteToFrequency(3, 10); break; 128 | 129 | // ASDF row 130 | case 'A': g_frequency = NoteToFrequency(2, 0); break; 131 | case 'S': g_frequency = NoteToFrequency(2, 1); break; 132 | case 'D': g_frequency = NoteToFrequency(2, 2); break; 133 | case 'F': g_frequency = NoteToFrequency(2, 3); break; 134 | case 'G': g_frequency = NoteToFrequency(2, 4); break; 135 | case 'H': g_frequency = NoteToFrequency(2, 5); break; 136 | case 'J': g_frequency = NoteToFrequency(2, 6); break; 137 | case 'K': g_frequency = NoteToFrequency(2, 7); break; 138 | case 'L': g_frequency = NoteToFrequency(2, 8); break; 139 | case -70: g_frequency = NoteToFrequency(2, 9); break; 140 | case -34: g_frequency = NoteToFrequency(2, 10); break; 141 | 142 | // ZXCV row 143 | case 'Z': g_frequency = NoteToFrequency(1, 0); break; 144 | case 'X': g_frequency = NoteToFrequency(1, 1); break; 145 | case 'C': g_frequency = NoteToFrequency(1, 2); break; 146 | case 'V': g_frequency = NoteToFrequency(1, 3); break; 147 | case 'B': g_frequency = NoteToFrequency(1, 4); break; 148 | case 'N': g_frequency = NoteToFrequency(1, 5); break; 149 | case 'M': g_frequency = NoteToFrequency(1, 6); break; 150 | case -68: g_frequency = NoteToFrequency(1, 7); break; 151 | case -66: g_frequency = NoteToFrequency(1, 8); break; 152 | case -65: g_frequency = NoteToFrequency(1, 9); break; 153 | case -95: g_frequency = NoteToFrequency(1, 10); break; // right shift 154 | 155 | // left shift = low freq 156 | case 16: g_frequency = NoteToFrequency(0, 5); break; 157 | 158 | // left shift = low freq 159 | case -94: g_frequency = NoteToFrequency(0, 0); break; 160 | 161 | // silence 162 | case ' ': g_frequency = 0.0f; break; 163 | default: { 164 | return; 165 | } 166 | } 167 | 168 | printf("Frequency = %0.2fhz, Volume = %i%%\r\n", g_frequency, int(g_volumeAmplifier*100.0f)); 169 | } 170 | 171 | //-------------------------------------------------------------------------------------------------- 172 | void OnEnterDemo () { 173 | g_frequency = 0.0f; 174 | g_volumeAmplifier = 1.0f; 175 | g_voiceState = e_stopped; 176 | printf("Number keys to adjust volume and adjust clipping.\r\nLetter key to play different sine tones. Space to silence.\r\nMelody1 = ZMAM. left shift / control is super low frequency.\r\nleft alt for a voice sample.\r\n"); 177 | printf("\r\nInstructions:\r\n"); 178 | printf("Turn up volume to 100, turn on clipping. Play notes. Space to silence.\r\n"); 179 | printf("press 3 to turn up volume more. Play notes. Space to silence.\r\n"); 180 | printf("Press 9 to turn up volume. press left alt for voice sample.\r\n"); 181 | } 182 | } -------------------------------------------------------------------------------- /MusicSynth/DemoDrum.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoDrum.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | #include "AudioEffects.h" 10 | #include 11 | 12 | namespace DemoDrum { 13 | 14 | enum EMode { 15 | e_modeSine, 16 | e_modeSineEnvelope, 17 | e_modeSineEnvelopeDecay, 18 | e_modeSineEnvelopeDecayPop, 19 | e_modeSineEnvelopeDecayPopReverb, 20 | 21 | e_modeCount, 22 | 23 | e_modeCymbal 24 | }; 25 | 26 | const char* ModeToString (EMode mode) { 27 | switch (mode) { 28 | case e_modeSine: return "Wave"; 29 | case e_modeSineEnvelope: return "Wave + Envelope"; 30 | case e_modeSineEnvelopeDecay: return "Wave + Envelope + Frequency Decay"; 31 | case e_modeSineEnvelopeDecayPop: return "Wave + Envelope + Frequency Decay + Pop"; 32 | case e_modeSineEnvelopeDecayPopReverb: return "Wave + Envelope + Frequency Decay + Pop + Reverb"; 33 | } 34 | return "???"; 35 | } 36 | 37 | struct SNote { 38 | SNote(float frequency, EMode mode) 39 | : m_frequency(frequency) 40 | , m_mode(mode) 41 | , m_age(0) 42 | , m_dead(false) 43 | , m_releaseAge(0) 44 | , m_phase(0.0f) {} 45 | 46 | float m_frequency; 47 | EMode m_mode; 48 | size_t m_age; 49 | bool m_dead; 50 | size_t m_releaseAge; 51 | float m_phase; 52 | }; 53 | 54 | std::vector g_notes; 55 | std::mutex g_notesMutex; 56 | EMode g_currentMode; 57 | 58 | //-------------------------------------------------------------------------------------------------- 59 | void OnInit() { } 60 | 61 | //-------------------------------------------------------------------------------------------------- 62 | void OnExit() { } 63 | 64 | //-------------------------------------------------------------------------------------------------- 65 | inline float GenerateEnvelope_Minimal (SNote& note, float ageInSeconds) { 66 | // note lifetime 67 | static const float c_noteLifeTime = 0.25f; 68 | 69 | // envelope point calculations 70 | static const float c_noteEnvelope = 0.05f; 71 | static const float c_envelopePtA = 0.0f; 72 | static const float c_envelopePtB = c_noteEnvelope; 73 | static const float c_envelopePtC = c_noteLifeTime - c_noteEnvelope; 74 | static const float c_envelopePtD = c_noteLifeTime; 75 | 76 | // put a small envelope on the front and back 77 | float envelope = Envelope4Pt( 78 | ageInSeconds, 79 | c_envelopePtA, 0.0f, 80 | c_envelopePtB, 1.0f, 81 | c_envelopePtC, 1.0f, 82 | c_envelopePtD, 0.0f 83 | ); 84 | 85 | // kill notes that are too old 86 | if (ageInSeconds > c_noteLifeTime) 87 | note.m_dead = true; 88 | 89 | return envelope; 90 | } 91 | 92 | //-------------------------------------------------------------------------------------------------- 93 | inline float GenerateEnvelope_Drum (SNote& note, float ageInSeconds, bool pop) { 94 | // use an envelope that sounds "drum like" 95 | float envelope = Envelope4Pt( 96 | ageInSeconds, 97 | 0.000f, pop ? 1.0f : 0.0f, // pop by starting at full volume, if we should 98 | 0.010f, 1.0f, // 10ms: attack (silence -> full volume) 99 | 0.020f, 1.0f, // 10ms: hold (full volume) 100 | 0.195f, 0.0f // 175ms: decay (full volume -> silence) 101 | ); 102 | 103 | // kill notes that are too old 104 | if (ageInSeconds > 0.195) 105 | note.m_dead = true; 106 | 107 | return envelope; 108 | } 109 | 110 | //-------------------------------------------------------------------------------------------------- 111 | inline float GenerateEnvelope_Cymbal (SNote& note, float ageInSeconds) { 112 | // use an envelope that sounds "drum like" 113 | float envelope = Envelope5Pt( 114 | ageInSeconds, 115 | 0.000f, 0.0f, 116 | 0.010f, 1.0f, // 10ms: attack (silence -> full volume) 117 | 0.020f, 1.0f, // 10ms: hold (full volume) 118 | 0.040f, 0.2f, // 20ms: decay1 (full volume -> less volume) 119 | 0.215f, 0.0f // 175ms: decay (less volume -> silence) 120 | ); 121 | 122 | // kill notes that are too old 123 | if (ageInSeconds > 0.215) 124 | note.m_dead = true; 125 | 126 | return envelope; 127 | } 128 | 129 | //-------------------------------------------------------------------------------------------------- 130 | inline float GenerateNoteSample_Cymbal (SNote& note, float sampleRate, float ageInSeconds) { 131 | 132 | // do the envelope specific behavior 133 | float envelope = 0.0f; 134 | switch (g_currentMode) { 135 | case e_modeSine: envelope = GenerateEnvelope_Minimal(note, ageInSeconds); break; 136 | default: envelope = GenerateEnvelope_Cymbal(note, ageInSeconds); break; 137 | } 138 | 139 | // return noise shaped by the envelope, taken down in amplitude, so it isn't so loud. 140 | return Noise() * envelope * 0.25f; 141 | } 142 | 143 | //-------------------------------------------------------------------------------------------------- 144 | inline float GenerateNoteSample (SNote& note, float sampleRate) { 145 | 146 | // calculate our age in seconds and advance our age in samples, by 1 sample 147 | float ageInSeconds = float(note.m_age) / sampleRate; 148 | ++note.m_age; 149 | 150 | if (note.m_mode == e_modeCymbal) 151 | return GenerateNoteSample_Cymbal(note, sampleRate, ageInSeconds); 152 | 153 | // do the envelope specific behavior 154 | float envelope = 0.0f; 155 | switch (note.m_mode) { 156 | case e_modeSine: envelope = GenerateEnvelope_Minimal(note, ageInSeconds); break; 157 | default: envelope = GenerateEnvelope_Drum(note, ageInSeconds, note.m_mode == e_modeSineEnvelopeDecayPop); break; 158 | } 159 | 160 | // make frequency decay over time if we should 161 | float frequency = note.m_frequency; 162 | if (note.m_mode >= e_modeSineEnvelopeDecay && ageInSeconds > 0.020f) { 163 | float percent = (ageInSeconds - 0.020f) / 0.175f; 164 | frequency = Lerp(frequency, frequency*0.2f, percent); 165 | } 166 | 167 | // advance phase 168 | note.m_phase = std::fmodf(note.m_phase + frequency / sampleRate, 1.0f); 169 | 170 | // generate the sine value for the current time. 171 | return SineWave(note.m_phase) * envelope; 172 | } 173 | 174 | //-------------------------------------------------------------------------------------------------- 175 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 176 | 177 | // initialize our effects 178 | static SMultiTapReverbEffect reverbEffect; 179 | static bool effectsInitialized = false; 180 | if (!effectsInitialized) { 181 | reverbEffect.SetEffectParams(sampleRate, numChannels); 182 | effectsInitialized = true; 183 | } 184 | 185 | // clear out the buffer when we turn on or off reverb, to clera out old sounds 186 | static bool wasReverbOn = false; 187 | bool isReverbOn = g_currentMode >= e_modeSineEnvelopeDecayPopReverb; 188 | if (wasReverbOn != isReverbOn) { 189 | wasReverbOn = isReverbOn; 190 | reverbEffect.ClearBuffer(); 191 | } 192 | 193 | // get a lock on our notes vector 194 | std::lock_guard guard(g_notesMutex); 195 | 196 | // for every sample in our output buffer 197 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 198 | 199 | // add up all notes to get the final value. 200 | float value = 0.0f; 201 | std::for_each( 202 | g_notes.begin(), 203 | g_notes.end(), 204 | [&value, sampleRate](SNote& note) { 205 | value += GenerateNoteSample(note, sampleRate); 206 | } 207 | ); 208 | 209 | // apply effects if appropriate 210 | if (isReverbOn) 211 | value = reverbEffect.AddSample(value); 212 | 213 | // copy the value to all audio channels 214 | for (size_t channel = 0; channel < numChannels; ++channel) 215 | outputBuffer[channel] = value; 216 | } 217 | 218 | // remove notes that have died 219 | auto iter = std::remove_if( 220 | g_notes.begin(), 221 | g_notes.end(), 222 | [] (const SNote& note) { 223 | return note.m_dead; 224 | } 225 | ); 226 | 227 | if (iter != g_notes.end()) 228 | g_notes.erase(iter); 229 | } 230 | 231 | //-------------------------------------------------------------------------------------------------- 232 | void ReportParams() { 233 | printf("Mode: %s\r\n", ModeToString(g_currentMode)); 234 | } 235 | 236 | //-------------------------------------------------------------------------------------------------- 237 | void OnKey (char key, bool pressed) { 238 | 239 | // nothing to do on key release 240 | if (!pressed) 241 | return; 242 | 243 | // pressing 1 switches modes 244 | if (key == '1') { 245 | g_currentMode = EMode((g_currentMode + 1) % e_modeCount); 246 | ReportParams(); 247 | return; 248 | } 249 | 250 | // space bar = cymbals 251 | if (key == ' ') { 252 | std::lock_guard guard(g_notesMutex); 253 | g_notes.push_back(SNote(0.0f, e_modeCymbal)); 254 | return; 255 | } 256 | 257 | // figure out what frequency to play 258 | float frequency = 0.0f; 259 | switch (key) { 260 | // QWERTY row 261 | case 'Q': frequency = NoteToFrequency(3, 0); break; 262 | case 'W': frequency = NoteToFrequency(3, 1); break; 263 | case 'E': frequency = NoteToFrequency(3, 2); break; 264 | case 'R': frequency = NoteToFrequency(3, 3); break; 265 | case 'T': frequency = NoteToFrequency(3, 4); break; 266 | case 'Y': frequency = NoteToFrequency(3, 5); break; 267 | case 'U': frequency = NoteToFrequency(3, 6); break; 268 | case 'I': frequency = NoteToFrequency(3, 7); break; 269 | case 'O': frequency = NoteToFrequency(3, 8); break; 270 | case 'P': frequency = NoteToFrequency(3, 9); break; 271 | case -37: frequency = NoteToFrequency(3, 10); break; 272 | 273 | // ASDF row 274 | case 'A': frequency = NoteToFrequency(2, 0); break; 275 | case 'S': frequency = NoteToFrequency(2, 1); break; 276 | case 'D': frequency = NoteToFrequency(2, 2); break; 277 | case 'F': frequency = NoteToFrequency(2, 3); break; 278 | case 'G': frequency = NoteToFrequency(2, 4); break; 279 | case 'H': frequency = NoteToFrequency(2, 5); break; 280 | case 'J': frequency = NoteToFrequency(2, 6); break; 281 | case 'K': frequency = NoteToFrequency(2, 7); break; 282 | case 'L': frequency = NoteToFrequency(2, 8); break; 283 | case -70: frequency = NoteToFrequency(2, 9); break; 284 | case -34: frequency = NoteToFrequency(2, 10); break; 285 | 286 | // ZXCV row 287 | case 'Z': frequency = NoteToFrequency(1, 0); break; 288 | case 'X': frequency = NoteToFrequency(1, 1); break; 289 | case 'C': frequency = NoteToFrequency(1, 2); break; 290 | case 'V': frequency = NoteToFrequency(1, 3); break; 291 | case 'B': frequency = NoteToFrequency(1, 4); break; 292 | case 'N': frequency = NoteToFrequency(1, 5); break; 293 | case 'M': frequency = NoteToFrequency(1, 6); break; 294 | case -68: frequency = NoteToFrequency(1, 7); break; 295 | case -66: frequency = NoteToFrequency(1, 8); break; 296 | case -65: frequency = NoteToFrequency(1, 9); break; 297 | case -95: frequency = NoteToFrequency(1, 10); break; // right shift 298 | 299 | // left shift = low freq 300 | case 16: frequency = NoteToFrequency(0, 5); break; 301 | 302 | // left shift = low freq 303 | case -94: frequency = NoteToFrequency(0, 0); break; 304 | 305 | default: { 306 | return; 307 | } 308 | } 309 | 310 | // get a lock on our notes vector and add the new note 311 | std::lock_guard guard(g_notesMutex); 312 | g_notes.push_back(SNote(frequency, g_currentMode)); 313 | } 314 | 315 | //-------------------------------------------------------------------------------------------------- 316 | void OnEnterDemo () { 317 | g_currentMode = e_modeSine; 318 | printf("1 = cycle drum mode.\r\nLetter keys to play drums.\r\nleft shift / control is super low frequency.\r\nSpace for Cymbals\r\nGood Beat = cmdm cmdm\r\nAnother good beat = mcm mcm\r\n"); 319 | printf("\r\nInstructions:\r\n"); 320 | printf("Play some drum notes and cymbals at each stage.\r\n"); 321 | 322 | // clear all the notes out 323 | std::lock_guard guard(g_notesMutex); 324 | g_notes.clear(); 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /MusicSynth/DemoDucking.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoDucking.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | #include "AudioEffects.h" 10 | #include "Samples.h" 11 | #include 12 | 13 | namespace DemoDucking { 14 | 15 | enum ESample { 16 | e_drum1, 17 | e_drum2, 18 | e_drum3, 19 | 20 | e_music 21 | }; 22 | 23 | struct SNote { 24 | SNote(ESample sample, bool duck, bool muteSample) 25 | : m_sample(sample) 26 | , m_age(0) 27 | , m_dead(false) 28 | , m_duck(duck) 29 | , m_muteSample(muteSample) {} 30 | 31 | ESample m_sample; 32 | size_t m_age; 33 | bool m_dead; 34 | bool m_duck; 35 | bool m_muteSample; 36 | }; 37 | 38 | std::vector g_notes; 39 | std::mutex g_notesMutex; 40 | 41 | bool g_musicOn; 42 | 43 | //-------------------------------------------------------------------------------------------------- 44 | void OnInit() { } 45 | 46 | //-------------------------------------------------------------------------------------------------- 47 | void OnExit() { } 48 | 49 | //-------------------------------------------------------------------------------------------------- 50 | SWavFile& GetWavFile(ESample sample) { 51 | switch (sample) { 52 | case e_drum1: return g_sample_clap; 53 | case e_drum2: return g_sample_kick; 54 | case e_music: return g_sample_pvd; 55 | default: return g_sample_ting; 56 | } 57 | } 58 | 59 | //-------------------------------------------------------------------------------------------------- 60 | float GenerateMusicSample (size_t sample, float sampleRate) { 61 | SWavFile& music = GetWavFile(e_music); 62 | 63 | // make the song loop by using modulus on the sample we were asked for 64 | size_t numSamples = (music.m_numSamples / music.m_numChannels); 65 | sample = sample % numSamples; 66 | 67 | // calculate and apply an envelope to the start and end of the sound 68 | const float c_envelopeTime = 0.005f; 69 | float ageInSeconds = float(sample) / float(sampleRate); 70 | float envelope = Envelope4Pt( 71 | ageInSeconds, 72 | 0.0f, 0.0f, 73 | c_envelopeTime, 1.0f, 74 | music.m_lengthSeconds - c_envelopeTime, 1.0f, 75 | music.m_lengthSeconds, 0.0f 76 | ); 77 | 78 | // return the current sample, multiplied by the envelope 79 | size_t sampleIndex = sample*music.m_numChannels; 80 | return music.m_samples[sampleIndex] * envelope; 81 | } 82 | 83 | //-------------------------------------------------------------------------------------------------- 84 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 85 | 86 | // handle starting or stopping music 87 | static bool musicWasOn = false; 88 | static size_t musicStarted = 0; 89 | bool musicIsOn = g_musicOn; 90 | if (musicIsOn != musicWasOn) { 91 | musicStarted = CDemoMgr::GetSampleClock(); 92 | musicWasOn = musicIsOn; 93 | } 94 | 95 | // get a lock on our notes vector 96 | std::lock_guard guard(g_notesMutex); 97 | 98 | // for every sample in our output buffer 99 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 100 | 101 | // add up all samples to get the final value. 102 | float value = 0.0f; 103 | float duckingEnvelopeMax = 0.0f; 104 | std::for_each( 105 | g_notes.begin(), 106 | g_notes.end(), 107 | [&value, &duckingEnvelopeMax, numChannels, sampleRate](SNote& note) { 108 | 109 | SWavFile& wavFile = GetWavFile(note.m_sample); 110 | 111 | size_t sampleIndex = note.m_age*numChannels; 112 | if (sampleIndex >= wavFile.m_numSamples) { 113 | note.m_dead = true; 114 | return; 115 | } 116 | 117 | // calculate and apply an envelope to the sound samples 118 | float ageInSeconds = float(note.m_age) / float(sampleRate); 119 | float envelope = Envelope4Pt( 120 | ageInSeconds, 121 | 0.0f, 0.0f, 122 | 0.1f, 1.0f, 123 | wavFile.m_lengthSeconds - 0.1f, 1.0f, 124 | wavFile.m_lengthSeconds, 0.0f 125 | ); 126 | 127 | float duckingEnvelope = Envelope4Pt( 128 | ageInSeconds, 129 | 0.00f, 0.0f, 130 | 0.10f, 1.0f, 131 | 0.15f, 1.0f, 132 | 0.20f, 0.0f 133 | ); 134 | 135 | if (note.m_duck && duckingEnvelope > duckingEnvelopeMax) 136 | duckingEnvelopeMax = duckingEnvelope; 137 | 138 | if (!note.m_muteSample) 139 | value += wavFile.m_samples[sampleIndex] * envelope; 140 | ++note.m_age; 141 | } 142 | ); 143 | 144 | // don't completely duck the background music, so decrease our ducking envelope a bit 145 | duckingEnvelopeMax *= dBToAmplitude(-3.0f); 146 | 147 | // get our music sample, apply ducking, and add it to our other sample 148 | float duckingEnvelope = (1.0f - duckingEnvelopeMax); 149 | if (musicIsOn) { 150 | float musicSample = GenerateMusicSample(CDemoMgr::GetSampleClock() + sample - musicStarted, sampleRate); 151 | musicSample *= duckingEnvelope; 152 | value += musicSample; 153 | } 154 | 155 | // copy the value to all audio channels 156 | for (size_t channel = 0; channel < numChannels; ++channel) 157 | outputBuffer[channel] = value; 158 | } 159 | 160 | // remove notes that have died 161 | auto iter = std::remove_if( 162 | g_notes.begin(), 163 | g_notes.end(), 164 | [] (const SNote& note) { 165 | return note.m_dead; 166 | } 167 | ); 168 | 169 | if (iter != g_notes.end()) 170 | g_notes.erase(iter); 171 | } 172 | 173 | //-------------------------------------------------------------------------------------------------- 174 | void ReportParams() { 175 | printf("Music: %s\r\n", g_musicOn ? "On" : "Off"); 176 | } 177 | 178 | //-------------------------------------------------------------------------------------------------- 179 | void OnKey (char key, bool pressed) { 180 | 181 | // nothing to do on key release 182 | if (!pressed) 183 | return; 184 | 185 | // pressing 1 toggles music 186 | if (key == '1') { 187 | g_musicOn = !g_musicOn; 188 | ReportParams(); 189 | return; 190 | } 191 | 192 | // figure out what sample to play 193 | ESample sample = e_drum1; 194 | bool duck = false; 195 | bool muteSample = false; 196 | float frequency = 0.0f; 197 | switch (key) { 198 | case 'Q': sample = e_drum1; break; 199 | case 'W': sample = e_drum2; break; 200 | case 'E': sample = e_drum3; break; 201 | 202 | case 'A': sample = e_drum1; duck = false; break; 203 | case 'S': sample = e_drum2; duck = true; break; 204 | case 'D': sample = e_drum3; duck = false; break; 205 | 206 | case 'Z': sample = e_drum1; duck = false; muteSample = true; break; 207 | case 'X': sample = e_drum2; duck = true; muteSample = true; break; 208 | case 'C': sample = e_drum3; duck = false; muteSample = true; break; 209 | default: { 210 | return; 211 | } 212 | } 213 | 214 | // get a lock on our notes vector and add the new note 215 | std::lock_guard guard(g_notesMutex); 216 | g_notes.push_back(SNote(sample, duck, muteSample)); 217 | } 218 | 219 | //-------------------------------------------------------------------------------------------------- 220 | void OnEnterDemo () { 221 | g_musicOn = false; 222 | 223 | printf("1 = toggle music\r\n"); 224 | printf("QWE = drum samples\r\n"); 225 | printf("ASD = drum samples with ducking\r\n"); 226 | printf("ZXC = ducking only\r\n"); 227 | printf("\r\nInstructions:\r\n"); 228 | printf("show the drum samples, then show with music on, highlight the ducking.\r\n"); 229 | 230 | // clear all the notes out 231 | std::lock_guard guard(g_notesMutex); 232 | g_notes.clear(); 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /MusicSynth/DemoList.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoList.h 3 | // 4 | // The list of demos. Used to generate boilerplate code via macro expansion 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | DEMO(Sine) 9 | DEMO(Popping) 10 | DEMO(Clipping) 11 | DEMO(Mixing) 12 | DEMO(Envelopes) 13 | DEMO(WaveForms) 14 | DEMO(BLWaveForms) 15 | DEMO(Additive) 16 | DEMO(TremVib) 17 | DEMO(FMSynth) 18 | DEMO(Delay) 19 | DEMO(Reverb) 20 | DEMO(Flange) 21 | DEMO(Drum) 22 | DEMO(Ducking) 23 | DEMO(Filtering) 24 | DEMO(Stereo) 25 | 26 | #undef DEMO -------------------------------------------------------------------------------- /MusicSynth/DemoMgr.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoMgr.cpp 3 | // 4 | // Handles switching between demos, passing the demos key events, knowing when to exit the app etc. 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | 10 | EDemo CDemoMgr::s_currentDemo = e_demoFirst; 11 | bool CDemoMgr::s_exit = false; 12 | int CDemoMgr::s_volumeMultiplier = 18; 13 | bool CDemoMgr::s_clippingOn = false; 14 | FILE* CDemoMgr::s_recordingWavFile = nullptr; 15 | 16 | // for recording audio 17 | std::mutex CDemoMgr::s_recordingBuffersMutex; 18 | std::queue> CDemoMgr::s_recordingBuffers; 19 | SWaveFileHeader CDemoMgr::s_waveFileHeader; 20 | size_t CDemoMgr::s_recordedNumSamples; 21 | size_t CDemoMgr::s_recordingNumChannels; 22 | size_t CDemoMgr::s_recordingSampleRate; 23 | size_t CDemoMgr::s_sampleClock; // yes in 32 bit mode this is a uint32 and could roll over, but it would take 27 hours. 24 | size_t CDemoMgr::s_numChannels; 25 | float CDemoMgr::s_sampleRate; 26 | 27 | //-------------------------------------------------------------------------------------------------- 28 | static bool FileExists (const char* fileName) { 29 | FILE *file = nullptr; 30 | fopen_s(&file, fileName, "rb"); 31 | if (file) { 32 | fclose(file); 33 | return true; 34 | } 35 | return false; 36 | } 37 | 38 | //-------------------------------------------------------------------------------------------------- 39 | void CDemoMgr::StartRecording() { 40 | 41 | // find a filename 42 | char fileName[256]; 43 | sprintf_s(fileName, 256, "recording.wav"); 44 | int i = 1; 45 | while (FileExists(fileName)) { 46 | sprintf_s(fileName, 256, "recording%i.wav", i); 47 | ++i; 48 | } 49 | 50 | // open the file for writing 51 | fopen_s(&s_recordingWavFile, fileName, "w+b"); 52 | if (!s_recordingWavFile) { 53 | printf("ERROR: could not start recording to %s\r\n", fileName); 54 | return; 55 | } 56 | 57 | // write a dummy header for now. It'll be re-written when we have all our data at the end of 58 | // the recording process. 59 | fwrite(&s_waveFileHeader, sizeof(s_waveFileHeader), 1, s_recordingWavFile); 60 | 61 | // remember that we've recorded 0 samples so far 62 | s_recordedNumSamples = 0; 63 | 64 | // tell the user we've started recording 65 | printf("Started recording audio to %s\r\n", fileName); 66 | } 67 | 68 | //-------------------------------------------------------------------------------------------------- 69 | void CDemoMgr::StopRecording() { 70 | FlushRecordingBuffers(); 71 | 72 | // seek to the beginning of the file 73 | fseek(s_recordingWavFile, 0, SEEK_SET); 74 | 75 | // Fill out the header with the correct information 76 | s_waveFileHeader.Fill(s_recordedNumSamples, s_recordingNumChannels, s_recordingSampleRate); 77 | 78 | // write the header again, now with correct info 79 | fwrite(&s_waveFileHeader, sizeof(s_waveFileHeader), 1, s_recordingWavFile); 80 | 81 | // close the file 82 | fclose(s_recordingWavFile); 83 | s_recordingWavFile = nullptr; 84 | 85 | // clear any data that may be left in the recording buffers 86 | ClearRecordingBuffers(); 87 | 88 | // tell the user we've stopped recording 89 | printf("Recording stopped.\r\n"); 90 | } 91 | 92 | //-------------------------------------------------------------------------------------------------- 93 | void CDemoMgr::Update() { 94 | if (IsRecording()) 95 | FlushRecordingBuffers(); 96 | } 97 | 98 | //-------------------------------------------------------------------------------------------------- 99 | void CDemoMgr::FlushRecordingBuffers() { 100 | std::lock_guard guard(s_recordingBuffersMutex); 101 | while (!s_recordingBuffers.empty()) { 102 | SRecordingBuffer& buffer = *s_recordingBuffers.front(); 103 | 104 | // cache off recording params 105 | s_recordingNumChannels = buffer.m_numChannels; 106 | s_recordingSampleRate = size_t(buffer.m_sampleRate); 107 | 108 | // write the samples to disk 109 | fwrite(buffer.m_buffer, sizeof(int16_t), buffer.m_framesPerBuffer * buffer.m_numChannels, s_recordingWavFile); 110 | 111 | // keep track of how many samples we've recorded 112 | s_recordedNumSamples += buffer.m_framesPerBuffer * buffer.m_numChannels; 113 | 114 | // pop the buffer now that we've consumed it 115 | s_recordingBuffers.pop(); 116 | } 117 | } 118 | 119 | //-------------------------------------------------------------------------------------------------- 120 | void CDemoMgr::ClearRecordingBuffers() { 121 | std::lock_guard guard(s_recordingBuffersMutex); 122 | while (!s_recordingBuffers.empty()) { 123 | s_recordingBuffers.pop(); 124 | } 125 | } 126 | 127 | //-------------------------------------------------------------------------------------------------- 128 | void CDemoMgr::AddRecordingBuffer (float *buffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 129 | 130 | // make a new SRecordingBuffer and store off the params 131 | std::unique_ptr newBuffer = std::make_unique(); 132 | newBuffer->m_framesPerBuffer = framesPerBuffer; 133 | newBuffer->m_numChannels = numChannels; 134 | newBuffer->m_sampleRate = sampleRate; 135 | 136 | // convert samples from floats to int16 137 | newBuffer->m_buffer = new int16_t[framesPerBuffer*numChannels]; 138 | for (size_t i = 0, c = framesPerBuffer*numChannels; i < c; ++i) 139 | newBuffer->m_buffer[i] = ConvertFloatToAudioSample(buffer[i]); 140 | 141 | // get a lock on the mutex and add this buffer 142 | std::lock_guard guard(s_recordingBuffersMutex); 143 | s_recordingBuffers.push(std::move(newBuffer)); 144 | } -------------------------------------------------------------------------------- /MusicSynth/DemoMgr.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoMgr.h 3 | // 4 | // Handles switching between demos, passing the demos key events, knowing when to exit the app etc. 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #pragma once 9 | 10 | #include 11 | #include "AudioUtils.h" 12 | #include "WavFile.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "Samples.h" 18 | 19 | //-------------------------------------------------------------------------------------------------- 20 | enum EDemo { 21 | e_demoUnknown = -1, 22 | #define DEMO(name) e_demo##name, 23 | #include "DemoList.h" 24 | e_demoCount, 25 | e_demoLast = e_demoCount - 1, 26 | e_demoFirst = e_demoUnknown + 1 27 | }; 28 | 29 | //-------------------------------------------------------------------------------------------------- 30 | // forward declarations of demo specific functions, in their respective namespaces 31 | #define DEMO(name) namespace Demo##name {\ 32 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate); \ 33 | void OnKey (char key, bool pressed); \ 34 | void OnEnterDemo (); \ 35 | void OnInit (); \ 36 | void OnExit (); \ 37 | }; 38 | #include "DemoList.h" 39 | 40 | //-------------------------------------------------------------------------------------------------- 41 | class CDemoMgr { 42 | public: 43 | inline static void Init (float sampleRate, size_t numChannels) { 44 | printf("\r\n\r\n\r\n\r\n============================================\r\n"); 45 | printf("Welcome!\r\nUp and down to adjust volume.\r\nLeft and right to change demo.\r\nEnter to toggle clipping.\r\nbackspace to toggle audio recording.\r\nEscape to exit.\r\n"); 46 | printf("sampleRate = %0.0f, numChannels = %i\r\n", sampleRate, numChannels); 47 | printf("============================================\r\n\r\n"); 48 | 49 | s_sampleRate = sampleRate; 50 | s_numChannels = numChannels; 51 | 52 | // load the audio samples 53 | LoadSamples(); 54 | 55 | // give each demo and OnInit() call 56 | #define DEMO(name) Demo##name::OnInit(); 57 | #include "DemoList.h" 58 | 59 | // let the demo know we've entered too 60 | OnEnterDemo(); 61 | } 62 | 63 | inline static void OnEnterDemo () { 64 | 65 | printf("\r\n\r\n--------------------------------------------\r\n"); 66 | 67 | // pass this call onto the current demo 68 | switch (s_currentDemo) { 69 | #define DEMO(name) case e_demo##name: printf( "Demo %i/%i: " #name "\r\n", e_demo##name+1, e_demoCount); Demo##name::OnEnterDemo(); break; 70 | #include "DemoList.h" 71 | } 72 | 73 | printf("--------------------------------------------\r\n\r\n"); 74 | } 75 | 76 | inline static void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 77 | // pass this call onto the current demo 78 | switch (s_currentDemo) { 79 | #define DEMO(name) case e_demo##name: Demo##name::GenerateAudioSamples(outputBuffer, framesPerBuffer, numChannels, sampleRate); break; 80 | #include "DemoList.h" 81 | } 82 | 83 | // store off the original value of outputBuffer so we can use it for recording 84 | float *bufferStart = outputBuffer; 85 | 86 | // apply volume adjustment smoothly over the buffer window via a lerp of amplitude. 87 | // also apply clipping. 88 | bool clip = s_clippingOn; 89 | static float lastVolumeMultiplier = 1.0; 90 | float volumeMultiplier = dBToAmplitude((1.0f - float(s_volumeMultiplier)/20.0f) * -60.0f); 91 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 92 | // lerp the volume change across the buffer 93 | float percent = float(sample) / float(framesPerBuffer); 94 | float volume = Lerp(lastVolumeMultiplier, volumeMultiplier, percent); 95 | 96 | // apply volume and clipping 97 | for (size_t channel = 0; channel < numChannels; ++channel) { 98 | float value = outputBuffer[channel] * volume; 99 | 100 | if (clip) { 101 | if (value > 1.0f) 102 | value = 1.0f; 103 | else if (value < -1.0f) 104 | value = -1.0f; 105 | } 106 | outputBuffer[channel] = value; 107 | } 108 | } 109 | 110 | s_sampleClock += framesPerBuffer; 111 | 112 | lastVolumeMultiplier = volumeMultiplier; 113 | 114 | // if we are recording, add this frame to our frame queue 115 | if (IsRecording()) 116 | AddRecordingBuffer(bufferStart, framesPerBuffer, numChannels, sampleRate); 117 | } 118 | 119 | static void OnKey(char key, bool pressed) { 120 | switch (key) { 121 | // exit when escape is pressed on any demo 122 | case 27: Exit(); return; 123 | // enter toggles clipping 124 | case 13: { 125 | if (pressed) { 126 | s_clippingOn = !s_clippingOn; 127 | printf("clipping = %s\r\n", s_clippingOn ? "on" : "off"); 128 | } 129 | return; 130 | } 131 | // backspace toggles recording 132 | case 8: { 133 | if (pressed) { 134 | if (!s_recordingWavFile) 135 | StartRecording(); 136 | else 137 | StopRecording(); 138 | } 139 | return; 140 | } 141 | // when up arrow pressed, increase volume 142 | case 38: { 143 | if (pressed) { 144 | s_volumeMultiplier++; 145 | if (s_volumeMultiplier > 20) 146 | s_volumeMultiplier = 20; 147 | printf("Master Volume = %i%%\r\n", s_volumeMultiplier * 5); 148 | } 149 | return; 150 | } 151 | // when down arrow pressed, decrease volume 152 | case 40: { 153 | if (pressed) { 154 | s_volumeMultiplier--; 155 | if (s_volumeMultiplier < 0) 156 | s_volumeMultiplier = 0; 157 | printf("Master Volume = %i%%\r\n", s_volumeMultiplier * 5); 158 | } 159 | return; 160 | } 161 | // left arrow means go to previous demo 162 | case 37: { 163 | if (pressed && s_currentDemo > e_demoFirst) { 164 | s_currentDemo = EDemo(int(s_currentDemo) - 1); 165 | OnEnterDemo(); 166 | } 167 | return; 168 | } 169 | // right arrow means go to next demo 170 | case 39: { 171 | if (pressed && s_currentDemo < e_demoLast) { 172 | s_currentDemo = EDemo(int(s_currentDemo) + 1); 173 | OnEnterDemo(); 174 | } 175 | return; 176 | } 177 | } 178 | 179 | // else pass this key event onto the current demo 180 | switch (s_currentDemo) { 181 | #define DEMO(name) case e_demo##name: Demo##name::OnKey(key, pressed); break; 182 | #include "DemoList.h" 183 | } 184 | } 185 | 186 | static bool IsRecording() { return s_recordingWavFile != nullptr; } 187 | 188 | static void StartRecording (); 189 | static void StopRecording (); 190 | static void Update (); 191 | 192 | static void Exit () { 193 | if (IsRecording()) 194 | StopRecording(); 195 | s_exit = true; 196 | 197 | // tell all of our demos about exit in case they need to do any clean up 198 | #define DEMO(name) Demo##name::OnExit(); 199 | #include "DemoList.h" 200 | } 201 | 202 | static bool WantsExit () { return s_exit; } 203 | 204 | static size_t GetSampleClock () { return s_sampleClock; } 205 | static size_t GetNumChannels () { return s_numChannels; } 206 | static float GetSampleRate () { return s_sampleRate; } 207 | 208 | private: 209 | static void FlushRecordingBuffers (); 210 | static void ClearRecordingBuffers (); 211 | static void AddRecordingBuffer (float *buffer, size_t framesPerBuffer, size_t numChannels, float sampleRate); 212 | 213 | private: 214 | struct SRecordingBuffer { 215 | 216 | SRecordingBuffer() { 217 | m_buffer = nullptr; 218 | m_framesPerBuffer = 0; 219 | m_numChannels = 0; 220 | m_sampleRate = 0.0f; 221 | } 222 | 223 | ~SRecordingBuffer() { 224 | delete[] m_buffer; 225 | m_buffer = nullptr; 226 | m_framesPerBuffer = 0; 227 | m_numChannels = 0; 228 | m_sampleRate = 0.0f; 229 | } 230 | 231 | int16_t* m_buffer; 232 | size_t m_framesPerBuffer; 233 | size_t m_numChannels; 234 | float m_sampleRate; 235 | }; 236 | 237 | static EDemo s_currentDemo; 238 | static bool s_exit; 239 | static int s_volumeMultiplier; 240 | static float s_lastVolumeMultiplier; 241 | static bool s_clippingOn; 242 | static FILE* s_recordingWavFile; 243 | 244 | // for recording audio 245 | static std::mutex s_recordingBuffersMutex; 246 | static std::queue> s_recordingBuffers; 247 | static SWaveFileHeader s_waveFileHeader; 248 | static size_t s_recordedNumSamples; 249 | static size_t s_recordingNumChannels; 250 | static size_t s_recordingSampleRate; 251 | static size_t s_sampleClock; 252 | static size_t s_numChannels; 253 | static float s_sampleRate; 254 | }; -------------------------------------------------------------------------------- /MusicSynth/DemoMixing.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoMixing.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | #include 10 | 11 | namespace DemoMixing { 12 | struct SNote { 13 | SNote(float frequency) :m_frequency(frequency), m_age(0), m_dead(false) {} 14 | float m_frequency; 15 | size_t m_age; 16 | bool m_dead; 17 | }; 18 | 19 | std::vector g_notes; 20 | std::mutex g_notesMutex; 21 | 22 | //-------------------------------------------------------------------------------------------------- 23 | void OnInit() { } 24 | 25 | //-------------------------------------------------------------------------------------------------- 26 | void OnExit() { } 27 | 28 | //-------------------------------------------------------------------------------------------------- 29 | inline float GenerateNoteSample (SNote& note, float sampleRate) { 30 | 31 | // note lifetime 32 | static const float c_noteLifeTime = 0.5f; 33 | 34 | // envelope point calculations 35 | static const float c_noteEnvelope = 0.05f; 36 | static const float c_envelopePtA = 0.0f; 37 | static const float c_envelopePtB = c_noteEnvelope; 38 | static const float c_envelopePtC = c_noteLifeTime - c_noteEnvelope; 39 | static const float c_envelopePtD = c_noteLifeTime; 40 | 41 | // calculate our age in seconds and advance our age in samples, by 1 sample 42 | float ageInSeconds = float(note.m_age) / sampleRate; 43 | ++note.m_age; 44 | 45 | // put a small envelope on the front and back 46 | float envelope = Envelope4Pt( 47 | ageInSeconds, 48 | c_envelopePtA, 0.0f, 49 | c_envelopePtB, 1.0f, 50 | c_envelopePtC, 1.0f, 51 | c_envelopePtD, 0.0f 52 | ); 53 | 54 | // kill notes that are too old 55 | if (ageInSeconds > c_noteLifeTime) 56 | note.m_dead = true; 57 | 58 | // generate the sine value for the current time. 59 | // Note that it is ok that we are basing audio samples on age instead of phase, because the 60 | // frequency never changes and we envelope the front and back to avoid popping. 61 | return std::sinf(ageInSeconds*note.m_frequency*2.0f*c_pi) * envelope; 62 | } 63 | 64 | //-------------------------------------------------------------------------------------------------- 65 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 66 | 67 | // get a lock on our notes vector 68 | std::lock_guard guard(g_notesMutex); 69 | 70 | // for every sample in our output buffer 71 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 72 | 73 | // add up all notes to get the final value. 74 | float value = 0.0f; 75 | std::for_each( 76 | g_notes.begin(), 77 | g_notes.end(), 78 | [&value, sampleRate](SNote& note) { 79 | value += GenerateNoteSample(note, sampleRate); 80 | } 81 | ); 82 | 83 | // copy the value to all audio channels 84 | for (size_t channel = 0; channel < numChannels; ++channel) 85 | outputBuffer[channel] = value; 86 | } 87 | 88 | // remove notes that have died 89 | auto iter = std::remove_if( 90 | g_notes.begin(), 91 | g_notes.end(), 92 | [] (const SNote& note) { 93 | return note.m_dead; 94 | } 95 | ); 96 | 97 | if (iter != g_notes.end()) 98 | g_notes.erase(iter); 99 | } 100 | 101 | //-------------------------------------------------------------------------------------------------- 102 | void OnKey (char key, bool pressed) { 103 | 104 | // only listen to key down events 105 | if (!pressed) 106 | return; 107 | 108 | // figure out what frequency to play 109 | float frequency = 0.0f; 110 | switch (key) { 111 | // number row 112 | case '1': frequency = NoteToFrequency(4, 0); break; 113 | case '2': frequency = NoteToFrequency(4, 1); break; 114 | case '3': frequency = NoteToFrequency(4, 2); break; 115 | case '4': frequency = NoteToFrequency(4, 3); break; 116 | case '5': frequency = NoteToFrequency(4, 4); break; 117 | case '6': frequency = NoteToFrequency(4, 5); break; 118 | case '7': frequency = NoteToFrequency(4, 6); break; 119 | case '8': frequency = NoteToFrequency(4, 7); break; 120 | case '9': frequency = NoteToFrequency(4, 8); break; 121 | case '0': frequency = NoteToFrequency(4, 9); break; 122 | case -67: frequency = NoteToFrequency(4, 10); break; 123 | 124 | // QWERTY row 125 | case 'Q': frequency = NoteToFrequency(3, 0); break; 126 | case 'W': frequency = NoteToFrequency(3, 1); break; 127 | case 'E': frequency = NoteToFrequency(3, 2); break; 128 | case 'R': frequency = NoteToFrequency(3, 3); break; 129 | case 'T': frequency = NoteToFrequency(3, 4); break; 130 | case 'Y': frequency = NoteToFrequency(3, 5); break; 131 | case 'U': frequency = NoteToFrequency(3, 6); break; 132 | case 'I': frequency = NoteToFrequency(3, 7); break; 133 | case 'O': frequency = NoteToFrequency(3, 8); break; 134 | case 'P': frequency = NoteToFrequency(3, 9); break; 135 | case -37: frequency = NoteToFrequency(3, 10); break; 136 | 137 | // ASDF row 138 | case 'A': frequency = NoteToFrequency(2, 0); break; 139 | case 'S': frequency = NoteToFrequency(2, 1); break; 140 | case 'D': frequency = NoteToFrequency(2, 2); break; 141 | case 'F': frequency = NoteToFrequency(2, 3); break; 142 | case 'G': frequency = NoteToFrequency(2, 4); break; 143 | case 'H': frequency = NoteToFrequency(2, 5); break; 144 | case 'J': frequency = NoteToFrequency(2, 6); break; 145 | case 'K': frequency = NoteToFrequency(2, 7); break; 146 | case 'L': frequency = NoteToFrequency(2, 8); break; 147 | case -70: frequency = NoteToFrequency(2, 9); break; 148 | case -34: frequency = NoteToFrequency(2, 10); break; 149 | 150 | // ZXCV row 151 | case 'Z': frequency = NoteToFrequency(1, 0); break; 152 | case 'X': frequency = NoteToFrequency(1, 1); break; 153 | case 'C': frequency = NoteToFrequency(1, 2); break; 154 | case 'V': frequency = NoteToFrequency(1, 3); break; 155 | case 'B': frequency = NoteToFrequency(1, 4); break; 156 | case 'N': frequency = NoteToFrequency(1, 5); break; 157 | case 'M': frequency = NoteToFrequency(1, 6); break; 158 | case -68: frequency = NoteToFrequency(1, 7); break; 159 | case -66: frequency = NoteToFrequency(1, 8); break; 160 | case -65: frequency = NoteToFrequency(1, 9); break; 161 | case -95: frequency = NoteToFrequency(1, 10); break; // right shift 162 | 163 | // left shift = low freq 164 | case 16: frequency = NoteToFrequency(0, 5); break; 165 | 166 | // left shift = low freq 167 | case -94: frequency = NoteToFrequency(0, 0); break; 168 | 169 | default: { 170 | return; 171 | } 172 | } 173 | 174 | // get a lock on our notes vector and add the new note 175 | std::lock_guard guard(g_notesMutex); 176 | g_notes.push_back(SNote(frequency)); 177 | } 178 | 179 | //-------------------------------------------------------------------------------------------------- 180 | void OnEnterDemo () { 181 | printf("Press the keys to play different sine tones polyphonically.\r\nleft shift / control is super low frequency.\r\n"); 182 | printf("\r\nInstructions:\r\n"); 183 | printf("Play some notes, show multiple playing at once.\r\n"); 184 | 185 | // clear all the notes out 186 | std::lock_guard guard(g_notesMutex); 187 | g_notes.clear(); 188 | } 189 | } -------------------------------------------------------------------------------- /MusicSynth/DemoPopping.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoPopping.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | 10 | namespace DemoPopping { 11 | 12 | enum EMode { 13 | e_silence, 14 | e_notesPop, 15 | e_notesNoPop, 16 | e_notesSlideNoPop, 17 | e_samplePop, 18 | e_sampleNoPop 19 | }; 20 | 21 | const char* ModeToString (EMode mode) { 22 | switch (mode) { 23 | case e_silence: return "Silence"; 24 | case e_notesPop: return "Popping Notes"; 25 | case e_notesNoPop: return "Notes Without Popping"; 26 | case e_notesSlideNoPop: return "Sliding Notes"; 27 | case e_samplePop: return "Popping Sample"; 28 | case e_sampleNoPop: return "Sample Without Popping"; 29 | } 30 | return "???"; 31 | } 32 | 33 | EMode g_mode = e_silence; 34 | 35 | //-------------------------------------------------------------------------------------------------- 36 | void OnInit() { } 37 | 38 | //-------------------------------------------------------------------------------------------------- 39 | void OnExit() { } 40 | 41 | //-------------------------------------------------------------------------------------------------- 42 | void SampleAudioSamples(float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate, size_t &baseIndex, bool pop) { 43 | 44 | SWavFile& sound = g_sample_dreams; 45 | 46 | // fill the buffer 47 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels, ++baseIndex) { 48 | 49 | // handle the sound dieing when it is done 50 | size_t sampleIndex = baseIndex*sound.m_numChannels; 51 | if (sampleIndex >= sound.m_numSamples) { 52 | g_mode = e_silence; 53 | for (size_t channel = 0; channel < numChannels; ++channel) 54 | outputBuffer[channel] = 0.0f; 55 | return; 56 | } 57 | 58 | // calculate and apply an envelope to the sound samples 59 | float envelope = 1.0f; 60 | if (!pop) { 61 | const float c_envelopeTime = 0.03f; 62 | float ageInSeconds = float(baseIndex) / sampleRate; 63 | envelope = Envelope4Pt( 64 | ageInSeconds, 65 | 0.0f, 0.0f, 66 | c_envelopeTime, 1.0f, 67 | sound.m_lengthSeconds - c_envelopeTime, 1.0f, 68 | sound.m_lengthSeconds, 0.0f 69 | ); 70 | } 71 | 72 | // return the sample value multiplied by the envelope 73 | float value = sound.m_samples[sampleIndex] * envelope; 74 | 75 | // copy the value to all audio channels 76 | for (size_t channel = 0; channel < numChannels; ++channel) 77 | outputBuffer[channel] = value; 78 | } 79 | 80 | } 81 | 82 | //-------------------------------------------------------------------------------------------------- 83 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 84 | 85 | // state information stored as statics 86 | static EMode mode = e_silence; 87 | static float phase = 0.0f; 88 | static size_t sampleIndex = 0; 89 | 90 | // switch modes if we should 91 | EMode newMode = g_mode; 92 | if (newMode != mode) { 93 | mode = newMode; 94 | phase = 0.0f; 95 | sampleIndex = 0; 96 | } 97 | 98 | // sample our audio samples if we should 99 | if (mode == e_samplePop || mode == e_sampleNoPop) { 100 | SampleAudioSamples(outputBuffer, framesPerBuffer, numChannels, sampleRate, sampleIndex, mode == e_samplePop); 101 | return; 102 | } 103 | 104 | // calculate how many audio samples happen in 1/4 of a second 105 | const size_t c_quarterSecond = size_t(sampleRate) / 4; 106 | 107 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels, ++sampleIndex) { 108 | 109 | // calculate a floating point time in seconds 110 | float timeInSeconds = float(sampleIndex) / sampleRate; 111 | 112 | // figure out how many quarter seconds have elapsed 113 | size_t quarterSeconds = size_t(timeInSeconds*4.0f); 114 | 115 | // calculate how far we are into our current quarter second chunk of time 116 | float quarterSecondsPercent = std::fmodf(timeInSeconds*4.0f, 1.0f); 117 | 118 | // go back to silence after 2 seconds 119 | if (quarterSeconds > 7) 120 | g_mode = e_silence; 121 | 122 | // figure out what our current audio sample should be, based on time and our behavior mode 123 | float value = 0.0f; 124 | switch (mode) { 125 | case e_notesPop: { 126 | // play a different note depending on which quarter second we are in 127 | float frequency = 0.0f; 128 | switch (quarterSeconds) { 129 | case 0: frequency = NoteToFrequency(3, 0); break; 130 | case 1: frequency = NoteToFrequency(3, 1); break; 131 | case 2: frequency = NoteToFrequency(3, 3); break; 132 | case 3: frequency = NoteToFrequency(3, 1); break; 133 | case 4: frequency = NoteToFrequency(3, 0); break; 134 | case 5: frequency = NoteToFrequency(3, 1); break; 135 | case 6: frequency = NoteToFrequency(3, 5); break; 136 | case 7: frequency = NoteToFrequency(3, 1); break; 137 | } 138 | 139 | // calculate the sine value based entirely on time and frequency 140 | value = std::sinf(timeInSeconds*frequency*2.0f*c_pi); 141 | break; 142 | } 143 | case e_notesNoPop: { 144 | // calculate an envelope for the beginning and end to avoid pops there 145 | float envelope = 1.0f; 146 | if (timeInSeconds < 0.05f) 147 | envelope = Lerp(0.0, 1.0, timeInSeconds / 0.05f); 148 | else if (timeInSeconds > 1.95) 149 | envelope = Lerp(1.0, 0.0, std::fmin((timeInSeconds - 1.95f) / 0.05f, 1.0f)); 150 | 151 | // play a different note depending on which quarter second we are in 152 | float frequency = 0.0f; 153 | switch (quarterSeconds) { 154 | case 0: frequency = NoteToFrequency(3, 0); break; 155 | case 1: frequency = NoteToFrequency(3, 1); break; 156 | case 2: frequency = NoteToFrequency(3, 3); break; 157 | case 3: frequency = NoteToFrequency(3, 1); break; 158 | case 4: frequency = NoteToFrequency(3, 0); break; 159 | case 5: frequency = NoteToFrequency(3, 1); break; 160 | case 6: frequency = NoteToFrequency(3, 5); break; 161 | case 7: frequency = NoteToFrequency(3, 1); break; 162 | } 163 | 164 | // calculate how much to advance our phase for this frequency 165 | float phaseAdvance = frequency / sampleRate; 166 | 167 | // calculate the sine value based entirely on phase 168 | value = std::sinf(phase * 2.0f * c_pi); 169 | 170 | // multiply in the envelope to avoid popping at the beginning and end 171 | value *= envelope; 172 | 173 | // advance the phase, making sure to stay within 0 and 1 174 | phase += phaseAdvance; 175 | phase = std::fmod(phase, 1.0f); 176 | break; 177 | } 178 | case e_notesSlideNoPop: { 179 | // calculate an envelope for the beginning and end to avoid pops there 180 | float envelope = 1.0f; 181 | if (timeInSeconds < 0.05f) 182 | envelope = Lerp(0.0, 1.0, timeInSeconds / 0.05f); 183 | else if (timeInSeconds > 1.95) 184 | envelope = Lerp(1.0, 0.0, std::fmin((timeInSeconds - 1.95f) / 0.05f, 1.0f)); 185 | 186 | // slide between different notes depending on which quarter second we are in 187 | float frequency = 0.0f; 188 | switch (quarterSeconds) { 189 | case 0: frequency = Lerp(NoteToFrequency(3, 0), NoteToFrequency(3, 1), quarterSecondsPercent); break; 190 | case 1: frequency = Lerp(NoteToFrequency(3, 1), NoteToFrequency(3, 3), quarterSecondsPercent); break; 191 | case 2: frequency = Lerp(NoteToFrequency(3, 3), NoteToFrequency(3, 1), quarterSecondsPercent); break; 192 | case 3: frequency = Lerp(NoteToFrequency(3, 1), NoteToFrequency(3, 0), quarterSecondsPercent); break; 193 | case 4: frequency = Lerp(NoteToFrequency(3, 0), NoteToFrequency(3, 1), quarterSecondsPercent); break; 194 | case 5: frequency = Lerp(NoteToFrequency(3, 1), NoteToFrequency(3, 5), quarterSecondsPercent); break; 195 | case 6: frequency = Lerp(NoteToFrequency(3, 5), NoteToFrequency(3, 1), quarterSecondsPercent); break; 196 | case 7: frequency = Lerp(NoteToFrequency(3, 1), NoteToFrequency(3, 0), quarterSecondsPercent); break; 197 | } 198 | 199 | // calculate how much to advance our phase for this frequency 200 | float phaseAdvance = frequency / sampleRate; 201 | 202 | // calculate the sine value based entirely on phase 203 | value = std::sinf(phase * 2.0f * c_pi); 204 | 205 | // multiply in the envelope to avoid popping at the beginning and end 206 | value *= envelope; 207 | 208 | // advance the phase, making sure to stay within 0 and 1 209 | phase += phaseAdvance; 210 | phase = std::fmod(phase, 1.0f); 211 | break; 212 | } 213 | } 214 | 215 | // copy the value to all audio channels 216 | for (size_t channel = 0; channel < numChannels; ++channel) 217 | outputBuffer[channel] = value; 218 | } 219 | } 220 | 221 | //-------------------------------------------------------------------------------------------------- 222 | void ReportParams () { 223 | printf("%s\r\n", ModeToString(g_mode)); 224 | } 225 | 226 | //-------------------------------------------------------------------------------------------------- 227 | void OnKey (char key, bool pressed) { 228 | 229 | // only listen to key down events 230 | if (!pressed) 231 | return; 232 | 233 | // switch mode based on key press 234 | switch (key) { 235 | case '1': g_mode = e_notesPop; ReportParams(); break; 236 | case '2': g_mode = e_notesNoPop; ReportParams(); break; 237 | case '3': g_mode = e_notesSlideNoPop; ReportParams(); break; 238 | case '4': g_mode = e_samplePop; ReportParams(); break; 239 | case '5': g_mode = e_sampleNoPop; ReportParams(); break; 240 | } 241 | } 242 | 243 | //-------------------------------------------------------------------------------------------------- 244 | void OnEnterDemo () { 245 | g_mode = e_silence; 246 | printf("1 = notes with pop.\r\n2 = notes without pop.\r\n3 = note slide without pop.\r\n4 = sample with pop\r\n5 = sample without pop\r\n"); 247 | printf("\r\nInstructions:\r\n"); 248 | printf("#1 and point out popping. #2 no popping. #3 shows what you can do.\r\n"); 249 | printf("#4/#5 to show how envelopes can help.\r\n"); 250 | } 251 | } -------------------------------------------------------------------------------- /MusicSynth/DemoSine.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoSine.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | 10 | namespace DemoSine { 11 | 12 | float g_frequency = 0.0f; 13 | 14 | //-------------------------------------------------------------------------------------------------- 15 | void OnInit() { } 16 | 17 | //-------------------------------------------------------------------------------------------------- 18 | void OnExit() { } 19 | 20 | //-------------------------------------------------------------------------------------------------- 21 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 22 | static float phase = 0.0f; 23 | 24 | // calculate how much our phase should change each sample 25 | float phaseAdvance = g_frequency / sampleRate; 26 | 27 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 28 | 29 | // get the sine wave amplitude for this phase (angle) 30 | float value = SineWave(phase); 31 | 32 | // advance the phase, making sure to stay within 0 and 1 33 | phase += phaseAdvance; 34 | phase = std::fmod(phase, 1.0f); 35 | 36 | // copy the value to all audio channels 37 | for (size_t channel = 0; channel < numChannels; ++channel) 38 | outputBuffer[channel] = value; 39 | } 40 | } 41 | 42 | //-------------------------------------------------------------------------------------------------- 43 | void OnKey (char key, bool pressed) { 44 | 45 | // only listen to key down events 46 | if (!pressed) 47 | return; 48 | 49 | switch (key) { 50 | // number row 51 | case '1': g_frequency = NoteToFrequency(4, 0); break; 52 | case '2': g_frequency = NoteToFrequency(4, 1); break; 53 | case '3': g_frequency = NoteToFrequency(4, 2); break; 54 | case '4': g_frequency = NoteToFrequency(4, 3); break; 55 | case '5': g_frequency = NoteToFrequency(4, 4); break; 56 | case '6': g_frequency = NoteToFrequency(4, 5); break; 57 | case '7': g_frequency = NoteToFrequency(4, 6); break; 58 | case '8': g_frequency = NoteToFrequency(4, 7); break; 59 | case '9': g_frequency = NoteToFrequency(4, 8); break; 60 | case '0': g_frequency = NoteToFrequency(4, 9); break; 61 | case -67: g_frequency = NoteToFrequency(4, 10); break; 62 | 63 | // QWERTY row 64 | case 'Q': g_frequency = NoteToFrequency(3, 0); break; 65 | case 'W': g_frequency = NoteToFrequency(3, 1); break; 66 | case 'E': g_frequency = NoteToFrequency(3, 2); break; 67 | case 'R': g_frequency = NoteToFrequency(3, 3); break; 68 | case 'T': g_frequency = NoteToFrequency(3, 4); break; 69 | case 'Y': g_frequency = NoteToFrequency(3, 5); break; 70 | case 'U': g_frequency = NoteToFrequency(3, 6); break; 71 | case 'I': g_frequency = NoteToFrequency(3, 7); break; 72 | case 'O': g_frequency = NoteToFrequency(3, 8); break; 73 | case 'P': g_frequency = NoteToFrequency(3, 9); break; 74 | case -37: g_frequency = NoteToFrequency(3, 10); break; 75 | 76 | // ASDF row 77 | case 'A': g_frequency = NoteToFrequency(2, 0); break; 78 | case 'S': g_frequency = NoteToFrequency(2, 1); break; 79 | case 'D': g_frequency = NoteToFrequency(2, 2); break; 80 | case 'F': g_frequency = NoteToFrequency(2, 3); break; 81 | case 'G': g_frequency = NoteToFrequency(2, 4); break; 82 | case 'H': g_frequency = NoteToFrequency(2, 5); break; 83 | case 'J': g_frequency = NoteToFrequency(2, 6); break; 84 | case 'K': g_frequency = NoteToFrequency(2, 7); break; 85 | case 'L': g_frequency = NoteToFrequency(2, 8); break; 86 | case -70: g_frequency = NoteToFrequency(2, 9); break; 87 | case -34: g_frequency = NoteToFrequency(2, 10); break; 88 | 89 | // ZXCV row 90 | case 'Z': g_frequency = NoteToFrequency(1, 0); break; 91 | case 'X': g_frequency = NoteToFrequency(1, 1); break; 92 | case 'C': g_frequency = NoteToFrequency(1, 2); break; 93 | case 'V': g_frequency = NoteToFrequency(1, 3); break; 94 | case 'B': g_frequency = NoteToFrequency(1, 4); break; 95 | case 'N': g_frequency = NoteToFrequency(1, 5); break; 96 | case 'M': g_frequency = NoteToFrequency(1, 6); break; 97 | case -68: g_frequency = NoteToFrequency(1, 7); break; 98 | case -66: g_frequency = NoteToFrequency(1, 8); break; 99 | case -65: g_frequency = NoteToFrequency(1, 9); break; 100 | case -95: g_frequency = NoteToFrequency(1, 10); break; // right shift 101 | 102 | // left shift = low freq 103 | case 16: g_frequency = NoteToFrequency(0, 5); break; 104 | 105 | // left shift = low freq 106 | case -94: g_frequency = NoteToFrequency(0, 0); break; 107 | 108 | // silence 109 | case ' ': g_frequency = 0.0f; break; 110 | default: { 111 | return; 112 | } 113 | } 114 | 115 | printf("Frequency = %0.2fhz\r\n", g_frequency); 116 | } 117 | 118 | //-------------------------------------------------------------------------------------------------- 119 | void OnEnterDemo () { 120 | g_frequency = 0.0f; 121 | printf("Press the keys to play different sine tones. Space to silence.\r\nMelody1 = ZMAM x4 then Z,A, x4. left shift / control is super low frequency.\r\n"); 122 | printf("\r\nInstructions:\r\n"); 123 | printf("play different tones, explain that it's different frequencies of sine waves.\r\n"); 124 | printf("Show how going up an octave sounds the same.\r\n"); 125 | } 126 | } -------------------------------------------------------------------------------- /MusicSynth/DemoStereo.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoStereo.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | #include "AudioEffects.h" 10 | #include 11 | 12 | namespace DemoStereo { 13 | 14 | struct SNote { 15 | SNote(float frequency) 16 | : m_frequency(frequency) 17 | , m_age(0) 18 | , m_dead(false) 19 | , m_releaseAge(0) 20 | , m_phase(0.0f) {} 21 | 22 | float m_frequency; 23 | size_t m_age; 24 | bool m_dead; 25 | size_t m_releaseAge; 26 | float m_phase; 27 | }; 28 | 29 | std::vector g_notes; 30 | std::mutex g_notesMutex; 31 | bool g_rotateSound; 32 | bool g_pingPongDelay; 33 | bool g_cymbalsOn; 34 | bool g_voiceOn; 35 | 36 | //-------------------------------------------------------------------------------------------------- 37 | void OnInit() { } 38 | 39 | //-------------------------------------------------------------------------------------------------- 40 | void OnExit() { } 41 | 42 | //-------------------------------------------------------------------------------------------------- 43 | inline float GenerateNoteSample (SNote& note, float sampleRate) { 44 | 45 | float c_decayTime = 1.5f; 46 | size_t c_numHarmonics = 10; 47 | 48 | // calculate our age in seconds and advance our age in samples, by 1 sample 49 | float ageInSeconds = float(note.m_age) / sampleRate; 50 | ++note.m_age; 51 | 52 | // handle the note dieing 53 | if (ageInSeconds > c_decayTime) 54 | { 55 | note.m_dead = true; 56 | return 0.0; 57 | } 58 | 59 | // add our harmonics together 60 | float ret = 0.0f; 61 | for (size_t index = 1; index <= c_numHarmonics; ++index) { 62 | float envelope = Envelope4Pt( 63 | ageInSeconds * float(index), 64 | 0.0f, 0.0f, 65 | c_decayTime*0.05f, 1.0f, 66 | c_decayTime*0.10f, 0.6f, 67 | c_decayTime, 0.0f 68 | ); 69 | float phase = std::fmodf(note.m_phase * float(index) , 1.0f); 70 | //ret += SineWave(phase) * envelope; 71 | ret += SineWave(phase) * envelope; 72 | } 73 | 74 | // advance phase 75 | note.m_phase = std::fmodf(note.m_phase + note.m_frequency / sampleRate, 1.0f); 76 | 77 | // return the value 78 | return ret; 79 | } 80 | 81 | //-------------------------------------------------------------------------------------------------- 82 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 83 | 84 | // handle effect params 85 | static SPingPongDelayEffect delayEffect; 86 | bool rotateSound = g_rotateSound; 87 | static bool wasDelayOn = false; 88 | bool isDelayOn = g_pingPongDelay; 89 | if (isDelayOn != wasDelayOn) { 90 | delayEffect.SetEffectParams(0.33f, sampleRate, numChannels, 0.0625f); 91 | wasDelayOn = isDelayOn; 92 | } 93 | 94 | // handle playing samples 95 | static bool cymbalsWereOn = false; 96 | static size_t cymbalsStarted = 0; 97 | bool cymbalsAreOn = g_cymbalsOn; 98 | if (cymbalsWereOn != cymbalsAreOn) { 99 | cymbalsWereOn = cymbalsAreOn; 100 | cymbalsStarted = CDemoMgr::GetSampleClock(); 101 | } 102 | static bool voiceWasOn = false; 103 | static size_t voiceStarted = 0; 104 | bool voiceIsOn = g_voiceOn; 105 | if (voiceWasOn != voiceIsOn) { 106 | voiceWasOn = voiceIsOn; 107 | voiceStarted = CDemoMgr::GetSampleClock(); 108 | } 109 | 110 | // get a lock on our notes vector 111 | std::lock_guard guard(g_notesMutex); 112 | 113 | // for every sample in our output buffer 114 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 115 | 116 | // add up all notes to get the final value. 117 | float valueMono = 0.0f; 118 | std::for_each( 119 | g_notes.begin(), 120 | g_notes.end(), 121 | [&valueMono, sampleRate](SNote& note) { 122 | valueMono += GenerateNoteSample(note, sampleRate) * 0.25f; 123 | } 124 | ); 125 | 126 | // sample the samples if we should 127 | if (cymbalsAreOn) { 128 | size_t sampleIndex = (CDemoMgr::GetSampleClock() + sample - cymbalsStarted) * numChannels; 129 | if (sampleIndex < g_sample_cymbal.m_numSamples) { 130 | valueMono += g_sample_cymbal.m_samples[sampleIndex] * 2.0f; 131 | } 132 | else { 133 | g_cymbalsOn = false; 134 | } 135 | } 136 | if (voiceIsOn) { 137 | size_t sampleIndex = (CDemoMgr::GetSampleClock() + sample - voiceStarted) * numChannels; 138 | if (sampleIndex < g_sample_legend2.m_numSamples) { 139 | valueMono += g_sample_legend2.m_samples[sampleIndex] * 2.0f; 140 | } 141 | else { 142 | g_voiceOn = false; 143 | } 144 | } 145 | 146 | // split the mono sound into a stereo sound 147 | float valueLeft = valueMono; 148 | float valueRight = valueMono; 149 | 150 | // if sound rotation is on, make some sine/cosine tones to simulate 3d 151 | if (rotateSound) { 152 | float timeInSeconds = float(CDemoMgr::GetSampleClock() + sample) / sampleRate; 153 | valueLeft *= std::sinf(timeInSeconds*0.25f*2.0f*c_pi) * 0.45f + 0.55f; 154 | valueRight *= std::cosf(timeInSeconds*0.25f*2.0f*c_pi) * 0.45f + 0.55f; 155 | } 156 | 157 | // do ping pong delay if we should 158 | if (isDelayOn && numChannels >= 2) { 159 | float echoLeft, echoRight; 160 | delayEffect.AddSample(valueMono, echoLeft, echoRight); 161 | valueLeft += echoLeft; 162 | valueRight += echoRight; 163 | } 164 | 165 | // copy the values to all audio channels 166 | for (size_t channel = 0; channel < numChannels; ++channel) { 167 | if (channel % 2 == 0) 168 | outputBuffer[channel] = valueLeft; 169 | else 170 | outputBuffer[channel] = valueRight; 171 | } 172 | } 173 | 174 | // remove notes that have died 175 | auto iter = std::remove_if( 176 | g_notes.begin(), 177 | g_notes.end(), 178 | [] (const SNote& note) { 179 | return note.m_dead; 180 | } 181 | ); 182 | 183 | if (iter != g_notes.end()) 184 | g_notes.erase(iter); 185 | } 186 | 187 | //-------------------------------------------------------------------------------------------------- 188 | void ReportParams() { 189 | printf("Rotate Sound: %s, Ping Pong Delay: %s\r\n", g_rotateSound ? "On" : "Off", g_pingPongDelay ? "On" : "Off"); 190 | } 191 | 192 | //-------------------------------------------------------------------------------------------------- 193 | void OnKey (char key, bool pressed) { 194 | 195 | // nothing to do on key release 196 | if (!pressed) 197 | return; 198 | 199 | // pressing 1 toggles music 200 | if (key == '1') { 201 | g_rotateSound = !g_rotateSound; 202 | ReportParams(); 203 | return; 204 | } 205 | 206 | // pressing 2 toggles ping pong delay 207 | if (key == '2') { 208 | g_pingPongDelay = !g_pingPongDelay; 209 | ReportParams(); 210 | return; 211 | } 212 | 213 | // samples 214 | if (key == '3') { 215 | g_cymbalsOn = !g_cymbalsOn; 216 | return; 217 | } 218 | if (key == '4') { 219 | g_voiceOn = !g_voiceOn; 220 | return; 221 | } 222 | 223 | // figure out what frequency to play 224 | float frequency = 0.0f; 225 | switch (key) { 226 | // QWERTY row 227 | case 'Q': frequency = NoteToFrequency(3, 0); break; 228 | case 'W': frequency = NoteToFrequency(3, 1); break; 229 | case 'E': frequency = NoteToFrequency(3, 2); break; 230 | case 'R': frequency = NoteToFrequency(3, 3); break; 231 | case 'T': frequency = NoteToFrequency(3, 4); break; 232 | case 'Y': frequency = NoteToFrequency(3, 5); break; 233 | case 'U': frequency = NoteToFrequency(3, 6); break; 234 | case 'I': frequency = NoteToFrequency(3, 7); break; 235 | case 'O': frequency = NoteToFrequency(3, 8); break; 236 | case 'P': frequency = NoteToFrequency(3, 9); break; 237 | case -37: frequency = NoteToFrequency(3, 10); break; 238 | 239 | // ASDF row 240 | case 'A': frequency = NoteToFrequency(2, 0); break; 241 | case 'S': frequency = NoteToFrequency(2, 1); break; 242 | case 'D': frequency = NoteToFrequency(2, 2); break; 243 | case 'F': frequency = NoteToFrequency(2, 3); break; 244 | case 'G': frequency = NoteToFrequency(2, 4); break; 245 | case 'H': frequency = NoteToFrequency(2, 5); break; 246 | case 'J': frequency = NoteToFrequency(2, 6); break; 247 | case 'K': frequency = NoteToFrequency(2, 7); break; 248 | case 'L': frequency = NoteToFrequency(2, 8); break; 249 | case -70: frequency = NoteToFrequency(2, 9); break; 250 | case -34: frequency = NoteToFrequency(2, 10); break; 251 | 252 | // ZXCV row 253 | case 'Z': frequency = NoteToFrequency(1, 0); break; 254 | case 'X': frequency = NoteToFrequency(1, 1); break; 255 | case 'C': frequency = NoteToFrequency(1, 2); break; 256 | case 'V': frequency = NoteToFrequency(1, 3); break; 257 | case 'B': frequency = NoteToFrequency(1, 4); break; 258 | case 'N': frequency = NoteToFrequency(1, 5); break; 259 | case 'M': frequency = NoteToFrequency(1, 6); break; 260 | case -68: frequency = NoteToFrequency(1, 7); break; 261 | case -66: frequency = NoteToFrequency(1, 8); break; 262 | case -65: frequency = NoteToFrequency(1, 9); break; 263 | case -95: frequency = NoteToFrequency(1, 10); break; // right shift 264 | 265 | // left shift = low freq 266 | case 16: frequency = NoteToFrequency(0, 5); break; 267 | 268 | // left shift = low freq 269 | case -94: frequency = NoteToFrequency(0, 0); break; 270 | 271 | default: { 272 | return; 273 | } 274 | } 275 | 276 | // get a lock on our notes vector and add the new note 277 | std::lock_guard guard(g_notesMutex); 278 | g_notes.push_back(SNote(frequency)); 279 | } 280 | 281 | //-------------------------------------------------------------------------------------------------- 282 | void OnEnterDemo () { 283 | g_rotateSound = false; 284 | g_pingPongDelay = false; 285 | g_cymbalsOn = false; 286 | g_voiceOn = false; 287 | printf("Letter keys to play notes.\r\nleft shift / control is super low frequency.\r\n"); 288 | printf("1 = Toggle sound rotation\r\n"); 289 | printf("2 = Toggle ping pong delay\r\n"); 290 | printf("3 = Cymbals Sample\r\n"); 291 | printf("4 = Voice Sample\r\n"); 292 | printf("\r\nInstructions:\r\n"); 293 | printf("Interesting sound with both on = afqt also z zma z zmak, also shift,control repeated\r\n"); 294 | 295 | // clear all the notes out 296 | std::lock_guard guard(g_notesMutex); 297 | g_notes.clear(); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /MusicSynth/DemoTremVib.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoTremVib.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | #include 10 | 11 | namespace DemoTremVib { 12 | 13 | enum EWaveForm { 14 | e_waveSine, 15 | e_waveSaw, 16 | e_waveSquare, 17 | e_waveTriangle 18 | }; 19 | 20 | enum EEffectSpeed { 21 | e_effectOff, 22 | e_effectSlow, 23 | e_effectMedium, 24 | e_effectFast 25 | }; 26 | 27 | struct SNote { 28 | SNote(float frequency, EWaveForm waveForm, EEffectSpeed tremolo, EEffectSpeed vibrato) 29 | : m_frequency(frequency) 30 | , m_waveForm(waveForm) 31 | , m_tremolo(tremolo) 32 | , m_vibrato(vibrato) 33 | , m_age(0) 34 | , m_dead(false) 35 | , m_wantsKeyRelease(false) 36 | , m_releaseAge(0) 37 | , m_phase(0.0f) {} 38 | 39 | float m_frequency; 40 | EWaveForm m_waveForm; 41 | EEffectSpeed m_tremolo; 42 | EEffectSpeed m_vibrato; 43 | size_t m_age; 44 | bool m_dead; 45 | bool m_wantsKeyRelease; 46 | size_t m_releaseAge; 47 | float m_phase; 48 | }; 49 | 50 | std::vector g_notes; 51 | std::mutex g_notesMutex; 52 | EWaveForm g_currentWaveForm; 53 | EEffectSpeed g_tremolo; 54 | EEffectSpeed g_vibrato; 55 | 56 | //-------------------------------------------------------------------------------------------------- 57 | void OnInit() { } 58 | 59 | //-------------------------------------------------------------------------------------------------- 60 | void OnExit() { } 61 | 62 | //-------------------------------------------------------------------------------------------------- 63 | inline float GenerateEnvelope (SNote& note, float ageInSeconds, float sampleRate) { 64 | 65 | // this just puts a short envelope on the beginning and end of the note and kills the note 66 | // when the release envelope is done. 67 | 68 | float envelope = 0.0f; 69 | 70 | static const float c_envelopeTime = 0.1f; 71 | 72 | // if the key isn't yet released 73 | if (note.m_releaseAge == 0) { 74 | // release the key if it wants to be released and has done the intro envelope 75 | if (note.m_wantsKeyRelease && ageInSeconds > c_envelopeTime) { 76 | note.m_releaseAge = note.m_age; 77 | } 78 | // else do the intro envelope 79 | else { 80 | envelope = Envelope2Pt( 81 | ageInSeconds, 82 | 0.0f, 0.0f, 83 | c_envelopeTime, 1.0f 84 | ); 85 | } 86 | } 87 | 88 | // if the key has been released, apply the outro envelope 89 | if (note.m_releaseAge != 0) { 90 | 91 | float releaseAgeInSeconds = float(note.m_releaseAge) / sampleRate; 92 | 93 | float secondsInRelease = ageInSeconds - releaseAgeInSeconds; 94 | 95 | envelope = Envelope2Pt( 96 | secondsInRelease, 97 | 0.0f, 1.0f, 98 | c_envelopeTime, 0.0f 99 | ); 100 | 101 | // kill the note when the release is done 102 | if (secondsInRelease > c_envelopeTime) 103 | note.m_dead = true; 104 | } 105 | 106 | return envelope; 107 | } 108 | 109 | //-------------------------------------------------------------------------------------------------- 110 | inline float GetEffectFrequency (EEffectSpeed speed) { 111 | switch (speed) { 112 | case e_effectOff: return 0.0; 113 | case e_effectSlow: return 2.8f; 114 | case e_effectMedium: return 10.0f; 115 | case e_effectFast: return 20.0f; 116 | } 117 | 118 | return 0.0f; 119 | } 120 | 121 | //-------------------------------------------------------------------------------------------------- 122 | inline float GenerateNoteSample (SNote& note, float sampleRate) { 123 | 124 | // calculate our age in seconds and advance our age in samples, by 1 sample 125 | float ageInSeconds = float(note.m_age) / sampleRate; 126 | ++note.m_age; 127 | 128 | // generate the envelope value for our note 129 | // decrease note volume a bit, because the volume adjustments don't seem to be quite enough 130 | float envelope = GenerateEnvelope(note, ageInSeconds, sampleRate) * 0.8f; 131 | 132 | // adjust our envelope by applying tremolo. 133 | // the tremolo affects the amplitude by multiplying it between 0.5 and 1.0 in a sine wave. 134 | envelope *= SineWave(ageInSeconds * GetEffectFrequency(note.m_tremolo)) * 0.25f + 0.5f; 135 | 136 | // calculate our frequency by starting with the base note and applying vibrato. 137 | // our vibratto adds plus or minus 5% of the frequency, on a sine wave. 138 | float frequency = note.m_frequency; 139 | frequency += frequency * SineWave(ageInSeconds * GetEffectFrequency(note.m_vibrato)) * 0.05f; 140 | 141 | // advance phase, making sure to keep it between 0 and 1 142 | note.m_phase += frequency / sampleRate; 143 | note.m_phase = std::fmodf(note.m_phase, 1.0); 144 | 145 | // generate the audio sample value for the current phase. 146 | switch (note.m_waveForm) { 147 | case e_waveSine: return SineWave(note.m_phase) * envelope; 148 | case e_waveSaw: return SawWaveBandLimited(note.m_phase, 10) * envelope; 149 | case e_waveSquare: return SquareWaveBandLimited(note.m_phase, 10) * envelope; 150 | case e_waveTriangle:return TriangleWaveBandLimited(note.m_phase, 10) * envelope; 151 | } 152 | 153 | return 0.0f; 154 | } 155 | 156 | //-------------------------------------------------------------------------------------------------- 157 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 158 | 159 | // get a lock on our notes vector 160 | std::lock_guard guard(g_notesMutex); 161 | 162 | // for every sample in our output buffer 163 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 164 | 165 | // add up all notes to get the final value. 166 | float value = 0.0f; 167 | std::for_each( 168 | g_notes.begin(), 169 | g_notes.end(), 170 | [&value, sampleRate](SNote& note) { 171 | value += GenerateNoteSample(note, sampleRate); 172 | } 173 | ); 174 | 175 | // copy the value to all audio channels 176 | for (size_t channel = 0; channel < numChannels; ++channel) 177 | outputBuffer[channel] = value; 178 | } 179 | 180 | // remove notes that have died 181 | auto iter = std::remove_if( 182 | g_notes.begin(), 183 | g_notes.end(), 184 | [] (const SNote& note) { 185 | return note.m_dead; 186 | } 187 | ); 188 | 189 | if (iter != g_notes.end()) 190 | g_notes.erase(iter); 191 | } 192 | 193 | //-------------------------------------------------------------------------------------------------- 194 | void StopNote (float frequency) { 195 | 196 | // get a lock on our notes vector 197 | std::lock_guard guard(g_notesMutex); 198 | 199 | // Any note that is this frequency should note that it wants to enter released state. 200 | std::for_each( 201 | g_notes.begin(), 202 | g_notes.end(), 203 | [frequency] (SNote& note) { 204 | if (note.m_frequency == frequency) { 205 | note.m_wantsKeyRelease = true; 206 | } 207 | } 208 | ); 209 | } 210 | 211 | //-------------------------------------------------------------------------------------------------- 212 | void OnKey (char key, bool pressed) { 213 | 214 | // pressing numbers switches instruments, or adjusts effects 215 | if (pressed) { 216 | switch (key) 217 | { 218 | case '1': g_currentWaveForm = e_waveSine; printf("instrument: sine\r\n"); return; 219 | case '2': g_currentWaveForm = e_waveSaw; printf("instrument: bl saw\r\n"); return; 220 | case '3': g_currentWaveForm = e_waveSquare; printf("instrument: bl square\r\n"); return; 221 | case '4': g_currentWaveForm = e_waveTriangle; printf("instrument: bl triangle\r\n"); return; 222 | case '5': g_tremolo = (EEffectSpeed)(((int)g_tremolo + 1) % (e_effectFast+1)); printf("tremolo = %i (%0.1f hz)\r\n", g_tremolo, GetEffectFrequency(g_tremolo)); return; 223 | case '6': g_vibrato = (EEffectSpeed)(((int)g_vibrato + 1) % (e_effectFast + 1)); printf("vibrato = %i (%0.1f hz)\r\n", g_vibrato, GetEffectFrequency(g_vibrato)); return; 224 | } 225 | } 226 | 227 | // figure out what frequency to play 228 | float frequency = 0.0f; 229 | switch (key) { 230 | 231 | // QWERTY row 232 | case 'Q': frequency = NoteToFrequency(3, 0); break; 233 | case 'W': frequency = NoteToFrequency(3, 1); break; 234 | case 'E': frequency = NoteToFrequency(3, 2); break; 235 | case 'R': frequency = NoteToFrequency(3, 3); break; 236 | case 'T': frequency = NoteToFrequency(3, 4); break; 237 | case 'Y': frequency = NoteToFrequency(3, 5); break; 238 | case 'U': frequency = NoteToFrequency(3, 6); break; 239 | case 'I': frequency = NoteToFrequency(3, 7); break; 240 | case 'O': frequency = NoteToFrequency(3, 8); break; 241 | case 'P': frequency = NoteToFrequency(3, 9); break; 242 | case -37: frequency = NoteToFrequency(3, 10); break; 243 | 244 | // ASDF row 245 | case 'A': frequency = NoteToFrequency(2, 0); break; 246 | case 'S': frequency = NoteToFrequency(2, 1); break; 247 | case 'D': frequency = NoteToFrequency(2, 2); break; 248 | case 'F': frequency = NoteToFrequency(2, 3); break; 249 | case 'G': frequency = NoteToFrequency(2, 4); break; 250 | case 'H': frequency = NoteToFrequency(2, 5); break; 251 | case 'J': frequency = NoteToFrequency(2, 6); break; 252 | case 'K': frequency = NoteToFrequency(2, 7); break; 253 | case 'L': frequency = NoteToFrequency(2, 8); break; 254 | case -70: frequency = NoteToFrequency(2, 9); break; 255 | case -34: frequency = NoteToFrequency(2, 10); break; 256 | 257 | // ZXCV row 258 | case 'Z': frequency = NoteToFrequency(1, 0); break; 259 | case 'X': frequency = NoteToFrequency(1, 1); break; 260 | case 'C': frequency = NoteToFrequency(1, 2); break; 261 | case 'V': frequency = NoteToFrequency(1, 3); break; 262 | case 'B': frequency = NoteToFrequency(1, 4); break; 263 | case 'N': frequency = NoteToFrequency(1, 5); break; 264 | case 'M': frequency = NoteToFrequency(1, 6); break; 265 | case -68: frequency = NoteToFrequency(1, 7); break; 266 | case -66: frequency = NoteToFrequency(1, 8); break; 267 | case -65: frequency = NoteToFrequency(1, 9); break; 268 | case -95: frequency = NoteToFrequency(1, 10); break; // right shift 269 | 270 | // left shift = low freq 271 | case 16: frequency = NoteToFrequency(0, 5); break; 272 | 273 | // left shift = low freq 274 | case -94: frequency = NoteToFrequency(0, 0); break; 275 | 276 | default: { 277 | return; 278 | } 279 | } 280 | 281 | 282 | // if releasing a note, we need to find and kill the flute note of the same frequency 283 | if (!pressed) { 284 | StopNote(frequency); 285 | return; 286 | } 287 | 288 | // get a lock on our notes vector and add the new note 289 | std::lock_guard guard(g_notesMutex); 290 | g_notes.push_back(SNote(frequency, g_currentWaveForm, g_tremolo, g_vibrato)); 291 | } 292 | 293 | //-------------------------------------------------------------------------------------------------- 294 | void OnEnterDemo () { 295 | g_currentWaveForm = e_waveSine; 296 | g_tremolo = e_effectOff; 297 | g_vibrato = e_effectOff; 298 | printf("Letter keys to play notes.\r\nleft shift / control is super low frequency.\r\n"); 299 | printf("1 = Sine\r\n"); 300 | printf("2 = Band Limited Saw\r\n"); 301 | printf("3 = Band Limited Square\r\n"); 302 | printf("4 = Band Limited Triangle\r\n"); 303 | printf("5 = Cycle Tremolo\r\n"); 304 | printf("6 = Cycle Vibrato\r\n"); 305 | printf("\r\nInstructions:\r\n"); 306 | printf("play notes with different settings so people can hear it.\r\n"); 307 | 308 | // clear all the notes out 309 | std::lock_guard guard(g_notesMutex); 310 | g_notes.clear(); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /MusicSynth/DemoWaveForms.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // DemoWaveForms.cpp 3 | // 4 | // Logic for the demo of the same name 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "DemoMgr.h" 9 | #include 10 | 11 | namespace DemoWaveForms { 12 | 13 | enum EWaveForm { 14 | e_waveSine, 15 | e_waveSaw, 16 | e_waveSquare, 17 | e_waveTriangle 18 | }; 19 | 20 | const char* WaveFormToString (EWaveForm waveForm) { 21 | switch (waveForm) { 22 | case e_waveSine: return "Sine"; 23 | case e_waveSaw: return "Saw"; 24 | case e_waveSquare: return "Square"; 25 | case e_waveTriangle: return "Triangle"; 26 | } 27 | return "???"; 28 | } 29 | 30 | struct SNote { 31 | SNote(float frequency, EWaveForm waveForm) 32 | : m_frequency(frequency) 33 | , m_waveForm(waveForm) 34 | , m_age(0) 35 | , m_dead(false) 36 | , m_wantsKeyRelease(false) 37 | , m_releaseAge(0) {} 38 | 39 | float m_frequency; 40 | EWaveForm m_waveForm; 41 | size_t m_age; 42 | bool m_dead; 43 | bool m_wantsKeyRelease; 44 | size_t m_releaseAge; 45 | }; 46 | 47 | std::vector g_notes; 48 | std::mutex g_notesMutex; 49 | EWaveForm g_currentWaveForm; 50 | 51 | //-------------------------------------------------------------------------------------------------- 52 | void OnInit() { } 53 | 54 | //-------------------------------------------------------------------------------------------------- 55 | void OnExit() { } 56 | 57 | //-------------------------------------------------------------------------------------------------- 58 | inline float GenerateEnvelope (SNote& note, float ageInSeconds, float sampleRate) { 59 | 60 | // this just puts a short envelope on the beginning and end of the note and kills the note 61 | // when the release envelope is done. 62 | 63 | float envelope = 0.0f; 64 | 65 | static const float c_envelopeTime = 0.1f; 66 | 67 | // if the key isn't yet released 68 | if (note.m_releaseAge == 0) { 69 | // release the key if it wants to be released and has done the intro envelope 70 | if (note.m_wantsKeyRelease && ageInSeconds > c_envelopeTime) { 71 | note.m_releaseAge = note.m_age; 72 | } 73 | // else do the intro envelope 74 | else { 75 | envelope = Envelope2Pt( 76 | ageInSeconds, 77 | 0.0f, 0.0f, 78 | c_envelopeTime, 1.0f 79 | ); 80 | } 81 | } 82 | 83 | // if the key has been released, apply the outro envelope 84 | if (note.m_releaseAge != 0) { 85 | 86 | float releaseAgeInSeconds = float(note.m_releaseAge) / sampleRate; 87 | 88 | float secondsInRelease = ageInSeconds - releaseAgeInSeconds; 89 | 90 | envelope = Envelope2Pt( 91 | secondsInRelease, 92 | 0.0f, 1.0f, 93 | c_envelopeTime, 0.0f 94 | ); 95 | 96 | // kill the note when the release is done 97 | if (secondsInRelease > c_envelopeTime) 98 | note.m_dead = true; 99 | } 100 | 101 | return envelope; 102 | } 103 | 104 | //-------------------------------------------------------------------------------------------------- 105 | inline float GenerateNoteSample (SNote& note, float sampleRate) { 106 | 107 | // calculate our age in seconds and advance our age in samples, by 1 sample 108 | float ageInSeconds = float(note.m_age) / sampleRate; 109 | ++note.m_age; 110 | 111 | // generate the envelope value for our note 112 | float envelope = GenerateEnvelope(note, ageInSeconds, sampleRate); 113 | 114 | // generate the audio sample value for the current time. 115 | // Note that it is ok that we are basing audio samples on age instead of phase, because the 116 | // frequency never changes and we envelope the front and back to avoid popping. 117 | float phase = std::fmodf(ageInSeconds * note.m_frequency, 1.0f); 118 | switch (note.m_waveForm) { 119 | case e_waveSine: return SineWave(phase) * envelope; 120 | case e_waveSaw: return SawWave(phase) * envelope; 121 | case e_waveSquare: return SquareWave(phase) * envelope; 122 | case e_waveTriangle:return TriangleWave(phase) * envelope; 123 | } 124 | 125 | return 0.0f; 126 | } 127 | 128 | //-------------------------------------------------------------------------------------------------- 129 | void GenerateAudioSamples (float *outputBuffer, size_t framesPerBuffer, size_t numChannels, float sampleRate) { 130 | 131 | // get a lock on our notes vector 132 | std::lock_guard guard(g_notesMutex); 133 | 134 | // for every sample in our output buffer 135 | for (size_t sample = 0; sample < framesPerBuffer; ++sample, outputBuffer += numChannels) { 136 | 137 | // add up all notes to get the final value. 138 | float value = 0.0f; 139 | std::for_each( 140 | g_notes.begin(), 141 | g_notes.end(), 142 | [&value, sampleRate](SNote& note) { 143 | value += GenerateNoteSample(note, sampleRate); 144 | } 145 | ); 146 | 147 | // copy the value to all audio channels 148 | for (size_t channel = 0; channel < numChannels; ++channel) 149 | outputBuffer[channel] = value; 150 | } 151 | 152 | // remove notes that have died 153 | auto iter = std::remove_if( 154 | g_notes.begin(), 155 | g_notes.end(), 156 | [] (const SNote& note) { 157 | return note.m_dead; 158 | } 159 | ); 160 | 161 | if (iter != g_notes.end()) 162 | g_notes.erase(iter); 163 | } 164 | 165 | //-------------------------------------------------------------------------------------------------- 166 | void StopNote (float frequency) { 167 | 168 | // get a lock on our notes vector 169 | std::lock_guard guard(g_notesMutex); 170 | 171 | // Any note that is this frequency should note that it wants to enter released state. 172 | std::for_each( 173 | g_notes.begin(), 174 | g_notes.end(), 175 | [frequency] (SNote& note) { 176 | if (note.m_frequency == frequency) { 177 | note.m_wantsKeyRelease = true; 178 | } 179 | } 180 | ); 181 | } 182 | 183 | //-------------------------------------------------------------------------------------------------- 184 | void ReportParams() { 185 | printf("Instrument: %s\r\n", WaveFormToString(g_currentWaveForm)); 186 | } 187 | 188 | //-------------------------------------------------------------------------------------------------- 189 | void OnKey (char key, bool pressed) { 190 | 191 | // pressing numbers switches instruments 192 | if (pressed) { 193 | switch (key) 194 | { 195 | case '1': g_currentWaveForm = e_waveSine; ReportParams(); return; 196 | case '2': g_currentWaveForm = e_waveSaw; ReportParams(); return; 197 | case '3': g_currentWaveForm = e_waveSquare; ReportParams(); return; 198 | case '4': g_currentWaveForm = e_waveTriangle; ReportParams(); return; 199 | } 200 | } 201 | 202 | // figure out what frequency to play 203 | float frequency = 0.0f; 204 | switch (key) { 205 | 206 | // QWERTY row 207 | case 'Q': frequency = NoteToFrequency(3, 0); break; 208 | case 'W': frequency = NoteToFrequency(3, 1); break; 209 | case 'E': frequency = NoteToFrequency(3, 2); break; 210 | case 'R': frequency = NoteToFrequency(3, 3); break; 211 | case 'T': frequency = NoteToFrequency(3, 4); break; 212 | case 'Y': frequency = NoteToFrequency(3, 5); break; 213 | case 'U': frequency = NoteToFrequency(3, 6); break; 214 | case 'I': frequency = NoteToFrequency(3, 7); break; 215 | case 'O': frequency = NoteToFrequency(3, 8); break; 216 | case 'P': frequency = NoteToFrequency(3, 9); break; 217 | case -37: frequency = NoteToFrequency(3, 10); break; 218 | 219 | // ASDF row 220 | case 'A': frequency = NoteToFrequency(2, 0); break; 221 | case 'S': frequency = NoteToFrequency(2, 1); break; 222 | case 'D': frequency = NoteToFrequency(2, 2); break; 223 | case 'F': frequency = NoteToFrequency(2, 3); break; 224 | case 'G': frequency = NoteToFrequency(2, 4); break; 225 | case 'H': frequency = NoteToFrequency(2, 5); break; 226 | case 'J': frequency = NoteToFrequency(2, 6); break; 227 | case 'K': frequency = NoteToFrequency(2, 7); break; 228 | case 'L': frequency = NoteToFrequency(2, 8); break; 229 | case -70: frequency = NoteToFrequency(2, 9); break; 230 | case -34: frequency = NoteToFrequency(2, 10); break; 231 | 232 | // ZXCV row 233 | case 'Z': frequency = NoteToFrequency(1, 0); break; 234 | case 'X': frequency = NoteToFrequency(1, 1); break; 235 | case 'C': frequency = NoteToFrequency(1, 2); break; 236 | case 'V': frequency = NoteToFrequency(1, 3); break; 237 | case 'B': frequency = NoteToFrequency(1, 4); break; 238 | case 'N': frequency = NoteToFrequency(1, 5); break; 239 | case 'M': frequency = NoteToFrequency(1, 6); break; 240 | case -68: frequency = NoteToFrequency(1, 7); break; 241 | case -66: frequency = NoteToFrequency(1, 8); break; 242 | case -65: frequency = NoteToFrequency(1, 9); break; 243 | case -95: frequency = NoteToFrequency(1, 10); break; // right shift 244 | 245 | // left shift = low freq 246 | case 16: frequency = NoteToFrequency(0, 5); break; 247 | 248 | // left shift = low freq 249 | case -94: frequency = NoteToFrequency(0, 0); break; 250 | 251 | default: { 252 | return; 253 | } 254 | } 255 | 256 | 257 | // if releasing a note, we need to find and kill the flute note of the same frequency 258 | if (!pressed) { 259 | StopNote(frequency); 260 | return; 261 | } 262 | 263 | // get a lock on our notes vector and add the new note 264 | std::lock_guard guard(g_notesMutex); 265 | g_notes.push_back(SNote(frequency, g_currentWaveForm)); 266 | } 267 | 268 | //-------------------------------------------------------------------------------------------------- 269 | void OnEnterDemo () { 270 | g_currentWaveForm = e_waveSine; 271 | printf("Letter keys to play notes.\r\nleft shift / control is super low frequency.\r\n"); 272 | printf("1 = Sine\r\n"); 273 | printf("2 = Saw\r\n"); 274 | printf("3 = Square\r\n"); 275 | printf("4 = Triangle\r\n"); 276 | printf("\r\nInstructions:\r\n"); 277 | printf("Play diff instruments. Mention harsh sounds.\r\n"); 278 | 279 | // clear all the notes out 280 | std::lock_guard guard(g_notesMutex); 281 | g_notes.clear(); 282 | } 283 | } -------------------------------------------------------------------------------- /MusicSynth/Main.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // Main.cpp 3 | // 4 | // This contains all the portaudio setup stuff. Nothing of major importance or interest. 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include 9 | #include "PortAudio/include/portaudio.h" 10 | #include "DemoMgr.h" 11 | #include 12 | #include // for getting key states 13 | 14 | // audio data needed by the audio sample generator 15 | float g_sampleRate = 0.0f; 16 | static const size_t g_numChannels = 2; 17 | 18 | //-------------------------------------------------------------------------------------------------- 19 | struct SKeyState { 20 | SHORT m_keys[256]; 21 | }; 22 | 23 | //-------------------------------------------------------------------------------------------------- 24 | static int GenerateAudioSamples ( 25 | const void *inputBuffer, 26 | void *outputBuffer, 27 | unsigned long framesPerBuffer, 28 | const PaStreamCallbackTimeInfo* timeInfo, 29 | PaStreamCallbackFlags statusFlags, 30 | void *userData 31 | ) { 32 | CDemoMgr::GenerateAudioSamples((float*)outputBuffer, framesPerBuffer, g_numChannels, g_sampleRate); 33 | return paContinue; 34 | } 35 | 36 | //-------------------------------------------------------------------------------------------------- 37 | void GatherKeyStates (SKeyState& state) { 38 | //GetKeyboardState(state.m_keys); 39 | for (size_t i = 0; i < 256; ++i) { 40 | state.m_keys[i] = GetAsyncKeyState(i); 41 | } 42 | } 43 | 44 | //-------------------------------------------------------------------------------------------------- 45 | void GenerateKeyEvents (SKeyState& oldState, SKeyState& newState) { 46 | for (size_t i = 0; i < 256; ++i) { 47 | if ((oldState.m_keys[i] != 0) != (newState.m_keys[i] != 0)) { 48 | CDemoMgr::OnKey(char(i), newState.m_keys[i] != 0); 49 | } 50 | } 51 | } 52 | 53 | //-------------------------------------------------------------------------------------------------- 54 | int main (int argc, char **argv) 55 | { 56 | // initialize port audio 57 | PaError err = Pa_Initialize(); 58 | if (err != paNoError) 59 | { 60 | printf("Pa_Initialize returned error: %i\n", err); 61 | return err; 62 | } 63 | 64 | // figure out what api index WASAPI is 65 | PaHostApiIndex hostApiIndex = Pa_HostApiTypeIdToHostApiIndex(paWASAPI); 66 | if (hostApiIndex < 0) 67 | { 68 | printf("Pa_HostApiTypeIdToHostApiIndex returned error: %i\n", err); 69 | return err; 70 | } 71 | 72 | // get information about the WASAPI host api 73 | const PaHostApiInfo *hostApiInfo = Pa_GetHostApiInfo(hostApiIndex); 74 | if (hostApiInfo == nullptr) 75 | { 76 | printf("Pa_GetHostApiInfo returned nullptr\n"); 77 | return -1; 78 | } 79 | 80 | // make sure there's an output device 81 | if (hostApiInfo->defaultOutputDevice == paNoDevice) { 82 | fprintf(stderr, "No default output device\n"); 83 | return -1; 84 | } 85 | 86 | // get device info if we can 87 | const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(hostApiInfo->defaultOutputDevice); 88 | if (hostApiInfo == nullptr) 89 | { 90 | printf("Pa_GetDeviceInfo returned nullptr\n"); 91 | return -1; 92 | } 93 | 94 | // open a stereo output stream 95 | PaStreamParameters outputParameters; 96 | outputParameters.device = hostApiInfo->defaultOutputDevice; 97 | outputParameters.channelCount = g_numChannels; 98 | outputParameters.sampleFormat = paFloat32; 99 | outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency; 100 | outputParameters.hostApiSpecificStreamInfo = nullptr; 101 | PaStream* stream = nullptr; 102 | err = Pa_OpenStream( 103 | &stream, 104 | nullptr, 105 | &outputParameters, 106 | deviceInfo->defaultSampleRate, 107 | 0, 108 | 0, 109 | GenerateAudioSamples, 110 | nullptr 111 | ); 112 | if (err != paNoError) { 113 | printf("Pa_OpenStream returned error: %i\n", err); 114 | return err; 115 | } 116 | 117 | // get the stream info for the stream we created 118 | const PaStreamInfo* streamInfo = Pa_GetStreamInfo(stream); 119 | if (streamInfo == nullptr) 120 | { 121 | printf("Pa_GetStreamInfo returned nullptr\n"); 122 | return -1; 123 | } 124 | g_sampleRate = (float)streamInfo->sampleRate; 125 | 126 | // start the stream 127 | err = Pa_StartStream(stream); 128 | if (err != paNoError) { 129 | printf("Pa_StartStream returned error: %i\n", err); 130 | return err; 131 | } 132 | 133 | // loop of sending key events to demo manager, until it wants to exit. 134 | // also give the demo manager an update 135 | CDemoMgr::Init(g_sampleRate, g_numChannels); 136 | SKeyState keyState1; 137 | SKeyState keyState2; 138 | SKeyState* oldKeyState = &keyState1; 139 | SKeyState* newKeyState = &keyState2; 140 | GatherKeyStates(*oldKeyState); 141 | while (!CDemoMgr::WantsExit()) { 142 | GatherKeyStates(*newKeyState); 143 | GenerateKeyEvents(*oldKeyState, *newKeyState); 144 | std::swap(oldKeyState, newKeyState); 145 | CDemoMgr::Update(); 146 | Sleep(0); 147 | } 148 | 149 | // stop the stream 150 | err = Pa_StopStream(stream); 151 | if (err != paNoError) { 152 | printf("Pa_StopStream returned error: %i\n", err); 153 | return err; 154 | } 155 | 156 | // close the stream 157 | err = Pa_CloseStream(stream); 158 | if (err != paNoError) { 159 | printf("Pa_CloseStream returned error: %i\n", err); 160 | return err; 161 | } 162 | 163 | // terminate port audio and return success 164 | Pa_Terminate(); 165 | return 0; 166 | } -------------------------------------------------------------------------------- /MusicSynth/MusicSynth.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/MusicSynth.exe -------------------------------------------------------------------------------- /MusicSynth/MusicSynth.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30723.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MusicSynth", "MusicSynth.vcxproj", "{A853E033-5BDC-406A-8BD2-51F5224455EF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A853E033-5BDC-406A-8BD2-51F5224455EF}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {A853E033-5BDC-406A-8BD2-51F5224455EF}.Debug|Win32.Build.0 = Debug|Win32 16 | {A853E033-5BDC-406A-8BD2-51F5224455EF}.Release|Win32.ActiveCfg = Release|Win32 17 | {A853E033-5BDC-406A-8BD2-51F5224455EF}.Release|Win32.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /MusicSynth/MusicSynth.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {A853E033-5BDC-406A-8BD2-51F5224455EF} 15 | MusicSynth 16 | 17 | 18 | 19 | Application 20 | true 21 | v120 22 | MultiByte 23 | 24 | 25 | Application 26 | false 27 | v120 28 | true 29 | MultiByte 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Level3 45 | Disabled 46 | true 47 | 48 | 49 | true 50 | portaudio.lib;%(AdditionalDependencies) 51 | $(SolutionDir)PortAudio\Bin\;%(AdditionalLibraryDirectories) 52 | 53 | 54 | 55 | 56 | Level3 57 | MaxSpeed 58 | true 59 | true 60 | true 61 | 62 | 63 | true 64 | true 65 | true 66 | portaudio.lib;%(AdditionalDependencies) 67 | $(SolutionDir)PortAudio\Bin\;%(AdditionalLibraryDirectories) 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /MusicSynth/MusicSynth.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {f0cb92a7-35f2-4553-96b3-8a4fa3198614} 18 | 19 | 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files\Demos 32 | 33 | 34 | Source Files\Demos 35 | 36 | 37 | Source Files\Demos 38 | 39 | 40 | Source Files\Demos 41 | 42 | 43 | Source Files\Demos 44 | 45 | 46 | Source Files\Demos 47 | 48 | 49 | Source Files\Demos 50 | 51 | 52 | Source Files\Demos 53 | 54 | 55 | Source Files\Demos 56 | 57 | 58 | Source Files\Demos 59 | 60 | 61 | Source Files\Demos 62 | 63 | 64 | Source Files\Demos 65 | 66 | 67 | Source Files\Demos 68 | 69 | 70 | Source Files\Demos 71 | 72 | 73 | Source Files\Demos 74 | 75 | 76 | Source Files\Demos 77 | 78 | 79 | Source Files 80 | 81 | 82 | Source Files\Demos 83 | 84 | 85 | 86 | 87 | Source Files 88 | 89 | 90 | Source Files 91 | 92 | 93 | Source Files 94 | 95 | 96 | Source Files 97 | 98 | 99 | Source Files 100 | 101 | 102 | Source Files 103 | 104 | 105 | Source Files 106 | 107 | 108 | -------------------------------------------------------------------------------- /MusicSynth/PortAudio/Bin/portaudio.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/PortAudio/Bin/portaudio.lib -------------------------------------------------------------------------------- /MusicSynth/PortAudio/include/pa_asio.h: -------------------------------------------------------------------------------- 1 | #ifndef PA_ASIO_H 2 | #define PA_ASIO_H 3 | /* 4 | * $Id: pa_asio.h 1667 2011-05-02 15:49:20Z rossb $ 5 | * PortAudio Portable Real-Time Audio Library 6 | * ASIO specific extensions 7 | * 8 | * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining 11 | * a copy of this software and associated documentation files 12 | * (the "Software"), to deal in the Software without restriction, 13 | * including without limitation the rights to use, copy, modify, merge, 14 | * publish, distribute, sublicense, and/or sell copies of the Software, 15 | * and to permit persons to whom the Software is furnished to do so, 16 | * subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 25 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 26 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | /* 31 | * The text above constitutes the entire PortAudio license; however, 32 | * the PortAudio community also makes the following non-binding requests: 33 | * 34 | * Any person wishing to distribute modifications to the Software is 35 | * requested to send the modifications to the original developer so that 36 | * they can be incorporated into the canonical version. It is also 37 | * requested that these non-binding requests be included along with the 38 | * license above. 39 | */ 40 | 41 | 42 | /** @file 43 | @ingroup public_header 44 | @brief ASIO-specific PortAudio API extension header file. 45 | */ 46 | 47 | #include "portaudio.h" 48 | 49 | #if PA_USE_ASIO == 1 50 | 51 | #ifdef __cplusplus 52 | extern "C" 53 | { 54 | #endif /* __cplusplus */ 55 | 56 | 57 | /** Retrieve legal native buffer sizes for the specificed device, in sample frames. 58 | 59 | @param device The global index of the device about which the query is being made. 60 | @param minBufferSizeFrames A pointer to the location which will receive the minimum buffer size value. 61 | @param maxBufferSizeFrames A pointer to the location which will receive the maximum buffer size value. 62 | @param preferredBufferSizeFrames A pointer to the location which will receive the preferred buffer size value. 63 | @param granularity A pointer to the location which will receive the "granularity". This value determines 64 | the step size used to compute the legal values between minBufferSizeFrames and maxBufferSizeFrames. 65 | If granularity is -1 then available buffer size values are powers of two. 66 | 67 | @see ASIOGetBufferSize in the ASIO SDK. 68 | 69 | @note: this function used to be called PaAsio_GetAvailableLatencyValues. There is a 70 | #define that maps PaAsio_GetAvailableLatencyValues to this function for backwards compatibility. 71 | */ 72 | PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device, 73 | long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity ); 74 | 75 | 76 | /** Backwards compatibility alias for PaAsio_GetAvailableBufferSizes 77 | 78 | @see PaAsio_GetAvailableBufferSizes 79 | */ 80 | #define PaAsio_GetAvailableLatencyValues PaAsio_GetAvailableBufferSizes 81 | 82 | 83 | /** Display the ASIO control panel for the specified device. 84 | 85 | @param device The global index of the device whose control panel is to be displayed. 86 | @param systemSpecific On Windows, the calling application's main window handle, 87 | on Macintosh this value should be zero. 88 | */ 89 | PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific ); 90 | 91 | 92 | 93 | 94 | /** Retrieve a pointer to a string containing the name of the specified 95 | input channel. The string is valid until Pa_Terminate is called. 96 | 97 | The string will be no longer than 32 characters including the null terminator. 98 | */ 99 | PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex, 100 | const char** channelName ); 101 | 102 | 103 | /** Retrieve a pointer to a string containing the name of the specified 104 | input channel. The string is valid until Pa_Terminate is called. 105 | 106 | The string will be no longer than 32 characters including the null terminator. 107 | */ 108 | PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex, 109 | const char** channelName ); 110 | 111 | 112 | /** Set the sample rate of an open paASIO stream. 113 | 114 | @param stream The stream to operate on. 115 | @param sampleRate The new sample rate. 116 | 117 | Note that this function may fail if the stream is alredy running and the 118 | ASIO driver does not support switching the sample rate of a running stream. 119 | 120 | Returns paIncompatibleStreamHostApi if stream is not a paASIO stream. 121 | */ 122 | PaError PaAsio_SetStreamSampleRate( PaStream* stream, double sampleRate ); 123 | 124 | 125 | #define paAsioUseChannelSelectors (0x01) 126 | 127 | typedef struct PaAsioStreamInfo{ 128 | unsigned long size; /**< sizeof(PaAsioStreamInfo) */ 129 | PaHostApiTypeId hostApiType; /**< paASIO */ 130 | unsigned long version; /**< 1 */ 131 | 132 | unsigned long flags; 133 | 134 | /* Support for opening only specific channels of an ASIO device. 135 | If the paAsioUseChannelSelectors flag is set, channelSelectors is a 136 | pointer to an array of integers specifying the device channels to use. 137 | When used, the length of the channelSelectors array must match the 138 | corresponding channelCount parameter to Pa_OpenStream() otherwise a 139 | crash may result. 140 | The values in the selectors array must specify channels within the 141 | range of supported channels for the device or paInvalidChannelCount will 142 | result. 143 | */ 144 | int *channelSelectors; 145 | }PaAsioStreamInfo; 146 | 147 | 148 | #ifdef __cplusplus 149 | } 150 | #endif /* __cplusplus */ 151 | 152 | #endif // PA_USE_ASIO == 1 153 | 154 | #endif /* PA_ASIO_H */ -------------------------------------------------------------------------------- /MusicSynth/PortAudio/include/pa_jack.h: -------------------------------------------------------------------------------- 1 | #ifndef PA_JACK_H 2 | #define PA_JACK_H 3 | 4 | /* 5 | * $Id: 6 | * PortAudio Portable Real-Time Audio Library 7 | * JACK-specific extensions 8 | * 9 | * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining 12 | * a copy of this software and associated documentation files 13 | * (the "Software"), to deal in the Software without restriction, 14 | * including without limitation the rights to use, copy, modify, merge, 15 | * publish, distribute, sublicense, and/or sell copies of the Software, 16 | * and to permit persons to whom the Software is furnished to do so, 17 | * subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be 20 | * included in all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 26 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | /* 32 | * The text above constitutes the entire PortAudio license; however, 33 | * the PortAudio community also makes the following non-binding requests: 34 | * 35 | * Any person wishing to distribute modifications to the Software is 36 | * requested to send the modifications to the original developer so that 37 | * they can be incorporated into the canonical version. It is also 38 | * requested that these non-binding requests be included along with the 39 | * license above. 40 | */ 41 | 42 | /** @file 43 | * @ingroup public_header 44 | * @brief JACK-specific PortAudio API extension header file. 45 | */ 46 | 47 | #include "portaudio.h" 48 | 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | 53 | /** Set the JACK client name. 54 | * 55 | * During Pa_Initialize, When PA JACK connects as a client of the JACK server, it requests a certain 56 | * name, which is for instance prepended to port names. By default this name is "PortAudio". The 57 | * JACK server may append a suffix to the client name, in order to avoid clashes among clients that 58 | * try to connect with the same name (e.g., different PA JACK clients). 59 | * 60 | * This function must be called before Pa_Initialize, otherwise it won't have any effect. Note that 61 | * the string is not copied, but instead referenced directly, so it must not be freed for as long as 62 | * PA might need it. 63 | * @sa PaJack_GetClientName 64 | */ 65 | PaError PaJack_SetClientName( const char* name ); 66 | 67 | /** Get the JACK client name used by PA JACK. 68 | * 69 | * The caller is responsible for freeing the returned pointer. 70 | */ 71 | PaError PaJack_GetClientName(const char** clientName); 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /MusicSynth/PortAudio/include/pa_linux_alsa.h: -------------------------------------------------------------------------------- 1 | #ifndef PA_LINUX_ALSA_H 2 | #define PA_LINUX_ALSA_H 3 | 4 | /* 5 | * $Id: pa_linux_alsa.h 1597 2011-02-11 00:15:51Z dmitrykos $ 6 | * PortAudio Portable Real-Time Audio Library 7 | * ALSA-specific extensions 8 | * 9 | * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining 12 | * a copy of this software and associated documentation files 13 | * (the "Software"), to deal in the Software without restriction, 14 | * including without limitation the rights to use, copy, modify, merge, 15 | * publish, distribute, sublicense, and/or sell copies of the Software, 16 | * and to permit persons to whom the Software is furnished to do so, 17 | * subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be 20 | * included in all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 26 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | /* 32 | * The text above constitutes the entire PortAudio license; however, 33 | * the PortAudio community also makes the following non-binding requests: 34 | * 35 | * Any person wishing to distribute modifications to the Software is 36 | * requested to send the modifications to the original developer so that 37 | * they can be incorporated into the canonical version. It is also 38 | * requested that these non-binding requests be included along with the 39 | * license above. 40 | */ 41 | 42 | /** @file 43 | * @ingroup public_header 44 | * @brief ALSA-specific PortAudio API extension header file. 45 | */ 46 | 47 | #include "portaudio.h" 48 | 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | 53 | typedef struct PaAlsaStreamInfo 54 | { 55 | unsigned long size; 56 | PaHostApiTypeId hostApiType; 57 | unsigned long version; 58 | 59 | const char *deviceString; 60 | } 61 | PaAlsaStreamInfo; 62 | 63 | /** Initialize host API specific structure, call this before setting relevant attributes. */ 64 | void PaAlsa_InitializeStreamInfo( PaAlsaStreamInfo *info ); 65 | 66 | /** Instruct whether to enable real-time priority when starting the audio thread. 67 | * 68 | * If this is turned on by the stream is started, the audio callback thread will be created 69 | * with the FIFO scheduling policy, which is suitable for realtime operation. 70 | **/ 71 | void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable ); 72 | 73 | #if 0 74 | void PaAlsa_EnableWatchdog( PaStream *s, int enable ); 75 | #endif 76 | 77 | /** Get the ALSA-lib card index of this stream's input device. */ 78 | PaError PaAlsa_GetStreamInputCard( PaStream *s, int *card ); 79 | 80 | /** Get the ALSA-lib card index of this stream's output device. */ 81 | PaError PaAlsa_GetStreamOutputCard( PaStream *s, int *card ); 82 | 83 | /** Set the number of periods (buffer fragments) to configure devices with. 84 | * 85 | * By default the number of periods is 4, this is the lowest number of periods that works well on 86 | * the author's soundcard. 87 | * @param numPeriods The number of periods. 88 | */ 89 | PaError PaAlsa_SetNumPeriods( int numPeriods ); 90 | 91 | /** Set the maximum number of times to retry opening busy device (sleeping for a 92 | * short interval inbetween). 93 | */ 94 | PaError PaAlsa_SetRetriesBusy( int retries ); 95 | 96 | /** Set the path and name of ALSA library file if PortAudio is configured to load it dynamically (see 97 | * PA_ALSA_DYNAMIC). This setting will overwrite the default name set by PA_ALSA_PATHNAME define. 98 | * @param pathName Full path with filename. Only filename can be used, but dlopen() will lookup default 99 | * searchable directories (/usr/lib;/usr/local/lib) then. 100 | */ 101 | void PaAlsa_SetLibraryPathName( const char *pathName ); 102 | 103 | #ifdef __cplusplus 104 | } 105 | #endif 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /MusicSynth/PortAudio/include/pa_mac_core.h: -------------------------------------------------------------------------------- 1 | #ifndef PA_MAC_CORE_H 2 | #define PA_MAC_CORE_H 3 | /* 4 | * PortAudio Portable Real-Time Audio Library 5 | * Macintosh Core Audio specific extensions 6 | * portaudio.h should be included before this file. 7 | * 8 | * Copyright (c) 2005-2006 Bjorn Roche 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining 11 | * a copy of this software and associated documentation files 12 | * (the "Software"), to deal in the Software without restriction, 13 | * including without limitation the rights to use, copy, modify, merge, 14 | * publish, distribute, sublicense, and/or sell copies of the Software, 15 | * and to permit persons to whom the Software is furnished to do so, 16 | * subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 25 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 26 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | /* 31 | * The text above constitutes the entire PortAudio license; however, 32 | * the PortAudio community also makes the following non-binding requests: 33 | * 34 | * Any person wishing to distribute modifications to the Software is 35 | * requested to send the modifications to the original developer so that 36 | * they can be incorporated into the canonical version. It is also 37 | * requested that these non-binding requests be included along with the 38 | * license above. 39 | */ 40 | 41 | /** @file 42 | * @ingroup public_header 43 | * @brief CoreAudio-specific PortAudio API extension header file. 44 | */ 45 | 46 | #include "portaudio.h" 47 | 48 | #include 49 | #include 50 | 51 | #ifdef __cplusplus 52 | extern "C" { 53 | #endif 54 | 55 | 56 | /** 57 | * A pointer to a paMacCoreStreamInfo may be passed as 58 | * the hostApiSpecificStreamInfo in the PaStreamParameters struct 59 | * when opening a stream or querying the format. Use NULL, for the 60 | * defaults. Note that for duplex streams, flags for input and output 61 | * should be the same or behaviour is undefined. 62 | */ 63 | typedef struct 64 | { 65 | unsigned long size; /**size of whole structure including this header */ 66 | PaHostApiTypeId hostApiType; /**host API for which this data is intended */ 67 | unsigned long version; /**structure version */ 68 | unsigned long flags; /** flags to modify behaviour */ 69 | SInt32 const * channelMap; /** Channel map for HAL channel mapping , if not needed, use NULL;*/ 70 | unsigned long channelMapSize; /** Channel map size for HAL channel mapping , if not needed, use 0;*/ 71 | } PaMacCoreStreamInfo; 72 | 73 | /** 74 | * Functions 75 | */ 76 | 77 | 78 | /** Use this function to initialize a paMacCoreStreamInfo struct 79 | * using the requested flags. Note that channel mapping is turned 80 | * off after a call to this function. 81 | * @param data The datastructure to initialize 82 | * @param flags The flags to initialize the datastructure with. 83 | */ 84 | void PaMacCore_SetupStreamInfo( PaMacCoreStreamInfo *data, unsigned long flags ); 85 | 86 | /** call this after pa_SetupMacCoreStreamInfo to use channel mapping as described in notes.txt. 87 | * @param data The stream info structure to assign a channel mapping to 88 | * @param channelMap The channel map array, as described in notes.txt. This array pointer will be used directly (ie the underlying data will not be copied), so the caller should not free the array until after the stream has been opened. 89 | * @param channelMapSize The size of the channel map array. 90 | */ 91 | void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, unsigned long channelMapSize ); 92 | 93 | /** 94 | * Retrieve the AudioDeviceID of the input device assigned to an open stream 95 | * 96 | * @param s The stream to query. 97 | * 98 | * @return A valid AudioDeviceID, or NULL if an error occurred. 99 | */ 100 | AudioDeviceID PaMacCore_GetStreamInputDevice( PaStream* s ); 101 | 102 | /** 103 | * Retrieve the AudioDeviceID of the output device assigned to an open stream 104 | * 105 | * @param s The stream to query. 106 | * 107 | * @return A valid AudioDeviceID, or NULL if an error occurred. 108 | */ 109 | AudioDeviceID PaMacCore_GetStreamOutputDevice( PaStream* s ); 110 | 111 | /** 112 | * Returns a statically allocated string with the device's name 113 | * for the given channel. NULL will be returned on failure. 114 | * 115 | * This function's implemenation is not complete! 116 | * 117 | * @param device The PortAudio device index. 118 | * @param channel The channel number who's name is requested. 119 | * @return a statically allocated string with the name of the device. 120 | * Because this string is statically allocated, it must be 121 | * coppied if it is to be saved and used by the user after 122 | * another call to this function. 123 | * 124 | */ 125 | const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input ); 126 | 127 | 128 | /** Retrieve the range of legal native buffer sizes for the specificed device, in sample frames. 129 | 130 | @param device The global index of the PortAudio device about which the query is being made. 131 | @param minBufferSizeFrames A pointer to the location which will receive the minimum buffer size value. 132 | @param maxBufferSizeFrames A pointer to the location which will receive the maximum buffer size value. 133 | 134 | @see kAudioDevicePropertyBufferFrameSizeRange in the CoreAudio SDK. 135 | */ 136 | PaError PaMacCore_GetBufferSizeRange( PaDeviceIndex device, 137 | long *minBufferSizeFrames, long *maxBufferSizeFrames ); 138 | 139 | 140 | /** 141 | * Flags 142 | */ 143 | 144 | /** 145 | * The following flags alter the behaviour of PA on the mac platform. 146 | * they can be ORed together. These should work both for opening and 147 | * checking a device. 148 | */ 149 | 150 | /** Allows PortAudio to change things like the device's frame size, 151 | * which allows for much lower latency, but might disrupt the device 152 | * if other programs are using it, even when you are just Querying 153 | * the device. */ 154 | #define paMacCoreChangeDeviceParameters (0x01) 155 | 156 | /** In combination with the above flag, 157 | * causes the stream opening to fail, unless the exact sample rates 158 | * are supported by the device. */ 159 | #define paMacCoreFailIfConversionRequired (0x02) 160 | 161 | /** These flags set the SR conversion quality, if required. The wierd ordering 162 | * allows Maximum Quality to be the default.*/ 163 | #define paMacCoreConversionQualityMin (0x0100) 164 | #define paMacCoreConversionQualityMedium (0x0200) 165 | #define paMacCoreConversionQualityLow (0x0300) 166 | #define paMacCoreConversionQualityHigh (0x0400) 167 | #define paMacCoreConversionQualityMax (0x0000) 168 | 169 | /** 170 | * Here are some "preset" combinations of flags (above) to get to some 171 | * common configurations. THIS IS OVERKILL, but if more flags are added 172 | * it won't be. 173 | */ 174 | 175 | /**This is the default setting: do as much sample rate conversion as possible 176 | * and as little mucking with the device as possible. */ 177 | #define paMacCorePlayNice (0x00) 178 | /**This setting is tuned for pro audio apps. It allows SR conversion on input 179 | and output, but it tries to set the appropriate SR on the device.*/ 180 | #define paMacCorePro (0x01) 181 | /**This is a setting to minimize CPU usage and still play nice.*/ 182 | #define paMacCoreMinimizeCPUButPlayNice (0x0100) 183 | /**This is a setting to minimize CPU usage, even if that means interrupting the device. */ 184 | #define paMacCoreMinimizeCPU (0x0101) 185 | 186 | 187 | #ifdef __cplusplus 188 | } 189 | #endif /** __cplusplus */ 190 | 191 | #endif /** PA_MAC_CORE_H */ 192 | -------------------------------------------------------------------------------- /MusicSynth/PortAudio/include/pa_win_ds.h: -------------------------------------------------------------------------------- 1 | #ifndef PA_WIN_DS_H 2 | #define PA_WIN_DS_H 3 | /* 4 | * $Id: $ 5 | * PortAudio Portable Real-Time Audio Library 6 | * DirectSound specific extensions 7 | * 8 | * Copyright (c) 1999-2007 Ross Bencina and Phil Burk 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining 11 | * a copy of this software and associated documentation files 12 | * (the "Software"), to deal in the Software without restriction, 13 | * including without limitation the rights to use, copy, modify, merge, 14 | * publish, distribute, sublicense, and/or sell copies of the Software, 15 | * and to permit persons to whom the Software is furnished to do so, 16 | * subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 25 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 26 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | /* 31 | * The text above constitutes the entire PortAudio license; however, 32 | * the PortAudio community also makes the following non-binding requests: 33 | * 34 | * Any person wishing to distribute modifications to the Software is 35 | * requested to send the modifications to the original developer so that 36 | * they can be incorporated into the canonical version. It is also 37 | * requested that these non-binding requests be included along with the 38 | * license above. 39 | */ 40 | 41 | /** @file 42 | @ingroup public_header 43 | @brief DirectSound-specific PortAudio API extension header file. 44 | */ 45 | 46 | #include "portaudio.h" 47 | #include "pa_win_waveformat.h" 48 | 49 | #ifdef __cplusplus 50 | extern "C" 51 | { 52 | #endif /* __cplusplus */ 53 | 54 | 55 | #define paWinDirectSoundUseLowLevelLatencyParameters (0x01) 56 | #define paWinDirectSoundUseChannelMask (0x04) 57 | 58 | 59 | typedef struct PaWinDirectSoundStreamInfo{ 60 | unsigned long size; /**< sizeof(PaWinDirectSoundStreamInfo) */ 61 | PaHostApiTypeId hostApiType; /**< paDirectSound */ 62 | unsigned long version; /**< 2 */ 63 | 64 | unsigned long flags; /**< enable other features of this struct */ 65 | 66 | /** 67 | low-level latency setting support 68 | Sets the size of the DirectSound host buffer. 69 | When flags contains the paWinDirectSoundUseLowLevelLatencyParameters 70 | this size will be used instead of interpreting the generic latency 71 | parameters to Pa_OpenStream(). If the flag is not set this value is ignored. 72 | 73 | If the stream is a full duplex stream the implementation requires that 74 | the values of framesPerBuffer for input and output match (if both are specified). 75 | */ 76 | unsigned long framesPerBuffer; 77 | 78 | /** 79 | support for WAVEFORMATEXTENSIBLE channel masks. If flags contains 80 | paWinDirectSoundUseChannelMask this allows you to specify which speakers 81 | to address in a multichannel stream. Constants for channelMask 82 | are specified in pa_win_waveformat.h 83 | 84 | */ 85 | PaWinWaveFormatChannelMask channelMask; 86 | 87 | }PaWinDirectSoundStreamInfo; 88 | 89 | 90 | 91 | #ifdef __cplusplus 92 | } 93 | #endif /* __cplusplus */ 94 | 95 | #endif /* PA_WIN_DS_H */ 96 | -------------------------------------------------------------------------------- /MusicSynth/PortAudio/include/pa_win_waveformat.h: -------------------------------------------------------------------------------- 1 | #ifndef PA_WIN_WAVEFORMAT_H 2 | #define PA_WIN_WAVEFORMAT_H 3 | 4 | /* 5 | * PortAudio Portable Real-Time Audio Library 6 | * Windows WAVEFORMAT* data structure utilities 7 | * portaudio.h should be included before this file. 8 | * 9 | * Copyright (c) 2007 Ross Bencina 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining 12 | * a copy of this software and associated documentation files 13 | * (the "Software"), to deal in the Software without restriction, 14 | * including without limitation the rights to use, copy, modify, merge, 15 | * publish, distribute, sublicense, and/or sell copies of the Software, 16 | * and to permit persons to whom the Software is furnished to do so, 17 | * subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be 20 | * included in all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 26 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | /* 32 | * The text above constitutes the entire PortAudio license; however, 33 | * the PortAudio community also makes the following non-binding requests: 34 | * 35 | * Any person wishing to distribute modifications to the Software is 36 | * requested to send the modifications to the original developer so that 37 | * they can be incorporated into the canonical version. It is also 38 | * requested that these non-binding requests be included along with the 39 | * license above. 40 | */ 41 | 42 | /** @file 43 | @ingroup public_header 44 | @brief Windows specific PortAudio API extension and utilities header file. 45 | */ 46 | 47 | #ifdef __cplusplus 48 | extern "C" { 49 | #endif 50 | 51 | /* 52 | The following #defines for speaker channel masks are the same 53 | as those in ksmedia.h, except with PAWIN_ prepended, KSAUDIO_ removed 54 | in some cases, and casts to PaWinWaveFormatChannelMask added. 55 | */ 56 | 57 | typedef unsigned long PaWinWaveFormatChannelMask; 58 | 59 | /* Speaker Positions: */ 60 | #define PAWIN_SPEAKER_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1) 61 | #define PAWIN_SPEAKER_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x2) 62 | #define PAWIN_SPEAKER_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x4) 63 | #define PAWIN_SPEAKER_LOW_FREQUENCY ((PaWinWaveFormatChannelMask)0x8) 64 | #define PAWIN_SPEAKER_BACK_LEFT ((PaWinWaveFormatChannelMask)0x10) 65 | #define PAWIN_SPEAKER_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20) 66 | #define PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER ((PaWinWaveFormatChannelMask)0x40) 67 | #define PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER ((PaWinWaveFormatChannelMask)0x80) 68 | #define PAWIN_SPEAKER_BACK_CENTER ((PaWinWaveFormatChannelMask)0x100) 69 | #define PAWIN_SPEAKER_SIDE_LEFT ((PaWinWaveFormatChannelMask)0x200) 70 | #define PAWIN_SPEAKER_SIDE_RIGHT ((PaWinWaveFormatChannelMask)0x400) 71 | #define PAWIN_SPEAKER_TOP_CENTER ((PaWinWaveFormatChannelMask)0x800) 72 | #define PAWIN_SPEAKER_TOP_FRONT_LEFT ((PaWinWaveFormatChannelMask)0x1000) 73 | #define PAWIN_SPEAKER_TOP_FRONT_CENTER ((PaWinWaveFormatChannelMask)0x2000) 74 | #define PAWIN_SPEAKER_TOP_FRONT_RIGHT ((PaWinWaveFormatChannelMask)0x4000) 75 | #define PAWIN_SPEAKER_TOP_BACK_LEFT ((PaWinWaveFormatChannelMask)0x8000) 76 | #define PAWIN_SPEAKER_TOP_BACK_CENTER ((PaWinWaveFormatChannelMask)0x10000) 77 | #define PAWIN_SPEAKER_TOP_BACK_RIGHT ((PaWinWaveFormatChannelMask)0x20000) 78 | 79 | /* Bit mask locations reserved for future use */ 80 | #define PAWIN_SPEAKER_RESERVED ((PaWinWaveFormatChannelMask)0x7FFC0000) 81 | 82 | /* Used to specify that any possible permutation of speaker configurations */ 83 | #define PAWIN_SPEAKER_ALL ((PaWinWaveFormatChannelMask)0x80000000) 84 | 85 | /* DirectSound Speaker Config */ 86 | #define PAWIN_SPEAKER_DIRECTOUT 0 87 | #define PAWIN_SPEAKER_MONO (PAWIN_SPEAKER_FRONT_CENTER) 88 | #define PAWIN_SPEAKER_STEREO (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT) 89 | #define PAWIN_SPEAKER_QUAD (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ 90 | PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT) 91 | #define PAWIN_SPEAKER_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ 92 | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_BACK_CENTER) 93 | #define PAWIN_SPEAKER_5POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ 94 | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ 95 | PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT) 96 | #define PAWIN_SPEAKER_7POINT1 (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ 97 | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ 98 | PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \ 99 | PAWIN_SPEAKER_FRONT_LEFT_OF_CENTER | PAWIN_SPEAKER_FRONT_RIGHT_OF_CENTER) 100 | #define PAWIN_SPEAKER_5POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ 101 | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ 102 | PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT) 103 | #define PAWIN_SPEAKER_7POINT1_SURROUND (PAWIN_SPEAKER_FRONT_LEFT | PAWIN_SPEAKER_FRONT_RIGHT | \ 104 | PAWIN_SPEAKER_FRONT_CENTER | PAWIN_SPEAKER_LOW_FREQUENCY | \ 105 | PAWIN_SPEAKER_BACK_LEFT | PAWIN_SPEAKER_BACK_RIGHT | \ 106 | PAWIN_SPEAKER_SIDE_LEFT | PAWIN_SPEAKER_SIDE_RIGHT) 107 | /* 108 | According to the Microsoft documentation: 109 | The following are obsolete 5.1 and 7.1 settings (they lack side speakers). Note this means 110 | that the default 5.1 and 7.1 settings (KSAUDIO_SPEAKER_5POINT1 and KSAUDIO_SPEAKER_7POINT1 are 111 | similarly obsolete but are unchanged for compatibility reasons). 112 | */ 113 | #define PAWIN_SPEAKER_5POINT1_BACK PAWIN_SPEAKER_5POINT1 114 | #define PAWIN_SPEAKER_7POINT1_WIDE PAWIN_SPEAKER_7POINT1 115 | 116 | /* DVD Speaker Positions */ 117 | #define PAWIN_SPEAKER_GROUND_FRONT_LEFT PAWIN_SPEAKER_FRONT_LEFT 118 | #define PAWIN_SPEAKER_GROUND_FRONT_CENTER PAWIN_SPEAKER_FRONT_CENTER 119 | #define PAWIN_SPEAKER_GROUND_FRONT_RIGHT PAWIN_SPEAKER_FRONT_RIGHT 120 | #define PAWIN_SPEAKER_GROUND_REAR_LEFT PAWIN_SPEAKER_BACK_LEFT 121 | #define PAWIN_SPEAKER_GROUND_REAR_RIGHT PAWIN_SPEAKER_BACK_RIGHT 122 | #define PAWIN_SPEAKER_TOP_MIDDLE PAWIN_SPEAKER_TOP_CENTER 123 | #define PAWIN_SPEAKER_SUPER_WOOFER PAWIN_SPEAKER_LOW_FREQUENCY 124 | 125 | 126 | /* 127 | PaWinWaveFormat is defined here to provide compatibility with 128 | compilation environments which don't have headers defining 129 | WAVEFORMATEXTENSIBLE (e.g. older versions of MSVC, Borland C++ etc. 130 | 131 | The fields for WAVEFORMATEX and WAVEFORMATEXTENSIBLE are declared as an 132 | unsigned char array here to avoid clients who include this file having 133 | a dependency on windows.h and mmsystem.h, and also to to avoid having 134 | to write separate packing pragmas for each compiler. 135 | */ 136 | #define PAWIN_SIZEOF_WAVEFORMATEX 18 137 | #define PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE (PAWIN_SIZEOF_WAVEFORMATEX + 22) 138 | 139 | typedef struct{ 140 | unsigned char fields[ PAWIN_SIZEOF_WAVEFORMATEXTENSIBLE ]; 141 | unsigned long extraLongForAlignment; /* ensure that compiler aligns struct to DWORD */ 142 | } PaWinWaveFormat; 143 | 144 | /* 145 | WAVEFORMATEXTENSIBLE fields: 146 | 147 | union { 148 | WORD wValidBitsPerSample; 149 | WORD wSamplesPerBlock; 150 | WORD wReserved; 151 | } Samples; 152 | DWORD dwChannelMask; 153 | GUID SubFormat; 154 | */ 155 | 156 | #define PAWIN_INDEXOF_WVALIDBITSPERSAMPLE (PAWIN_SIZEOF_WAVEFORMATEX+0) 157 | #define PAWIN_INDEXOF_DWCHANNELMASK (PAWIN_SIZEOF_WAVEFORMATEX+2) 158 | #define PAWIN_INDEXOF_SUBFORMAT (PAWIN_SIZEOF_WAVEFORMATEX+6) 159 | 160 | 161 | /* 162 | Valid values to pass for the waveFormatTag PaWin_InitializeWaveFormatEx and 163 | PaWin_InitializeWaveFormatExtensible functions below. These must match 164 | the standard Windows WAVE_FORMAT_* values. 165 | */ 166 | #define PAWIN_WAVE_FORMAT_PCM (1) 167 | #define PAWIN_WAVE_FORMAT_IEEE_FLOAT (3) 168 | #define PAWIN_WAVE_FORMAT_DOLBY_AC3_SPDIF (0x0092) 169 | #define PAWIN_WAVE_FORMAT_WMA_SPDIF (0x0164) 170 | 171 | 172 | /* 173 | returns PAWIN_WAVE_FORMAT_PCM or PAWIN_WAVE_FORMAT_IEEE_FLOAT 174 | depending on the sampleFormat parameter. 175 | */ 176 | int PaWin_SampleFormatToLinearWaveFormatTag( PaSampleFormat sampleFormat ); 177 | 178 | /* 179 | Use the following two functions to initialize the waveformat structure. 180 | */ 181 | 182 | void PaWin_InitializeWaveFormatEx( PaWinWaveFormat *waveFormat, 183 | int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate ); 184 | 185 | 186 | void PaWin_InitializeWaveFormatExtensible( PaWinWaveFormat *waveFormat, 187 | int numChannels, PaSampleFormat sampleFormat, int waveFormatTag, double sampleRate, 188 | PaWinWaveFormatChannelMask channelMask ); 189 | 190 | 191 | /* Map a channel count to a speaker channel mask */ 192 | PaWinWaveFormatChannelMask PaWin_DefaultChannelMask( int numChannels ); 193 | 194 | 195 | #ifdef __cplusplus 196 | } 197 | #endif /* __cplusplus */ 198 | 199 | #endif /* PA_WIN_WAVEFORMAT_H */ -------------------------------------------------------------------------------- /MusicSynth/PortAudio/include/pa_win_wdmks.h: -------------------------------------------------------------------------------- 1 | #ifndef PA_WIN_WDMKS_H 2 | #define PA_WIN_WDMKS_H 3 | /* 4 | * $Id: pa_win_wdmks.h 1812 2012-02-14 09:32:57Z robiwan $ 5 | * PortAudio Portable Real-Time Audio Library 6 | * WDM/KS specific extensions 7 | * 8 | * Copyright (c) 1999-2007 Ross Bencina and Phil Burk 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining 11 | * a copy of this software and associated documentation files 12 | * (the "Software"), to deal in the Software without restriction, 13 | * including without limitation the rights to use, copy, modify, merge, 14 | * publish, distribute, sublicense, and/or sell copies of the Software, 15 | * and to permit persons to whom the Software is furnished to do so, 16 | * subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 25 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 26 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | /* 31 | * The text above constitutes the entire PortAudio license; however, 32 | * the PortAudio community also makes the following non-binding requests: 33 | * 34 | * Any person wishing to distribute modifications to the Software is 35 | * requested to send the modifications to the original developer so that 36 | * they can be incorporated into the canonical version. It is also 37 | * requested that these non-binding requests be included along with the 38 | * license above. 39 | */ 40 | 41 | /** @file 42 | @ingroup public_header 43 | @brief WDM Kernel Streaming-specific PortAudio API extension header file. 44 | */ 45 | 46 | 47 | #include "portaudio.h" 48 | 49 | #include 50 | 51 | #ifdef __cplusplus 52 | extern "C" 53 | { 54 | #endif /* __cplusplus */ 55 | typedef struct PaWinWDMKSInfo{ 56 | unsigned long size; /**< sizeof(PaWinWDMKSInfo) */ 57 | PaHostApiTypeId hostApiType; /**< paWDMKS */ 58 | unsigned long version; /**< 1 */ 59 | 60 | /* The number of packets to use for WaveCyclic devices, range is [2, 8]. Set to zero for default value of 2. */ 61 | unsigned noOfPackets; 62 | } PaWinWDMKSInfo; 63 | 64 | typedef enum PaWDMKSType 65 | { 66 | Type_kNotUsed, 67 | Type_kWaveCyclic, 68 | Type_kWaveRT, 69 | Type_kCnt, 70 | } PaWDMKSType; 71 | 72 | typedef enum PaWDMKSSubType 73 | { 74 | SubType_kUnknown, 75 | SubType_kNotification, 76 | SubType_kPolled, 77 | SubType_kCnt, 78 | } PaWDMKSSubType; 79 | 80 | typedef struct PaWinWDMKSDeviceInfo { 81 | wchar_t filterPath[MAX_PATH]; /**< KS filter path in Unicode! */ 82 | wchar_t topologyPath[MAX_PATH]; /**< Topology filter path in Unicode! */ 83 | PaWDMKSType streamingType; 84 | GUID deviceProductGuid; /**< The product GUID of the device (if supported) */ 85 | } PaWinWDMKSDeviceInfo; 86 | 87 | typedef struct PaWDMKSDirectionSpecificStreamInfo 88 | { 89 | PaDeviceIndex device; 90 | unsigned channels; /**< No of channels the device is opened with */ 91 | unsigned framesPerHostBuffer; /**< No of frames of the device buffer */ 92 | int endpointPinId; /**< Endpoint pin ID (on topology filter if topologyName is not empty) */ 93 | int muxNodeId; /**< Only valid for input */ 94 | PaWDMKSSubType streamingSubType; /**< Not known until device is opened for streaming */ 95 | } PaWDMKSDirectionSpecificStreamInfo; 96 | 97 | typedef struct PaWDMKSSpecificStreamInfo { 98 | PaWDMKSDirectionSpecificStreamInfo input; 99 | PaWDMKSDirectionSpecificStreamInfo output; 100 | } PaWDMKSSpecificStreamInfo; 101 | 102 | #ifdef __cplusplus 103 | } 104 | #endif /* __cplusplus */ 105 | 106 | #endif /* PA_WIN_DS_H */ 107 | -------------------------------------------------------------------------------- /MusicSynth/PortAudio/include/pa_win_wmme.h: -------------------------------------------------------------------------------- 1 | #ifndef PA_WIN_WMME_H 2 | #define PA_WIN_WMME_H 3 | /* 4 | * $Id: pa_win_wmme.h 1592 2011-02-04 10:41:58Z rossb $ 5 | * PortAudio Portable Real-Time Audio Library 6 | * MME specific extensions 7 | * 8 | * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining 11 | * a copy of this software and associated documentation files 12 | * (the "Software"), to deal in the Software without restriction, 13 | * including without limitation the rights to use, copy, modify, merge, 14 | * publish, distribute, sublicense, and/or sell copies of the Software, 15 | * and to permit persons to whom the Software is furnished to do so, 16 | * subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 25 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 26 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | /* 31 | * The text above constitutes the entire PortAudio license; however, 32 | * the PortAudio community also makes the following non-binding requests: 33 | * 34 | * Any person wishing to distribute modifications to the Software is 35 | * requested to send the modifications to the original developer so that 36 | * they can be incorporated into the canonical version. It is also 37 | * requested that these non-binding requests be included along with the 38 | * license above. 39 | */ 40 | 41 | /** @file 42 | @ingroup public_header 43 | @brief WMME-specific PortAudio API extension header file. 44 | */ 45 | 46 | #include "portaudio.h" 47 | #include "pa_win_waveformat.h" 48 | 49 | #ifdef __cplusplus 50 | extern "C" 51 | { 52 | #endif /* __cplusplus */ 53 | 54 | 55 | /* The following are flags which can be set in 56 | PaWinMmeStreamInfo's flags field. 57 | */ 58 | 59 | #define paWinMmeUseLowLevelLatencyParameters (0x01) 60 | #define paWinMmeUseMultipleDevices (0x02) /* use mme specific multiple device feature */ 61 | #define paWinMmeUseChannelMask (0x04) 62 | 63 | /* By default, the mme implementation drops the processing thread's priority 64 | to THREAD_PRIORITY_NORMAL and sleeps the thread if the CPU load exceeds 100% 65 | This flag disables any priority throttling. The processing thread will always 66 | run at THREAD_PRIORITY_TIME_CRITICAL. 67 | */ 68 | #define paWinMmeDontThrottleOverloadedProcessingThread (0x08) 69 | 70 | /* Flags for non-PCM spdif passthrough. 71 | */ 72 | #define paWinMmeWaveFormatDolbyAc3Spdif (0x10) 73 | #define paWinMmeWaveFormatWmaSpdif (0x20) 74 | 75 | 76 | typedef struct PaWinMmeDeviceAndChannelCount{ 77 | PaDeviceIndex device; 78 | int channelCount; 79 | }PaWinMmeDeviceAndChannelCount; 80 | 81 | 82 | typedef struct PaWinMmeStreamInfo{ 83 | unsigned long size; /**< sizeof(PaWinMmeStreamInfo) */ 84 | PaHostApiTypeId hostApiType; /**< paMME */ 85 | unsigned long version; /**< 1 */ 86 | 87 | unsigned long flags; 88 | 89 | /* low-level latency setting support 90 | These settings control the number and size of host buffers in order 91 | to set latency. They will be used instead of the generic parameters 92 | to Pa_OpenStream() if flags contains the PaWinMmeUseLowLevelLatencyParameters 93 | flag. 94 | 95 | If PaWinMmeStreamInfo structures with PaWinMmeUseLowLevelLatencyParameters 96 | are supplied for both input and output in a full duplex stream, then the 97 | input and output framesPerBuffer must be the same, or the larger of the 98 | two must be a multiple of the smaller, otherwise a 99 | paIncompatibleHostApiSpecificStreamInfo error will be returned from 100 | Pa_OpenStream(). 101 | */ 102 | unsigned long framesPerBuffer; 103 | unsigned long bufferCount; /* formerly numBuffers */ 104 | 105 | /* multiple devices per direction support 106 | If flags contains the PaWinMmeUseMultipleDevices flag, 107 | this functionality will be used, otherwise the device parameter to 108 | Pa_OpenStream() will be used instead. 109 | If devices are specified here, the corresponding device parameter 110 | to Pa_OpenStream() should be set to paUseHostApiSpecificDeviceSpecification, 111 | otherwise an paInvalidDevice error will result. 112 | The total number of channels accross all specified devices 113 | must agree with the corresponding channelCount parameter to 114 | Pa_OpenStream() otherwise a paInvalidChannelCount error will result. 115 | */ 116 | PaWinMmeDeviceAndChannelCount *devices; 117 | unsigned long deviceCount; 118 | 119 | /* 120 | support for WAVEFORMATEXTENSIBLE channel masks. If flags contains 121 | paWinMmeUseChannelMask this allows you to specify which speakers 122 | to address in a multichannel stream. Constants for channelMask 123 | are specified in pa_win_waveformat.h 124 | 125 | */ 126 | PaWinWaveFormatChannelMask channelMask; 127 | 128 | }PaWinMmeStreamInfo; 129 | 130 | 131 | /** Retrieve the number of wave in handles used by a PortAudio WinMME stream. 132 | Returns zero if the stream is output only. 133 | 134 | @return A non-negative value indicating the number of wave in handles 135 | or, a PaErrorCode (which are always negative) if PortAudio is not initialized 136 | or an error is encountered. 137 | 138 | @see PaWinMME_GetStreamInputHandle 139 | */ 140 | int PaWinMME_GetStreamInputHandleCount( PaStream* stream ); 141 | 142 | 143 | /** Retrieve a wave in handle used by a PortAudio WinMME stream. 144 | 145 | @param stream The stream to query. 146 | @param handleIndex The zero based index of the wave in handle to retrieve. This 147 | should be in the range [0, PaWinMME_GetStreamInputHandleCount(stream)-1]. 148 | 149 | @return A valid wave in handle, or NULL if an error occurred. 150 | 151 | @see PaWinMME_GetStreamInputHandle 152 | */ 153 | HWAVEIN PaWinMME_GetStreamInputHandle( PaStream* stream, int handleIndex ); 154 | 155 | 156 | /** Retrieve the number of wave out handles used by a PortAudio WinMME stream. 157 | Returns zero if the stream is input only. 158 | 159 | @return A non-negative value indicating the number of wave out handles 160 | or, a PaErrorCode (which are always negative) if PortAudio is not initialized 161 | or an error is encountered. 162 | 163 | @see PaWinMME_GetStreamOutputHandle 164 | */ 165 | int PaWinMME_GetStreamOutputHandleCount( PaStream* stream ); 166 | 167 | 168 | /** Retrieve a wave out handle used by a PortAudio WinMME stream. 169 | 170 | @param stream The stream to query. 171 | @param handleIndex The zero based index of the wave out handle to retrieve. 172 | This should be in the range [0, PaWinMME_GetStreamOutputHandleCount(stream)-1]. 173 | 174 | @return A valid wave out handle, or NULL if an error occurred. 175 | 176 | @see PaWinMME_GetStreamOutputHandleCount 177 | */ 178 | HWAVEOUT PaWinMME_GetStreamOutputHandle( PaStream* stream, int handleIndex ); 179 | 180 | 181 | #ifdef __cplusplus 182 | } 183 | #endif /* __cplusplus */ 184 | 185 | #endif /* PA_WIN_WMME_H */ 186 | -------------------------------------------------------------------------------- /MusicSynth/SampleList.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // SampleList.h 3 | // 4 | // The list of samples loaded from the Samples Directory. 5 | // Used to generate boilerplate code via macro expansion. 6 | // Creates g_sample_ variables for each sample loaded. 7 | // Loads file "Samples/.wav" 8 | // 9 | //-------------------------------------------------------------------------------------------------- 10 | 11 | SAMPLE(clap) 12 | SAMPLE(cymbal) 13 | SAMPLE(kick) 14 | SAMPLE(legend1) 15 | SAMPLE(legend2) 16 | SAMPLE(ting) 17 | //SAMPLE(oakenfold) 18 | SAMPLE(pvd) 19 | SAMPLE(dreams) 20 | 21 | #undef SAMPLE -------------------------------------------------------------------------------- /MusicSynth/Samples.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // Samples.cpp 3 | // 4 | // The loaded audio samples 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "Samples.h" 9 | #include 10 | #include "DemoMgr.h" 11 | 12 | #define SAMPLE(name) SWavFile g_sample_##name; 13 | #include "SampleList.h" 14 | 15 | void LoadSamples() { 16 | 17 | #define SAMPLE(name) \ 18 | if (!g_sample_##name.Load("Samples/" #name ".wav", CDemoMgr::GetNumChannels(), (size_t)CDemoMgr::GetSampleRate())) \ 19 | printf("Could not load Samples/" #name ".wav.\r\n"); 20 | #include "SampleList.h" 21 | } -------------------------------------------------------------------------------- /MusicSynth/Samples.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // Samples.h 3 | // 4 | // The loaded audio samples 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | #pragma once 8 | 9 | #include "WavFile.h" 10 | 11 | #define SAMPLE(name) extern SWavFile g_sample_##name; 12 | #include "SampleList.h" 13 | 14 | void LoadSamples(); -------------------------------------------------------------------------------- /MusicSynth/Samples/clap.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/Samples/clap.wav -------------------------------------------------------------------------------- /MusicSynth/Samples/cymbal.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/Samples/cymbal.wav -------------------------------------------------------------------------------- /MusicSynth/Samples/dreams.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/Samples/dreams.wav -------------------------------------------------------------------------------- /MusicSynth/Samples/kick.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/Samples/kick.wav -------------------------------------------------------------------------------- /MusicSynth/Samples/legend1.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/Samples/legend1.wav -------------------------------------------------------------------------------- /MusicSynth/Samples/legend2.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/Samples/legend2.wav -------------------------------------------------------------------------------- /MusicSynth/Samples/oakenfold.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/Samples/oakenfold.wav -------------------------------------------------------------------------------- /MusicSynth/Samples/pvd.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/Samples/pvd.wav -------------------------------------------------------------------------------- /MusicSynth/Samples/ting.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/Samples/ting.wav -------------------------------------------------------------------------------- /MusicSynth/WavFile.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------------------------- 2 | // WavFile.cpp 3 | // 4 | // Helper stuff for interacting with wave files 5 | // 6 | //-------------------------------------------------------------------------------------------------- 7 | 8 | #include "WavFile.h" 9 | #include "AudioUtils.h" 10 | #include 11 | #include 12 | 13 | void NormalizeAudioData(float *pData, int nNumSamples) 14 | { 15 | //figure out what the maximum and minimum value is 16 | float fMaxValue = pData[0]; 17 | float fMinValue = pData[0]; 18 | for (int nIndex = 0; nIndex < nNumSamples; ++nIndex) 19 | { 20 | if (pData[nIndex] > fMaxValue) 21 | { 22 | fMaxValue = pData[nIndex]; 23 | } 24 | 25 | if (pData[nIndex] < fMinValue) 26 | { 27 | fMinValue = pData[nIndex]; 28 | } 29 | } 30 | 31 | //calculate the center and the height 32 | float fCenter = (fMinValue + fMaxValue) / 2.0f; 33 | float fHeight = fMaxValue - fMinValue; 34 | 35 | //center and normalize the samples 36 | for (int nIndex = 0; nIndex < nNumSamples; ++nIndex) 37 | { 38 | //center the samples 39 | pData[nIndex] -= fCenter; 40 | 41 | //normalize the samples 42 | pData[nIndex] /= fHeight; 43 | } 44 | } 45 | 46 | void ChangeNumChannels(float *&pData, int &nNumSamples, int nSrcChannels, int nDestChannels) 47 | { 48 | //if the number of channels requested is the number of channels already there, or either number of channels is not mono or stereo, return 49 | if (nSrcChannels == nDestChannels || 50 | nSrcChannels < 1 || nSrcChannels > 2 || 51 | nDestChannels < 1 || nDestChannels > 2) 52 | { 53 | return; 54 | } 55 | 56 | //if converting to stereo 57 | if (nDestChannels == 2) 58 | { 59 | float *pNewData = new float[nNumSamples * 2]; 60 | for (int nIndex = 0; nIndex < nNumSamples; ++nIndex) 61 | { 62 | pNewData[nIndex * 2] = pData[nIndex]; 63 | pNewData[nIndex * 2 + 1] = pData[nIndex]; 64 | } 65 | 66 | delete pData; 67 | pData = pNewData; 68 | nNumSamples *= 2; 69 | } 70 | //else converting to mono 71 | else 72 | { 73 | float *pNewData = new float[nNumSamples / 2]; 74 | for (int nIndex = 0; nIndex < nNumSamples / 2; ++nIndex) 75 | { 76 | pNewData[nIndex] = pData[nIndex * 2] + pData[nIndex * 2 + 1]; 77 | } 78 | 79 | delete pData; 80 | pData = pNewData; 81 | nNumSamples /= 2; 82 | } 83 | } 84 | 85 | float GetInterpolatedAudioSample(float *pData, int nNumSamples, float fIndex) 86 | { 87 | int nIndex1 = (int)fIndex; 88 | int nIndex0 = nIndex1 - 1; 89 | int nIndex2 = nIndex1 + 1; 90 | int nIndex3 = nIndex1 + 2; 91 | 92 | CLAMP(nIndex0, 0, nNumSamples - 1); 93 | CLAMP(nIndex1, 0, nNumSamples - 1); 94 | CLAMP(nIndex2, 0, nNumSamples - 1); 95 | CLAMP(nIndex3, 0, nNumSamples - 1); 96 | 97 | float percent = std::fmodf(fIndex, 1.0f); 98 | 99 | float fSample0 = pData[nIndex0]; 100 | float fSample1 = pData[nIndex1]; 101 | float fSample2 = pData[nIndex2]; 102 | float fSample3 = pData[nIndex3]; 103 | 104 | // use cubic hermite interpolation for C1 continuity. Better than lerp, but not as good as sync of course! 105 | return CubicHermite(fSample0, fSample1, fSample2, fSample3, percent); 106 | } 107 | 108 | void ResampleData(float *&pData, int &nNumSamples, int nSrcSampleRate, int nDestSampleRate) 109 | { 110 | //if the requested sample rate is the sample rate it already is, bail out and do nothing 111 | if (nSrcSampleRate == nDestSampleRate) 112 | return; 113 | 114 | //calculate the ratio of the old sample rate to the new 115 | float fResampleRatio = (float)nDestSampleRate / (float)nSrcSampleRate; 116 | 117 | //calculate how many samples the new data will have and allocate the new sample data 118 | int nNewDataNumSamples = (int)((float)nNumSamples * fResampleRatio); 119 | float *pNewData = new float[nNewDataNumSamples]; 120 | 121 | //get each interpolated output sample. 122 | for (int nIndex = 0; nIndex < nNewDataNumSamples; ++nIndex) 123 | pNewData[nIndex] = GetInterpolatedAudioSample(pData, nNumSamples, (float)nIndex / fResampleRatio); 124 | 125 | //free the old data and set the new data 126 | delete[] pData; 127 | pData = pNewData; 128 | nNumSamples = nNewDataNumSamples; 129 | } 130 | 131 | float PCMToFloat(unsigned char *pPCMData, int nNumBytes) 132 | { 133 | int32_t nData = 0; 134 | unsigned char *pData = (unsigned char *)&nData; 135 | 136 | switch (nNumBytes) 137 | { 138 | case 1: 139 | { 140 | pData[3] = pPCMData[0]; 141 | return ((float)nData) / ((float)0x000000ff); 142 | } 143 | case 2: 144 | { 145 | pData[2] = pPCMData[0]; 146 | pData[3] = pPCMData[1]; 147 | return ((float)nData) / ((float)0x00007fff); 148 | } 149 | case 3: 150 | { 151 | pData[1] = pPCMData[0]; 152 | pData[2] = pPCMData[1]; 153 | pData[3] = pPCMData[2]; 154 | return ((float)nData) / ((float)0x007fffff); 155 | } 156 | case 4: 157 | { 158 | pData[0] = pPCMData[0]; 159 | pData[1] = pPCMData[1]; 160 | pData[2] = pPCMData[2]; 161 | pData[3] = pPCMData[3]; 162 | return ((float)nData) / ((float)0x7fffffff); 163 | } 164 | default: 165 | { 166 | return 0.0f; 167 | } 168 | } 169 | } 170 | 171 | bool ReadWaveFile(const char *fileName, float *&data, size_t &numSamples, size_t numChannels, size_t sampleRate, bool normalizeData) { 172 | //open the file if we can 173 | FILE *File = nullptr; 174 | fopen_s(&File, fileName, "rb"); 175 | if (!File) 176 | { 177 | return false; 178 | } 179 | 180 | //read the main chunk ID and make sure it's "RIFF" 181 | char buffer[5]; 182 | buffer[4] = 0; 183 | if (fread(buffer, 4, 1, File) != 1 || strcmp(buffer, "RIFF")) 184 | { 185 | fclose(File); 186 | return false; 187 | } 188 | 189 | //read the main chunk size 190 | uint32_t nChunkSize; 191 | if (fread(&nChunkSize, 4, 1, File) != 1) 192 | { 193 | fclose(File); 194 | return false; 195 | } 196 | 197 | //read the format and make sure it's "WAVE" 198 | if (fread(buffer, 4, 1, File) != 1 || strcmp(buffer, "WAVE")) 199 | { 200 | fclose(File); 201 | return false; 202 | } 203 | 204 | long chunkPosFmt = -1; 205 | long chunkPosData = -1; 206 | 207 | while (chunkPosFmt == -1 || chunkPosData == -1) 208 | { 209 | //read a sub chunk id and a chunk size if we can 210 | if (fread(buffer, 4, 1, File) != 1 || fread(&nChunkSize, 4, 1, File) != 1) 211 | { 212 | fclose(File); 213 | return false; 214 | } 215 | 216 | //if we hit a fmt 217 | if (!strcmp(buffer, "fmt ")) 218 | { 219 | chunkPosFmt = ftell(File) - 8; 220 | } 221 | //else if we hit a data 222 | else if (!strcmp(buffer, "data")) 223 | { 224 | chunkPosData = ftell(File) - 8; 225 | } 226 | 227 | //skip to the next chunk 228 | fseek(File, nChunkSize, SEEK_CUR); 229 | } 230 | 231 | //we'll use this handy struct to load in 232 | SWaveFileHeader waveData; 233 | 234 | //load the fmt part if we can 235 | fseek(File, chunkPosFmt, SEEK_SET); 236 | if (fread(&waveData.m_szSubChunk1ID, 24, 1, File) != 1) 237 | { 238 | fclose(File); 239 | return false; 240 | } 241 | 242 | //load the data part if we can 243 | fseek(File, chunkPosData, SEEK_SET); 244 | if (fread(&waveData.m_szSubChunk2ID, 8, 1, File) != 1) 245 | { 246 | fclose(File); 247 | return false; 248 | } 249 | 250 | //verify a couple things about the file data 251 | if (waveData.m_nAudioFormat != 1 || //only pcm data 252 | waveData.m_nNumChannels < 1 || //must have a channel 253 | waveData.m_nNumChannels > 2 || //must not have more than 2 254 | waveData.m_nBitsPerSample > 32 || //32 bits per sample max 255 | waveData.m_nBitsPerSample % 8 != 0 || //must be a multiple of 8 bites 256 | waveData.m_nBlockAlign > 8) //blocks must be 8 bytes or lower 257 | { 258 | fclose(File); 259 | return false; 260 | } 261 | 262 | //figure out how many samples and blocks there are total in the source data 263 | int nBytesPerBlock = waveData.m_nBlockAlign; 264 | int nNumBlocks = waveData.m_nSubChunk2Size / nBytesPerBlock; 265 | int nNumSourceSamples = nNumBlocks * waveData.m_nNumChannels; 266 | 267 | //allocate space for the source samples 268 | float *pSourceSamples = new float[nNumSourceSamples]; 269 | 270 | //maximum size of a block is 8 bytes. 4 bytes per samples, 2 channels 271 | unsigned char pBlockData[8]; 272 | memset(pBlockData, 0, 8); 273 | 274 | //read in the source samples at whatever sample rate / number of channels it might be in 275 | int nBytesPerSample = nBytesPerBlock / waveData.m_nNumChannels; 276 | for (int nIndex = 0; nIndex < nNumSourceSamples; nIndex += waveData.m_nNumChannels) 277 | { 278 | //read in a block 279 | if (fread(pBlockData, waveData.m_nBlockAlign, 1, File) != 1) 280 | { 281 | delete[] pSourceSamples; 282 | fclose(File); 283 | return false; 284 | } 285 | 286 | //get the first sample 287 | pSourceSamples[nIndex] = PCMToFloat(pBlockData, nBytesPerSample); 288 | 289 | //get the second sample if there is one 290 | if (waveData.m_nNumChannels == 2) 291 | { 292 | pSourceSamples[nIndex + 1] = PCMToFloat(&pBlockData[nBytesPerSample], nBytesPerSample); 293 | } 294 | } 295 | 296 | //re-sample the sample rate up or down as needed 297 | ResampleData(pSourceSamples, nNumSourceSamples, waveData.m_nSampleRate, sampleRate); 298 | 299 | //handle switching from mono to stereo or vice versa 300 | ChangeNumChannels(pSourceSamples, nNumSourceSamples, waveData.m_nNumChannels, numChannels); 301 | 302 | //normalize the data if we should 303 | if (normalizeData) 304 | NormalizeAudioData(pSourceSamples, nNumSourceSamples); 305 | 306 | //return our data 307 | data = pSourceSamples; 308 | numSamples = nNumSourceSamples; 309 | 310 | return true; 311 | } -------------------------------------------------------------------------------- /MusicSynth/WavFile.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/WavFile.h -------------------------------------------------------------------------------- /MusicSynth/portaudio.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Atrix256/MusicSynth/a7712bb72fe451c6f656ef0109ca20831d365646/MusicSynth/portaudio.dll -------------------------------------------------------------------------------- /Structure.txt: -------------------------------------------------------------------------------- 1 | Presentation is on 7/1/16 2 | 3 | 4 | * invite nathan and sound guys too 5 | 6 | ? lament of tim curry may make good intro music? play it looping 7 | * or maybe some looping mellow house music clip kinda thing? 8 | * maybe tim curry without the intro and outro 9 | 10 | ! make the music that brings it all together? 11 | * maybe make the music as we go? i dunno... 12 | 13 | ! make the demo 0_BGMusic 14 | * make it from things you showcase in the demos, so it comes together by the end. 15 | * demo the bg music again at the end to show how it all came together. 16 | 17 | ! make sure video recording and room will pick up audio ok. 18 | * talk to paul haban. 19 | 20 | 21 | Presentation Outline: 22 | 23 | --1-- (DONE) 24 | * title / background music? explaining that bg music was made with the things being talked about. 25 | * I wanted to make this presentation since paul started doing code & tells, finally got around to it. 26 | * DEMO = 0_BGMusic 27 | 28 | --2-- (DONE) 29 | * What is audio data? 30 | * usually from -1.0 to 1.0. (*HDR audio, non floating point, etc) 31 | * changes in values are what makes sounds, a constant value is silence, no matter what the value is. 32 | * sound = pressure waves in the air, so speaker needs to move to make changes in pressure. 33 | * sound value dictates what position the speaker should be at. 34 | * better speakers are able get to the right places the samples specify more quickly and more accurately. worse speakers end up with wrong frequencies. 35 | * resolution: 36 | * x axis = sample rate. samples per second. 37 | * y axis = bit depth. bits per sample. 38 | * may also have multiple channels (like, stereo) data interleaved. 39 | * an uncompressed wav file is literally just a header, and then audio samples 40 | * decibels vs amplitude and perception of loudness? 41 | 42 | --2.5-- (DONE) 43 | * what is a note? octave? 44 | * explain how to calculate the frequency of notes, and show some notes & frequencies in a table 45 | * note that other music styles (asian, middle eastern) have different systems. it "sounds" right to us, but only because it's what we were taught. 46 | 47 | --3-- (DONE) 48 | * sine wave = gently changing up and down. all audio data is made of sine waves. 49 | * more sine waves per second = higher frequency, fewer sine waves per second = lower frequency. 50 | * show sine tones at diff frequencies 51 | * demo: press buttons to change frequencies from low to high 52 | * DEMO = Sine 53 | * like audio data, it's the difference between notes that is important not the notes themselves. 54 | * midi devices are not that hard to get working, but using QWERTY 55 | ! note that keyboards are bad at pressing multiple keys at once. 56 | * keyboardsareevil.com 57 | 58 | --4-- (DONE) 59 | * Note sliding and popping: free spinning oscilators vs not. audio needs to be smooth! 60 | * show playing notes one after the other, having popping 61 | * Note: can also come from changing volume quickly, so need to apply smooth volume over time. 62 | * demo: let people hear it 63 | * show the fix: free spinning oscilator 64 | * show note sliding 65 | * demo: let people hear it with popping and without. 66 | * DEMO = Popping 67 | 68 | --5-- (DONE) 69 | * clipping: when things get too high or low, it clips then, making distortion. 70 | * show clipped sine tones at different volumes 71 | * sometimes desired though. Electric guitar distortion takes it's roots from this for instance. 72 | * demo: play some clipped notes. adjust volume to make more or less clipping? 73 | * DEMO = Clipping 74 | ! Note: sounds "8 bit" cause the taller a sine wave becomes, the more "square wave" it becomes. old systems did square waves cause they are fast to compute! 75 | 76 | --6-- (DONE) 77 | * Mixing: add values together to mix them together and make them both be able to be heard! 78 | * don't thinks clip? in practice it takes quite a bit to make that happen! manual process to correct levels though. although you can do "automatic volume adjustments" both in real time and "offline" with limiters and compressors 79 | * don't play at full volume though? 80 | * demo: polyphonic tones with a decently long envelope. maybe some w/ extra long envelope and cause clipping? button to silence it :P 81 | * Note: polyphonic 82 | * Note: turn down volume a bit to keep from clipping. turn volume up to max, show how you can cause clipping. 83 | * Note: can also kill all but last 3 or 4 notes played. 84 | * NOTE: this clipping is done 100% in my software. I'm not maxing out any speakers or anything. no damage 85 | * DEMO = Mixing 86 | 87 | --7-- 88 | * Envelopes (DONE) 89 | * show a few examples 90 | * demo: demo a few examples 91 | * DEMO = Envelopes 92 | 93 | --8-- (DONE) 94 | * Common wave forms 95 | * show square, saw, triangle and sine 96 | * demo: each wave form. point out how similar sine and triangle are. triangle was used because sine was too costly to compute. 97 | * DEMO = WaveForms 98 | 99 | --9-- (DONE) 100 | * Aliasing 101 | * nyquist frequency 102 | * bandlimited wave forms (w/ equations?) 103 | * mention additive synthesis being a way of building up a sound from nothing by adding things together. 104 | * demo: bandlimited vs not wave forms. 105 | * DEMO = BLWaveForms 106 | 107 | --9.5-- (DONE) 108 | * Additive Synth 109 | * can analyze a sound to see what frequencies are present, at which volumes, over time. 110 | * pick the N most prominent frequencies, and play them, makign envelopes that match the real sound. 111 | * instead of analyzing you can also just throw them together. 112 | * DEMO = Additive 113 | 114 | --10-- (DONE) 115 | * Tremolo & Vibrato 116 | * Tremolo: modify amplitude on a low frequency sine wave 117 | * Vibrato: modify frequency on a low frequency sine wave 118 | * (modular synth = plug anything into anything else, so like, you could plug a saw wave into vibrato etc) 119 | * demo: some notes with vibrato, some notes with tremolo 120 | * DEMO = TremVib 121 | * note: The effects have 2 params: frequency and depth. tremolo depth = how long and high it makes it. vibrato depth = how much higher or lower the frequency gets. 122 | * note: i have them on a sine wave (LFO - low frequency oscilator), but you could put the effects on other wave forms, even have some other sound controlling the effect! 123 | 124 | --11-- (DONE) 125 | * Frequency Modulation Synthesis (FM Synth) 126 | * bringing tremolo up to audble frequencies = AM synth, aka amplitude modulation synth. 127 | * Never been able to make it sound interesting, so not so useful IMO. 128 | * bring vibrato up to audible frequencies = FM sytnh. 129 | * show: book about it, talk about complexities 130 | * demo: play some FM synth notes 131 | * great read: http://the-all.org/tx81z/fm_overview.html (get some notes there, like what increasing speed vs depth does) 132 | * DEMO = FMSynth 133 | 134 | --12-- (DONE) 135 | * Delay (echo) 136 | * talk about having a buffer of the right number of samples with some feedback volume. Circular buffer. 137 | * demo: play some notes that are aligned to timing, and some that aren't. 138 | * mention wet vs dry. should demo let you adjust wet vs dry? 139 | * DEMO = Delay 140 | 141 | --13-- (DONE) 142 | * Reverb (echo) 143 | * explain multitap. mention convolution reverb. Reference blog post on how to do it. 144 | * demo: play some sweet sweet notes with multitap reverb 145 | * could play multitap next to convolution reverb to show difference in quality 146 | ! note: multitap is aproximation of convolution. more taps = closer to convolution 147 | * Demo = Reverb 148 | 149 | --14-- (DONE) 150 | * Flange (echo) 151 | * explain origins. explain how to simulate. talk about sub sample interpolation. linear, cubic hermite, sync, etc. 152 | * demo: wave forms with flange. 153 | ! note: the sauce = "reverb + flange" 154 | * Demo = Flange 155 | 156 | --15-- (DONE) 157 | * Basic Drum (echo) 158 | * explain how to do it. 159 | * demo: step by step to improve it's sound. 160 | * mention you could use sound clips 161 | ! note: using sound clips (samples of actual drums) is easy and gives good results. Synthing drums is more work. 162 | ! http://blog.demofox.org/2015/03/14/diy-synth-basic-drum/ 163 | * Demo = Drum 164 | 165 | --16-- (DONE) 166 | * cymbal from static? (echo) 167 | * a 5th common wave form: "noise" 168 | * show how to use an envelope to make the noise meaningful 169 | * demo: play some drums and symbols 170 | * Demo = Drum, but use space bar. 171 | 172 | --17-- (DONE) 173 | * Automatic volume adjustments 174 | * briefly explain compressors and limiters 175 | * useful for keeping things from clipping 176 | * also useful to make things "pop out" more. 177 | * demo: drums with side chain 178 | * mention that we do this in heroes 179 | * mention battlefield's HDR audio system 180 | * we aren't doing automatic. we made an envelope to go with the deep drum to make it sound more powerful 181 | * demo: a button to toggle a melody. a button to toggle ducking melody based on drum volume or not. 182 | * Demo = Ducking 183 | 184 | --18-- (DONE) 185 | ? space bar = cymbal, other things = drum notes. press 1 to toggle an auto melody? 186 | ? also on audio samples 187 | * could show how the same notes over and over sound more interesting with an effect powered by LFO 188 | * Basic LPF and HPF 189 | * demo: how they sound 190 | * demo: use them in echo? 191 | * mention: biquads, IIR/FIR. 192 | * mention: subtractive synth: use filters to carve the block. different wave forms have different frequencies at different amplitudes so sound differently when filtered. 193 | ? figure out biquads, or something similar? 194 | * can also talk about band pass and band stop filters 195 | * DEMO = Filtering 196 | * mention subtractive synthesis 197 | * mention vuvuzellas 198 | 199 | --19-- (DONE) 200 | ! NO! this is stereo sound. 201 | * DEMO = Stereo 202 | * Positional sound 203 | * delay per ear more important than volume changes 204 | * demo: some 3d sound? does it make sense to do in a large room? maybe not... 205 | ? probably won't work with the demo room 206 | 207 | --20-- 208 | * real time vs offline 209 | * how many ms to not feel a delay? was it 7ms? 210 | * better effects in offline processing, and can even look into future for cooler sounding effects (like a reverse echo) 211 | 212 | -- 21-- 213 | * closing slide 214 | * shadertoy -> lets you do audio 215 | * my blog -> more audio synth info there now and future content 216 | * where to get code from presentation -> https://github.com/Atrix256/MusicSynth 217 | * using audio synth for smooth terrain generation. 218 | * also dsp for other things. low pass filter movement data to smooth it out. 219 | 220 | -- stretch topics 221 | * mention 303 and 707 with a sound clip? 222 | * FFT / IFFT? do we care? maybe drop this 223 | * 8 bit music: I'm not an expert, but the sound isn't from being "8 bit". low sample rate starts to sound like it, but more in faked chords by playing very short notes in a row? 224 | * arpegiators 225 | * automatic note timing fixing (to get timing more correct) 226 | * converting timing (like in 4/4, im on note 3, bar 5) to be a sample number 227 | 228 | 229 | ============================================== 230 | Topics Brainstorm: 231 | * audio samples: the delta is what counts, not absolute value 232 | * amplitude vs db? 233 | * common wave forms (sine, square, saw, triangle) 234 | * popping (but useful in some sounds sometimes) 235 | * clipping (but useful for distortion sounds) 236 | * bandlimited wave forms and nyquist frequency 237 | * additive synthesis and mixing 238 | * vibrato & tremolo effects 239 | * AM / FM synth basics 240 | * basic percussion sounds 241 | * delay (echo) and reverb 242 | * flange 243 | * also quick mention of interpolation stuff? linear works for me but... 244 | * basic filtering and subtractive synthesis 245 | * dynamics processing (automatic volume adjustments) 246 | ? HDR audio stuff? 247 | * simulating in heroes. 248 | * real time playback vs offline concerns (speed, and buffering causing latency). 249 | * how many ms does a musician need to make something not feel laggy? was it 7ms? 250 | * positional audio? using delay per ear more than volume 251 | * envelopes 252 | * note sliding 253 | ? mention shadertoy lets you program audio? 254 | ! library / programs to do live and offline stuff 255 | ? mention fft type stuff? quickly, along with pros and cons? 256 | 257 | ! more info on my blog 258 | * http://blog.demofox.org/category/audio-synthesis/ 259 | --------------------------------------------------------------------------------