#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <sys/ioctl.h>
#include <syslog.h>
#include <unistd.h>
#include <pthread.h>
#include <sched.h>
#include <alsa/asoundlib.h>
#include <ltc.h>
#define SAMPLE_RATE 48000
#define FPS 25
#define SAMPLES_PER_FRAME (SAMPLE_RATE / FPS) // Exactly 1920 samples
// Mutex-protected cross-thread state registers
static pthread_mutex_t sync_mutex = PTHREAD_MUTEX_INITIALIZER;
static struct timespec anchor_monotonic = {0, 0};
static struct timespec anchor_realtime = {0, 0};
static int64_t target_drift_offset_ns = 0;
static char active_os_time[32] = "00:00:00.000";
// Dedicated UI registers to isolate terminal formatting overhead
static int ui_hours = 0, ui_minutes = 0, ui_seconds = 0, ui_frame = 0;
static uint32_t ui_user_bits = 0;
static void load_config_file(char *tz_setting, size_t tz_len, char *device_name, size_t dev_len, int *null_mode) {
FILE *fp = fopen("/etc/ltc_master.conf", "r");
if (!fp) return;
char line[256];
while (fgets(line, sizeof(line), fp)) {
line[strcspn(line, "\r\n")] = 0;
if (line[0] == '#' || line[0] == '\0' || line[0] == ' ') continue;
char key[128], val[128];
if (sscanf(line, "%127[^= ] = %127s", key, val) == 2) {
if (strcmp(key, "timezone") == 0) {
strncpy(tz_setting, val, tz_len - 1);
} else if (strcmp(key, "audio_device") == 0) {
strncpy(device_name, val, dev_len - 1);
if (strcasecmp(device_name, "null") == 0) *null_mode = 1;
}
}
}
fclose(fp);
setenv("TZ", tz_setting, 1);
tzset();
}
static int configure_alsa_subsystem(snd_pcm_t *pcm) {
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
unsigned int rate = SAMPLE_RATE;
int dir = 0;
snd_pcm_hw_params_alloca(&hw_params);
snd_pcm_sw_params_alloca(&sw_params);
// Hardware Parameter Allocations
if (snd_pcm_hw_params_any(pcm, hw_params) < 0) return -1;
if (snd_pcm_hw_params_set_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) return -1;
if (snd_pcm_hw_params_set_format(pcm, hw_params, SND_PCM_FORMAT_S16_LE) < 0) return -1;
if (snd_pcm_hw_params_set_channels(pcm, hw_params, 2) < 0) return -1;
if (snd_pcm_hw_params_set_rate_near(pcm, hw_params, &rate, 0) < 0) return -1;
// Strict Hardware Invariant Check
if (rate != SAMPLE_RATE) {
syslog(LOG_CRIT, "ALSA Hardware Error: Soundcard refused 48000Hz sampling lock. Allocated rate: %u", rate);
return -2;
}
unsigned int period_time = 40000; // 40ms physical period chunks
if (snd_pcm_hw_params_set_period_time_near(pcm, hw_params, &period_time, &dir) < 0) return -1;
unsigned int periods = 2; // Strict double buffering to eliminate processing latency
if (snd_pcm_hw_params_set_periods_near(pcm, hw_params, &periods, &dir) < 0) return -1;
if (snd_pcm_hw_params(pcm, hw_params) < 0) return -1;
// Software Parameter Configuration
if (snd_pcm_sw_params_current(pcm, sw_params) < 0) return -1;
if (snd_pcm_sw_params_set_start_threshold(pcm, sw_params, SAMPLES_PER_FRAME) < 0) return -1;
if (snd_pcm_sw_params_set_avail_min(pcm, sw_params, SAMPLES_PER_FRAME) < 0) return -1;
if (snd_pcm_sw_params(pcm, sw_params) < 0) return -1;
return 0;
}
/* -------------------------------------------------------------------------
THREAD 1: ASYNCHRONOUS GRANDMASTER HOUSEKEEPING REFERENCE (PLL DRIVER)
------------------------------------------------------------------------- */
void *sync_thread_worker(void *arg) {
(void)arg;
struct timespec ts_mono, ts_real;
struct tm tm_os;
while (1) {
clock_gettime(CLOCK_MONOTONIC_RAW, &ts_mono);
clock_gettime(CLOCK_REALTIME, &ts_real);
localtime_r(&ts_real.tv_sec, &tm_os);
int64_t mono_ns = (ts_mono.tv_sec * 1000000000LL) + ts_mono.tv_nsec;
int64_t real_ns = (ts_real.tv_sec * 1000000000LL) + ts_real.tv_nsec;
pthread_mutex_lock(&sync_mutex);
target_drift_offset_ns = real_ns - mono_ns;
snprintf(active_os_time, sizeof(active_os_time), "%02d:%02d:%02d.%03ld",
tm_os.tm_hour, tm_os.tm_min, tm_os.tm_sec, ts_real.tv_nsec / 1000000L);
pthread_mutex_unlock(&sync_mutex);
usleep(200000);
}
return NULL;
}
/* -------------------------------------------------------------------------
THREAD 2: HARD-REALTIME COMPENSATED AUDIO REPROCESSING ENGINE
------------------------------------------------------------------------- */
void *audio_thread_worker(void *arg) {
(void)arg;
snd_pcm_t *pcm = NULL;
if (!null_device_mode) {
if (snd_pcm_open(&pcm, device_name, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
syslog(LOG_CRIT, "CRITICAL: Audio hardware device target open failed.");
exit(1);
}
int rc = configure_alsa_subsystem(pcm);
if (rc < 0) {
if (rc == -2) fprintf(stderr, "FATAL: Card refused 48000Hz. Check syslog.\n");
exit(1);
}
}
LTCEncoder *enc = ltc_encoder_create(SAMPLE_RATE, FPS, LTC_TV_625_50, 0);
ltc_encoder_set_filter(enc, 0.0);
size_t ltc_max_alloc_size = ltc_encoder_get_buffersize(enc) + 16;
ltcsnd_sample_t *ltc_buf = malloc(ltc_max_alloc_size * sizeof(ltcsnd_sample_t));
int16_t *audio = malloc(ltc_max_alloc_size * 2 * sizeof(int16_t));
if (!ltc_buf || !audio) {
syslog(LOG_CRIT, "FATAL: Out of memory in audio worker allocations.");
exit(1);
}
uint64_t hardware_samples_written = 0;
struct timespec loop_anchor_mono;
int64_t operational_drift_offset_ns = 0;
clock_gettime(CLOCK_MONOTONIC_RAW, &loop_anchor_mono);
pthread_mutex_lock(&sync_mutex);
operational_drift_offset_ns = target_drift_offset_ns;
pthread_mutex_unlock(&sync_mutex);
while (1) {
snd_pcm_sframes_t delay_samples = 0;
if (!null_device_mode && pcm) {
if (snd_pcm_delay(pcm, &delay_samples) < 0) {
delay_samples = 0;
}
if (delay_samples < 0) delay_samples = 0;
if (delay_samples > SAMPLE_RATE) delay_samples = SAMPLE_RATE;
}
int64_t exact_sample_index = (int64_t)hardware_samples_written - (int64_t)delay_samples;
if (exact_sample_index < 0) exact_sample_index = 0;
int64_t timeline_ns = (exact_sample_index * 1000000000LL) / SAMPLE_RATE;
int64_t anchor_base_ns = (loop_anchor_mono.tv_sec * 1000000000LL) + loop_anchor_mono.tv_nsec;
int64_t absolute_realtime_ns = anchor_base_ns + timeline_ns + operational_drift_offset_ns;
time_t absolute_secs = absolute_realtime_ns / 1000000000LL;
int64_t residual_ns = absolute_realtime_ns % 1000000000LL;
struct tm calculation_tm;
localtime_r(&absolute_secs, &calculation_tm);
int calculated_frame_index = (exact_sample_index / SAMPLES_PER_FRAME) % FPS;
SMPTETimecode rolling_tc;
rolling_tc.hours = calculation_tm.tm_hour;
rolling_tc.mins = calculation_tm.tm_min;
rolling_tc.secs = calculation_tm.tm_sec;
rolling_tc.frame = calculated_frame_index;
rolling_tc.days = calculation_tm.tm_mday;
rolling_tc.months = calculation_tm.tm_mon + 1;
rolling_tc.years = (1900 + calculation_tm.tm_year) % 100;
uint8_t bcd_year = ((rolling_tc.years / 10) << 4) | (rolling_tc.years % 10);
uint8_t bcd_month = ((rolling_tc.months / 10) << 4) | (rolling_tc.months % 10);
uint8_t bcd_day = ((rolling_tc.days / 10) << 4) | (rolling_tc.days % 10);
uint32_t user_bits = (0x00 << 24) | (bcd_year << 16) | (bcd_month << 8) | bcd_day;
ltc_encoder_set_timecode(enc, &rolling_tc);
ltc_encoder_set_user_bits(enc, user_bits);
ltc_encoder_encode_frame(enc);
int n = ltc_encoder_get_buffersize(enc);
if (n != SAMPLES_PER_FRAME) {
syslog(LOG_CRIT, "FATAL: libltc buffer size mismatch. Expected %d, got %d", SAMPLES_PER_FRAME, n);
exit(1);
}
// Native Mint-safe buffer map extraction
ltcsnd_sample_t *encoder_raw_buffer = ltc_encoder_get_buf(enc);
memcpy(ltc_buf, encoder_raw_buffer, n * sizeof(ltcsnd_sample_t));
pthread_mutex_lock(&sync_mutex);
ui_hours = rolling_tc.hours;
ui_minutes = rolling_tc.mins;
ui_seconds = rolling_tc.secs;
ui_frame = rolling_tc.frame;
ui_user_bits = user_bits;
if (calculated_frame_index == 0) {
operational_drift_offset_ns = target_drift_offset_ns;
}
pthread_mutex_unlock(&sync_mutex);
if (!null_device_mode && pcm) {
for (int i = 0; i < n; i++) {
int16_t sample = ((int16_t)ltc_buf[i] - 128) << 8;
audio[i * 2] = sample;
audio[i * 2 + 1] = (int16_t)(-sample);
}
snd_pcm_sframes_t written = snd_pcm_writei(pcm, audio, SAMPLES_PER_FRAME);
if (written < 0) {
syslog(LOG_CRIT, "[ALSA PIPELINE XRUN] Executing baseline reset: %s", snd_strerror(written));
snd_pcm_prepare(pcm);
clock_gettime(CLOCK_MONOTONIC_RAW, &loop_anchor_mono);
pthread_mutex_lock(&sync_mutex);
operational_drift_offset_ns = target_drift_offset_ns;
hardware_samples_written = 0;
pthread_mutex_unlock(&sync_mutex);
} else {
hardware_samples_written += written;
}
} else {
usleep(40000);
hardware_samples_written += SAMPLES_PER_FRAME;
}
}
free(ltc_buf); free(audio);
ltc_encoder_free(enc);
if (pcm) snd_pcm_close(pcm);
return NULL;
}
/* -------------------------------------------------------------------------
THREAD 3: LOW-PRIORITY REAL-TIME SCREEN REPAINT LOOP
------------------------------------------------------------------------- */
void *ui_thread_worker(void *arg) {
(void)arg;
int last_frame = -1;
printf("\033[2J\033[H");
fflush(stdout);
while (1) {
pthread_mutex_lock(&sync_mutex);
if (ui_frame != last_frame) {
printf("\r[LTC TRANSMISSION LOCK] %02d:%02d:%02d:%02d | USER BITS: UB=%08X | MONITORED WALL: %s\033[K",
ui_hours, ui_minutes, ui_seconds, ui_frame, ui_user_bits, active_os_time);
fflush(stdout);
last_frame = ui_frame;
}
pthread_mutex_unlock(&sync_mutex);
usleep(10000);
}
return NULL;
}
/* -------------------------------------------------------------------------
MAIN CONFIGURATION ENTRY POINT
------------------------------------------------------------------------- */
int main(void) {
openlog("ltc_master", LOG_PID | LOG_CONS, LOG_DAEMON);
char tz_setting[128] = "Pacific/Auckland";
char device_name[128] = "hw:0,0";
int null_device_mode_local = 0;
load_config_file(tz_setting, sizeof(tz_setting), device_name, sizeof(device_name), &null_device_mode_local);
null_device_mode = null_device_mode_local;
pthread_attr_t realtime_attr;
struct sched_param sched_reg;
pthread_attr_init(&realtime_attr);
pthread_attr_setinheritsched(&realtime_attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&realtime_attr, SCHED_FIFO);
sched_reg.sched_priority = 65;
pthread_attr_setschedparam(&realtime_attr, &sched_reg);
printf("Launching Transmitter-Grade Phase-Locked LTC Generator...\n");
fflush(stdout);
pthread_t sync_thread, audio_thread, ui_thread;
pthread_create(&sync_thread, NULL, sync_thread_worker, NULL);
if (pthread_create(&audio_thread, &realtime_attr, audio_thread_worker, NULL) != 0) {
syslog(LOG_ERR, "CRITICAL: Realtime prioritization failed. Check execution permissions.");
pthread_create(&audio_thread, NULL, audio_thread_worker, NULL);
} else {
syslog(LOG_INFO, "SUCCESS: Hard-Realtime SCHED_FIFO 65 applied to audio thread.");
}
pthread_create(&ui_thread, NULL, ui_thread_worker, NULL);
pthread_attr_destroy(&realtime_attr);
pthread_join(sync_thread, NULL);
pthread_join(audio_thread, NULL);
pthread_join(ui_thread, NULL);
closelog();
return 0;
}