Quellcode durchsuchen

Merge pull request #28 from el-supremo/master

Fixed AudioSynthWaveform. Uses LUT with linear interpolation for sinewav...
dds
Paul Stoffregen vor 10 Jahren
Ursprung
Commit
533ef61ed6
9 geänderte Dateien mit 452 neuen und 91 gelöschten Zeilen
  1. +24
    -1
      examples/PlaySynthMusic/PlaySynthMusic.h
  2. +31
    -22
      examples/PlaySynthMusic/PlaySynthMusic.ino
  3. +51
    -20
      examples/fir_filter/fir_filter.ino
  4. +4
    -17
      filter_fir.cpp
  5. +9
    -5
      filter_fir.h
  6. +18
    -0
      filter_fir.md
  7. +194
    -23
      synth_waveform.cpp
  8. +85
    -3
      synth_waveform.h
  9. +36
    -0
      synth_waveform.md

+ 24
- 1
examples/PlaySynthMusic/PlaySynthMusic.h Datei anzeigen

@@ -4,6 +4,7 @@
// Generated from Excel by =ROUND(2*440/32*(2^((x-9)/12)),0) for 0<x<128
// The lowest notes might not work, depending on the Arduino clock frequency

#ifdef ORIGINAL_TABLE
const unsigned int tune_frequencies2_PGM[128] =
{
16,17,18,19,21,22,23,24,26,28,29,31,33,35,37,39,41,
@@ -18,7 +19,29 @@ const unsigned int tune_frequencies2_PGM[128] =
13290,14080,14917,15804,16744,17740,18795,19912,21096,
22351,23680,25088
};

#else
// This is for the Teensy Audio library which specifies
// frequencies as floating point. See make_notetab.xlsx
const float tune_frequencies2_PGM[128] =
{
8.1758, 8.6620, 9.1770, 9.7227, 10.3009, 10.9134, 11.5623, 12.2499,
12.9783, 13.7500, 14.5676, 15.4339, 16.3516, 17.3239, 18.3540, 19.4454,
20.6017, 21.8268, 23.1247, 24.4997, 25.9565, 27.5000, 29.1352, 30.8677,
32.7032, 34.6478, 36.7081, 38.8909, 41.2034, 43.6535, 46.2493, 48.9994,
51.9131, 55.0000, 58.2705, 61.7354, 65.4064, 69.2957, 73.4162, 77.7817,
82.4069, 87.3071, 92.4986, 97.9989, 103.8262, 110.0000, 116.5409, 123.4708,
130.8128, 138.5913, 146.8324, 155.5635, 164.8138, 174.6141, 184.9972, 195.9977,
207.6523, 220.0000, 233.0819, 246.9417, 261.6256, 277.1826, 293.6648, 311.1270,
329.6276, 349.2282, 369.9944, 391.9954, 415.3047, 440.0000, 466.1638, 493.8833,
523.2511, 554.3653, 587.3295, 622.2540, 659.2551, 698.4565, 739.9888, 783.9909,
830.6094, 880.0000, 932.3275, 987.7666, 1046.5023, 1108.7305, 1174.6591, 1244.5079,
1318.5102, 1396.9129, 1479.9777, 1567.9817, 1661.2188, 1760.0000, 1864.6550, 1975.5332,
2093.0045, 2217.4610, 2349.3181, 2489.0159, 2637.0205, 2793.8259, 2959.9554, 3135.9635,
3322.4376, 3520.0000, 3729.3101, 3951.0664, 4186.0090, 4434.9221, 4698.6363, 4978.0317,
5274.0409, 5587.6517, 5919.9108, 6271.9270, 6644.8752, 7040.0000, 7458.6202, 7902.1328,
8372.0181, 8869.8442, 9397.2726, 9956.0635, 10548.0818, 11175.3034, 11839.8215, 12543.8540
};
#endif

#define CMD_PLAYNOTE 0x90 /* play a note: low nibble is generator #, note is next byte */
#define CMD_STOPNOTE 0x80 /* stop a note: low nibble is generator # */

