Change baseband audio processing pipeline to all floats.

This commit is contained in:
Jared Boone 2016-01-11 16:15:42 -08:00
parent b9f124850b
commit 55e3a70fde
12 changed files with 90 additions and 56 deletions

View File

@ -29,11 +29,12 @@
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#include <array>
void AudioOutput::configure( void AudioOutput::configure(
const iir_biquad_config_t& hpf_config, const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config, const iir_biquad_config_t& deemph_config,
const uint32_t squelch_threshold const float squelch_threshold
) { ) {
hpf.configure(hpf_config); hpf.configure(hpf_config);
deemph.configure(deemph_config); deemph.configure(deemph_config);
@ -42,6 +43,20 @@ void AudioOutput::configure(
void AudioOutput::write( void AudioOutput::write(
const buffer_s16_t& audio const buffer_s16_t& audio
) {
std::array<float, 32> audio_f;
for(size_t i=0; i<audio.count; i++) {
audio_f[i] = audio.p[i];
}
write(buffer_f32_t {
audio_f.data(),
audio.count,
audio.sampling_rate
});
}
void AudioOutput::write(
const buffer_f32_t& audio
) { ) {
const auto audio_present_now = squelch.execute(audio); const auto audio_present_now = squelch.execute(audio);
@ -63,16 +78,18 @@ void AudioOutput::write(
fill_audio_buffer(audio); fill_audio_buffer(audio);
} }
void AudioOutput::fill_audio_buffer(const buffer_s16_t& audio) { void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio) {
auto audio_buffer = audio::dma::tx_empty_buffer(); auto audio_buffer = audio::dma::tx_empty_buffer();
for(size_t i=0; i<audio_buffer.count; i++) { for(size_t i=0; i<audio_buffer.count; i++) {
audio_buffer.p[i].left = audio_buffer.p[i].right = audio.p[i]; const int32_t sample_int = audio.p[i];
const int32_t sample_saturated = __SSAT(sample_int, 16);
audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated;
} }
feed_audio_stats(audio); feed_audio_stats(audio);
} }
void AudioOutput::feed_audio_stats(const buffer_s16_t& audio) { void AudioOutput::feed_audio_stats(const buffer_f32_t& audio) {
audio_stats.feed( audio_stats.feed(
audio, audio,
[](const AudioStatistics& statistics) { [](const AudioStatistics& statistics) {

View File

@ -36,10 +36,11 @@ public:
void configure( void configure(
const iir_biquad_config_t& hpf_config, const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config = iir_config_passthrough, const iir_biquad_config_t& deemph_config = iir_config_passthrough,
const uint32_t squelch_threshold = 0 const float squelch_threshold = 0.0f
); );
void write(const buffer_s16_t& audio); void write(const buffer_s16_t& audio);
void write(const buffer_f32_t& audio);
private: private:
IIRBiquadFilter hpf; IIRBiquadFilter hpf;
@ -50,8 +51,8 @@ private:
uint64_t audio_present_history = 0; uint64_t audio_present_history = 0;
void fill_audio_buffer(const buffer_s16_t& audio); void fill_audio_buffer(const buffer_f32_t& audio);
void feed_audio_stats(const buffer_s16_t& audio); void feed_audio_stats(const buffer_f32_t& audio);
}; };
extern AudioOutput audio_output; extern AudioOutput audio_output;

View File

@ -23,12 +23,12 @@
#include "utility.hpp" #include "utility.hpp"
void AudioStatsCollector::consume_audio_buffer(const buffer_s16_t& src) { void AudioStatsCollector::consume_audio_buffer(const buffer_f32_t& src) {
auto src_p = src.p; auto src_p = src.p;
const auto src_end = &src.p[src.count]; const auto src_end = &src.p[src.count];
while(src_p < src_end) { while(src_p < src_end) {
const auto sample = *(src_p++); const auto sample = *(src_p++);
const uint64_t sample_squared = sample * sample; const auto sample_squared = sample * sample;
squared_sum += sample_squared; squared_sum += sample_squared;
if( sample_squared > max_squared ) { if( sample_squared > max_squared ) {
max_squared = sample_squared; max_squared = sample_squared;
@ -42,11 +42,8 @@ bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t s
const size_t samples_per_update = sampling_rate * update_interval; const size_t samples_per_update = sampling_rate * update_interval;
if( count >= samples_per_update ) { if( count >= samples_per_update ) {
const float squared_sum_f = squared_sum; statistics.rms_db = complex16_mag_squared_to_dbv_norm(squared_sum / count);
const float max_squared_f = max_squared; statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared);
const float squared_avg_f = squared_sum_f / count;
statistics.rms_db = complex16_mag_squared_to_dbv_norm(squared_avg_f);
statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared_f);
statistics.count = count; statistics.count = count;
squared_sum = 0; squared_sum = 0;
@ -59,7 +56,7 @@ bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t s
} }
} }
bool AudioStatsCollector::feed(const buffer_s16_t& src) { bool AudioStatsCollector::feed(const buffer_f32_t& src) {
consume_audio_buffer(src); consume_audio_buffer(src);
return update_stats(src.count, src.sampling_rate); return update_stats(src.count, src.sampling_rate);

View File

@ -31,7 +31,7 @@
class AudioStatsCollector { class AudioStatsCollector {
public: public:
template<typename Callback> template<typename Callback>
void feed(const buffer_s16_t& src, Callback callback) { void feed(const buffer_f32_t& src, Callback callback) {
if( feed(src) ) { if( feed(src) ) {
callback(statistics); callback(statistics);
} }
@ -46,17 +46,17 @@ public:
private: private:
static constexpr float update_interval { 0.1f }; static constexpr float update_interval { 0.1f };
uint64_t squared_sum { 0 }; float squared_sum { 0 };
uint32_t max_squared { 0 }; float max_squared { 0 };
size_t count { 0 }; size_t count { 0 };
AudioStatistics statistics; AudioStatistics statistics;
void consume_audio_buffer(const buffer_s16_t& src); void consume_audio_buffer(const buffer_f32_t& src);
bool update_stats(const size_t sample_count, const size_t sampling_rate); bool update_stats(const size_t sample_count, const size_t sampling_rate);
bool feed(const buffer_s16_t& src); bool feed(const buffer_f32_t& src);
bool mute(const size_t sample_count, const size_t sampling_rate); bool mute(const size_t sample_count, const size_t sampling_rate);
}; };

View File

@ -30,9 +30,9 @@
namespace dsp { namespace dsp {
namespace demodulate { namespace demodulate {
buffer_s16_t AM::execute( buffer_f32_t AM::execute(
const buffer_c16_t& src, const buffer_c16_t& src,
const buffer_s16_t& dst const buffer_f32_t& dst
) { ) {
/* Intermediate maximum value: 46341 (when input is -32768,-32768). */ /* Intermediate maximum value: 46341 (when input is -32768,-32768). */
/* Normalized to maximum 32767 for int16_t representation. */ /* Normalized to maximum 32767 for int16_t representation. */
@ -49,15 +49,8 @@ buffer_s16_t AM::execute(
const uint32_t sample1 = *__SIMD32(src_p)++; const uint32_t sample1 = *__SIMD32(src_p)++;
const uint32_t mag_sq0 = __SMUAD(sample0, sample0); const uint32_t mag_sq0 = __SMUAD(sample0, sample0);
const uint32_t mag_sq1 = __SMUAD(sample1, sample1); const uint32_t mag_sq1 = __SMUAD(sample1, sample1);
const int32_t mag0_int = __builtin_sqrtf(mag_sq0); *(dst_p++) = __builtin_sqrtf(mag_sq0);
const int32_t mag0_sat = __SSAT(mag0_int, 16); *(dst_p++) = __builtin_sqrtf(mag_sq1);
const int32_t mag1_int = __builtin_sqrtf(mag_sq1);
const int32_t mag1_sat = __SSAT(mag1_int, 16);
*__SIMD32(dst_p)++ = __PKHBT(
mag0_sat,
mag1_sat,
16
);
} }
return { dst.p, src.count, src.sampling_rate }; return { dst.p, src.count, src.sampling_rate };
@ -81,6 +74,29 @@ static inline float angle_precise(const complex32_t t) {
return atan2f(t.imag(), t.real()); return atan2f(t.imag(), t.real());
} }
buffer_f32_t FM::execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
) {
auto z = z_;
const auto src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
const auto s0 = *__SIMD32(src_p)++;
const auto s1 = *__SIMD32(src_p)++;
const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1;
*(dst_p++) = angle_approx_0deg27(t0) * k;
*(dst_p++) = angle_approx_0deg27(t1) * k;
}
z_ = z;
return { dst.p, src.count, src.sampling_rate };
}
buffer_s16_t FM::execute( buffer_s16_t FM::execute(
const buffer_c16_t& src, const buffer_c16_t& src,
const buffer_s16_t& dst const buffer_s16_t& dst

View File

@ -29,14 +29,19 @@ namespace demodulate {
class AM { class AM {
public: public:
buffer_s16_t execute( buffer_f32_t execute(
const buffer_c16_t& src, const buffer_c16_t& src,
const buffer_s16_t& dst const buffer_f32_t& dst
); );
}; };
class FM { class FM {
public: public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
);
buffer_s16_t execute( buffer_s16_t execute(
const buffer_c16_t& src, const buffer_c16_t& src,
const buffer_s16_t& dst const buffer_s16_t& dst

View File

@ -27,7 +27,7 @@ void IIRBiquadFilter::configure(const iir_biquad_config_t& new_config) {
config = new_config; config = new_config;
} }
void IIRBiquadFilter::execute(const buffer_s16_t& buffer_in, const buffer_s16_t& buffer_out) { void IIRBiquadFilter::execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out) {
const auto a_ = config.a; const auto a_ = config.a;
const auto b_ = config.b; const auto b_ = config.b;
@ -45,15 +45,13 @@ void IIRBiquadFilter::execute(const buffer_s16_t& buffer_in, const buffer_s16_t&
y_[2] = b_[0] * x_[2] + b_[1] * x_[1] + b_[2] * x_[0] y_[2] = b_[0] * x_[2] + b_[1] * x_[1] + b_[2] * x_[0]
- a_[1] * y_[1] - a_[2] * y_[0]; - a_[1] * y_[1] - a_[2] * y_[0];
const int32_t output_sample = y_[2]; buffer_out.p[i] = y_[2];
const int32_t output_sample_saturated = __SSAT(output_sample, 16);
buffer_out.p[i] = output_sample_saturated;
} }
x = x_; x = x_;
y = y_; y = y_;
} }
void IIRBiquadFilter::execute_in_place(const buffer_s16_t& buffer) { void IIRBiquadFilter::execute_in_place(const buffer_f32_t& buffer) {
execute(buffer, buffer); execute(buffer, buffer);
} }

View File

@ -58,8 +58,8 @@ public:
void configure(const iir_biquad_config_t& new_config); void configure(const iir_biquad_config_t& new_config);
void execute(const buffer_s16_t& buffer_in, const buffer_s16_t& buffer_out); void execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out);
void execute_in_place(const buffer_s16_t& buffer); void execute_in_place(const buffer_f32_t& buffer);
private: private:
iir_biquad_config_t config; iir_biquad_config_t config;

View File

@ -24,22 +24,22 @@
#include <cstdint> #include <cstdint>
#include <array> #include <array>
bool FMSquelch::execute(const buffer_s16_t& audio) { bool FMSquelch::execute(const buffer_f32_t& audio) {
if( threshold_squared == 0 ) { if( threshold_squared == 0.0f ) {
return true; return true;
} }
// TODO: No hard-coded array size. // TODO: No hard-coded array size.
std::array<int16_t, N> squelch_energy_buffer; std::array<float, N> squelch_energy_buffer;
const buffer_s16_t squelch_energy { const buffer_f32_t squelch_energy {
squelch_energy_buffer.data(), squelch_energy_buffer.data(),
squelch_energy_buffer.size() squelch_energy_buffer.size()
}; };
non_audio_hpf.execute(audio, squelch_energy); non_audio_hpf.execute(audio, squelch_energy);
uint32_t non_audio_max_squared = 0; float non_audio_max_squared = 0;
for(const auto sample : squelch_energy_buffer) { for(const auto sample : squelch_energy_buffer) {
const uint32_t sample_squared = sample * sample; const float sample_squared = sample * sample;
if( sample_squared > non_audio_max_squared ) { if( sample_squared > non_audio_max_squared ) {
non_audio_max_squared = sample_squared; non_audio_max_squared = sample_squared;
} }
@ -48,6 +48,6 @@ bool FMSquelch::execute(const buffer_s16_t& audio) {
return (non_audio_max_squared < threshold_squared); return (non_audio_max_squared < threshold_squared);
} }
void FMSquelch::set_threshold(const uint32_t new_value) { void FMSquelch::set_threshold(const float new_value) {
threshold_squared = new_value * new_value; threshold_squared = new_value * new_value;
} }

View File

@ -31,13 +31,13 @@
class FMSquelch { class FMSquelch {
public: public:
bool execute(const buffer_s16_t& audio); bool execute(const buffer_f32_t& audio);
void set_threshold(const uint32_t new_value); void set_threshold(const float new_value);
private: private:
static constexpr size_t N = 32; static constexpr size_t N = 32;
uint32_t threshold_squared { 0 }; float threshold_squared { 0.0f };
IIRBiquadFilter non_audio_hpf { non_audio_hpf_config }; IIRBiquadFilter non_audio_hpf { non_audio_hpf_config };
}; };

View File

@ -43,9 +43,9 @@ private:
dst.data(), dst.data(),
dst.size() dst.size()
}; };
const buffer_s16_t work_audio_buffer { const buffer_f32_t work_audio_buffer {
(int16_t*)dst.data(), (float*)dst.data(),
sizeof(dst) / sizeof(int16_t) sizeof(dst) / sizeof(float)
}; };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0; dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0;

View File

@ -41,9 +41,9 @@ private:
dst.data(), dst.data(),
dst.size() dst.size()
}; };
const buffer_s16_t work_audio_buffer { const buffer_f32_t work_audio_buffer {
(int16_t*)dst.data(), (float*)dst.data(),
sizeof(dst) / sizeof(int16_t) sizeof(dst) / sizeof(float)
}; };
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0; dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0;