浏览代码

Fixed AudioSynthWaveform. Uses LUT with linear interpolation for sinewave.

Square, triangle and sawtooth are generated directly.
dds
Pete (El Supremo) 10 年前
父节点
当前提交
0c0731862c
共有 3 个文件被更改,包括 315 次插入26 次删除
  1. +194
    -23
      synth_waveform.cpp
  2. +85
    -3
      synth_waveform.h
  3. +36
    -0
      synth_waveform.md

+ 194
- 23
synth_waveform.cpp 查看文件

@@ -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 查看文件

@@ -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 查看文件

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



正在加载...
取消
保存