Skip to content

Commit 52e9457

Browse files
committed
runtime: add selectable call alert events
1 parent 4d81c05 commit 52e9457

26 files changed

Lines changed: 532 additions & 19 deletions

include/dsd-neo/core/opts.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ struct dsd_opts {
218218
/* DMR: when set, relax CRC gating (ignore final CRC when no irrecoverable errors).
219219
Off by default; enabled via -F like other protocols. */
220220
uint8_t dmr_crc_relaxed_default;
221+
uint8_t call_alert_events;
221222
int frame_ysf;
222223
int inverted_ysf;
223224
short int aggressive_framesync;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// SPDX-License-Identifier: GPL-3.0-or-later
2+
/*
3+
* Copyright (C) 2026 by arancormonk <180709949+arancormonk@users.noreply.github.com>
4+
*/
5+
6+
/**
7+
* @file
8+
* @brief Call-alert event selection helpers.
9+
*/
10+
11+
#pragma once
12+
13+
#include <stdint.h>
14+
15+
typedef enum {
16+
DSD_CALL_ALERT_EVENT_VOICE_START = 1u << 0,
17+
DSD_CALL_ALERT_EVENT_VOICE_END = 1u << 1,
18+
DSD_CALL_ALERT_EVENT_DATA = 1u << 2,
19+
DSD_CALL_ALERT_EVENT_ALL =
20+
DSD_CALL_ALERT_EVENT_VOICE_START | DSD_CALL_ALERT_EVENT_VOICE_END | DSD_CALL_ALERT_EVENT_DATA
21+
} dsd_call_alert_event_t;
22+
23+
static inline uint8_t
24+
dsd_call_alert_mask_events(uint8_t events) {
25+
return (uint8_t)(events & DSD_CALL_ALERT_EVENT_ALL);
26+
}
27+
28+
static inline uint8_t
29+
dsd_call_alert_normalize_events(uint8_t events) {
30+
uint8_t normalized = dsd_call_alert_mask_events(events);
31+
return normalized ? normalized : (uint8_t)DSD_CALL_ALERT_EVENT_ALL;
32+
}
33+
34+
static inline uint8_t
35+
dsd_call_alert_effective_events(int enabled, uint8_t events) {
36+
if (!enabled) {
37+
return 0;
38+
}
39+
return dsd_call_alert_normalize_events(events);
40+
}
41+
42+
static inline int
43+
dsd_call_alert_event_enabled(int enabled, uint8_t configured_events, uint8_t event) {
44+
uint8_t events = dsd_call_alert_effective_events(enabled, configured_events);
45+
return (events & event) != 0;
46+
}

include/dsd-neo/runtime/config.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#define DSD_NEO_RUNTIME_CONFIG_H
1616

1717
/* Include schema types first (before extern "C" for C++ compat) */
18+
#include <dsd-neo/runtime/call_alert.h>
1819
#include <dsd-neo/runtime/config_schema.h>
1920

2021
#ifdef __cplusplus
@@ -679,6 +680,11 @@ typedef struct dsdneoUserConfig {
679680
char event_log[1024];
680681
char frame_log[1024];
681682

683+
/* [alerts] */
684+
int has_alerts;
685+
int call_alert_enabled; /* bool */
686+
int call_alert_events; /* bitmask of dsd_call_alert_event_t values */
687+
682688
/* [recording] */
683689
int has_recording;
684690
int per_call_wav; /* bool */

include/dsd-neo/ui/ui_cmd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ enum UiCmdId {
4040
UI_CMD_HPF_D_TOGGLE = 55,
4141
UI_CMD_AGGR_SYNC_TOGGLE = 56,
4242
UI_CMD_CALL_ALERT_TOGGLE = 57,
43+
UI_CMD_CALL_ALERT_EVENTS_SET = 58, // payload: uint8_t event mask (0 disables master switch)
4344

4445
// Views and visualization controls
4546
UI_CMD_CONST_TOGGLE = 70,

src/core/util/dsd_events.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "dsd-neo/core/opts_fwd.h"
2929
#include "dsd-neo/core/state_fwd.h"
30+
#include "dsd-neo/runtime/call_alert.h"
3031

3132
// Safe bounded copy helper that tolerates potential overlap
3233
static inline void
@@ -260,7 +261,8 @@ watchdog_event_history(dsd_opts* opts, dsd_state* state, uint8_t slot) {
260261
}
261262

262263
//call alert beep when new call detected
263-
if (last_source_id == 0 && source_id != 0 && opts->call_alert == 1) {
264+
if (last_source_id == 0 && source_id != 0
265+
&& dsd_call_alert_event_enabled(opts->call_alert, opts->call_alert_events, DSD_CALL_ALERT_EVENT_VOICE_START)) {
264266
beeper(opts, state, slot, 40, 86, 3);
265267
}
266268

@@ -300,7 +302,7 @@ watchdog_event_history(dsd_opts* opts, dsd_state* state, uint8_t slot) {
300302
}
301303

302304
//end of voice call alert
303-
if (opts->call_alert == 1) {
305+
if (dsd_call_alert_event_enabled(opts->call_alert, opts->call_alert_events, DSD_CALL_ALERT_EVENT_VOICE_END)) {
304306
beeper(opts, state, slot, 40, 86, 3);
305307
}
306308
}
@@ -1165,7 +1167,7 @@ watchdog_event_datacall(dsd_opts* opts, dsd_state* state, uint32_t src, uint32_t
11651167
/* stack buffers; no free */
11661168

11671169
//call alert on data calls
1168-
if (opts->call_alert) {
1170+
if (dsd_call_alert_event_enabled(opts->call_alert, opts->call_alert_events, DSD_CALL_ALERT_EVENT_DATA)) {
11691171
beeper(opts, state, slot, 80, 20, 3);
11701172
}
11711173
}

src/core/util/dsd_init.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "dsd-neo/core/state_fwd.h"
2727
#include "dsd-neo/dsp/p25p1_heuristics.h"
2828
#include "dsd-neo/platform/sockets.h"
29+
#include "dsd-neo/runtime/call_alert.h"
2930

3031
#ifdef USE_CODEC2
3132
#include <codec2/codec2.h>
@@ -246,6 +247,7 @@ initOpts(dsd_opts* opts) {
246247
opts->p2counter = 0;
247248

248249
opts->call_alert = 0; //call alert beeper for ncurses
250+
opts->call_alert_events = (uint8_t)DSD_CALL_ALERT_EVENT_ALL;
249251

250252
//rigctl options
251253
opts->use_rigctl = 0;

src/runtime/cli/args.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "dsd-neo/core/state_fwd.h"
3232
#include "dsd-neo/io/iq_types.h"
3333
#include "dsd-neo/platform/platform.h"
34+
#include "dsd-neo/runtime/call_alert.h"
3435
#if defined(__MINGW32__) || defined(__MINGW64__)
3536
#include <getopt.h>
3637
#include <unistd.h>
@@ -911,7 +912,10 @@ dsd_parse_short_opts(int argc, char** argv, dsd_opts* opts, dsd_state* state, in
911912
dsd_cli_usage();
912913
cli_set_exit_rc(out_exit_rc, 0);
913914
return DSD_PARSE_ONE_SHOT;
914-
case 'a': opts->call_alert = 1; break;
915+
case 'a':
916+
opts->call_alert = 1;
917+
opts->call_alert_events = (uint8_t)DSD_CALL_ALERT_EVENT_ALL;
918+
break;
915919
case '~':
916920
state->debug_mode = 1;
917921
LOG_NOTICE("Debug Mode Enabled; \n");

src/runtime/cli/usage.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ dsd_cli_usage(void) {
115115
printf(" --rdio-upload-timeout-ms <ms> API upload timeout per call (default 5000)\n");
116116
printf(" --rdio-upload-retries <n> API upload retry attempts per call (default 1)\n");
117117
printf(" --rdio-api-delete-after-upload Delete per-call WAV after successful API-only upload\n");
118-
printf(" -a Enable Call Alert Beep\n");
118+
printf(" -a Enable Call Alert Beep for start, end, and data events\n");
119119
printf(" (Warning! Might be annoying.)\n");
120120
printf(" -J <file> Specify Filename for Event Log Output.\n");
121121
printf(" -L <file> Specify Filename for LRRP Data Output.\n");

src/runtime/config_schema.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ static const dsdcfg_schema_entry_t s_schema[] = {
7070
{"logging", "event_log", "Event history log file path", "", NULL, DSDCFG_TYPE_PATH, 0, 0, 0},
7171
{"logging", "frame_log", "Frame trace log file path", "", NULL, DSDCFG_TYPE_PATH, 0, 0, 0},
7272

73+
/* [alerts] section */
74+
{"alerts", "enabled", "Enable audible call-alert beeps", "false", NULL, DSDCFG_TYPE_BOOL, 0, 0, 0},
75+
{"alerts", "call_alert", "Enable audible call-alert beeps (alias for enabled)", "false", NULL, DSDCFG_TYPE_BOOL, 0,
76+
0, 1},
77+
{"alerts", "voice_start", "Beep when a voice call starts", "true", NULL, DSDCFG_TYPE_BOOL, 0, 0, 0},
78+
{"alerts", "start", "Beep when a voice call starts (alias for voice_start)", "true", NULL, DSDCFG_TYPE_BOOL, 0, 0,
79+
1},
80+
{"alerts", "voice_end", "Beep when a voice call ends", "true", NULL, DSDCFG_TYPE_BOOL, 0, 0, 0},
81+
{"alerts", "end", "Beep when a voice call ends (alias for voice_end)", "true", NULL, DSDCFG_TYPE_BOOL, 0, 0, 1},
82+
{"alerts", "data", "Beep when a data call is logged", "true", NULL, DSDCFG_TYPE_BOOL, 0, 0, 0},
83+
7384
/* [recording] section */
7485
{"recording", "per_call_wav", "Enable per-call WAV output", "false", NULL, DSDCFG_TYPE_BOOL, 0, 0, 0},
7586
{"recording", "per_call_wav_dir", "Per-call WAV output directory", "./WAV", NULL, DSDCFG_TYPE_PATH, 0, 0, 0},

src/runtime/config_user.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@
2121
#include <dsd-neo/runtime/decode_mode.h>
2222
#include <dsd-neo/runtime/freq_parse.h>
2323
#include <dsd-neo/runtime/rdio_export.h>
24+
#include <stdint.h>
2425
#include <stdio.h>
2526
#include <stdlib.h>
2627
#include <string.h>
2728

2829
#include "config_user_internal.h"
2930
#include "dsd-neo/core/opts_fwd.h"
3031
#include "dsd-neo/core/state_fwd.h"
32+
#include "dsd-neo/runtime/call_alert.h"
3133

3234
#if defined(_WIN32)
3335
#include <windows.h>
@@ -86,6 +88,9 @@ user_cfg_reset(dsdneoUserConfig* cfg) {
8688

8789
cfg->rtl_auto_ppm = 0;
8890

91+
cfg->call_alert_enabled = 0;
92+
cfg->call_alert_events = DSD_CALL_ALERT_EVENT_ALL;
93+
8994
// Recording defaults (match initOpts)
9095
cfg->per_call_wav = 0;
9196
snprintf(cfg->per_call_wav_dir, sizeof cfg->per_call_wav_dir, "%s", "./WAV");
@@ -684,6 +689,16 @@ dsd_user_config_render_ini(const dsdneoUserConfig* cfg, FILE* out) {
684689
fprintf(out, "\n");
685690
}
686691

692+
if (cfg->has_alerts) {
693+
uint8_t events = dsd_call_alert_mask_events((uint8_t)cfg->call_alert_events);
694+
fprintf(out, "[alerts]\n");
695+
fprintf(out, "enabled = %s\n", cfg->call_alert_enabled ? "true" : "false");
696+
fprintf(out, "voice_start = %s\n", (events & DSD_CALL_ALERT_EVENT_VOICE_START) ? "true" : "false");
697+
fprintf(out, "voice_end = %s\n", (events & DSD_CALL_ALERT_EVENT_VOICE_END) ? "true" : "false");
698+
fprintf(out, "data = %s\n", (events & DSD_CALL_ALERT_EVENT_DATA) ? "true" : "false");
699+
fprintf(out, "\n");
700+
}
701+
687702
if (cfg->has_recording) {
688703
fprintf(out, "[recording]\n");
689704
fprintf(out, "per_call_wav = %s\n", cfg->per_call_wav ? "true" : "false");
@@ -914,6 +929,12 @@ dsd_apply_user_config_to_opts_impl(const dsdneoUserConfig* cfg, dsd_opts* opts,
914929
opts->frame_log_file[sizeof opts->frame_log_file - 1] = '\0';
915930
}
916931

932+
if (cfg->has_alerts) {
933+
uint8_t events = dsd_call_alert_mask_events((uint8_t)cfg->call_alert_events);
934+
opts->call_alert = (cfg->call_alert_enabled && events != 0) ? 1 : 0;
935+
opts->call_alert_events = events;
936+
}
937+
917938
if (cfg->has_recording) {
918939
if (cfg->per_call_wav_dir[0]) {
919940
snprintf(opts->wav_out_dir, sizeof opts->wav_out_dir, "%s", cfg->per_call_wav_dir);
@@ -1118,6 +1139,12 @@ dsd_snapshot_opts_to_user_config(const dsd_opts* opts, const dsd_state* state, d
11181139
snprintf(cfg->frame_log, sizeof cfg->frame_log, "%s", opts->frame_log_file);
11191140
cfg->frame_log[sizeof cfg->frame_log - 1] = '\0';
11201141

1142+
// Alert snapshot
1143+
cfg->has_alerts = 1;
1144+
cfg->call_alert_enabled = opts->call_alert ? 1 : 0;
1145+
cfg->call_alert_events = opts->call_alert ? dsd_call_alert_normalize_events(opts->call_alert_events)
1146+
: dsd_call_alert_mask_events(opts->call_alert_events);
1147+
11211148
// Recording snapshot
11221149
cfg->has_recording = 1;
11231150
cfg->per_call_wav = opts->dmr_stereo_wav ? 1 : 0;

0 commit comments

Comments
 (0)