mirror of
https://github.com/radio95-rnt/fm95.git
synced 2026-07-30 00:09:16 +02:00
generate the 66.5 inside the pll via the pilot
This commit is contained in:
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"port": 13452,
|
||||
"time": 1742717063787,
|
||||
"time": 1742742025054,
|
||||
"version": "0.0.3"
|
||||
}
|
||||
+46
-42
@@ -1,55 +1,59 @@
|
||||
#include "filters.h"
|
||||
|
||||
void init_preemphasis(ResistorCapacitor *filter, float tau, float sample_rate) {
|
||||
filter->prev_sample = 0.0f;
|
||||
filter->alpha = exp(-1 / (tau*sample_rate));
|
||||
filter->prev_sample = 0.0f;
|
||||
filter->alpha = exp(-1 / (tau*sample_rate));
|
||||
}
|
||||
float apply_preemphasis(ResistorCapacitor *filter, float sample) {
|
||||
float out = sample-filter->alpha*filter->prev_sample;
|
||||
filter->prev_sample = sample;
|
||||
return out;
|
||||
float out = sample-filter->alpha*filter->prev_sample;
|
||||
filter->prev_sample = sample;
|
||||
return out;
|
||||
}
|
||||
|
||||
float hard_clip(float sample, float threshold) {
|
||||
return fmaxf(-threshold, fminf(threshold, sample));
|
||||
return fmaxf(-threshold, fminf(threshold, sample));
|
||||
}
|
||||
|
||||
void init_pll(PLL *pll, float output_freq, float reference_freq, float loop_filter_bandwidth, int quadrature_mode, int sample_rate) {
|
||||
pll->phase = 0.0f;
|
||||
pll->freq = output_freq;
|
||||
pll->ref_freq = reference_freq;
|
||||
pll->loop_filter_state = 0.0f;
|
||||
pll->kp = 2.0f * M_PI * loop_filter_bandwidth;
|
||||
pll->ki = 0.25f * pll->kp * pll->kp;
|
||||
pll->sample_rate = sample_rate;
|
||||
pll->quadrature_mode = quadrature_mode;
|
||||
void init_pll(PLL *pll, int interpolation, int decimation, float freq, float loop_filter_bandwidth, int quadrature_mode, int sample_rate) {
|
||||
pll->phase = 0.0f;
|
||||
pll->freq = freq;
|
||||
pll->loop_filter_state = 0.0f;
|
||||
pll->kp = M_2PI * loop_filter_bandwidth;
|
||||
pll->ki = 0.25f * pll->kp * pll->kp;
|
||||
pll->sample_rate = sample_rate;
|
||||
pll->quadrature_mode = quadrature_mode;
|
||||
pll->last_output = 0.0f;
|
||||
pll->interpolation = interpolation;
|
||||
pll->decimation = decimation;
|
||||
}
|
||||
|
||||
float apply_pll(PLL *pll, float ref_sample, float input_sample) {
|
||||
float phase_error;
|
||||
|
||||
float output = sinf(pll->phase);
|
||||
|
||||
if (pll->quadrature_mode) {
|
||||
output = sinf(pll->phase + (M_PI / 2.0f));
|
||||
}
|
||||
|
||||
phase_error = ref_sample * input_sample;
|
||||
|
||||
pll->loop_filter_state += pll->ki * phase_error / pll->sample_rate;
|
||||
float loop_output = pll->loop_filter_state + pll->kp * phase_error;
|
||||
|
||||
float freq_adjustment = loop_output / (2.0f * M_PI);
|
||||
float instantaneous_freq = pll->freq + freq_adjustment;
|
||||
|
||||
pll->phase += 2.0f * M_PI * instantaneous_freq / pll->sample_rate;
|
||||
|
||||
while (pll->phase >= 2.0f * M_PI) {
|
||||
pll->phase -= 2.0f * M_PI;
|
||||
}
|
||||
while (pll->phase < 0.0f) {
|
||||
pll->phase += 2.0f * M_PI;
|
||||
}
|
||||
|
||||
return output;
|
||||
float apply_pll(PLL *pll, float ref_sample) {
|
||||
float phase_error;
|
||||
|
||||
float vco_phase = pll->phase;
|
||||
if(pll->quadrature_mode) vco_phase += M_PI/2.0f;
|
||||
|
||||
float vco_output = sinf(pll->phase);
|
||||
if (pll->quadrature_mode) vco_output = sinf(pll->phase + (M_PI / 2.0f)); // 90 degrees
|
||||
|
||||
phase_error = ref_sample * pll->last_output;
|
||||
|
||||
pll->loop_filter_state += pll->ki * phase_error / pll->sample_rate;
|
||||
float loop_output = pll->loop_filter_state + pll->kp * phase_error;
|
||||
|
||||
float freq_adjustment = loop_output / M_2PI;
|
||||
float instantaneous_freq = pll->freq + freq_adjustment;
|
||||
|
||||
pll->phase += M_2PI * instantaneous_freq / pll->sample_rate;
|
||||
|
||||
while (pll->phase >= M_2PI) {
|
||||
pll->phase -= M_2PI;
|
||||
}
|
||||
while (pll->phase < 0.0f) {
|
||||
pll->phase += M_2PI;
|
||||
}
|
||||
|
||||
pll->last_output = sinf((vco_phase*pll->interpolation)/pll->decimation);
|
||||
|
||||
return pll->last_output;
|
||||
}
|
||||
+14
-12
@@ -7,8 +7,8 @@
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float alpha;
|
||||
float prev_sample;
|
||||
float alpha;
|
||||
float prev_sample;
|
||||
} ResistorCapacitor;
|
||||
|
||||
void init_preemphasis(ResistorCapacitor *filter, float tau, float sample_rate);
|
||||
@@ -17,14 +17,16 @@ float apply_preemphasis(ResistorCapacitor *filter, float sample);
|
||||
float hard_clip(float sample, float threshold);
|
||||
|
||||
typedef struct {
|
||||
float phase;
|
||||
float freq;
|
||||
float ref_freq;
|
||||
float loop_filter_state;
|
||||
float kp;
|
||||
float ki;
|
||||
int sample_rate;
|
||||
int quadrature_mode;
|
||||
float phase;
|
||||
float freq;
|
||||
float loop_filter_state;
|
||||
float kp;
|
||||
float ki;
|
||||
float last_output;
|
||||
int interpolation;
|
||||
int decimation;
|
||||
int sample_rate;
|
||||
int quadrature_mode;
|
||||
} PLL;
|
||||
void init_pll(PLL *pll, float output_freq, float refrence_freq, float loop_filter_bandwidth, int quadrature_mode, int sample_rate);
|
||||
float apply_pll(PLL *pll, float ref_sample, float input_sample);
|
||||
void init_pll(PLL *pll, int interpolation, int decimation, float freq, float loop_filter_bandwidth, int quadrature_mode, int sample_rate);
|
||||
float apply_pll(PLL *pll, float ref_sample);
|
||||
+9
-9
@@ -1,16 +1,16 @@
|
||||
#include "fm_modulator.h"
|
||||
|
||||
void init_fm_modulator(FMModulator *fm, float frequency, float deviation, float sample_rate) {
|
||||
fm->frequency = frequency;
|
||||
fm->deviation = deviation;
|
||||
fm->sample_rate = sample_rate;
|
||||
fm->osc_phase = 0.0f;
|
||||
fm->frequency = frequency;
|
||||
fm->deviation = deviation;
|
||||
fm->sample_rate = sample_rate;
|
||||
fm->osc_phase = 0.0f;
|
||||
}
|
||||
|
||||
float modulate_fm(FMModulator *fm, float sample) {
|
||||
float inst_freq = fm->frequency+(sample*fm->deviation);
|
||||
if (inst_freq < 0.0f) inst_freq = 0.0f;
|
||||
float out = sinf(fm->osc_phase);
|
||||
fm->osc_phase = fmodf(fm->osc_phase + ((M_2PI * inst_freq) / fm->sample_rate), M_2PI);
|
||||
return out;
|
||||
float inst_freq = fm->frequency+(sample*fm->deviation);
|
||||
if (inst_freq < 0.0f) inst_freq = 0.0f;
|
||||
float out = sinf(fm->osc_phase);
|
||||
fm->osc_phase = fmodf(fm->osc_phase + ((M_2PI * inst_freq) / fm->sample_rate), M_2PI);
|
||||
return out;
|
||||
}
|
||||
+4
-4
@@ -4,10 +4,10 @@
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float frequency;
|
||||
float deviation;
|
||||
float osc_phase;
|
||||
float sample_rate;
|
||||
float frequency;
|
||||
float deviation;
|
||||
float osc_phase;
|
||||
float sample_rate;
|
||||
} FMModulator;
|
||||
|
||||
void init_fm_modulator(FMModulator *fm, float frequency, float deviation, float sample_rate);
|
||||
|
||||
+16
-16
@@ -1,37 +1,37 @@
|
||||
#include "oscillator.h"
|
||||
|
||||
void init_oscillator(Oscillator *osc, float frequency, float sample_rate) {
|
||||
osc->phase = 0.0f;
|
||||
osc->phase_increment = (M_2PI * frequency) / sample_rate;
|
||||
osc->sample_rate = sample_rate;
|
||||
osc->phase = 0.0f;
|
||||
osc->phase_increment = (M_2PI * frequency) / sample_rate;
|
||||
osc->sample_rate = sample_rate;
|
||||
}
|
||||
|
||||
void change_oscillator_frequency(Oscillator *osc, float frequency) {
|
||||
osc->phase_increment = (M_2PI * frequency) / osc->sample_rate;
|
||||
osc->phase_increment = (M_2PI * frequency) / osc->sample_rate;
|
||||
}
|
||||
|
||||
float get_oscillator_sin_sample(Oscillator *osc) {
|
||||
float sample = sinf(osc->phase);
|
||||
advance_oscillator(osc);
|
||||
return sample;
|
||||
float sample = sinf(osc->phase);
|
||||
advance_oscillator(osc);
|
||||
return sample;
|
||||
}
|
||||
|
||||
float get_oscillator_cos_sample(Oscillator *osc) {
|
||||
float sample = cosf(osc->phase);
|
||||
advance_oscillator(osc);
|
||||
return sample;
|
||||
float sample = cosf(osc->phase);
|
||||
advance_oscillator(osc);
|
||||
return sample;
|
||||
}
|
||||
|
||||
float get_oscillator_sin_multiplier_ni(Oscillator *osc, float multiplier) { // ni = No Increment
|
||||
return sinf(fmodf(osc->phase * multiplier, M_2PI));
|
||||
return sinf(fmodf(osc->phase * multiplier, M_2PI));
|
||||
}
|
||||
float get_oscillator_cos_multiplier_ni(Oscillator *osc, float multiplier) {
|
||||
return cosf(fmodf(osc->phase * multiplier, M_2PI));
|
||||
return cosf(fmodf(osc->phase * multiplier, M_2PI));
|
||||
}
|
||||
|
||||
void advance_oscillator(Oscillator *osc) {
|
||||
osc->phase += osc->phase_increment;
|
||||
if (osc->phase >= M_2PI) {
|
||||
osc->phase -= M_2PI;
|
||||
}
|
||||
osc->phase += osc->phase_increment;
|
||||
if (osc->phase >= M_2PI) {
|
||||
osc->phase -= M_2PI;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -4,9 +4,9 @@
|
||||
#include "math.h"
|
||||
|
||||
typedef struct {
|
||||
float phase;
|
||||
float phase_increment;
|
||||
float sample_rate;
|
||||
float phase;
|
||||
float phase_increment;
|
||||
float sample_rate;
|
||||
} Oscillator;
|
||||
|
||||
void init_oscillator(Oscillator *osc, float frequency, float sample_rate);
|
||||
|
||||
+211
-211
@@ -41,266 +41,266 @@ volatile int sequence_type = SEQ_NONE;
|
||||
volatile time_t last_sequence_time = 0;
|
||||
|
||||
static void stop(int signum) {
|
||||
(void)signum;
|
||||
printf("\nReceived stop signal.\n");
|
||||
to_run = 0;
|
||||
(void)signum;
|
||||
printf("\nReceived stop signal.\n");
|
||||
to_run = 0;
|
||||
}
|
||||
|
||||
void show_version() {
|
||||
printf("chimer95 (GTS time signal encoder by radio95) version 1.1\n");
|
||||
printf("chimer95 (GTS time signal encoder by radio95) version 1.1\n");
|
||||
}
|
||||
|
||||
void show_help(char *name) {
|
||||
printf(
|
||||
"Usage: %s\n"
|
||||
" -o,--output Override output device [default: %s]\n"
|
||||
" -F,--frequency GTS Frequency [default: %.1f Hz]\n"
|
||||
" -s,--samplerate Output Samplerate [default: %d]\n"
|
||||
" -v,--volume Output volume [default: %.2f]\n"
|
||||
" -t,--offset GTS Offset [default: %d s]\n"
|
||||
" -T,--test Enable test mode (plays full hour signal at end of every minute)\n"
|
||||
,name
|
||||
,OUTPUT_DEVICE
|
||||
,DEFAULT_FREQ
|
||||
,DEFAULT_SAMPLE_RATE
|
||||
,DEFAULT_MASTER_VOLUME
|
||||
,DEFAULT_OFFSET
|
||||
);
|
||||
printf(
|
||||
"Usage: %s\n"
|
||||
" -o,--output Override output device [default: %s]\n"
|
||||
" -F,--frequency GTS Frequency [default: %.1f Hz]\n"
|
||||
" -s,--samplerate Output Samplerate [default: %d]\n"
|
||||
" -v,--volume Output volume [default: %.2f]\n"
|
||||
" -t,--offset GTS Offset [default: %d s]\n"
|
||||
" -T,--test Enable test mode (plays full hour signal at end of every minute)\n"
|
||||
,name
|
||||
,OUTPUT_DEVICE
|
||||
,DEFAULT_FREQ
|
||||
,DEFAULT_SAMPLE_RATE
|
||||
,DEFAULT_MASTER_VOLUME
|
||||
,DEFAULT_OFFSET
|
||||
);
|
||||
}
|
||||
|
||||
void generate_signal(float *output, int buffer_size, Oscillator *osc, float volume,
|
||||
int *elapsed_samples, int total_samples, int pip_samples,
|
||||
int pause_samples, int beep_samples, int num_pips) {
|
||||
int *elapsed_samples, int total_samples, int pip_samples,
|
||||
int pause_samples, int beep_samples, int num_pips) {
|
||||
|
||||
for (int i = 0; i < buffer_size; i++) {
|
||||
if (*elapsed_samples >= total_samples) {
|
||||
output[i] = 0;
|
||||
playing_sequence = 0;
|
||||
} else {
|
||||
int cycle_position = *elapsed_samples;
|
||||
int pip_cycle = pip_samples + pause_samples;
|
||||
for (int i = 0; i < buffer_size; i++) {
|
||||
if (*elapsed_samples >= total_samples) {
|
||||
output[i] = 0;
|
||||
playing_sequence = 0;
|
||||
} else {
|
||||
int cycle_position = *elapsed_samples;
|
||||
int pip_cycle = pip_samples + pause_samples;
|
||||
|
||||
if (cycle_position < num_pips * pip_cycle) {
|
||||
int within_cycle = cycle_position % pip_cycle;
|
||||
if (within_cycle < pip_samples) {
|
||||
output[i] = get_oscillator_sin_sample(osc) * volume;
|
||||
} else {
|
||||
output[i] = 0;
|
||||
}
|
||||
} else if (cycle_position < num_pips * pip_cycle + beep_samples) {
|
||||
output[i] = get_oscillator_sin_sample(osc) * volume;
|
||||
} else {
|
||||
output[i] = 0;
|
||||
}
|
||||
if (cycle_position < num_pips * pip_cycle) {
|
||||
int within_cycle = cycle_position % pip_cycle;
|
||||
if (within_cycle < pip_samples) {
|
||||
output[i] = get_oscillator_sin_sample(osc) * volume;
|
||||
} else {
|
||||
output[i] = 0;
|
||||
}
|
||||
} else if (cycle_position < num_pips * pip_cycle + beep_samples) {
|
||||
output[i] = get_oscillator_sin_sample(osc) * volume;
|
||||
} else {
|
||||
output[i] = 0;
|
||||
}
|
||||
|
||||
(*elapsed_samples)++;
|
||||
}
|
||||
}
|
||||
(*elapsed_samples)++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int check_time_for_sequence(int test_mode, int offset) {
|
||||
static time_t last_check = 0;
|
||||
static int last_minute = -1;
|
||||
static time_t last_check = 0;
|
||||
static int last_minute = -1;
|
||||
|
||||
time_t now = time(NULL);
|
||||
if (now == last_check) {
|
||||
return SEQ_NONE;
|
||||
}
|
||||
time_t now = time(NULL);
|
||||
if (now == last_check) {
|
||||
return SEQ_NONE;
|
||||
}
|
||||
|
||||
last_check = now;
|
||||
struct tm *utc_time = gmtime(&now);
|
||||
int minute = utc_time->tm_min;
|
||||
int second = utc_time->tm_sec;
|
||||
last_check = now;
|
||||
struct tm *utc_time = gmtime(&now);
|
||||
int minute = utc_time->tm_min;
|
||||
int second = utc_time->tm_sec;
|
||||
|
||||
if (difftime(now, last_sequence_time) < 1.0) {
|
||||
return SEQ_NONE;
|
||||
}
|
||||
if (difftime(now, last_sequence_time) < 1.0) {
|
||||
return SEQ_NONE;
|
||||
}
|
||||
|
||||
if (minute == 29 && second == (56 + offset)) {
|
||||
last_sequence_time = now;
|
||||
return SEQ_29_56;
|
||||
}
|
||||
if (minute == 29 && second == (56 + offset)) {
|
||||
last_sequence_time = now;
|
||||
return SEQ_29_56;
|
||||
}
|
||||
|
||||
if (minute == 59 && second == (55 + offset)) {
|
||||
last_sequence_time = now;
|
||||
return SEQ_59_55;
|
||||
}
|
||||
if (minute == 59 && second == (55 + offset)) {
|
||||
last_sequence_time = now;
|
||||
return SEQ_59_55;
|
||||
}
|
||||
|
||||
if (test_mode && second == (55 + offset) && minute != last_minute) {
|
||||
last_minute = minute;
|
||||
last_sequence_time = now;
|
||||
return SEQ_TEST_HOUR;
|
||||
}
|
||||
if (test_mode && second == (55 + offset) && minute != last_minute) {
|
||||
last_minute = minute;
|
||||
last_sequence_time = now;
|
||||
return SEQ_TEST_HOUR;
|
||||
}
|
||||
|
||||
return SEQ_NONE;
|
||||
return SEQ_NONE;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
show_version();
|
||||
show_version();
|
||||
|
||||
pa_simple *output_device;
|
||||
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
||||
float freq = DEFAULT_FREQ;
|
||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
||||
int offset = DEFAULT_OFFSET;
|
||||
int test_mode = 0;
|
||||
pa_simple *output_device;
|
||||
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
||||
float freq = DEFAULT_FREQ;
|
||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
||||
int offset = DEFAULT_OFFSET;
|
||||
int test_mode = 0;
|
||||
|
||||
// Parse command line arguments
|
||||
int opt;
|
||||
const char *short_opt = "o:F:s:v:t:Th";
|
||||
struct option long_opt[] = {
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"frequency", required_argument, NULL, 'F'},
|
||||
{"samplerate", required_argument, NULL, 's'},
|
||||
{"volume", required_argument, NULL, 'v'},
|
||||
{"offset", required_argument, NULL, 't'},
|
||||
{"test", no_argument, NULL, 'T'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
// Parse command line arguments
|
||||
int opt;
|
||||
const char *short_opt = "o:F:s:v:t:Th";
|
||||
struct option long_opt[] = {
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"frequency", required_argument, NULL, 'F'},
|
||||
{"samplerate", required_argument, NULL, 's'},
|
||||
{"volume", required_argument, NULL, 'v'},
|
||||
{"offset", required_argument, NULL, 't'},
|
||||
{"test", no_argument, NULL, 'T'},
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||
switch(opt) {
|
||||
case 'o':
|
||||
strncpy(audio_output_device, optarg, sizeof(audio_output_device) - 1);
|
||||
audio_output_device[sizeof(audio_output_device) - 1] = '\0';
|
||||
break;
|
||||
case 'F':
|
||||
freq = strtof(optarg, NULL);
|
||||
break;
|
||||
case 's':
|
||||
sample_rate = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'v':
|
||||
master_volume = strtof(optarg, NULL);
|
||||
break;
|
||||
case 't':
|
||||
offset = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'T':
|
||||
test_mode = 1;
|
||||
break;
|
||||
case 'h':
|
||||
show_help(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||
switch(opt) {
|
||||
case 'o':
|
||||
strncpy(audio_output_device, optarg, sizeof(audio_output_device) - 1);
|
||||
audio_output_device[sizeof(audio_output_device) - 1] = '\0';
|
||||
break;
|
||||
case 'F':
|
||||
freq = strtof(optarg, NULL);
|
||||
break;
|
||||
case 's':
|
||||
sample_rate = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'v':
|
||||
master_volume = strtof(optarg, NULL);
|
||||
break;
|
||||
case 't':
|
||||
offset = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'T':
|
||||
test_mode = 1;
|
||||
break;
|
||||
case 'h':
|
||||
show_help(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Configuration:\n");
|
||||
printf(" Output device: %s\n", audio_output_device);
|
||||
printf(" Frequency: %.1f Hz\n", freq);
|
||||
printf(" Sample rate: %d Hz\n", sample_rate);
|
||||
printf(" Volume: %.2f\n", master_volume);
|
||||
printf(" Time offset: %d seconds\n", offset);
|
||||
printf(" Test mode: %s\n", test_mode ? "Enabled" : "Disabled");
|
||||
printf("Configuration:\n");
|
||||
printf(" Output device: %s\n", audio_output_device);
|
||||
printf(" Frequency: %.1f Hz\n", freq);
|
||||
printf(" Sample rate: %d Hz\n", sample_rate);
|
||||
printf(" Volume: %.2f\n", master_volume);
|
||||
printf(" Time offset: %d seconds\n", offset);
|
||||
printf(" Test mode: %s\n", test_mode ? "Enabled" : "Disabled");
|
||||
|
||||
// Setup PulseAudio
|
||||
pa_sample_spec mono_format = {
|
||||
.format = PA_SAMPLE_FLOAT32NE,
|
||||
.channels = 1,
|
||||
.rate = sample_rate
|
||||
};
|
||||
// Setup PulseAudio
|
||||
pa_sample_spec mono_format = {
|
||||
.format = PA_SAMPLE_FLOAT32NE,
|
||||
.channels = 1,
|
||||
.rate = sample_rate
|
||||
};
|
||||
|
||||
pa_buffer_attr output_buffer_atr = {
|
||||
.maxlength = buffer_maxlength,
|
||||
.tlength = buffer_tlength_fragsize,
|
||||
.prebuf = buffer_prebuf
|
||||
};
|
||||
pa_buffer_attr output_buffer_atr = {
|
||||
.maxlength = buffer_maxlength,
|
||||
.tlength = buffer_tlength_fragsize,
|
||||
.prebuf = buffer_prebuf
|
||||
};
|
||||
|
||||
int pulse_error;
|
||||
int pulse_error;
|
||||
|
||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
||||
|
||||
output_device = pa_simple_new(
|
||||
NULL,
|
||||
"chimer95",
|
||||
PA_STREAM_PLAYBACK,
|
||||
audio_output_device,
|
||||
"GTS Output",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&output_buffer_atr,
|
||||
&pulse_error
|
||||
);
|
||||
output_device = pa_simple_new(
|
||||
NULL,
|
||||
"chimer95",
|
||||
PA_STREAM_PLAYBACK,
|
||||
audio_output_device,
|
||||
"GTS Output",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&output_buffer_atr,
|
||||
&pulse_error
|
||||
);
|
||||
|
||||
if (!output_device) {
|
||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(pulse_error));
|
||||
return 1;
|
||||
}
|
||||
if (!output_device) {
|
||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(pulse_error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
Oscillator osc;
|
||||
init_oscillator(&osc, freq, sample_rate);
|
||||
Oscillator osc;
|
||||
init_oscillator(&osc, freq, sample_rate);
|
||||
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
|
||||
float output[BUFFER_SIZE];
|
||||
float output[BUFFER_SIZE];
|
||||
|
||||
int pip_samples = (int)((PIP_DURATION / 1000.0) * sample_rate);
|
||||
int pause_samples = (int)((PIP_PAUSE / 1000.0) * sample_rate);
|
||||
int beep_samples = (int)((BEEP_DURATION / 1000.0) * sample_rate);
|
||||
int pip_samples = (int)((PIP_DURATION / 1000.0) * sample_rate);
|
||||
int pause_samples = (int)((PIP_PAUSE / 1000.0) * sample_rate);
|
||||
int beep_samples = (int)((BEEP_DURATION / 1000.0) * sample_rate);
|
||||
|
||||
int samples_29_56 = 4 * (pip_samples + pause_samples) + beep_samples;
|
||||
int samples_59_55 = 5 * (pip_samples + pause_samples) + beep_samples;
|
||||
int samples_29_56 = 4 * (pip_samples + pause_samples) + beep_samples;
|
||||
int samples_59_55 = 5 * (pip_samples + pause_samples) + beep_samples;
|
||||
|
||||
printf("Ready to play time signals.\n");
|
||||
printf("Will trigger at XX:29:%02d and XX:59:%02d\n", 56+offset, 55+offset);
|
||||
if (test_mode) {
|
||||
printf("TEST MODE: Will also play full hour signal at the end of every minute\n");
|
||||
}
|
||||
printf("Ready to play time signals.\n");
|
||||
printf("Will trigger at XX:29:%02d and XX:59:%02d\n", 56+offset, 55+offset);
|
||||
if (test_mode) {
|
||||
printf("TEST MODE: Will also play full hour signal at the end of every minute\n");
|
||||
}
|
||||
|
||||
int elapsed_samples = 0;
|
||||
int total_sequence_samples = 0;
|
||||
int sequence_completed = 0;
|
||||
int elapsed_samples = 0;
|
||||
int total_sequence_samples = 0;
|
||||
int sequence_completed = 0;
|
||||
|
||||
while (to_run) {
|
||||
if (!playing_sequence) {
|
||||
int new_sequence = check_time_for_sequence(test_mode, offset);
|
||||
while (to_run) {
|
||||
if (!playing_sequence) {
|
||||
int new_sequence = check_time_for_sequence(test_mode, offset);
|
||||
|
||||
if (new_sequence != SEQ_NONE) {
|
||||
playing_sequence = 1;
|
||||
sequence_type = new_sequence;
|
||||
elapsed_samples = 0;
|
||||
sequence_completed = 0;
|
||||
if (new_sequence != SEQ_NONE) {
|
||||
playing_sequence = 1;
|
||||
sequence_type = new_sequence;
|
||||
elapsed_samples = 0;
|
||||
sequence_completed = 0;
|
||||
|
||||
if (new_sequence == SEQ_29_56) {
|
||||
total_sequence_samples = samples_29_56;
|
||||
} else {
|
||||
total_sequence_samples = samples_59_55;
|
||||
}
|
||||
if (new_sequence == SEQ_29_56) {
|
||||
total_sequence_samples = samples_29_56;
|
||||
} else {
|
||||
total_sequence_samples = samples_59_55;
|
||||
}
|
||||
|
||||
memset(output, 0, sizeof(output));
|
||||
} else {
|
||||
static int idle_counter = 0;
|
||||
if (idle_counter++ % 10 == 0) {
|
||||
memset(output, 0, sizeof(output));
|
||||
pa_simple_write(output_device, output, sizeof(output), &pulse_error);
|
||||
}
|
||||
memset(output, 0, sizeof(output));
|
||||
} else {
|
||||
static int idle_counter = 0;
|
||||
if (idle_counter++ % 10 == 0) {
|
||||
memset(output, 0, sizeof(output));
|
||||
pa_simple_write(output_device, output, sizeof(output), &pulse_error);
|
||||
}
|
||||
|
||||
struct timespec ts = {0, 5000000}; // 5ms sleep
|
||||
nanosleep(&ts, NULL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
struct timespec ts = {0, 5000000}; // 5ms sleep
|
||||
nanosleep(&ts, NULL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
int num_pips = (sequence_type == SEQ_29_56) ? 4 : 5;
|
||||
generate_signal(output, BUFFER_SIZE, &osc, master_volume,
|
||||
&elapsed_samples, total_sequence_samples,
|
||||
pip_samples, pause_samples, beep_samples, num_pips);
|
||||
int num_pips = (sequence_type == SEQ_29_56) ? 4 : 5;
|
||||
generate_signal(output, BUFFER_SIZE, &osc, master_volume,
|
||||
&elapsed_samples, total_sequence_samples,
|
||||
pip_samples, pause_samples, beep_samples, num_pips);
|
||||
|
||||
if (!playing_sequence && !sequence_completed) {
|
||||
sequence_completed = 1;
|
||||
}
|
||||
if (!playing_sequence && !sequence_completed) {
|
||||
sequence_completed = 1;
|
||||
}
|
||||
|
||||
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pa_simple_write(output_device, output, sizeof(output), &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(output_device);
|
||||
return 0;
|
||||
printf("Cleaning up...\n");
|
||||
pa_simple_free(output_device);
|
||||
return 0;
|
||||
}
|
||||
+337
-337
@@ -49,437 +49,437 @@ volatile int dcf77_bits[60];
|
||||
unsigned int lfsr = 0;
|
||||
|
||||
static void stop(int signum) {
|
||||
(void)signum;
|
||||
printf("\nReceived stop signal.\n");
|
||||
to_run = 0;
|
||||
(void)signum;
|
||||
printf("\nReceived stop signal.\n");
|
||||
to_run = 0;
|
||||
}
|
||||
|
||||
unsigned int generate_chip() {
|
||||
unsigned int chip = lfsr & 1;
|
||||
unsigned int chip = lfsr & 1;
|
||||
|
||||
lfsr >>= 1;
|
||||
if (chip || !lfsr)
|
||||
lfsr ^= 0x110;
|
||||
lfsr >>= 1;
|
||||
if (chip || !lfsr)
|
||||
lfsr ^= 0x110;
|
||||
|
||||
return chip;
|
||||
return chip;
|
||||
}
|
||||
|
||||
void reset_lfsr() {
|
||||
lfsr = 0;
|
||||
lfsr = 0;
|
||||
}
|
||||
|
||||
int is_cet_dst(struct tm *tm_time) {
|
||||
int month = tm_time->tm_mon + 1;
|
||||
int day = tm_time->tm_mday;
|
||||
int hour = tm_time->tm_hour;
|
||||
int month = tm_time->tm_mon + 1;
|
||||
int day = tm_time->tm_mday;
|
||||
int hour = tm_time->tm_hour;
|
||||
|
||||
if (month == 3) {
|
||||
int last_sunday = 31 - ((5 + 31) % 7);
|
||||
if ((day > last_sunday) || (day == last_sunday && hour >= 2)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (month > 3 && month < 10) {
|
||||
return 1;
|
||||
} else if (month == 10) {
|
||||
int last_sunday = 31 - ((5 + 31) % 7);
|
||||
if ((day < last_sunday) || (day == last_sunday && hour < 3)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (month == 3) {
|
||||
int last_sunday = 31 - ((5 + 31) % 7);
|
||||
if ((day > last_sunday) || (day == last_sunday && hour >= 2)) {
|
||||
return 1;
|
||||
}
|
||||
} else if (month > 3 && month < 10) {
|
||||
return 1;
|
||||
} else if (month == 10) {
|
||||
int last_sunday = 31 - ((5 + 31) % 7);
|
||||
if ((day < last_sunday) || (day == last_sunday && hour < 3)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int is_timezone_change_soon() {
|
||||
time_t now, in_an_hour;
|
||||
struct tm cet_now, cet_later;
|
||||
time_t now, in_an_hour;
|
||||
struct tm cet_now, cet_later;
|
||||
|
||||
time(&now);
|
||||
in_an_hour = now + 3600;
|
||||
time(&now);
|
||||
in_an_hour = now + 3600;
|
||||
|
||||
memset(&cet_now, 0, sizeof(struct tm));
|
||||
memset(&cet_later, 0, sizeof(struct tm));
|
||||
memset(&cet_now, 0, sizeof(struct tm));
|
||||
memset(&cet_later, 0, sizeof(struct tm));
|
||||
|
||||
struct tm *gm_now = gmtime(&now);
|
||||
struct tm *gm_later = gmtime(&in_an_hour);
|
||||
struct tm *gm_now = gmtime(&now);
|
||||
struct tm *gm_later = gmtime(&in_an_hour);
|
||||
|
||||
cet_now = *gm_now;
|
||||
cet_later = *gm_later;
|
||||
cet_now = *gm_now;
|
||||
cet_later = *gm_later;
|
||||
|
||||
cet_now.tm_hour += 1;
|
||||
cet_later.tm_hour += 1;
|
||||
cet_now.tm_hour += 1;
|
||||
cet_later.tm_hour += 1;
|
||||
|
||||
int is_dst_now = is_cet_dst(&cet_now);
|
||||
int is_dst_later = is_cet_dst(&cet_later);
|
||||
int is_dst_now = is_cet_dst(&cet_now);
|
||||
int is_dst_later = is_cet_dst(&cet_later);
|
||||
|
||||
if (is_dst_now) cet_now.tm_hour += 1;
|
||||
if (is_dst_later) cet_later.tm_hour += 1;
|
||||
if (is_dst_now) cet_now.tm_hour += 1;
|
||||
if (is_dst_later) cet_later.tm_hour += 1;
|
||||
|
||||
mktime(&cet_now);
|
||||
mktime(&cet_later);
|
||||
mktime(&cet_now);
|
||||
mktime(&cet_later);
|
||||
|
||||
return is_dst_now != is_dst_later;
|
||||
return is_dst_now != is_dst_later;
|
||||
}
|
||||
|
||||
void calculate_dcf77_bits(time_t now, int *bits) {
|
||||
struct tm *t = gmtime(&now);
|
||||
int cest = is_cet_dst(t);
|
||||
struct tm *t = gmtime(&now);
|
||||
int cest = is_cet_dst(t);
|
||||
|
||||
memset(bits, 0, 60 * sizeof(int));
|
||||
memset(bits, 0, 60 * sizeof(int));
|
||||
|
||||
bits[16] = is_timezone_change_soon();
|
||||
if(cest) {
|
||||
bits[17] = 1;
|
||||
} else {
|
||||
bits[18] = 1;
|
||||
}
|
||||
bits[20] = 1;
|
||||
bits[16] = is_timezone_change_soon();
|
||||
if(cest) {
|
||||
bits[17] = 1;
|
||||
} else {
|
||||
bits[18] = 1;
|
||||
}
|
||||
bits[20] = 1;
|
||||
|
||||
int minutes = t->tm_min;
|
||||
bits[21] = (minutes % 10) & 0x01;
|
||||
bits[22] = ((minutes % 10) >> 1) & 0x01;
|
||||
bits[23] = ((minutes % 10) >> 2) & 0x01;
|
||||
bits[24] = ((minutes % 10) >> 3) & 0x01;
|
||||
bits[25] = ((minutes / 10) & 0x01);
|
||||
bits[26] = ((minutes / 10) >> 1) & 0x01;
|
||||
bits[27] = ((minutes / 10) >> 2) & 0x01;
|
||||
int minutes = t->tm_min;
|
||||
bits[21] = (minutes % 10) & 0x01;
|
||||
bits[22] = ((minutes % 10) >> 1) & 0x01;
|
||||
bits[23] = ((minutes % 10) >> 2) & 0x01;
|
||||
bits[24] = ((minutes % 10) >> 3) & 0x01;
|
||||
bits[25] = ((minutes / 10) & 0x01);
|
||||
bits[26] = ((minutes / 10) >> 1) & 0x01;
|
||||
bits[27] = ((minutes / 10) >> 2) & 0x01;
|
||||
|
||||
int parity = 0;
|
||||
for (int i = 21; i <= 27; i++) {
|
||||
parity ^= bits[i];
|
||||
}
|
||||
bits[28] = parity;
|
||||
int parity = 0;
|
||||
for (int i = 21; i <= 27; i++) {
|
||||
parity ^= bits[i];
|
||||
}
|
||||
bits[28] = parity;
|
||||
|
||||
int hours = t->tm_hour-1;
|
||||
hours += 1;
|
||||
if(cest) hours += 1;
|
||||
bits[29] = (hours % 10) & 0x01;
|
||||
bits[30] = ((hours % 10) >> 1) & 0x01;
|
||||
bits[31] = ((hours % 10) >> 2) & 0x01;
|
||||
bits[32] = ((hours % 10) >> 3) & 0x01;
|
||||
bits[33] = ((hours / 10) & 0x01);
|
||||
bits[34] = ((hours / 10) >> 1) & 0x01;
|
||||
int hours = t->tm_hour-1;
|
||||
hours += 1;
|
||||
if(cest) hours += 1;
|
||||
bits[29] = (hours % 10) & 0x01;
|
||||
bits[30] = ((hours % 10) >> 1) & 0x01;
|
||||
bits[31] = ((hours % 10) >> 2) & 0x01;
|
||||
bits[32] = ((hours % 10) >> 3) & 0x01;
|
||||
bits[33] = ((hours / 10) & 0x01);
|
||||
bits[34] = ((hours / 10) >> 1) & 0x01;
|
||||
|
||||
parity = 0;
|
||||
for (int i = 29; i <= 34; i++) {
|
||||
parity ^= bits[i];
|
||||
}
|
||||
bits[35] = parity;
|
||||
parity = 0;
|
||||
for (int i = 29; i <= 34; i++) {
|
||||
parity ^= bits[i];
|
||||
}
|
||||
bits[35] = parity;
|
||||
|
||||
int day = t->tm_mday;
|
||||
bits[36] = (day % 10) & 0x01;
|
||||
bits[37] = ((day % 10) >> 1) & 0x01;
|
||||
bits[38] = ((day % 10) >> 2) & 0x01;
|
||||
bits[39] = ((day % 10) >> 3) & 0x01;
|
||||
bits[40] = ((day / 10) & 0x01);
|
||||
bits[41] = ((day / 10) >> 1) & 0x01;
|
||||
int day = t->tm_mday;
|
||||
bits[36] = (day % 10) & 0x01;
|
||||
bits[37] = ((day % 10) >> 1) & 0x01;
|
||||
bits[38] = ((day % 10) >> 2) & 0x01;
|
||||
bits[39] = ((day % 10) >> 3) & 0x01;
|
||||
bits[40] = ((day / 10) & 0x01);
|
||||
bits[41] = ((day / 10) >> 1) & 0x01;
|
||||
|
||||
int dow = t->tm_wday == 0 ? 7 : t->tm_wday;
|
||||
bits[42] = dow & 0x01;
|
||||
bits[43] = (dow >> 1) & 0x01;
|
||||
bits[44] = (dow >> 2) & 0x01;
|
||||
int dow = t->tm_wday == 0 ? 7 : t->tm_wday;
|
||||
bits[42] = dow & 0x01;
|
||||
bits[43] = (dow >> 1) & 0x01;
|
||||
bits[44] = (dow >> 2) & 0x01;
|
||||
|
||||
int month = t->tm_mon + 1;
|
||||
bits[45] = (month % 10) & 0x01;
|
||||
bits[46] = ((month % 10) >> 1) & 0x01;
|
||||
bits[47] = ((month % 10) >> 2) & 0x01;
|
||||
bits[48] = ((month % 10) >> 3) & 0x01;
|
||||
bits[49] = (month / 10) & 0x01;
|
||||
int month = t->tm_mon + 1;
|
||||
bits[45] = (month % 10) & 0x01;
|
||||
bits[46] = ((month % 10) >> 1) & 0x01;
|
||||
bits[47] = ((month % 10) >> 2) & 0x01;
|
||||
bits[48] = ((month % 10) >> 3) & 0x01;
|
||||
bits[49] = (month / 10) & 0x01;
|
||||
|
||||
int year = t->tm_year % 100;
|
||||
bits[50] = (year % 10) & 0x01;
|
||||
bits[51] = ((year % 10) >> 1) & 0x01;
|
||||
bits[52] = ((year % 10) >> 2) & 0x01;
|
||||
bits[53] = ((year % 10) >> 3) & 0x01;
|
||||
bits[54] = ((year / 10) & 0x01);
|
||||
bits[55] = ((year / 10) >> 1) & 0x01;
|
||||
bits[56] = ((year / 10) >> 2) & 0x01;
|
||||
bits[57] = ((year / 10) >> 3) & 0x01;
|
||||
int year = t->tm_year % 100;
|
||||
bits[50] = (year % 10) & 0x01;
|
||||
bits[51] = ((year % 10) >> 1) & 0x01;
|
||||
bits[52] = ((year % 10) >> 2) & 0x01;
|
||||
bits[53] = ((year % 10) >> 3) & 0x01;
|
||||
bits[54] = ((year / 10) & 0x01);
|
||||
bits[55] = ((year / 10) >> 1) & 0x01;
|
||||
bits[56] = ((year / 10) >> 2) & 0x01;
|
||||
bits[57] = ((year / 10) >> 3) & 0x01;
|
||||
|
||||
parity = 0;
|
||||
for (int i = 36; i <= 57; i++) {
|
||||
parity ^= bits[i];
|
||||
}
|
||||
bits[58] = parity;
|
||||
parity = 0;
|
||||
for (int i = 36; i <= 57; i++) {
|
||||
parity ^= bits[i];
|
||||
}
|
||||
bits[58] = parity;
|
||||
|
||||
bits[59] = 2;
|
||||
bits[59] = 2;
|
||||
}
|
||||
|
||||
void print_dcf77_bits(const int *bits) {
|
||||
printf("DCF77 Bit Pattern: ");
|
||||
for (int i = 0; i < 60; i++) {
|
||||
printf("%d", bits[i]);
|
||||
if ((i+1) % 10 == 0) printf(" ");
|
||||
}
|
||||
printf("\n");
|
||||
printf("DCF77 Bit Pattern: ");
|
||||
for (int i = 0; i < 60; i++) {
|
||||
printf("%d", bits[i]);
|
||||
if ((i+1) % 10 == 0) printf(" ");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void show_version() {
|
||||
printf("dcf95 (DCF77 time signal encoder by radio95) version 1.1\n");
|
||||
printf("dcf95 (DCF77 time signal encoder by radio95) version 1.1\n");
|
||||
}
|
||||
|
||||
void show_help(char *name) {
|
||||
printf(
|
||||
"Usage: %s\n"
|
||||
" -o,--output Override output device [default: %s]\n"
|
||||
" -F,--frequency DCF77 Frequency [default: %.1f Hz]\n"
|
||||
" -s,--samplerate Output Samplerate [default: %d]\n"
|
||||
" -v,--volume Output volume [default: %.2f]\n"
|
||||
" -t,--offset Time Offset [default: %ds]\n"
|
||||
" -T,--test Enable test mode\n"
|
||||
" -n,--no-phase Disable phase modulation\n"
|
||||
,name
|
||||
,OUTPUT_DEVICE
|
||||
,DEFAULT_FREQ
|
||||
,DEFAULT_SAMPLE_RATE
|
||||
,DEFAULT_MASTER_VOLUME
|
||||
,DEFAULT_OFFSET
|
||||
);
|
||||
printf(
|
||||
"Usage: %s\n"
|
||||
" -o,--output Override output device [default: %s]\n"
|
||||
" -F,--frequency DCF77 Frequency [default: %.1f Hz]\n"
|
||||
" -s,--samplerate Output Samplerate [default: %d]\n"
|
||||
" -v,--volume Output volume [default: %.2f]\n"
|
||||
" -t,--offset Time Offset [default: %ds]\n"
|
||||
" -T,--test Enable test mode\n"
|
||||
" -n,--no-phase Disable phase modulation\n"
|
||||
,name
|
||||
,OUTPUT_DEVICE
|
||||
,DEFAULT_FREQ
|
||||
,DEFAULT_SAMPLE_RATE
|
||||
,DEFAULT_MASTER_VOLUME
|
||||
,DEFAULT_OFFSET
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
show_version();
|
||||
show_version();
|
||||
|
||||
pa_simple *output_device;
|
||||
pa_simple *output_device;
|
||||
|
||||
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||
|
||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
||||
float freq = DEFAULT_FREQ;
|
||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
||||
int offset = DEFAULT_OFFSET;
|
||||
int test_mode = 0;
|
||||
int no_phase = 0;
|
||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
||||
float freq = DEFAULT_FREQ;
|
||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
||||
int offset = DEFAULT_OFFSET;
|
||||
int test_mode = 0;
|
||||
int no_phase = 0;
|
||||
|
||||
// #region Parse Arguments
|
||||
int opt;
|
||||
const char *short_opt = "o:F:s:v:t:Tnh";
|
||||
struct option long_opt[] =
|
||||
{
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"frequency", required_argument, NULL, 'F'},
|
||||
{"samplerate", required_argument, NULL, 's'},
|
||||
{"volume", required_argument, NULL, 'v'},
|
||||
{"offset", required_argument, NULL, 't'},
|
||||
{"test", no_argument, NULL, 'T'},
|
||||
{"no-phase", no_argument, NULL, 'n'},
|
||||
// #region Parse Arguments
|
||||
int opt;
|
||||
const char *short_opt = "o:F:s:v:t:Tnh";
|
||||
struct option long_opt[] =
|
||||
{
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"frequency", required_argument, NULL, 'F'},
|
||||
{"samplerate", required_argument, NULL, 's'},
|
||||
{"volume", required_argument, NULL, 'v'},
|
||||
{"offset", required_argument, NULL, 't'},
|
||||
{"test", no_argument, NULL, 'T'},
|
||||
{"no-phase", no_argument, NULL, 'n'},
|
||||
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||
switch(opt) {
|
||||
case 'o': // Output Device
|
||||
memcpy(audio_output_device, optarg, 63);
|
||||
audio_output_device[63] = '\0'; // Ensure null-termination
|
||||
break;
|
||||
case 'F': // Frequency
|
||||
freq = strtof(optarg, NULL);
|
||||
break;
|
||||
case 's': // Sample rate
|
||||
sample_rate = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'v': // Volume
|
||||
master_volume = strtof(optarg, NULL);
|
||||
break;
|
||||
case 't': // Offset
|
||||
offset = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'T': // Test mode
|
||||
test_mode = 1;
|
||||
break;
|
||||
case 'n': // Disable phase modulation
|
||||
no_phase = 1;
|
||||
break;
|
||||
case 'h':
|
||||
show_help(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||
switch(opt) {
|
||||
case 'o': // Output Device
|
||||
memcpy(audio_output_device, optarg, 63);
|
||||
audio_output_device[63] = '\0'; // Ensure null-termination
|
||||
break;
|
||||
case 'F': // Frequency
|
||||
freq = strtof(optarg, NULL);
|
||||
break;
|
||||
case 's': // Sample rate
|
||||
sample_rate = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'v': // Volume
|
||||
master_volume = strtof(optarg, NULL);
|
||||
break;
|
||||
case 't': // Offset
|
||||
offset = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'T': // Test mode
|
||||
test_mode = 1;
|
||||
break;
|
||||
case 'n': // Disable phase modulation
|
||||
no_phase = 1;
|
||||
break;
|
||||
case 'h':
|
||||
show_help(argv[0]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
if(test_mode) {
|
||||
time_t now = time(NULL) + offset + 60;
|
||||
calculate_dcf77_bits(now, (int *)dcf77_bits);
|
||||
print_dcf77_bits((int *)dcf77_bits);
|
||||
return 0;
|
||||
}
|
||||
if(test_mode) {
|
||||
time_t now = time(NULL) + offset + 60;
|
||||
calculate_dcf77_bits(now, (int *)dcf77_bits);
|
||||
print_dcf77_bits((int *)dcf77_bits);
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Configuration:\n");
|
||||
printf(" Output device: %s\n", audio_output_device);
|
||||
printf(" Frequency: %.1f Hz\n", freq);
|
||||
printf(" Sample rate: %d Hz\n", sample_rate);
|
||||
printf(" Volume: %.2f\n", master_volume);
|
||||
printf(" Time offset: %d seconds\n", offset);
|
||||
if (no_phase) {
|
||||
printf(" Phase modulation: Disabled\n");
|
||||
} else {
|
||||
printf(" Phase modulation: +/- %.1f degrees\n", PHASE_SHIFT);
|
||||
}
|
||||
printf("Configuration:\n");
|
||||
printf(" Output device: %s\n", audio_output_device);
|
||||
printf(" Frequency: %.1f Hz\n", freq);
|
||||
printf(" Sample rate: %d Hz\n", sample_rate);
|
||||
printf(" Volume: %.2f\n", master_volume);
|
||||
printf(" Time offset: %d seconds\n", offset);
|
||||
if (no_phase) {
|
||||
printf(" Phase modulation: Disabled\n");
|
||||
} else {
|
||||
printf(" Phase modulation: +/- %.1f degrees\n", PHASE_SHIFT);
|
||||
}
|
||||
|
||||
// #region Setup devices
|
||||
pa_sample_spec mono_format = {
|
||||
.format = PA_SAMPLE_FLOAT32NE,
|
||||
.channels = 1,
|
||||
.rate = sample_rate
|
||||
};
|
||||
// #region Setup devices
|
||||
pa_sample_spec mono_format = {
|
||||
.format = PA_SAMPLE_FLOAT32NE,
|
||||
.channels = 1,
|
||||
.rate = sample_rate
|
||||
};
|
||||
|
||||
pa_buffer_attr output_buffer_atr = {
|
||||
.maxlength = buffer_maxlength,
|
||||
.tlength = buffer_tlength_fragsize,
|
||||
.prebuf = buffer_prebuf
|
||||
};
|
||||
pa_buffer_attr output_buffer_atr = {
|
||||
.maxlength = buffer_maxlength,
|
||||
.tlength = buffer_tlength_fragsize,
|
||||
.prebuf = buffer_prebuf
|
||||
};
|
||||
|
||||
int opentime_pulse_error;
|
||||
int opentime_pulse_error;
|
||||
|
||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
||||
|
||||
output_device = pa_simple_new(
|
||||
NULL,
|
||||
"dcf95",
|
||||
PA_STREAM_PLAYBACK,
|
||||
audio_output_device,
|
||||
"DCF77 Output",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&output_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!output_device) {
|
||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
return 1;
|
||||
}
|
||||
// #endregion
|
||||
output_device = pa_simple_new(
|
||||
NULL,
|
||||
"dcf95",
|
||||
PA_STREAM_PLAYBACK,
|
||||
audio_output_device,
|
||||
"DCF77 Output",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&output_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!output_device) {
|
||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
return 1;
|
||||
}
|
||||
// #endregion
|
||||
|
||||
Oscillator osc;
|
||||
init_oscillator(&osc, freq, sample_rate);
|
||||
Oscillator osc;
|
||||
init_oscillator(&osc, freq, sample_rate);
|
||||
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
|
||||
int pulse_error;
|
||||
float output[BUFFER_SIZE];
|
||||
int pulse_error;
|
||||
float output[BUFFER_SIZE];
|
||||
|
||||
int current_second = -1;
|
||||
int ms_within_second = 0;
|
||||
int current_second = -1;
|
||||
int ms_within_second = 0;
|
||||
|
||||
int dsss_start_samples = (int)((DSSS_START_MS / 1000.0) * sample_rate);
|
||||
int dsss_duration_samples = (int)((DSSS_DURATION_MS / 1000.0) * sample_rate);
|
||||
int dsss_end_samples = dsss_start_samples + dsss_duration_samples;
|
||||
float phase_shift_rad = (PHASE_SHIFT * M_PI) / 180.0;
|
||||
int dsss_start_samples = (int)((DSSS_START_MS / 1000.0) * sample_rate);
|
||||
int dsss_duration_samples = (int)((DSSS_DURATION_MS / 1000.0) * sample_rate);
|
||||
int dsss_end_samples = dsss_start_samples + dsss_duration_samples;
|
||||
float phase_shift_rad = (PHASE_SHIFT * M_PI) / 180.0;
|
||||
|
||||
int current_chip_count = 0;
|
||||
int current_cycle_count = 0;
|
||||
int in_dsss_period = 0;
|
||||
int current_chip_count = 0;
|
||||
int current_cycle_count = 0;
|
||||
int in_dsss_period = 0;
|
||||
|
||||
int elapsed_samples = 0;
|
||||
int elapsed_samples = 0;
|
||||
|
||||
printf("DCF77 encoder ready.\n");
|
||||
printf("DCF77 encoder ready.\n");
|
||||
|
||||
while (to_run) {
|
||||
memset(output, 0, sizeof(output));
|
||||
while (to_run) {
|
||||
memset(output, 0, sizeof(output));
|
||||
|
||||
time_t now = time(NULL) + offset + 60;
|
||||
struct tm *t = gmtime(&now);
|
||||
int second = t->tm_sec;
|
||||
time_t now = time(NULL) + offset + 60;
|
||||
struct tm *t = gmtime(&now);
|
||||
int second = t->tm_sec;
|
||||
|
||||
if (second == 0 && current_second != 0) {
|
||||
calculate_dcf77_bits(now, (int *)dcf77_bits);
|
||||
#ifdef DEBUG
|
||||
print_dcf77_bits((int *)dcf77_bits);
|
||||
#endif
|
||||
if (second == 0 && current_second != 0) {
|
||||
calculate_dcf77_bits(now, (int *)dcf77_bits);
|
||||
#ifdef DEBUG
|
||||
print_dcf77_bits((int *)dcf77_bits);
|
||||
#endif
|
||||
|
||||
bit_position = 0;
|
||||
elapsed_samples = 0;
|
||||
transmitting = 1;
|
||||
bit_position = 0;
|
||||
elapsed_samples = 0;
|
||||
transmitting = 1;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Starting new DCF77 transmission for %02d:%02d:%02d UTC\n",
|
||||
t->tm_hour, t->tm_min, t->tm_sec);
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("Starting new DCF77 transmission for %02d:%02d:%02d UTC\n",
|
||||
t->tm_hour, t->tm_min, t->tm_sec);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (second != current_second) {
|
||||
current_second = second;
|
||||
if (second != current_second) {
|
||||
current_second = second;
|
||||
|
||||
reset_lfsr();
|
||||
current_chip_count = 0;
|
||||
current_cycle_count = 0;
|
||||
reset_lfsr();
|
||||
current_chip_count = 0;
|
||||
current_cycle_count = 0;
|
||||
|
||||
if (transmitting) {
|
||||
if (bit_position < 59) {
|
||||
#ifdef DEBUG
|
||||
printf("Bit %2d: %d\n", bit_position, dcf77_bits[bit_position]);
|
||||
#endif
|
||||
bit_position++;
|
||||
} else {
|
||||
bit_position = 0;
|
||||
#ifdef DEBUG
|
||||
printf("End of minute, restarting bit sequence.\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (transmitting) {
|
||||
if (bit_position < 59) {
|
||||
#ifdef DEBUG
|
||||
printf("Bit %2d: %d\n", bit_position, dcf77_bits[bit_position]);
|
||||
#endif
|
||||
bit_position++;
|
||||
} else {
|
||||
bit_position = 0;
|
||||
#ifdef DEBUG
|
||||
printf("End of minute, restarting bit sequence.\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
elapsed_samples = 0;
|
||||
}
|
||||
elapsed_samples = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
ms_within_second = (int)((elapsed_samples * 1000.0) / sample_rate);
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
ms_within_second = (int)((elapsed_samples * 1000.0) / sample_rate);
|
||||
|
||||
int current_bit = bit_position > 0 ? bit_position - 1 : 59;
|
||||
int current_bit = bit_position > 0 ? bit_position - 1 : 59;
|
||||
|
||||
in_dsss_period = (elapsed_samples >= dsss_start_samples &&
|
||||
elapsed_samples < dsss_end_samples);
|
||||
in_dsss_period = (elapsed_samples >= dsss_start_samples &&
|
||||
elapsed_samples < dsss_end_samples);
|
||||
|
||||
float phase_offset = 0.0;
|
||||
float phase_offset = 0.0;
|
||||
|
||||
if (in_dsss_period && transmitting && !no_phase) {
|
||||
if (current_cycle_count == 0) {
|
||||
if (current_chip_count < CHIPS_PER_BIT) {
|
||||
unsigned int chip = generate_chip();
|
||||
if (in_dsss_period && transmitting && !no_phase) {
|
||||
if (current_cycle_count == 0) {
|
||||
if (current_chip_count < CHIPS_PER_BIT) {
|
||||
unsigned int chip = generate_chip();
|
||||
|
||||
unsigned int modulated_chip = chip ^ dcf77_bits[current_bit];
|
||||
unsigned int modulated_chip = chip ^ dcf77_bits[current_bit];
|
||||
|
||||
if (modulated_chip == 0) {
|
||||
phase_offset = phase_shift_rad;
|
||||
} else {
|
||||
phase_offset = -phase_shift_rad;
|
||||
}
|
||||
if (modulated_chip == 0) {
|
||||
phase_offset = phase_shift_rad;
|
||||
} else {
|
||||
phase_offset = -phase_shift_rad;
|
||||
}
|
||||
|
||||
current_chip_count++;
|
||||
}
|
||||
}
|
||||
current_chip_count++;
|
||||
}
|
||||
}
|
||||
|
||||
current_cycle_count = (current_cycle_count + 1) % CHIP_CYCLES;
|
||||
}
|
||||
current_cycle_count = (current_cycle_count + 1) % CHIP_CYCLES;
|
||||
}
|
||||
|
||||
float t = osc.phase + phase_offset;
|
||||
float carrier = sinf(t);
|
||||
advance_oscillator(&osc);
|
||||
float t = osc.phase + phase_offset;
|
||||
float carrier = sinf(t);
|
||||
advance_oscillator(&osc);
|
||||
|
||||
if (transmitting) {
|
||||
if ((dcf77_bits[current_bit] == 0 && ms_within_second < PULSE_0_DURATION) ||
|
||||
(dcf77_bits[current_bit] == 1 && ms_within_second < PULSE_1_DURATION)) {
|
||||
output[i] = carrier * master_volume * REDUCED_AMPLITUDE;
|
||||
} else {
|
||||
output[i] = carrier * master_volume;
|
||||
}
|
||||
} else {
|
||||
output[i] = carrier * master_volume;
|
||||
}
|
||||
if (transmitting) {
|
||||
if ((dcf77_bits[current_bit] == 0 && ms_within_second < PULSE_0_DURATION) ||
|
||||
(dcf77_bits[current_bit] == 1 && ms_within_second < PULSE_1_DURATION)) {
|
||||
output[i] = carrier * master_volume * REDUCED_AMPLITUDE;
|
||||
} else {
|
||||
output[i] = carrier * master_volume;
|
||||
}
|
||||
} else {
|
||||
output[i] = carrier * master_volume;
|
||||
}
|
||||
|
||||
elapsed_samples++;
|
||||
}
|
||||
elapsed_samples++;
|
||||
}
|
||||
|
||||
if (pa_simple_write(output_device, output, sizeof(output), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pa_simple_write(output_device, output, sizeof(output), &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(output_device);
|
||||
return 0;
|
||||
printf("Cleaning up...\n");
|
||||
pa_simple_free(output_device);
|
||||
return 0;
|
||||
}
|
||||
+415
-418
@@ -35,490 +35,487 @@
|
||||
#define DEFAULT_MASTER_VOLUME 1.0f // Volume of everything combined, for calibration
|
||||
#define DEFAULT_AUDIO_VOLUME 1.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, should be same as MONO
|
||||
#define RDS_VOLUME 0.075f // RDS Volume, after dsb-sc
|
||||
#define RDS2_VOLUME 0.075f // RDS2 Volume
|
||||
#define SCA_VOLUME 0.1f // FM SCA signal, 10%
|
||||
#define MPX_VOLUME 1.0f // Passtrough
|
||||
#define MONO_VOLUME 0.45f
|
||||
#define PILOT_VOLUME 0.09f
|
||||
#define STEREO_VOLUME 0.45f
|
||||
#define RDS_VOLUME 0.075f
|
||||
#define RDS2_VOLUME 0.0675f
|
||||
#define SCA_VOLUME 0.1f
|
||||
#define MPX_VOLUME 1.0f
|
||||
#define MPX_CLIPPER_THRESHOLD 1.0f
|
||||
|
||||
static volatile sig_atomic_t to_run = 1;
|
||||
|
||||
void uninterleave(const float *input, float *left, float *right, size_t num_samples) {
|
||||
for (size_t i = 0; i < num_samples/2; i++) {
|
||||
left[i] = input[i * 2];
|
||||
right[i] = input[i * 2 + 1];
|
||||
}
|
||||
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.\n");
|
||||
to_run = 0;
|
||||
(void)signum;
|
||||
printf("\nReceived stop signal.\n");
|
||||
to_run = 0;
|
||||
}
|
||||
|
||||
void show_version() {
|
||||
printf("fm95 (an FM Processor by radio95) version 1.5\n");
|
||||
printf("fm95 (an FM Processor by radio95) version 1.5\n");
|
||||
}
|
||||
void show_help(char *name) {
|
||||
printf(
|
||||
"Usage: %s\n"
|
||||
" -s,--stereo Force Stereo [default: %d]\n"
|
||||
" -i,--input Override input device [default: %s]\n"
|
||||
" -o,--output Override output device [default: %s]\n"
|
||||
" -M,--mpx Override MPX input device [default: %s]\n"
|
||||
" -r,--rds Override RDS95 input device [default: %s]\n"
|
||||
" -C,--sca Override the SCA input device [default: %s]\n"
|
||||
" -f,--sca_freq Override the SCA frequency [default: %.1f]\n"
|
||||
" -F,--sca_dev Override the SCA deviation [default: %.2f]\n"
|
||||
" -L,--sca_clip Override the SCA clipper threshold [default: %.2f]\n"
|
||||
" -c,--clipper Override the clipper threshold [default: %.2f]\n"
|
||||
" -P,--polar Force Polar Stereo (does not take effect with -m%s)\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
|
||||
,INPUT_DEVICE
|
||||
,OUTPUT_DEVICE
|
||||
#ifdef MPX_DEVICE
|
||||
,MPX_DEVICE
|
||||
#else
|
||||
,"not set"
|
||||
#endif
|
||||
#ifdef RDS_DEVICE
|
||||
,RDS_DEVICE
|
||||
#else
|
||||
,"not set"
|
||||
#endif
|
||||
#ifdef SCA_DEVICE
|
||||
,SCA_DEVICE
|
||||
#else
|
||||
,"not set"
|
||||
#endif
|
||||
,DEFAULT_SCA_FREQUENCY
|
||||
,DEFAULT_SCA_DEVIATION
|
||||
,DEFAULT_SCA_CLIPPER_THRESHOLD
|
||||
,DEFAULT_CLIPPER_THRESHOLD
|
||||
,(DEFAULT_STEREO_POLAR == 1) ? ", default" : ""
|
||||
,DEFAULT_PREEMPHASIS_TAU/0.000001
|
||||
,DEFAULT_MASTER_VOLUME
|
||||
,DEFAULT_AUDIO_VOLUME
|
||||
);
|
||||
printf(
|
||||
"Usage: %s\n"
|
||||
" -s,--stereo Force Stereo [default: %d]\n"
|
||||
" -i,--input Override input device [default: %s]\n"
|
||||
" -o,--output Override output device [default: %s]\n"
|
||||
" -M,--mpx Override MPX input device [default: %s]\n"
|
||||
" -r,--rds Override RDS95 input device [default: %s]\n"
|
||||
" -C,--sca Override the SCA input device [default: %s]\n"
|
||||
" -f,--sca_freq Override the SCA frequency [default: %.1f]\n"
|
||||
" -F,--sca_dev Override the SCA deviation [default: %.2f]\n"
|
||||
" -L,--sca_clip Override the SCA clipper threshold [default: %.2f]\n"
|
||||
" -c,--clipper Override the clipper threshold [default: %.2f]\n"
|
||||
" -P,--polar Force Polar Stereo (does not take effect with -m%s)\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
|
||||
,INPUT_DEVICE
|
||||
,OUTPUT_DEVICE
|
||||
#ifdef MPX_DEVICE
|
||||
,MPX_DEVICE
|
||||
#else
|
||||
,"not set"
|
||||
#endif
|
||||
#ifdef RDS_DEVICE
|
||||
,RDS_DEVICE
|
||||
#else
|
||||
,"not set"
|
||||
#endif
|
||||
#ifdef SCA_DEVICE
|
||||
,SCA_DEVICE
|
||||
#else
|
||||
,"not set"
|
||||
#endif
|
||||
,DEFAULT_SCA_FREQUENCY
|
||||
,DEFAULT_SCA_DEVIATION
|
||||
,DEFAULT_SCA_CLIPPER_THRESHOLD
|
||||
,DEFAULT_CLIPPER_THRESHOLD
|
||||
,(DEFAULT_STEREO_POLAR == 1) ? ", default" : ""
|
||||
,DEFAULT_PREEMPHASIS_TAU/0.000001
|
||||
,DEFAULT_MASTER_VOLUME
|
||||
,DEFAULT_AUDIO_VOLUME
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
show_version();
|
||||
show_version();
|
||||
|
||||
pa_simple *mpx_device = NULL;
|
||||
pa_simple *rds_device = NULL;
|
||||
pa_simple *sca_device = NULL;
|
||||
pa_simple *mpx_device = NULL;
|
||||
pa_simple *rds_device = NULL;
|
||||
pa_simple *sca_device = NULL;
|
||||
|
||||
pa_simple *input_device;
|
||||
pa_simple *output_device;
|
||||
pa_simple *input_device;
|
||||
pa_simple *output_device;
|
||||
|
||||
float clipper_threshold = DEFAULT_CLIPPER_THRESHOLD;
|
||||
int stereo = DEFAULT_STEREO;
|
||||
int polar_stereo = DEFAULT_STEREO_POLAR;
|
||||
float clipper_threshold = DEFAULT_CLIPPER_THRESHOLD;
|
||||
int stereo = DEFAULT_STEREO;
|
||||
int polar_stereo = DEFAULT_STEREO_POLAR;
|
||||
|
||||
float sca_frequency = DEFAULT_SCA_FREQUENCY;
|
||||
float sca_deviation = DEFAULT_SCA_DEVIATION;
|
||||
float sca_clipper_threshold = DEFAULT_SCA_CLIPPER_THRESHOLD;
|
||||
float sca_frequency = DEFAULT_SCA_FREQUENCY;
|
||||
float sca_deviation = DEFAULT_SCA_DEVIATION;
|
||||
float sca_clipper_threshold = DEFAULT_SCA_CLIPPER_THRESHOLD;
|
||||
|
||||
char audio_input_device[64] = INPUT_DEVICE;
|
||||
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||
char audio_input_device[64] = INPUT_DEVICE;
|
||||
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||
#ifndef MPX_DEVICE
|
||||
char audio_mpx_device[64] = "\0";
|
||||
char audio_mpx_device[64] = "\0";
|
||||
#else
|
||||
char audio_mpx_device[64] = MPX_DEVICE;
|
||||
char audio_mpx_device[64] = MPX_DEVICE;
|
||||
#endif
|
||||
#ifndef RDS_DEVICE
|
||||
char audio_rds_device[64] = "\0";
|
||||
char audio_rds_device[64] = "\0";
|
||||
#else
|
||||
char audio_rds_device[64] = RDS_DEVICE;
|
||||
char audio_rds_device[64] = RDS_DEVICE;
|
||||
#endif
|
||||
#ifndef SCA_DEVICE
|
||||
char audio_sca_device[64] = "\0";
|
||||
char audio_sca_device[64] = "\0";
|
||||
#else
|
||||
char audio_sca_device[64] = SCA_DEVICE;
|
||||
char audio_sca_device[64] = SCA_DEVICE;
|
||||
#endif
|
||||
float preemphasis_tau = DEFAULT_PREEMPHASIS_TAU;
|
||||
float preemphasis_tau = DEFAULT_PREEMPHASIS_TAU;
|
||||
|
||||
int calibration_mode = 0;
|
||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
||||
float audio_volume = DEFAULT_AUDIO_VOLUME;
|
||||
int calibration_mode = 0;
|
||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
||||
float audio_volume = DEFAULT_AUDIO_VOLUME;
|
||||
|
||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
||||
|
||||
// #region Parse Arguments
|
||||
int opt;
|
||||
const char *short_opt = "s::i:o:M:r:C:f:F:L:c:P::R:VA:v:h";
|
||||
struct option long_opt[] =
|
||||
// #region Parse Arguments
|
||||
int opt;
|
||||
const char *short_opt = "s::i:o:M:r:C:f:F:L:c:P::R:VA:v:h";
|
||||
struct option long_opt[] =
|
||||
{
|
||||
{"stereo", optional_argument, NULL, 's'},
|
||||
{"input", required_argument, NULL, 'i'},
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"mpx", required_argument, NULL, 'M'},
|
||||
{"rds", required_argument, NULL, 'r'},
|
||||
{"sca", required_argument, NULL, 'C'},
|
||||
{"sca_freq", required_argument, NULL, 'f'},
|
||||
{"sca_dev", required_argument, NULL, 'F'},
|
||||
{"sca_clip", required_argument, NULL, 'L'},
|
||||
{"clipper", required_argument, NULL, 'c'},
|
||||
{"polar", optional_argument, NULL, 'P'},
|
||||
{"preemp", required_argument, NULL, 'R'},
|
||||
{"calibrate", no_argument, NULL, 'V'},
|
||||
{"master_vol", required_argument, NULL, 'A'},
|
||||
{"audio_vol", required_argument, NULL, 'v'},
|
||||
{"stereo", optional_argument, NULL, 's'},
|
||||
{"input", required_argument, NULL, 'i'},
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"mpx", required_argument, NULL, 'M'},
|
||||
{"rds", required_argument, NULL, 'r'},
|
||||
{"sca", required_argument, NULL, 'C'},
|
||||
{"sca_freq", required_argument, NULL, 'f'},
|
||||
{"sca_dev", required_argument, NULL, 'F'},
|
||||
{"sca_clip", required_argument, NULL, 'L'},
|
||||
{"clipper", required_argument, NULL, 'c'},
|
||||
{"polar", optional_argument, NULL, 'P'},
|
||||
{"preemp", required_argument, NULL, 'R'},
|
||||
{"calibrate", no_argument, NULL, 'V'},
|
||||
{"master_vol", required_argument, NULL, 'A'},
|
||||
{"audio_vol", required_argument, NULL, 'v'},
|
||||
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
{"help", no_argument, NULL, 'h'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||
switch(opt) {
|
||||
case 's': // Stereo
|
||||
if(optarg) {
|
||||
stereo = atoi(optarg);
|
||||
} else {
|
||||
stereo = 1;
|
||||
}
|
||||
break;
|
||||
case 'i': // Input Device
|
||||
memcpy(audio_input_device, optarg, 63);
|
||||
break;
|
||||
case 'o': // Output Device
|
||||
memcpy(audio_output_device, optarg, 63);
|
||||
break;;
|
||||
case 'M': //MPX in
|
||||
memcpy(audio_mpx_device, optarg, 63);
|
||||
break;
|
||||
case 'r': // RDS in
|
||||
memcpy(audio_rds_device, optarg, 63);
|
||||
break;
|
||||
case 'C': //SCA in
|
||||
memcpy(audio_sca_device, optarg, 63);
|
||||
break;
|
||||
case 'f': //SCA freq
|
||||
sca_frequency = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'F': //SCA deviation
|
||||
sca_deviation = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'L': //SCA clip
|
||||
sca_clipper_threshold = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'c': //Clipper
|
||||
clipper_threshold = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'P': //Polar
|
||||
if(optarg) {
|
||||
polar_stereo = atoi(optarg);
|
||||
} else {
|
||||
polar_stereo = 1;
|
||||
}
|
||||
break;
|
||||
case 'R': // Preemp
|
||||
preemphasis_tau = strtof(optarg, NULL)*0.000001;
|
||||
break;
|
||||
case 'V': // Calibration
|
||||
calibration_mode = 1;
|
||||
break;
|
||||
case 'A': // Master vol
|
||||
master_volume = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'v': // Audio Volume
|
||||
audio_volume = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'h':
|
||||
show_help(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||
switch(opt) {
|
||||
case 's': // Stereo
|
||||
if(optarg) {
|
||||
stereo = atoi(optarg);
|
||||
} else {
|
||||
stereo = 1;
|
||||
}
|
||||
break;
|
||||
case 'i': // Input Device
|
||||
memcpy(audio_input_device, optarg, 63);
|
||||
break;
|
||||
case 'o': // Output Device
|
||||
memcpy(audio_output_device, optarg, 63);
|
||||
break;;
|
||||
case 'M': //MPX in
|
||||
memcpy(audio_mpx_device, optarg, 63);
|
||||
break;
|
||||
case 'r': // RDS in
|
||||
memcpy(audio_rds_device, optarg, 63);
|
||||
break;
|
||||
case 'C': //SCA in
|
||||
memcpy(audio_sca_device, optarg, 63);
|
||||
break;
|
||||
case 'f': //SCA freq
|
||||
sca_frequency = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'F': //SCA deviation
|
||||
sca_deviation = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'L': //SCA clip
|
||||
sca_clipper_threshold = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'c': //Clipper
|
||||
clipper_threshold = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'P': //Polar
|
||||
if(optarg) {
|
||||
polar_stereo = atoi(optarg);
|
||||
} else {
|
||||
polar_stereo = 1;
|
||||
}
|
||||
break;
|
||||
case 'R': // Preemp
|
||||
preemphasis_tau = strtof(optarg, NULL)*0.000001;
|
||||
break;
|
||||
case 'V': // Calibration
|
||||
calibration_mode = 1;
|
||||
break;
|
||||
case 'A': // Master vol
|
||||
master_volume = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'v': // Audio Volume
|
||||
audio_volume = strtof(optarg, NULL);
|
||||
break;
|
||||
case 'h':
|
||||
show_help(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
int mpx_on = (strlen(audio_mpx_device) != 0);
|
||||
int rds_on = (strlen(audio_rds_device) != 0);
|
||||
int sca_on = (strlen(audio_sca_device) != 0);
|
||||
int mpx_on = (strlen(audio_mpx_device) != 0);
|
||||
int rds_on = (strlen(audio_rds_device) != 0);
|
||||
int sca_on = (strlen(audio_sca_device) != 0);
|
||||
|
||||
// #region Setup devices
|
||||
// #region Setup devices
|
||||
|
||||
// Define formats and buffer atributes
|
||||
pa_sample_spec stereo_format = {
|
||||
.format = PA_SAMPLE_FLOAT32NE,
|
||||
.channels = 2,
|
||||
.rate = sample_rate
|
||||
};
|
||||
pa_sample_spec mono_format = {
|
||||
.format = PA_SAMPLE_FLOAT32NE,
|
||||
.channels = 1,
|
||||
.rate = sample_rate
|
||||
};
|
||||
// Define formats and buffer atributes
|
||||
pa_sample_spec stereo_format = {
|
||||
.format = PA_SAMPLE_FLOAT32NE,
|
||||
.channels = 2,
|
||||
.rate = sample_rate
|
||||
};
|
||||
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
|
||||
};
|
||||
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
|
||||
};
|
||||
|
||||
int opentime_pulse_error;
|
||||
int opentime_pulse_error;
|
||||
|
||||
printf("Connecting to input device... (%s)\n", audio_input_device);
|
||||
printf("Connecting to input device... (%s)\n", audio_input_device);
|
||||
|
||||
input_device = pa_simple_new(
|
||||
NULL,
|
||||
"fm95",
|
||||
PA_STREAM_RECORD,
|
||||
audio_input_device,
|
||||
"Main Audio Input",
|
||||
&stereo_format,
|
||||
NULL,
|
||||
&input_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!input_device) {
|
||||
fprintf(stderr, "Error: cannot open input device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
return 1;
|
||||
}
|
||||
input_device = pa_simple_new(
|
||||
NULL,
|
||||
"fm95",
|
||||
PA_STREAM_RECORD,
|
||||
audio_input_device,
|
||||
"Main Audio Input",
|
||||
&stereo_format,
|
||||
NULL,
|
||||
&input_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!input_device) {
|
||||
fprintf(stderr, "Error: cannot open input device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(mpx_on) {
|
||||
printf("Connecting to MPX device... (%s)\n", audio_mpx_device);
|
||||
if(mpx_on) {
|
||||
printf("Connecting to MPX device... (%s)\n", audio_mpx_device);
|
||||
|
||||
mpx_device = pa_simple_new(
|
||||
NULL,
|
||||
"fm95",
|
||||
PA_STREAM_RECORD,
|
||||
audio_mpx_device,
|
||||
"MPX Input",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&input_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!mpx_device) {
|
||||
fprintf(stderr, "Error: cannot open MPX device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
pa_simple_free(input_device);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(rds_on) {
|
||||
printf("Connecting to RDS95 device... (%s)\n", audio_rds_device);
|
||||
mpx_device = pa_simple_new(
|
||||
NULL,
|
||||
"fm95",
|
||||
PA_STREAM_RECORD,
|
||||
audio_mpx_device,
|
||||
"MPX Input",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&input_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!mpx_device) {
|
||||
fprintf(stderr, "Error: cannot open MPX device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
pa_simple_free(input_device);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if(rds_on) {
|
||||
printf("Connecting to RDS95 device... (%s)\n", audio_rds_device);
|
||||
|
||||
rds_device = pa_simple_new(
|
||||
NULL,
|
||||
"fm95",
|
||||
PA_STREAM_RECORD,
|
||||
audio_rds_device,
|
||||
"RDS Input",
|
||||
&stereo_format,
|
||||
NULL,
|
||||
&input_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!rds_device) {
|
||||
fprintf(stderr, "Error: cannot open RDS device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
pa_simple_free(input_device);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
rds_device = pa_simple_new(
|
||||
NULL,
|
||||
"fm95",
|
||||
PA_STREAM_RECORD,
|
||||
audio_rds_device,
|
||||
"RDS Input",
|
||||
&stereo_format,
|
||||
NULL,
|
||||
&input_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!rds_device) {
|
||||
fprintf(stderr, "Error: cannot open RDS device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
pa_simple_free(input_device);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(sca_on) {
|
||||
printf("Connecting to SCA device... (%s)\n", audio_sca_device);
|
||||
if(sca_on) {
|
||||
printf("Connecting to SCA device... (%s)\n", audio_sca_device);
|
||||
|
||||
sca_device = pa_simple_new(
|
||||
NULL,
|
||||
"fm95",
|
||||
PA_STREAM_RECORD,
|
||||
audio_sca_device,
|
||||
"SCA Input",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&input_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!sca_device) {
|
||||
fprintf(stderr, "Error: cannot open SCA device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
pa_simple_free(input_device);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
if(rds_on) pa_simple_free(rds_device);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
sca_device = pa_simple_new(
|
||||
NULL,
|
||||
"fm95",
|
||||
PA_STREAM_RECORD,
|
||||
audio_sca_device,
|
||||
"SCA Input",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&input_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!sca_device) {
|
||||
fprintf(stderr, "Error: cannot open SCA device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
pa_simple_free(input_device);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
if(rds_on) pa_simple_free(rds_device);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
||||
|
||||
output_device = pa_simple_new(
|
||||
NULL,
|
||||
"StereoEncoder",
|
||||
PA_STREAM_PLAYBACK,
|
||||
audio_output_device,
|
||||
"MPX Output",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&output_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!output_device) {
|
||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
pa_simple_free(input_device);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
if(rds_on) pa_simple_free(rds_device);
|
||||
if(sca_on) pa_simple_free(sca_device);
|
||||
return 1;
|
||||
}
|
||||
// #endregion
|
||||
output_device = pa_simple_new(
|
||||
NULL,
|
||||
"StereoEncoder",
|
||||
PA_STREAM_PLAYBACK,
|
||||
audio_output_device,
|
||||
"MPX Output",
|
||||
&mono_format,
|
||||
NULL,
|
||||
&output_buffer_atr,
|
||||
&opentime_pulse_error
|
||||
);
|
||||
if (!output_device) {
|
||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(opentime_pulse_error));
|
||||
pa_simple_free(input_device);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
if(rds_on) pa_simple_free(rds_device);
|
||||
if(sca_on) pa_simple_free(sca_device);
|
||||
return 1;
|
||||
}
|
||||
// #endregion
|
||||
|
||||
if(calibration_mode) {
|
||||
Oscillator osc;
|
||||
init_oscillator(&osc, 400, sample_rate);
|
||||
if(calibration_mode) {
|
||||
Oscillator osc;
|
||||
init_oscillator(&osc, 400, sample_rate);
|
||||
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
int pulse_error;
|
||||
float output[BUFFER_SIZE];
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
int pulse_error;
|
||||
float output[BUFFER_SIZE];
|
||||
|
||||
while(to_run) {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
output[i] = get_oscillator_sin_sample(&osc)*master_volume;
|
||||
}
|
||||
if (pa_simple_write(output_device, output, sizeof(output), &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);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
if(rds_on) pa_simple_free(rds_device);
|
||||
if(sca_on) pa_simple_free(sca_device);
|
||||
pa_simple_free(output_device);
|
||||
return 0;
|
||||
}
|
||||
while(to_run) {
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
output[i] = get_oscillator_sin_sample(&osc)*master_volume;
|
||||
}
|
||||
if (pa_simple_write(output_device, output, sizeof(output), &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);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
if(rds_on) pa_simple_free(rds_device);
|
||||
if(sca_on) pa_simple_free(sca_device);
|
||||
pa_simple_free(output_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// #region Setup Filters/Modulaltors/Oscillators
|
||||
Oscillator osc;
|
||||
init_oscillator(&osc, polar_stereo ? 31250.0 : 19000, sample_rate);
|
||||
// #region Setup Filters/Modulaltors/Oscillators
|
||||
Oscillator osc;
|
||||
init_oscillator(&osc, polar_stereo ? 31250.0 : 19000, sample_rate);
|
||||
|
||||
Oscillator rds2_osc;
|
||||
init_oscillator(&rds2_osc, 66500, sample_rate);
|
||||
FMModulator sca_mod;
|
||||
init_fm_modulator(&sca_mod, sca_frequency, sca_deviation, sample_rate);
|
||||
|
||||
FMModulator sca_mod;
|
||||
init_fm_modulator(&sca_mod, sca_frequency, sca_deviation, sample_rate);
|
||||
ResistorCapacitor preemp_l, preemp_r;
|
||||
init_preemphasis(&preemp_l, preemphasis_tau, sample_rate);
|
||||
init_preemphasis(&preemp_r, preemphasis_tau, sample_rate);
|
||||
|
||||
ResistorCapacitor preemp_l, preemp_r;
|
||||
init_preemphasis(&preemp_l, preemphasis_tau, sample_rate);
|
||||
init_preemphasis(&preemp_r, preemphasis_tau, sample_rate);
|
||||
PLL rds2_pll;
|
||||
init_pll(&rds2_pll, 7, 2, 19000, 100, 1, sample_rate);
|
||||
// #endregion
|
||||
|
||||
PLL rds2_pll;
|
||||
init_pll(&rds2_pll, 66500, 19000, 100, 1, sample_rate);
|
||||
// #endregion
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
|
||||
signal(SIGINT, stop);
|
||||
signal(SIGTERM, stop);
|
||||
int pulse_error;
|
||||
|
||||
int pulse_error;
|
||||
float audio_stereo_input[BUFFER_SIZE*2];
|
||||
|
||||
float audio_stereo_input[BUFFER_SIZE*2];
|
||||
float rds1_rds2_in[BUFFER_SIZE*2] = {0};
|
||||
float rds1_in[BUFFER_SIZE] = {0};
|
||||
float rds2_in[BUFFER_SIZE] = {0};
|
||||
|
||||
float rds1_rds2_in[BUFFER_SIZE*2] = {0};
|
||||
float rds1_in[BUFFER_SIZE] = {0};
|
||||
float rds2_in[BUFFER_SIZE] = {0};
|
||||
float mpx_in[BUFFER_SIZE] = {0};
|
||||
float sca_in[BUFFER_SIZE] = {0};
|
||||
float left[BUFFER_SIZE], right[BUFFER_SIZE];
|
||||
float output[BUFFER_SIZE];
|
||||
while (to_run) {
|
||||
if (pa_simple_read(input_device, audio_stereo_input, sizeof(audio_stereo_input), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
uninterleave(audio_stereo_input, left, right, BUFFER_SIZE*2);
|
||||
if(mpx_on) {
|
||||
if (pa_simple_read(mpx_device, mpx_in, sizeof(mpx_in), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error reading from MPX device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(rds_on) {
|
||||
if (pa_simple_read(rds_device, rds1_rds2_in, sizeof(rds1_rds2_in), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error reading from RDS95 device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
uninterleave(rds1_rds2_in, rds1_in, rds2_in, BUFFER_SIZE*2);
|
||||
}
|
||||
if(sca_on) {
|
||||
if (pa_simple_read(sca_device, sca_in, sizeof(sca_in), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error reading from SCA device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float mpx_in[BUFFER_SIZE] = {0};
|
||||
float sca_in[BUFFER_SIZE] = {0};
|
||||
float left[BUFFER_SIZE], right[BUFFER_SIZE];
|
||||
float output[BUFFER_SIZE];
|
||||
while (to_run) {
|
||||
if (pa_simple_read(input_device, audio_stereo_input, sizeof(audio_stereo_input), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
uninterleave(audio_stereo_input, left, right, BUFFER_SIZE*2);
|
||||
if(mpx_on) {
|
||||
if (pa_simple_read(mpx_device, mpx_in, sizeof(mpx_in), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error reading from MPX device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(rds_on) {
|
||||
if (pa_simple_read(rds_device, rds1_rds2_in, sizeof(rds1_rds2_in), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error reading from RDS95 device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
uninterleave(rds1_rds2_in, rds1_in, rds2_in, BUFFER_SIZE*2);
|
||||
}
|
||||
if(sca_on) {
|
||||
if (pa_simple_read(sca_device, sca_in, sizeof(sca_in), &pulse_error) < 0) {
|
||||
fprintf(stderr, "Error reading from SCA device: %s\n", pa_strerror(pulse_error));
|
||||
to_run = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
float l_in = left[i];
|
||||
float r_in = right[i];
|
||||
float current_mpx_in = mpx_in[i];
|
||||
float current_rds_in = rds1_in[i];
|
||||
float current_rds2_in = rds2_in[i];
|
||||
float current_sca_in = sca_in[i];
|
||||
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
float l_in = left[i];
|
||||
float r_in = right[i];
|
||||
float current_mpx_in = mpx_in[i];
|
||||
float current_rds_in = rds1_in[i];
|
||||
float current_rds2_in = rds2_in[i];
|
||||
float current_sca_in = sca_in[i];
|
||||
float ready_l = apply_preemphasis(&preemp_l, l_in)*2;
|
||||
float ready_r = apply_preemphasis(&preemp_r, r_in)*2;
|
||||
ready_l = hard_clip(ready_l*audio_volume, clipper_threshold);
|
||||
ready_r = hard_clip(ready_r*audio_volume, clipper_threshold);
|
||||
|
||||
float ready_l = apply_preemphasis(&preemp_l, l_in)*2;
|
||||
float ready_r = apply_preemphasis(&preemp_r, r_in)*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;
|
||||
output[i] = mono*MONO_VOLUME;
|
||||
if(stereo) {
|
||||
float stereo = (ready_l - ready_r) / 2.0f;
|
||||
float stereo_carrier = get_oscillator_sin_multiplier_ni(&osc, polar_stereo ? 1 : 2);
|
||||
|
||||
float mono = (ready_l + ready_r) / 2.0f;
|
||||
output[i] = mono*MONO_VOLUME;
|
||||
if(stereo) {
|
||||
float stereo = (ready_l - ready_r) / 2.0f;
|
||||
float stereo_carrier = get_oscillator_sin_multiplier_ni(&osc, polar_stereo ? 1 : 2);
|
||||
if(polar_stereo) {
|
||||
output[i] += ((stereo+0.2)*stereo_carrier)*STEREO_VOLUME;
|
||||
} else {
|
||||
float pilot = get_oscillator_sin_multiplier_ni(&osc, 1);
|
||||
output[i] += pilot*PILOT_VOLUME +
|
||||
(stereo*stereo_carrier)*STEREO_VOLUME;
|
||||
}
|
||||
}
|
||||
if(rds_on && polar_stereo == 0) {
|
||||
float rds_carrier = get_oscillator_sin_multiplier_ni(&osc, 3);
|
||||
output[i] += (current_rds_in*rds_carrier)*RDS_VOLUME;
|
||||
if(!sca_on) {
|
||||
float rds2_carrier_66 = apply_pll(&rds2_pll, get_oscillator_sin_multiplier_ni(&osc, 1));
|
||||
output[i] += (current_rds2_in*rds2_carrier_66)*RDS2_VOLUME;
|
||||
}
|
||||
}
|
||||
if(rds_on || stereo) advance_oscillator(&osc);
|
||||
if(mpx_on) output[i] += hard_clip(current_mpx_in, MPX_CLIPPER_THRESHOLD)*MPX_VOLUME;
|
||||
if(sca_on) output[i] += modulate_fm(&sca_mod, hard_clip(current_sca_in, sca_clipper_threshold))*SCA_VOLUME;
|
||||
output[i] *= master_volume;
|
||||
}
|
||||
|
||||
if(polar_stereo) {
|
||||
output[i] += ((stereo+0.2)*stereo_carrier)*STEREO_VOLUME;
|
||||
} else {
|
||||
float pilot = get_oscillator_sin_multiplier_ni(&osc, 1);
|
||||
output[i] += pilot*PILOT_VOLUME +
|
||||
(stereo*stereo_carrier)*STEREO_VOLUME;
|
||||
}
|
||||
}
|
||||
if(rds_on && polar_stereo == 0) {
|
||||
float rds_carrier = get_oscillator_sin_multiplier_ni(&osc, 3);
|
||||
output[i] += (current_rds_in*rds_carrier)*RDS_VOLUME;
|
||||
if(!sca_on) {
|
||||
float rds2_carrier_66 = apply_pll(&rds2_pll, get_oscillator_sin_multiplier_ni(&osc, 1), get_oscillator_cos_sample(&rds2_osc));
|
||||
output[i] += (current_rds2_in*rds2_carrier_66)*RDS2_VOLUME;
|
||||
}
|
||||
}
|
||||
if(rds_on || stereo) advance_oscillator(&osc);
|
||||
if(mpx_on) output[i] += hard_clip(current_mpx_in, MPX_CLIPPER_THRESHOLD)*MPX_VOLUME;
|
||||
if(sca_on) output[i] += modulate_fm(&sca_mod, hard_clip(current_sca_in, sca_clipper_threshold))*SCA_VOLUME;
|
||||
output[i] *= master_volume;
|
||||
}
|
||||
|
||||
if (pa_simple_write(output_device, output, sizeof(output), &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);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
if(rds_on) pa_simple_free(rds_device);
|
||||
if(sca_on) pa_simple_free(sca_device);
|
||||
pa_simple_free(output_device);
|
||||
return 0;
|
||||
if (pa_simple_write(output_device, output, sizeof(output), &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);
|
||||
if(mpx_on) pa_simple_free(mpx_device);
|
||||
if(rds_on) pa_simple_free(rds_device);
|
||||
if(sca_on) pa_simple_free(sca_device);
|
||||
pa_simple_free(output_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user