0
1
mirror of https://github.com/radio95-rnt/fm95.git synced 2026-02-27 03:23:54 +01:00

light changes

This commit is contained in:
2025-01-23 10:03:39 +01:00
parent 43521d90a8
commit 7096a6e572
13 changed files with 106 additions and 300 deletions

View File

@@ -1,197 +0,0 @@
#include <stdio.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include "../lib/constants.h"
#include "../lib/oscillator.h"
#include "../lib/filters.h"
#include "options.h"
#define SAMPLE_RATE 192000 // Don't go lower than 108 KHz, becuase it (53000*2) and (38000+15000)
#define INPUT_DEVICE "real_real_tx_audio_input.monitor"
#define OUTPUT_DEVICE "alsa_output.platform-soc_sound.stereo-fallback"
#define BUFFER_SIZE 512
#define CLIPPER_THRESHOLD 0.525 // Adjust this as needed
#define MONO_VOLUME 0.5f // L+R Signal
#define STEREO_VOLUME_AUDIO 1.0f // L-R signal
#define STEREO_VOLUME_MODULATION 0.5f // L-R signal
#ifdef PREEMPHASIS
#define PREEMPHASIS_TAU 0.00005 // 50 microseconds, use 0.000075 if in america
#endif
#ifdef LPF
#define LPF_CUTOFF 15000
#endif
volatile sig_atomic_t to_run = 1;
float clip(float sample) {
if (sample > CLIPPER_THRESHOLD) {
return CLIPPER_THRESHOLD; // Clip to the upper threshold
} else if (sample < -CLIPPER_THRESHOLD) {
return -CLIPPER_THRESHOLD; // Clip to the lower threshold
} else {
return sample; // No clipping
}
}
void uninterleave(const float *input, float *left, float *right, size_t num_samples) {
// For stereo, usually it is like this: LEFT RIGHT LEFT RIGHT LEFT RIGHT so this is used to get LEFT LEFT LEFT and RIGHT RIGHT RIGHT
for (size_t i = 0; i < num_samples/2; i++) {
left[i] = input[i * 2];
right[i] = input[i * 2 + 1];
}
}
static void stop(int signum) {
(void)signum;
printf("\nReceived stop signal. Cleaning up...\n");
to_run = 0;
}
int main() {
printf("CrosbySTCode : Stereo encoder (using the crosby system) made by radio95 (with help of ChatGPT and Claude, thanks!)\n");
// Define formats and buffer atributes
pa_sample_spec stereo_format = {
.format = PA_SAMPLE_FLOAT32NE, //Float32 NE, or Float32 Native Endian, the float in c uses the endianess of your pc, or native endian, and float is float32, and double is float64
.channels = 2,
.rate = SAMPLE_RATE // Same sample rate makes it easy, leave the resampling to pipewire, it should know better
};
pa_sample_spec mono_format = {
.format = PA_SAMPLE_FLOAT32NE,
.channels = 1,
.rate = SAMPLE_RATE
};
pa_buffer_attr input_buffer_atr = {
.maxlength = buffer_maxlength,
.fragsize = buffer_tlength_fragsize
};
pa_buffer_attr output_buffer_atr = {
.maxlength = buffer_maxlength,
.tlength = buffer_tlength_fragsize,
.prebuf = buffer_prebuf
};
printf("Connecting to input device... (%s)\n", INPUT_DEVICE);
pa_simple *input_device = pa_simple_new(
NULL,
"CrosbyStereoEncoder",
PA_STREAM_RECORD,
INPUT_DEVICE,
"Audio Input",
&stereo_format,
NULL,
&input_buffer_atr,
NULL
);
if (!input_device) {
fprintf(stderr, "Error: cannot open input device.\n");
return 1;
}
printf("Connecting to output device... (%s)\n", OUTPUT_DEVICE);
pa_simple *output_device = pa_simple_new(
NULL,
"CrosbyStereoEncoder",
PA_STREAM_PLAYBACK,
OUTPUT_DEVICE,
"MPX",
&mono_format,
NULL,
&output_buffer_atr,
NULL
);
if (!output_device) {
fprintf(stderr, "Error: cannot open output device.\n");
pa_simple_free(input_device);
return 1;
}
Oscillator osc;
init_oscillator(&osc, 50000.0, SAMPLE_RATE);
#ifdef PREEMPHASIS
ResistorCapacitor preemp_l, preemp_r;
init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE);
#endif
#ifdef LPF
LowPassFilter lpf_l, lpf_r;
init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE);
init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE);
#endif
signal(SIGINT, stop);
signal(SIGTERM, stop);
int pulse_error;
float input[BUFFER_SIZE*2]; // Input from device, interleaved stereo
float left[BUFFER_SIZE+64], right[BUFFER_SIZE+64]; // Audio, same thing as in input but ininterleaved, ai told be there could be a buffer overflow here
float mpx[BUFFER_SIZE]; // MPX, this goes to the output
while (to_run) {
if (pa_simple_read(input_device, input, sizeof(input), &pulse_error) < 0) {
fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
to_run = 0;
break;
}
uninterleave(input, left, right, BUFFER_SIZE*2);
for (int i = 0; i < BUFFER_SIZE; i++) {
float l_in = left[i];
float r_in = right[i];
#ifdef PREEMPHASIS
#ifdef LPF
float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in);
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float preemphasized_left = apply_pre_emphasis(&preemp_l, lowpassed_left);
float preemphasized_right = apply_pre_emphasis(&preemp_r, lowpassed_right);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
#else
float preemphasized_left = apply_pre_emphasis(&preemp_l, l_in);
float preemphasized_right = apply_pre_emphasis(&preemp_r, r_in);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
#endif
#else
#ifdef LPF
float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in);
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float current_left_input = clip(lowpassed_left);
float current_right_input = clip(lowpassed_right);
#else
float current_left_input = clip(l_in);
float current_right_input = clip(r_in);
#endif
#endif
float mono = (current_left_input + current_right_input) / 2.0f; // Stereo to Mono
float stereo = (current_left_input - current_right_input) / 2.0f; // Also Stereo to Mono but a bit diffrent
change_oscillator_frequency(&osc, (50000+((stereo*STEREO_VOLUME_AUDIO)*15000)));
mpx[i] = mono * MONO_VOLUME +
get_oscillator_sin_sample(&osc) * STEREO_VOLUME_MODULATION;
}
if (pa_simple_write(output_device, mpx, sizeof(mpx), &pulse_error) < 0) {
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
to_run = 0;
break;
}
}
printf("Cleaning up...\n");
pa_simple_free(input_device);
pa_simple_free(output_device);
return 0;
}