+ 31
- 22
examples/PlaySynthMusic/PlaySynthMusic.ino Datei anzeigen

@@ -1,7 +1,6 @@
// Implement the midi player inside the Audio library.
// This uses the new version of the waveform generator code
// See PlayMidiTones for code which uses the old version

#include <Audio.h>
#include <Wire.h>
@@ -38,46 +37,57 @@ AudioSynthWaveform *waves[8] = {
&sine7,
};

// allocate a wave type to each channel.
// The types used and their order is purely arbitrary.
short wave_type[8] = {
TONE_TYPE_SINE,
TONE_TYPE_SQUARE,
TONE_TYPE_SAWTOOTH,
TONE_TYPE_TRIANGLE,
TONE_TYPE_SINE,
TONE_TYPE_SQUARE,
TONE_TYPE_SAWTOOTH,
TONE_TYPE_TRIANGLE,
};

// Two mixers are needed to handle the 8 channels of music
AudioMixer4 mixer1;
AudioMixer4 mixer2;
AudioOutputI2S audioOut;

// Mix the first four channels into mixer1
AudioConnection c0(sine0, 0, mixer1, 0);
AudioConnection c1(sine1, 0, mixer1, 1);
AudioConnection c2(sine2, 0, mixer1, 2);
AudioConnection c3(sine3, 0, mixer1, 3);

// and the last 4 channels into mixer2
AudioConnection c4(sine4, 0, mixer2, 0);
AudioConnection c5(sine5, 0, mixer2, 1);
AudioConnection c6(sine6, 0, mixer2, 2);
AudioConnection c7(sine7, 0, mixer2, 3);

// Output mixre1 to the left channel and mixer2 to the right
AudioConnection c11(mixer1, 0, audioOut, 0);
AudioConnection c12(mixer2, 0, audioOut, 1);

//AudioControl_WM8731 codec;
AudioControlSGTL5000 codec;

// Initial value of the volume control
int volume = 50;

// allocate a wave type to each channel.
// The types used and their order is purely arbitrary.
short wave_type[8] = {
TONE_TYPE_SINE,
TONE_TYPE_SQUARE,
TONE_TYPE_SAWTOOTH,
TONE_TYPE_TRIANGLE,
TONE_TYPE_SINE,
TONE_TYPE_SQUARE,
TONE_TYPE_SAWTOOTH,
TONE_TYPE_TRIANGLE,
};

