Browse Source

Move other objects to their own files

dds
PaulStoffregen 10 years ago
parent
commit
f16fb938bf
22 changed files with 1990 additions and 1951 deletions
  1. +0
    -1951
      Audio.cpp
  2. +130
    -0
      analyze_fft256.cpp
  3. +133
    -0
      analyze_tonedetect.cpp
  4. +0
    -0
      control_sgtl5000.cpp
  5. +0
    -0
      control_wm8731.cpp
  6. +0
    -0
      data_ulaw.c
  7. +0
    -0
      data_waveforms.c
  8. +0
    -0
      data_windows.c
  9. +176
    -0
      effect_chorus.cpp
  10. +75
    -0
      effect_fade.cpp
  11. +249
    -0
      effect_flange.cpp
  12. +79
    -0
      filter_biquad.cpp
  13. +70
    -0
      filter_fir.cpp
  14. +0
    -0
      input_adc.cpp
  15. +78
    -0
      mixer.cpp
  16. +0
    -0
      output_dac.cpp
  17. +0
    -0
      output_i2s.cpp
  18. +0
    -0
      output_pwm.cpp
  19. +165
    -0
      play_memory.cpp
  20. +68
    -0
      play_sd_raw.cpp
  21. +450
    -0
      play_sd_wav.cpp
  22. +317
    -0
      synth_waveform.cpp

+ 0
- 1951
Audio.cpp
File diff suppressed because it is too large
View File


+ 130
- 0
analyze_fft256.cpp View File

@@ -0,0 +1,130 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"



static arm_cfft_radix4_instance_q15 fft_inst;

void AudioAnalyzeFFT256::init(void)
{
// TODO: replace this with static const version
arm_cfft_radix4_init_q15(&fft_inst, 256, 0, 1);

//for (int i=0; i<2048; i++) {
//buffer[i] = i * 3;
//}
//__disable_irq();
//ARM_DEMCR |= ARM_DEMCR_TRCENA;
//ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
//uint32_t n = ARM_DWT_CYCCNT;
//arm_cfft_radix2_q15(&fft_inst, buffer);
//n = ARM_DWT_CYCCNT - n;
//__enable_irq();
//cycles = n;
//arm_cmplx_mag_q15(buffer, buffer, 512);

// each audio block is 278525 cycles @ 96 MHz
// 256 point fft2 takes 65408 cycles
// 256 point fft4 takes 49108 cycles
// 128 point cmag takes 10999 cycles
// 1024 point fft2 takes 125948 cycles
// 1024 point fft4 takes 125840 cycles
// 512 point cmag takes 43764 cycles

//state = 0;
//outputflag = false;
}

static void copy_to_fft_buffer(void *destination, const void *source)
{
const int16_t *src = (const int16_t *)source;
int16_t *dst = (int16_t *)destination;

// TODO: optimize this
for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
*dst++ = *src++; // real
*dst++ = 0; // imaginary
}
}

static void apply_window_to_fft_buffer(void *buffer, const void *window)
{
int16_t *buf = (int16_t *)buffer;
const int16_t *win = (int16_t *)window;;

for (int i=0; i < 256; i++) {
int32_t val = *buf * *win++;
//*buf = signed_saturate_rshift(val, 16, 15);
*buf = val >> 15;
buf += 2;
}

}

void AudioAnalyzeFFT256::update(void)
{
audio_block_t *block;

block = receiveReadOnly();
if (!block) return;
if (!prevblock) {
prevblock = block;
return;
}
copy_to_fft_buffer(buffer, prevblock->data);
copy_to_fft_buffer(buffer+256, block->data);
//window = AudioWindowBlackmanNuttall256;
//window = NULL;
if (window) apply_window_to_fft_buffer(buffer, window);
arm_cfft_radix4_q15(&fft_inst, buffer);
// TODO: is this averaging correct? G. Heinzel's paper says we're
// supposed to average the magnitude squared, then do the square
// root at the end after dividing by naverage.
arm_cmplx_mag_q15(buffer, buffer, 128);
if (count == 0) {
for (int i=0; i < 128; i++) {
output[i] = buffer[i];
}
} else {
for (int i=0; i < 128; i++) {
output[i] += buffer[i];
}
}
if (++count == naverage) {
count = 0;
for (int i=0; i < 128; i++) {
output[i] /= naverage;
}
outputflag = true;
}

release(prevblock);
prevblock = block;

#if 0
block = receiveReadOnly();
if (state == 0) {
//Serial.print("0");
if (block == NULL) return;
copy_to_fft_buffer(buffer, block->data);
state = 1;
} else if (state == 1) {
//Serial.print("1");
if (block == NULL) return;
copy_to_fft_buffer(buffer+256, block->data);
arm_cfft_radix4_q15(&fft_inst, buffer);
state = 2;
} else {
//Serial.print("2");
arm_cmplx_mag_q15(buffer, output, 128);
outputflag = true;
if (block == NULL) return;
copy_to_fft_buffer(buffer, block->data);
state = 1;
}
release(block);
#endif
}



+ 133
- 0
analyze_tonedetect.cpp View File

@@ -0,0 +1,133 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"



static inline int32_t multiply_32x32_rshift30(int32_t a, int32_t b) __attribute__((always_inline));
static inline int32_t multiply_32x32_rshift30(int32_t a, int32_t b)
{
return ((int64_t)a * (int64_t)b) >> 30;
}

//#define TONE_DETECT_FAST

void AudioAnalyzeToneDetect::update(void)
{
audio_block_t *block;
int32_t q0, q1, q2, coef;
const int16_t *p, *end;
uint16_t n;

block = receiveReadOnly();
if (!block) return;
if (!enabled) {
release(block);
return;
}
p = block->data;
end = p + AUDIO_BLOCK_SAMPLES;
n = count;
coef = coefficient;
q1 = s1;
q2 = s2;
do {
// the Goertzel algorithm is kinda magical ;-)
#ifdef TONE_DETECT_FAST
q0 = (*p++) + (multiply_32x32_rshift32_rounded(coef, q1) << 2) - q2;
#else
q0 = (*p++) + multiply_32x32_rshift30(coef, q1) - q2;
// TODO: is this only 1 cycle slower? if so, always use it
#endif
q2 = q1;
q1 = q0;
if (--n == 0) {
out1 = q1;
out2 = q2;
q1 = 0; // TODO: does clearing these help or hinder?
q2 = 0;
new_output = true;
n = length;
}
} while (p < end);
count = n;
s1 = q1;
s2 = q2;
release(block);
}

