0
1
mirror of https://github.com/radio95-rnt/fm95.git synced 2026-02-26 19:23:51 +01:00
This commit is contained in:
2026-02-15 15:00:20 +01:00
parent b0bea04e96
commit 1089f8d1ea
3 changed files with 51 additions and 53 deletions

View File

@@ -1,49 +1,53 @@
#include "stereo_encoder.h" #include "stereo_encoder.h"
// Multiplier is the multiplier to get to 19 khz // Multiplier is the multiplier to get to 19 khz
void init_stereo_encoder(StereoEncoder* st, uint8_t multiplier, Oscillator* osc, float audio_volume, float pilot_volume) { void init_stereo_encoder(StereoEncoder* st, uint8_t stereo_ssb, uint8_t multiplier, Oscillator* osc, float audio_volume, float pilot_volume) {
st->multiplier = multiplier; st->multiplier = multiplier;
st->osc = osc; st->osc = osc;
st->pilot_volume = pilot_volume; st->pilot_volume = pilot_volume;
st->audio_volume = audio_volume; st->audio_volume = audio_volume;
#ifdef STEREO_SSB if(stereo_ssb) {
init_delay_line(&st->delay_pilot, STEREO_SSB*2+1); init_delay_line(&st->delay_pilot, stereo_ssb*2+1);
init_delay_line(&st->delay, STEREO_SSB*2+1); init_delay_line(&st->delay, stereo_ssb*2+1);
#endif st->stereo_hilbert = firhilbf_create(stereo_ssb, 60);
} else st->stereo_hilbert = NULL;
} }
float stereo_encode(StereoEncoder* st, uint8_t enabled, float left, float right, firhilbf *hilbert) { float stereo_encode(StereoEncoder* st, uint8_t enabled, float left, float right) {
float mid = (left+right) * 0.5f; float mid = (left+right) * 0.5f;
if(!enabled) return mid * st->audio_volume; if(!enabled) return mid * st->audio_volume;
#ifdef STEREO_SSB
mid = delay_line(&st->delay, mid); if(st->stereo_hilbert) mid = delay_line(&st->delay, mid);
#endif
float half_audio = st->audio_volume * 0.5f; float half_audio = st->audio_volume * 0.5f;
float side = (left-right) * 0.5f; float side = (left-right) * 0.5f;
#ifdef STEREO_SSB float complex stereo_hilbert = 0+0*I;
float complex stereo_hilbert; if(st->stereo_hilbert) firhilbf_r2c_execute(st->stereo_hilbert, side, &stereo_hilbert);
firhilbf_r2c_execute(*hilbert, side, &stereo_hilbert);
float signalx2cos = get_oscillator_cos_multiplier_ni(st->osc, st->multiplier * 2.0f);
#endif
float signalx1 = get_oscillator_sin_multiplier_ni(st->osc, st->multiplier); float signalx1 = get_oscillator_sin_multiplier_ni(st->osc, st->multiplier);
#ifdef STEREO_SSB
signalx1 = delay_line(&st->delay_pilot, signalx1); float signalx2cos = 0.0f;
#endif if(st->stereo_hilbert) {
signalx1 = delay_line(&st->delay_pilot, signalx1);
signalx2cos = get_oscillator_cos_multiplier_ni(st->osc, st->multiplier * 2.0f);
}
float signalx2 = get_oscillator_sin_multiplier_ni(st->osc, st->multiplier * 2.0f); float signalx2 = get_oscillator_sin_multiplier_ni(st->osc, st->multiplier * 2.0f);
#ifdef STEREO_SSB if(st->stereo_hilbert) {
float stereo = (crealf(stereo_hilbert) * signalx2cos) + (cimagf(stereo_hilbert) * signalx2); float stereo = (crealf(stereo_hilbert) * signalx2cos) + (cimagf(stereo_hilbert) * signalx2);
return (mid*half_audio) + (signalx1*st->pilot_volume) + (stereo * half_audio); return (mid*half_audio) + (signalx1*st->pilot_volume) + (stereo * half_audio);
#else } else {
return (mid*half_audio) + (signalx1*st->pilot_volume) + ((side*signalx2) * half_audio); return (mid*half_audio) + (signalx1*st->pilot_volume) + ((side*signalx2) * half_audio);
#endif }
} }
void exit_stereo_encoder(StereoEncoder* st) { void exit_stereo_encoder(StereoEncoder* st) {
exit_delay_line(&st->delay); if(st->stereo_hilbert) {
exit_delay_line(&st->delay_pilot); exit_delay_line(&st->delay);
exit_delay_line(&st->delay_pilot);
firhilbf_destroy(st->stereo_hilbert);
st->stereo_hilbert = NULL;
}
} }

