parent
dbde888b6a
commit
d7710000bc
@ -0,0 +1,214 @@ |
|||||||
|
/* filter_ir_cabsim.cpp
|
||||||
|
* |
||||||
|
* Piotr Zapart 01.2024 www.hexefx.com |
||||||
|
* - Combined into a stereo speaker simulator with included set of IRs. |
||||||
|
* - Added stereo enhancer for double tracking emulation |
||||||
|
*
|
||||||
|
* based on: |
||||||
|
* A u d i o FilterConvolutionUP |
||||||
|
* Uniformly-Partitioned Convolution Filter for Teeny 4.0 |
||||||
|
* Written by Brian Millier November 2019 |
||||||
|
* adapted from routines written for Teensy 4.0 by Frank DD4WH
|
||||||
|
* that were based upon code/literature by Warren Pratt
|
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software Foundation,
|
||||||
|
* either version 3 of the License, or (at your option) any later version. |
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details. |
||||||
|
* You should have received a copy of the GNU General Public License along with this program.
|
||||||
|
* If not, see <https://www.gnu.org/licenses/>."
|
||||||
|
*/ |
||||||
|
#include "filter_ir_cabsim_F32.h" |
||||||
|
#include <arm_const_structs.h> |
||||||
|
#include "HxFx_memcpy.h" |
||||||
|
|
||||||
|
float32_t DMAMEM maskgen[IR_FFT_LENGTH * 2]; |
||||||
|
float32_t DMAMEM fmask[IR_NFORMAX][IR_FFT_LENGTH * 2]; //
|
||||||
|
float32_t DMAMEM fftin[IR_FFT_LENGTH * 2]; |
||||||
|
float32_t DMAMEM accum[IR_FFT_LENGTH * 2]; |
||||||
|
|
||||||
|
float32_t DMAMEM last_sample_buffer_L[IR_BUFFER_SIZE * IR_N_B]; |
||||||
|
float32_t DMAMEM last_sample_buffer_R[IR_BUFFER_SIZE * IR_N_B]; |
||||||
|
float32_t DMAMEM fftout[IR_NFORMAX][IR_FFT_LENGTH * 2]; |
||||||
|
float32_t DMAMEM ac2[512]; |
||||||
|
|
||||||
|
const static arm_cfft_instance_f32 *S; |
||||||
|
// complex iFFT with the new library CMSIS V4.5
|
||||||
|
const static arm_cfft_instance_f32 *iS; |
||||||
|
// FFT instance for direct calculation of the filter mask
|
||||||
|
// from the impulse response of the FIR - the coefficients
|
||||||
|
const static arm_cfft_instance_f32 *maskS; |
||||||
|
|
||||||
|
AudioFilterIRCabsim_F32::AudioFilterIRCabsim_F32() : AudioStream_F32(2, inputQueueArray_f32) |
||||||
|
{ |
||||||
|
if (!delay.init()) return; |
||||||
|
initialized = true; |
||||||
|
} |
||||||
|
|
||||||
|
void AudioFilterIRCabsim_F32::update() |
||||||
|
{ |
||||||
|
#if defined(__ARM_ARCH_7EM__) |
||||||
|
if (!initialized) return; |
||||||
|
audio_block_f32_t *blockL, *blockR; |
||||||
|
|
||||||
|
blockL = AudioStream_F32::receiveWritable_f32(0); |
||||||
|
blockR = AudioStream_F32::receiveWritable_f32(1); |
||||||
|
if (!blockL || !blockR) |
||||||
|
{ |
||||||
|
if (blockL) AudioStream_F32::release(blockL); |
||||||
|
if (blockR) AudioStream_F32::release(blockR); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!ir_loaded) // ir not loaded yet or bypass mode
|
||||||
|
{ |
||||||
|
// bypass clean signal
|
||||||
|
// TODO: Add bypass signal gain stage to match the processed signal volume
|
||||||
|
AudioStream_F32::transmit(blockL, 0); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (first_block) // fill real & imaginaries with zeros for the first BLOCKSIZE samples
|
||||||
|
{ |
||||||
|
arm_fill_f32(0.0f, fftin, blockL->length * 4); |
||||||
|
first_block = 0; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
memcpyInterleave_f32(last_sample_buffer_L, last_sample_buffer_R, fftin, blockL->length); |
||||||
|
} |
||||||
|
if (doubleTrack) |
||||||
|
{ |
||||||
|
// invert phase for channel R
|
||||||
|
arm_scale_f32(blockR->data, -1.0f, blockR->data, blockR->length); |
||||||
|
// run channelR allpass
|
||||||
|
for (int i=0; i<blockR->length; i++) |
||||||
|
{ |
||||||
|
blockR->data[i] = delay.process(blockR->data[i]); |
||||||
|
delay.updateIndex(); |
||||||
|
} |
||||||
|
} |
||||||
|
arm_copy_f32(blockL->data, last_sample_buffer_L, blockL->length); |
||||||
|
arm_copy_f32(blockR->data, last_sample_buffer_R, blockR->length); |
||||||
|
|
||||||
|
memcpyInterleave_f32(last_sample_buffer_L, last_sample_buffer_R, fftin + FFT_L, blockL->length); // interleave copy it to fftin at offset FFT_L
|
||||||
|
arm_cfft_f32(S, fftin, 0, 1); |
||||||
|
|
||||||
|
uint32_t buffidx512 = buffidx * 512; |
||||||
|
ptr1 = ptr_fftout + (buffidx512); // set pointer to proper segment of fftout array
|
||||||
|
memcpy(ptr1, fftin, 2048); // copy 512 samples from fftin to fftout (at proper segment)
|
||||||
|
k = buffidx; |
||||||
|
memset(accum, 0, IR_BUFFER_SIZE * 16); // clear accum array
|
||||||
|
k512 = k * 512; // save 7 k*512 multiplications per inner loop
|
||||||
|
j512 = 0; |
||||||
|
for (uint32_t j = 0; j < nfor; j++) // BM np was nfor
|
||||||
|
{ |
||||||
|
ptr1 = ptr_fftout + k512; |
||||||
|
ptr2 = ptr_fmask + j512; |
||||||
|
// do a complex MAC (multiply/accumulate)
|
||||||
|
arm_cmplx_mult_cmplx_f32(ptr1, ptr2, ac2, 256); // This is the complex multiply
|
||||||
|
for (int q = 0; q < 512; q = q + 8) |
||||||
|
{ // this is the accumulate
|
||||||
|
accum[q] += ac2[q]; |
||||||
|
accum[q + 1] += ac2[q + 1]; |
||||||
|
accum[q + 2] += ac2[q + 2]; |
||||||
|
accum[q + 3] += ac2[q + 3]; |
||||||
|
accum[q + 4] += ac2[q + 4]; |
||||||
|
accum[q + 5] += ac2[q + 5]; |
||||||
|
accum[q + 6] += ac2[q + 6]; |
||||||
|
accum[q + 7] += ac2[q + 7]; |
||||||
|
} |
||||||
|
k--; |
||||||
|
if (k < 0) |
||||||
|
{ |
||||||
|
k = nfor - 1; |
||||||
|
} |
||||||
|
k512 = k * 512; |
||||||
|
j512 += 512; |
||||||
|
} // end np loop
|
||||||
|
buffidx++; |
||||||
|
buffidx = buffidx % nfor; |
||||||
|
|
||||||
|
arm_cfft_f32(iS, accum, 1, 1); |
||||||
|
|
||||||
|
for (int i = 0; i < blockL->length; i++) |
||||||
|
{ |
||||||
|
blockL->data[i] = accum[i * 2 + 0]; |
||||||
|
blockR->data[i] = accum[i * 2 + 1]; |
||||||
|
} |
||||||
|
// restore the channel R phase
|
||||||
|
if (doubleTrack) arm_scale_f32(blockR->data, -1.0f, blockR->data, blockR->length); |
||||||
|
AudioStream_F32::transmit(blockL, 0); |
||||||
|
AudioStream_F32::release(blockL); |
||||||
|
AudioStream_F32::transmit(blockR, 1); |
||||||
|
AudioStream_F32::release(blockR); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
void AudioFilterIRCabsim_F32::ir_register(const float32_t *irPtr, uint8_t position) |
||||||
|
{ |
||||||
|
if (position >= IR_MAX_REG_NUM) |
||||||
|
return; |
||||||
|
irPtrTable[position] = irPtr; |
||||||
|
} |
||||||
|
|
||||||
|
void AudioFilterIRCabsim_F32::ir_load(uint8_t idx) |
||||||
|
{ |
||||||
|
|
||||||
|
const float32_t *newIrPtr = NULL; |
||||||
|
uint32_t nc = 0; |
||||||
|
|
||||||
|
if (idx >= IR_MAX_REG_NUM) |
||||||
|
return; |
||||||
|
if (idx == ir_idx) |
||||||
|
return; // load only once
|
||||||
|
ir_idx = idx; |
||||||
|
newIrPtr = irPtrTable[idx]; |
||||||
|
ir_loaded = 0; |
||||||
|
|
||||||
|
if (newIrPtr == NULL) // bypass
|
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
AudioNoInterrupts(); |
||||||
|
nc = newIrPtr[0]; |
||||||
|
nfor = nc / IR_BUFFER_SIZE; |
||||||
|
if (nfor > nforMax) nfor = nforMax; |
||||||
|
ptr_fmask = &fmask[0][0]; |
||||||
|
ptr_fftout = &fftout[0][0]; |
||||||
|
memset(ptr_fftout, 0, nfor*512*4); // clear fftout array
|
||||||
|
memset(fftin, 0, 512 * 4); // clear fftin array
|
||||||
|
|
||||||
|
S = &arm_cfft_sR_f32_len256; |
||||||
|
iS = &arm_cfft_sR_f32_len256; |
||||||
|
maskS = &arm_cfft_sR_f32_len256; |
||||||
|
init_partitioned_filter_masks(newIrPtr);
|
||||||
|
|
||||||
|
delay.reset(); |
||||||
|
ir_loaded = 1; |
||||||
|
AudioInterrupts(); |
||||||
|
|
||||||
|
//Serial.printf("Loaded IR+ %d, part count = %d\r\n", ir_idx, nfor);
|
||||||
|
} |
||||||
|
|
||||||
|
void AudioFilterIRCabsim_F32::init_partitioned_filter_masks(const float32_t *irPtr) |
||||||
|
{ |
||||||
|
for (uint32_t j = 0; j < nfor; j++) |
||||||
|
{ |
||||||
|
arm_fill_f32(0.0f, maskgen, IR_BUFFER_SIZE * 4); |
||||||
|
for (unsigned i = 0; i < IR_BUFFER_SIZE; i++) |
||||||
|
{ |
||||||
|
maskgen[i * 2 + IR_BUFFER_SIZE * 2] = irPtr[2 + i + j * IR_BUFFER_SIZE] * irPtr[1]; // added gain!
|
||||||
|
} |
||||||
|
arm_cfft_f32(maskS, maskgen, 0, 1); |
||||||
|
for (unsigned i = 0; i < IR_BUFFER_SIZE * 4; i++) |
||||||
|
{ |
||||||
|
fmask[j][i] = maskgen[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
/* filter_ir_cabsim.h
|
||||||
|
* |
||||||
|
* Piotr Zapart 01.2024 www.hexefx.com |
||||||
|
* - Combined into a stereo speaker simulator with included set of IRs. |
||||||
|
* - Added stereo enhancer for double tracking emulation |
||||||
|
*
|
||||||
|
* based on: |
||||||
|
* A u d i o FilterConvolutionUP |
||||||
|
* Uniformly-Partitioned Convolution Filter for Teeny 4.0 |
||||||
|
* Written by Brian Millier November 2019 |
||||||
|
* adapted from routines written for Teensy 4.0 by Frank DD4WH
|
||||||
|
* that were based upon code/literature by Warren Pratt
|
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software Foundation,
|
||||||
|
* either version 3 of the License, or (at your option) any later version. |
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details. |
||||||
|
* You should have received a copy of the GNU General Public License along with this program.
|
||||||
|
* If not, see <https://www.gnu.org/licenses/>."
|
||||||
|
*/ |
||||||
|
#ifndef _FILTER_IR_CABSIM_H |
||||||
|
#define _FILTER_IR_CABSIM_H |
||||||
|
|
||||||
|
#include <Arduino.h> |
||||||
|
#include <Audio.h> |
||||||
|
#include "AudioStream.h" |
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include "arm_math.h" |
||||||
|
#include "filter_ir_cabsim_irs.h" |
||||||
|
#include "basic_allpass.h" |
||||||
|
#include "basic_delay.h" |
||||||
|
#include "basic_shelvFilter.h" |
||||||
|
|
||||||
|
#define IR_BUFFER_SIZE 128 |
||||||
|
#define IR_NFORMAX (8192 / IR_BUFFER_SIZE) |
||||||
|
#define IR_FFT_LENGTH (2 * IR_BUFFER_SIZE) |
||||||
|
#define IR_N_B (1) |
||||||
|
#define IR_MAX_REG_NUM 11 // max number of registered IRs
|
||||||
|
|
||||||
|
class AudioFilterIRCabsim_F32 : public AudioStream_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
AudioFilterIRCabsim_F32(); |
||||||
|
virtual void update(void); |
||||||
|
void ir_register(const float32_t *irPtr, uint8_t position); |
||||||
|
void ir_load(uint8_t idx); |
||||||
|
uint8_t ir_get(void) {return ir_idx;}
|
||||||
|
float ir_get_len_ms(void) |
||||||
|
{ |
||||||
|
float32_t slen = 0.0f; |
||||||
|
if (irPtrTable[ir_idx]) slen = irPtrTable[ir_idx][0]; |
||||||
|
return (slen / AUDIO_SAMPLE_RATE_EXACT)*1000.0f; |
||||||
|
} |
||||||
|
bool doubler_tgl() |
||||||
|
{ |
||||||
|
__disable_irq(); |
||||||
|
doubleTrack = doubleTrack ? false : true; |
||||||
|
if (doubleTrack)
|
||||||
|
{ |
||||||
|
delay.reset(); |
||||||
|
} |
||||||
|
__enable_irq(); |
||||||
|
return doubleTrack; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
audio_block_f32_t *inputQueueArray_f32[2]; |
||||||
|
float32_t audio_gain = 0.3f; |
||||||
|
int idx_t = 0; |
||||||
|
int16_t *sp_L; |
||||||
|
int16_t *sp_R; |
||||||
|
const uint32_t FFT_L = IR_FFT_LENGTH; |
||||||
|
uint8_t first_block = 1; |
||||||
|
uint8_t ir_loaded = 0;
|
||||||
|
uint8_t ir_idx = 0xFF; |
||||||
|
uint32_t nfor = 0; |
||||||
|
const uint32_t nforMax = IR_NFORMAX; |
||||||
|
|
||||||
|
int buffidx = 0; |
||||||
|
int k = 0; |
||||||
|
|
||||||
|
float32_t *ptr_fftout; |
||||||
|
float32_t *ptr_fmask; |
||||||
|
float32_t* ptr1; |
||||||
|
float32_t* ptr2; |
||||||
|
int k512; |
||||||
|
int j512; |
||||||
|
|
||||||
|
uint32_t N_BLOCKS = IR_N_B; |
||||||
|
|
||||||
|
static const uint32_t delay_l = AUDIO_SAMPLE_RATE * 0.01277f; //15ms delay
|
||||||
|
AudioBasicDelay<delay_l> delay; |
||||||
|
|
||||||
|
// default IR table, use NULL for bypass
|
||||||
|
const float32_t *irPtrTable[IR_MAX_REG_NUM] =
|
||||||
|
{ |
||||||
|
ir_1_guitar, ir_2_guitar, ir_3_guitar, ir_4_guitar, ir_10_guitar, ir_11_guitar, ir_6_guitar, ir_7_bass, ir_8_bass, ir_9_bass, NULL |
||||||
|
}; |
||||||
|
void init_partitioned_filter_masks(const float32_t *irPtr); |
||||||
|
bool initialized = false; |
||||||
|
bool doubleTrack = true; |
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // _FILTER_IR_CONVOLVER_H
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@ |
|||||||
|
/**
|
||||||
|
* @file filter_ir_cabsim_irs.h |
||||||
|
* @author Piotr Zapart www.hexefx.com |
||||||
|
* @brief Guitar / Bass cabinet impulse responses |
||||||
|
* @version 0.1 |
||||||
|
* @date 2024-01-22 |
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024 |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU General Public License as published by the Free Software Foundation,
|
||||||
|
* either version 3 of the License, or (at your option) any later version. |
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||||
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
* See the GNU General Public License for more details. |
||||||
|
* You should have received a copy of the GNU General Public License along with this program.
|
||||||
|
* If not, see <https://www.gnu.org/licenses/>."
|
||||||
|
*/ |
||||||
|
#ifndef _FILTER_IR_CABSIM_IRS_H |
||||||
|
#define _FILTER_IR_CABSIM_IRS_H |
||||||
|
|
||||||
|
#include <arm_math.h> |
||||||
|
|
||||||
|
extern const float32_t ir_1_guitar[]; // guitar cabs
|
||||||
|
extern const float32_t ir_2_guitar[]; //
|
||||||
|
extern const float32_t ir_3_guitar[]; |
||||||
|
extern const float32_t ir_4_guitar[]; |
||||||
|
extern const float32_t ir_5_guitar[]; |
||||||
|
extern const float32_t ir_6_guitar[]; |
||||||
|
extern const float32_t ir_7_bass[]; // bass cab
|
||||||
|
extern const float32_t ir_8_bass[]; |
||||||
|
extern const float32_t ir_9_bass[]; |
||||||
|
extern const float32_t ir_10_guitar[]; |
||||||
|
extern const float32_t ir_11_guitar[]; |
||||||
|
|
||||||
|
|
||||||
|
#endif // _FILTER_IR_CABSIM_IRS_H
|
Loading…
Reference in new issue