void setup()
{
Serial.begin(115200);
while (!Serial) ;
delay(3000);

delay(2000);
// http://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
Serial.print("Begin ");
Serial.println(__FILE__);
// Proc = 12 (13), Mem = 2 (8)
// Audio connections require memory to work.
// The memory usage code indicates that 8 is the maximum
// so give it 10 just to be sure.
@@ -89,14 +99,11 @@ void setup()
// Comment this if you don't it
codec.unmuteLineout();

// Set the ramp time for each wave object
for(int i = 0; i < 8;i++) {
waves[i]->set_ramp_length(88);
}

Serial.println("Begin PlayMidiTones");

Serial.println("setup done");
// Initialize processor and memory measurements
@@ -111,8 +118,8 @@ void loop()
unsigned char c,opcode,chan;
unsigned long d_time;
// Change this to if(1) for measurement output
if(0) {
// Change this to if(1) for measurement output every 5 seconds
if(1) {
/*
For PlaySynthMusic this produces:
Proc = 20 (21), Mem = 2 (8)
@@ -138,6 +145,7 @@ if(0) {
codec.volume((float)n / 10.23);
}
// read the next note from the table
c = *sp++;
opcode = c & 0xf0;
// was 0x0f but I'm only handling 8 channels
@@ -163,7 +171,8 @@ if(0) {
return;
}
// Play the note on 'chan'
// Play the note on 'chan' - divide the frequency by two because the
// table of frequencies has been doubled.
if(opcode == CMD_PLAYNOTE) {
waves[chan]->begin(AMPLITUDE,tune_frequencies2_PGM[*sp++],
wave_type[chan]);

+ 51
- 20
examples/fir_filter/fir_filter.ino Datei anzeigen

@@ -1,4 +1,8 @@
/*
c
- released
b
- Use FIR filters with fast_fft option

The audio board uses the following pins.
6 - MEMCS
@@ -25,14 +29,15 @@ The audio board uses the following pins.
#include <Bounce.h>
#include "filters.h"

// If this pin is grounded the FIR filter is turned which
// makes just pass through the audio
// If this pin is grounded the FIR filter is turned off
// which just passes the audio sraight through
// Don't use any of the pins listed above
#define PASSTHRU_PIN 1
// If this pin goes low the next FIR filter in the list
// is switched in.
#define FILTER_PIN 0

// debounce the passthru and filter switching pins
Bounce b_passthru = Bounce(PASSTHRU_PIN,15);
Bounce b_filter = Bounce(FILTER_PIN,15);

@@ -41,16 +46,18 @@ const int myInput = AUDIO_INPUT_LINEIN;


AudioInputI2S audioInput; // audio shield: mic or line-in
AudioFilterFIR myFilter;
// Use the fast FIR filter for left and right channels
AudioFilterFIR myFilterL(true);
AudioFilterFIR myFilterR(true);
AudioOutputI2S audioOutput; // audio shield: headphones & line-out

// Create Audio connections between the components
// Both channels of the audio input go to the FIR filter
AudioConnection c1(audioInput, 0, myFilter, 0);
AudioConnection c2(audioInput, 1, myFilter, 1);
// both channels from the FIR filter go to the audio output
AudioConnection c3(myFilter, 0, audioOutput, 0);
AudioConnection c4(myFilter, 1, audioOutput, 1);
// Route audio into the left and right filters
AudioConnection c1(audioInput, 0, myFilterL, 0);
AudioConnection c2(audioInput, 1, myFilterR, 0);
// Route the output of the filters to their respective channels
AudioConnection c3(myFilterL, 0, audioOutput, 0);
AudioConnection c4(myFilterR, 0, audioOutput, 1);
AudioControlSGTL5000 audioShield;


@@ -62,9 +69,9 @@ struct fir_filter {
// index of current filter. Start with the low pass.
int fir_idx = 0;
struct fir_filter fir_list[] = {
low_pass , 100, // low pass with cutoff at 1kHz and -60dB at 2kHz
band_pass, 100, // bandpass 1200Hz - 1700Hz
NULL, 0
{low_pass , 100}, // low pass with cutoff at 1kHz and -60dB at 2kHz
{band_pass, 100}, // bandpass 1200Hz - 1700Hz
{NULL, 0}
};


@@ -98,8 +105,8 @@ void setup() {
Serial.println(") is grounded");
}
// Initialize the filter
myFilter.begin(fir_list[0].coeffs,fir_list[0].num_coeffs);
myFilterL.begin(fir_list[0].coeffs,fir_list[0].num_coeffs);
myFilterR.begin(fir_list[0].coeffs,fir_list[0].num_coeffs);
Serial.println("setup done");
}

@@ -108,7 +115,7 @@ int old_idx = -1;

// audio volume
int volume = 0;
unsigned long last_time = millis();
void loop()
{
// Volume control
@@ -122,18 +129,20 @@ void loop()
b_passthru.update();
b_filter.update();

// If the passthru button is pushed, save the current
// filter index and then switch the filter to passthru
if(b_passthru.fallingEdge()) {
old_idx = fir_idx;
myFilter.begin(FIR_PASSTHRU,0);
myFilterL.begin(FIR_PASSTHRU,0);
myFilterR.begin(FIR_PASSTHRU,0);
}
// If passthru button is released, restore previous filter
if(b_passthru.risingEdge()) {
if(old_idx != -1)myFilter.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
if(old_idx != -1) {
myFilterL.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
myFilterR.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
}
old_idx = -1;
}
@@ -141,9 +150,31 @@ void loop()
if(b_filter.fallingEdge()) {
fir_idx++;
if(fir_list[fir_idx].num_coeffs == 0)fir_idx = 0;
myFilter.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
myFilterL.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
myFilterR.begin(fir_list[fir_idx].coeffs,fir_list[fir_idx].num_coeffs);
}


if(1) {
// With fast_fir
// Proc = 18 (18), Mem = 4 (6)

if(millis() - last_time >= 5000) {
Serial.print("Proc = ");
Serial.print(AudioProcessorUsage());
Serial.print(" (");
Serial.print(AudioProcessorUsageMax());
Serial.print("), Mem = ");
Serial.print(AudioMemoryUsage());
Serial.print(" (");
Serial.print(AudioMemoryUsageMax());
Serial.println(")");
last_time = millis();
}
}


}




+ 4
- 17
filter_fir.cpp Datei anzeigen

@@ -29,7 +29,6 @@ void AudioFilterFIR::begin(short *cp,int n_coeffs)
// Initialize FIR instances for the left and right channels
if(coeff_p && (coeff_p != FIR_PASSTHRU)) {
arm_fir_init_q15(&l_fir_inst, n_coeffs, coeff_p, &l_StateQ15[0], AUDIO_BLOCK_SAMPLES);
arm_fir_init_q15(&r_fir_inst, n_coeffs, coeff_p, &r_StateQ15[0], AUDIO_BLOCK_SAMPLES);
}
}

@@ -55,11 +54,6 @@ void AudioFilterFIR::update(void)
transmit(block,0);
release(block);
}
block = receiveReadOnly(1);
if(block) {
transmit(block,1);
release(block);
}
return;
}
// Left Channel
@@ -67,22 +61,15 @@ void AudioFilterFIR::update(void)
// get a block for the FIR output
b_new = allocate();
if(block && b_new) {
arm_fir_q15(&l_fir_inst, (q15_t *)block->data, (q15_t *)b_new->data, AUDIO_BLOCK_SAMPLES);
if(arm_fast)
arm_fir_fast_q15(&l_fir_inst, (q15_t *)block->data, (q15_t *)b_new->data, AUDIO_BLOCK_SAMPLES);
else
arm_fir_q15(&l_fir_inst, (q15_t *)block->data, (q15_t *)b_new->data, AUDIO_BLOCK_SAMPLES);
// send the FIR output to the left channel
transmit(b_new,0);
}
if(block)release(block);
if(b_new)release(b_new);

// Right Channel
block = receiveReadOnly(1);
b_new = allocate();
if(block && b_new) {
arm_fir_q15(&r_fir_inst, (q15_t *)block->data, (q15_t *)b_new->data, AUDIO_BLOCK_SAMPLES);
transmit(b_new,1);
}
if(block)release(block);
if(b_new)release(b_new);
}



+ 9
- 5
filter_fir.h Datei anzeigen

@@ -26,6 +26,10 @@
#include "AudioStream.h"
#include "arm_math.h"

#define USE_FAST_FIR true
#define USE_SLOW_FIR false


// Maximum number of coefficients in a FIR filter
// The audio breaks up with 128 coefficients so a
// maximum of 150 is more than sufficient
@@ -39,12 +43,12 @@ class AudioFilterFIR :
public AudioStream
{
public:
AudioFilterFIR(void):
AudioStream(2,inputQueueArray), coeff_p(NULL)
AudioFilterFIR(const boolean a_f):
AudioStream(2,inputQueueArray), arm_fast(a_f), coeff_p(NULL)
{
}

void begin(short *coeff_p,int f_pin);
void begin(short *coeff_p,int n_coeffs);
virtual void update(void);
void stop(void);
@@ -54,11 +58,11 @@ private:
// the state arrays are defined to handle a maximum of MAX_COEFFS
// coefficients in a filter
q15_t l_StateQ15[AUDIO_BLOCK_SAMPLES + MAX_COEFFS];
q15_t r_StateQ15[AUDIO_BLOCK_SAMPLES + MAX_COEFFS];
arm_fir_instance_q15 l_fir_inst;
arm_fir_instance_q15 r_fir_inst;
// pointer to current coefficients or NULL or FIR_PASSTHRU
short *coeff_p;
// Whether to use the fast arm FIR code
const boolean arm_fast;
};

#endif

+ 18
- 0
filter_fir.md Datei anzeigen

@@ -0,0 +1,18 @@
filter_fir 140418
Filters the audio stream using FIR coefficients supplied by the user.
The ARM library has two q15 functions which perform an FIR filter. One uses
a 64-bit accumulator (arm_fir_q15) and the other uses a 32-bit accumulator
(arm_fir_fast_q15). When instantiating a filter a boolean argument specifies
which version to use. Specifying USE_FAST_FIR (defined as boolean true) uses
the fast code otherwise the slower code is used. For example:
AudioFilterFIR myFilterL(USE_FAST_FIR);

void begin(short *cp,int n_coeffs)
Starts the filter using coefficients at *cp and the number of coefficients
is n_coeffs. The special value FIR_PASSTHRU can be used in place of the
address of the coefficient array, in which case the function will just pass
audio samples through without filtering.

void stop(void)
Stops the filter.


+ 194
- 23
synth_waveform.cpp Datei anzeigen

@@ -29,9 +29,95 @@
#include "utility/dspinst.h"


#ifdef ORIGINAL_AUDIOSYNTHWAVEFORM
/******************************************************************/
// PAH - add ramp-up and ramp-down to the onset of the wave
// the length is specified in samples
void AudioSynthWaveform::set_ramp_length(uint16_t r_length)
{
if(r_length < 0) {
ramp_length = 0;
return;
}
// Don't set the ramp length longer than about 4 milliseconds
if(r_length > 44*4) {
ramp_length = 44*4;
return;
}
ramp_length = r_length;
}

void AudioSynthWaveform::update(void)
{
audio_block_t *block;
uint32_t i, ph, inc, index, scale;
int32_t val1, val2, val3;

//Serial.println("AudioSynthWaveform::update");
if (((magnitude > 0) || ramp_down) && (block = allocate()) != NULL) {
ph = phase;
inc = phase_increment;
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
index = ph >> 24;
val1 = wavetable[index];
val2 = wavetable[index+1];
scale = (ph >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0xFFFF - scale;
val3 = (val1 + val2) >> 16;

// The value of ramp_up is always initialized to RAMP_LENGTH and then is
// decremented each time through here until it reaches zero.
// The value of ramp_up is used to generate a Q15 fraction which varies
// from [0 - 1), and multiplies this by the current sample
if(ramp_up) {
// ramp up to the new magnitude
// ramp_mag is the Q15 representation of the fraction
// Since ramp_up can't be zero, this cannot generate +1
ramp_mag = ((ramp_length-ramp_up)<<15)/ramp_length;
ramp_up--;
block->data[i] = (val3 * ((ramp_mag * magnitude)>>15)) >> 15;

} else if(ramp_down) {
// ramp down to zero from the last magnitude
// The value of ramp_down is always initialized to RAMP_LENGTH and then is
// decremented each time through here until it reaches zero.
// The value of ramp_down is used to generate a Q15 fraction which varies
// from (1 - 0], and multiplies this by the current sample
// avoid RAMP_LENGTH/RAMP_LENGTH because Q15 format
// cannot represent +1
ramp_mag = ((ramp_down - 1)<<15)/ramp_length;
ramp_down--;
block->data[i] = (val3 * ((ramp_mag * last_magnitude)>>15)) >> 15;
} else {
block->data[i] = (val3 * magnitude) >> 15;
}

//Serial.print(block->data[i]);
//Serial.print(", ");
//if ((i % 12) == 11) Serial.println();
ph += inc;
}
//Serial.println();
phase = ph;
transmit(block);
release(block);
} else {
// is this numerical overflow ok?
phase += phase_increment * AUDIO_BLOCK_SAMPLES;
}
}

#else

/******************************************************************/
// PAH 140415 - change sin to use Paul's interpolation which is much
// faster than arm's sin function
// PAH 140316 - fix calculation of sample (amplitude error)
// PAH 140314 - change t_hi from int to float
// PAH - add ramp-up and ramp-down to the onset of the wave
// the length is specified in samples

void AudioSynthWaveform::set_ramp_length(int16_t r_length)
{
if(r_length < 0) {
@@ -47,17 +133,15 @@ void AudioSynthWaveform::set_ramp_length(int16_t r_length)
}


boolean AudioSynthWaveform::begin(float t_amp,int t_hi,short type)
boolean AudioSynthWaveform::begin(float t_amp,float t_hi,short type)
{
tone_type = type;
// tone_amp = t_amp;
amplitude(t_amp);
tone_freq = t_hi;
if(t_hi < 1)return false;
tone_freq = t_hi > 0.0;
if(t_hi <= 0.0)return false;
if(t_hi >= AUDIO_SAMPLE_RATE_EXACT/2)return false;
tone_phase = 0;
// tone_incr = (0x100000000LL*t_hi)/AUDIO_SAMPLE_RATE_EXACT;
tone_incr = (0x80000000LL*t_hi)/AUDIO_SAMPLE_RATE_EXACT;
frequency(t_hi);
if(0) {
Serial.print("AudioSynthWaveform.begin(tone_amp = ");
Serial.print(t_amp);
@@ -72,8 +156,6 @@ boolean AudioSynthWaveform::begin(float t_amp,int t_hi,short type)
return(true);
}

// PAH - 140313 fixed the calculation of the tone so that its spectrum
// is much improved
// PAH - 140313 fixed a problem with ramping
void AudioSynthWaveform::update(void)
{
@@ -81,10 +163,12 @@ void AudioSynthWaveform::update(void)
short *bp;
// temporary for ramp in sine
uint32_t ramp_mag;
int32_t val1, val2, val3;
uint32_t index, scale;
// temporaries for TRIANGLE
uint32_t mag;
short tmp_amp;

if(tone_freq == 0)return;
// L E F T C H A N N E L O N L Y
@@ -94,6 +178,14 @@ void AudioSynthWaveform::update(void)
switch(tone_type) {
case TONE_TYPE_SINE:
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
// Calculate interpolated sin
index = tone_phase >> 23;
val1 = AudioWaveformSine[index];
val2 = AudioWaveformSine[index+1];
scale = (tone_phase >> 7) & 0xFFFF;
val2 *= scale;
val1 *= 0xFFFF - scale;
val3 = (val1 + val2) >> 16;
// The value of ramp_up is always initialized to RAMP_LENGTH and then is
// decremented each time through here until it reaches zero.
// The value of ramp_up is used to generate a Q15 fraction which varies
@@ -106,8 +198,8 @@ void AudioSynthWaveform::update(void)
ramp_up--;
// adjust tone_phase to Q15 format and then adjust the result
// of the multiplication
// calculate the sample
tmp_amp = (short)((arm_sin_q15(tone_phase>>16) * tone_amp) >> 17);
// calculate the sample
tmp_amp = (short)((val3 * tone_amp) >> 15);
*bp++ = (tmp_amp * ramp_mag)>>15;
}
else if(ramp_down) {
@@ -120,15 +212,10 @@ void AudioSynthWaveform::update(void)
// cannot represent +1
ramp_mag = ((ramp_down - 1)<<15)/ramp_length;
ramp_down--;
// adjust tone_phase to Q15 format and then adjust the result
// of the multiplication
tmp_amp = (short)((arm_sin_q15(tone_phase>>16) * last_tone_amp) >> 17);
tmp_amp = (short)((val3 * last_tone_amp) >> 15);
*bp++ = (tmp_amp * ramp_mag)>>15;
} else {
// adjust tone_phase to Q15 format and then adjust the result
// of the multiplication
tmp_amp = (short)((arm_sin_q15(tone_phase>>16) * tone_amp) >> 17);
*bp++ = tmp_amp;
*bp++ = (short)((val3 * tone_amp) >> 15);
}
// phase and incr are both unsigned 32-bit fractions
@@ -140,7 +227,7 @@ void AudioSynthWaveform::update(void)
case TONE_TYPE_SQUARE:
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
if(tone_phase & 0x80000000)*bp++ = -tone_amp;
if(tone_phase & 0x40000000)*bp++ = -tone_amp;
else *bp++ = tone_amp;
// phase and incr are both unsigned 32-bit fractions
tone_phase += tone_incr;
@@ -149,10 +236,9 @@ void AudioSynthWaveform::update(void)
case TONE_TYPE_SAWTOOTH:
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
*bp = ((short)(tone_phase>>16)*tone_amp) >> 15;
bp++;
*bp++ = ((short)(tone_phase>>15)*tone_amp) >> 15;
// phase and incr are both unsigned 32-bit fractions
tone_phase += tone_incr;
tone_phase += tone_incr;
}
break;

@@ -173,7 +259,7 @@ void AudioSynthWaveform::update(void)
mag = ~mag + 1;
}
*bp++ = ((short)(mag>>17)*tmp_amp) >> 15;
tone_phase += tone_incr;
tone_phase += 2*tone_incr;
}
break;
}
@@ -182,3 +268,88 @@ void AudioSynthWaveform::update(void)
release(block);
}
}


