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 <cstddef>
#include <array>
void AudioOutput::configure(
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config,
const uint32_t squelch_threshold
const float squelch_threshold
) {
hpf.configure(hpf_config);
deemph.configure(deemph_config);
@ -42,6 +43,20 @@ void AudioOutput::configure(
void AudioOutput::write(
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);
@ -63,16 +78,18 @@ void AudioOutput::write(
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();
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);
}
void AudioOutput::feed_audio_stats(const buffer_s16_t& audio) {
void AudioOutput::feed_audio_stats(const buffer_f32_t& audio) {
audio_stats.feed(
audio,
[](const AudioStatistics& statistics) {

View File

@ -36,10 +36,11 @@ public:
void configure(
const iir_biquad_config_t& hpf_config,
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_f32_t& audio);
private:
IIRBiquadFilter hpf;
@ -50,8 +51,8 @@ private:
uint64_t audio_present_history = 0;
void fill_audio_buffer(const buffer_s16_t& audio);
void feed_audio_stats(const buffer_s16_t& audio);
void fill_audio_buffer(const buffer_f32_t& audio);
void feed_audio_stats(const buffer_f32_t& audio);
};
extern AudioOutput audio_output;

View File

@ -23,12 +23,12 @@
#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;
const auto src_end = &src.p[src.count];
while(src_p < src_end) {
const auto sample = *(src_p++);
const uint64_t sample_squared = sample * sample;
const auto sample_squared = sample * sample;
squared_sum += sample_squared;
if( sample_squared > max_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;
if( count >= samples_per_update ) {
const float squared_sum_f = squared_sum;
const float max_squared_f = 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.rms_db = complex16_mag_squared_to_dbv_norm(squared_sum / count);
statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared);
statistics.count = count;
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);
return update_stats(src.count, src.sampling_rate);

View File

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

View File

@ -30,9 +30,9 @@
namespace dsp {
namespace demodulate {
buffer_s16_t AM::execute(
buffer_f32_t AM::execute(
const buffer_c16_t& src,
const buffer_s16_t& dst
const buffer_f32_t& dst
) {
/* Intermediate maximum value: 46341 (when input is -32768,-32768). */
/* 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 mag_sq0 = __SMUAD(sample0, sample0);
const uint32_t mag_sq1 = __SMUAD(sample1, sample1);
const int32_t mag0_int = __builtin_sqrtf(mag_sq0);
const int32_t mag0_sat = __SSAT(mag0_int, 16);
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
);
*(dst_p++) = __builtin_sqrtf(mag_sq0);
*(dst_p++) = __builtin_sqrtf(mag_sq1);
}
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());
}
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(
const buffer_c16_t& src,
const buffer_s16_t& dst

View File

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

View File

@ -27,7 +27,7 @@ void IIRBiquadFilter::configure(const iir_biquad_config_t& 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 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]
- a_[1] * y_[1] - a_[2] * y_[0];
const int32_t output_sample = y_[2];
const int32_t output_sample_saturated = __SSAT(output_sample, 16);
buffer_out.p[i] = output_sample_saturated;
buffer_out.p[i] = y_[2];
}
x = x_;
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);
}

View File

@ -58,8 +58,8 @@ public:
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_in_place(const buffer_s16_t& buffer);
void execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out);
void execute_in_place(const buffer_f32_t& buffer);
private:
iir_biquad_config_t config;

View File

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

View File

@ -31,13 +31,13 @@
class FMSquelch {
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:
static constexpr size_t N = 32;
uint32_t threshold_squared { 0 };
float threshold_squared { 0.0f };
IIRBiquadFilter non_audio_hpf { non_audio_hpf_config };
};

View File

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

View File

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