diff --git a/libobs/obs-hotkey.c b/libobs/obs-hotkey.c index d6507c161..c49316237 100644 --- a/libobs/obs-hotkey.c +++ b/libobs/obs-hotkey.c @@ -1495,3 +1495,15 @@ void obs_hotkey_update_atomic(obs_hotkey_atomic_update_func func, void *data) unlock(); } + +void obs_hotkeys_set_audio_hotkeys_translations( + const char *mute, const char *unmute, + const char *push_to_mute, const char *push_to_talk) +{ +#define SET_T(n) bfree(obs->hotkeys.n); obs->hotkeys.n = bstrdup(n) + SET_T(mute); + SET_T(unmute); + SET_T(push_to_mute); + SET_T(push_to_talk); +#undef SET_T +} diff --git a/libobs/obs-hotkey.h b/libobs/obs-hotkey.h index 8b5f20262..17d9f850b 100644 --- a/libobs/obs-hotkey.h +++ b/libobs/obs-hotkey.h @@ -120,6 +120,9 @@ EXPORT void obs_hotkeys_set_translations_s( obs_hotkeys_set_translations_s(translations, \ sizeof(struct obs_hotkeys_translations)) +EXPORT void obs_hotkeys_set_audio_hotkeys_translations( + const char *mute, const char *unmute, + const char *push_to_mute, const char *push_to_talk); /* registering hotkeys (giving hotkeys a name and a function) */ diff --git a/libobs/obs-internal.h b/libobs/obs-internal.h index a5dd80e6a..dbdd34064 100644 --- a/libobs/obs-internal.h +++ b/libobs/obs-internal.h @@ -299,6 +299,10 @@ struct obs_core_hotkeys { signal_handler_t *signals; char *translations[OBS_KEY_LAST_VALUE]; + char *mute; + char *unmute; + char *push_to_mute; + char *push_to_talk; }; struct obs_core { @@ -512,6 +516,19 @@ struct obs_source { gs_texrender_t *filter_texrender; enum obs_allow_direct_render allow_direct; bool rendering_filter; + + /* sources specific hotkeys */ + obs_hotkey_pair_id mute_unmute_key; + obs_hotkey_id push_to_mute_key; + obs_hotkey_id push_to_talk_key; + bool push_to_mute_enabled : 1; + bool push_to_mute_pressed : 1; + bool push_to_talk_enabled : 1; + bool push_to_talk_pressed : 1; + uint64_t push_to_mute_delay; + uint64_t push_to_mute_stop_time; + uint64_t push_to_talk_delay; + uint64_t push_to_talk_stop_time; }; extern const struct obs_source_info *find_source(struct darray *list, diff --git a/libobs/obs-source.c b/libobs/obs-source.c index 230285577..072283fc9 100644 --- a/libobs/obs-source.c +++ b/libobs/obs-source.c @@ -79,6 +79,10 @@ static const char *source_signals[] = { "void show(ptr source)", "void hide(ptr source)", "void mute(ptr source, bool muted)", + "void push_to_mute_changed(ptr source, bool enabled)", + "void push_to_mute_delay(ptr source, int delay)", + "void push_to_talk_changed(ptr source, bool enabled)", + "void push_to_talk_delay(ptr source, int delay)", "void enable(ptr source, bool enabled)", "void rename(ptr source, string new_name, string prev_name)", "void volume(ptr source, in out float volume)", @@ -155,6 +159,83 @@ bool obs_source_init(struct obs_source *source, return true; } +static bool obs_source_hotkey_mute(void *data, + obs_hotkey_pair_id id, obs_hotkey_t *key, bool pressed) +{ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(key); + + struct obs_source *source = data; + + if (!pressed || obs_source_muted(source)) return false; + + obs_source_set_muted(source, true); + return true; +} + +static bool obs_source_hotkey_unmute(void *data, + obs_hotkey_pair_id id, obs_hotkey_t *key, bool pressed) +{ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(key); + + struct obs_source *source = data; + + if (!pressed || !obs_source_muted(source)) return false; + + obs_source_set_muted(source, false); + return true; +} + +static void obs_source_hotkey_push_to_mute(void *data, + obs_hotkey_id id, obs_hotkey_t *key, bool pressed) +{ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(key); + + struct obs_source *source = data; + + pthread_mutex_lock(&source->audio_mutex); + source->push_to_mute_pressed = pressed; + pthread_mutex_unlock(&source->audio_mutex); +} + +static void obs_source_hotkey_push_to_talk(void *data, + obs_hotkey_id id, obs_hotkey_t *key, bool pressed) +{ + UNUSED_PARAMETER(id); + UNUSED_PARAMETER(key); + + struct obs_source *source = data; + + pthread_mutex_lock(&source->audio_mutex); + source->push_to_talk_pressed = pressed; + pthread_mutex_unlock(&source->audio_mutex); +} + +static void obs_source_init_audio_hotkeys(struct obs_source *source) +{ + if (!(source->info.output_flags & OBS_SOURCE_AUDIO)) { + source->mute_unmute_key = OBS_INVALID_HOTKEY_ID; + source->push_to_talk_key = OBS_INVALID_HOTKEY_ID; + return; + } + + source->mute_unmute_key = obs_hotkey_pair_register_source(source, + "libobs.mute", obs->hotkeys.mute, + "libobs.unmute", obs->hotkeys.unmute, + obs_source_hotkey_mute, obs_source_hotkey_unmute, + source, source); + + source->push_to_mute_key = obs_hotkey_register_source(source, + "libobs.push-to-mute", obs->hotkeys.push_to_mute, + obs_source_hotkey_push_to_mute, source); + + source->push_to_talk_key = obs_hotkey_register_source(source, + "libobs.push-to-talk", obs->hotkeys.push_to_talk, + obs_source_hotkey_push_to_talk, source); +} + static inline void obs_source_dosignal(struct obs_source *source, const char *signal_obs, const char *signal_source) { @@ -186,6 +267,10 @@ obs_source_t *obs_source_create(enum obs_source_type type, const char *id, source->info = *info; } + source->mute_unmute_key = OBS_INVALID_HOTKEY_PAIR_ID; + source->push_to_mute_key = OBS_INVALID_HOTKEY_ID; + source->push_to_talk_key = OBS_INVALID_HOTKEY_ID; + if (!obs_source_init_context(source, settings, name, hotkey_data)) goto fail; @@ -195,6 +280,8 @@ obs_source_t *obs_source_create(enum obs_source_type type, const char *id, if (!obs_source_init(source, info)) goto fail; + obs_source_init_audio_hotkeys(source); + /* allow the source to be created even if creation fails so that the * user's data doesn't become lost */ if (info) @@ -274,6 +361,10 @@ void obs_source_destroy(struct obs_source *source) source->context.data = NULL; } + obs_hotkey_unregister(source->push_to_talk_key); + obs_hotkey_unregister(source->push_to_mute_key); + obs_hotkey_pair_unregister(source->mute_unmute_key); + for (i = 0; i < source->async_cache.num; i++) obs_source_frame_decref(source->async_cache.array[i].frame); @@ -829,7 +920,22 @@ static void source_output_audio_line(obs_source_t *source, source->present_volume * obs->audio.user_volume * obs->audio.present_volume; - bool muted = !source->enabled || source->muted; + if (source->push_to_mute_enabled && source->push_to_mute_pressed) + source->push_to_mute_stop_time = os_time + + source->push_to_mute_delay * 1000000; + + if (source->push_to_talk_enabled && source->push_to_talk_pressed) + source->push_to_talk_stop_time = os_time + + source->push_to_talk_delay * 1000000; + + bool push_to_mute_active = source->push_to_mute_pressed || + os_time < source->push_to_mute_stop_time; + bool push_to_talk_active = source->push_to_talk_pressed || + os_time < source->push_to_talk_stop_time; + + bool muted = !source->enabled || source->muted || + (source->push_to_mute_enabled && push_to_mute_active) || + (source->push_to_talk_enabled && !push_to_talk_active); if (muted) in.volume = 0.0f; @@ -2820,3 +2926,135 @@ void obs_source_set_muted(obs_source_t *source, bool muted) calldata_free(&data); } + +static void source_signal_push_to_changed(obs_source_t *source, + const char *signal, bool enabled) +{ + struct calldata data = {0}; + + calldata_set_ptr (&data, "source", source); + calldata_set_bool(&data, "enabled", enabled); + + signal_handler_signal(source->context.signals, signal, &data); + calldata_free(&data); +} + +static void source_signal_push_to_delay(obs_source_t *source, + const char *signal, uint64_t delay) +{ + struct calldata data = {0}; + + calldata_set_ptr (&data, "source", source); + calldata_set_bool(&data, "delay", delay); + + signal_handler_signal(source->context.signals, signal, &data); + calldata_free(&data); +} + +bool obs_source_push_to_mute_enabled(obs_source_t *source) +{ + bool enabled; + if (!source) return false; + + pthread_mutex_lock(&source->audio_mutex); + enabled = source->push_to_mute_enabled; + pthread_mutex_unlock(&source->audio_mutex); + + return enabled; +} + +void obs_source_enable_push_to_mute(obs_source_t *source, bool enabled) +{ + if (!source) return; + + pthread_mutex_lock(&source->audio_mutex); + bool changed = source->push_to_mute_enabled != enabled; + if (obs_source_get_output_flags(source) & OBS_SOURCE_AUDIO && changed) + blog(LOG_INFO, "source '%s' %s push-to-mute", + obs_source_get_name(source), + enabled ? "enabled" : "disabled"); + + source->push_to_mute_enabled = enabled; + + if (changed) + source_signal_push_to_changed(source, "push_to_mute_changed", + enabled); + pthread_mutex_unlock(&source->audio_mutex); +} + +uint64_t obs_source_get_push_to_mute_delay(obs_source_t *source) +{ + uint64_t delay; + if (!source) return 0; + + pthread_mutex_lock(&source->audio_mutex); + delay = source->push_to_mute_delay; + pthread_mutex_unlock(&source->audio_mutex); + + return delay; +} + +void obs_source_set_push_to_mute_delay(obs_source_t *source, uint64_t delay) +{ + if (!source) return; + + pthread_mutex_lock(&source->audio_mutex); + source->push_to_mute_delay = delay; + + source_signal_push_to_delay(source, "push_to_mute_delay", delay); + pthread_mutex_unlock(&source->audio_mutex); +} + +bool obs_source_push_to_talk_enabled(obs_source_t *source) +{ + bool enabled; + if (!source) return false; + + pthread_mutex_lock(&source->audio_mutex); + enabled = source->push_to_talk_enabled; + pthread_mutex_unlock(&source->audio_mutex); + + return enabled; +} + +void obs_source_enable_push_to_talk(obs_source_t *source, bool enabled) +{ + if (!source) return; + + pthread_mutex_lock(&source->audio_mutex); + bool changed = source->push_to_talk_enabled != enabled; + if (obs_source_get_output_flags(source) & OBS_SOURCE_AUDIO && changed) + blog(LOG_INFO, "source '%s' %s push-to-talk", + obs_source_get_name(source), + enabled ? "enabled" : "disabled"); + + source->push_to_talk_enabled = enabled; + + if (changed) + source_signal_push_to_changed(source, "push_to_talk_changed", + enabled); + pthread_mutex_unlock(&source->audio_mutex); +} + +uint64_t obs_source_get_push_to_talk_delay(obs_source_t *source) +{ + uint64_t delay; + if (!source) return 0; + + pthread_mutex_lock(&source->audio_mutex); + delay = source->push_to_talk_delay; + pthread_mutex_unlock(&source->audio_mutex); + + return delay; +} + +void obs_source_set_push_to_talk_delay(obs_source_t *source, uint64_t delay) +{ + if (!source) return; + + pthread_mutex_lock(&source->audio_mutex); + source->push_to_talk_delay = delay; + + source_signal_push_to_delay(source, "push_to_talk_delay", delay); + pthread_mutex_unlock(&source->audio_mutex); +} diff --git a/libobs/obs.c b/libobs/obs.c index 235ac472c..eede4f4f7 100644 --- a/libobs/obs.c +++ b/libobs/obs.c @@ -642,6 +642,10 @@ static inline bool obs_init_hotkeys(void) da_init(hotkeys->hotkeys); hotkeys->signals = obs->signals; hotkeys->name_map_init_token = obs_pthread_once_init_token; + hotkeys->mute = bstrdup("Mute"); + hotkeys->unmute = bstrdup("Unmute"); + hotkeys->push_to_mute = bstrdup("Push-to-mute"); + hotkeys->push_to_talk = bstrdup("Push-to-talk"); if (!obs_hotkeys_platform_init(hotkeys)) return false; @@ -687,6 +691,11 @@ static inline void obs_free_hotkeys(void) { struct obs_core_hotkeys *hotkeys = &obs->hotkeys; + bfree(hotkeys->mute); + bfree(hotkeys->unmute); + bfree(hotkeys->push_to_mute); + bfree(hotkeys->push_to_talk); + obs_hotkey_name_map_free(); obs_hotkeys_platform_free(hotkeys); @@ -1451,6 +1460,14 @@ static obs_source_t *obs_load_source_type(obs_data_t *source_data, obs_data_set_default_bool(source_data, "muted", false); obs_source_set_muted(source, obs_data_get_bool(source_data, "muted")); + obs_data_set_default_bool(source_data, "push-to-talk", false); + obs_source_enable_push_to_talk(source, + obs_data_get_bool(source_data, "push-to-talk")); + + obs_data_set_default_int(source_data, "push-to-talk-delay", 0); + obs_source_set_push_to_talk_delay(source, + obs_data_get_int(source_data, "push-to-talk-delay")); + if (filters) { size_t count = obs_data_array_count(filters); @@ -1524,6 +1541,8 @@ obs_data_t *obs_save_source(obs_source_t *source) const char *id = obs_source_get_id(source); bool enabled = obs_source_enabled(source); bool muted = obs_source_muted(source); + bool push_to_talk= obs_source_push_to_talk_enabled(source); + uint64_t ptt_delay = obs_source_get_push_to_talk_delay(source); obs_source_save(source); hotkeys = obs_hotkeys_save_source(source); @@ -1543,6 +1562,8 @@ obs_data_t *obs_save_source(obs_source_t *source) obs_data_set_double(source_data, "volume", volume); obs_data_set_bool (source_data, "enabled", enabled); obs_data_set_bool (source_data, "muted", muted); + obs_data_set_bool (source_data, "push-to-talk", push_to_talk); + obs_data_set_int (source_data, "push-to-talk-delay", ptt_delay); obs_data_set_obj (source_data, "hotkeys", hotkey_data); pthread_mutex_lock(&source->filter_mutex); diff --git a/libobs/obs.h b/libobs/obs.h index e7fa32b5d..30f2feaa8 100644 --- a/libobs/obs.h +++ b/libobs/obs.h @@ -865,6 +865,20 @@ EXPORT void obs_source_set_enabled(obs_source_t *source, bool enabled); EXPORT bool obs_source_muted(const obs_source_t *source); EXPORT void obs_source_set_muted(obs_source_t *source, bool muted); +EXPORT bool obs_source_push_to_mute_enabled(obs_source_t *source); +EXPORT void obs_source_enable_push_to_mute(obs_source_t *source, bool enabled); + +EXPORT uint64_t obs_source_get_push_to_mute_delay(obs_source_t *source); +EXPORT void obs_source_set_push_to_mute_delay(obs_source_t *source, + uint64_t delay); + +EXPORT bool obs_source_push_to_talk_enabled(obs_source_t *source); +EXPORT void obs_source_enable_push_to_talk(obs_source_t *source, bool enabled); + +EXPORT uint64_t obs_source_get_push_to_talk_delay(obs_source_t *source); +EXPORT void obs_source_set_push_to_talk_delay(obs_source_t *source, + uint64_t delay); + /* ------------------------------------------------------------------------- */ /* Functions used by sources */