void AudioAnalyzeToneDetect::set_params(int32_t coef, uint16_t cycles, uint16_t len)
{
__disable_irq();
coefficient = coef;
ncycles = cycles;
length = len;
count = len;
s1 = 0;
s2 = 0;
enabled = true;
__enable_irq();
//Serial.printf("Tone: coef=%d, ncycles=%d, length=%d\n", coefficient, ncycles, length);
}

float AudioAnalyzeToneDetect::read(void)
{
int32_t coef, q1, q2, power;
uint16_t len;

__disable_irq();
coef = coefficient;
q1 = out1;
q2 = out2;
len = length;
__enable_irq();
#ifdef TONE_DETECT_FAST
power = multiply_32x32_rshift32_rounded(q2, q2);
power = multiply_accumulate_32x32_rshift32_rounded(power, q1, q1);
power = multiply_subtract_32x32_rshift32_rounded(power,
multiply_32x32_rshift30(q1, q2), coef);
power <<= 4;
#else
int64_t power64;
power64 = (int64_t)q2 * (int64_t)q2;
power64 += (int64_t)q1 * (int64_t)q1;
power64 -= (((int64_t)q1 * (int64_t)q2) >> 30) * (int64_t)coef;
power = power64 >> 28;
#endif
return sqrtf((float)power) / (float)len;
}


AudioAnalyzeToneDetect::operator bool()
{
int32_t coef, q1, q2, power, trigger;
uint16_t len;

__disable_irq();
coef = coefficient;
q1 = out1;
q2 = out2;
len = length;
__enable_irq();
#ifdef TONE_DETECT_FAST
power = multiply_32x32_rshift32_rounded(q2, q2);
power = multiply_accumulate_32x32_rshift32_rounded(power, q1, q1);
power = multiply_subtract_32x32_rshift32_rounded(power,
multiply_32x32_rshift30(q1, q2), coef);
power <<= 4;
#else
int64_t power64;
power64 = (int64_t)q2 * (int64_t)q2;
power64 += (int64_t)q1 * (int64_t)q1;
power64 -= (((int64_t)q1 * (int64_t)q2) >> 30) * (int64_t)coef;
power = power64 >> 28;
#endif
trigger = (uint32_t)len * thresh;
trigger = multiply_32x32_rshift32(trigger, trigger);

Serial.printf("bool: power=%d, trig=%d\n", power, trigger);
return (power >= trigger);
}




sgtl5000.cpp → control_sgtl5000.cpp View File


wm8731.cpp → control_wm8731.cpp View File


ulaw.c → data_ulaw.c View File


waveforms.c → data_waveforms.c View File


windows.c → data_windows.c View File


+ 176
- 0
effect_chorus.cpp View File

@@ -0,0 +1,176 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"


/******************************************************************/

// A u d i o E f f e c t C h o r u s
// Written by Pete (El Supremo) Jan 2014

// circular addressing indices for left and right channels
short AudioEffectChorus::l_circ_idx;
short AudioEffectChorus::r_circ_idx;

short * AudioEffectChorus::l_delayline = NULL;
short * AudioEffectChorus::r_delayline = NULL;
int AudioEffectChorus::delay_length;
// An initial value of zero indicates passthru
int AudioEffectChorus::num_chorus = 0;


// All three must be valid.
boolean AudioEffectChorus::begin(short *delayline,int d_length,int n_chorus)
{
Serial.print("AudioEffectChorus.begin(Chorus delay line length = ");
Serial.print(d_length);
Serial.print(", n_chorus = ");
Serial.print(n_chorus);
Serial.println(")");

l_delayline = NULL;
r_delayline = NULL;
delay_length = 0;
l_circ_idx = 0;
r_circ_idx = 0;

if(delayline == NULL) {
return(false);
}
if(d_length < 10) {
return(false);
}
if(n_chorus < 1) {
return(false);
}
l_delayline = delayline;
r_delayline = delayline + d_length/2;
delay_length = d_length/2;
num_chorus = n_chorus;
return(true);
}

// This has the same effect as begin(NULL,0);
void AudioEffectChorus::stop(void)
{

}

void AudioEffectChorus::modify(int n_chorus)
{
num_chorus = n_chorus;
}

int iabs(int x)
{
if(x < 0)return(-x);
return(x);
}
//static int d_count = 0;

int last_idx = 0;
void AudioEffectChorus::update(void)
{
audio_block_t *block;
short *bp;
int sum;
int c_idx;

if(l_delayline == NULL)return;
if(r_delayline == NULL)return;
// do passthru
// It stores the unmodified data in the delay line so that
// it isn't as likely to click
if(num_chorus < 1) {
// Just passthrough
block = receiveWritable(0);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
l_circ_idx++;
if(l_circ_idx >= delay_length) {
l_circ_idx = 0;
}
l_delayline[l_circ_idx] = *bp++;
}
transmit(block,0);
release(block);
}
block = receiveWritable(1);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
r_circ_idx++;
if(r_circ_idx >= delay_length) {
r_circ_idx = 0;
}
r_delayline[r_circ_idx] = *bp++;
}
transmit(block,1);
release(block);
}
return;
}

// L E F T C H A N N E L

block = receiveWritable(0);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
l_circ_idx++;
if(l_circ_idx >= delay_length) {
l_circ_idx = 0;
}
l_delayline[l_circ_idx] = *bp;
sum = 0;
c_idx = l_circ_idx;
for(int k = 0; k < num_chorus; k++) {
sum += l_delayline[c_idx];
if(num_chorus > 1)c_idx -= delay_length/(num_chorus - 1) - 1;
if(c_idx < 0) {
c_idx += delay_length;
}
}
*bp++ = sum/num_chorus;
}

