mirror of
https://github.com/radio95-rnt/rds95.git
synced 2026-02-26 20:33:53 +01:00
part 2 for some reason
This commit is contained in:
2
.vscode/.server-controller-port.log
vendored
2
.vscode/.server-controller-port.log
vendored
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"port": 13452,
|
"port": 13452,
|
||||||
"time": 1745844256741,
|
"time": 1751819046475,
|
||||||
"version": "0.0.3"
|
"version": "0.0.3"
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,16 @@
|
|||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
project(rds95 VERSION 1.4)
|
project(rds95 VERSION 1.5)
|
||||||
|
|
||||||
add_compile_options(-Wall -Werror -Wextra -pedantic -O2 -std=c18 -march=native -DVERSION=\"${PROJECT_VERSION}\")
|
add_compile_options(-Wall -Werror -Wextra -pedantic -O2 -std=c18 -march=native -DVERSION=\"${PROJECT_VERSION}\")
|
||||||
|
|
||||||
|
file(GLOB INIH_FILES "inih/*.c")
|
||||||
|
add_library(inih OBJECT ${INIH_FILES})
|
||||||
|
|
||||||
file(GLOB SOURCES src/*.c)
|
file(GLOB SOURCES src/*.c)
|
||||||
|
|
||||||
add_executable(rds95 ${SOURCES})
|
add_executable(rds95 ${SOURCES})
|
||||||
|
|
||||||
target_link_libraries(rds95 PRIVATE m pthread pulse pulse-simple)
|
target_link_libraries(rds95 PRIVATE m pthread pulse pulse-simple inih)
|
||||||
|
|
||||||
install(TARGETS rds95 DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
install(TARGETS rds95 DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||||
|
|||||||
107
src/rds95.c
107
src/rds95.c
@@ -4,6 +4,7 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <pulse/simple.h>
|
#include <pulse/simple.h>
|
||||||
#include <pulse/error.h>
|
#include <pulse/error.h>
|
||||||
|
#include "../inih/ini.h"
|
||||||
|
|
||||||
#include "rds.h"
|
#include "rds.h"
|
||||||
#include "modulator.h"
|
#include "modulator.h"
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
#include "ascii_cmd.h"
|
#include "ascii_cmd.h"
|
||||||
|
|
||||||
#define RDS_DEVICE "RDS"
|
#define RDS_DEVICE "RDS"
|
||||||
|
#define DEFAULT_CONFIG_PATH "/etc/rds95.conf"
|
||||||
#define DEFAULT_STREAMS 2
|
#define DEFAULT_STREAMS 2
|
||||||
#define MAX_STREAMS 4
|
#define MAX_STREAMS 4
|
||||||
|
|
||||||
@@ -35,10 +37,6 @@ static void *control_pipe_worker(void* modulator) {
|
|||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void show_version() {
|
|
||||||
printf("rds95 (a RDS encoder by radio95) version %s\n", VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void show_help(char *name) {
|
static inline void show_help(char *name) {
|
||||||
printf(
|
printf(
|
||||||
"\n"
|
"\n"
|
||||||
@@ -55,12 +53,41 @@ static inline void show_help(char *name) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
typedef struct
|
||||||
show_version();
|
{
|
||||||
|
char control_pipe[51];
|
||||||
|
char rds_device_name[32];
|
||||||
|
uint8_t num_streams;
|
||||||
|
} RDS95_Config;
|
||||||
|
|
||||||
char control_pipe[51] = "\0";
|
static int config_handler(void* user, const char* section, const char* name, const char* value) {
|
||||||
char rds_device_name[32] = RDS_DEVICE;
|
RDS95_Config* config = (RDS95_Config*)user;
|
||||||
uint8_t num_streams = DEFAULT_STREAMS;
|
|
||||||
|
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
|
||||||
|
if(MATCH("rds95", "control_pipe")) {
|
||||||
|
strncpy(config->control_pipe, value, 49);
|
||||||
|
config->control_pipe[50] = '\0';
|
||||||
|
} else if(MATCH("devices", "rds95")) {
|
||||||
|
strncpy(config->rds_device_name, value, 30);
|
||||||
|
config->rds_device_name[31] = '\0';
|
||||||
|
} else if(MATCH("rds95", "streams")) {
|
||||||
|
config->num_streams = atoi(value);
|
||||||
|
if(config->num_streams > MAX_STREAMS || config->num_streams == 0) return 1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
.control_pipe = "\0",
|
||||||
|
.rds_device_name = RDS_DEVICE,
|
||||||
|
.num_streams = DEFAULT_STREAMS
|
||||||
|
};
|
||||||
|
|
||||||
pa_simple *rds_device = NULL;
|
pa_simple *rds_device = NULL;
|
||||||
pa_sample_spec format;
|
pa_sample_spec format;
|
||||||
@@ -69,13 +96,11 @@ int main(int argc, char **argv) {
|
|||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
pthread_t control_pipe_thread;
|
pthread_t control_pipe_thread;
|
||||||
|
|
||||||
const char *short_opt = "C:d:s:h";
|
const char *short_opt = "c:h";
|
||||||
|
|
||||||
struct option long_opt[] =
|
struct option long_opt[] =
|
||||||
{
|
{
|
||||||
{"ctl", required_argument, NULL, 'C'},
|
{"config", required_argument, NULL, 'c'},
|
||||||
{"device", required_argument, NULL, 'd'},
|
|
||||||
{"streams", required_argument, NULL, 's'},
|
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{ 0, 0, 0, 0 }
|
{ 0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
@@ -83,19 +108,9 @@ int main(int argc, char **argv) {
|
|||||||
int opt;
|
int opt;
|
||||||
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'C':
|
case 'c':
|
||||||
memcpy(control_pipe, optarg, 50);
|
memcpy(config_path, optarg, 62);
|
||||||
break;
|
config_path[63] = '\0';
|
||||||
case 'd':
|
|
||||||
memcpy(rds_device_name, optarg, 31);
|
|
||||||
rds_device_name[31] = '\0';
|
|
||||||
break;
|
|
||||||
case 's':
|
|
||||||
num_streams = (uint8_t)atoi(optarg);
|
|
||||||
if (num_streams < 1 || num_streams > MAX_STREAMS) {
|
|
||||||
fprintf(stderr, "Error: Number of streams must be between 1 and %d\n", MAX_STREAMS);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
show_help(argv[0]);
|
show_help(argv[0]);
|
||||||
@@ -106,7 +121,13 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Using %d RDS stream(s)\n", num_streams);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Using %d RDS stream(s)\n", config.num_streams);
|
||||||
|
|
||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
|
|
||||||
@@ -114,18 +135,18 @@ int main(int argc, char **argv) {
|
|||||||
signal(SIGTERM, stop);
|
signal(SIGTERM, stop);
|
||||||
|
|
||||||
format.format = PA_SAMPLE_FLOAT32NE;
|
format.format = PA_SAMPLE_FLOAT32NE;
|
||||||
format.channels = num_streams; // Use dynamic stream count
|
format.channels = config.num_streams; // Use dynamic stream count
|
||||||
format.rate = RDS_SAMPLE_RATE;
|
format.rate = RDS_SAMPLE_RATE;
|
||||||
|
|
||||||
buffer.prebuf = 0;
|
buffer.prebuf = 0;
|
||||||
buffer.tlength = NUM_MPX_FRAMES * num_streams;
|
buffer.tlength = NUM_MPX_FRAMES * config.num_streams;
|
||||||
buffer.maxlength = NUM_MPX_FRAMES * num_streams;
|
buffer.maxlength = NUM_MPX_FRAMES * config.num_streams;
|
||||||
|
|
||||||
rds_device = pa_simple_new(
|
rds_device = pa_simple_new(
|
||||||
NULL,
|
NULL,
|
||||||
"rds95",
|
"rds95",
|
||||||
PA_STREAM_PLAYBACK,
|
PA_STREAM_PLAYBACK,
|
||||||
rds_device_name,
|
config.rds_device_name,
|
||||||
"RDS Generator",
|
"RDS Generator",
|
||||||
&format,
|
&format,
|
||||||
NULL,
|
NULL,
|
||||||
@@ -140,38 +161,38 @@ int main(int argc, char **argv) {
|
|||||||
RDSEncoder rdsEncoder;
|
RDSEncoder rdsEncoder;
|
||||||
RDSModulator rdsModulator;
|
RDSModulator rdsModulator;
|
||||||
init_rds_encoder(&rdsEncoder);
|
init_rds_encoder(&rdsEncoder);
|
||||||
init_rds_modulator(&rdsModulator, &rdsEncoder, num_streams);
|
init_rds_modulator(&rdsModulator, &rdsEncoder, config.num_streams);
|
||||||
|
|
||||||
if (control_pipe[0]) {
|
if (config.control_pipe[0]) {
|
||||||
if (open_control_pipe(control_pipe) == 0) {
|
if (open_control_pipe(config.control_pipe) == 0) {
|
||||||
fprintf(stderr, "Reading control commands on %s.\n", control_pipe);
|
fprintf(stderr, "Reading control commands on %s.\n", config.control_pipe);
|
||||||
int r = pthread_create(&control_pipe_thread, &attr, control_pipe_worker, (void*)&rdsModulator);
|
int r = pthread_create(&control_pipe_thread, &attr, control_pipe_worker, (void*)&rdsModulator);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
fprintf(stderr, "Could not create control pipe thread.\n");
|
fprintf(stderr, "Could not create control pipe thread.\n");
|
||||||
control_pipe[0] = 0;
|
config.control_pipe[0] = 0;
|
||||||
goto exit;
|
goto exit;
|
||||||
} else fprintf(stderr, "Created control pipe thread.\n");
|
} else fprintf(stderr, "Created control pipe thread.\n");
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Failed to open control pipe: %s.\n", control_pipe);
|
fprintf(stderr, "Failed to open control pipe: %s.\n", config.control_pipe);
|
||||||
control_pipe[0] = 0;
|
config.control_pipe[0] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int pulse_error;
|
int pulse_error;
|
||||||
|
|
||||||
// Dynamically allocate buffer based on stream count
|
// Dynamically allocate buffer based on stream count
|
||||||
float *rds_buffer = (float*)malloc(NUM_MPX_FRAMES * num_streams * sizeof(float));
|
float *rds_buffer = (float*)malloc(NUM_MPX_FRAMES * config.num_streams * sizeof(float));
|
||||||
if (rds_buffer == NULL) {
|
if (rds_buffer == NULL) {
|
||||||
fprintf(stderr, "Error: Could not allocate memory for RDS buffer\n");
|
fprintf(stderr, "Error: Could not allocate memory for RDS buffer\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!stop_rds) {
|
while(!stop_rds) {
|
||||||
for (uint16_t i = 0; i < NUM_MPX_FRAMES * num_streams; i++) {
|
for (uint16_t i = 0; i < NUM_MPX_FRAMES * config.num_streams; i++) {
|
||||||
rds_buffer[i] = get_rds_sample(&rdsModulator, i % num_streams);
|
rds_buffer[i] = get_rds_sample(&rdsModulator, i % config.num_streams);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pa_simple_write(rds_device, rds_buffer, NUM_MPX_FRAMES * num_streams * sizeof(float), &pulse_error) != 0) {
|
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);
|
fprintf(stderr, "Error: could not play audio. (%s : %d)\n", pa_strerror(pulse_error), pulse_error);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -180,7 +201,7 @@ int main(int argc, char **argv) {
|
|||||||
free(rds_buffer);
|
free(rds_buffer);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
if (control_pipe[0]) {
|
if (config.control_pipe[0]) {
|
||||||
fprintf(stderr, "Waiting for pipe thread to shut down.\n");
|
fprintf(stderr, "Waiting for pipe thread to shut down.\n");
|
||||||
pthread_join(control_pipe_thread, NULL);
|
pthread_join(control_pipe_thread, NULL);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user