0
1
mirror of https://github.com/radio95-rnt/fm95.git synced 2026-02-26 19:23:51 +01:00

start modularization

This commit is contained in:
2025-06-21 15:06:27 +02:00
parent 33ffcd9682
commit 16f0a53c05
18 changed files with 79 additions and 43 deletions

View File

@@ -35,7 +35,8 @@
"cdecl.h": "c",
"algorithm": "c",
"socket.h": "c",
"time.h": "c"
"time.h": "c",
"vban.h": "c"
},
"C_Cpp.errorSquiggles": "disabled"
}

View File

@@ -12,13 +12,19 @@ endif()
file(GLOB SRC_FILES "src/*.c")
file(GLOB DSP_FILES "dsp/*.c")
# file(GLOB LIB_FILES "lib/*.c")
file(GLOB LIB_FILES "lib/*.c")
file(GLOB FILTER_FILES "filter/*.c")
file(GLOB MODULATION_FILES "modulation/*.c")
file(GLOB DSP_FILES "dsp/*.c")
file(GLOB IO_FILES "io/*.c")
add_library(libfm OBJECT ${LIB_FILES})
# add_library(libfm OBJECT ${LIB_FILES})
add_library(libfmfilter OBJECT ${FILTER_FILES})
add_library(libfmmodulation OBJECT ${MODULATION_FILES})
add_library(libfmdsp OBJECT ${DSP_FILES})
@@ -26,12 +32,14 @@ add_library(libfmio OBJECT ${IO_FILES})
# Define DEBUG macro for Debug builds on libraries
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
target_compile_definitions(libfm PRIVATE DEBUG=1)
# target_compile_definitions(libfm PRIVATE DEBUG=1)
target_compile_definitions(libfmfilter PRIVATE DEBUG=1)
target_compile_definitions(libfmmodulation PRIVATE DEBUG=1)
target_compile_definitions(libfmdsp PRIVATE DEBUG=1)
target_compile_definitions(libfmio PRIVATE DEBUG=1)
endif()
set(FM_LIBS libfm libfmio libfmdsp pulse pulse-simple m liquid)
set(FM_LIBS libfmfilter libfmmodulation libfmio libfmdsp pulse pulse-simple m liquid)
foreach(SRC_FILE ${SRC_FILES})
get_filename_component(EXEC_NAME ${SRC_FILE} NAME_WE)

View File