#endif








#if 0
void AudioSineWaveMod::frequency(float f)
{
if (f > AUDIO_SAMPLE_RATE_EXACT / 2 || f < 0.0) return;
phase_increment = (f / AUDIO_SAMPLE_RATE_EXACT) * 4294967296.0f;
}

void AudioSineWaveMod::update(void)
{
audio_block_t *block, *modinput;
uint32_t i, ph, inc, index, scale;
int32_t val1, val2;

//Serial.println("AudioSineWave::update");
modinput = receiveReadOnly();
ph = phase;
inc = phase_increment;
block = allocate();
if (!block) {
// unable to allocate memory, so we'll send nothing
if (modinput) {
// but if we got modulation data, update the phase
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
ph += inc + modinput->data[i] * modulation_factor;
}
release(modinput);
} else {
ph += phase_increment * AUDIO_BLOCK_SAMPLES;
}
phase = ph;
return;
}
if (modinput) {
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
index = ph >> 24;
val1 = sine_table[index];
val2 = sine_table[index+1];
scale = (ph >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0xFFFF - scale;
block->data[i] = (val1 + val2) >> 16;
//Serial.print(block->data[i]);
//Serial.print(", ");
//if ((i % 12) == 11) Serial.println();
ph += inc + modinput->data[i] * modulation_factor;
}
release(modinput);
} else {
ph = phase;
inc = phase_increment;
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
index = ph >> 24;
val1 = sine_table[index];
val2 = sine_table[index+1];
scale = (ph >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0xFFFF - scale;
block->data[i] = (val1 + val2) >> 16;
//Serial.print(block->data[i]);
//Serial.print(", ");
//if ((i % 12) == 11) Serial.println();
ph += inc;
}
}
phase = ph;
transmit(block);
release(block);
}
#endif






