mirror of
https://github.com/radio95-rnt/fm95.git
synced 2026-02-26 19:23:51 +01:00
restructure chimer95
This commit is contained in:
199
src/chimer95.c
199
src/chimer95.c
@@ -3,6 +3,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define buffer_maxlength 1024
|
#define buffer_maxlength 1024
|
||||||
#define buffer_tlength_fragsize 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 time_t last_check = 0;
|
||||||
static int last_minute = -1;
|
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 minute = utc_time->tm_min;
|
||||||
int second = utc_time->tm_sec;
|
int second = utc_time->tm_sec;
|
||||||
|
|
||||||
if (difftime(now, last_sequence_time) < 1.0) {
|
if (difftime(now, last_sequence_time) < 1.0) return SEQ_NONE;
|
||||||
return SEQ_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
last_sequence_time = now;
|
last_sequence_time = now;
|
||||||
if (minute == 29 && second == (56 + offset)) return SEQ_29_56;
|
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;
|
return SEQ_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
typedef struct
|
||||||
printf("chimer95 (GTS time signal encoder by radio95) version 1.1\n");
|
{
|
||||||
|
float master_volume;
|
||||||
|
float freq;
|
||||||
|
uint32_t sample_rate;
|
||||||
|
int16_t offset;
|
||||||
|
bool test_mode;
|
||||||
|
} Chimer95_Config;
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
PulseOutputDevice output_device;
|
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;
|
char audio_output_device[64] = OUTPUT_DEVICE;
|
||||||
float master_volume = DEFAULT_MASTER_VOLUME;
|
|
||||||
float freq = DEFAULT_FREQ;
|
Chimer95_Config config = {
|
||||||
int sample_rate = DEFAULT_SAMPLE_RATE;
|
.master_volume = DEFAULT_MASTER_VOLUME,
|
||||||
int offset = DEFAULT_OFFSET;
|
.freq = DEFAULT_FREQ,
|
||||||
int test_mode = 0;
|
.sample_rate = DEFAULT_SAMPLE_RATE,
|
||||||
|
.offset = DEFAULT_OFFSET,
|
||||||
|
.test_mode = 0
|
||||||
|
};
|
||||||
|
|
||||||
// Parse command line arguments
|
// Parse command line arguments
|
||||||
int opt;
|
int opt;
|
||||||
@@ -141,19 +225,19 @@ int main(int argc, char **argv) {
|
|||||||
audio_output_device[sizeof(audio_output_device) - 1] = '\0';
|
audio_output_device[sizeof(audio_output_device) - 1] = '\0';
|
||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
freq = strtof(optarg, NULL);
|
config.freq = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
sample_rate = strtol(optarg, NULL, 10);
|
config.sample_rate = strtol(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
master_volume = strtof(optarg, NULL);
|
config.master_volume = strtof(optarg, NULL);
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
offset = strtol(optarg, NULL, 10);
|
config.offset = strtoul(optarg, NULL, 10);
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
test_mode = 1;
|
config.test_mode = 1;
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
show_help(argv[0]);
|
show_help(argv[0]);
|
||||||
@@ -163,11 +247,11 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
printf("Configuration:\n");
|
printf("Configuration:\n");
|
||||||
printf("\tOutput device: %s\n", audio_output_device);
|
printf("\tOutput device: %s\n", audio_output_device);
|
||||||
printf("\tFrequency: %.1f Hz\n", freq);
|
printf("\tFrequency: %.1f Hz\n", config.freq);
|
||||||
printf("\tSample rate: %d Hz\n", sample_rate);
|
printf("\tSample rate: %d Hz\n", config.sample_rate);
|
||||||
printf("\tVolume: %.2f\n", master_volume);
|
printf("\tVolume: %.2f\n", config.master_volume);
|
||||||
printf("\tTime offset: %d seconds\n", offset);
|
printf("\tTime offset: %d seconds\n", config.offset);
|
||||||
printf("\tTest mode: %s\n", test_mode ? "Enabled" : "Disabled");
|
printf("\tTest mode: %s\n", config.test_mode ? "Enabled" : "Disabled");
|
||||||
|
|
||||||
// Setup PulseAudio
|
// Setup PulseAudio
|
||||||
pa_buffer_attr output_buffer_atr = {
|
pa_buffer_attr output_buffer_atr = {
|
||||||
@@ -176,81 +260,22 @@ int main(int argc, char **argv) {
|
|||||||
.prebuf = buffer_prebuf
|
.prebuf = buffer_prebuf
|
||||||
};
|
};
|
||||||
|
|
||||||
int pulse_error;
|
Chimer95_Runtime runtime;
|
||||||
|
memset(&runtime, 0, sizeof(runtime));
|
||||||
|
|
||||||
printf("Connecting to output device... (%s)\n", audio_output_device);
|
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) {
|
if (pulse_error) {
|
||||||
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(pulse_error));
|
fprintf(stderr, "Error: cannot open output device: %s\n", pa_strerror(pulse_error));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Oscillator osc;
|
|
||||||
init_oscillator(&osc, freq, sample_rate);
|
|
||||||
|
|
||||||
signal(SIGINT, stop);
|
signal(SIGINT, stop);
|
||||||
signal(SIGTERM, stop);
|
signal(SIGTERM, stop);
|
||||||
|
|
||||||
float output[BUFFER_SIZE];
|
int ret = run_chimer95(config, &runtime);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Cleaning up...\n");
|
printf("Cleaning up...\n");
|
||||||
free_PulseOutputDevice(&output_device);
|
free_PulseOutputDevice(&runtime.output_device);
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
20
src/fm95.c
20
src/fm95.c
@@ -122,8 +122,6 @@ int run_fm95(FM95_Config config, FM95_Runtime* runtime) {
|
|||||||
Oscillator osc;
|
Oscillator osc;
|
||||||
init_oscillator(&osc, (config.calibration == 2) ? 60 : 400, config.sample_rate);
|
init_oscillator(&osc, (config.calibration == 2) ? 60 : 400, config.sample_rate);
|
||||||
|
|
||||||
signal(SIGINT, stop);
|
|
||||||
signal(SIGTERM, stop);
|
|
||||||
int pulse_error;
|
int pulse_error;
|
||||||
float output[BUFFER_SIZE];
|
float output[BUFFER_SIZE];
|
||||||
|
|
||||||
@@ -166,9 +164,6 @@ int run_fm95(FM95_Config config, FM95_Runtime* runtime) {
|
|||||||
// fs target min max attack release
|
// fs target min max attack release
|
||||||
initAGC(&agc, config.sample_rate, 0.65f, 0.0f, 1.75f, 0.03f, 0.225f);
|
initAGC(&agc, config.sample_rate, 0.65f, 0.0f, 1.75f, 0.03f, 0.225f);
|
||||||
|
|
||||||
signal(SIGINT, stop);
|
|
||||||
signal(SIGTERM, stop);
|
|
||||||
|
|
||||||
int pulse_error;
|
int pulse_error;
|
||||||
|
|
||||||
float audio_stereo_input[BUFFER_SIZE*2]; // Stereo
|
float audio_stereo_input[BUFFER_SIZE*2]; // Stereo
|
||||||
@@ -181,23 +176,20 @@ int run_fm95(FM95_Config config, FM95_Runtime* runtime) {
|
|||||||
|
|
||||||
while (to_run) {
|
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 = 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.");
|
fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
|
||||||
else fprintf(stderr, "Error reading from input device: %s\n", pa_strerror(pulse_error));
|
|
||||||
to_run = 0;
|
to_run = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(mpx_on) {
|
if(mpx_on) {
|
||||||
if((pulse_error = read_PulseInputDevice(&runtime->mpx_device, mpx_in, sizeof(mpx_in)))) {
|
if((pulse_error = read_PulseInputDevice(&runtime->mpx_device, mpx_in, sizeof(mpx_in)))) {
|
||||||
if(pulse_error == -1) fprintf(stderr, "MPX PulseInputDevice reported as uninitialized.");
|
fprintf(stderr, "Error reading from MPX device: %s\n", pa_strerror(pulse_error));
|
||||||
else fprintf(stderr, "Error reading from MPX device: %s\n", pa_strerror(pulse_error));
|
|
||||||
fprintf(stderr, "Disabling MPX.\n");
|
fprintf(stderr, "Disabling MPX.\n");
|
||||||
mpx_on = 0;
|
mpx_on = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(rds_on) {
|
if(rds_on) {
|
||||||
if((pulse_error = read_PulseInputDevice(&runtime->rds_device, rds_in, sizeof(float) * BUFFER_SIZE * config.rds_streams))) {
|
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.");
|
fprintf(stderr, "Error reading from RDS95 device: %s\n", pa_strerror(pulse_error));
|
||||||
else fprintf(stderr, "Error reading from RDS95 device: %s\n", pa_strerror(pulse_error));
|
|
||||||
fprintf(stderr, "Disabling RDS.\n");
|
fprintf(stderr, "Disabling RDS.\n");
|
||||||
rds_on = 0;
|
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 = write_PulseOutputDevice(&runtime->output_device, output, sizeof(output)))) {
|
||||||
if(pulse_error == -1) fprintf(stderr, "Main PulseOutputDevice reported as uninitialized.");
|
fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
||||||
else fprintf(stderr, "Error writing to output device: %s\n", pa_strerror(pulse_error));
|
|
||||||
to_run = 0;
|
to_run = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -429,6 +420,9 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
signal(SIGINT, stop);
|
||||||
|
signal(SIGTERM, stop);
|
||||||
|
|
||||||
int ret = run_fm95(config, &runtime);
|
int ret = run_fm95(config, &runtime);
|
||||||
printf("Cleaning up...\n");
|
printf("Cleaning up...\n");
|
||||||
free_PulseInputDevice(&runtime.input_device);
|
free_PulseInputDevice(&runtime.input_device);
|
||||||
|
|||||||
Reference in New Issue
Block a user