diff --git a/README.md b/README.md index d23d990..2bc50f4 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,13 @@ FM95 is a audio processor for FM, it does: - Pre-Emphasis - Low Pass Filter - Stereo -- SSB Stereo - Polar Stereo -- Polar SSB Stereo (huh) - SCA Supports 2 inputs: - Audio (via Pulse) - MPX (via Pulse) +- SCA (via Pulse) and one output: - MPX (via Pulse) diff --git a/lib/filters.c b/lib/filters.c index 2e12cbd..ceb3ccc 100644 --- a/lib/filters.c +++ b/lib/filters.c @@ -77,45 +77,4 @@ float hard_clip(float sample, float threshold) { } else { return sample; // No clipping } -} - -void init_delay_line(DelayLine *delay_line, int max_delay) { - delay_line->buffer = (float *)calloc(max_delay, sizeof(float)); - delay_line->size = max_delay; - delay_line->write_idx = 0; - delay_line->read_idx = 0; - delay_line->delay = 0; -} - -void set_delay_line(DelayLine *delay_line, int new_delay) { - if (new_delay >= delay_line->size) { - new_delay = delay_line->size - 1; - } - if (new_delay < 0) { - new_delay = 0; - } - - delay_line->delay = new_delay; - delay_line->read_idx = (delay_line->write_idx - new_delay + delay_line->size) % delay_line->size; -} - -float delay_line(DelayLine *delay_line, float in) { - float out; - - // Read the delayed sample - out = delay_line->buffer[delay_line->read_idx]; - - // Write the new sample - delay_line->buffer[delay_line->write_idx] = in; - - // Update indices - delay_line->write_idx = (delay_line->write_idx + 1) % delay_line->size; - delay_line->read_idx = (delay_line->read_idx + 1) % delay_line->size; - - return out; -} - -void exit_delay_line(DelayLine *delay_line) { - free(delay_line->buffer); - delay_line->buffer = NULL; } \ No newline at end of file diff --git a/lib/hilbert.c b/lib/hilbert.c deleted file mode 100644 index 8a38ee3..0000000 --- a/lib/hilbert.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "hilbert.h" - -void compute_hilbert_coeffs(float* coeffs, int taps) { - int mid = taps / 2; - for (int i = 0; i < taps; i++) { - if ((i - mid) % 2 == 0) { - coeffs[i] = 0.0f; - } else { - coeffs[i] = 2.0f / (M_PI * (i - mid)); - } - } -} - -// Initialize the Hilbert transformer -void init_hilbert(HilbertTransformer* filter) { - compute_hilbert_coeffs(filter->coeffs, HILBERT_TAPS); - memset(filter->delay, 0, sizeof(filter->delay)); - filter->index = 0; -} - -// Apply the Hilbert transformer -void apply_hilbert(HilbertTransformer* filter, float input, float* inphase, float* quadrature) { - // Insert the new sample into the circular buffer - filter->delay[filter->index] = input; - - // Compute the in-phase and quadrature components - float i_sum = 0.0f; // In-phase (0-degree output) - float q_sum = 0.0f; // Quadrature (90-degree output) - - int coeff_index = 0; - for (int i = filter->index; coeff_index < HILBERT_TAPS; coeff_index++) { - i_sum += filter->delay[i] * (coeff_index == HILBERT_TAPS / 2 ? 1.0f : 0.0f); - q_sum += filter->delay[i] * filter->coeffs[coeff_index]; - - i = (i > 0) ? i - 1 : HILBERT_TAPS - 1; - } - - // Update the index for the next sample - filter->index = (filter->index + 1) % HILBERT_TAPS; - - *inphase = i_sum; - *quadrature = q_sum; -} \ No newline at end of file diff --git a/lib/hilbert.h b/lib/hilbert.h deleted file mode 100644 index 8ca9a6e..0000000 --- a/lib/hilbert.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include -#include "constants.h" -#include - -#define HILBERT_TAPS 95 - -typedef struct { - float coeffs[HILBERT_TAPS]; - float delay[HILBERT_TAPS]; - int index; -} HilbertTransformer; - -void init_hilbert(HilbertTransformer* filter); -void apply_hilbert(HilbertTransformer* filter, float input, float* inphase, float* quadrature); \ No newline at end of file diff --git a/src/fm95.c b/src/fm95.c index 5e87ff0..93e95d6 100644 --- a/src/fm95.c +++ b/src/fm95.c @@ -8,8 +8,7 @@ #define DEFAULT_STEREO 1 #define DEFAULT_STEREO_POLAR 0 -#define DEFAULT_STEREO_SSB 0 -#define DEFAULT_CLIPPER_THRESHOLD 1.0f +#define DEFAULT_CLIPPER_THRESHOLD 2.0f #define DEFAULT_SCA_FREQUENCY 67000.0f #define DEFAULT_SCA_DEVIATION 7000.0f #define DEFAULT_SCA_CLIPPER_THRESHOLD 1.0f // Full deviation, if you set this to 0.5 then you may as well set the deviation to 3.5k @@ -20,27 +19,27 @@ #include "../lib/constants.h" #include "../lib/oscillator.h" #include "../lib/filters.h" -#include "../lib/hilbert.h" #include "../lib/fm_modulator.h" #define SAMPLE_RATE 192000 #define INPUT_DEVICE "FM_Audio.monitor" -#define OUTPUT_DEVICE "alsa_output.pci-0000_00_14.2.analog-stereo" +#define OUTPUT_DEVICE "alsa_output.platform-soc_sound.stereo-fallback" #define MPX_DEVICE "FM_MPX.monitor" // #define SCA_DEVICE "" -#define BUFFER_SIZE 768 +#define BUFFER_SIZE 1024 #include #include -#define MASTER_VOLUME 1.0f // Volume of everything combined +#define MASTER_VOLUME 1.0f // Volume of everything combined, for calibration +#define AUDIO_VOLUME 2.0f // Audio volume, before clipper #define MONO_VOLUME 0.45f // L+R Signal #define PILOT_VOLUME 0.09f // 19 KHz Pilot -#define STEREO_VOLUME 0.45f // L-R signal -#define SCA_VOLUME 0.1f -#define MPX_VOLUME 1.0f +#define STEREO_VOLUME 0.45f // L-R signal, should be same as MONO +#define SCA_VOLUME 0.1f // FM SCA signal, 10% +#define MPX_VOLUME 1.0f // Passtrough #define LPF_CUTOFF 15000 // Should't need to be changed @@ -61,7 +60,7 @@ static void stop(int signum) { } void show_version() { - printf("fm95 (an FM Processor by radio95) version 1.1\n"); + printf("fm95 (an FM Processor by radio95) version 1.2\n"); } void show_help(char *name) { printf( @@ -78,11 +77,10 @@ void show_help(char *name) { " -c,--clipper Override the clipper threshold [default: %.2f]\n" " -P,--polar Force Polar Stereo (does not take effect with -m%s)\n" " -g,--ge Force Zenith/GE stereo (does not take effect with -m%s)\n" - " -S,--ssb Force SSB [default: %d]\n" - " -D,--dsb Force DSB [default: %d]\n" " -R,--preemp Override preemphasis [default: %.2f µs]\n" " -V,--calibrate Enable Calibration mode [default: off]\n" " -A,--master_vol Set master volume [default: %.3f]\n" + " -v,--audio_vol Set audio volume [default: %.3f]\n" ,name ,DEFAULT_STEREO^1 ,DEFAULT_STEREO @@ -104,10 +102,9 @@ void show_help(char *name) { ,DEFAULT_CLIPPER_THRESHOLD ,(DEFAULT_STEREO_POLAR == 1) ? ", default" : "" ,(DEFAULT_STEREO_POLAR == 1) ? "" : ", default" - ,DEFAULT_STEREO_SSB - ,DEFAULT_STEREO_SSB^1 ,DEFAULT_PREEMPHASIS_TAU/0.000001 ,MASTER_VOLUME + ,AUDIO_VOLUME ); } @@ -121,7 +118,6 @@ int main(int argc, char **argv) { float clipper_threshold = DEFAULT_CLIPPER_THRESHOLD; int stereo = DEFAULT_STEREO; int polar_stereo = DEFAULT_STEREO_POLAR; - int ssb = DEFAULT_STEREO_SSB; float sca_frequency = DEFAULT_SCA_FREQUENCY; float sca_deviation = DEFAULT_SCA_DEVIATION; @@ -143,10 +139,11 @@ int main(int argc, char **argv) { int calibration_mode = 0; float master_volume = MASTER_VOLUME; + float audio_volume = AUDIO_VOLUME; // #region Parse Arguments int opt; - const char *short_opt = "msi:o:apM:C:f:F:L:c:l:PgSDR:VA:hv"; + const char *short_opt = "msi:o:apM:C:f:F:L:c:l:PgSDR:VA:v:h"; struct option long_opt[] = { {"mono", no_argument, NULL, 'm'}, @@ -161,14 +158,12 @@ int main(int argc, char **argv) { {"clipper", required_argument, NULL, 'c'}, {"polar", no_argument, NULL, 'P'}, {"ge", no_argument, NULL, 'g'}, - {"ssb", no_argument, NULL, 'S'}, - {"dsb", no_argument, NULL, 'D'}, {"preemp", required_argument, NULL, 'R'}, {"calibrate", no_argument, NULL, 'V'}, - {"master_vol", no_argument, NULL, 'A'}, + {"master_vol", required_argument, NULL, 'A'}, + {"audio_vol", required_argument, NULL, 'v'}, {"help", no_argument, NULL, 'h'}, - {"version", no_argument, NULL, 'v'}, {0, 0, 0, 0} }; @@ -210,12 +205,6 @@ int main(int argc, char **argv) { case 'g': //GE polar_stereo = 0; break; - case 'S': //SSB - ssb = 1; - break; - case 'D': //DSB - ssb = 0; - break; case 'R': // Preemp preemphasis_tau = strtof(optarg, NULL)*0.000001; break; @@ -225,9 +214,9 @@ int main(int argc, char **argv) { case 'A': // Master vol master_volume = strtof(optarg, NULL); break; - case 'v': // Version - show_version(); - return 0; + case 'v': // Audio Volume + audio_volume = strtof(optarg, NULL); + break; case 'h': show_help(argv[0]); return 1; @@ -372,27 +361,18 @@ int main(int argc, char **argv) { // #region Setup Filters/Modulaltors/Oscillators Oscillator osc; - if(polar_stereo) { - init_oscillator(&osc, 31250.0, SAMPLE_RATE); // The stereo carrier itself, the stereo carrier in polar is modulated directly on 31.25 khz with a bit of a carrier - } else { - init_oscillator(&osc, 19000.0, SAMPLE_RATE); // Pilot, it's there to indicate stereo and as a refrence signal with the stereo carrier - } + init_oscillator(&osc, polar_stereo ? 31250.0 : 19000, SAMPLE_RATE); // Pilot, it's there to indicate stereo and as a refrence signal with the stereo carrier FMModulator sca_mod; init_fm_modulator(&sca_mod, sca_frequency, sca_deviation, SAMPLE_RATE); - HilbertTransformer hilbert; // An Hilbert shifts a signal in quadrature, generating the I/Q data - init_hilbert(&hilbert); - DelayLine monoDelay; // Hilbert introduces a delay, this should be here to sync the mono with stereo to a sample - init_delay_line(&monoDelay, (HILBERT_TAPS-1)/2); - ResistorCapacitor preemp_l, preemp_r; init_preemphasis(&preemp_l, preemphasis_tau, SAMPLE_RATE); init_preemphasis(&preemp_r, preemphasis_tau, SAMPLE_RATE); BiquadFilter lpf_l, lpf_r; - init_lpf(&lpf_l, LPF_CUTOFF, 1.0f, SAMPLE_RATE); - init_lpf(&lpf_r, LPF_CUTOFF, 1.0f, SAMPLE_RATE); + init_lpf(&lpf_l, LPF_CUTOFF, 1.25f, SAMPLE_RATE); + init_lpf(&lpf_r, LPF_CUTOFF, 1.25f, SAMPLE_RATE); // #endregion signal(SIGINT, stop); @@ -435,53 +415,26 @@ int main(int argc, char **argv) { float ready_l = apply_frequency_filter(&lpf_l, l_in); float ready_r = apply_frequency_filter(&lpf_r, r_in); - ready_l = apply_preemphasis(&preemp_l, ready_l); - ready_r = apply_preemphasis(&preemp_r, ready_r); - ready_l = hard_clip(ready_l, clipper_threshold); - ready_r = hard_clip(ready_r, clipper_threshold); + ready_l = apply_preemphasis(&preemp_l, ready_l)*2; + ready_r = apply_preemphasis(&preemp_r, ready_r)*2; + ready_l = hard_clip(ready_l*audio_volume, clipper_threshold); + ready_r = hard_clip(ready_r*audio_volume, clipper_threshold); float mono = (ready_l + ready_r) / 2.0f; // Stereo to Mono + output[i] = mono*MONO_VOLUME; if(stereo == 1) { - float stereo = (ready_l - ready_r) / 2.0f; // Also Stereo to Mono but a bit diffrent - - float stereo_i, stereo_q; - if(ssb) { - // Compute hilbert here - apply_hilbert(&hilbert, stereo, &stereo_i, &stereo_q); - mono = delay_line(&monoDelay, mono); // Delay Mono - } - + float stereo = (ready_l - ready_r) / 2.0f; // Also Stereo to Mono but a bit diffrent + float stereo_carrier = get_oscillator_sin_multiplier_ni(&osc, polar_stereo ? 1 : 2); if(polar_stereo) { - float stereo_carrier = get_oscillator_sin_multiplier_ni(&osc, 1); - if(ssb) { - float stereo_carrier_cos = get_oscillator_cos_multiplier_ni(&osc, 1); // Get Carrier Q of I/Q - - output[i] = mono*MONO_VOLUME + - ((stereo_i+0.2)*stereo_carrier_cos+(stereo_q+0.2)*stereo_carrier)*STEREO_VOLUME; - } else { - float stereo_carrier = get_oscillator_sin_multiplier_ni(&osc, 1); - output[i] = mono*MONO_VOLUME + - ((stereo+0.2)*stereo_carrier)*STEREO_VOLUME; - } + output[i] += ((stereo+0.2)*stereo_carrier)*STEREO_VOLUME; } else { - float stereo_carrier = get_oscillator_sin_multiplier_ni(&osc, 2); // Get stereo carrier via multiplication float pilot = get_oscillator_sin_multiplier_ni(&osc, 1); - if(ssb) { - float stereo_carrier_cos = get_oscillator_cos_multiplier_ni(&osc, 2); // Get Carrier Q of I/Q - output[i] = mono*MONO_VOLUME + - pilot*PILOT_VOLUME + - (stereo_i*stereo_carrier_cos+stereo_q*stereo_carrier)*STEREO_VOLUME; - } else { - output[i] = mono*MONO_VOLUME + - pilot*PILOT_VOLUME + - (stereo*stereo_carrier)*STEREO_VOLUME; - } + output[i] += pilot*PILOT_VOLUME + + (stereo*stereo_carrier)*STEREO_VOLUME; } advance_oscillator(&osc); - } else { - output[i] = mono*MONO_VOLUME; // Only Mono } - if(strlen(audio_mpx_device) != 0) output[i] += current_mpx_in*MPX_VOLUME; + if(strlen(audio_mpx_device) != 0) output[i] += hard_clip(current_mpx_in, 1.0f)*MPX_VOLUME; if(strlen(audio_sca_device) != 0) output[i] += modulate_fm(&sca_mod, hard_clip(current_sca_in, sca_clipper_threshold))*SCA_VOLUME; output[i] *= master_volume; } @@ -497,6 +450,5 @@ int main(int argc, char **argv) { if(strlen(audio_mpx_device) != 0) pa_simple_free(mpx_device); if(strlen(audio_sca_device) != 0) pa_simple_free(sca_device); pa_simple_free(output_device); - exit_delay_line(&monoDelay); return 0; }