parent
a63ead7a27
commit
f5d1c01a58
@ -0,0 +1,183 @@ |
||||
// VoiceClipper.ino Bob Larkin W7PUA
|
||||
// Uses radioVoiceClipper_F32.h and .cpp. See the .h file for
|
||||
// more information and references. These clippers, when used with
|
||||
// radio systems, are for AM and FM (or NBFM) modulation, and NOT SSB.
|
||||
// See the similar class CESSBtransmit_F32 for SSB use.
|
||||
//
|
||||
// Tests with voice from SD Card file and a 1 second 750 Hz tone burst.
|
||||
// Input is from the WAV file on the SD card. Output is to the Teensy
|
||||
// Audio Adaptor left and right Line Outputs.
|
||||
//
|
||||
// The SD card may connect to different pins, depending on the
|
||||
// hardware you are using. Configure the SD card
|
||||
// pins to match your hardware. It is set for T4.x Rev D PJRC
|
||||
// Teensy Audio Adaptor card here.
|
||||
//
|
||||
// Your microSD card must have W9GR12.WAV file loaded to it, found at
|
||||
// https://github.com/chipaudette/OpenAudio_ArduinoLibrary/blob/master/utility/
|
||||
//
|
||||
// ******** This example runs at 48 ksps. *********
|
||||
// As of March 2023, this could be adapted to any rate, 11 to 12 or 44 to 50 ksps.
|
||||
//
|
||||
// This example code is in the public domain.
|
||||
|
||||
#include <Audio.h> |
||||
#include <Wire.h> |
||||
#include <SPI.h> |
||||
#include <SD.h> |
||||
#include "OpenAudio_ArduinoLibrary.h" |
||||
//#include "radioVoiceClipper_F32.h" part of "radioVoiceClipper_F32.h"
|
||||
|
||||
// T3.x supported sample rates: 2000, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 44117, 48000,
|
||||
// 88200, 88235 (44117*2), 95680, 96000, 176400, 176470, 192000
|
||||
// T4.x supports any sample rate the codec will handle.
|
||||
const float sample_rate_Hz = 48000.0f; |
||||
const int audio_block_samples = 128; // Always 128
|
||||
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples); |
||||
|
||||
AudioSynthWaveformSine_F32 sine1(audio_settings); |
||||
AudioSDPlayer_F32 playWav1(audio_settings); |
||||
AudioMixer4_F32 mixer4_0; |
||||
radioVoiceClipper_F32 clipper1(audio_settings); |
||||
AudioOutputI2S_F32 audioOutput(audio_settings); |
||||
AudioAnalyzeFFT1024_F32 fft1; |
||||
|
||||
AudioConnection_F32 patchCord0(playWav1, 0, mixer4_0, 0); |
||||
AudioConnection_F32 patchCord1(sine1, 0, mixer4_0, 1); |
||||
AudioConnection_F32 patchCord2(mixer4_0, 0, clipper1, 0); |
||||
AudioConnection_F32 patchCord3(clipper1, 0, audioOutput, 0); |
||||
AudioConnection_F32 patchCord4(clipper1, 0, audioOutput, 1); |
||||
AudioConnection_F32 patchCord5(clipper1, 0, fft1, 0); |
||||
AudioControlSGTL5000 sgtl5000_1; |
||||
|
||||
// Use these with the Teensy 4.x Rev D Audio Shield (NOT for T3.x)
|
||||
#define SDCARD_CS_PIN_Z 10 |
||||
#define SDCARD_MOSI_PIN_Z 11 |
||||
#define SDCARD_SCK_PIN_Z 13 |
||||
|
||||
// wavData is a global struct, definined in AudioSDPlayer_F32.h
|
||||
// This provides information about the current WAV file to this .INO
|
||||
struct wavData* pCurrentWavData; |
||||
// And data about the clipper
|
||||
struct levelClipper* pLevelData; |
||||
uint32_t writeOne = 0; |
||||
uint32_t cntFFT = 0; |
||||
uint32_t ttt; // For timing test audio
|
||||
|
||||
uint32_t tp; |
||||
|
||||
void setup() { // ********** SETUP **********
|
||||
Serial.begin(9600); delay(1000); |
||||
Serial.println("*** Test Voice Clipper from SD Card Voice Sample ***"); |
||||
|
||||
AudioMemory_F32(70, audio_settings); |
||||
pCurrentWavData = playWav1.getCurrentWavData(); |
||||
sgtl5000_1.enable(); |
||||
delay(500); |
||||
SPI.setMOSI(SDCARD_MOSI_PIN_Z); |
||||
SPI.setSCK(SDCARD_SCK_PIN_Z); |
||||
Serial.print("SD.begin() returns "); Serial.println(SD.begin(SDCARD_CS_PIN_Z)); |
||||
|
||||
sine1.frequency(750.0f); |
||||
sine1.amplitude(0.707107); |
||||
|
||||
clipper1.setSampleRate_Hz(48000.0f); |
||||
// Set input, correction, and output gains
|
||||
float32_t Pre_Clip_Gain = 1.5f; // Use to set amount of clipping, 1.0 to 2.0f, 3 is excessive
|
||||
// Correction gain=1.8 leaves max output overshoot at 2.2% for W9GR sample audio
|
||||
clipper1.setGains(Pre_Clip_Gain, 1.8f, 1.0f); |
||||
pLevelData = clipper1.getLevels(0); // Gets pointer to struct
|
||||
|
||||
audioOutput.setGain(0.02); // <<< Output volume control
|
||||
fft1.setOutputType(FFT_DBFS); |
||||
fft1.windowFunction(AudioWindowBlackmanHarris1024); |
||||
fft1.setNAverage(16); |
||||
ttt = millis(); // Time test audio
|
||||
tp=millis(); |
||||
} |
||||
|
||||
void playFile(const char *filename) { |
||||
if(playWav1.isPlaying()) |
||||
return; |
||||
Serial.println(""); |
||||
Serial.print("Playing file: "); |
||||
Serial.println(filename); |
||||
playWav1.play(filename); // Start playing the file.
|
||||
// A brief delay for the library read WAV info
|
||||
delay(25); |
||||
Serial.print("WAV file format = "); Serial.println(pCurrentWavData->audio_format); |
||||
Serial.print("WAV number channels = "); Serial.println(pCurrentWavData->num_channels); |
||||
Serial.print("WAV File Sample Rate = "); Serial.println(pCurrentWavData->sample_rate); |
||||
Serial.print("Number of bits per Sample = "); Serial.println(pCurrentWavData->bits); |
||||
Serial.print("File length, seconds = "); |
||||
Serial.println(0.001f*(float32_t)playWav1.lengthMillis(), 3); |
||||
} |
||||
|
||||
void loop() { |
||||
uint32_t tt=millis() - ttt; |
||||
if(tt < 2000) |
||||
{ |
||||
// Thanks to W9GR for the test file, W9GR48.WAV.
|
||||
playFile("W9GR48.WAV"); |
||||
mixer4_0.gain(0, 1.41421356f); // Play WAV file on
|
||||
mixer4_0.gain(1, 0.0f); // Sine Wave 1 off
|
||||
} |
||||
else if(tt > 12300 && tt<13300) |
||||
{ |
||||
mixer4_0.gain(1, 0.0f); // Play WAV file off
|
||||
// The following puts a 1-sec 750 Hz, full amplitude tone into the input
|
||||
// .707 on the generator and 1.414 here make the peak sine wave 1.000 at the CESSB input
|
||||
mixer4_0.gain(1, 1.41421356f); // Sine Wave 1 on, 750 Hz
|
||||
} |
||||
else if(tt >= 13300) |
||||
{ |
||||
mixer4_0.gain(0, 1.41421356f); // Play WAV file on
|
||||
mixer4_0.gain(1, 0.0f); // Sine Wave 1 off
|
||||
ttt = millis(); // Start again
|
||||
} |
||||
delay(1); |
||||
|
||||
// Un-comment the following to print out the spectrum
|
||||
/* if(fft1.available() && ++cntFFT>100 && cntFFT<102)
|
||||
{ |
||||
for(int kk=0; kk<512; kk++) |
||||
{ |
||||
Serial.print(46.875f*(float32_t)kk); Serial.print(","); |
||||
Serial.println(fft1.read(kk)); |
||||
} |
||||
} |
||||
*/ |
||||
if(clipper1.levelDataCount() > 300) // Typically 300 to 3000
|
||||
{ |
||||
clipper1.getLevels(1); // Cause write of data to struct & reset
|
||||
|
||||
// Detailed Report
|
||||
Serial.print(10.0f*log10f(pLevelData->pwr0)); |
||||
Serial.print(" In Ave Pwr Out "); |
||||
Serial.println(10.0f*log10f(pLevelData->pwr1)); |
||||
Serial.print(20.0f*log10f(pLevelData->peak0)); |
||||
Serial.print(" In Peak Out "); |
||||
Serial.println(20.0f*log10f(pLevelData->peak1)); |
||||
Serial.print(pLevelData->peak0, 6); |
||||
Serial.print(" In Peak Volts Out "); |
||||
Serial.println(pLevelData->peak1, 6); |
||||
Serial.print("Enhancement = "); |
||||
float32_t enhance = (10.0f*log10f(pLevelData->pwr1) - 20.0f*log10f(pLevelData->peak1)) - |
||||
(10.0f*log10f(pLevelData->pwr0) - 20.0f*log10f(pLevelData->peak0)); |
||||
if(enhance<1.0f) enhance = 1.0f; |
||||
Serial.print(enhance); Serial.println(" dB"); |
||||
|
||||
/*
|
||||
// CSV Report suitable for entering to spread sheet
|
||||
// InAve, InPk, OutAve, OutPk, EnhancementdB
|
||||
Serial.print(pLevelData->pwr0, 5); Serial.print(","); |
||||
Serial.print(pLevelData->peak0, 5); Serial.print(","); |
||||
Serial.print(pLevelData->pwr1, 5); Serial.print(","); |
||||
Serial.print(pLevelData->peak1, 5); Serial.print(","); |
||||
float32_t enhance = (10.0f*log10f(pLevelData->pwr1) - 20.0f*log10f(pLevelData->peak1)) - |
||||
(10.0f*log10f(pLevelData->pwr0) - 20.0f*log10f(pLevelData->peak0)); |
||||
if(enhance<1.0f) enhance = 1.0f; |
||||
Serial.println(enhance); |
||||
*/ |
||||
} |
||||
} |
@ -0,0 +1,184 @@ |
||||
// VoiceClipper12.ino Bob Larkin W7PUA
|
||||
// Uses radioVoiceClipper_F32.h and .cpp. See the .h file for
|
||||
// more information and references. These clippers, when used with
|
||||
// radio systems, are for AM and FM (or NBFM) modulation, and NOT SSB.
|
||||
// See the similar class CESSBtransmit_F32 for SSB use.
|
||||
//
|
||||
// Tests with voice from SD Card file and a 1 second 750 Hz tone burst.
|
||||
// Input is from the WAV file on the SD card. Output is to the Teensy
|
||||
// Audio Adaptor left and right Line Outputs.
|
||||
//
|
||||
// The SD card may connect to different pins, depending on the
|
||||
// hardware you are using. Configure the SD card
|
||||
// pins to match your hardware. It is set for T4.x Rev D PJRC
|
||||
// Teensy Audio Adaptor card here.
|
||||
//
|
||||
// Your microSD card must have W9GR12.WAV file loaded to it, found at
|
||||
// https://github.com/chipaudette/OpenAudio_ArduinoLibrary/blob/master/utility/
|
||||
//
|
||||
// ******** This example runs at 12 ksps. *********
|
||||
// As of March 2023, this could be adapted to any rate, 11 to 12 or 44 to 50 ksps.
|
||||
//
|
||||
// This example code is in the public domain.
|
||||
|
||||
#include <Audio.h> |
||||
#include <Wire.h> |
||||
#include <SPI.h> |
||||
#include <SD.h> |
||||
#include "OpenAudio_ArduinoLibrary.h" |
||||
//#include "radioVoiceClipper_F32.h" // part of OpenAudio_ArduinoLibrary.h
|
||||
|
||||
// T3.x supported sample rates: 2000, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 44117, 48000,
|
||||
// 88200, 88235 (44117*2), 95680, 96000, 176400, 176470, 192000
|
||||
// T4.x supports any sample rate the codec will handle.
|
||||
const float sample_rate_Hz = 12000.0f; |
||||
const int audio_block_samples = 128; // Always 128
|
||||
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples); |
||||
|
||||
AudioSynthWaveformSine_F32 sine1(audio_settings); |
||||
AudioSDPlayer_F32 playWav1(audio_settings); |
||||
AudioMixer4_F32 mixer4_0; |
||||
radioVoiceClipper_F32 clipper1(audio_settings); |
||||
AudioOutputI2S_F32 audioOutput(audio_settings); |
||||
AudioAnalyzeFFT1024_F32 fft1; |
||||
|
||||
AudioConnection_F32 patchCord0(playWav1, 0, mixer4_0, 0); |
||||
AudioConnection_F32 patchCord1(sine1, 0, mixer4_0, 1); |
||||
AudioConnection_F32 patchCord2(mixer4_0, 0, clipper1, 0); |
||||
AudioConnection_F32 patchCord3(clipper1, 0, audioOutput, 0); |
||||
AudioConnection_F32 patchCord4(clipper1, 0, audioOutput, 1); |
||||
AudioConnection_F32 patchCord5(clipper1, 0, fft1, 0); |
||||
AudioControlSGTL5000 sgtl5000_1; |
||||
|
||||
// Use these with the Teensy 4.x Rev D Audio Shield (NOT for T3.x)
|
||||
#define SDCARD_CS_PIN_Z 10 |
||||
#define SDCARD_MOSI_PIN_Z 11 |
||||
#define SDCARD_SCK_PIN_Z 13 |
||||
|
||||
// wavData is a global struct, definined in AudioSDPlayer_F32.h
|
||||
// This provides information about the current WAV file to this .INO
|
||||
struct wavData* pCurrentWavData; |
||||
// And data about the clipper
|
||||
struct levelClipper* pLevelData; |
||||
uint32_t writeOne = 0; |
||||
uint32_t cntFFT = 0; |
||||
uint32_t ttt; // For timing test audio
|
||||
|
||||
uint32_t tp; |
||||
|
||||
void setup() { // ********** SETUP **********
|
||||
Serial.begin(9600); delay(1000); |
||||
Serial.println("*** Test Voice Clipper from SD Card Voice Sample ***"); |
||||
|
||||
AudioMemory_F32(70, audio_settings); |
||||
pCurrentWavData = playWav1.getCurrentWavData(); |
||||
sgtl5000_1.enable(); |
||||
delay(500); |
||||
SPI.setMOSI(SDCARD_MOSI_PIN_Z); |
||||
SPI.setSCK(SDCARD_SCK_PIN_Z); |
||||
Serial.print("SD.begin() returns "); Serial.println(SD.begin(SDCARD_CS_PIN_Z)); |
||||
|
||||
sine1.frequency(750.0f); |
||||
sine1.amplitude(0.707107); |
||||
|
||||
clipper1.setSampleRate_Hz(12000.0f); |
||||
// Set input, correction, and output gains
|
||||
float32_t Pre_Clip_Gain = 1.5f; // Use to set amount of clipping, 1.0 to 2.0f, 3 is excessive
|
||||
// Correction gain=1.8 leaves max output overshoot at 2.2% for W9GR sample audio
|
||||
clipper1.setGains(Pre_Clip_Gain, 1.8f, 1.0f); |
||||
pLevelData = clipper1.getLevels(0); // Gets pointer to struct
|
||||
|
||||
audioOutput.setGain(0.02); // <<< Output volume control
|
||||
fft1.setOutputType(FFT_DBFS); |
||||
fft1.windowFunction(AudioWindowBlackmanHarris1024); |
||||
fft1.setNAverage(16); |
||||
ttt = millis(); // Time test audio
|
||||
tp=millis(); |
||||
} |
||||
|
||||
void playFile(const char *filename) { |
||||
if(playWav1.isPlaying()) |
||||
return; |
||||
Serial.println(""); |
||||
Serial.print("Playing file: "); |
||||
Serial.println(filename); |
||||
playWav1.play(filename); // Start playing the file.
|
||||
// A brief delay for the library read WAV info
|
||||
delay(25); |
||||
Serial.print("WAV file format = "); Serial.println(pCurrentWavData->audio_format); |
||||
Serial.print("WAV number channels = "); Serial.println(pCurrentWavData->num_channels); |
||||
Serial.print("WAV File Sample Rate = "); Serial.println(pCurrentWavData->sample_rate); |
||||
Serial.print("Number of bits per Sample = "); Serial.println(pCurrentWavData->bits); |
||||
Serial.print("File length, seconds = "); |
||||
Serial.println(0.001f*(float32_t)playWav1.lengthMillis(), 3); |
||||
} |
||||
|
||||
void loop() { |
||||
uint32_t tt=millis() - ttt; |
||||
if(tt < 2000) |
||||
{ |
||||
// Thanks to W9GR for the test file, W9GR482.WAV from which '12' was derived..
|
||||
playFile("W9GR12.WAV"); |
||||
mixer4_0.gain(0, 1.41421356f); // Play WAV file on
|
||||
mixer4_0.gain(1, 0.0f); // Sine Wave 1 off
|
||||
} |
||||
else if(tt > 12300 && tt<13300) |
||||
{ |
||||
mixer4_0.gain(1, 0.0f); // Play WAV file off
|
||||
// The following puts a 1-sec 750 Hz, full amplitude tone into the input
|
||||
// .707 on the generator and 1.414 here make the peak sine wave 1.000 at the CESSB input
|
||||
mixer4_0.gain(1, 1.41421356f); // Sine Wave 1 on, 750 Hz
|
||||
} |
||||
else if(tt >= 13300) |
||||
{ |
||||
mixer4_0.gain(0, 1.41421356f); // Play WAV file on
|
||||
mixer4_0.gain(1, 0.0f); // Sine Wave 1 off
|
||||
ttt = millis(); // Start again
|
||||
} |
||||
delay(1); |
||||
|
||||
// Un-comment the following to print out the spectrum
|
||||
if(fft1.available() && ++cntFFT>10 && cntFFT<12) // For 12 ksps, about 8 seconds in
|
||||
{ |
||||
for(int kk=0; kk<512; kk++) |
||||
{ |
||||
Serial.print(11.71875f*(float32_t)kk); Serial.print(","); |
||||
Serial.println(fft1.read(kk)); |
||||
} |
||||
} |
||||
|
||||
if(clipper1.levelDataCount() > 300) // Typically 300 to 3000
|
||||
{ |
||||
clipper1.getLevels(1); // Cause write of data to struct & reset
|
||||
|
||||
/* // Detailed Report
|
||||
Serial.print(10.0f*log10f(pLevelData->pwr0)); |
||||
Serial.print(" In Ave Pwr Out "); |
||||
Serial.println(10.0f*log10f(pLevelData->pwr1)); |
||||
Serial.print(20.0f*log10f(pLevelData->peak0)); |
||||
Serial.print(" In Peak Out "); |
||||
Serial.println(20.0f*log10f(pLevelData->peak1)); |
||||
Serial.print(pLevelData->peak0, 6); |
||||
Serial.print(" In Peak Volts Out "); |
||||
Serial.println(pLevelData->peak1, 6); |
||||
Serial.print("Enhancement = "); |
||||
float32_t enhance = (10.0f*log10f(pLevelData->pwr1) - 20.0f*log10f(pLevelData->peak1)) - |
||||
(10.0f*log10f(pLevelData->pwr0) - 20.0f*log10f(pLevelData->peak0)); |
||||
if(enhance<1.0f) enhance = 1.0f; |
||||
Serial.print(enhance); Serial.println(" dB"); |
||||
*/ |
||||
|
||||
/*
|
||||
// CSV Report suitable for entering to spread sheet
|
||||
// InAve, InPk, OutAve, OutPk, EnhancementdB
|
||||
Serial.print(pLevelData->pwr0, 5); Serial.print(","); |
||||
Serial.print(pLevelData->peak0, 5); Serial.print(","); |
||||
Serial.print(pLevelData->pwr1, 5); Serial.print(","); |
||||
Serial.print(pLevelData->peak1, 5); Serial.print(","); |
||||
float32_t enhance = (10.0f*log10f(pLevelData->pwr1) - 20.0f*log10f(pLevelData->peak1)) - |
||||
(10.0f*log10f(pLevelData->pwr0) - 20.0f*log10f(pLevelData->peak0)); |
||||
if(enhance<1.0f) enhance = 1.0f; |
||||
Serial.println(enhance); |
||||
*/ |
||||
} |
||||
} |
Binary file not shown.
@ -0,0 +1,184 @@ |
||||
/*
|
||||
* radioVoiceClipper_F32.cpp |
||||
* |
||||
* Bob Larkin, in support of the library: |
||||
* Chip Audette, OpenAudio, Dec 2022 |
||||
* MIT License, Use at your own risk. |
||||
* |
||||
* See radioVoiceClipper_F32.h for technical info. |
||||
*/ |
||||
// NOTE: 96 ksps sample rate not yet implemented
|
||||
|
||||
#include "radioVoiceClipper_F32.h" |
||||
|
||||
void radioVoiceClipper_F32::update(void) { |
||||
audio_block_f32_t *blockIn, *blockOut ; |
||||
|
||||
// Temporary storage. Max size for 12 ksps where 128 points at input
|
||||
// and 256 at interpolated 24ksps
|
||||
float32_t workingData[256]; |
||||
float32_t delayedDataI[256]; // Allows batching of 64 data points
|
||||
float32_t diffI[256]; |
||||
|
||||
if(sampleRate!=VC_SAMPLE_RATE_11_12 && sampleRate!=VC_SAMPLE_RATE_44_50 && sampleRate!=VC_SAMPLE_RATE_88_100) |
||||
return; |
||||
// Get all needed resources, or return if not available.
|
||||
blockIn = AudioStream_F32::receiveReadOnly_f32(); |
||||
if (!blockIn) |
||||
{ return; } |
||||
blockOut = AudioStream_F32::allocate_f32(); |
||||
if (!blockOut) |
||||
{ |
||||
AudioStream_F32::release(blockIn); |
||||
return; |
||||
} |
||||
|
||||
// The audio input peak levels for start of clipping are -1.0, 1.0
|
||||
// when gainIn==1.0.
|
||||
// uint32_t ttt=micros();
|
||||
|
||||
if(sampleRate==VC_SAMPLE_RATE_11_12) |
||||
{ |
||||
// No decimation, 128 samples
|
||||
for(int k=0; k<128; k++) |
||||
workingData[k] = blockIn->data[k]; |
||||
// We now have nW=128 (for 12 ksps) samples to process
|
||||
} |
||||
else if(sampleRate==VC_SAMPLE_RATE_44_50) |
||||
{ |
||||
// Decimate 48 ksps to 12 ksps, 128 to 32 samples
|
||||
// or 96 ksps to 12 ksps, 128 to 16 samples
|
||||
arm_fir_decimate_f32(&decimateInst, &(blockIn->data[0]), |
||||
&workingData[0], 128); |
||||
// We now have nW=32 (for 48 ksps) or 16 (for 96 ksps) samples to process
|
||||
} |
||||
// Measure input power and peak envelope, before any clipping.
|
||||
for(int k=0; k<nW; k++) |
||||
{ |
||||
float32_t pwrWorkingData = workingData[k]*workingData[k]; // Replace with absf() <<<<<<<<<<<<<<<<<<<<<<<<
|
||||
float32_t vWD = sqrtf(pwrWorkingData); // Envelope
|
||||
powerSum0 += pwrWorkingData; |
||||
if(vWD > maxMag0) |
||||
maxMag0 = vWD; // Peak envelope
|
||||
countPower0++; |
||||
} |
||||
|
||||
for(int k=0; k<nW; k++) |
||||
{ |
||||
workingData[k] *= gainIn; // Sets the amount of clipping for 1.0 in
|
||||
//Serial.println(workingData[k]);
|
||||
} |
||||
|
||||
// Interpolate by 2 up to 24 ksps rate
|
||||
for(int k=0; k<nW; k++) // 48 ksps: 0 to 31
|
||||
{ |
||||
int k2 = 2*(nW - k) - 1; // 48 ksps: 63 to 1
|
||||
// Zero pack, working from the bottom to not overwrite
|
||||
workingData[k2] = 0.0f; // 48 ksps: 64 element array
|
||||
workingData[k2-1] = workingData[nW-k-1]; |
||||
} |
||||
|
||||
// LPF with gain of 2 built into coefficients, correct for added zeros.
|
||||
arm_fir_f32(&firInstInterpolate1I, workingData, workingData, nC); |
||||
// workingData are now at 24 ksps and ready for clipping
|
||||
// For input 48 ksps this produces 64 numbers
|
||||
|
||||
for(int kk=0; kk<nC; kk++) |
||||
{ |
||||
float32_t power = workingData[kk]*workingData[kk]; // Change to absf()
|
||||
float32_t mag = sqrtf(power); |
||||
if(mag > 1.0f) // This the clipping, scaled to 1.0, desired max
|
||||
{ |
||||
workingData[kk] /= mag; |
||||
} |
||||
} |
||||
|
||||
// clipperIn needs spectrum control, so LP filter it.
|
||||
// Both BW of the signal and the sample rate have been doubled.
|
||||
arm_fir_f32(&firInstClipperI, workingData, workingData, nC); |
||||
|
||||
// Ready to compensate for filter overshoots
|
||||
for (int k=0; k<nC; k++) |
||||
{ |
||||
// Circular delay line for signal to align data with FIR output
|
||||
// Put I & Q data points into the delay arrays
|
||||
osDelayI[indexOsDelay & 0X3F] = workingData[k]; |
||||
// Remove 64 points delayed data from line and save for later
|
||||
delayedDataI[k] = osDelayI[(indexOsDelay - 63) & 0X3F]; |
||||
indexOsDelay++; |
||||
// Delay line to allow strongest envelope to be used for compensation
|
||||
// We only need to look ahead 1 or behind 1, so delay line of 4 is OK.
|
||||
// Enter latest envelope to delay array
|
||||
osEnv[indexOsEnv & 0X03] = sqrtf( |
||||
workingData[k]*workingData[k]); // + workingDataQ[k]*workingDataQ[k]);
|
||||
|
||||
// look over the envelope curve to find the max
|
||||
float32_t eMax = 0.0f; |
||||
if(osEnv[(indexOsEnv) & 0X03] > eMax) // Data point just entered
|
||||
eMax = osEnv[(indexOsEnv) & 0X03]; |
||||
if(osEnv[(indexOsEnv-1) & 0X03] > eMax) // Entered one before
|
||||
eMax = osEnv[(indexOsEnv-1) & 0X03]; |
||||
if(osEnv[(indexOsEnv-2) & 0X03] > eMax) // Entered one before that
|
||||
eMax = osEnv[(indexOsEnv-2) & 0X03]; |
||||
if(eMax < 1.0f) |
||||
eMax = 1.0f; // Below clipping region
|
||||
indexOsEnv++; |
||||
|
||||
// Clip the signal to 1.0. -2 allows 1 look ahead on signal.
|
||||
float32_t eCorrectedI = osDelayI[(indexOsDelay - 2) & 0X3F] / eMax; |
||||
// Filtering is linear, so we only need to filter the difference between
|
||||
// the signal and the clipper output. This needs less filtering, as the
|
||||
// difference is many dB below the signal to begin with. Hershberger 2014
|
||||
diffI[k] = osDelayI[(indexOsDelay - 2) & 0X3F] - eCorrectedI; |
||||
} // End, for k=0 to 63
|
||||
|
||||
// Filter the differences, osFilter has 123 taps and 61 delay
|
||||
arm_fir_f32(&firInstOShootI, diffI, diffI, nC); |
||||
|
||||
// Do the overshoot compensation
|
||||
for(int k=0; k<nC; k++) |
||||
{ |
||||
workingData[k] = delayedDataI[k] - gainCompensate*diffI[k]; |
||||
} |
||||
|
||||
// Measure average output power and peak envelope, after CESSB
|
||||
// but before gainOut
|
||||
for(int k=0; k<nC; k++) |
||||
{ |
||||
float32_t pwrOut = workingData[k]*workingData[k]; |
||||
float32_t vWD = sqrtf(pwrOut); // Envelope
|
||||
powerSum1 += pwrOut; |
||||
if(vWD > maxMag1) |
||||
maxMag1 = vWD; // Peak envelope
|
||||
countPower1++; |
||||
} |
||||
|
||||
|
||||
if(sampleRate==VC_SAMPLE_RATE_11_12) |
||||
{ |
||||
// Decimat24 to 12, 128 samples out. No LPF needed as we just did that
|
||||
for(int k=0; k<128; k++) |
||||
blockOut->data[k] = workingData[2*k]; |
||||
} |
||||
else if(sampleRate==VC_SAMPLE_RATE_44_50) |
||||
{ |
||||
// Finally interpolate to 48 or 96 ksps. Data is in workingData[k]
|
||||
// and is 64 samples for audio 48 ksps.
|
||||
for(int k=0; k<nC; k++) // Audio sampling at 48 ksps: 0 to 63
|
||||
{ |
||||
int k2 = 2*(nC - k) - 1; // 48 ksps 63 to 1
|
||||
// Zero pack, working from the bottom to not overwrite
|
||||
workingData[k2] = 0.0f; |
||||
workingData[k2-1] = gainOut*workingData[nC-k-1]; // gainOut does not change CESSB
|
||||
} |
||||
// LPF with gain of 2 built into coefficients, correct for zeros.
|
||||
arm_fir_f32(&firInstInterpolate2I, workingData, &blockOut->data[0], 128); |
||||
// Voltage gain from blockIn->data to here for small sine wave is 1.0
|
||||
} |
||||
AudioStream_F32::transmit(blockOut, 0); // send the outputs
|
||||
AudioStream_F32::release(blockIn); // Release the blocks
|
||||
AudioStream_F32::release(blockOut); |
||||
|
||||
jjj++; //For test printing
|
||||
// Serial.println(micros() - ttt);
|
||||
} // end update()
|
@ -0,0 +1,343 @@ |
||||
/*
|
||||
* radioVoiceClipper_F32.h |
||||
* |
||||
* 12 March 2023 (c) copyright Bob Larkin |
||||
* But with With much credit to: |
||||
* Chip Audette (OpenAudio) |
||||
* and of course, to PJRC for the Teensy and Teensy Audio Library |
||||
* |
||||
* The development of this Voice Clipper was by Bob Larkin, W7PUA, based |
||||
* entirely on ideas and suggestions from Dave Hershberger, W9GR. |
||||
* Many thanks to Dave. Note that this clipper is is a "real variable" |
||||
* version of the Single Sideband CESSB clipper. See the companion
|
||||
* radioCESSBtransmit_F32.h class which uses all the same principles. |
||||
*
|
||||
* The input signal is a voice (or tones) that will, in general, have |
||||
* been compressed in amplitude, keeping the maximum amplitude close to |
||||
* 1.0 peak-to-center. For this class, clipping occurs for any input
|
||||
* greater than 1/gainIn where gainIn comes from the public function |
||||
* setGains(). Normally gainIn has a value around 1.5 and so clipping occurs |
||||
* for inputs above peak levels of 2/3=0.667. For this level of gaiIn, |
||||
* there will be about 3 dB of increase in the average power of the voice |
||||
* but still minimal perception of "over-processing." |
||||
*
|
||||
* Internally the audio is clipped at the higher levels and the resulting |
||||
* out-of-band distion is low pass filtered. Next, the overshoot that |
||||
* occurs with the filter is removed by measuring the overshoot, low-pass |
||||
* filtering the overshoot and subtracting it off. All this requires |
||||
* care with the timing as all of the filtering steps involve delays. |
||||
*
|
||||
* The compressor2 class in this F32 library is intended to precede this |
||||
* class. |
||||
*
|
||||
* NOTE: Do NOT follow this block with any non-linear phase filtering, |
||||
* such as IIR. Minimize any linear-phase filtering such as FIR. |
||||
* Such activities enhance the overshoots and defeat the purpose of clipping. |
||||
*
|
||||
* An important note: This clipper is suitable for voice modes, such as |
||||
* AM or NBFM. Do not use this clipper ahead of a single sideband |
||||
* transmitter. That is what the CESSB class is for. |
||||
* |
||||
* The following reference has information on CESSB, in detail, as well |
||||
* as on the use of clippers, similar to this one, in broadcast work: |
||||
* Hershberger, D.L. (2014): Controlled Envelope Single Sideband. QEX |
||||
* November/December 2014 pp3-13. |
||||
* http://www.arrl.org/files/file/QEX_Next_Issue/2014/Nov-Dec_2014/Hershberger_QEX_11_14.pdf
|
||||
* |
||||
* Status: Experimental |
||||
* |
||||
* Inputs: 0 is voice audio input |
||||
* Outputs: 0 is clipped voice. |
||||
* |
||||
* Functions, available during operation: |
||||
* void setSampleRate_Hz(float32_t fs_Hz) Allows dynamic sample rate change. |
||||
* |
||||
* struct levels* getLevels(int what) { |
||||
* what = 0 returns a pointer to struct levels before data is ready |
||||
* what = 1 returns a pointer to struct levels |
||||
* |
||||
* uint32_t levelDataCount() return countPower0 |
||||
* |
||||
* void setGains(float32_t gIn, float32_t gCompensate, float32_t gOut) |
||||
* |
||||
* Time: T3.6 For an update of a 128 sample block, estimated microseconds |
||||
* T4.0 For an update of a 128 sample block, measured microseconds |
||||
* These times are for a 48 ksps rate. |
||||
* |
||||
* NOTE: Do NOT follow this block with any non-linear phase filtering, |
||||
* such as IIR. Minimize any linear-phase filtering such as FIR. |
||||
* Such activities enhance the overshoots and defeat the purpose of clipping. |
||||
*/ |
||||
|
||||
#ifndef _radioVoiceClipper_f32_h |
||||
#define _radioVoiceClipper_f32_h |
||||
|
||||
#include "Arduino.h" |
||||
#include "AudioStream_F32.h" |
||||
#include "arm_math.h" |
||||
#include "mathDSP_F32.h" |
||||
|
||||
#define VC_SAMPLE_RATE_0 0 |
||||
#define VC_SAMPLE_RATE_11_12 1 |
||||
#define VC_SAMPLE_RATE_44_50 2 |
||||
#define VC_SAMPLE_RATE_88_100 3 |
||||
|
||||
#ifndef M_PI |
||||
#define M_PI 3.141592653589793f |
||||
#endif |
||||
|
||||
#ifndef M_PI_2 |
||||
#define M_PI_2 1.570796326794897f |
||||
#endif |
||||
|
||||
#ifndef M_TWOPI |
||||
#define M_TWOPI (M_PI * 2.0f) |
||||
#endif |
||||
|
||||
// For the average power and peak voltage readings, global
|
||||
struct levelClipper { |
||||
float32_t pwr0; |
||||
float32_t peak0; |
||||
float32_t pwr1; |
||||
float32_t peak1; |
||||
uint32_t countP; // Number of averaged samples for pwr0.
|
||||
}; |
||||
|
||||
class radioVoiceClipper_F32 : public AudioStream_F32 { |
||||
//GUI: inputs:1, outputs:2 //this line used for automatic generation of GUI node
|
||||
//GUI: shortName:CESSBTransmit //this line used for automatic generation of GUI node
|
||||
public: |
||||
radioVoiceClipper_F32(void) : |
||||
AudioStream_F32(1, inputQueueArray_f32) |
||||
{ |
||||
setSampleRate_Hz(AUDIO_SAMPLE_RATE); |
||||
//uses default AUDIO_SAMPLE_RATE from AudioStream.h
|
||||
//setBlockLength(128); Always default 128
|
||||
} |
||||
|
||||
radioVoiceClipper_F32(const AudioSettings_F32 &settings) : |
||||
AudioStream_F32(1, inputQueueArray_f32) |
||||
{ |
||||
setSampleRate_Hz(settings.sample_rate_Hz); |
||||
//setBlockLength(128); Always default 128
|
||||
} |
||||
|
||||
// Sample rate starts at default 44.1 ksps. That will work. Filters
|
||||
// are designed for 48 and 96 ksps, however. This is a *required*
|
||||
// function at setup().
|
||||
void setSampleRate_Hz(const float _fs_Hz) { |
||||
sample_rate_Hz = _fs_Hz; |
||||
if(sample_rate_Hz>10900.0f && sample_rate_Hz<12600.0f) |
||||
{ |
||||
// Design point is 12 ksps. No initial decimation. Interpolate
|
||||
// to 24 ksps for clipping and then decimate back to 12 at the end.
|
||||
sampleRate = VC_SAMPLE_RATE_11_12; |
||||
nW = 128; |
||||
nC = 256; |
||||
countLevelMax = 10; // About 0.1 sec for 12 ksps
|
||||
inverseMaxCount = 1.0f/(float32_t)countLevelMax; |
||||
arm_fir_init_f32(&firInstInterpolate1I, 23, (float32_t*)interpolateFilter1, |
||||
&pStateInterpolate1I[0], nC); |
||||
arm_fir_init_f32(&firInstClipperI, 123, (float32_t*)clipperOut, |
||||
&pStateClipperI[0], nC); |
||||
arm_fir_init_f32(&firInstOShootI, 123, (float32_t*)clipperOut, |
||||
&pStateOShootI[0], nC); |
||||
} |
||||
else if(sample_rate_Hz>43900.0f && sample_rate_Hz<50100.0f) |
||||
{ |
||||
// Design point is 48 ksps
|
||||
sampleRate = VC_SAMPLE_RATE_44_50; |
||||
nW = 32; |
||||
nC = 64; |
||||
countLevelMax = 37; // About 0.1 sec for 48 ksps
|
||||
inverseMaxCount = 1.0f/(float32_t)countLevelMax; |
||||
arm_fir_decimate_init_f32(&decimateInst, 65, 4, |
||||
(float32_t*)decimateFilter48, &pStateDecimate[0], 128); |
||||
arm_fir_init_f32(&firInstInterpolate1I, 23, (float32_t*)interpolateFilter1, |
||||
&pStateInterpolate1I[0], nC); |
||||
arm_fir_init_f32(&firInstClipperI, 123, (float32_t*)clipperOut, |
||||
&pStateClipperI[0], nC); |
||||
arm_fir_init_f32(&firInstOShootI, 123, (float32_t*)clipperOut, |
||||
&pStateOShootI[0], nC); |
||||
arm_fir_init_f32(&firInstInterpolate2I, 23, (float32_t*)interpolateFilter1, |
||||
&pStateInterpolate2I[0], nC); |
||||
} |
||||
else if(sample_rate_Hz>88000.0f && sample_rate_Hz<100100.0f) |
||||
{ |
||||
// GET THINGS WORKING AT VC_SAMPLE_RATE_44_50 FIRST AND THEN FIX UP 96 ksps
|
||||
// Design point is 96 ksps
|
||||
/* sampleRate = VC_SAMPLE_RATE_88_100; //<<<<<<<<<<<<<<<<<<<<<<FIXUP
|
||||
nW = 16; |
||||
nC = 32; |
||||
countLevelMax = 75; // About 0.1 sec for 96 ksps
|
||||
inverseMaxCount = 1.0f/(float32_t)countLevelMax; |
||||
arm_fir_decimate_init_f32 (&decimateInst, 55, 4, |
||||
(float32_t*)decimateFilter48, pStateDecimate, 128); |
||||
arm_fir_init_f32(&firInstClipper, 199, basebandFilter, |
||||
&StateFirClipperF32[0], 128); |
||||
*/ |
||||
} |
||||
else |
||||
{ |
||||
// Unsupported sample rate
|
||||
sampleRate = VC_SAMPLE_RATE_0; |
||||
nW = 1; |
||||
nC = 1; |
||||
} |
||||
newLevelDataReady = false; |
||||
} |
||||
|
||||
struct levelClipper* getLevels(int what) { |
||||
if(what != 0) // 0 leaves a way to get pointer before data is ready
|
||||
{ |
||||
levelData.pwr0 = powerSum0/((float32_t)countPower0); |
||||
levelData.peak0 = maxMag0; |
||||
levelData.pwr1 = powerSum1/(float32_t)countPower1; |
||||
levelData.peak1 = maxMag1; |
||||
levelData.countP = countPower0; |
||||
|
||||
// Automatic reset for next set of readings
|
||||
powerSum0 = 0.0f; |
||||
maxMag0 = -1.0f; |
||||
powerSum1 = 0.0f; |
||||
maxMag1 = -1.0f; |
||||
countPower0 = 0; |
||||
countPower1 = 0; |
||||
} |
||||
return &levelData; |
||||
} |
||||
|
||||
uint32_t levelDataCount(void) { |
||||
return countPower0; // Input count, out may be different
|
||||
} |
||||
|
||||
void setGains(float32_t gIn, float32_t gCompensate, float32_t gOut) |
||||
{ |
||||
gainIn = gIn; |
||||
gainCompensate = gCompensate; |
||||
gainOut = gOut; |
||||
} |
||||
|
||||
virtual void update(void); |
||||
|
||||
private: |
||||
void sincos_Z_(float32_t ph); |
||||
struct levelClipper levelData; |
||||
audio_block_f32_t *inputQueueArray_f32[1]; |
||||
uint32_t jjj = 0; // Used for diagnostic printing
|
||||
|
||||
// Input/Output is at 12, 48 or 96 ksps.
|
||||
// Clipping and overshoot processing is at 24 ksps.
|
||||
// Next line is to indicate that setSampleRateHz() has not executed
|
||||
int sampleRate = VC_SAMPLE_RATE_0; |
||||
float32_t sample_rate_Hz = AUDIO_SAMPLE_RATE; // 44.1 ksps
|
||||
int16_t nW = 32; // 128, 32 or 16
|
||||
int16_t nC = 64; // 256, 64 or 32
|
||||
uint16_t block_length = 128; |
||||
|
||||
float32_t pStateDecimate[128 + 65 - 1]; // Goes with CMSIS decimate function
|
||||
arm_fir_decimate_instance_f32 decimateInst; |
||||
|
||||
// For 12 ksps case, 24 kHz clipper uses 256 points
|
||||
float32_t pStateInterpolate1I[256 + 23 - 1]; // For interpolate 12 to 24 ksps
|
||||
arm_fir_instance_f32 firInstInterpolate1I; |
||||
|
||||
float32_t pStateClipperI[256 + 123 - 1]; // Goes with Clipper filter
|
||||
arm_fir_instance_f32 firInstClipperI; // at 24 ksps
|
||||
|
||||
float32_t pStateOShootI[256+123-1]; |
||||
arm_fir_instance_f32 firInstOShootI; |
||||
|
||||
float32_t pStateInterpolate2I[256 + 23 - 1]; // For interpolate 12 to 24 ksps
|
||||
arm_fir_instance_f32 firInstInterpolate2I; |
||||
|
||||
float32_t gainIn = 1.0f; |
||||
float32_t gainCompensate = 1.4f; |
||||
float32_t gainOut = 1.0f; // Does not change Clipping, here for convenience to set out level
|
||||
|
||||
// A tiny delay to allow negative time for the previous path
|
||||
float32_t osEnv[4]; |
||||
uint16_t indexOsEnv = 4; // 0 to 3 by using a 2-bit mask
|
||||
|
||||
// We need a delay for overshoot remove to account for the FIR
|
||||
// filter in the correction path. Some where around 128 taps works
|
||||
// but if we make the delay exactly 2^6=64 the delay line is simple
|
||||
// resulting in a FIR size of 2*64+1=129 taps.
|
||||
float32_t osDelayI[64]; |
||||
uint16_t indexOsDelay = 64; |
||||
|
||||
// RMS and Peak variable for monitoring levels and changes to the
|
||||
// Peak to RMS ratio. These are temporary storage. Data is
|
||||
// transferred by global levelData struct at the top of this file.
|
||||
float32_t powerSum0 = 0.0f; |
||||
float32_t maxMag0 = -1.0f; |
||||
float32_t powerSum1 = 0.0f; |
||||
float32_t maxMag1 = -1.0f; |
||||
uint32_t countPower0 = 0; |
||||
uint32_t countPower1 = 0; |
||||
|
||||
bool newLevelDataReady = false; |
||||
int countLevel = 0; |
||||
int countLevelMax = 37; // About 0.1 sec for 48 ksps
|
||||
float32_t inverseMaxCount = 1.0f/(float32_t)countLevelMax; |
||||
|
||||
/* Input filter for decimate by 4:
|
||||
* FIR filter designed with http://t-filter.appspot.com
|
||||
* Sampling frequency: 48000 Hz |
||||
* 0 Hz - 3000 Hz ripple = 0.075 dB |
||||
* 6000 Hz - 24000 Hz atten = -95.93 dB */ |
||||
const float32_t decimateFilter48[65] = { |
||||
0.00004685f, 0.00016629f, 0.00038974f, 0.00073279f, 0.00113663f, 0.00148721f, |
||||
0.00159057f, 0.00125129f, 0.00032821f,-0.00114283f,-0.00289782f,-0.00441933f, |
||||
-0.00505118f,-0.00418143f,-0.00151748f, 0.00268876f, 0.00751487f, 0.01147689f, |
||||
0.01286243f, 0.01027735f, 0.00323528f,-0.00737003f,-0.01913035f,-0.02842381f, |
||||
-0.03117447f,-0.02390063f,-0.00480378f, 0.02544011f, 0.06344286f, 0.10357132f, |
||||
0.13904464f, 0.16342506f, 0.17210799f, 0.16342506f, 0.13904464f, 0.10357132f, |
||||
0.06344286f, 0.02544011f,-0.00480378f,-0.02390063f,-0.03117447f,-0.02842381f, |
||||
-0.01913035f,-0.00737003f, 0.00323528f, 0.01027735f, 0.01286243f, 0.01147689f, |
||||
0.00751487f, 0.00268876f,-0.00151748f,-0.00418143f,-0.00505118f,-0.00441933f, |
||||
-0.00289782f,-0.00114283f, 0.00032821f, 0.00125129f, 0.00159057f, 0.00148721f, |
||||
0.00113663f, 0.00073279f, 0.00038974f, 0.00016629f, 0.00004685}; |
||||
|
||||
/* Filter for outputs of clipper
|
||||
* Use also overshoot corrector, but might be able to use less terms. |
||||
* FIR filter designed with http://t-filter.appspot.com
|
||||
* Sample frequency: 24000 Hz |
||||
* 0 Hz - 2800 Hz ripple = 0.14 dB |
||||
* 3200 Hz - 12000 Hz atten = 40.51 dB */ |
||||
const float32_t clipperOut[123] = { |
||||
-0.003947255f, 0.001759588f, 0.002221444f, 0.002407244f, 0.001833343f, 0.000524622f, |
||||
-0.000946260f,-0.001768428f,-0.001395297f, 0.000055916f, 0.001779024f, 0.002694998f, |
||||
0.002099736f, 0.000157764f,-0.002092190f,-0.003282801f,-0.002542927f,-0.000116969f, |
||||
0.002694319f, 0.004153363f, 0.003197589f, 0.000143560f,-0.003346600f,-0.005148200f, |
||||
-0.003947437f,-0.000152425f, 0.004166345f, 0.006378882f, 0.004871469f, 0.000164557f, |
||||
-0.005173898f,-0.007896395f,-0.006014470f,-0.000173552f, 0.006447615f, 0.009828080f, |
||||
0.007480359f, 0.000184482f,-0.008116957f,-0.012379161f,-0.009436712f,-0.000194737f, |
||||
0.010412610f, 0.015941971f, 0.012213107f, 0.000200845f,-0.013823966f,-0.021360759f, |
||||
-0.016552097f,-0.000205707f, 0.019544260f, 0.030836344f, 0.024523278f, 0.000211298f, |
||||
-0.031509151f,-0.052450055f,-0.044811840f,-0.000214078f, 0.074661107f, 0.158953216f, |
||||
0.225159581f, 0.250214862f, 0.225159581f, 0.158953216f, 0.074661107f,-0.000214078f, |
||||
-0.044811840f,-0.052450055f,-0.031509151f, 0.000211298f, 0.024523278f, 0.030836344f, |
||||
0.019544260f,-0.000205707f,-0.016552097f,-0.021360759f,-0.013823966f, 0.000200845f, |
||||
0.012213107f, 0.015941971f, 0.010412610f,-0.000194737f,-0.009436712f,-0.012379161f, |
||||
-0.008116957f, 0.000184482f, 0.007480359f, 0.009828080f, 0.006447615f,-0.000173552f, |
||||
-0.006014470f,-0.007896395f,-0.005173898f, 0.000164557f, 0.004871469f, 0.006378882f, |
||||
0.004166345f,-0.000152425f,-0.003947437f,-0.005148200f,-0.003346600f, 0.000143560f, |
||||
0.003197589f, 0.004153363f, 0.002694319f,-0.000116969f,-0.002542927f,-0.003282801f, |
||||
-0.002092190f, 0.000157764f, 0.002099736f, 0.002694998f, 0.001779024f, 0.000055916f, |
||||
-0.001395297f,-0.001768428f,-0.000946260f, 0.000524622f, 0.001833343f, 0.002407244f, |
||||
0.002221444f, 0.001759588f,-0.003947255f}; |
||||
|
||||
/* FIR filter designed with http://t-filter.appspot.com
|
||||
* Sampling frequency: 24000 sps |
||||
* 0 Hz - 3000 Hz gain = 2 ripple = 0.11 dB |
||||
* 6000 Hz - 12000 Hz atten = -62.4 dB |
||||
* (At Sampling Frequency=48ksps, double all frequency values) */ |
||||
const float32_t interpolateFilter1[23] = { |
||||
-0.00413402f,-0.01306124f,-0.01106321f, 0.01383359f, 0.04386756f, 0.02731837f, |
||||
-0.05470066f,-0.12407408f,-0.04389386f, 0.23355907f, 0.56707488f, 0.71763165f, |
||||
0.56707488f, 0.23355907f,-0.04389386f,-0.12407408f,-0.05470066f, 0.02731837f, |
||||
0.04386756f, 0.01383359f,-0.01106321f,-0.01306124f,-0.00413402}; |
||||
|
||||
}; // end Class
|
||||
#endif |
Loading…
Reference in new issue