// send the effect output to the left channel
transmit(block,0);
release(block);
}

// R I G H T C H A N N E L

block = receiveWritable(1);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
r_circ_idx++;
if(r_circ_idx >= delay_length) {
r_circ_idx = 0;
}
r_delayline[r_circ_idx] = *bp;
sum = 0;
c_idx = r_circ_idx;
for(int k = 0; k < num_chorus; k++) {
sum += r_delayline[c_idx];
if(num_chorus > 1)c_idx -= delay_length/(num_chorus - 1) - 1;
if(c_idx < 0) {
c_idx += delay_length;
}
}
*bp++ = sum/num_chorus;
}

// send the effect output to the left channel
transmit(block,1);
release(block);
}
}




+ 75
- 0
effect_fade.cpp View File

@@ -0,0 +1,75 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"



extern "C" {
extern const int16_t fader_table[256];
};


void AudioEffectFade::update(void)
{
audio_block_t *block;
uint32_t i, pos, inc, index, scale;
int32_t val1, val2, val, sample;
uint8_t dir;

pos = position;
if (pos == 0) {
// output is silent
block = receiveReadOnly();
if (block) release(block);
return;
} else if (pos == 0xFFFFFFFF) {
// output is 100%
block = receiveReadOnly();
if (!block) return;
transmit(block);
release(block);
return;
}
block = receiveWritable();
if (!block) return;
inc = rate;
dir = direction;
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
index = pos >> 24;
val1 = fader_table[index];
val2 = fader_table[index+1];
scale = (pos >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0x10000 - scale;
val = (val1 + val2) >> 16;
sample = block->data[i];
sample = (sample * val) >> 15;
block->data[i] = sample;
if (dir > 0) {
// output is increasing
if (inc < 0xFFFFFFFF - pos) pos += inc;
else pos = 0xFFFFFFFF;
} else {
// output is decreasing
if (inc < pos) pos -= inc;
else pos = 0;
}
}
position = pos;
transmit(block);
release(block);
}

void AudioEffectFade::fadeBegin(uint32_t newrate, uint8_t dir)
{
__disable_irq();
uint32_t pos = position;
if (pos == 0) position = 1;
else if (pos == 0xFFFFFFFF) position = 0xFFFFFFFE;
rate = newrate;
direction = dir;
__enable_irq();
}




+ 249
- 0
effect_flange.cpp View File

@@ -0,0 +1,249 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"




/******************************************************************/
// A u d i o E f f e c t F l a n g e
// Written by Pete (El Supremo) Jan 2014
// 140207 - fix calculation of delay_rate_incr which is expressed as
// a fraction of 2*PI
// 140207 - cosmetic fix to begin()

// circular addressing indices for left and right channels
short AudioEffectFlange::l_circ_idx;
short AudioEffectFlange::r_circ_idx;

short * AudioEffectFlange::l_delayline = NULL;
short * AudioEffectFlange::r_delayline = NULL;

// User-supplied offset for the delayed sample
// but start with passthru
int AudioEffectFlange::delay_offset_idx = DELAY_PASSTHRU;
int AudioEffectFlange::delay_length;

int AudioEffectFlange::delay_depth;
int AudioEffectFlange::delay_rate_incr;
unsigned int AudioEffectFlange::l_delay_rate_index;
unsigned int AudioEffectFlange::r_delay_rate_index;
// fails if the user provides unreasonable values but will
// coerce them and go ahead anyway. e.g. if the delay offset
// is >= CHORUS_DELAY_LENGTH, the code will force it to
// CHORUS_DELAY_LENGTH-1 and return false.
// delay_rate is the rate (in Hz) of the sine wave modulation
// delay_depth is the maximum variation around delay_offset
// i.e. the total offset is delay_offset + delay_depth * sin(delay_rate)
boolean AudioEffectFlange::begin(short *delayline,int d_length,int delay_offset,int d_depth,float delay_rate)
{
boolean all_ok = true;

if(0) {
Serial.print("AudioEffectFlange.begin(offset = ");
Serial.print(delay_offset);
Serial.print(", depth = ");
Serial.print(d_depth);
Serial.print(", rate = ");
Serial.print(delay_rate,3);
Serial.println(")");
Serial.print(" FLANGE_DELAY_LENGTH = ");
Serial.println(d_length);
}
delay_length = d_length/2;
l_delayline = delayline;
r_delayline = delayline + delay_length;
delay_depth = d_depth;
// initial index
l_delay_rate_index = 0;
r_delay_rate_index = 0;
l_circ_idx = 0;
r_circ_idx = 0;
delay_rate_incr = delay_rate/44100.*2147483648.;
//Serial.println(delay_rate_incr,HEX);

delay_offset_idx = delay_offset;
// Allow the passthru code to go through
if(delay_offset_idx < -1) {
delay_offset_idx = 0;
all_ok = false;
}
if(delay_offset_idx >= delay_length) {
delay_offset_idx = delay_length - 1;
all_ok = false;
}
return(all_ok);
}


boolean AudioEffectFlange::modify(int delay_offset,int d_depth,float delay_rate)
{
boolean all_ok = true;
delay_depth = d_depth;

delay_rate_incr = delay_rate/44100.*2147483648.;
delay_offset_idx = delay_offset;
// Allow the passthru code to go through
if(delay_offset_idx < -1) {
delay_offset_idx = 0;
all_ok = false;
}
if(delay_offset_idx >= delay_length) {
delay_offset_idx = delay_length - 1;
all_ok = false;
}
l_delay_rate_index = 0;
r_delay_rate_index = 0;
l_circ_idx = 0;
r_circ_idx = 0;
return(all_ok);
}