+ 85
- 3
synth_waveform.h Datei anzeigen

@@ -30,6 +30,64 @@
#include "AudioStream.h"
#include "arm_math.h"

//#define ORIGINAL_AUDIOSYNTHWAVEFORM

#ifdef ORIGINAL_AUDIOSYNTHWAVEFORM
// waveforms.c
extern "C" {
extern const int16_t AudioWaveformSine[257];
extern const int16_t AudioWaveformTriangle[257];
extern const int16_t AudioWaveformSquare[257];
extern const int16_t AudioWaveformSawtooth[257];
}

class AudioSynthWaveform : public AudioStream
{
public:
AudioSynthWaveform(const int16_t *waveform)
: AudioStream(0, NULL), wavetable(waveform), magnitude(0), phase(0)
, ramp_down(0), ramp_up(0), ramp_mag(0), ramp_length(0)
{ }
void frequency(float freq) {
if (freq > AUDIO_SAMPLE_RATE_EXACT / 2 || freq < 0.0) return;
phase_increment = (freq / AUDIO_SAMPLE_RATE_EXACT) * 4294967296.0f;
}
void amplitude(float n) { // 0 to 1.0
if (n < 0) n = 0;
else if (n > 1.0) n = 1.0;
// Ramp code
if(magnitude && (n == 0)) {
ramp_down = ramp_length;
ramp_up = 0;
last_magnitude = magnitude;
}
else if((magnitude == 0) && n) {
ramp_up = ramp_length;
ramp_down = 0;
}
// set new magnitude
magnitude = n * 32767.0;
}
virtual void update(void);
void set_ramp_length(uint16_t r_length);
private:
const int16_t *wavetable;
uint16_t magnitude;
uint16_t last_magnitude;
uint32_t phase;
uint32_t phase_increment;
uint32_t ramp_down;
uint32_t ramp_up;
uint32_t ramp_mag;
uint16_t ramp_length;
};

