#include "common.h" #include #include #include #include #include #include "../inih/ini.h" #include "rds.h" #include "fs.h" #include "modulator.h" #include "udp_server.h" #include "lib.h" #include "ascii_cmd.h" #define DEFAULT_CONFIG_PATH "/etc/rds95.conf" #define DEFAULT_STREAMS 1 #define MAX_STREAMS 4 #define NUM_MPX_FRAMES 128 static uint8_t stop_rds; static void stop() { printf("Received an stopping signal\n"); stop_rds = 1; } static void *udp_server_worker() { while (!stop_rds) { poll_udp_server(); msleep(READ_TIMEOUT_MS); } close_udp_server(); pthread_exit(NULL); } static inline void show_help(char *name) { printf( "\n" "Usage: %s [options]\n" "\n" "\t-c,--config\tSet the config path [default: %s]\n" "\n", name, DEFAULT_CONFIG_PATH ); } typedef struct { uint16_t udp_port; char rds_device_name[48]; uint8_t num_streams; } RDS95_Config; static int config_handler(void* user, const char* section, const char* name, const char* value) { RDS95_Config* config = (RDS95_Config*)user; #define MATCH(s, n) (strcmp(section, s) == 0 && strcmp(name, n) == 0) if (MATCH("rds95", "udp_port")) config->udp_port = (uint16_t)atoi(value); else if (MATCH("devices", "rds95")) { strncpy(config->rds_device_name, value, sizeof(config->rds_device_name) - 1); config->rds_device_name[sizeof(config->rds_device_name) - 1] = '\0'; } else if (MATCH("rds95", "streams")) { int streams = atoi(value); if (streams > MAX_STREAMS || streams == 0) return 0; config->num_streams = (uint8_t)streams; } else return 0; return 1; } int main(int argc, char **argv) { printf("rds95 (a RDS encoder by radio95) version %s\n", VERSION); char config_path[64] = DEFAULT_CONFIG_PATH; RDS95_Config config = { .udp_port = 0, .rds_device_name = "\0", .num_streams = DEFAULT_STREAMS }; pa_simple *rds_device = NULL; pa_sample_spec format; pa_buffer_attr buffer; pthread_attr_t attr; pthread_t udp_server_thread; const char *short_opt = "c:h"; struct option long_opt[] = { {"config", required_argument, NULL, 'c'}, {"help", no_argument, NULL, 'h'}, { 0, 0, 0, 0 } }; int opt; while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) { switch (opt) { case 'c': memcpy(config_path, optarg, 62); config_path[63] = '\0'; break; case 'h': show_help(argv[0]); return 0; default: show_help(argv[0]); return 1; } } int res = ini_parse(config_path, config_handler, &config); if(res != 0) { fprintf(stderr, "Error: Could not read ini config, error code as return code.\n"); return res; } if(_strnlen(config.rds_device_name, 48) == 0) { printf("Error: No output device\n"); return 1; } printf("Using %d RDS stream(s)\n", config.num_streams); pthread_attr_init(&attr); signal(SIGINT, stop); signal(SIGTERM, stop); format.format = PA_SAMPLE_FLOAT32NE; format.channels = config.num_streams; format.rate = RDS_SAMPLE_RATE; buffer.prebuf = 0; buffer.tlength = buffer.maxlength = NUM_MPX_FRAMES * config.num_streams; rds_device = pa_simple_new( NULL, "rds95", PA_STREAM_PLAYBACK, config.rds_device_name, "RDS Generator", &format, NULL, &buffer, NULL ); if (rds_device == NULL) { fprintf(stderr, "Error: cannot open sound device.\n"); goto exit; } RDSEncoder rdsEncoder; RDSModulator rdsModulator; init_lua(&rdsModulator); init_rds_encoder(&rdsEncoder); init_rds_modulator(&rdsModulator, &rdsEncoder, config.num_streams); if(config.udp_port) { if(open_udp_server(config.udp_port, &rdsModulator) == 0) { fprintf(stderr, "Reading control commands on UDP:%d.\n", config.udp_port); int r = pthread_create(&udp_server_thread, &attr, udp_server_worker, NULL); if (r < 0) { fprintf(stderr, "Could not create UDP server thread.\n"); config.udp_port = 0; goto exit; } else fprintf(stderr, "Created UDP server thread.\n"); } else { fprintf(stderr, "Failed to open UDP server\n"); config.udp_port = 0; } } int pulse_error; float *rds_buffer = (float*)malloc(NUM_MPX_FRAMES * config.num_streams * sizeof(float)); if (rds_buffer == NULL) { fprintf(stderr, "Error: Could not allocate memory for RDS buffer\n"); goto exit; } while(!stop_rds) { for (uint16_t i = 0; i < NUM_MPX_FRAMES * config.num_streams; i++) rds_buffer[i] = get_rds_sample(&rdsModulator, i % config.num_streams); if (pa_simple_write(rds_device, rds_buffer, NUM_MPX_FRAMES * config.num_streams * sizeof(float), &pulse_error) != 0) { fprintf(stderr, "Error: could not play audio. (%s : %d)\n", pa_strerror(pulse_error), pulse_error); break; } } free(rds_buffer); exit: if(config.udp_port) { fprintf(stderr, "Waiting for UDP thread to shut down.\n"); pthread_join(udp_server_thread, NULL); } encoder_saveToFile(&rdsEncoder); Modulator_saveToFile(&rdsModulator.params); cleanup_rds_modulator(&rdsModulator); pthread_attr_destroy(&attr); if (rds_device != NULL) pa_simple_free(rds_device); return 0; }