void AudioEffectFlange::update(void)
{
audio_block_t *block;
int idx;
short *bp;
short frac;
int idx1;

if(l_delayline == NULL)return;
if(r_delayline == NULL)return;

// do passthru
if(delay_offset_idx == DELAY_PASSTHRU) {
// Just passthrough
block = receiveWritable(0);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
l_circ_idx++;
if(l_circ_idx >= delay_length) {
l_circ_idx = 0;
}
l_delayline[l_circ_idx] = *bp++;
}
transmit(block,0);
release(block);
}
block = receiveWritable(1);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
r_circ_idx++;
if(r_circ_idx >= delay_length) {
r_circ_idx = 0;
}
r_delayline[r_circ_idx] = *bp++;
}
transmit(block,1);
release(block);
}
return;
}

// L E F T C H A N N E L

block = receiveWritable(0);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
l_circ_idx++;
if(l_circ_idx >= delay_length) {
l_circ_idx = 0;
}
l_delayline[l_circ_idx] = *bp;
idx = arm_sin_q15( (q15_t)((l_delay_rate_index >> 16) & 0x7fff));
idx = (idx * delay_depth) >> 15;
//Serial.println(idx);
idx = l_circ_idx - (delay_offset_idx + idx);
if(idx < 0) {
idx += delay_length;
}
if(idx >= delay_length) {
idx -= delay_length;
}

if(frac < 0)
idx1 = idx - 1;
else
idx1 = idx + 1;
if(idx1 < 0) {
idx1 += delay_length;
}
if(idx1 >= delay_length) {
idx1 -= delay_length;
}
frac = (l_delay_rate_index >> 1) &0x7fff;
frac = (( (int)(l_delayline[idx1] - l_delayline[idx])*frac) >> 15);

*bp++ = (l_delayline[l_circ_idx]
+ l_delayline[idx] + frac
)/2;

l_delay_rate_index += delay_rate_incr;
if(l_delay_rate_index & 0x80000000) {
l_delay_rate_index &= 0x7fffffff;
}
}
// send the effect output to the left channel
transmit(block,0);
release(block);
}

// R I G H T C H A N N E L

block = receiveWritable(1);
if(block) {
bp = block->data;
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
r_circ_idx++;
if(r_circ_idx >= delay_length) {
r_circ_idx = 0;
}
r_delayline[r_circ_idx] = *bp;
idx = arm_sin_q15( (q15_t)((r_delay_rate_index >> 16)&0x7fff));
idx = (idx * delay_depth) >> 15;

idx = r_circ_idx - (delay_offset_idx + idx);
if(idx < 0) {
idx += delay_length;
}
if(idx >= delay_length) {
idx -= delay_length;
}

if(frac < 0)
idx1 = idx - 1;
else
idx1 = idx + 1;
if(idx1 < 0) {
idx1 += delay_length;
}
if(idx1 >= delay_length) {
idx1 -= delay_length;
}
frac = (r_delay_rate_index >> 1) &0x7fff;
frac = (( (int)(r_delayline[idx1] - r_delayline[idx])*frac) >> 15);

*bp++ = (r_delayline[r_circ_idx]
+ r_delayline[idx] + frac
)/2;

r_delay_rate_index += delay_rate_incr;
if(r_delay_rate_index & 0x80000000) {
r_delay_rate_index &= 0x7fffffff;
}

}
// send the effect output to the right channel
transmit(block,1);
release(block);
}
}




+ 79
- 0
filter_biquad.cpp View File

@@ -0,0 +1,79 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"


void AudioFilterBiquad::update(void)
{
audio_block_t *block;
int32_t a0, a1, a2, b1, b2, sum;
uint32_t in2, out2, aprev, bprev, flag;
uint32_t *data, *end;
int32_t *state;

block = receiveWritable();
if (!block) return;
data = (uint32_t *)(block->data);
end = data + AUDIO_BLOCK_SAMPLES/2;
state = (int32_t *)definition;
do {
a0 = *state++;
a1 = *state++;
a2 = *state++;
b1 = *state++;
b2 = *state++;
aprev = *state++;
bprev = *state++;
sum = *state & 0x3FFF;
do {
in2 = *data;
sum = signed_multiply_accumulate_32x16b(sum, a0, in2);
sum = signed_multiply_accumulate_32x16t(sum, a1, aprev);
sum = signed_multiply_accumulate_32x16b(sum, a2, aprev);
sum = signed_multiply_accumulate_32x16t(sum, b1, bprev);
sum = signed_multiply_accumulate_32x16b(sum, b2, bprev);
out2 = (uint32_t)sum >> 14;
sum &= 0x3FFF;
sum = signed_multiply_accumulate_32x16t(sum, a0, in2);
sum = signed_multiply_accumulate_32x16b(sum, a1, in2);
sum = signed_multiply_accumulate_32x16t(sum, a2, aprev);
sum = signed_multiply_accumulate_32x16b(sum, b1, out2);
sum = signed_multiply_accumulate_32x16t(sum, b2, bprev);
aprev = in2;
bprev = pack_16x16(sum >> 14, out2);
sum &= 0x3FFF;
aprev = in2;
*data++ = bprev;
} while (data < end);
flag = *state & 0x80000000;
*state++ = sum | flag;
*(state-2) = bprev;
*(state-3) = aprev;
} while (flag);
transmit(block);
release(block);
}

void AudioFilterBiquad::updateCoefs(int *source, bool doReset)
{
int32_t *dest=(int32_t *)definition;
int32_t *src=(int32_t *)source;
__disable_irq();
for(uint8_t index=0;index<5;index++)
{
*dest++=*src++;
}
if(doReset)
{
*dest++=0;
*dest++=0;
*dest++=0;
}
__enable_irq();
}

void AudioFilterBiquad::updateCoefs(int *source)
{
updateCoefs(source,false);
}


+ 70
- 0
filter_fir.cpp View File

@@ -0,0 +1,70 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"



void AudioFilterFIR::begin(short *cp,int n_coeffs)
{
// pointer to coefficients
coeff_p = cp;
// 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);
}
}

// This has the same effect as begin(NULL,0);
void AudioFilterFIR::stop(void)
{
coeff_p = NULL;
}