View File

@@ -1,7 +1,5 @@
#pragma once #pragma once
#define STEREO_SSB 12
#include <stdint.h> #include <stdint.h>
#include "../dsp/oscillator.h" #include "../dsp/oscillator.h"
#include <liquid/liquid.h> #include <liquid/liquid.h>
@@ -16,8 +14,9 @@ typedef struct
float pilot_volume; float pilot_volume;
delay_line_t delay; delay_line_t delay;
delay_line_t delay_pilot; delay_line_t delay_pilot;
firhilbf stereo_hilbert;
} StereoEncoder; } StereoEncoder;
void init_stereo_encoder(StereoEncoder *st, uint8_t multiplier, Oscillator *osc, float audio_volume, float pilot_volume); void init_stereo_encoder(StereoEncoder *st, uint8_t stereo_ssb, uint8_t multiplier, Oscillator *osc, float audio_volume, float pilot_volume);
float stereo_encode(StereoEncoder* st, uint8_t enabled, float left, float right, firhilbf *hilbert); float stereo_encode(StereoEncoder* st, uint8_t enabled, float left, float right);
void exit_stereo_encoder(StereoEncoder* st); void exit_stereo_encoder(StereoEncoder* st);

View File

@@ -5,8 +5,8 @@
#define DEFAULT_INI_PATH "/etc/fm95.conf" #define DEFAULT_INI_PATH "/etc/fm95.conf"
#define buffer_maxlength 73728 #define buffer_maxlength 64512
#define buffer_tlength_fragsize 73728 #define buffer_tlength_fragsize 64512
#include "../dsp/oscillator.h" #include "../dsp/oscillator.h"
#include "../filter/iir.h" #include "../filter/iir.h"
@@ -14,7 +14,7 @@
#include "../filter/bs412.h" #include "../filter/bs412.h"
#include "../filter/gain_control.h" #include "../filter/gain_control.h"
#define BUFFER_SIZE 6144 // This defines how many samples to process at a time, because the loop here is this: get signal -> process signal -> output signal, and when we get signal we actually get BUFFER_SIZE of them #define BUFFER_SIZE 7168 // This defines how many samples to process at a time, because the loop here is this: get signal -> process signal -> output signal, and when we get signal we actually get BUFFER_SIZE of them
#include "../io/audio.h" #include "../io/audio.h"
@@ -47,6 +47,7 @@ typedef struct
FM95_Volumes volumes; FM95_Volumes volumes;
bool stereo; bool stereo;
int stereo_ssb;
uint8_t rds_streams; uint8_t rds_streams;
@@ -83,7 +84,6 @@ typedef struct
float* rds_in; float* rds_in;
Oscillator osc; Oscillator osc;
iirfilt_rrrf lpf_l, lpf_r; iirfilt_rrrf lpf_l, lpf_r;
firhilbf stereo_hilbert;
ResistorCapacitor preemp_l, preemp_r; ResistorCapacitor preemp_l, preemp_r;
BS412Compressor bs412; BS412Compressor bs412;
StereoEncoder stencode; StereoEncoder stencode;
@@ -142,10 +142,7 @@ void cleanup_runtime(FM95_Runtime* runtime, const FM95_Config config) {
iirfilt_rrrf_destroy(runtime->lpf_l); iirfilt_rrrf_destroy(runtime->lpf_l);
iirfilt_rrrf_destroy(runtime->lpf_r); iirfilt_rrrf_destroy(runtime->lpf_r);
} }
#ifdef STEREO_SSB
firhilbf_destroy(runtime->stereo_hilbert);
exit_stereo_encoder(&runtime->stencode); exit_stereo_encoder(&runtime->stencode);
#endif
} }
void cleanup_audio_runtime(FM95_Runtime *rt, const FM95_Options options) { void cleanup_audio_runtime(FM95_Runtime *rt, const FM95_Options options) {
@@ -213,7 +210,7 @@ int run_fm95(const FM95_Config config, FM95_Runtime* runtime) {
float l = audio_stereo_input[2*i+0]*config.audio_preamp; float l = audio_stereo_input[2*i+0]*config.audio_preamp;
float r = audio_stereo_input[2*i+1]*config.audio_preamp; float r = audio_stereo_input[2*i+1]*config.audio_preamp;
if(config.agc_max != 0.0) { if(config.agc_max != 0.0) {
float agc_gain = process_agc(&runtime->agc, 0.5f * (fabsf(l) + fabsf(r))); float agc_gain = process_agc(&runtime->agc, 0.5f * (fabsf(l) + fabsf(r)));
l *= agc_gain; l *= agc_gain;
@@ -231,13 +228,13 @@ int run_fm95(const FM95_Config config, FM95_Runtime* runtime) {
iirfilt_rrrf_execute(runtime->lpf_l, l, &mod_l); iirfilt_rrrf_execute(runtime->lpf_l, l, &mod_l);
iirfilt_rrrf_execute(runtime->lpf_r, r, &mod_r); iirfilt_rrrf_execute(runtime->lpf_r, r, &mod_r);
} }
if(config.preemphasis != 0) { if(config.preemphasis != 0) {
mod_l = apply_preemphasis(&runtime->preemp_l, mod_l); mod_l = apply_preemphasis(&runtime->preemp_l, mod_l);
mod_r = apply_preemphasis(&runtime->preemp_r, mod_r); mod_r = apply_preemphasis(&runtime->preemp_r, mod_r);
} }
mpx = stereo_encode(&runtime->stencode, config.stereo, mod_l, mod_r, &runtime->stereo_hilbert); mpx = stereo_encode(&runtime->stencode, config.stereo, mod_l, mod_r);
if(rds_on) { if(rds_on) {
float rds_level = config.volumes.rds; float rds_level = config.volumes.rds;
@@ -245,11 +242,8 @@ int run_fm95(const FM95_Config config, FM95_Runtime* runtime) {
uint8_t osc_stream = 12 + stream; uint8_t osc_stream = 12 + stream;
if(osc_stream >= 13) osc_stream++; if(osc_stream >= 13) osc_stream++;
#ifdef STEREO_SSB if(config.stereo_ssb) mpx += (runtime->rds_in[config.rds_streams * i + stream] * delay_line(&runtime->rds_delays[stream], get_oscillator_cos_multiplier_ni(&runtime->osc, osc_stream))) * rds_level;
mpx += (runtime->rds_in[config.rds_streams * i + stream] * delay_line(&runtime->rds_delays[stream], get_oscillator_cos_multiplier_ni(&runtime->osc, osc_stream))) * rds_level; else mpx += (runtime->rds_in[config.rds_streams * i + stream] * get_oscillator_cos_multiplier_ni(&runtime->osc, osc_stream)) * rds_level;
#else
mpx += (runtime->rds_in[config.rds_streams * i + stream] * get_oscillator_cos_multiplier_ni(&runtime->osc, osc_stream)) * rds_level;
#endif
rds_level *= config.volumes.rds_step; // Prepare level for the next stream rds_level *= config.volumes.rds_step; // Prepare level for the next stream
} }
@@ -370,6 +364,8 @@ static int config_handler(void* user, const char* section, const char* name, con
pconfig->bs412_release = strtof(value, NULL); pconfig->bs412_release = strtof(value, NULL);
} else if(MATCH("advanced", "lpf_order")) { } else if(MATCH("advanced", "lpf_order")) {
pconfig->lpf_order = atoi(value); pconfig->lpf_order = atoi(value);
} else if(MATCH("advanced", "stereo_ssb")) {
pconfig->stereo_ssb = atoi(value);
} else if(MATCH("advanced", "preemp_unity")) { } else if(MATCH("advanced", "preemp_unity")) {
pconfig->preemp_unity_freq = strtof(value, NULL); pconfig->preemp_unity_freq = strtof(value, NULL);
} else if(MATCH("advanced", "sample_rate")) { } else if(MATCH("advanced", "sample_rate")) {
@@ -479,7 +475,7 @@ int setup_audio(FM95_Runtime* runtime, const FM95_DeviceNames dv_names, const FM
return 0; return 0;
} }
void init_runtime(FM95_Runtime* runtime, const FM95_Config config) { void init_runtime(FM95_Runtime* runtime, const FM95_Config config) {
if(config.calibration != 0) { if(config.calibration != 0) {
init_oscillator(&runtime->osc, (config.calibration == 2) ? 60 : ((config.calibration == 1) ? 400 : 19000), config.sample_rate); init_oscillator(&runtime->osc, (config.calibration == 2) ? 60 : ((config.calibration == 1) ? 400 : 19000), config.sample_rate);
return; return;
@@ -487,15 +483,13 @@ void init_runtime(FM95_Runtime* runtime, const FM95_Config config) {
else init_oscillator(&runtime->osc, 4750, config.sample_rate); else init_oscillator(&runtime->osc, 4750, config.sample_rate);
if(config.lpf_cutoff != 0) { if(config.lpf_cutoff != 0) {
runtime->lpf_l = iirfilt_rrrf_create_prototype(LIQUID_IIRDES_CHEBY2, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, config.lpf_order, (config.lpf_cutoff/config.sample_rate), 0.0f, 1.0f, 60.0f); runtime->lpf_l = iirfilt_rrrf_create_prototype(LIQUID_IIRDES_CHEBY2, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, config.lpf_order, (config.lpf_cutoff/config.sample_rate), 0.0f, 1.0f, 40.0f);
runtime->lpf_r = iirfilt_rrrf_create_prototype(LIQUID_IIRDES_CHEBY2, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, config.lpf_order, (config.lpf_cutoff/config.sample_rate), 0.0f, 1.0f, 60.0f); runtime->lpf_r = iirfilt_rrrf_create_prototype(LIQUID_IIRDES_CHEBY2, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, config.lpf_order, (config.lpf_cutoff/config.sample_rate), 0.0f, 1.0f, 40.0f);
} }
#ifdef STEREO_SSB if(config.stereo_ssb) {
runtime->stereo_hilbert = firhilbf_create(STEREO_SSB, 80); for(int i = 0; i < config.rds_streams; i++) init_delay_line(&runtime->rds_delays[i], config.stereo_ssb*2+1);
}
for(int i = 0; i < config.rds_streams; i++) init_delay_line(&runtime->rds_delays[i], STEREO_SSB*2+1);
#endif
if(config.preemphasis != 0) { if(config.preemphasis != 0) {
init_preemphasis(&runtime->preemp_l, (float)config.preemphasis * 1.0e-6f, config.sample_rate, config.preemp_unity_freq); init_preemphasis(&runtime->preemp_l, (float)config.preemphasis * 1.0e-6f, config.sample_rate, config.preemp_unity_freq);
@@ -521,7 +515,7 @@ void init_runtime(FM95_Runtime* runtime, const FM95_Config config) {
runtime->bs412.sample_counter = last_sample_counter; runtime->bs412.sample_counter = last_sample_counter;
runtime->bs412.second_counter = last_second_counter; runtime->bs412.second_counter = last_second_counter;
init_stereo_encoder(&runtime->stencode, 4.0f, &runtime->osc, config.volumes.audio, config.volumes.pilot); init_stereo_encoder(&runtime->stencode, config.stereo_ssb, 4.0f, &runtime->osc, config.volumes.audio, config.volumes.pilot);
if(config.agc_max != 0.0) { if(config.agc_max != 0.0) {
last_gain = 1.0f; last_gain = 1.0f;
@@ -544,6 +538,7 @@ int main(int argc, char **argv) {
.headroom = 0.05f .headroom = 0.05f
}, },
.stereo = 1, .stereo = 1,
.stereo_ssb = 0,
.rds_streams = 1, // You have to match this with RDS95, otherwise may god have mercy on your RDS decoders .rds_streams = 1, // You have to match this with RDS95, otherwise may god have mercy on your RDS decoders