Fixed AudioSynthWaveform. Uses LUT with linear interpolation for sinewav...dds
| @@ -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 # */ | |||
| @@ -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]); | |||
| @@ -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(); | |||
| } | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| @@ -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 | |||
| @@ -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. | |||
| @@ -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 | |||
| @@ -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 | |||
| @@ -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. | |||