void AudioFilterFIR::update(void)
{
audio_block_t *block,*b_new;
// If there's no coefficient table, give up.
if(coeff_p == NULL)return;

// do passthru
if(coeff_p == FIR_PASSTHRU) {
// Just passthrough
block = receiveWritable(0);
if(block) {
transmit(block,0);
release(block);
}
block = receiveWritable(1);
if(block) {
transmit(block,1);
release(block);
}
return;
}
// Left Channel
block = receiveWritable(0);
// 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);
// 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 = receiveWritable(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);
}



adc.cpp → input_adc.cpp View File


+ 78
- 0
mixer.cpp View File

@@ -0,0 +1,78 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"



void applyGain(int16_t *data, int32_t mult)
{
uint32_t *p = (uint32_t *)data;
const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES);

do {
uint32_t tmp32 = *p; // read 2 samples from *data
int32_t val1 = signed_multiply_32x16b(mult, tmp32);
int32_t val2 = signed_multiply_32x16t(mult, tmp32);
val1 = signed_saturate_rshift(val1, 16, 0);
val2 = signed_saturate_rshift(val2, 16, 0);
*p++ = pack_16x16(val2, val1);
} while (p < end);
}

// page 133

void applyGainThenAdd(int16_t *data, const int16_t *in, int32_t mult)
{
uint32_t *dst = (uint32_t *)data;
const uint32_t *src = (uint32_t *)in;
const uint32_t *end = (uint32_t *)(data + AUDIO_BLOCK_SAMPLES);

if (mult == 65536) {
do {
uint32_t tmp32 = *dst;
*dst++ = signed_add_16_and_16(tmp32, *src++);
tmp32 = *dst;
*dst++ = signed_add_16_and_16(tmp32, *src++);
} while (dst < end);
} else {
do {
uint32_t tmp32 = *src++; // read 2 samples from *data
int32_t val1 = signed_multiply_32x16b(mult, tmp32);
int32_t val2 = signed_multiply_32x16t(mult, tmp32);
val1 = signed_saturate_rshift(val1, 16, 0);
val2 = signed_saturate_rshift(val2, 16, 0);
tmp32 = pack_16x16(val2, val1);
uint32_t tmp32b = *dst;
*dst++ = signed_add_16_and_16(tmp32, tmp32b);
} while (dst < end);
}
}


void AudioMixer4::update(void)
{
audio_block_t *in, *out=NULL;
unsigned int channel;

for (channel=0; channel < 4; channel++) {
if (!out) {
out = receiveWritable(channel);
if (out) {
int32_t mult = multiplier[channel];
if (mult != 65536) applyGain(out->data, mult);
}
} else {
in = receiveReadOnly(channel);
if (in) {
applyGainThenAdd(out->data, in->data, multiplier[channel]);
release(in);
}
}
}
if (out) {
transmit(out);
release(out);
}
}



dac.cpp → output_dac.cpp View File


i2s.cpp → output_i2s.cpp View File


pwm.cpp → output_pwm.cpp View File


+ 165
- 0
play_memory.cpp View File

@@ -0,0 +1,165 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"



void AudioPlayMemory::play(const unsigned int *data)
{
uint32_t format;

playing = 0;
prior = 0;
format = *data++;
next = data;
length = format & 0xFFFFFF;
playing = format >> 24;
}

void AudioPlayMemory::stop(void)
{
playing = 0;
}

extern "C" {
extern const int16_t ulaw_decode_table[256];
};

void AudioPlayMemory::update(void)
{
audio_block_t *block;
const unsigned int *in;
int16_t *out;
uint32_t tmp32, consumed;
int16_t s0, s1, s2, s3, s4;
int i;

if (!playing) return;
block = allocate();
if (block == NULL) return;

//Serial.write('.');

out = block->data;
in = next;
s0 = prior;

switch (playing) {
case 0x01: // u-law encoded, 44100 Hz
for (i=0; i < AUDIO_BLOCK_SAMPLES; i += 4) {
tmp32 = *in++;
*out++ = ulaw_decode_table[(tmp32 >> 0) & 255];
*out++ = ulaw_decode_table[(tmp32 >> 8) & 255];
*out++ = ulaw_decode_table[(tmp32 >> 16) & 255];
*out++ = ulaw_decode_table[(tmp32 >> 24) & 255];
}
consumed = 128;
break;

case 0x81: // 16 bit PCM, 44100 Hz
for (i=0; i < AUDIO_BLOCK_SAMPLES; i += 2) {
tmp32 = *in++;
*out++ = (int16_t)(tmp32 & 65535);
*out++ = (int16_t)(tmp32 >> 16);
}
consumed = 128;
break;

case 0x02: // u-law encoded, 22050 Hz
for (i=0; i < AUDIO_BLOCK_SAMPLES; i += 8) {
tmp32 = *in++;
s1 = ulaw_decode_table[(tmp32 >> 0) & 255];
s2 = ulaw_decode_table[(tmp32 >> 8) & 255];
s3 = ulaw_decode_table[(tmp32 >> 16) & 255];
s4 = ulaw_decode_table[(tmp32 >> 24) & 255];
*out++ = (s0 + s1) >> 1;
*out++ = s1;
*out++ = (s1 + s2) >> 1;
*out++ = s2;
*out++ = (s2 + s3) >> 1;
*out++ = s3;
*out++ = (s3 + s4) >> 1;
*out++ = s4;
s0 = s4;
}
consumed = 64;
break;

case 0x82: // 16 bits PCM, 22050 Hz
for (i=0; i < AUDIO_BLOCK_SAMPLES; i += 4) {
tmp32 = *in++;
s1 = (int16_t)(tmp32 & 65535);
s2 = (int16_t)(tmp32 >> 16);
*out++ = (s0 + s1) >> 1;
*out++ = s1;
*out++ = (s1 + s2) >> 1;
*out++ = s2;
s0 = s2;
}
consumed = 64;
break;

case 0x03: // u-law encoded, 11025 Hz
for (i=0; i < AUDIO_BLOCK_SAMPLES; i += 16) {
tmp32 = *in++;
s1 = ulaw_decode_table[(tmp32 >> 0) & 255];
s2 = ulaw_decode_table[(tmp32 >> 8) & 255];
s3 = ulaw_decode_table[(tmp32 >> 16) & 255];
s4 = ulaw_decode_table[(tmp32 >> 24) & 255];
*out++ = (s0 * 3 + s1) >> 2;
*out++ = (s0 + s1) >> 1;
*out++ = (s0 + s1 * 3) >> 2;
*out++ = s1;
*out++ = (s1 * 3 + s2) >> 2;
*out++ = (s1 + s2) >> 1;
*out++ = (s1 + s2 * 3) >> 2;
*out++ = s2;
*out++ = (s2 * 3 + s3) >> 2;
*out++ = (s2 + s3) >> 1;
*out++ = (s2 + s3 * 3) >> 2;
*out++ = s3;
*out++ = (s3 * 3 + s4) >> 2;
*out++ = (s3 + s4) >> 1;
*out++ = (s3 + s4 * 3) >> 2;
*out++ = s4;
s0 = s4;
}
consumed = 32;
break;

case 0x83: // 16 bit PCM, 11025 Hz
for (i=0; i < AUDIO_BLOCK_SAMPLES; i += 8) {
tmp32 = *in++;
s1 = (int16_t)(tmp32 & 65535);
s2 = (int16_t)(tmp32 >> 16);
*out++ = (s0 * 3 + s1) >> 2;
*out++ = (s0 + s1) >> 1;
*out++ = (s0 + s1 * 3) >> 2;
*out++ = s1;
*out++ = (s1 * 3 + s2) >> 2;
*out++ = (s1 + s2) >> 1;
*out++ = (s1 + s2 * 3) >> 2;
*out++ = s2;
s0 = s2;
}
consumed = 32;
break;

default:
release(block);
playing = 0;
return;
}
prior = s0;
next = in;
if (length > consumed) {
length -= consumed;
} else {
playing = 0;
}
transmit(block);
release(block);
}




