Browse Source

Adding SimpleDrum synthesis object to audio library.

Adding CPP/H implementation files, plus example sketch and update to
HTML file.
dds
Jacquot-SFE 8 years ago
parent
commit
4d3f56dd0e
4 changed files with 453 additions and 0 deletions
  1. +104
    -0
      examples/Synthesis/SimpleDrum/SimpleDrum.ino
  2. +46
    -0
      gui/index.html
  3. +208
    -0
      synth_simple_drum.cpp
  4. +95
    -0
      synth_simple_drum.h

+ 104
- 0
examples/Synthesis/SimpleDrum/SimpleDrum.ino View File

@@ -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();
}
}

+ 46
- 0
gui/index.html View File

@@ -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 &gt; Examples &gt; Synthesis &gt; 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>

+ 208
- 0
synth_simple_drum.cpp View File

@@ -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);

}


+ 95
- 0
synth_simple_drum.h View File

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


Loading…
Cancel
Save