Adding CPP/H implementation files, plus example sketch and update to HTML file.dds
@@ -0,0 +1,104 @@ | |||
#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(); | |||
} | |||
} |
@@ -361,6 +361,7 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
{"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":"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":"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"}}, | |||
@@ -1336,6 +1337,51 @@ The actual packets are taken | |||
</div> | |||
</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"> | |||
<h3>Summary</h3> | |||
<div class=tooltipinfo> |
@@ -0,0 +1,208 @@ | |||
/* 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); | |||
} | |||
@@ -0,0 +1,95 @@ | |||
/* 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 | |||