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 | |||||