@@ -13,8 +13,7 @@ Supports these inputs:
- Audio (via Pulse)
- MPX (via Pulse, basically passthrough, i don't recommend this unless you have something else than rds or sca to modulate, you could run chimer95 via here)
- RDS (via Pulse, expects unmodulated RDS, stereo, left channel on 57 KHz, right on 66.5, rds95 is recommended here, in modulation this is inphase to the pilot)
- RDS2 (via Pulse, expects unmodulated RDS, stereo, left channel on 71.25 KHz, rigth on 76 KHz)
- RDS (via Pulse, expects unmodulated RDS, rds95 is recommended here, in modulation this is quadrature to the pilot, number of channels is specified by the argument, each of the channels (max 4) go on these freqs: 57, 66.5, 71.25, 76)
- SCA (via Pulse, by default on 67 khz with a 7 khz deviation)
and one output:

View File

@@ -1,6 +1,6 @@
#pragma once
#include "constants.h"
#include "../lib/constants.h"
#include <math.h>
typedef struct {

View File

@@ -1,4 +1,4 @@
#include "filters.h"
#include "iir.h"
void init_preemphasis(ResistorCapacitor *filter, float tau, float sample_rate, float ref_freq) {
float dt = 1.0f / sample_rate;

View File

@@ -1,7 +1,6 @@
#pragma once
#include <math.h>
#include "../lib/optimization.h"
#include "../lib/constants.h"
typedef struct

View File

@@ -1,6 +1,6 @@
#pragma once
#include "../lib/oscillator.h"
#include "../dsp/oscillator.h"
typedef struct
{

View File

@@ -0,0 +1,26 @@
#include "stereo_encoder.h"
// Multiplier is the multiplier to get to 19 khz, or 31.25 if polar
void init_stereo_encoder(StereoEncoder* st, uint8_t multiplier, Oscillator* osc, uint8_t polar, float mono_volume, float pilot_volume, float stereo_volume) {
st->multiplier = multiplier;
st->osc = osc;
st->polar = polar;
st->mono_volume = mono_volume;
st->pilot_volume = pilot_volume;
st->stereo_volume = stereo_volume;
}
float stereo_encode(StereoEncoder* st, uint8_t enabled, float left, float right) {
float mid = (left+right) * 0.5f;
if(!enabled) return mid * st->mono_volume;
float side = (left-right) * 0.5f;
if(!st->polar) {
float pilot = get_oscillator_sin_multiplier_ni(st->osc, st->multiplier);
float carrier = get_oscillator_sin_multiplier_ni(st->osc, st->multiplier * 2.0f);
return (mid*st->mono_volume) + (pilot*st->pilot_volume) + ((side*carrier) * st->stereo_volume);
} else {
float carrier = get_oscillator_sin_multiplier_ni(st->osc, st->multiplier);
return (mid*st->mono_volume) + (((side+0.2f)*carrier) * st->stereo_volume); // Polar stereo does not contain a pilot, but it contains a -14 db carrier wave on the stereo subcarrier
}
}

View File

@@ -0,0 +1,18 @@
#pragma once
#include <stdint.h>
#include "../dsp/oscillator.h"
typedef struct
{
uint8_t multiplier;
Oscillator* osc;
uint8_t polar;
float mono_volume;
float pilot_volume;
float stereo_volume;
} StereoEncoder;
void init_stereo_encoder(StereoEncoder *st, uint8_t multiplier, Oscillator *osc, uint8_t polar, float mono_volume, float pilot_volume, float stereo_volume);
float stereo_encode(StereoEncoder* st, uint8_t enabled, float left, float right);

View File

@@ -8,7 +8,7 @@
#define buffer_tlength_fragsize 1024
#define buffer_prebuf 0
#include "../lib/oscillator.h"
#include "../dsp/oscillator.h"
#include "../lib/optimization.h"
#define DEFAULT_FREQ 1000.0f

View File

@@ -10,7 +10,7 @@
// #define DEBUG
#include "../lib/oscillator.h"
#include "../dsp/oscillator.h"
#define DEFAULT_FREQ 77500.0f
#define DEFAULT_SAMPLE_RATE 192000

View File

@@ -19,12 +19,13 @@
#define DEFAULT_MPX_DEVIATION 75000.0f // for BS412
#define DEFAULT_DEVIATION 75000.0f // another way to set the volume
#include "../lib/oscillator.h"
#include "../dsp/filters.h"
#include "../dsp/fm_modulator.h"
#include "../dsp/oscillator.h"
#include "../filter/iir.h"
#include "../modulation/fm_modulator.h"
#include "../modulation/stereo_encoder.h"
#include "../lib/optimization.h"
#include "../dsp/bs412.h"
#include "../dsp/gain_control.h"
#include "../filter/bs412.h"
#include "../filter/gain_control.h"
#define DEFAULT_SAMPLE_RATE 192000
@@ -343,18 +344,19 @@ int main(int argc, char **argv) {
FMModulator sca_mod;
init_fm_modulator(&sca_mod, sca_frequency, sca_deviation, sample_rate);
iirfilt_rrrf lpf_l, lpf_r, mpx_lpf;
iirfilt_rrrf lpf_l, lpf_r;
lpf_l = iirfilt_rrrf_create_prototype(LIQUID_IIRDES_CHEBY2, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, LPF_ORDER, (15000.0f/sample_rate), 0.0f, 1.0f, 40.0f);
lpf_r = iirfilt_rrrf_create_prototype(LIQUID_IIRDES_CHEBY2, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, LPF_ORDER, (15000.0f/sample_rate), 0.0f, 1.0f, 40.0f);
mpx_lpf = iirfilt_rrrf_create_prototype(LIQUID_IIRDES_BUTTER, LIQUID_IIRDES_LOWPASS, LIQUID_IIRDES_SOS, 1, (polar_stereo ? (46250.0f/sample_rate) : (53000.0f/sample_rate)), 0.0f, 1.0f, 1.0f);
ResistorCapacitor preemp_l, preemp_r;
init_preemphasis(&preemp_l, preemphasis_tau, sample_rate, 15250.0f);
init_preemphasis(&preemp_r, preemphasis_tau, sample_rate, 15250.0f);
MPXPowerMeasurement power, mpx_only_power;
MPXPowerMeasurement power;
init_modulation_power_measure(&power, sample_rate);
init_modulation_power_measure(&mpx_only_power, sample_rate);
StereoEncoder stencode;
init_stereo_encoder(&stencode, 4.0f, &osc, polar_stereo, MONO_VOLUME, PILOT_VOLUME, STEREO_VOLUME);
float bs412_audio_gain = 1.0f;
@@ -410,7 +412,6 @@ int main(int argc, char **argv) {
for (uint16_t i = 0; i < BUFFER_SIZE; i++) {
float mpx = 0.0f;
float audio = 0.0f;
float ready_l = apply_preemphasis(&preemp_l, audio_stereo_input[2*i+0]);
float ready_r = apply_preemphasis(&preemp_r, audio_stereo_input[2*i+1]);
@@ -424,20 +425,8 @@ int main(int argc, char **argv) {
ready_l = hard_clip(ready_l*audio_volume, clipper_threshold);
ready_r = hard_clip(ready_r*audio_volume, clipper_threshold);
float mid = (ready_l + ready_r) * 0.5f;
float side = (ready_l - ready_r) * 0.5f;
mpx = stereo_encode(&stencode, stereo, ready_l, ready_r);
audio = mid*MONO_VOLUME;
if(stereo) {
float stereo_carrier = get_oscillator_sin_multiplier_ni(&osc, 8); // 31.25 or 38 KHz
if(polar_stereo) audio += ((side+0.2)*stereo_carrier)*STEREO_VOLUME; // 0.2 in polar stereo because it also includes a carrier wave, so we add a carrier wave via DC
else {
float pilot = get_oscillator_sin_multiplier_ni(&osc, 4); // 19 KHz
mpx += pilot*PILOT_VOLUME;
audio += (side*stereo_carrier)*STEREO_VOLUME;
}
}
if(rds_on && !polar_stereo) {
for(uint8_t stream = 0; stream < rds_streams; stream++) {
uint8_t osc_stream = 12+stream;
@@ -448,11 +437,9 @@ int main(int argc, char **argv) {
if(mpx_on) mpx += mpx_in[i];
if(sca_on) mpx += modulate_fm(&sca_mod, hard_clip(sca_in[i], sca_clipper_threshold))*SCA_VOLUME;
float mpxonly_power = measure_mpx(&mpx_only_power, mpx * mpx_deviation);
float mpx_power = measure_mpx(&power, (audio+mpx) * mpx_deviation); // Standard requires that the output is measured specifically
float mpx_power = measure_mpx(&power, mpx * mpx_deviation);
if (mpx_power > max_mpx_power) {
float excess_power = mpx_power - max_mpx_power;
excess_power = deviation_to_dbr(dbr_to_deviation(excess_power) - dbr_to_deviation(mpxonly_power));
if (excess_power > 0.0f && excess_power < 10.0f) {
float target_gain = dbr_to_deviation(-excess_power) / mpx_deviation;
@@ -466,9 +453,8 @@ int main(int argc, char **argv) {
bs412_audio_gain = fminf(1.0f, bs412_audio_gain + 0.001f);
}
audio *= bs412_audio_gain;
mpx *= bs412_audio_gain;
iirfilt_rrrf_execute(mpx_lpf, audio, &audio); // Should have no effect, as audio should be 0-15, and 23-53, this is a filter for 53, assuming the filter is good, this is precaution and recomendation
audio = hard_clip(audio, 1.0f-mpx); // Prevent clipping, via clipping the audio signal with relation to the mpx signal
output[i] = hard_clip((audio+mpx), 1.0f)*master_volume; // Ensure peak deviation of 75 khz, assuming we're calibrated correctly
@@ -485,7 +471,6 @@ int main(int argc, char **argv) {
printf("Cleaning up...\n");
iirfilt_rrrf_destroy(lpf_l);
iirfilt_rrrf_destroy(lpf_r);
iirfilt_rrrf_destroy(mpx_lpf);
free_PulseInputDevice(&input_device);
if(mpx_on) free_PulseInputDevice(&mpx_device);