Removed old mozzi test code.

Added displaying name of chorus waveform in menu.
Added a copy of Teensy audio library SynthWaveform for adding a method for
changing the waveform at runtime. Don't know if this can also be done without
this method...
dev
Holger Wirtz 5 years ago
parent bd42d373c0
commit c2c284a14b
  1. 3
      MicroMDAEPiano.ino
  2. 46
      UI.hpp
  3. 89
      mozzi/DCfilter.h
  4. 50
      mozzi/DCfilter.ino
  5. 403
      synth_waveform_extended.cpp
  6. 221
      synth_waveform_extended.h

@ -37,6 +37,7 @@
#include "UI.hpp" #include "UI.hpp"
#include "midi_devices.hpp" #include "midi_devices.hpp"
#include "config.h" #include "config.h"
#include "synth_waveform_extended.h"
//************************************************************************************************* //*************************************************************************************************
//* GLOBAL VARIABLES //* GLOBAL VARIABLES
@ -56,7 +57,7 @@ AudioAmplifier volume_l;
AudioAmplifier inverter; AudioAmplifier inverter;
AudioEffectModulatedDelay modchorus_r; AudioEffectModulatedDelay modchorus_r;
AudioEffectModulatedDelay modchorus_l; AudioEffectModulatedDelay modchorus_l;
AudioSynthWaveform modulator; AudioSynthWaveformExtended modulator;
AudioConnection patchCord0(queue_r, peak_r); AudioConnection patchCord0(queue_r, peak_r);
AudioConnection patchCord1(queue_l, peak_l); AudioConnection patchCord1(queue_l, peak_l);
AudioConnection patchCord2(queue_r, freeverb_r); AudioConnection patchCord2(queue_r, freeverb_r);

