Adding CPP/H implementation files, plus example sketch and update to HTML file.dds
| #include <Audio.h> | |||||
| #include <Wire.h> | |||||
| #include <SPI.h> | |||||
| #include <SD.h> | |||||
| #include <SerialFlash.h> | |||||
| #include <synth_simple_drum.h> | |||||
| #include <Audio.h> | |||||
| #include <Wire.h> | |||||
| #include <SPI.h> | |||||
| #include <SD.h> | |||||
| #include <SerialFlash.h> | |||||
| // GUItool: begin automatically generated code | |||||
| AudioSynthSimpleDrum drum2; //xy=399,244 | |||||
| AudioSynthSimpleDrum drum3; //xy=424,310 | |||||
| AudioSynthSimpleDrum drum1; //xy=431,197 | |||||
| AudioSynthSimpleDrum drum4; //xy=464,374 | |||||
| AudioMixer4 mixer1; //xy=737,265 | |||||
| AudioOutputI2S i2s1; //xy=979,214 | |||||
| AudioConnection patchCord1(drum2, 0, mixer1, 1); | |||||
| AudioConnection patchCord2(drum3, 0, mixer1, 2); | |||||
| AudioConnection patchCord3(drum1, 0, mixer1, 0); | |||||
| AudioConnection patchCord4(drum4, 0, mixer1, 3); | |||||
| AudioConnection patchCord5(mixer1, 0, i2s1, 0); | |||||
| AudioConnection patchCord6(mixer1, 0, i2s1, 1); | |||||
| AudioControlSGTL5000 sgtl5000_1; //xy=930,518 | |||||
| // GUItool: end automatically generated code | |||||
| static uint32_t next; | |||||
| void setup() { | |||||
| // put your setup code here, to run once: | |||||
| Serial.begin(115200); | |||||
| // audio library init | |||||
| AudioMemory(15); | |||||
| next = millis() + 1000; | |||||
| AudioNoInterrupts(); | |||||
| drum1.frequency(60); | |||||
| drum1.length(1500); | |||||
| drum1.secondMix(0.0); | |||||
| drum1.pitchMod(0.55); | |||||
| drum2.frequency(60); | |||||
| drum2.length(300); | |||||
| drum2.secondMix(0.0); | |||||
| drum2.pitchMod(1.0); | |||||
| drum3.frequency(550); | |||||
| drum3.length(400); | |||||
| drum3.secondMix(1.0); | |||||
| drum3.pitchMod(0.5); | |||||
| drum4.frequency(1200); | |||||
| drum4.length(150); | |||||
| drum4.secondMix(0.0); | |||||
| drum4.pitchMod(0.0); | |||||
| sgtl5000_1.enable(); | |||||
| sgtl5000_1.volume(0.5); | |||||
| AudioInterrupts(); | |||||
| } | |||||
| void loop() { | |||||
| // put your main code here, to run repeatedly: | |||||
| static uint32_t num = 0; | |||||
| if(millis() == next) | |||||
| { | |||||
| next = millis() + 1000; | |||||
| switch(num % 4) | |||||
| { | |||||
| case 0: | |||||
| drum1.noteOn(); | |||||
| break; | |||||
| case 1: | |||||
| drum2.noteOn(); | |||||
| break; | |||||
| case 2: | |||||
| drum3.noteOn(); | |||||
| break; | |||||
| case 3: | |||||
| drum4.noteOn(); | |||||
| break; | |||||
| } | |||||
| num++; | |||||
| Serial.print("Diagnostics: "); | |||||
| Serial.print(AudioProcessorUsageMax()); | |||||
| Serial.print(" "); | |||||
| Serial.println(AudioMemoryUsageMax()); | |||||
| AudioProcessorUsageMaxReset(); | |||||
| } | |||||
| } |
| {"type":"AudioPlaySerialflashRaw","data":{"defaults":{"name":{"value":"new"}},"shortName":"playFlashRaw","inputs":0,"outputs":1,"category":"play-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | {"type":"AudioPlaySerialflashRaw","data":{"defaults":{"name":{"value":"new"}},"shortName":"playFlashRaw","inputs":0,"outputs":1,"category":"play-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | ||||
| {"type":"AudioPlayQueue","data":{"defaults":{"name":{"value":"new"}},"shortName":"queue","inputs":0,"outputs":1,"category":"play-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | {"type":"AudioPlayQueue","data":{"defaults":{"name":{"value":"new"}},"shortName":"queue","inputs":0,"outputs":1,"category":"play-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | ||||
| {"type":"AudioRecordQueue","data":{"defaults":{"name":{"value":"new"}},"shortName":"queue","inputs":1,"outputs":0,"category":"record-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | {"type":"AudioRecordQueue","data":{"defaults":{"name":{"value":"new"}},"shortName":"queue","inputs":1,"outputs":0,"category":"record-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | ||||
| {"type":"AudioSynthSimpleDrum","data":{"defaults":{"name":{"value":"new"}},"shortName":"drum","inputs":0,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | |||||
| {"type":"AudioSynthWaveformSine","data":{"defaults":{"name":{"value":"new"}},"shortName":"sine","inputs":0,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | {"type":"AudioSynthWaveformSine","data":{"defaults":{"name":{"value":"new"}},"shortName":"sine","inputs":0,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | ||||
| {"type":"AudioSynthWaveformSineHires","data":{"defaults":{"name":{"value":"new"}},"shortName":"sine_hires","inputs":0,"outputs":2,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | {"type":"AudioSynthWaveformSineHires","data":{"defaults":{"name":{"value":"new"}},"shortName":"sine_hires","inputs":0,"outputs":2,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | ||||
| {"type":"AudioSynthWaveformSineModulated","data":{"defaults":{"name":{"value":"new"}},"shortName":"sine_fm","inputs":1,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | {"type":"AudioSynthWaveformSineModulated","data":{"defaults":{"name":{"value":"new"}},"shortName":"sine_fm","inputs":1,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | ||||
| </div> | </div> | ||||
| </script> | </script> | ||||
| <script type="text/x-red" data-help-name="AudioSynthSimpleDrum"> | |||||
| <h3>Summary</h3> | |||||
| <div class=tooltipinfo> | |||||
| <p>Generate a synthesised drum sound. Also useful for laser pistol and bursting | |||||
| bubble sound effects.</p> | |||||
| </div> | |||||
| <h3>Audio Connections</h3> | |||||
| <table class=doc align=center cellpadding=3> | |||||
| <tr class=top><th>Port</th><th>Purpose</th></tr> | |||||
| <tr class=odd><td align=center>Out 0</td><td>Drum Tone Output</td></tr> | |||||
| </table> | |||||
| <h3>Functions</h3> | |||||
| <p class=func><span class=keyword>noteOn</span>();</p> | |||||
| <p class=desc>Trigger the drum. | |||||
| </p> | |||||
| <p class=func><span class=keyword>frequency</span>(frequency);</p> | |||||
| <p class=desc>Set the base frequency of the drum. | |||||
| </p> | |||||
| <p class=func><span class=keyword>length</span>(milliseconds);</p> | |||||
| <p class=desc>Set the duration of the envelope, in milliseconds. | |||||
| </p> | |||||
| <p class=func><span class=keyword>secondMix</span>(level);</p> | |||||
| <p class=desc>Emulates a two-headed tom, by adding a second sine wave that is | |||||
| harmonized a perfect fifth above | |||||
| the base frequency. Using this involves a slight CPU penalty. | |||||
| </p> | |||||
| <p class=func><span class=keyword>pitchMod</span>(depth);</p> | |||||
| <p class=desc>Set the depth of envelope of the pitch, by a maximum of two octaves. | |||||
| Default is 0.5, with no modulation. Values above 0.5 cause the pitch to sweep | |||||
| downwards, values lower than 0.5 cause the pitch to sweep upwards. | |||||
| </p> | |||||
| <h3>Examples</h3> | |||||
| <p class=exam>File > Examples > Synthesis > SimpleDrumm | |||||
| </p> | |||||
| <h3>Notes</h3> | |||||
| <p></p> | |||||
| </script> | |||||
| <script type="text/x-red" data-template-name="AudioSynthSimpleDrum"> | |||||
| <div class="form-row"> | |||||
| <label for="node-input-name"><i class="fa fa-tag"></i> Name</label> | |||||
| <input type="text" id="node-input-name" placeholder="Name"> | |||||
| </div> | |||||
| </script> | |||||
| <script type="text/x-red" data-help-name="AudioSynthWaveformSineHires"> | <script type="text/x-red" data-help-name="AudioSynthWaveformSineHires"> | ||||
| <h3>Summary</h3> | <h3>Summary</h3> | ||||
| <div class=tooltipinfo> | <div class=tooltipinfo> |
| /* Audio Library for Teensy 3.X | |||||
| * Copyright (c) 2016, Byron Jacquot, SparkFun Electronics | |||||
| * | |||||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
| * open source software by purchasing Teensy or other PJRC products. | |||||
| * | |||||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| * of this software and associated documentation files (the "Software"), to deal | |||||
| * in the Software without restriction, including without limitation the rights | |||||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| * copies of the Software, and to permit persons to whom the Software is | |||||
| * furnished to do so, subject to the following conditions: | |||||
| * | |||||
| * The above copyright notice, development funding notice, and this permission | |||||
| * notice shall be included in all copies or substantial portions of the Software. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
| * THE SOFTWARE. | |||||
| */ | |||||
| #include "synth_simple_drum.h" | |||||
| extern "C" { | |||||
| extern const int16_t AudioWaveformSine[257]; | |||||
| } | |||||
| void AudioSynthSimpleDrum::noteOn(void) | |||||
| { | |||||
| __disable_irq(); | |||||
| wav_phasor = 0; | |||||
| wav_phasor2 = 0; | |||||
| env_lin_current = 0x7fff0000; | |||||
| __enable_irq(); | |||||
| } | |||||
| void AudioSynthSimpleDrum::secondMix(float level) | |||||
| { | |||||
| // As level goes from 0.0 to 1.0, | |||||
| // second goes from 0 to 1/2 scale | |||||
| // first goes from full scale to half scale. | |||||
| if(level < 0) | |||||
| { | |||||
| level = 0; | |||||
| } | |||||
| else if(level > 1.0) | |||||
| { | |||||
| level = 1.0; | |||||
| } | |||||
| __disable_irq(); | |||||
| wav_amplitude2 = level * 0x3fff; | |||||
| wav_amplitude1 = 0x7fff - wav_amplitude2; | |||||
| __enable_irq(); | |||||
| } | |||||
| void AudioSynthSimpleDrum::pitchMod(float depth) | |||||
| { | |||||
| int32_t intdepth, calc; | |||||
| // Validate parameter | |||||
| if(depth < 0) | |||||
| { | |||||
| depth = 0; | |||||
| } | |||||
| else if(depth > 1.0) | |||||
| { | |||||
| depth = 1.0; | |||||
| } | |||||
| // Depth is float, 0.0..1.0 | |||||
| // turn 0.0 to 1.0 into | |||||
| // 0x0 to 0x3fff; | |||||
| intdepth = depth * 0x7fff; | |||||
| // Lets turn it into 2.14, in range between -0.75 and 2.9999, woth 0 at 0.5 | |||||
| // It becomes the scalar for the modulation component of the phasor increment. | |||||
| if(intdepth < 0x4000) | |||||
| { | |||||
| // 0 to 0.5 becomes | |||||
| // -0x3000 (0xffffCfff) to 0 () | |||||
| calc = ((0x4000 - intdepth) * 0x3000 )>> 14; | |||||
| calc = -calc; | |||||
| } | |||||
| else | |||||
| { | |||||
| // 0.5 to 1.0 becomes | |||||
| // 0x00 to 0xbfa0 | |||||
| calc = ((intdepth - 0x4000) * 0xc000)>> 14; | |||||
| } | |||||
| // Call result 2.14 format (max of ~3.99...approx 4) | |||||
| // See note in update(). | |||||
| wav_pitch_mod = calc; | |||||
| } | |||||
| void AudioSynthSimpleDrum::update(void) | |||||
| { | |||||
| audio_block_t *block_wav; | |||||
| int16_t *p_wave, *end; | |||||
| int32_t sin_l, sin_r, interp, mod, mod2, delta; | |||||
| int32_t interp2; | |||||
| int32_t index, scale; | |||||
| bool do_second; | |||||
| int32_t env_sqr_current; // the square of the linear value - inexpensive quasi exponential decay. | |||||
| block_wav = allocate(); | |||||
| if (!block_wav) return; | |||||
| p_wave = (block_wav->data); | |||||
| end = p_wave + AUDIO_BLOCK_SAMPLES; | |||||
| // 50 is arbitrary threshold... | |||||
| // low values of second are inaudible, and we can save CPU cycles | |||||
| // by not calculating second when it's really quiet. | |||||
| do_second = (wav_amplitude2 > 50); | |||||
| while(p_wave < end) | |||||
| { | |||||
| // Do envelope first | |||||
| if(env_lin_current < 0x0000ffff) | |||||
| { | |||||
| // If envelope has expired, then stuff zeros into output buffer. | |||||
| *p_wave = 0; | |||||
| p_wave++; | |||||
| } | |||||
| else | |||||
| { | |||||
| env_lin_current -= env_decrement; | |||||
| env_sqr_current = multiply_16tx16t(env_lin_current, env_lin_current) ; | |||||
| // do wave second; | |||||
| wav_phasor += wav_increment; | |||||
| // modulation changes how we use the increment | |||||
| // the increment will be scaled by the modulation amount. | |||||
| // | |||||
| // Pitch mod is in range [-0.75 .. 3.99999] in 2.14 format | |||||
| // Current envelope value gets scaled by mod depth. | |||||
| // Then phasor increment gets scaled by that. | |||||
| mod = signed_multiply_32x16b((env_sqr_current), (wav_pitch_mod>>1)) >> 13; | |||||
| mod2 = signed_multiply_32x16b(wav_increment<<3, mod>>1); | |||||
| wav_phasor += (mod2); | |||||
| wav_phasor &= 0x7fffffff; | |||||
| if(do_second) | |||||
| { | |||||
| // A perfect fifth uses increment of 1.5 times regular increment | |||||
| wav_phasor2 += wav_increment; | |||||
| wav_phasor2 += (wav_increment >> 1); | |||||
| wav_phasor2 += mod2; | |||||
| wav_phasor2 += (mod2 >> 1); | |||||
| wav_phasor2 &= 0x7fffffff; | |||||
| } | |||||
| // Phase to Sine lookup * interp: | |||||
| index = wav_phasor >> 23; // take top valid 8 bits | |||||
| sin_l = AudioWaveformSine[index]; | |||||
| sin_r = AudioWaveformSine[index+1]; | |||||
| // The fraction of the phasor in time we are between L and R | |||||
| // is the same as the fraction of the ampliture of that interval we should add | |||||
| // to L. | |||||
| delta = sin_r-sin_l; | |||||
| scale = (wav_phasor >> 7) & 0xfFFF; | |||||
| delta = (delta * scale)>> 16; | |||||
| interp = sin_l + delta; | |||||
| if(do_second) | |||||
| { | |||||
| index = wav_phasor2 >> 23; // take top valid 8 bits | |||||
| sin_l = AudioWaveformSine[index]; | |||||
| sin_r = AudioWaveformSine[index+1]; | |||||
| delta = sin_r-sin_l; | |||||
| scale = (wav_phasor2 >> 7) & 0xFFFF; | |||||
| delta = (delta * scale)>> 16; | |||||
| interp2 = sin_l + delta; | |||||
| // Then scale and add the two waves | |||||
| interp2 = (interp2 * wav_amplitude2 ) >> 15; | |||||
| interp = (interp * wav_amplitude1) >> 15; | |||||
| interp = interp + interp2; | |||||
| } | |||||
| *p_wave = signed_multiply_32x16b(env_sqr_current, interp ) >> 15 ; | |||||
| p_wave++; | |||||
| } | |||||
| } | |||||
| transmit(block_wav, 0); | |||||
| release(block_wav); | |||||
| } | |||||
| /* Audio Library for Teensy 3.X | |||||
| * Copyright (c) 2016, Byron Jacquot, SparkFun Electronics | |||||
| * | |||||
| * Development of this audio library was funded by PJRC.COM, LLC by sales of | |||||
| * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop | |||||
| * open source software by purchasing Teensy or other PJRC products. | |||||
| * | |||||
| * Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
| * of this software and associated documentation files (the "Software"), to deal | |||||
| * in the Software without restriction, including without limitation the rights | |||||
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
| * copies of the Software, and to permit persons to whom the Software is | |||||
| * furnished to do so, subject to the following conditions: | |||||
| * | |||||
| * The above copyright notice, development funding notice, and this permission | |||||
| * notice shall be included in all copies or substantial portions of the Software. | |||||
| * | |||||
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
| * THE SOFTWARE. | |||||
| */ | |||||
| #pragma once | |||||
| #ifndef _SYNTH_SIMPLE_DRUM_H_ | |||||
| #define _SYNTH_SIMPLE_DRUM_H_ | |||||
| #include "AudioStream.h" | |||||
| #include "utility/dspinst.h" | |||||
| class AudioSynthSimpleDrum : public AudioStream | |||||
| { | |||||
| public: | |||||
| AudioSynthSimpleDrum() : AudioStream(1, inputQueueArray) | |||||
| { | |||||
| length(600); | |||||
| frequency(60); | |||||
| pitchMod(0x200); | |||||
| wav_amplitude1 = 0x7fff; | |||||
| wav_amplitude2 = 0; | |||||
| } | |||||
| void noteOn(); | |||||
| void frequency(float freq) | |||||
| { | |||||
| if(freq < 0.0) | |||||
| freq = 0; | |||||
| else if(freq > (AUDIO_SAMPLE_RATE_EXACT/2)) | |||||
| freq = AUDIO_SAMPLE_RATE_EXACT/2; | |||||
| wav_increment = (freq * (0x7fffffffLL/AUDIO_SAMPLE_RATE_EXACT)) + 0.5; | |||||
| } | |||||
| void length(int32_t milliseconds) | |||||
| { | |||||
| if(milliseconds < 0) | |||||
| return; | |||||
| if(milliseconds > 5000) | |||||
| milliseconds = 5000; | |||||
| int32_t len_samples = milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0); | |||||
| env_decrement = (0x7fff0000/len_samples); | |||||
| }; | |||||
| void secondMix(float level); | |||||
| void pitchMod(float depth); | |||||
| using AudioStream::release; | |||||
| virtual void update(void); | |||||
| private: | |||||
| audio_block_t *inputQueueArray[1]; | |||||
| // Envelope params | |||||
| int32_t env_lin_current; // present value of linear slope. | |||||
| int32_t env_decrement; // how each sample deviates from previous. | |||||
| // Waveform params | |||||
| uint32_t wav_phasor; | |||||
| uint32_t wav_phasor2; | |||||
| int16_t wav_amplitude1; | |||||
| int16_t wav_amplitude2; | |||||
| uint32_t wav_increment; | |||||
| int32_t wav_pitch_mod; | |||||
| }; | |||||
| #endif | |||||