View File

@@ -31,7 +31,7 @@
volatile sig_atomic_t to_run = 1;
float clip(float sample) {
float hard_clip(float sample) {
if (sample > CLIPPER_THRESHOLD) {
return CLIPPER_THRESHOLD; // Clip to the upper threshold
} else if (sample < -CLIPPER_THRESHOLD) {
@@ -105,10 +105,10 @@ int main() {
#ifdef PREEMPHASIS
ResistorCapacitor preemp;
init_rc(&preemp, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc_tau(&preemp, PREEMPHASIS_TAU, SAMPLE_RATE);
#endif
#ifdef LPF
LowPassFilter lpf;
ResistorCapacitor lpf;
init_low_pass_filter(&lpf, LPF_CUTOFF, SAMPLE_RATE);
#endif
@@ -132,17 +132,17 @@ int main() {
#ifdef LPF
float lowpassed = apply_low_pass_filter(&lpf, in);
float preemphasized = apply_pre_emphasis(&preemp, lowpassed);
float current_input = clip(preemphasized);
float current_input = hard_clip(preemphasized);
#else
float preemphasized = apply_pre_emphasis(&preemp, in);
float current_input = clip(preemphasized);
float current_input = hard_clip(preemphasized);
#endif
#else
#ifdef LPF
float lowpassed = apply_low_pass_filter(&lpf, in);
float current_input = clip(lowpassed);
float current_input = hard_clip(lowpassed);
#else
float current_input = clip(in);
float current_input = hard_clip(in);
#endif
#endif

View File

@@ -1,5 +1,5 @@
#define PREEMPHASIS
// #define LPF
#define LPF
#define buffer_maxlength 12288
#define buffer_tlength_fragsize 8192

View File

@@ -33,7 +33,7 @@
volatile sig_atomic_t to_run = 1;
float clip(float sample) {
float hard_clip(float sample) {
if (sample > CLIPPER_THRESHOLD) {
return CLIPPER_THRESHOLD; // Clip to the upper threshold
} else if (sample < -CLIPPER_THRESHOLD) {
@@ -122,11 +122,11 @@ int main() {
init_oscillator(&stereo_osc, 31250.0, SAMPLE_RATE);
#ifdef PREEMPHASIS
ResistorCapacitor preemp_l, preemp_r;
init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc_tau(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc_tau(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE);
#endif
#ifdef LPF
LowPassFilter lpf_l, lpf_r;
ResistorCapacitor lpf_l, lpf_r;
init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE);
init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE);
#endif
@@ -156,23 +156,23 @@ int main() {
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float preemphasized_left = apply_pre_emphasis(&preemp_l, lowpassed_left);
float preemphasized_right = apply_pre_emphasis(&preemp_r, lowpassed_right);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
float current_left_input = hard_clip(preemphasized_left);
float current_right_input = hard_clip(preemphasized_right);
#else
float preemphasized_left = apply_pre_emphasis(&preemp_l, l_in);
float preemphasized_right = apply_pre_emphasis(&preemp_r, r_in);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
float current_left_input = hard_clip(preemphasized_left);
float current_right_input = hard_clip(preemphasized_right);
#endif
#else
#ifdef LPF
float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in);
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float current_left_input = clip(lowpassed_left);
float current_right_input = clip(lowpassed_right);
float current_left_input = hard_clip(lowpassed_left);
float current_right_input = hard_clip(lowpassed_right);
#else
float current_left_input = clip(l_in);
float current_right_input = clip(r_in);
float current_left_input = hard_clip(l_in);
float current_right_input = hard_clip(r_in);
#endif
#endif
@@ -180,10 +180,10 @@ int main() {
float stereo = (current_left_input - current_right_input) / 2.0f; // Also Stereo to Mono but a bit diffrent
float stereo_carrier = get_oscillator_sin_sample(&stereo_osc);
// 14 db is somewhere around 20% of a 1 volt signal
// -14 db is somewhere around 20% of a 1 volt signal
mpx[i] = mono * MONO_VOLUME +
((stereo+0.2) * stereo_carrier)*STEREO_VOLUME;
((stereo+0.2) * stereo_carrier)*STEREO_VOLUME; // the 0.2 add DC, you know what happens then? Carrier wave
}
if (pa_simple_write(output_device, mpx, sizeof(mpx), &pulse_error) < 0) {

View File

@@ -36,7 +36,7 @@
volatile sig_atomic_t to_run = 1;
float clip(float sample) {
float hard_clip(float sample) {
if (sample > CLIPPER_THRESHOLD) {
return CLIPPER_THRESHOLD; // Clip to the upper threshold
} else if (sample < -CLIPPER_THRESHOLD) {
@@ -113,10 +113,10 @@ int main() {
init_fm_modulator(&mod, FREQUENCY, DEVIATION, SAMPLE_RATE);
#ifdef PREEMPHASIS
ResistorCapacitor preemp;
init_rc(&preemp, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc_tau(&preemp, PREEMPHASIS_TAU, SAMPLE_RATE);
#endif
#ifdef LPF
LowPassFilter lpf;
ResistorCapacitor lpf;
init_low_pass_filter(&lpf, LPF_CUTOFF, SAMPLE_RATE);
#endif
@@ -140,17 +140,17 @@ int main() {
#ifdef LPF
float lowpassed = apply_low_pass_filter(&lpf, in);
float preemphasized = apply_pre_emphasis(&preemp, lowpassed);
float current_input = clip(preemphasized);
float current_input = hard_clip(preemphasized);
#else
float preemphasized = apply_pre_emphasis(&preemp, in);
float current_input = clip(preemphasized);
float current_input = hard_clip(preemphasized);
#endif
#else
#ifdef LPF
float lowpassed = apply_low_pass_filter(&lpf, in);
float current_input = clip(lowpassed);
float current_input = hard_clip(lowpassed);
#else
float current_input = clip(in);
float current_input = hard_clip(in);
#endif
#endif

View File

@@ -1,216 +0,0 @@
#include <stdio.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include "../lib/constants.h"
#include "../lib/oscillator.h"
#include "../lib/filters.h"
#include "../lib/hilbert.h"
#include "options.h"
//#define USB
#define SAMPLE_RATE 192000 // Don't go lower than 108 KHz, becuase it (53000*2) and (38000+15000)
#define INPUT_DEVICE "real_real_tx_audio_input.monitor"
#define OUTPUT_DEVICE "alsa_output.platform-soc_sound.stereo-fallback"
#define BUFFER_SIZE 512
#define CLIPPER_THRESHOLD 0.525 // Adjust this as needed
#define MONO_VOLUME 0.45f // L+R Signal
#define PILOT_VOLUME 0.09f // 19 KHz Pilot
#define STEREO_VOLUME 0.45f // L-R signal possibly can be set to .9 because im not sure if usb will be 2 times stronger than dsb-sc
#ifdef PREEMPHASIS
#define PREEMPHASIS_TAU 0.00005 // 50 microseconds, use 0.000075 if in america
#endif
#ifdef LPF
#define LPF_CUTOFF 15000
#endif
volatile sig_atomic_t to_run = 1;
float clip(float sample) {
if (sample > CLIPPER_THRESHOLD) {
return CLIPPER_THRESHOLD; // Clip to the upper threshold
} else if (sample < -CLIPPER_THRESHOLD) {
return -CLIPPER_THRESHOLD; // Clip to the lower threshold
} else {
return sample; // No clipping
}
}
void uninterleave(const float *input, float *left, float *right, size_t num_samples) {
// For stereo, usually it is like this: LEFT RIGHT LEFT RIGHT LEFT RIGHT so this is used to get LEFT LEFT LEFT and RIGHT RIGHT RIGHT
for (size_t i = 0; i < num_samples/2; i++) {
left[i] = input[i * 2];
right[i] = input[i * 2 + 1];
}
}
static void stop(int signum) {
(void)signum;
printf("\nReceived stop signal. Cleaning up...\n");
to_run = 0;
}
int main() {
printf("SSB-STCode : Stereo encoder made by radio95 (with help of ChatGPT and Claude, thanks!)\n");
// Define formats and buffer atributes
pa_sample_spec stereo_format = {
.format = PA_SAMPLE_FLOAT32NE, //Float32 NE, or Float32 Native Endian, the float in c uses the endianess of your pc, or native endian, and float is float32, and double is float64
.channels = 2,
.rate = SAMPLE_RATE // Same sample rate makes it easy, leave the resampling to pipewire, it should know better
};
pa_sample_spec mono_format = {
.format = PA_SAMPLE_FLOAT32NE,
.channels = 1,
.rate = SAMPLE_RATE
};
pa_buffer_attr input_buffer_atr = {
.maxlength = buffer_maxlength,
.fragsize = buffer_tlength_fragsize
};
pa_buffer_attr output_buffer_atr = {
.maxlength = buffer_maxlength,
.tlength = buffer_tlength_fragsize,
.prebuf = buffer_prebuf
};
printf("Connecting to input device... (%s)\n", INPUT_DEVICE);
pa_simple *input_device = pa_simple_new(
NULL,
"StereoEncoder",
PA_STREAM_RECORD,
INPUT_DEVICE,
"Audio Input",
&stereo_format,
NULL,
&input_buffer_atr,
NULL
);
if (!input_device) {
fprintf(stderr, "Error: cannot open input device.\n");
return 1;
}
printf("Connecting to output device... (%s)\n", OUTPUT_DEVICE);
pa_simple *output_device = pa_simple_new(
NULL,
"StereoEncoder",
PA_STREAM_PLAYBACK,
OUTPUT_DEVICE,
"MPX",
&mono_format,
NULL,
&output_buffer_atr,
NULL
);
if (!output_device) {
fprintf(stderr, "Error: cannot open output device.\n");
pa_simple_free(input_device);
return 1;
}
Oscillator pilot_osc;
init_oscillator(&pilot_osc, 19000.0, SAMPLE_RATE); // Pilot, it's there to indicate stereo and as a refrence signal with the stereo carrier
HilbertTransformer hilbert;
init_hilbert(&hilbert);
DelayLine monoDelay;
init_delay_line(&monoDelay, 99);
#ifdef PREEMPHASIS
ResistorCapacitor preemp_l, preemp_r;
init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE);
#endif
#ifdef LPF
LowPassFilter lpf_l, lpf_r;
init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE);
init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE);
#endif
signal(SIGINT, stop);
signal(SIGTERM, stop);
int pulse_error;
float input[BUFFER_SIZE*2]; // Input from device, interleaved stereo
float left[BUFFER_SIZE+64], right[BUFFER_SIZE+64]; // Audio, same thing as in input but ininterleaved, ai told be there could be a buffer overflow here
float mpx[BUFFER_SIZE]; // MPX, this goes to the output
while (to_run) {
if (pa_simple_read(input_device, input, sizeof(input), &pulse_error) < 0) {
fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
to_run = 0;
break;
}
uninterleave(input, left, right, BUFFER_SIZE*2);
for (int i = 0; i < BUFFER_SIZE; i++) {
float sin38 = get_oscillator_sin_multiplier_ni(&pilot_osc, 2);
float cos38 = get_oscillator_cos_multiplier_ni(&pilot_osc, 2); // Stereo carrier should be a harmonic of the pilot which is in phase, best way to generate the harmonic is to multiply the pilot's phase by two, so it is mathematically impossible for them to not be in phase
float pilot = get_oscillator_sin_sample(&pilot_osc); // This is after because if it was before then the stereo would be out of phase by one increment, so [GET STEREO] ([GET PILOT] [INCREMENT PHASE])
float l_in = left[i];
float r_in = right[i];
#ifdef PREEMPHASIS
#ifdef LPF
float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in);
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float preemphasized_left = apply_pre_emphasis(&preemp_l, lowpassed_left);
float preemphasized_right = apply_pre_emphasis(&preemp_r, lowpassed_right);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
#else
float preemphasized_left = apply_pre_emphasis(&preemp_l, l_in);
float preemphasized_right = apply_pre_emphasis(&preemp_r, r_in);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
#endif
#else
#ifdef LPF
float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in);
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float current_left_input = clip(lowpassed_left);
float current_right_input = clip(lowpassed_right);
#else
float current_left_input = clip(l_in);
float current_right_input = clip(r_in);
#endif
#endif
float mono = (current_left_input + current_right_input) / 2.0f; // Stereo to Mono
float stereo = (current_left_input - current_right_input) / 2.0f; // Also Stereo to Mono but a bit diffrent
float stereo_i, stereo_q;
apply_hilbert(&hilbert, stereo, &stereo_i, &stereo_q); // I/Q, the Quadrature data is 90 degrees apart from the In-phase data
#ifdef USB
float signal = (stereo_i*cos38+stereo_q*(sin38*0.775f)); // Compute LSB/USB, as the Hilbert isn't perfect, i'll have to a bit silence down the Q carrier in order to make it better, also, it is just perfect as FM Stereo LSB shouldn't be fully LSB
#else
float signal = (stereo_i*cos38-stereo_q*(sin38*0.775f)); // Compute LSB/USB, as the Hilbert isn't perfect, i'll have to a bit silence down the Q carrier in order to make it better, also, it is just perfect as FM Stereo LSB shouldn't be fully LSB
#endif
mpx[i] = delay_line(&monoDelay, mono) * MONO_VOLUME +
pilot * PILOT_VOLUME +
signal*STEREO_VOLUME;
}
if (pa_simple_write(output_device, mpx, sizeof(mpx), &pulse_error) < 0) {
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
to_run = 0;
break;
}
}
printf("Cleaning up...\n");
pa_simple_free(input_device);
pa_simple_free(output_device);
exit_hilbert(&hilbert);
return 0;
}

View File

@@ -7,11 +7,17 @@
#include <signal.h>
#include <string.h>
#include "options.h"
//#define SSB
//#define USB
#include "../lib/constants.h"
#include "../lib/oscillator.h"
#include "../lib/filters.h"
#include "options.h"
#ifdef SSB
#include "../lib/hilbert.h"
#endif
#define SAMPLE_RATE 192000 // Don't go lower than 108 KHz, becuase it (53000*2) and (38000+15000)
@@ -22,7 +28,7 @@
#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 STEREO_VOLUME 0.45f // L-R signal possibly can be set to .9 because im not sure if usb will be 2 times stronger than dsb-sc
#ifdef PREEMPHASIS
#define PREEMPHASIS_TAU 0.00005 // 50 microseconds, use 0.000075 if in america
@@ -34,7 +40,7 @@
volatile sig_atomic_t to_run = 1;
float clip(float sample) {
float hard_clip(float sample) {
if (sample > CLIPPER_THRESHOLD) {
return CLIPPER_THRESHOLD; // Clip to the upper threshold
} else if (sample < -CLIPPER_THRESHOLD) {
@@ -107,7 +113,7 @@ int main() {
"StereoEncoder",
PA_STREAM_PLAYBACK,
OUTPUT_DEVICE,
"MPX",
"MPX Output",
&mono_format,
NULL,
&output_buffer_atr,
@@ -121,13 +127,20 @@ int main() {
Oscillator pilot_osc;
init_oscillator(&pilot_osc, 19000.0, SAMPLE_RATE); // Pilot, it's there to indicate stereo and as a refrence signal with the stereo carrier
#ifdef SSB
HilbertTransformer hilbert;
init_hilbert(&hilbert);
DelayLine monoDelay;
init_delay_line(&monoDelay, 99);
#endif
#ifdef PREEMPHASIS
ResistorCapacitor preemp_l, preemp_r;
init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc_tau(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc_tau(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE);
#endif
#ifdef LPF
LowPassFilter lpf_l, lpf_r;
ResistorCapacitor lpf_l, lpf_r;
init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE);
init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE);
#endif
@@ -148,7 +161,10 @@ int main() {
uninterleave(input, left, right, BUFFER_SIZE*2);
for (int i = 0; i < BUFFER_SIZE; i++) {
float stereo_carrier = get_oscillator_sin_multiplier_ni(&pilot_osc, 2); // Stereo carrier should be a harmonic of the pilot which is in phase, best way to generate the harmonic is to multiply the pilot's phase by two, so it is mathematically impossible for them to not be in phase
float sin38 = get_oscillator_sin_multiplier_ni(&pilot_osc, 2); // Stereo carrier should be a harmonic of the pilot which is in phase, best way to generate the harmonic is to multiply the pilot's phase by two, so it is mathematically impossible for them to not be in phase
#ifdef SSB
float cos38 = get_oscillator_cos_multiplier_ni(&pilot_osc, 2);
#endif
float pilot = get_oscillator_sin_sample(&pilot_osc); // This is after because if it was before then the stereo would be out of phase by one increment, so [GET STEREO] ([GET PILOT] [INCREMENT PHASE])
float l_in = left[i];
float r_in = right[i];
@@ -159,32 +175,45 @@ int main() {
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float preemphasized_left = apply_pre_emphasis(&preemp_l, lowpassed_left);
float preemphasized_right = apply_pre_emphasis(&preemp_r, lowpassed_right);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
float current_left_input = hard_clip(preemphasized_left);
float current_right_input = hard_clip(preemphasized_right);
#else
float preemphasized_left = apply_pre_emphasis(&preemp_l, l_in);
float preemphasized_right = apply_pre_emphasis(&preemp_r, r_in);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
float current_left_input = hard_clip(preemphasized_left);
float current_right_input = hard_clip(preemphasized_right);
#endif
#else
#ifdef LPF
float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in);
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float current_left_input = clip(lowpassed_left);
float current_right_input = clip(lowpassed_right);
float current_left_input = hard_clip(lowpassed_left);
float current_right_input = hard_clip(lowpassed_right);
#else
float current_left_input = clip(l_in);
float current_right_input = clip(r_in);
float current_left_input = hard_clip(l_in);
float current_right_input = hard_clip(r_in);
#endif
#endif
float mono = (current_left_input + current_right_input) / 2.0f; // Stereo to Mono
float stereo = (current_left_input - current_right_input) / 2.0f; // Also Stereo to Mono but a bit diffrent
mpx[i] = mono * MONO_VOLUME +
#ifdef SSB
float stereo_i, stereo_q;
apply_hilbert(&hilbert, stereo, &stereo_i, &stereo_q); // I/Q, the Quadrature data is 90 degrees apart from the In-phase data
#ifdef USB
float signal = (stereo_i*cos38+stereo_q*(sin38*0.775f)); // Compute USB, as the Hilbert isn't perfect, i'll have to a bit silence down the Q carrier in order to make it better, also, it is just perfect as FM Stereo LSB shouldn't be fully LSB
#else
float signal = (stereo_i*cos38-stereo_q*(sin38*0.775f)); // Compute LSB
#endif
mpx[i] = delay_line(&monoDelay, mono) * MONO_VOLUME +
pilot * PILOT_VOLUME +
(stereo * stereo_carrier) * STEREO_VOLUME; // DSB-SC modulation
signal*STEREO_VOLUME;
#else
mpx[i] = mono*MONO_VOLUME +
pilot*PILOT_VOLUME +
(stereo*sin38)*STEREO_VOLUME;
#endif
}
if (pa_simple_write(output_device, mpx, sizeof(mpx), &pulse_error) < 0) {
@@ -196,5 +225,9 @@ int main() {
printf("Cleaning up...\n");
pa_simple_free(input_device);
pa_simple_free(output_device);
#ifdef SSB
exit_hilbert(&hilbert);
exit_delay_line(&monoDelay);
#endif
return 0;
}

View File

@@ -1,198 +0,0 @@
#include <stdio.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include <signal.h>
#include <string.h>
#include "../lib/constants.h"
#include "../lib/oscillator.h"
#include "../lib/filters.h"
#include "../lib/fm_modulator.h"
#include "options.h"
#define SAMPLE_RATE 192000
#define INPUT_DEVICE "SCA.monitor"
#define OUTPUT_DEVICE "alsa_output.platform-soc_sound.stereo-fallback"
#define BUFFER_SIZE 512
#define CLIPPER_THRESHOLD 0.75 // Adjust this as needed, this also limits deviation, so if you set this to 0.5 then the deviation will be limited to half
#define MONO_VOLUME 0.075f // Mono Volume
#define STEREO_VOLUME 0.025f // Stereo Volume
#ifdef PREEMPHASIS
#define PREEMPHASIS_TAU 0.00005 // 50 microseconds, use 0.000075 if in america
#endif
#ifdef LPF
#define LPF_CUTOFF 8000
#endif
volatile sig_atomic_t to_run = 1;
void uninterleave(const float *input, float *left, float *right, size_t num_samples) {
// For stereo, usually it is like this: LEFT RIGHT LEFT RIGHT LEFT RIGHT so this is used to get LEFT LEFT LEFT and RIGHT RIGHT RIGHT
for (size_t i = 0; i < num_samples/2; i++) {
left[i] = input[i * 2];
right[i] = input[i * 2 + 1];
}
}
float clip(float sample) {
if (sample > CLIPPER_THRESHOLD) {
return CLIPPER_THRESHOLD; // Clip to the upper threshold
} else if (sample < -CLIPPER_THRESHOLD) {
return -CLIPPER_THRESHOLD; // Clip to the lower threshold
} else {
return sample; // No clipping
}
}
static void stop(int signum) {
(void)signum;
printf("\nReceived stop signal. Cleaning up...\n");
to_run = 0;
}
int main() {
printf("StereoSCAMod : Stereo SCA Modulator (based on the SCA encoder SCAMod) made by radio95 (with help of ChatGPT and Claude, thanks!)\n");
// Define formats and buffer atributes
pa_sample_spec mono_audio_format = {
.format = PA_SAMPLE_FLOAT32LE,
.channels = 1,
.rate = SAMPLE_RATE // Same sample rate makes it easy, leave the resampling to pipewire, it should know better
};
pa_sample_spec stereo_audio_format = {
.format = PA_SAMPLE_FLOAT32LE,
.channels = 2,
.rate = SAMPLE_RATE // Same sample rate makes it easy, leave the resampling to pipewire, it should know better
};
pa_buffer_attr input_buffer_atr = {
.maxlength = buffer_maxlength,
.fragsize = buffer_tlength_fragsize
};
pa_buffer_attr output_buffer_atr = {
.maxlength = buffer_maxlength,
.tlength = buffer_tlength_fragsize,
.prebuf = buffer_prebuf
};
printf("Connecting to input device... (%s)\n", INPUT_DEVICE);
pa_simple *input_device = pa_simple_new(
NULL,
"StereoSCAMod",
PA_STREAM_RECORD,
INPUT_DEVICE,
"Audio Input",
&stereo_audio_format,
NULL,
&input_buffer_atr,
NULL
);
if (!input_device) {
fprintf(stderr, "Error: cannot open input device.\n");
return 1;
}
printf("Connecting to output device... (%s)\n", OUTPUT_DEVICE);
pa_simple *output_device = pa_simple_new(
NULL,
"StereoSCAMod",
PA_STREAM_PLAYBACK,
OUTPUT_DEVICE,
"Signal",
&mono_audio_format,
NULL,
&output_buffer_atr,
NULL
);
if (!output_device) {
fprintf(stderr, "Error: cannot open output device.\n");
pa_simple_free(input_device);
return 1;
}
FMModulator mod_mono, mod_stereo;
init_fm_modulator(&mod_mono, 67000, 6000, SAMPLE_RATE);
init_fm_modulator(&mod_stereo, 80000, 6000, SAMPLE_RATE);
#ifdef PREEMPHASIS
ResistorCapacitor preemp_l, preemp_r;
init_rc(&preemp_l, PREEMPHASIS_TAU, SAMPLE_RATE);
init_rc(&preemp_r, PREEMPHASIS_TAU, SAMPLE_RATE);
#endif
#ifdef LPF
LowPassFilter lpf_l, lpf_r;
init_low_pass_filter(&lpf_l, LPF_CUTOFF, SAMPLE_RATE);
init_low_pass_filter(&lpf_r, LPF_CUTOFF, SAMPLE_RATE);
#endif
signal(SIGINT, stop);
signal(SIGTERM, stop);
int pulse_error;
float input[BUFFER_SIZE*2]; // Input from device
float left[BUFFER_SIZE+64], right[BUFFER_SIZE+64]; // Audio, same thing as in input but ininterleaved, ai told be there could be a buffer overflow here
float signal[BUFFER_SIZE]; // this goes to the output
while (to_run) {
if (pa_simple_read(input_device, input, sizeof(input), &pulse_error) < 0) {
fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
to_run = 0;
break;
}
uninterleave(input, left, right, BUFFER_SIZE*2);
for (int i = 0; i < BUFFER_SIZE; i++) {
float l_in = left[i];
float r_in = right[i];
#ifdef PREEMPHASIS
#ifdef LPF
float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in);
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float preemphasized_left = apply_pre_emphasis(&preemp_l, lowpassed_left);
float preemphasized_right = apply_pre_emphasis(&preemp_r, lowpassed_right);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
#else
float preemphasized_left = apply_pre_emphasis(&preemp_l, l_in);
float preemphasized_right = apply_pre_emphasis(&preemp_r, r_in);
float current_left_input = clip(preemphasized_left);
float current_right_input = clip(preemphasized_right);
#endif
#else
#ifdef LPF
float lowpassed_left = apply_low_pass_filter(&lpf_l, l_in);
float lowpassed_right = apply_low_pass_filter(&lpf_r, r_in);
float current_left_input = clip(lowpassed_left);
float current_right_input = clip(lowpassed_right);
#else
float current_left_input = clip(l_in);
float current_right_input = clip(r_in);
#endif
#endif
float mono = (current_left_input+current_right_input)/2.0f;
float stereo = (current_left_input-current_right_input)/2.0f;
signal[i] = modulate_fm(&mod_mono, mono)*MONO_VOLUME+
modulate_fm(&mod_stereo, stereo)*STEREO_VOLUME;
}
if (pa_simple_write(output_device, signal, sizeof(signal), &pulse_error) < 0) {
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
to_run = 0;
break;
}
}
printf("Cleaning up...\n");
pa_simple_free(input_device);
pa_simple_free(output_device);
return 0;
}