+ 68
- 0
play_sd_raw.cpp View File

@@ -0,0 +1,68 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"



void AudioPlaySDcardRAW::begin(void)
{
playing = false;
if (block) {
release(block);
block = NULL;
}
}


bool AudioPlaySDcardRAW::play(const char *filename)
{
stop();
rawfile = SD.open(filename);
if (!rawfile) {
Serial.println("unable to open file");
return false;
}
Serial.println("able to open file");
playing = true;
return true;
}

void AudioPlaySDcardRAW::stop(void)
{
__disable_irq();
if (playing) {
playing = false;
__enable_irq();
rawfile.close();
} else {
__enable_irq();
}
}


void AudioPlaySDcardRAW::update(void)
{
unsigned int i, n;

// only update if we're playing
if (!playing) return;

// allocate the audio blocks to transmit
block = allocate();
if (block == NULL) return;

if (rawfile.available()) {
// we can read more data from the file...
n = rawfile.read(block->data, AUDIO_BLOCK_SAMPLES*2);
for (i=n/2; i < AUDIO_BLOCK_SAMPLES; i++) {
block->data[i] = 0;
}
transmit(block);
release(block);
} else {
rawfile.close();
playing = false;
}
}



+ 450
- 0
play_sd_wav.cpp View File

@@ -0,0 +1,450 @@
#include "Audio.h"
#include "arm_math.h"
#include "utility/dspinst.h"



#define STATE_DIRECT_8BIT_MONO 0 // playing mono at native sample rate
#define STATE_DIRECT_8BIT_STEREO 1 // playing stereo at native sample rate
#define STATE_DIRECT_16BIT_MONO 2 // playing mono at native sample rate
#define STATE_DIRECT_16BIT_STEREO 3 // playing stereo at native sample rate
#define STATE_CONVERT_8BIT_MONO 4 // playing mono, converting sample rate
#define STATE_CONVERT_8BIT_STEREO 5 // playing stereo, converting sample rate
#define STATE_CONVERT_16BIT_MONO 6 // playing mono, converting sample rate
#define STATE_CONVERT_16BIT_STEREO 7 // playing stereo, converting sample rate
#define STATE_PARSE1 8 // looking for 20 byte ID header
#define STATE_PARSE2 9 // looking for 16 byte format header
#define STATE_PARSE3 10 // looking for 8 byte data header
#define STATE_PARSE4 11 // ignoring unknown chunk
#define STATE_STOP 12

void AudioPlaySDcardWAV::begin(void)
{
state = STATE_STOP;
state_play = STATE_STOP;
data_length = 0;
if (block_left) {
release(block_left);
block_left = NULL;
}
if (block_right) {
release(block_right);
block_right = NULL;
}
}


bool AudioPlaySDcardWAV::play(const char *filename)
{
stop();
wavfile = SD.open(filename);
if (!wavfile) return false;
buffer_remaining = 0;
state_play = STATE_STOP;
data_length = 0;
state = STATE_PARSE1;
return true;
}

void AudioPlaySDcardWAV::stop(void)
{
__disable_irq();
if (state != STATE_STOP) {
audio_block_t *b1 = block_left;
block_left = NULL;
audio_block_t *b2 = block_right;
block_right = NULL;
state = STATE_STOP;
__enable_irq();
if (b1) release(b1);
if (b2) release(b2);
wavfile.close();
} else {
__enable_irq();
}
}

bool AudioPlaySDcardWAV::start(void)
{
__disable_irq();
if (state == STATE_STOP) {
if (state_play == STATE_STOP) {
__enable_irq();
return false;
}
state = state_play;
}
__enable_irq();
return true;
}


