diff --git a/src/chimer95.c b/src/chimer95.c index eff50e8..73c11c4 100644 --- a/src/chimer95.c +++ b/src/chimer95.c @@ -3,6 +3,7 @@ #include #include #include +#include #define buffer_maxlength 1024 #define buffer_tlength_fragsize 1024 @@ -81,7 +82,7 @@ void generate_signal(float *output, Oscillator *osc, float volume, int *elapsed_ } } -int check_time_for_sequence(int test_mode, int offset) { +int check_time_for_sequence(int test_mode, int16_t offset) { static time_t last_check = 0; static int last_minute = -1; @@ -93,9 +94,7 @@ int check_time_for_sequence(int test_mode, int offset) { 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; last_sequence_time = now; if (minute == 29 && second == (56 + offset)) return SEQ_29_56; @@ -108,17 +107,102 @@ int check_time_for_sequence(int test_mode, int offset) { return SEQ_NONE; } -int main(int argc, char **argv) { - printf("chimer95 (GTS time signal encoder by radio95) version 1.1\n"); - +typedef struct +{ + float master_volume; + float freq; + uint32_t sample_rate; + int16_t offset; + bool test_mode; +} Chimer95_Config; +typedef struct +{ PulseOutputDevice output_device; +} Chimer95_Runtime; + +int run_chimer95(Chimer95_Config config, Chimer95_Runtime* runtime) { + int pulse_error; + + Oscillator osc; + init_oscillator(&osc, config.freq, config.sample_rate); + + float output[BUFFER_SIZE]; + + int pip_samples = (int)((PIP_DURATION / 1000.0) * config.sample_rate); + int pause_samples = (int)((PIP_PAUSE / 1000.0) * config.sample_rate); + int beep_samples = (int)((BEEP_DURATION / 1000.0) * config.sample_rate); + + 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+config.offset, 55+config.offset); + if (config.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; + + while (to_run) { + if (!playing_sequence) { + int new_sequence = check_time_for_sequence(config.test_mode, config.offset); + + 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; + + memset(output, 0, sizeof(output)); + } else { + static int idle_counter = 0; + if (idle_counter++ % 10 == 0) { + memset(output, 0, sizeof(output)); + if((pulse_error = write_PulseOutputDevice(&runtime->output_device, output, sizeof(output)))) { + fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error)); + to_run = 0; + break; + } + } + + struct timespec ts = {0, 5000000}; // 5ms sleep + nanosleep(&ts, NULL); + continue; + } + } + + int num_pips = (sequence_type == SEQ_29_56) ? 4 : 5; + generate_signal(output, &osc, config.master_volume, + &elapsed_samples, total_sequence_samples, + pip_samples, pause_samples, beep_samples, num_pips); + + if (!playing_sequence && !sequence_completed) sequence_completed = 1; + + if((pulse_error = write_PulseOutputDevice(&runtime->output_device, output, sizeof(output)))) { + fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error)); + to_run = 0; + break; + } + } + + return 0; +} + +int main(int argc, char **argv) { + printf("chimer95 (GTS time signal encoder by radio95) version 1.2\n"); 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; + + Chimer95_Config config = { + .master_volume = DEFAULT_MASTER_VOLUME, + .freq = DEFAULT_FREQ, + .sample_rate = DEFAULT_SAMPLE_RATE, + .offset = DEFAULT_OFFSET, + .test_mode = 0 + }; // Parse command line arguments int opt; @@ -141,19 +225,19 @@ int main(int argc, char **argv) { audio_output_device[sizeof(audio_output_device) - 1] = '\0'; break; case 'F': - freq = strtof(optarg, NULL); + config.freq = strtof(optarg, NULL); break; case 's': - sample_rate = strtol(optarg, NULL, 10); + config.sample_rate = strtol(optarg, NULL, 10); break; case 'v': - master_volume = strtof(optarg, NULL); + config.master_volume = strtof(optarg, NULL); break; case 't': - offset = strtol(optarg, NULL, 10); + config.offset = strtoul(optarg, NULL, 10); break; case 'T': - test_mode = 1; + config.test_mode = 1; break; case 'h': show_help(argv[0]); @@ -163,11 +247,11 @@ int main(int argc, char **argv) { printf("Configuration:\n"); printf("\tOutput device: %s\n", audio_output_device); - printf("\tFrequency: %.1f Hz\n", freq); - printf("\tSample rate: %d Hz\n", sample_rate); - printf("\tVolume: %.2f\n", master_volume); - printf("\tTime offset: %d seconds\n", offset); - printf("\tTest mode: %s\n", test_mode ? "Enabled" : "Disabled"); + printf("\tFrequency: %.1f Hz\n", config.freq); + printf("\tSample rate: %d Hz\n", config.sample_rate); + printf("\tVolume: %.2f\n", config.master_volume); + printf("\tTime offset: %d seconds\n", config.offset); + printf("\tTest mode: %s\n", config.test_mode ? "Enabled" : "Disabled"); // Setup PulseAudio pa_buffer_attr output_buffer_atr = { @@ -176,81 +260,22 @@ int main(int argc, char **argv) { .prebuf = buffer_prebuf }; - int pulse_error; + Chimer95_Runtime runtime; + memset(&runtime, 0, sizeof(runtime)); printf("Connecting to output device... (%s)\n", audio_output_device); - pulse_error = init_PulseOutputDevice(&output_device, sample_rate, 1, "chimer95", "Main Audio Output", audio_output_device, &output_buffer_atr, PA_SAMPLE_FLOAT32NE); + int pulse_error = init_PulseOutputDevice(&runtime.output_device, config.sample_rate, 1, "chimer95", "Main Audio Output", audio_output_device, &output_buffer_atr, PA_SAMPLE_FLOAT32NE); if (pulse_error) { fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(pulse_error)); return 1; } - Oscillator osc; - init_oscillator(&osc, freq, sample_rate); - signal(SIGINT, stop); signal(SIGTERM, stop); - 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 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"); - - 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); - - 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; - - memset(output, 0, sizeof(output)); - } else { - static int idle_counter = 0; - if (idle_counter++ % 10 == 0) { - memset(output, 0, sizeof(output)); - write_PulseOutputDevice(&output_device, output, sizeof(output)); - } - - struct timespec ts = {0, 5000000}; // 5ms sleep - nanosleep(&ts, NULL); - continue; - } - } - - int num_pips = (sequence_type == SEQ_29_56) ? 4 : 5; - generate_signal(output, &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((pulse_error = write_PulseOutputDevice(&output_device, output, sizeof(output)))) { - fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error)); - to_run = 0; - break; - } - } - + int ret = run_chimer95(config, &runtime); printf("Cleaning up...\n"); - free_PulseOutputDevice(&output_device); - return 0; + free_PulseOutputDevice(&runtime.output_device); + return ret; } \ No newline at end of file diff --git a/src/fm95.c b/src/fm95.c index 0d4ddc7..5ef79f6 100644 --- a/src/fm95.c +++ b/src/fm95.c @@ -122,8 +122,6 @@ int run_fm95(FM95_Config config, FM95_Runtime* runtime) { Oscillator osc; init_oscillator(&osc, (config.calibration == 2) ? 60 : 400, config.sample_rate); - signal(SIGINT, stop); - signal(SIGTERM, stop); int pulse_error; float output[BUFFER_SIZE]; @@ -166,9 +164,6 @@ int run_fm95(FM95_Config config, FM95_Runtime* runtime) { // fs target min max attack release initAGC(&agc, config.sample_rate, 0.65f, 0.0f, 1.75f, 0.03f, 0.225f); - signal(SIGINT, stop); - signal(SIGTERM, stop); - int pulse_error; float audio_stereo_input[BUFFER_SIZE*2]; // Stereo @@ -181,23 +176,20 @@ int run_fm95(FM95_Config config, FM95_Runtime* runtime) { while (to_run) { if((pulse_error = read_PulseInputDevice(&runtime->input_device, audio_stereo_input, sizeof(audio_stereo_input)))) { // get output from the function and assign it into pulse_error, this comment to avoid confusion - if(pulse_error == -1) fprintf(stderr, "Main PulseInputDevice reported as uninitialized."); - else fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error)); + fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error)); to_run = 0; break; } if(mpx_on) { if((pulse_error = read_PulseInputDevice(&runtime->mpx_device, mpx_in, sizeof(mpx_in)))) { - if(pulse_error == -1) fprintf(stderr, "MPX PulseInputDevice reported as uninitialized."); - else fprintf(stderr, "Error reading from MPX device: %s\n", pa_strerror(pulse_error)); + fprintf(stderr, "Error reading from MPX device: %s\n", pa_strerror(pulse_error)); fprintf(stderr, "Disabling MPX.\n"); mpx_on = 0; } } if(rds_on) { if((pulse_error = read_PulseInputDevice(&runtime->rds_device, rds_in, sizeof(float) * BUFFER_SIZE * config.rds_streams))) { - if(pulse_error == -1) fprintf(stderr, "RDS95 PulseInputDevice reported as uninitialized."); - else fprintf(stderr, "Error reading from RDS95 device: %s\n", pa_strerror(pulse_error)); + fprintf(stderr, "Error reading from RDS95 device: %s\n", pa_strerror(pulse_error)); fprintf(stderr, "Disabling RDS.\n"); rds_on = 0; } @@ -249,8 +241,7 @@ int run_fm95(FM95_Config config, FM95_Runtime* runtime) { } if((pulse_error = write_PulseOutputDevice(&runtime->output_device, output, sizeof(output)))) { - if(pulse_error == -1) fprintf(stderr, "Main PulseOutputDevice reported as uninitialized."); - else fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error)); + fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error)); to_run = 0; break; } @@ -429,6 +420,9 @@ int main(int argc, char **argv) { return 1; } + signal(SIGINT, stop); + signal(SIGTERM, stop); + int ret = run_fm95(config, &runtime); printf("Cleaning up...\n"); free_PulseInputDevice(&runtime.input_device);