@ -49,6 +49,7 @@
#include <Bounce.h> #include <Bounce.h>
#include "Encoder4.h" #include "Encoder4.h"
#include "config.h" #include "config.h"
#include "synth_waveform_extended.h"
LiquidCrystal_I2C lcd(LCD_I2C_ADDRESS, LCD_CHARS, LCD_LINES); LiquidCrystal_I2C lcd(LCD_I2C_ADDRESS, LCD_CHARS, LCD_LINES);
Encoder4 enc[NUM_ENCODER] = {Encoder4(ENC_L_PIN_A, ENC_L_PIN_B), Encoder4(ENC_R_PIN_A, ENC_R_PIN_B)}; Encoder4 enc[NUM_ENCODER] = {Encoder4(ENC_L_PIN_A, ENC_L_PIN_B), Encoder4(ENC_R_PIN_A, ENC_R_PIN_B)};
@ -123,7 +124,7 @@ extern void eeprom_config_write(uint8_t value);
extern AudioControlSGTL5000 sgtl5000_1; extern AudioControlSGTL5000 sgtl5000_1;
extern AudioEffectFreeverb freeverb_r; extern AudioEffectFreeverb freeverb_r;
extern AudioEffectFreeverb freeverb_l; extern AudioEffectFreeverb freeverb_l;
extern AudioSynthWaveform modulator; extern AudioSynthWaveformExtended modulator;
extern AudioEffectModulatedDelay modchorus_r; extern AudioEffectModulatedDelay modchorus_r;
extern AudioEffectModulatedDelay modchorus_l; extern AudioEffectModulatedDelay modchorus_l;
extern AudioMixer4 mixer_r; extern AudioMixer4 mixer_r;
@ -244,6 +245,23 @@ char* get_chorus_delay_value_text(void)
return (chorus_delay_value_text1); return (chorus_delay_value_text1);
} }
char chorus_waveform_value_text1[] = " ";
char* get_chorus_waveform_value_text(void)
{
switch (configuration.chorus_waveform)
{
case 1:
sprintf(chorus_waveform_value_text1, "TRIANGLE");
break;
case 2:
sprintf(chorus_waveform_value_text1, "SINE");
break;
default:
sprintf(chorus_waveform_value_text1, "TRIANGLE");
}
return (chorus_waveform_value_text1);
}
char midi_channel_value_text1[] = " "; char midi_channel_value_text1[] = " ";
char* get_midi_channel_value_text(void) char* get_midi_channel_value_text(void)
{ {
@ -435,14 +453,14 @@ const char effects_text6[] PROGMEM = "Comp. Limit";
const char effects_text7[] PROGMEM = "Comp. Threshold"; const char effects_text7[] PROGMEM = "Comp. Threshold";
const char effects_text8[] PROGMEM = "Comp. Attack"; const char effects_text8[] PROGMEM = "Comp. Attack";
const char effects_text9[] PROGMEM = "Comp. Decay"; const char effects_text9[] PROGMEM = "Comp. Decay";
const char effects_text10[] PROGMEM = "Rev. Roomsize"; const char effects_text10[] PROGMEM = "Reverb Roomsize";
const char effects_text11[] PROGMEM = "Rev. Damping"; const char effects_text11[] PROGMEM = "Reverb Damping";
const char effects_text12[] PROGMEM = "Rev. Level"; const char effects_text12[] PROGMEM = "Reverb Level";
const char effects_text13[] PROGMEM = "Chorus Freq."; const char effects_text13[] PROGMEM = "Chorus Freq.";
const char effects_text14[] PROGMEM = "Chorus Delay"; const char effects_text14[] PROGMEM = "Chorus Delay";
const char effects_text15[] PROGMEM = "Chorus Intens."; const char effects_text15[] PROGMEM = "Chorus Intens.";
const char effects_text16[] PROGMEM = "Chorus FB"; const char effects_text16[] PROGMEM = "Chorus Feedback";
const char effects_text17[] PROGMEM = "Chorus Wave"; const char effects_text17[] PROGMEM = "Chorus Waveform";
const char effects_text18[] PROGMEM = "Chorus Level"; const char effects_text18[] PROGMEM = "Chorus Level";
const char effects_text19[] PROGMEM = "Bass LR Level"; const char effects_text19[] PROGMEM = "Bass LR Level";
const char effects_text20[] PROGMEM = "Bass M Level"; const char effects_text20[] PROGMEM = "Bass M Level";
@ -629,7 +647,7 @@ LiquidMenu chorus_intensity_menu(lcd);
CHORUS_FEEDBACK MENU CHORUS_FEEDBACK MENU
******************************************/ ******************************************/
#define NUM_CHORUS_FEEDBACK_MENUS 1 #define NUM_CHORUS_FEEDBACK_MENUS 1
const char chorus_feedback_text1[] PROGMEM = "Chorus FB"; const char chorus_feedback_text1[] PROGMEM = "Chorus Feedback";
LiquidLine chorus_feedback_line1(1, 0, chorus_feedback_text1); LiquidLine chorus_feedback_line1(1, 0, chorus_feedback_text1);
LiquidLine chorus_feedback_line2(1, 1, configuration.chorus_feedback); LiquidLine chorus_feedback_line2(1, 1, configuration.chorus_feedback);
LiquidScreen chorus_feedback_screen; LiquidScreen chorus_feedback_screen;
@ -639,9 +657,9 @@ LiquidMenu chorus_feedback_menu(lcd);
CHORUS_WAVEFORM MENU CHORUS_WAVEFORM MENU
******************************************/ ******************************************/
#define NUM_CHORUS_WAVEFORM_MENUS 1 #define NUM_CHORUS_WAVEFORM_MENUS 1
const char chorus_waveform_text1[] PROGMEM = "Chorus Wave"; const char chorus_waveform_text1[] PROGMEM = "Chorus Waveform";
LiquidLine chorus_waveform_line1(1, 0, chorus_waveform_text1); LiquidLine chorus_waveform_line1(1, 0, chorus_waveform_text1);
LiquidLine chorus_waveform_line2(1, 1, configuration.chorus_waveform); LiquidLine chorus_waveform_line2(1, 1, get_chorus_waveform_value_text);
LiquidScreen chorus_waveform_screen; LiquidScreen chorus_waveform_screen;
LiquidMenu chorus_waveform_menu(lcd); LiquidMenu chorus_waveform_menu(lcd);
@ -2144,18 +2162,18 @@ void set_chorus_waveform(uint8_t value)
{ {
#ifdef SHOW_DEBUG #ifdef SHOW_DEBUG
Serial.print(F("Set CHORUS_WAVEFORM ")); Serial.print(F("Set CHORUS_WAVEFORM "));
//Serial.println(value); Serial.println(value);
Serial.println("=> Setting CHORUS_WAVEFORM not yet available!");
#endif #endif
switch (value) switch (value)
{ {
case 1: case 1:
//modulator.waveform(WAVEFORM_TRIANGLE); modulator.waveform(WAVEFORM_TRIANGLE);
break; break;
case 2: case 2:
//modulator.waveform(WAVEFORM_SINE); modulator.waveform(WAVEFORM_SINE);
break;
default: default:
//modulator.waveform(WAVEFORM_TRIANGLE); modulator.waveform(WAVEFORM_TRIANGLE);
break; break;
} }
configuration.chorus_waveform = value; configuration.chorus_waveform = value;

@ -1,89 +0,0 @@
/*
* DCfilter.h
*
* Copyright 2012 Tim Barrass.
*
* This file is part of Mozzi.
*
* Mozzi is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
*
*/
#ifndef DCFILTER_H
#define DCFILTER_H
/*
tb2010 adapted from:
robert bristow-johnson, DSP Trick: Fixed-Point DC Blocking Filter with Noise-Shaping
http://www.dspguru.com/book/export/html/126
y[n] = x[n] - x[n-1] + a * y[n-1]
Where y[n] is the output at the current time n, and x[n] is the input at the current time n.
also, see DC Blocker Algorithms, http://www.ingelec.uns.edu.ar/pds2803/materiales/articulos/04472252.pdf
*/
/**
A DC-blocking filter useful for highlighting changes in control signals.
The output of the filter settles to 0 if the incoming signal stays constant. If the input changes, the
filter output swings to track the change and eventually settles back to 0.
*/
class DCfilter
{
public:
/**
Instantiate a DC-blocking filter.
@param pole sets the responsiveness of the filter,
how long it takes to settle to 0 if the input signal levels out at a constant value.
*/
DCfilter(float pole):acc(0),prev_x(0),prev_y(0)
{
A = (int)(32768.0*(1.0 - pole));
}
/* almost original
// timing: 20us
int next(int x)
{
setPin13High();
acc -= prev_x;
prev_x = (long)x<<15;
acc += prev_x;
acc -= A*prev_y;
prev_y = acc>>15; // quantization happens here
int filtered = (int)prev_y;
// acc has y[n] in upper 17 bits and -e[n] in lower 15 bits
setPin13Low();
return filtered;
}
*/
/**
Filter the incoming value and return the result.
@param x the value to filter
@return filtered signal
*/
// timing :8us
inline
int next(int x)
{
acc += ((long)(x-prev_x)<<16)>>1;
prev_x = x;
acc -= (long)A*prev_y; // acc has y[n] in upper 17 bits and -e[n] in lower 15 bits
prev_y = (acc>>16)<<1; // faster than >>15 but loses bit 0
if (acc & 32784) prev_y += 1; // adds 1 if it was in the 0 bit position lost in the shifts above
return prev_y;
}
private:
long acc;
int prev_x, prev_y,A;
};
/**
@example 05.Control_Filters/DCFilter/DCFilter.ino
This example demonstrates the DCFilter class.
*/
#endif // #ifndef DCFILTER_H

@ -1,50 +0,0 @@
/* Example of filtering an analog input to remove DC bias,
using Mozzi sonification library.
Demonstrates DCfilter(), DC-blocking filter useful for
highlighting changes in control signals.
The output of the filter settles to 0 if the incoming signal stays constant.
If the input changes, the filter output swings to track the change and
eventually settles back to 0.
Mozzi documentation/API
https://sensorium.github.io/Mozzi/doc/html/index.html
Mozzi help/discussion/announcements:
https://groups.google.com/forum/#!forum/mozzi-users
Tim Barrass 2013, CC by-nc-sa.
*/
#include <MozziGuts.h>
#include <DCfilter.h>
int sensorPin = A0;
DCfilter dcFiltered(0.9); // parameter sets how long the filter takes to settle
void setup() {
//Serial.begin(9600); // for Teensy 3.1, beware printout can cause glitches
Serial.begin(115200);
startMozzi();
}
void updateControl(){
// read the value from the sensor:
int sensorValue = mozziAnalogRead(sensorPin);
Serial.print(sensorValue);
Serial.print(" Filtered = ");
Serial.println(dcFiltered.next(sensorValue));
}
int updateAudio(){
return 0;
}
void loop(){
audioHook();
}

@ -0,0 +1,403 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2018, Paul Stoffregen, paul@pjrc.com
*
* 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 <Arduino.h>
#include "synth_waveform_extended.h"
#include "arm_math.h"
#include "utility/dspinst.h"
// uncomment for more accurate but more computationally expensive frequency modulation
//#define IMPROVE_EXPONENTIAL_ACCURACY
void AudioSynthWaveformExtended::update(void)
{
audio_block_t *block;
int16_t *bp, *end;
int32_t val1, val2;
int16_t magnitude15;
uint32_t i, ph, index, index2, scale;
const uint32_t inc = phase_increment;
ph = phase_accumulator + phase_offset;
if (magnitude == 0) {
phase_accumulator += inc * AUDIO_BLOCK_SAMPLES;
return;
}
block = allocate();
if (!block) {
phase_accumulator += inc * AUDIO_BLOCK_SAMPLES;
return;
}
bp = block->data;
switch(tone_type) {
case WAVEFORM_SINE:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
index = ph >> 24;
val1 = AudioWaveformSine[index];
val2 = AudioWaveformSine[index+1];
scale = (ph >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0x10000 - scale;
*bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
ph += inc;
}
break;
case WAVEFORM_ARBITRARY:
if (!arbdata) {
release(block);
phase_accumulator += inc * AUDIO_BLOCK_SAMPLES;
return;
}
// len = 256
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
index = ph >> 24;
index2 = index + 1;
if (index2 >= 256) index2 = 0;
val1 = *(arbdata + index);
val2 = *(arbdata + index2);
scale = (ph >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0x10000 - scale;
*bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
ph += inc;
}
break;
case WAVEFORM_SQUARE:
magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
if (ph & 0x80000000) {
*bp++ = -magnitude15;
} else {
*bp++ = magnitude15;
}
ph += inc;
}
break;
case WAVEFORM_SAWTOOTH:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
*bp++ = signed_multiply_32x16t(magnitude, ph);
ph += inc;
}
break;
case WAVEFORM_SAWTOOTH_REVERSE:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
*bp++ = signed_multiply_32x16t(0xFFFFFFFFu - magnitude, ph);
ph += inc;
}
break;
case WAVEFORM_TRIANGLE:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
uint32_t phtop = ph >> 30;
if (phtop == 1 || phtop == 2) {
*bp++ = ((0xFFFF - (ph >> 15)) * magnitude) >> 16;
} else {
*bp++ = (((int32_t)ph >> 15) * magnitude) >> 16;
}
ph += inc;
}
break;
case WAVEFORM_TRIANGLE_VARIABLE:
do {
uint32_t rise = 0xFFFFFFFF / (pulse_width >> 16);
uint32_t fall = 0xFFFFFFFF / (0xFFFF - (pulse_width >> 16));
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
if (ph < pulse_width/2) {
uint32_t n = (ph >> 16) * rise;
*bp++ = ((n >> 16) * magnitude) >> 16;
} else if (ph < 0xFFFFFFFF - pulse_width/2) {
uint32_t n = 0x7FFFFFFF - (((ph - pulse_width/2) >> 16) * fall);
*bp++ = (((int32_t)n >> 16) * magnitude) >> 16;
} else {
uint32_t n = ((ph + pulse_width/2) >> 16) * rise + 0x80000000;
*bp++ = (((int32_t)n >> 16) * magnitude) >> 16;
}
ph += inc;
}
} while (0);
break;
case WAVEFORM_PULSE:
magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
if (ph < pulse_width) {
*bp++ = magnitude15;
} else {
*bp++ = -magnitude15;
}
ph += inc;
}
break;
case WAVEFORM_SAMPLE_HOLD:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
*bp++ = sample;
uint32_t newph = ph + inc;
if (newph < ph) {
sample = random(magnitude) - (magnitude >> 1);
}
ph = newph;
}
break;
}
phase_accumulator = ph - phase_offset;
if (tone_offset) {
bp = block->data;
end = bp + AUDIO_BLOCK_SAMPLES;
do {
val1 = *bp;
*bp++ = signed_saturate_rshift(val1 + tone_offset, 16, 0);
} while (bp < end);
}
transmit(block, 0);
release(block);
}
//--------------------------------------------------------------------------------
void AudioSynthWaveformExtendedModulated::update(void)
{
audio_block_t *block, *moddata, *shapedata;
int16_t *bp, *end;
int32_t val1, val2;
int16_t magnitude15;
uint32_t i, ph, index, index2, scale, priorphase;
const uint32_t inc = phase_increment;
moddata = receiveReadOnly(0);
shapedata = receiveReadOnly(1);
// Pre-compute the phase angle for every output sample of this update
ph = phase_accumulator;
priorphase = phasedata[AUDIO_BLOCK_SAMPLES-1];
if (moddata && modulation_type == 0) {
// Frequency Modulation
bp = moddata->data;
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
int32_t n = (*bp++) * modulation_factor; // n is # of octaves to mod
int32_t ipart = n >> 27; // 4 integer bits
n &= 0x7FFFFFF; // 27 fractional bits
#ifdef IMPROVE_EXPONENTIAL_ACCURACY
// exp2 polynomial suggested by Stefan Stenzel on "music-dsp"
// mail list, Wed, 3 Sep 2014 10:08:55 +0200
int32_t x = n << 3;
n = multiply_accumulate_32x32_rshift32_rounded(536870912, x, 1494202713);
int32_t sq = multiply_32x32_rshift32_rounded(x, x);
n = multiply_accumulate_32x32_rshift32_rounded(n, sq, 1934101615);
n = n + (multiply_32x32_rshift32_rounded(sq,
multiply_32x32_rshift32_rounded(x, 1358044250)) << 1);
n = n << 1;
#else
// exp2 algorithm by Laurent de Soras
// https://www.musicdsp.org/en/latest/Other/106-fast-exp2-approximation.html
n = (n + 134217728) << 3;
n = multiply_32x32_rshift32_rounded(n, n);
n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;
n = n + 715827882;
#endif
uint32_t scale = n >> (14 - ipart);
uint64_t phstep = (uint64_t)inc * scale;
uint32_t phstep_msw = phstep >> 32;
if (phstep_msw < 0x7FFE) {
ph += phstep >> 16;
} else {
ph += 0x7FFE0000;
}
phasedata[i] = ph;
}
release(moddata);
} else if (moddata) {
// Phase Modulation
bp = moddata->data;
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
// more than +/- 180 deg shift by 32 bit overflow of "n"
uint32_t n = (uint16_t)(*bp++) * modulation_factor;
phasedata[i] = ph + n;
ph += inc;
}
release(moddata);
} else {
// No Modulation Input
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
phasedata[i] = ph;
ph += inc;
}
}
phase_accumulator = ph;
// If the amplitude is zero, no output, but phase still increments properly
if (magnitude == 0) {
if (shapedata) release(shapedata);
return;
}
block = allocate();
if (!block) {
if (shapedata) release(shapedata);
return;
}
bp = block->data;
// Now generate the output samples using the pre-computed phase angles
switch(tone_type) {
case WAVEFORM_SINE:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
ph = phasedata[i];
index = ph >> 24;
val1 = AudioWaveformSine[index];
val2 = AudioWaveformSine[index+1];
scale = (ph >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0x10000 - scale;
*bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
}
break;
case WAVEFORM_ARBITRARY:
if (!arbdata) {
release(block);
if (shapedata) release(shapedata);
return;
}
// len = 256
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
ph = phasedata[i];
index = ph >> 24;
index2 = index + 1;
if (index2 >= 256) index2 = 0;
val1 = *(arbdata + index);
val2 = *(arbdata + index2);
scale = (ph >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0x10000 - scale;
*bp++ = multiply_32x32_rshift32(val1 + val2, magnitude);
}
break;
case WAVEFORM_PULSE:
if (shapedata) {
magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
uint32_t width = ((shapedata->data[i] + 0x8000) & 0xFFFF) << 16;
if (phasedata[i] < width) {
*bp++ = magnitude15;
} else {
*bp++ = -magnitude15;
}
}
break;
} // else fall through to orginary square without shape modulation
case WAVEFORM_SQUARE:
magnitude15 = signed_saturate_rshift(magnitude, 16, 1);
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
if (phasedata[i] & 0x80000000) {
*bp++ = -magnitude15;
} else {
*bp++ = magnitude15;
}
}
break;
case WAVEFORM_SAWTOOTH:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
*bp++ = signed_multiply_32x16t(magnitude, phasedata[i]);
}
break;
case WAVEFORM_SAWTOOTH_REVERSE:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
*bp++ = signed_multiply_32x16t(0xFFFFFFFFu - magnitude, phasedata[i]);
}
break;
case WAVEFORM_TRIANGLE_VARIABLE:
if (shapedata) {
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
uint32_t width = (shapedata->data[i] + 0x8000) & 0xFFFF;
uint32_t rise = 0xFFFFFFFF / width;
uint32_t fall = 0xFFFFFFFF / (0xFFFF - width);
uint32_t halfwidth = width << 15;
uint32_t n;
ph = phasedata[i];
if (ph < halfwidth) {
n = (ph >> 16) * rise;
*bp++ = ((n >> 16) * magnitude) >> 16;
} else if (ph < 0xFFFFFFFF - halfwidth) {
n = 0x7FFFFFFF - (((ph - halfwidth) >> 16) * fall);
*bp++ = (((int32_t)n >> 16) * magnitude) >> 16;
} else {
n = ((ph + halfwidth) >> 16) * rise + 0x80000000;
*bp++ = (((int32_t)n >> 16) * magnitude) >> 16;
}
ph += inc;
}
break;
} // else fall through to orginary triangle without shape modulation
case WAVEFORM_TRIANGLE:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
ph = phasedata[i];
uint32_t phtop = ph >> 30;
if (phtop == 1 || phtop == 2) {
*bp++ = ((0xFFFF - (ph >> 15)) * magnitude) >> 16;
} else {
*bp++ = (((int32_t)ph >> 15) * magnitude) >> 16;
}
}
break;
case WAVEFORM_SAMPLE_HOLD:
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
ph = phasedata[i];
if (ph < priorphase) { // does not work for phase modulation
sample = random(magnitude) - (magnitude >> 1);
}
priorphase = ph;
*bp++ = sample;
}
break;
}
if (tone_offset) {
bp = block->data;
end = bp + AUDIO_BLOCK_SAMPLES;
do {
val1 = *bp;
*bp++ = signed_saturate_rshift(val1 + tone_offset, 16, 0);
} while (bp < end);
}
if (shapedata) release(shapedata);
transmit(block, 0);
release(block);
}

