@@ -0,0 +1,136 @@ | |||
// Waveform Modulation Example - Create waveforms with | |||
// modulated frequency | |||
// | |||
// This example is meant to be used with 3 buttons (pin 0, | |||
// 1, 2) and 2 knobs (pins 16/A2, 17/A3), which are present | |||
// on the audio tutorial kit. | |||
// https://www.pjrc.com/store/audio_tutorial_kit.html | |||
// | |||
// Use an oscilloscope to view the 2 waveforms. | |||
// | |||
// Button0 changes the waveform shape | |||
// | |||
// Knob A2 changes the amount of frequency modulation | |||
// | |||
// Knob A3 varies the shape (only for Pulse & Variable Triangle) | |||
// | |||
// This example code is in the public domain. | |||
#include <Audio.h> | |||
#include <Wire.h> | |||
#include <SPI.h> | |||
#include <SD.h> | |||
#include <SerialFlash.h> | |||
#include <Bounce.h> | |||
AudioSynthWaveformSine sine1; //xy=131,97 | |||
AudioSynthWaveformSine sine2; //xy=152,170 | |||
AudioSynthWaveformModulated waveformMod1; //xy=354,69 | |||
AudioOutputAnalogStereo dacs1; //xy=490,209 | |||
AudioOutputI2S i2s1; //xy=532,140 | |||
AudioConnection patchCord1(sine1, 0, i2s1, 1); | |||
AudioConnection patchCord2(sine1, 0, dacs1, 1); | |||
AudioConnection patchCord3(sine1, 0, waveformMod1, 0); | |||
AudioConnection patchCord4(sine2, 0, waveformMod1, 1); | |||
AudioConnection patchCord5(waveformMod1, 0, i2s1, 0); | |||
AudioConnection patchCord6(waveformMod1, 0, dacs1, 0); | |||
AudioControlSGTL5000 sgtl5000_1; //xy=286,240 | |||
Bounce button0 = Bounce(0, 15); | |||
Bounce button1 = Bounce(1, 15); | |||
Bounce button2 = Bounce(2, 15); | |||
int current_waveform=0; | |||
extern const int16_t myWaveform[256]; // defined in myWaveform.ino | |||
void setup() { | |||
Serial.begin(9600); | |||
pinMode(0, INPUT_PULLUP); | |||
pinMode(1, INPUT_PULLUP); | |||
pinMode(2, INPUT_PULLUP); | |||
delay(300); | |||
Serial.println("Waveform Modulation Test"); | |||
// Audio connections require memory to work. For more | |||
// detailed information, see the MemoryAndCpuUsage example | |||
AudioMemory(12); | |||
// Comment these out if not using the audio adaptor board. | |||
sgtl5000_1.enable(); | |||
sgtl5000_1.volume(0.8); // caution: very loud - use oscilloscope only! | |||
// Confirgure both to use "myWaveform" for WAVEFORM_ARBITRARY | |||
waveformMod1.arbitraryWaveform(myWaveform, 172.0); | |||
// Configure for middle C note without modulation | |||
waveformMod1.frequency(261.63); | |||
waveformMod1.amplitude(1.0); | |||
sine1.frequency(20.3); // Sine waves are low frequency oscillators (LFO) | |||
sine2.frequency(1.2); | |||
current_waveform = WAVEFORM_TRIANGLE_VARIABLE; | |||
waveformMod1.begin(current_waveform); | |||
// uncomment to try modulating phase instead of frequency | |||
//waveformMod1.phaseModulation(720.0); | |||
} | |||
void loop() { | |||
// Read the buttons and knobs, scale knobs to 0-1.0 | |||
button0.update(); | |||
button1.update(); | |||
button2.update(); | |||
float knob_A2 = (float)analogRead(A2) / 1023.0; | |||
float knob_A3 = (float)analogRead(A3) / 1023.0; | |||
// use Knobsto adjust the amount of modulation | |||
sine1.amplitude(knob_A2); | |||
sine2.amplitude(knob_A3); | |||
// Button 0 or 2 changes the waveform type | |||
if (button0.fallingEdge() || button2.fallingEdge()) { | |||
switch (current_waveform) { | |||
case WAVEFORM_SINE: | |||
current_waveform = WAVEFORM_SAWTOOTH; | |||
Serial.println("Sawtooth"); | |||
break; | |||
case WAVEFORM_SAWTOOTH: | |||
current_waveform = WAVEFORM_SAWTOOTH_REVERSE; | |||
Serial.println("Reverse Sawtooth"); | |||
break; | |||
case WAVEFORM_SAWTOOTH_REVERSE: | |||
current_waveform = WAVEFORM_SQUARE; | |||
Serial.println("Square"); | |||
break; | |||
case WAVEFORM_SQUARE: | |||
current_waveform = WAVEFORM_TRIANGLE; | |||
Serial.println("Triangle"); | |||
break; | |||
case WAVEFORM_TRIANGLE: | |||
current_waveform = WAVEFORM_TRIANGLE_VARIABLE; | |||
Serial.println("Variable Triangle"); | |||
break; | |||
case WAVEFORM_TRIANGLE_VARIABLE: | |||
current_waveform = WAVEFORM_ARBITRARY; | |||
Serial.println("Arbitary Waveform"); | |||
break; | |||
case WAVEFORM_ARBITRARY: | |||
current_waveform = WAVEFORM_PULSE; | |||
Serial.println("Pulse"); | |||
break; | |||
case WAVEFORM_PULSE: | |||
current_waveform = WAVEFORM_SAMPLE_HOLD; | |||
Serial.println("Sample & Hold"); | |||
break; | |||
case WAVEFORM_SAMPLE_HOLD: | |||
current_waveform = WAVEFORM_SINE; | |||
Serial.println("Sine"); | |||
break; | |||
} | |||
waveformMod1.begin(current_waveform); | |||
} | |||
} | |||
@@ -0,0 +1,47 @@ | |||
const int16_t myWaveform[256] = { | |||
0, 1895, 3748, 5545, 7278, 8934, 10506, 11984, 13362, 14634, | |||
15794, 16840, 17769, 18580, 19274, 19853, 20319, 20678, 20933, 21093, | |||
21163, 21153, 21072, 20927, 20731, 20492, 20221, 19929, 19625, 19320, | |||
19022, 18741, 18486, 18263, 18080, 17942, 17853, 17819, 17841, 17920, | |||
18058, 18254, 18507, 18813, 19170, 19573, 20017, 20497, 21006, 21538, | |||
22085, 22642, 23200, 23753, 24294, 24816, 25314, 25781, 26212, 26604, | |||
26953, 27256, 27511, 27718, 27876, 27986, 28049, 28068, 28047, 27989, | |||
27899, 27782, 27644, 27490, 27326, 27159, 26996, 26841, 26701, 26582, | |||
26487, 26423, 26392, 26397, 26441, 26525, 26649, 26812, 27012, 27248, | |||
27514, 27808, 28122, 28451, 28787, 29124, 29451, 29762, 30045, 30293, | |||
30495, 30643, 30727, 30738, 30667, 30509, 30254, 29897, 29433, 28858, | |||
28169, 27363, 26441, 25403, 24251, 22988, 21620, 20150, 18587, 16939, | |||
15214, 13423, 11577, 9686, 7763, 5820, 3870, 1926, 0, -1895, | |||
-3748, -5545, -7278, -8934,-10506,-11984,-13362,-14634,-15794,-16840, | |||
-17769,-18580,-19274,-19853,-20319,-20678,-20933,-21093,-21163,-21153, | |||
-21072,-20927,-20731,-20492,-20221,-19929,-19625,-19320,-19022,-18741, | |||
-18486,-18263,-18080,-17942,-17853,-17819,-17841,-17920,-18058,-18254, | |||
-18507,-18813,-19170,-19573,-20017,-20497,-21006,-21538,-22085,-22642, | |||
-23200,-23753,-24294,-24816,-25314,-25781,-26212,-26604,-26953,-27256, | |||
-27511,-27718,-27876,-27986,-28049,-28068,-28047,-27989,-27899,-27782, | |||
-27644,-27490,-27326,-27159,-26996,-26841,-26701,-26582,-26487,-26423, | |||
-26392,-26397,-26441,-26525,-26649,-26812,-27012,-27248,-27514,-27808, | |||
-28122,-28451,-28787,-29124,-29451,-29762,-30045,-30293,-30495,-30643, | |||
-30727,-30738,-30667,-30509,-30254,-29897,-29433,-28858,-28169,-27363, | |||
-26441,-25403,-24251,-22988,-21620,-20150,-18587,-16939,-15214,-13423, | |||
-11577, -9686, -7763, -5820, -3870, -1926 | |||
}; | |||
/* | |||
#! /usr/bin/perl | |||
$len = 256; | |||
print "const int16_t myWaveform[256] = {\n"; | |||
for ($i=0; $i < $len; $i++) { | |||
$x = $i / $len * 2 * 3.141592654; | |||
$r = $x - 0.12486762; | |||
$y = 0.95 * sin($r) + 0.25 * sin($r * 3 + 0.7) + 0.15 * sin($r * 5 - 5.4); | |||
#print "$x $y\n"; | |||
$d = sprintf "%.0f", $y * 32767.0; | |||
printf "%6d", $d + 0; | |||
print "," if ($i < $len-1); | |||
print "\n" if ($i % 10) == 9; | |||
} | |||
print "\n" unless ($len % 10) == 9; | |||
print "};\n"; | |||
*/ |
@@ -375,7 +375,7 @@ span.mainfunction {color: #993300; font-weight: bolder} | |||
{"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":"AudioSynthWaveform","data":{"defaults":{"name":{"value":"new"}},"shortName":"waveform","inputs":0,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | |||
{"type":"AudioSynthWaveformModulated","data":{"defaults":{"name":{"value":"new"}},"shortName":"waveform","inputs":2,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | |||
{"type":"AudioSynthWaveformModulated","data":{"defaults":{"name":{"value":"new"}},"shortName":"waveformMod","inputs":2,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | |||
{"type":"AudioSynthWaveformPWM","data":{"defaults":{"name":{"value":"new"}},"shortName":"pwm","inputs":1,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | |||
{"type":"AudioSynthToneSweep","data":{"defaults":{"name":{"value":"new"}},"shortName":"tonesweep","inputs":0,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | |||
{"type":"AudioSynthWaveformDc","data":{"defaults":{"name":{"value":"new"}},"shortName":"dc","inputs":0,"outputs":1,"category":"synth-function","color":"#E6E0F8","icon":"arrow-in.png"}}, | |||
@@ -1982,6 +1982,10 @@ The actual packets are taken | |||
<p class=func><span class=keyword>amplitude</span>(level);</p> | |||
<p class=desc>Change the amplitude. Set to 0 to turn the signal off. | |||
</p> | |||
<p class=func><span class=keyword>offset</span>(level);</p> | |||
<p class=desc>Add a DC offset, from -1.0 to +1.0. Useful for generating | |||
waveforms to use as control or modulation signals. | |||
</p> | |||
<p class=func><span class=keyword>phase</span>(angle);</p> | |||
<p class=desc> | |||
Cause the generated waveform to jump to a specific point within | |||
@@ -2002,6 +2006,8 @@ The actual packets are taken | |||
do this automatically. | |||
</p> | |||
<h3>Examples</h3> | |||
<p class=exam>File > Examples > Audio > Synthesis > Waveforms | |||
</p> | |||
<p class=exam>File > Examples > Audio > Synthesis > PlaySynthMusic | |||
</p> | |||
<p class=exam>File > Examples > Audio > Synthesis > pulseWidth | |||
@@ -2030,6 +2036,92 @@ The actual packets are taken | |||
</div> | |||
</script> | |||
<script type="text/x-red" data-help-name="AudioSynthWaveformModulated"> | |||
<h3>Summary</h3> | |||
<div class=tooltipinfo> | |||
<p>Create a waveform <b>with modulation</b>: sine, sawtooth, square, triangle, pulse, random S&H or arbitrary.</p> | |||
<p align=center><img src="img/waveformsmod.png"></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>In 0</td><td>Frequency or Phase</td></tr> | |||
<tr class=odd><td align=center>In 1</td><td>Shape (Pulse & Var Triangle)</td></tr> | |||
<tr class=odd><td align=center>Out 0</td><td>Waveform Output</td></tr> | |||
</table> | |||
<h3>Functions</h3> | |||
<p class=func><span class=keyword>begin</span>(waveform);</p> | |||
<p class=desc>Configure the waveform type to create. | |||
</p> | |||
<p class=func><span class=keyword>begin</span>(level, frequency, waveform);</p> | |||
<p class=desc>Output a waveform, and set the amplitude and base frequency. | |||
</p> | |||
<p class=func><span class=keyword>frequency</span>(freq);</p> | |||
<p class=desc>Change the base (unmodulated) frequency. | |||
</p> | |||
<p class=func><span class=keyword>amplitude</span>(level);</p> | |||
<p class=desc>Change the amplitude. Set to 0 to turn the signal off. | |||
</p> | |||
<p class=func><span class=keyword>offset</span>(level);</p> | |||
<p class=desc>Add a DC offset, from -1.0 to +1.0. Useful for generating | |||
waveforms to use as control or modulation signals. | |||
</p> | |||
<p class=func><span class=keyword>frequencyModulation</span>(octaves);</p> | |||
<p class=desc> | |||
Configure for frequency modulation mode (the default) where the | |||
input signal will adjust the frequency by a specific number of | |||
octaves (the default is 8 octaves). If the -1.0 to +1.0 signal | |||
represents a ±10 volt range and you wish to have control | |||
at 1 volt/octave, then configure for 10 octaves range. The | |||
maximum modulation sensitivity is 12 octaves. | |||
</p> | |||
<p class=func><span class=keyword>phaseModulation</span>(degrees);</p> | |||
<p class=desc> | |||
Configure for phase modulation mode where the input signal will | |||
adjust the waveform phase angle a specific number of degrees. | |||
180.0 allows a full scale ±1.0 signal to span 1 full | |||
cycle of the waveform. Maximum modulation sensitivity is 9000 | |||
degrees (±25 cycles). | |||
</p> | |||
<p class=func><span class=keyword>arbitraryWaveform</span>(array, maxFreq);</p> | |||
<p class=desc> | |||
Configure the waveform to be used with WAVEFORM_ARBITRARY. Array | |||
must be an array of 256 samples. Currently, the data is used | |||
without any filtering, which can cause aliasing with frequencies | |||
above 172 Hz. For higher frequency output, you must bandwidth | |||
limit your waveform data. Someday, "maxFreq" will be used to | |||
do this automatically. | |||
</p> | |||
<h3>Examples</h3> | |||
<p class=exam>File > Examples > Audio > Synthesis > WaveformsModulated | |||
</p> | |||
<h3>Notes</h3> | |||
<p>Supported Waveforms:<br> | |||
<ul> | |||
<li><span class=literal>WAVEFORM_SINE</span></li> | |||
<li><span class=literal>WAVEFORM_SAWTOOTH</span></li> | |||
<li><span class=literal>WAVEFORM_SAWTOOTH_REVERSE</span></li> | |||
<li><span class=literal>WAVEFORM_SQUARE</span></li> | |||
<li><span class=literal>WAVEFORM_TRIANGLE</span></li> | |||
<li><span class=literal>WAVEFORM_TRIANGLE_VARIABLE</span></li> | |||
<li><span class=literal>WAVEFORM_ARBITRARY</span></li> | |||
<li><span class=literal>WAVEFORM_PULSE</span></li> | |||
<li><span class=literal>WAVEFORM_SAMPLE_HOLD</span></li> | |||
</ul> | |||
</p> | |||
<p>The Sample & Hold waveform does not support phase modulation. | |||
Attempting to modulate its phase may give random or | |||
inconsistent results. Use only frequency modulation | |||
to vary the Sample & Hold waveform speed | |||
</p> | |||
</script> | |||
<script type="text/x-red" data-template-name="AudioSynthWaveformModulated"> | |||
<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="AudioSynthWaveformPWM"> | |||
<h3>Summary</h3> | |||
<div class=tooltipinfo> |