mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-23 22:39:22 -05:00
Change baseband audio processing pipeline to all floats.
This commit is contained in:
parent
b9f124850b
commit
55e3a70fde
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 };
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user