@ -0,0 +1,221 @@
/* Audio Library for Teensy 3.X
Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
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.
*/
/*
Extension for setting the waveform while running by H. Wirtz <wirtz@parasitstudio.de>
*/
#ifndef synth_waveform_extended_h_
#define synth_waveform_extended_h_
#include <Arduino.h>
#include "AudioStream.h"
#include "arm_math.h"
// waveforms.c
extern "C" {
extern const int16_t AudioWaveformSine[257];
}
#define WAVEFORM_SINE 0
#define WAVEFORM_SAWTOOTH 1
#define WAVEFORM_SQUARE 2
#define WAVEFORM_TRIANGLE 3
#define WAVEFORM_ARBITRARY 4
#define WAVEFORM_PULSE 5
#define WAVEFORM_SAWTOOTH_REVERSE 6
#define WAVEFORM_SAMPLE_HOLD 7
#define WAVEFORM_TRIANGLE_VARIABLE 8
class AudioSynthWaveformExtended : public AudioStream
{
public:
AudioSynthWaveformExtended(void) : AudioStream(0, NULL),
phase_accumulator(0), phase_increment(0), phase_offset(0),
magnitude(0), pulse_width(0x40000000),
arbdata(NULL), sample(0), tone_type(WAVEFORM_SINE),
tone_offset(0) {
}
void frequency(float freq) {
if (freq < 0.0) {
freq = 0.0;
} else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) {
freq = AUDIO_SAMPLE_RATE_EXACT / 2;
}
phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT);
if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
}
void phase(float angle) {
if (angle < 0.0) {
angle = 0.0;
} else if (angle > 360.0) {
angle = angle - 360.0;
if (angle >= 360.0) return;
}
phase_offset = angle * (4294967296.0 / 360.0);
}
void amplitude(float n) { // 0 to 1.0
if (n < 0) {
n = 0;
} else if (n > 1.0) {
n = 1.0;
}
magnitude = n * 65536.0;
}
void offset(float n) {
if (n < -1.0) {
n = -1.0;
} else if (n > 1.0) {
n = 1.0;
}
tone_offset = n * 32767.0;
}
void pulseWidth(float n) { // 0.0 to 1.0
if (n < 0) {
n = 0;
} else if (n > 1.0) {
n = 1.0;
}
pulse_width = n * 4294967296.0;
}
void waveform(short t_type) {
phase_offset = 0;
tone_type = t_type;
}
void begin(short t_type) {
phase_offset = 0;
tone_type = t_type;
}
void begin(float t_amp, float t_freq, short t_type) {
amplitude(t_amp);
frequency(t_freq);
phase_offset = 0;
tone_type = t_type;
}
void arbitraryWaveform(const int16_t *data, float maxFreq) {
arbdata = data;
}
virtual void update(void);
private:
uint32_t phase_accumulator;
uint32_t phase_increment;
uint32_t phase_offset;
int32_t magnitude;
uint32_t pulse_width;
const int16_t *arbdata;
int16_t sample; // for WAVEFORM_SAMPLE_HOLD
short tone_type;
int16_t tone_offset;
};
class AudioSynthWaveformExtendedModulated : public AudioStream
{
public:
AudioSynthWaveformExtendedModulated(void) : AudioStream(2, inputQueueArray),
phase_accumulator(0), phase_increment(0), modulation_factor(32768),
magnitude(0), arbdata(NULL), sample(0), tone_offset(0),
tone_type(WAVEFORM_SINE), modulation_type(0) {
}
void frequency(float freq) {
if (freq < 0.0) {
freq = 0.0;
} else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) {
freq = AUDIO_SAMPLE_RATE_EXACT / 2;
}
phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT);
if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
}
void amplitude(float n) { // 0 to 1.0
if (n < 0) {
n = 0;
} else if (n > 1.0) {
n = 1.0;
}
magnitude = n * 65536.0;
}
void offset(float n) {
if (n < -1.0) {
n = -1.0;
} else if (n > 1.0) {
n = 1.0;
}
tone_offset = n * 32767.0;
}
void waveform(short t_type) {
tone_type = t_type;
}
void begin(short t_type) {
tone_type = t_type;
}
void begin(float t_amp, float t_freq, short t_type) {
amplitude(t_amp);
frequency(t_freq);
tone_type = t_type;
}
void arbitraryWaveform(const int16_t *data, float maxFreq) {
arbdata = data;
}
void frequencyModulation(float octaves) {
if (octaves > 12.0) {
octaves = 12.0;
} else if (octaves < 0.1) {
octaves = 0.1;
}
modulation_factor = octaves * 4096.0;
modulation_type = 0;
}
void phaseModulation(float degrees) {
if (degrees > 9000.0) {
degrees = 9000.0;
} else if (degrees < 30.0) {
degrees = 30.0;
}
modulation_factor = degrees * (65536.0 / 180.0);
modulation_type = 1;
}
virtual void update(void);
private:
audio_block_t *inputQueueArray[2];
uint32_t phase_accumulator;
uint32_t phase_increment;
uint32_t modulation_factor;
int32_t magnitude;
const int16_t *arbdata;
uint32_t phasedata[AUDIO_BLOCK_SAMPLES];
int16_t sample; // for WAVEFORM_SAMPLE_HOLD
int16_t tone_offset;
uint8_t tone_type;
uint8_t modulation_type;
};
#endif
Loading…
Cancel
Save