kd5rxt-mark
Well-known member
SUMMARY:
- I need help creating a functionally correct AudioSynthKarplusStrongModulated class that allows for a tuning control input
CHALLENGE:
- the standard AudioSynthKarplusStrong object does not have a tuning input
PROBLEMS ENCOUNTERED:
- my attempt at creating an AudioSynthKarplusStrongModulated object does not have a consistently clean "pluck" at the beginning like the standard AudioSynthKarplusStrong object does
- the sound from my attempt at creating an AudioSynthKarplusStrongModulated object sounds "pinched" in comparison to the sound from the standard AudioSynthKarplusStrong object
MY DESIRE:
- where/what should I be looking at to address the two problems identified above ??
REQUIRED HARDWARE:
- the test code listed below runs on the T4.x with the Audio Adapter connected (the pot connected to the A2 input needs to be installed/connected, as it is used for adjusting the tuning of the generated sound for the modulated objects)
IMPLEMENTATION DETAILS:
In my TeensyMIDIPolySynth (TMPS) project, the three voices each have individual tuning control inputs. In addition, there is an overall tuning control input that affects each of the three voices as well. The individual controls allow me to improve the width of the sound (by de-tuning the individual voices with respect to each other). The overall tuning control allows me to tune my TMPS to match physical/analog instruments (reed instruments, horns, keyboards, other synths, etc.).
In my TMPS, as part of the sound generating capabilities, each of the voices currently includes the use of the AudioSynthKarplusStrong audio object (implemented in the standard audio library, specifically in synth_karplusstrong.cpp) in addition to waveform & noise objects (other synth_*.cpp & effect_*.cpp files).
MAIN OBJECTIVE:
Unfortunately, the AudioSynthKarplusStrong implementation has no equivalent "tuning" capability like the AudioSynthWaveformModulated object, so I have undertaken the task of adding a tuneable string object (attempting to add an AudioSynthKarplusStrongModulated class). Unfortunately, I am in well over my head.
MY ATTEMPT AT CREATION (using the SynthWaveformModulated object as my guide):
So, here's my first stab at adding a AudioSynthKarplusStrongModulated class to synth_karplusstrong.h & synth_karplusstrong.cpp:
DEMO/TEST SKETCH:
Here's my test sketch to exercise this new capability (simple capabilities are controlled by way of the Serial Monitor):
I'd really appreciate any advice from anyone (@PaulStoffregen ??) who understands the intimate details of the audio library implementation.
Thanks in advance !!
Mark J Culross
KD5RXT
- I need help creating a functionally correct AudioSynthKarplusStrongModulated class that allows for a tuning control input
CHALLENGE:
- the standard AudioSynthKarplusStrong object does not have a tuning input
PROBLEMS ENCOUNTERED:
- my attempt at creating an AudioSynthKarplusStrongModulated object does not have a consistently clean "pluck" at the beginning like the standard AudioSynthKarplusStrong object does
- the sound from my attempt at creating an AudioSynthKarplusStrongModulated object sounds "pinched" in comparison to the sound from the standard AudioSynthKarplusStrong object
MY DESIRE:
- where/what should I be looking at to address the two problems identified above ??
REQUIRED HARDWARE:
- the test code listed below runs on the T4.x with the Audio Adapter connected (the pot connected to the A2 input needs to be installed/connected, as it is used for adjusting the tuning of the generated sound for the modulated objects)
IMPLEMENTATION DETAILS:
In my TeensyMIDIPolySynth (TMPS) project, the three voices each have individual tuning control inputs. In addition, there is an overall tuning control input that affects each of the three voices as well. The individual controls allow me to improve the width of the sound (by de-tuning the individual voices with respect to each other). The overall tuning control allows me to tune my TMPS to match physical/analog instruments (reed instruments, horns, keyboards, other synths, etc.).
In my TMPS, as part of the sound generating capabilities, each of the voices currently includes the use of the AudioSynthKarplusStrong audio object (implemented in the standard audio library, specifically in synth_karplusstrong.cpp) in addition to waveform & noise objects (other synth_*.cpp & effect_*.cpp files).
MAIN OBJECTIVE:
Unfortunately, the AudioSynthKarplusStrong implementation has no equivalent "tuning" capability like the AudioSynthWaveformModulated object, so I have undertaken the task of adding a tuneable string object (attempting to add an AudioSynthKarplusStrongModulated class). Unfortunately, I am in well over my head.
MY ATTEMPT AT CREATION (using the SynthWaveformModulated object as my guide):
So, here's my first stab at adding a AudioSynthKarplusStrongModulated class to synth_karplusstrong.h & synth_karplusstrong.cpp:
C++:
/* Audio Library for Teensy 3.X
* Copyright (c) 2016, 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.
*/
#ifndef synth_karplusstrong_h_
#define synth_karplusstrong_h_
#include <Arduino.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/Arduino.h
#include <AudioStream.h> // github.com/PaulStoffregen/cores/blob/master/teensy4/AudioStream.h
#include "utility/dspinst.h"
class AudioSynthKarplusStrong : public AudioStream
{
public:
AudioSynthKarplusStrong() : AudioStream(0, NULL) {
state = 0;
}
void noteOn(float frequency, float velocity) {
if (velocity > 1.0f) {
velocity = 0.0f;
} else if (velocity <= 0.0f) {
noteOff(1.0f);
return;
}
magnitude = velocity * 65535.0f;
int len = (AUDIO_SAMPLE_RATE_EXACT / frequency) + 0.5f;
if (len > 536) len = 536;
bufferLen = len;
bufferIndex = 0;
state = 1;
}
void noteOff(float velocity) {
state = 0;
}
virtual void update(void);
private:
uint8_t state; // 0=steady output, 1=begin on next update, 2=playing
uint16_t bufferLen;
uint16_t bufferIndex;
int32_t magnitude; // current output
static uint32_t seed; // must start at 1
int16_t buffer[536]; // TODO: dynamically use audio memory blocks
};
class AudioSynthKarplusStrongModulated : public AudioStream
{
public:
AudioSynthKarplusStrongModulated() : AudioStream(1, inputQueueArray),
phase_accumulator(0), phase_increment(0), modulation_factor(32768),
modulation_type(0) {
magnitude = 0;
state = 0;
}
void noteOn(float frequency, float velocity) {
if (velocity > 1.0f) {
velocity = 0.0f;
} else if (velocity <= 0.0f) {
noteOff(1.0f);
return;
}
if (frequency < 0.0f) {
frequency = 0.0;
} else if (frequency > AUDIO_SAMPLE_RATE_EXACT / 2.0f) {
frequency = AUDIO_SAMPLE_RATE_EXACT / 2.0f;
}
phase_increment = frequency * (4292967296.0f / AUDIO_SAMPLE_RATE_EXACT);
if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
magnitude = velocity * 65535.0f;
int len = (AUDIO_SAMPLE_RATE_EXACT / frequency) + 0.5f;
if (len > 536) len = 536;
bufferLen = len;
bufferIndex = 0;
state = 1;
}
void noteOff(float velocity) {
state = 0;
}
void frequencyModulation(float octaves) {
if (octaves > 12.0f) {
octaves = 12.0f;
} else if (octaves < 0.1f) {
octaves = 0.1f;
}
modulation_factor = octaves * 4096.0f;
modulation_type = 0;
}
void phaseModulation(float degrees) {
if (degrees > 9000.0f) {
degrees = 9000.0f;
} else if (degrees < 30.0f) {
degrees = 30.0f;
}
modulation_factor = degrees * (float)(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;
uint32_t phasedata[AUDIO_BLOCK_SAMPLES];
uint8_t modulation_type;
uint8_t state; // 0=steady output, 1=begin on next update, 2=playing
uint16_t bufferLen;
uint16_t bufferIndex;
int32_t magnitude; // current output
static uint32_t seed; // must start at 1
int16_t buffer[536]; // TODO: dynamically use audio memory blocks
};
#endif
C++:
/* Audio Library for Teensy 3.X
* Copyright (c) 2016, 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_karplusstrong.h"
#if defined(KINETISK) || defined(__IMXRT1062__)
static uint32_t pseudorand(uint32_t lo)
{
uint32_t hi;
hi = multiply_16bx16t(16807, lo); // 16807 * (lo >> 16)
lo = 16807 * (lo & 0xFFFF);
lo += (hi & 0x7FFF) << 16;
lo += hi >> 15;
lo = (lo & 0x7FFFFFFF) + (lo >> 31);
return lo;
}
#endif
void AudioSynthKarplusStrong::update(void)
{
#if defined(KINETISK) || defined(__IMXRT1062__)
audio_block_t *block;
if (state == 0) return;
if (state == 1) {
uint32_t lo = seed;
for (int i=0; i < bufferLen; i++) {
lo = pseudorand(lo);
buffer[i] = signed_multiply_32x16b(magnitude, lo);
}
seed = lo;
state = 2;
}
block = allocate();
if (!block) {
state = 0;
return;
}
int16_t prior;
if (bufferIndex > 0) {
prior = buffer[bufferIndex - 1];
} else {
prior = buffer[bufferLen - 1];
}
int16_t *data = block->data;
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
int16_t in = buffer[bufferIndex];
//int16_t out = (in * 32604 + prior * 32604) >> 16;
int16_t out = (in * 32686 + prior * 32686) >> 16;
//int16_t out = (in * 32768 + prior * 32768) >> 16;
*data++ = out;
buffer[bufferIndex] = out;
prior = in;
if (++bufferIndex >= bufferLen) bufferIndex = 0;
}
transmit(block);
release(block);
#endif
}
void AudioSynthKarplusStrongModulated::update(void)
{
#if defined(KINETISK) || defined(__IMXRT1062__)
audio_block_t *block, *moddata;
int16_t *bp;
int32_t val1, val2;
uint32_t i, ph, index, scale;
const uint32_t inc = phase_increment;
moddata = receiveReadOnly(0);
// Pre-compute the phase angle for every output sample for this update
ph = phase_accumulator;
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 = ((uint32_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 (state == 0) return;
if (state == 1) {
uint32_t lo = seed;
for (int i=0; i < bufferLen; i++) {
lo = pseudorand(lo);
buffer[i] = signed_multiply_32x16b(magnitude, lo);
}
seed = lo;
state = 2;
}
block = allocate();
if (!block) {
state = 0;
return;
}
int16_t prior;
if (bufferIndex > 0) {
prior = buffer[bufferIndex - 1];
} else {
prior = buffer[bufferLen - 1];
}
int16_t *data = block->data;
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
ph = phasedata[i];
index = ph >> 24;
val1 = buffer[index];
val2 = buffer[index+1];
scale = (ph >> 8) && 0xFFFF;
val2 *= scale;
val1 *= 0x10000 - scale;
*data++ = multiply_32x32_rshift32(val1 + val2, magnitude);
}
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
int16_t in = buffer[bufferIndex];
//int16_t out = (in * 32604 + prior * 32604) >> 16;
int16_t out = (in * 32686 + prior * 32686) >> 16;
//int16_t out = (in * 32768 + prior * 32768) >> 16;
*data++ = out;
buffer[bufferIndex] = out;
prior = in;
if (++bufferIndex >= bufferLen) bufferIndex = 0;
}
transmit(block, 0);
release(block);
#endif
}
uint32_t AudioSynthKarplusStrong::seed = 1;
uint32_t AudioSynthKarplusStrongModulated::seed = 1;
DEMO/TEST SKETCH:
Here's my test sketch to exercise this new capability (simple capabilities are controlled by way of the Serial Monitor):
Code:
// Teensy Modulated Strings Test - version 1.0 dated 20250331-1015
// - written by Mark J Culross (KD5RXT)
//
//
// Arduino IDE Configuration:
// Tools/Board: "Teensy 4.0"
// Tools/USB Type: "Serial"
// Tools/CPU Speed: "600MHz"
// Tools/Optimize: "Fastest"
// Tools/Keyboard Layout: "US English"
// Tools/Port: "COMx Serial (Teensy 4.0)"
//
//#define DISABLE_MENU // uncomment this line to prevent the menu from printing to the Serial Monitor
#define TITLE ("Teensy Modulated Strings Test")
#define VERDAT ("version 1.0 dated 20250331-1015")
#define AUTHOR ("written by Mark J Culross (KD5RXT)")
#include <Audio.h>
// GUItool: begin automatically generated code
AudioSynthWaveformDc tuner; //xy=160,250
AudioSynthWaveformModulated waveform_mod; //xy=360,250
AudioSynthKarplusStrongModulated strings_mod; //xy=370,370
AudioSynthWaveformModulated waveform; //xy=370,200
AudioSynthKarplusStrong strings; //xy=380,320
AudioMixer4 mixer; //xy=680,210
AudioOutputI2S i2s_out; //xy=890,210
AudioConnection patchCord1(tuner, 0, waveform_mod, 0);
AudioConnection patchCord2(tuner, 0, strings_mod, 0);
AudioConnection patchCord3(waveform, 0, mixer, 0);
AudioConnection patchCord4(waveform_mod, 0, mixer, 1);
AudioConnection patchCord5(strings, 0, mixer, 2);
AudioConnection patchCord6(strings_mod, 0, mixer, 3);
AudioConnection patchCord7(mixer, 0, i2s_out, 0);
AudioConnection patchCord8(mixer, 0, i2s_out, 1);
AudioControlSGTL5000 sgtl5000; //xy=700,80
// GUItool: end automatically generated code
typedef enum
{
SOUND_STRINGS = 0, SOUND_STRINGS_MODULATED, SOUND_WAVEFORM, SOUND_WAVEFORM_MODULATED,
} SOUND_TYPE;
SOUND_TYPE sound_type = SOUND_STRINGS_MODULATED;
#define TUNING_INPUT A1
#define CR 0x0d
#define LF 0x0a
#define TOGGLE_TIME_MILLIS 2000
unsigned long toggle_time = millis();
boolean toggle = false;
boolean sound_on = true;
// current setting of the tuning signal
int tuning = 0;
int previous_tuning = -1;
#define PLAY_THIS_NOTE FLOAT_NOTE_C5
#define LED_PIN 13
// MIDI note-to-frequency data from: http://tonalsoft.com/pub/news/pitch-bend.aspx
#define FLOAT_NOTE_C0 16.352
#define FLOAT_NOTE_CS0 17.324
#define FLOAT_NOTE_D0 18.354
#define FLOAT_NOTE_DS0 19.445
#define FLOAT_NOTE_E0 20.602
#define FLOAT_NOTE_F0 21.827
#define FLOAT_NOTE_FS0 23.125
#define FLOAT_NOTE_G0 24.500
#define FLOAT_NOTE_GS0 25.957
#define FLOAT_NOTE_A0 27.500
#define FLOAT_NOTE_AS0 29.135
#define FLOAT_NOTE_B0 30.868
#define FLOAT_NOTE_C1 32.703
#define FLOAT_NOTE_CS1 34.648
#define FLOAT_NOTE_D1 36.708
#define FLOAT_NOTE_DS1 38.891
#define FLOAT_NOTE_E1 41.203
#define FLOAT_NOTE_F1 43.654
#define FLOAT_NOTE_FS1 46.249
#define FLOAT_NOTE_G1 48.999
#define FLOAT_NOTE_GS1 51.913
#define FLOAT_NOTE_A1 55.000
#define FLOAT_NOTE_AS1 58.270
#define FLOAT_NOTE_B1 61.735
#define FLOAT_NOTE_C2 65.406
#define FLOAT_NOTE_CS2 69.296
#define FLOAT_NOTE_D2 73.416
#define FLOAT_NOTE_DS2 77.782
#define FLOAT_NOTE_E2 82.407
#define FLOAT_NOTE_F2 87.307
#define FLOAT_NOTE_FS2 92.499
#define FLOAT_NOTE_G2 97.999
#define FLOAT_NOTE_GS2 103.826
#define FLOAT_NOTE_A2 110.000
#define FLOAT_NOTE_AS2 116.541
#define FLOAT_NOTE_B2 123.471
#define FLOAT_NOTE_C3 130.813
#define FLOAT_NOTE_CS3 138.591
#define FLOAT_NOTE_D3 146.832
#define FLOAT_NOTE_DS3 155.563
#define FLOAT_NOTE_E3 164.814
#define FLOAT_NOTE_F3 174.614
#define FLOAT_NOTE_FS3 184.997
#define FLOAT_NOTE_G3 195.998
#define FLOAT_NOTE_GS3 207.652
#define FLOAT_NOTE_A3 220.000
#define FLOAT_NOTE_AS3 233.082
#define FLOAT_NOTE_B3 246.942
#define FLOAT_NOTE_C4 261.626
#define FLOAT_NOTE_CS4 277.183
#define FLOAT_NOTE_D4 293.665
#define FLOAT_NOTE_DS4 311.127
#define FLOAT_NOTE_E4 329.628
#define FLOAT_NOTE_F4 349.228
#define FLOAT_NOTE_FS4 369.994
#define FLOAT_NOTE_G4 391.995
#define FLOAT_NOTE_GS4 415.305
#define FLOAT_NOTE_A4 440.000
#define FLOAT_NOTE_AS4 466.164
#define FLOAT_NOTE_B4 493.883
#define FLOAT_NOTE_C5 523.251
#define FLOAT_NOTE_CS5 554.365
#define FLOAT_NOTE_D5 587.330
#define FLOAT_NOTE_DS5 622.254
#define FLOAT_NOTE_E5 659.255
#define FLOAT_NOTE_F5 698.456
#define FLOAT_NOTE_FS5 739.989
#define FLOAT_NOTE_G5 783.991
#define FLOAT_NOTE_GS5 830.609
#define FLOAT_NOTE_A5 880.000
#define FLOAT_NOTE_AS5 932.328
#define FLOAT_NOTE_B5 987.767
#define FLOAT_NOTE_C6 1046.502
#define FLOAT_NOTE_CS6 1108.731
#define FLOAT_NOTE_D6 1174.659
#define FLOAT_NOTE_DS6 1244.508
#define FLOAT_NOTE_E6 1318.510
#define FLOAT_NOTE_F6 1396.913
#define FLOAT_NOTE_FS6 1479.978
#define FLOAT_NOTE_G6 1567.982
#define FLOAT_NOTE_GS6 1661.219
#define FLOAT_NOTE_A6 1760.000
#define FLOAT_NOTE_AS6 1864.655
#define FLOAT_NOTE_B6 1975.533
#define FLOAT_NOTE_C7 2093.005
#define FLOAT_NOTE_CS7 2217.461
#define FLOAT_NOTE_D7 2349.318
#define FLOAT_NOTE_DS7 2489.016
#define FLOAT_NOTE_E7 2637.020
#define FLOAT_NOTE_F7 2793.826
#define FLOAT_NOTE_FS7 2959.955
#define FLOAT_NOTE_G7 3135.963
#define FLOAT_NOTE_GS7 3322.438
#define FLOAT_NOTE_A7 3520.000
#define FLOAT_NOTE_AS7 3729.310
#define FLOAT_NOTE_B7 3951.066
#define FLOAT_NOTE_C8 4186.009
#define FLOAT_NOTE_CS8 4434.922
#define FLOAT_NOTE_D8 4698.636
#define FLOAT_NOTE_DS8 4978.032
#define FLOAT_NOTE_E8 5274.041
#define FLOAT_NOTE_F8 5587.651
#define FLOAT_NOTE_FS8 5919.911
#define FLOAT_NOTE_G8 6271.927
#define FLOAT_NOTE_GS8 6644.875
#define FLOAT_NOTE_A8 7040.000
#define FLOAT_NOTE_AS8 7458.620
#define FLOAT_NOTE_B8 7902.133
#define FLOAT_NOTE_C9 8372.018
#define FLOAT_NOTE_CS9 8869.844
#define FLOAT_NOTE_D9 9397.273
#define FLOAT_NOTE_DS9 9956.063
#define FLOAT_NOTE_E9 10548.082
#define FLOAT_NOTE_F9 11175.303
#define FLOAT_NOTE_FS9 11839.822
#define FLOAT_NOTE_G9 12543.854
#define FLOAT_NOTE_GS9 13289.750
#define FLOAT_NOTE_A9 14080.000
#define FLOAT_NOTE_AS9 14917.240
#define FLOAT_NOTE_B9 15804.266
#define FLOAT_NOTE_C10 16744.036
// -----------------------------------------------------------------------------
void display_controls(void);
void loop();
void setup();
void display_controls(void)
{
#ifndef DISABLE_MENU
Serial.println("");
Serial.println("");
Serial.println(TITLE);
Serial.println(VERDAT);
Serial.println(AUTHOR);
Serial.println("");
Serial.println("MENU:");
Serial.println("");
if (sound_type == SOUND_STRINGS)
{
Serial.print(">> ");
} else {
Serial.print(" ");
}
Serial.println("1) : STRINGS");
if (sound_type == SOUND_STRINGS_MODULATED)
{
Serial.print(">> ");
} else {
Serial.print(" ");
}
Serial.println("2) : MODULATED: STRINGS");
if (sound_type == SOUND_WAVEFORM)
{
Serial.print(">> ");
} else {
Serial.print(" ");
}
Serial.println("3) : WAVEFORM");
if (sound_type == SOUND_WAVEFORM_MODULATED)
{
Serial.print(">> ");
} else {
Serial.print(" ");
}
Serial.println("4) : MODULATED: WAVEFORM");
Serial.println("");
Serial.print(" ");
Serial.println("T) : TOGGLE SOUND ON/OFF");
Serial.println("");
#endif
} // display_controls()
void loop()
{
boolean key_pressed = false;
if (Serial.available() > 0)
{
byte inchar = Serial.read();
switch (inchar)
{
case '1':
{
sound_type = SOUND_STRINGS;
key_pressed = true;
strings.noteOff(0);
strings_mod.noteOff(0);
waveform.amplitude(0.0f);
waveform_mod.amplitude(0.0f);
}
break;
case '2':
{
sound_type = SOUND_STRINGS_MODULATED;
key_pressed = true;
strings.noteOff(0);
strings_mod.noteOff(0);
waveform.amplitude(0.0f);
waveform_mod.amplitude(0.0f);
}
break;
case '3':
{
sound_type = SOUND_WAVEFORM;
key_pressed = true;
strings.noteOff(0);
strings_mod.noteOff(0);
waveform.amplitude(0.0f);
waveform_mod.amplitude(0.0f);
}
break;
case '4':
{
sound_type = SOUND_WAVEFORM_MODULATED;
key_pressed = true;
strings.noteOff(0);
strings_mod.noteOff(0);
waveform.amplitude(0.0f);
waveform_mod.amplitude(0.0f);
}
break;
case 't':
case 'T':
{
key_pressed = true;
sound_on = !sound_on;
if (!sound_on)
{
strings.noteOff(0);
strings_mod.noteOff(0);
waveform.amplitude(0.0f);
waveform_mod.amplitude(0.0f);
}
}
break;
default:
{
if (inchar != CR)
{
key_pressed = true;
if (inchar != LF)
{
Serial.println("...UNRECOGNIZED...");
}
}
}
}
}
tuning = (previous_tuning + analogRead(TUNING_INPUT)) / 2.0f;
if (tuning != previous_tuning)
{
previous_tuning = tuning;
tuner.amplitude((float)((tuning) / 511.0f) - 1.0);
}
if (key_pressed)
{
key_pressed = false;
display_controls();
}
if ((millis() - toggle_time) > TOGGLE_TIME_MILLIS)
{
toggle_time = millis();
toggle = !toggle;
if (sound_on)
{
AudioNoInterrupts();
if (toggle)
{
key_pressed = true;
switch (sound_type)
{
case SOUND_STRINGS:
{
Serial.println("STRINGS ON");
strings.noteOn(PLAY_THIS_NOTE, 1);
strings_mod.noteOff(0);
waveform.amplitude(0.0f);
waveform_mod.amplitude(0.0f);
}
break;
case SOUND_STRINGS_MODULATED:
{
Serial.println("MODULATED STRINGS ON");
strings.noteOff(0);
strings_mod.noteOn(PLAY_THIS_NOTE, 1);
waveform.amplitude(0.0f);
waveform_mod.amplitude(0.0f);
}
break;
case SOUND_WAVEFORM:
{
Serial.println("WAVEFORM ON");
strings.noteOff(0);
strings_mod.noteOff(0);
waveform.amplitude(1.0f);
waveform_mod.amplitude(0.0f);
}
break;
case SOUND_WAVEFORM_MODULATED:
{
Serial.println("MODULATED WAVEFORM ON");
strings.noteOff(0);
strings_mod.noteOff(0);
waveform.amplitude(0.0f);
waveform_mod.amplitude(1.0f);
}
break;
}
} else {
key_pressed = true;
Serial.println("SOUND OFF");
strings.noteOff(0);
strings_mod.noteOff(0);
waveform.amplitude(0.0f);
waveform_mod.amplitude(0.0f);
}
AudioInterrupts();
}
}
} // loop()
void setup()
{
Serial.begin(57600);
Serial.println(TITLE);
Serial.println(VERDAT);
Serial.println(AUTHOR);
Serial.println("");
Serial.println("");
pinMode(LED_PIN, OUTPUT);
AudioNoInterrupts();
AudioMemory(255);
waveform.begin(WAVEFORM_SINE);
waveform.frequency(PLAY_THIS_NOTE);
waveform.amplitude(0.0);
waveform_mod.begin(WAVEFORM_SINE);
waveform_mod.frequency(PLAY_THIS_NOTE);
waveform_mod.frequencyModulation(0.20f);
waveform_mod.amplitude(0.0);
strings.noteOff(0);
strings_mod.noteOff(0);
strings_mod.frequencyModulation(0.20f);
tuner.amplitude(0.0f);
mixer.gain(0, 0.125f);
mixer.gain(1, 0.125f);
mixer.gain(2, 1.0f);
mixer.gain(3, 1.0f);
sgtl5000.enable();
sgtl5000.unmuteHeadphone();
sgtl5000.volume(0.8);
AudioInterrupts();
display_controls();
} // setup()
// EOF PLACEHOLDER
I'd really appreciate any advice from anyone (@PaulStoffregen ??) who understands the intimate details of the audio library implementation.
Thanks in advance !!
Mark J Culross
KD5RXT