void AudioPlaySDcardWAV::update(void)
{
// only update if we're playing
if (state == STATE_STOP) return;

// allocate the audio blocks to transmit
block_left = allocate();
if (block_left == NULL) return;
if (state < 8 && (state & 1) == 1) {
// if we're playing stereo, allocate another
// block for the right channel output
block_right = allocate();
if (block_right == NULL) {
release(block_left);
return;
}
} else {
// if we're playing mono or just parsing
// the WAV file header, no right-side block
block_right = NULL;
}
block_offset = 0;

//Serial.println("update");

// is there buffered data?
if (buffer_remaining > 0) {
// we have buffered data
if (consume()) return; // it was enough to transmit audio
}

// we only get to this point when buffer[512] is empty
if (state != STATE_STOP && wavfile.available()) {
// we can read more data from the file...
buffer_remaining = wavfile.read(buffer, 512);
if (consume()) {
// good, it resulted in audio transmit
return;
} else {
// not good, no audio was transmitted
buffer_remaining = 0;
if (block_left) {
release(block_left);
block_left = NULL;
}
if (block_right) {
release(block_right);
block_right = NULL;
}
// if we're still playing, well, there's going to
// be a gap in output, but we can't keep burning
// time trying to read more data. Hopefully things
// will go better next time?
if (state != STATE_STOP) return;
}
}
// end of file reached or other reason to stop
wavfile.close();
if (block_left) {
release(block_left);
block_left = NULL;
}
if (block_right) {
release(block_right);
block_right = NULL;
}
state_play = STATE_STOP;
state = STATE_STOP;
}


// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

// Consume already buffered data. Returns true if audio transmitted.
bool AudioPlaySDcardWAV::consume(void)
{
uint32_t len, size;
uint8_t lsb, msb;
const uint8_t *p;

size = buffer_remaining;
p = buffer + 512 - size;
start:
if (size == 0) return false;
//Serial.print("AudioPlaySDcardWAV write, size = ");
//Serial.print(size);
//Serial.print(", data_length = ");
//Serial.print(data_length);
//Serial.print(", state = ");
//Serial.println(state);
switch (state) {
// parse wav file header, is this really a .wav file?
case STATE_PARSE1:
len = 20 - data_length;
if (size < len) len = size;
memcpy((uint8_t *)header + data_length, p, len);
data_length += len;
if (data_length < 20) return false;
// parse the header...
if (header[0] == 0x46464952 && header[2] == 0x45564157
&& header[3] == 0x20746D66 && header[4] == 16) {
//Serial.println("header ok");
state = STATE_PARSE2;
p += len;
size -= len;
data_length = 0;
goto start;
}
//Serial.println("unknown WAV header");
break;

// check & extract key audio parameters
case STATE_PARSE2:
len = 16 - data_length;
if (size < len) len = size;
memcpy((uint8_t *)header + data_length, p, len);
data_length += len;
if (data_length < 16) return false;
if (parse_format()) {
//Serial.println("audio format ok");
p += len;
size -= len;
data_length = 0;
state = STATE_PARSE3;
goto start;
}
//Serial.println("unknown audio format");
break;

// find the data chunk
case STATE_PARSE3:
len = 8 - data_length;
if (size < len) len = size;
memcpy((uint8_t *)header + data_length, p, len);
data_length += len;
if (data_length < 8) return false;
//Serial.print("chunk id = ");
//Serial.print(header[0], HEX);
//Serial.print(", length = ");
//Serial.println(header[1]);
p += len;
size -= len;
data_length = header[1];
if (header[0] == 0x61746164) {
//Serial.println("found data chunk");
// TODO: verify offset in file is an even number
// as required by WAV format. abort if odd. Code
// below will depend upon this and fail if not even.
leftover_bytes = 0;
state = state_play;
if (state & 1) {
// if we're going to start stereo
// better allocate another output block
block_right = allocate();
if (!block_right) return false;
}
} else {
state = STATE_PARSE4;
}
goto start;

// ignore any extra unknown chunks (title & artist info)
case STATE_PARSE4:
if (size < data_length) {
data_length -= size;
return false;
}
p += data_length;
size -= data_length;
data_length = 0;
state = STATE_PARSE3;
goto start;

// playing mono at native sample rate
case STATE_DIRECT_8BIT_MONO:
return false;

// playing stereo at native sample rate
case STATE_DIRECT_8BIT_STEREO:
return false;

// playing mono at native sample rate
case STATE_DIRECT_16BIT_MONO:
if (size > data_length) size = data_length;
data_length -= size;
while (1) {
lsb = *p++;
msb = *p++;
size -= 2;
block_left->data[block_offset++] = (msb << 8) | lsb;
if (block_offset >= AUDIO_BLOCK_SAMPLES) {
transmit(block_left, 0);
transmit(block_left, 1);
//Serial1.print('%');
//delayMicroseconds(90);
release(block_left);
block_left = NULL;
data_length += size;
buffer_remaining = size;
if (block_right) release(block_right);
return true;
}
if (size == 0) {
if (data_length == 0) break;
return false;
}
}
// end of file reached
if (block_offset > 0) {
// TODO: fill remainder of last block with zero and transmit
}
state = STATE_STOP;
return false;

// playing stereo at native sample rate
case STATE_DIRECT_16BIT_STEREO:
if (size > data_length) size = data_length;
data_length -= size;
if (leftover_bytes) {
block_left->data[block_offset] = header[0];
goto right16;
}
while (1) {
lsb = *p++;
msb = *p++;
size -= 2;
if (size == 0) {
if (data_length == 0) break;
header[0] = (msb << 8) | lsb;
leftover_bytes = 2;
return false;
}
block_left->data[block_offset] = (msb << 8) | lsb;
right16:
lsb = *p++;
msb = *p++;
size -= 2;
block_right->data[block_offset++] = (msb << 8) | lsb;
if (block_offset >= AUDIO_BLOCK_SAMPLES) {
transmit(block_left, 0);
release(block_left);
block_left = NULL;
transmit(block_right, 1);
release(block_right);
block_right = NULL;
data_length += size;
buffer_remaining = size;
return true;
}
if (size == 0) {
if (data_length == 0) break;
leftover_bytes = 0;
return false;
}
}
// end of file reached
if (block_offset > 0) {
// TODO: fill remainder of last block with zero and transmit
}
state = STATE_STOP;
return false;

// playing mono, converting sample rate
case STATE_CONVERT_8BIT_MONO :
return false;

// playing stereo, converting sample rate
case STATE_CONVERT_8BIT_STEREO:
return false;

// playing mono, converting sample rate
case STATE_CONVERT_16BIT_MONO:
return false;

// playing stereo, converting sample rate
case STATE_CONVERT_16BIT_STEREO:
return false;

// ignore any extra data after playing
// or anything following any error
case STATE_STOP:
return false;

// this is not supposed to happen!
//default:
//Serial.println("AudioPlaySDcardWAV, unknown state");
}
state_play = STATE_STOP;
state = STATE_STOP;
return false;
}


