From fc111261b3657dcee77ae70397c6b771bb5d981e Mon Sep 17 00:00:00 2001 From: Sebastian Reimers Date: Sat, 10 Jan 2026 14:34:58 +0100 Subject: [PATCH] libsl,webui: add local track mute handling --- libsl/include/studiolink.h | 2 ++ libsl/src/audio.c | 11 ++++++----- libsl/src/http/server.c | 16 ++++++++++++++++ libsl/src/tracks.c | 15 +++++++++++++++ test/integration.sh | 5 +++++ test/src/tracks.c | 4 ++-- webui/src/api.ts | 4 ++++ webui/src/components/BottomActions.vue | 23 ++++++++++------------- webui/src/states/tracks.ts | 8 ++++++++ 9 files changed, 68 insertions(+), 20 deletions(-) diff --git a/libsl/include/studiolink.h b/libsl/include/studiolink.h index 4e774e3..bb67cae 100644 --- a/libsl/include/studiolink.h +++ b/libsl/include/studiolink.h @@ -173,6 +173,7 @@ struct slaudio *sl_track_audio(struct sl_track *track); int sl_track_dial(struct sl_track *track, struct pl *peer); void sl_track_accept(struct sl_track *track); void sl_track_hangup(struct sl_track *track); +void sl_track_toggle_mute(struct sl_track *track); void sl_track_ws_send(void); @@ -185,6 +186,7 @@ int sl_audio_close(void); int sl_audio_alloc(struct slaudio **audiop, struct sl_track *track); int slaudio_odict(struct odict **o, struct slaudio *a); int sl_audio_set_device(struct slaudio *audio, int play_idx, int src_idx); +void sl_audio_mute(struct slaudio *audio, bool mute); /****************************************************************************** diff --git a/libsl/src/audio.c b/libsl/src/audio.c index 78e9fd6..fc49aa7 100644 --- a/libsl/src/audio.c +++ b/libsl/src/audio.c @@ -25,7 +25,6 @@ struct amix { struct aumix_source *aumix_src; char *device; uint16_t speaker_id; - bool muted; }; struct ausrc_st { @@ -252,8 +251,6 @@ static int amix_alloc(struct amix **amixp, const char *device) aumix_source_readh(amix->aumix_src, mix_readh); - amix->muted = true; - out: if (err) { mem_deref(amix); @@ -322,7 +319,6 @@ static int src_alloc(struct ausrc_st **stp, const struct ausrc *as, st->amix = mem_ref(amix); - /* aumix_source_mute(amix->aumix_src, amix->muted); */ aumix_source_enable(amix->aumix_src, true); warning("debug %H\n", aumix_debug, aumix); @@ -396,7 +392,6 @@ static int play_alloc(struct auplay_st **stp, const struct auplay *ap, st->amix = mem_ref(amix); - /* aumix_source_mute(amix->aumix_src, amix->muted); */ aumix_source_enable(amix->aumix_src, true); goto out; @@ -666,6 +661,12 @@ int sl_audio_alloc(struct slaudio **audiop, struct sl_track *track) } +void sl_audio_mute(struct slaudio *audio, bool mute) +{ + aumix_source_mute(audio->mix_src, mute); +} + + int sl_audio_init(void) { struct sl_config *conf = sl_conf(); diff --git a/libsl/src/http/server.c b/libsl/src/http/server.c index 50813d2..a00d2ee 100644 --- a/libsl/src/http/server.c +++ b/libsl/src/http/server.c @@ -328,6 +328,22 @@ static void http_req_handler(struct http_conn *conn, goto out; } + + if (0 == pl_strcasecmp(&msg->path, "/api/v1/track/mute") && + 0 == pl_strcasecmp(&msg->met, "POST")) { + struct pl pltrack = PL_INIT; + err = re_regex(msg->prm.p, msg->prm.l, "track=[0-9]+", + &pltrack); + if (err) + goto err; + + sl_track_toggle_mute(sl_track_by_id(pl_u32(&pltrack))); + http_sreply(conn, 200, "OK", "text/html", "", 0); + + goto out; + } + + #ifndef RELEASE /* Default return OPTIONS - needed on dev for preflight CORS Check * @TODO: add release test */ diff --git a/libsl/src/tracks.c b/libsl/src/tracks.c index 5395ac0..3eda732 100644 --- a/libsl/src/tracks.c +++ b/libsl/src/tracks.c @@ -21,6 +21,7 @@ struct sl_track { char name[64]; char error[128]; enum sl_track_status status; + bool muted; union { struct local local; @@ -90,6 +91,7 @@ int sl_tracks_json(struct re_printf *pf, void *arg) odict_entry_add(o_track, "name", ODICT_STRING, track->name); odict_entry_add(o_track, "status", ODICT_INT, track->status); odict_entry_add(o_track, "error", ODICT_STRING, track->error); + odict_entry_add(o_track, "muted", ODICT_BOOL, track->muted); odict_entry_add(o_tracks, str_itoa(track->id, id, 10), ODICT_OBJECT, o_track); o_track = mem_deref(o_track); @@ -170,6 +172,7 @@ int sl_track_add(struct sl_track **trackp, enum sl_track_type type) track->id = sl_track_next_id(); track->type = type; track->status = SL_TRACK_IDLE; + track->muted = false; list_append(&tracks, &track->le, track); list_sort(&tracks, sort_handler, NULL); @@ -302,6 +305,18 @@ void sl_track_hangup(struct sl_track *track) } +void sl_track_toggle_mute(struct sl_track *track) +{ + /* only allowed for local track currently */ + if (!track || track->id != 1) + return; + + local_track->muted = !local_track->muted; + sl_audio_mute(local_track->u.local.slaudio, local_track->muted); + sl_track_ws_send(); +} + + void sl_track_ws_send(void) { char *json_str = mem_zalloc(SL_MAX_JSON + 1, NULL); diff --git a/test/integration.sh b/test/integration.sh index 35a5eb4..4c0707b 100755 --- a/test/integration.sh +++ b/test/integration.sh @@ -125,6 +125,10 @@ a_user_can_add_tracks() { [ "$track_count" == "2" ] } +a_user_can_mute_track() { + curl_post /api/v1/track/mute?track=1 +} + a_user_can_delete_tracks() { track_count=$(ws_test /ws/v1/tracks | jq ".[].type" | grep -c remote) [ "$track_count" == "2" ] @@ -152,6 +156,7 @@ main() { a_user_gets_404_if_page_not_exists a_user_can_connect_with_websocket a_user_can_add_tracks + a_user_can_mute_track a_user_can_delete_tracks } diff --git a/test/src/tracks.c b/test/src/tracks.c index 3fe5100..e3c45bb 100644 --- a/test/src/tracks.c +++ b/test/src/tracks.c @@ -4,8 +4,8 @@ static int test_track_add(void) { - char json_str[8192] = {0}; - struct odict *o = NULL; + char json_str[8192] = {0}; + struct odict *o = NULL; const struct odict_entry *o_entry = NULL; struct sl_track *track; int err; diff --git a/webui/src/api.ts b/webui/src/api.ts index db4f65c..1dc2fe5 100644 --- a/webui/src/api.ts +++ b/webui/src/api.ts @@ -28,6 +28,10 @@ export default { api_request('DELETE', '/tracks', String(id)); }, + track_mute(id: number) { + api_request('POST', '/track/mute?track=' + String(id), null); + }, + audio_device(track: number, mic: number, speaker: number) { api_request('PUT', '/audio/device?track=' + String(track), String(mic) + ";" + String(speaker)); }, diff --git a/webui/src/components/BottomActions.vue b/webui/src/components/BottomActions.vue index 56f951f..4c52872 100644 --- a/webui/src/components/BottomActions.vue +++ b/webui/src/components/BottomActions.vue @@ -1,8 +1,8 @@ - diff --git a/webui/src/states/tracks.ts b/webui/src/states/tracks.ts index 62a46b1..b13dd13 100644 --- a/webui/src/states/tracks.ts +++ b/webui/src/states/tracks.ts @@ -21,6 +21,7 @@ interface Track { id: number name: string status: TrackStatus + muted: boolean error: string } @@ -58,6 +59,7 @@ interface Tracks { websocket(ws_host: string): void isValid(id: number): boolean isSelected(id: number): boolean + isMuted(id: number): boolean localState(id: number): LocalTrackStates select(id: number): void selected(): number, @@ -109,12 +111,14 @@ export const tracks: Tracks = { error: "", local: LocalTrackStates.Setup, name: tracks[key].name, + muted: tracks[key].muted } last_key = parseInt(key) } this.state[tracks[key].id].name = tracks[key].name this.state[tracks[key].id].status = tracks[key].status + this.state[tracks[key].id].muted = tracks[key].muted if (tracks[key].type == 'local') { this.local_tracks.push(tracks[key]) @@ -152,6 +156,10 @@ export const tracks: Tracks = { return false }, + isMuted(id: number): boolean { + return this.state[id]?.muted + }, + localState(id: number): LocalTrackStates { return this.state[id].local },