#else
// waveforms.c
extern "C" {
extern const int16_t AudioWaveformSine[257];
}

#define AUDIO_SAMPLE_RATE_ROUNDED (44118)

@@ -51,9 +109,10 @@ public:
{
}
void frequency(int t_hi)
void frequency(float t_hi)
{
tone_incr = (0x80000000LL*t_hi)/AUDIO_SAMPLE_RATE_EXACT;
if (t_hi > AUDIO_SAMPLE_RATE_EXACT / 2 || t_hi < 0.0) return;
tone_incr = ((0x80000000LL*t_hi)/AUDIO_SAMPLE_RATE_EXACT) + 0.5;
}
// If ramp_length is non-zero this will set up
@@ -85,7 +144,7 @@ public:
tone_amp = n * 32767.0;
}
boolean begin(float t_amp,int t_hi,short t_type);
boolean begin(float t_amp,float t_hi,short t_type);
virtual void update(void);
void set_ramp_length(int16_t r_length);
@@ -102,4 +161,27 @@ private:
uint32_t ramp_up;
uint16_t ramp_length;
};

#endif



#if 0
class AudioSineWaveMod : public AudioStream
{
public:
AudioSineWaveMod() : AudioStream(1, inputQueueArray) {}
void frequency(float freq);
//void amplitude(q15 n);
virtual void update(void);
private:
uint32_t phase;
uint32_t phase_increment;
uint32_t modulation_factor;
audio_block_t *inputQueueArray[1];
};
#endif