/*
00000000 52494646 66EA6903 57415645 666D7420 RIFFf.i.WAVEfmt
00000010 10000000 01000200 44AC0000 10B10200 ........D.......
00000020 04001000 4C495354 3A000000 494E464F ....LIST:...INFO
00000030 494E414D 14000000 49205761 6E742054 INAM....I Want T
00000040 6F20436F 6D65204F 76657200 49415254 o Come Over.IART
00000050 12000000 4D656C69 73736120 45746865 ....Melissa Ethe
00000060 72696467 65006461 746100EA 69030100 ridge.data..i...
00000070 FEFF0300 FCFF0400 FDFF0200 0000FEFF ................
00000080 0300FDFF 0200FFFF 00000100 FEFF0300 ................
00000090 FDFF0300 FDFF0200 FFFF0100 0000FFFF ................
*/





// SD library on Teensy3 at 96 MHz
// 256 byte chunks, speed is 443272 bytes/sec
// 512 byte chunks, speed is 468023 bytes/sec





bool AudioPlaySDcardWAV::parse_format(void)
{
uint8_t num = 0;
uint16_t format;
uint16_t channels;
uint32_t rate;
uint16_t bits;

format = header[0];
//Serial.print(" format = ");
//Serial.println(format);
if (format != 1) return false;

channels = header[0] >> 16;
//Serial.print(" channels = ");
//Serial.println(channels);
if (channels == 1) {
} else if (channels == 2) {
num = 1;
} else {
return false;
}

bits = header[3] >> 16;
//Serial.print(" bits = ");
//Serial.println(bits);
if (bits == 8) {
} else if (bits == 16) {
num |= 2;
} else {
return false;
}

rate = header[1];
//Serial.print(" rate = ");
//Serial.println(rate);
if (rate == AUDIO_SAMPLE_RATE) {
} else if (rate >= 8000 && rate <= 48000) {
num |= 4;
} else {
return false;
}
// we're not checking the byte rate and block align fields
// if they're not the expected values, all we could do is
// return false. Do any real wav files have unexpected
// values in these other fields?
state_play = num;
return true;
}



+ 317
- 0
synth_waveform.cpp View File

@@ -0,0 +1,317 @@
#include "Audio.h"
#include "arm_math.h"
#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 - 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;
}

boolean AudioSynthWaveform::begin(float t_amp,int t_hi,short type)
{
tone_type = type;
// tone_amp = t_amp;
amplitude(t_amp);
tone_freq = t_hi;
if(t_hi < 1)return false;
if(t_hi >= AUDIO_SAMPLE_RATE_EXACT/2)return false;
tone_phase = 0;
tone_incr = (0x100000000LL*t_hi)/AUDIO_SAMPLE_RATE_EXACT;
if(0) {
Serial.print("AudioSynthWaveform.begin(tone_amp = ");
Serial.print(t_amp);
Serial.print(", tone_hi = ");
Serial.print(t_hi);
Serial.print(", tone_incr = ");
Serial.print(tone_incr,HEX);
// Serial.print(", tone_hi = ");
// Serial.print(t_hi);
Serial.println(")");
}
return(true);
}


void AudioSynthWaveform::update(void)
{
audio_block_t *block;
short *bp;
// temporary for ramp in sine
uint32_t ramp_mag;
// 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
block = allocate();
if(block) {
bp = block->data;
switch(tone_type) {
case TONE_TYPE_SINE:
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
// 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--;
// adjust tone_phase to Q15 format and then adjust the result
// of the multiplication
*bp = (short)((arm_sin_q15(tone_phase>>17) * tone_amp) >> 15);
*bp++ = (*bp * ramp_mag)>>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--;
// adjust tone_phase to Q15 format and then adjust the result
// of the multiplication
*bp = (short)((arm_sin_q15(tone_phase>>17) * last_tone_amp) >> 15);
*bp++ = (*bp * ramp_mag)>>15;
} else {
// adjust tone_phase to Q15 format and then adjust the result
// of the multiplication
*bp++ = (short)((arm_sin_q15(tone_phase>>17) * tone_amp) >> 15);
}
// phase and incr are both unsigned 32-bit fractions
tone_phase += tone_incr;
}
break;
case TONE_TYPE_SQUARE:
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
if(tone_phase & 0x80000000)*bp++ = -tone_amp;
else *bp++ = tone_amp;
// phase and incr are both unsigned 32-bit fractions
tone_phase += tone_incr;
}
break;
case TONE_TYPE_SAWTOOTH:
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
*bp = ((short)(tone_phase>>16)*tone_amp) >> 15;
bp++;
// phase and incr are both unsigned 32-bit fractions
tone_phase += tone_incr;
}
break;

case TONE_TYPE_TRIANGLE:
for(int i = 0;i < AUDIO_BLOCK_SAMPLES;i++) {
if(tone_phase & 0x80000000) {
// negative half-cycle
tmp_amp = -tone_amp;
}
else {
// positive half-cycle
tmp_amp = tone_amp;
}
mag = tone_phase << 2;
// Determine which quadrant
if(tone_phase & 0x40000000) {
// negate the magnitude
mag = ~mag + 1;
}
*bp++ = ((short)(mag>>17)*tmp_amp) >> 15;
tone_phase += tone_incr;
}
break;
}
// send the samples to the left channel
transmit(block,0);
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






Loading…
Cancel
Save