diff --git a/.vscode/settings.json b/.vscode/settings.json index 006e659..704b741 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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" } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 647cdfb..13790df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md index dff9cbd..4f582d7 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/lib/oscillator.c b/dsp/oscillator.c similarity index 100% rename from lib/oscillator.c rename to dsp/oscillator.c diff --git a/lib/oscillator.h b/dsp/oscillator.h similarity index 94% rename from lib/oscillator.h rename to dsp/oscillator.h index 778c98d..3c5a244 100644 --- a/lib/oscillator.h +++ b/dsp/oscillator.h @@ -1,6 +1,6 @@ #pragma once -#include "constants.h" +#include "../lib/constants.h" #include typedef struct { diff --git a/dsp/bs412.c b/filter/bs412.c similarity index 100% rename from dsp/bs412.c rename to filter/bs412.c diff --git a/dsp/bs412.h b/filter/bs412.h similarity index 100% rename from dsp/bs412.h rename to filter/bs412.h diff --git a/dsp/gain_control.c b/filter/gain_control.c similarity index 100% rename from dsp/gain_control.c rename to filter/gain_control.c diff --git a/dsp/gain_control.h b/filter/gain_control.h similarity index 100% rename from dsp/gain_control.h rename to filter/gain_control.h diff --git a/dsp/filters.c b/filter/iir.c similarity index 96% rename from dsp/filters.c rename to filter/iir.c index 15a992a..fb19f04 100644 --- a/dsp/filters.c +++ b/filter/iir.c @@ -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; diff --git a/dsp/filters.h b/filter/iir.h similarity index 90% rename from dsp/filters.h rename to filter/iir.h index 9ca4340..9005513 100644 --- a/dsp/filters.h +++ b/filter/iir.h @@ -1,7 +1,6 @@ #pragma once #include -#include "../lib/optimization.h" #include "../lib/constants.h" typedef struct diff --git a/dsp/fm_modulator.c b/modulation/fm_modulator.c similarity index 100% rename from dsp/fm_modulator.c rename to modulation/fm_modulator.c diff --git a/dsp/fm_modulator.h b/modulation/fm_modulator.h similarity index 89% rename from dsp/fm_modulator.h rename to modulation/fm_modulator.h index e3f2f30..624b244 100644 --- a/dsp/fm_modulator.h +++ b/modulation/fm_modulator.h @@ -1,6 +1,6 @@ #pragma once -#include "../lib/oscillator.h" +#include "../dsp/oscillator.h" typedef struct { diff --git a/modulation/stereo_encoder.c b/modulation/stereo_encoder.c new file mode 100644 index 0000000..fefecff --- /dev/null +++ b/modulation/stereo_encoder.c @@ -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 + } +} \ No newline at end of file diff --git a/modulation/stereo_encoder.h b/modulation/stereo_encoder.h new file mode 100644 index 0000000..53a29dd --- /dev/null +++ b/modulation/stereo_encoder.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#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); diff --git a/src/chimer95.c b/src/chimer95.c index e7d528e..b67d4a8 100644 --- a/src/chimer95.c +++ b/src/chimer95.c @@ -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 diff --git a/src/dcf95.c b/src/dcf95.c index 05ea7ac..bd74973 100644 --- a/src/dcf95.c +++ b/src/dcf95.c @@ -10,7 +10,7 @@ // #define DEBUG -#include "../lib/oscillator.h" +#include "../dsp/oscillator.h" #define DEFAULT_FREQ 77500.0f #define DEFAULT_SAMPLE_RATE 192000 diff --git a/src/fm95.c b/src/fm95.c index 964899d..4dcf97f 100644 --- a/src/fm95.c +++ b/src/fm95.c @@ -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);