#endif

+ 36
- 0
synth_waveform.md Datei anzeigen

@@ -0,0 +1,36 @@
synth_waveform 140404
This synthesizes a waveform of the specified type with a given amplitude
and frequency. There are currently four types of waveform:
#define TONE_TYPE_SINE 0
#define TONE_TYPE_SAWTOOTH 1
#define TONE_TYPE_SQUARE 2
#define TONE_TYPE_TRIANGLE 3
Sine wave generation uses a lookup table and linear interpolation.
The other three waveforms are generated directly without using table lookup.
boolean begin(float t_amp,float t_freq,short t_type)
This starts generation of a waveform of given type, amplitude and frequency
Example: begin(0.8,440.0,TONE_TYPE_SINE)
void set_ramp_length(int16_t r_length)
When a tone starts, or ends, playing it can generate an audible "thump" which can
be very distracting, especially when playing musical notes. This function specifies
a "ramp" length (in number of samples) and the beginning of the generated waveform
will be ramped up in volume from zero to t_amp over the course of r_length samples.
When the tone is switched off, by changing its volume to zero, instead of ending
abruptly it will be ramped down to zero over the next r_length samples.
For example, if r_length is 44, the beginning and end of the wave will have a ramp
of approximately one millisecond.
void frequency(float t_freq)
Changes the frequency of the wave to the specified t_freq. This is done in a phase-
continuous manner which should allow generation of audio frequency shift keying and
other effects requiring a changing frequency.
If the frequency is set to zero sample generation is stopped.
void amplitude(float n)
Changes the amplitude to 'n'. If 'n' is zero the wave is turned off and any further
audio output will be zero.



Laden…
Abbrechen
Speichern