diff --git a/src/actions.cpp b/src/actions.cpp new file mode 100644 index 00000000..461a75aa --- /dev/null +++ b/src/actions.cpp @@ -0,0 +1,526 @@ +#include "actions.h" +#include + +#include "utils.h" +#include "midi-agent.h" +QString ActionsClass::action_to_string(const ActionsClass::Actions &enumval) +{ + return QVariant::fromValue(enumval).toString(); +} + +ActionsClass::Actions ActionsClass::string_to_action(const QString &action) +{ + return QVariant(action).value(); +} +QString ActionsClass::event_to_string(const ActionsClass::obs_event_type &enumval) +{ + return QVariant::fromValue(enumval).toString(); +} + +ActionsClass::obs_event_type ActionsClass::string_to_event(const QString &action) +{ + return QVariant(action).value(); +} + + +/** + * Sets the currently active scene + */ +void ActionsClass::SetCurrentScene(QString sceneName) +{ + OBSSourceAutoRelease source = + obs_get_source_by_name(sceneName.toStdString().c_str()); + + if (source) { + obs_frontend_set_current_scene(source); + } else { + throw("requested scene does not exist"); + } +} + +/** + * Sets the scene in preview. Must be in Studio mode or will throw error + */ +void ActionsClass::SetPreviewScene(QString sceneName) +{ + if (!obs_frontend_preview_program_mode_active()) { + throw("studio mode not enabled"); + } + OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); + if (!scene) { + throw("specified scene doesn't exist"); + } + + obs_frontend_set_current_preview_scene(obs_scene_get_source(scene)); +} + +/** + * Change the active scene collection. + */ +void ActionsClass::SetCurrentSceneCollection(QString sceneCollection) +{ + if (sceneCollection.isEmpty()) { + throw("Scene Collection name is empty"); + } + + // TODO : Check if specified profile exists and if changing is allowed + obs_frontend_set_current_scene_collection(sceneCollection.toUtf8()); +} + +/** +* Reset a scene item. +*/ +void ActionsClass::ResetSceneItem(QString sceneName, QString itemName) +{ + OBSScene scene = Utils::GetSceneFromNameOrCurrent(sceneName); + if (!scene) { + throw("requested scene doesn't exist"); + } + + obs_data_t *params = obs_data_create(); + obs_data_set_string(params, "scene-name", + sceneName.toStdString().c_str()); + OBSDataItemAutoRelease itemField = obs_data_item_byname(params, "item"); + + OBSSceneItemAutoRelease sceneItem = + Utils::GetSceneItemFromRequestField(scene, itemField); + if (!sceneItem) { + throw("specified scene item doesn't exist"); + } + + OBSSource sceneItemSource = obs_sceneitem_get_source(sceneItem); + + OBSDataAutoRelease settings = obs_source_get_settings(sceneItemSource); + obs_source_update(sceneItemSource, settings); +} + +/** + * Transitions the currently previewed scene to the main output. + */ +void ActionsClass::TransitionToProgram() +{ + obs_frontend_preview_program_trigger_transition(); +} + +/** + * Transitions the currently previewed scene to the main output using specified transition. + * transitionDuration is optional. (milliseconds) + */ +void ActionsClass::TransitionToProgram(QString transitionName, + int transitionDuration) +{ + if (!obs_frontend_preview_program_mode_active()) { + throw("studio mode not enabled"); + } + + if (transitionName.isEmpty()) { + throw("transition name can not be empty"); + } + bool success = Utils::SetTransitionByName(transitionName); + if (!success) { + throw("specified transition doesn't exist"); + } + obs_frontend_set_transition_duration(transitionDuration); + + obs_frontend_preview_program_trigger_transition(); +} + +/** + * Set the active transition. + */ +void ActionsClass::SetCurrentTransition(QString name) +{ + bool success = Utils::SetTransitionByName(name); + if (!success) { + throw("requested transition does not exist"); + } +} + +/** + * Set the duration of the currently active transition + */ +void ActionsClass::SetTransitionDuration(int duration) +{ + obs_frontend_set_transition_duration(duration); +} + +void ActionsClass::SetSourceVisibility() {} //DOESNT EXIST + +void ActionsClass::ToggleSourceVisibility() {} //DOESNT EXIST + +/** +* Inverts the mute status of a specified source. +*/ +void ActionsClass::ToggleMute(QString sourceName) +{ + if (sourceName.isEmpty()) { + throw("sourceName is empty"); + } + + OBSSourceAutoRelease source = + obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + throw("sourceName not found"); + } + + obs_source_set_muted(source, !obs_source_muted(source)); +} + +/** + * Sets the mute status of a specified source. + */ +void ActionsClass::SetMute(QString sourceName, bool mute) +{ + if (sourceName.isEmpty()) { + throw("sourceName is empty"); + } + + OBSSourceAutoRelease source = + obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + throw("specified source doesn't exist"); + } + + obs_source_set_muted(source, mute); +} + +/** + * Toggle streaming on or off. + */ +void ActionsClass::StartStopStreaming() +{ + if (obs_frontend_streaming_active()) + StopStreaming(); + else + StartStreaming(); +} + +/** + * Start streaming. + */ +void ActionsClass::StartStreaming() +{ + if (obs_frontend_streaming_active() == false) { + obs_frontend_streaming_start(); + } +} + +/** + * Stop streaming. + */ +void ActionsClass::StopStreaming() +{ + if (obs_frontend_streaming_active() == true) { + obs_frontend_streaming_stop(); + } +} + +/** + * Toggle recording on or off. + */ +void ActionsClass::StartStopRecording() +{ + (obs_frontend_recording_active() ? obs_frontend_recording_stop() + : obs_frontend_recording_start()); +} + +/** + * Start recording. + */ +void ActionsClass::StartRecording() +{ + if (!obs_frontend_recording_active()) { + obs_frontend_recording_start(); + } +} + +/** + * Stop recording. + */ +void ActionsClass::StopRecording() +{ + if (obs_frontend_recording_active()) { + obs_frontend_recording_stop(); + } +} + +/** +* Pause the current recording. +*/ +void ActionsClass::PauseRecording() +{ + if (obs_frontend_recording_active()) { + obs_frontend_recording_pause(true); + } +} + +/** +* Resume/unpause the current recording (if paused). +*/ +void ActionsClass::ResumeRecording() +{ + if (obs_frontend_recording_active()) { + obs_frontend_recording_pause(false); + } +} + +/** +* Toggle the Replay Buffer on/off. +*/ +void ActionsClass::StartStopReplayBuffer() +{ + if (obs_frontend_replay_buffer_active()) { + obs_frontend_replay_buffer_stop(); + } else { + Utils::StartReplayBuffer(); + } +} + +/** +* Start recording into the Replay Buffer. +* Will throw an error if "Save Replay Buffer" hotkey is not set in OBS' settings. +* Setting this hotkey is mandatory, even when triggering saves only +* through obs-midi. +*/ +void ActionsClass::StartReplayBuffer() +{ + if (!Utils::ReplayBufferEnabled()) { + throw("replay buffer disabled in settings"); + } + + if (obs_frontend_replay_buffer_active() == false) { + Utils::StartReplayBuffer(); + } +} + +/** +* Stop recording into the Replay Buffer. +*/ +void ActionsClass::StopReplayBuffer() +{ + if (obs_frontend_replay_buffer_active() == true) { + obs_frontend_replay_buffer_stop(); + } +} + +/** +* Flush and save the contents of the Replay Buffer to disk. This is +* basically the same as triggering the "Save Replay Buffer" hotkey. +* Will return an `error` if the Replay Buffer is not active. +*/ +void ActionsClass::SaveReplayBuffer() +{ + if (!obs_frontend_replay_buffer_active()) { + throw("replay buffer not active"); + } + + OBSOutputAutoRelease replayOutput = + obs_frontend_get_replay_buffer_output(); + + calldata_t cd = {0}; + proc_handler_t *ph = obs_output_get_proc_handler(replayOutput); + proc_handler_call(ph, "save", &cd); + calldata_free(&cd); +} + +void ActionsClass::SetCurrentProfile(QString profileName) +{ + if (profileName.isEmpty()) { + throw("profile name is empty"); + } + + // TODO : check if profile exists + obs_frontend_set_current_profile(profileName.toUtf8()); +} + +void ActionsClass::SetTextGDIPlusText(QString text) {} + +void ActionsClass::SetBrowserSourceURL(QString url) { +} + +void ActionsClass::ReloadBrowserSource() +{ +} + +void ActionsClass::TakeSourceScreenshot(QString source) +{ + obs_frontend_take_source_screenshot( + obs_get_source_by_name(source.toStdString().c_str())); +} + +void ActionsClass::EnableSourceFilter() {} + +void ActionsClass::DisableSourceFilter() {} + +void ActionsClass::ToggleSourceFilter() {} + +//////////////// +// CC ACTIONS // +//////////////// + +void ActionsClass::SetVolume(QString source, float volume) +{ + OBSSourceAutoRelease obsSource = + obs_get_source_by_name(source.toUtf8()); + if (!obsSource) { + return; // source does not exist + } + + obs_source_set_volume(obsSource, volume); +} + +/** + * Set the audio sync offset of a specified source. + */ +void ActionsClass::SetSyncOffset(QString sourceName, int64_t sourceSyncOffset) +{ + if (sourceName.isEmpty()) { + throw("source name is empty"); + } + + OBSSourceAutoRelease source = + obs_get_source_by_name(sourceName.toUtf8()); + if (!source) { + throw("specified source doesn't exist"); + } + + obs_source_set_sync_offset(source, sourceSyncOffset); +} + +void ActionsClass::SetSourcePosition() {} + +void ActionsClass::SetSourceRotation() {} + +void ActionsClass::SetSourceScale() {} + +void ActionsClass::SetGainFilter() {} + +void ActionsClass::SetOpacity() {} + +void ActionsClass::do_obs_action(MidiHook *hook, int MidiVal, + ActionsClass::Actions action) +{ + switch (action) { + case ActionsClass::Actions::Set_Current_Scene: + ActionsClass::SetCurrentScene(hook->scene); + break; + case ActionsClass::Actions::Reset_Scene_Item: + ActionsClass::ResetSceneItem(hook->scene, hook->item); + break; + case ActionsClass::Actions::Toggle_Mute: + ActionsClass::ToggleMute(hook->audio_source); + break; + case ActionsClass::Actions::Do_Transition: + if (hook->transition.isEmpty()) { + ActionsClass::TransitionToProgram(); + } else if (hook->duration != -1) { + ActionsClass::TransitionToProgram(hook->transition, + hook->duration); + } else { + ActionsClass::TransitionToProgram(hook->transition); + } + break; + case ActionsClass::Actions::Set_Current_Transition: + ActionsClass::SetCurrentTransition(hook->transition); + break; + case ActionsClass::Actions::Set_Mute: + ActionsClass::SetMute(hook->audio_source, hook->bool_override); + break; + case ActionsClass::Actions::Toggle_Start_Stop_Streaming: + ActionsClass::StartStopStreaming(); + break; + case ActionsClass::Actions::Set_Preview_Scene: + ActionsClass::SetPreviewScene(hook->scene); + break; + case ActionsClass::Actions::Set_Current_Scene_Collection: + ActionsClass::SetCurrentSceneCollection(hook->scene_collection); + break; + case ActionsClass::Actions::Set_Transition_Duration: + if (hook->duration != -1) { + ActionsClass::SetTransitionDuration(hook->duration); + } else { + ActionsClass::SetTransitionDuration(MidiVal); + } + break; + case ActionsClass::Actions::Start_Streaming: + ActionsClass::StartStreaming(); + break; + case ActionsClass::Actions::Stop_Streaming: + ActionsClass::StopStreaming(); + break; + case ActionsClass::Actions::Start_Recording: + ActionsClass::StartRecording(); + break; + case ActionsClass::Actions::Stop_Recording: + ActionsClass::StopRecording(); + break; + case ActionsClass::Actions::Start_Replay_Buffer: + ActionsClass::StartReplayBuffer(); + break; + case ActionsClass::Actions::Stop_Replay_Buffer: + ActionsClass::StopReplayBuffer(); + break; + case ActionsClass::Actions::Set_Volume: + ActionsClass::SetVolume(hook->audio_source, + pow(Utils::mapper(MidiVal), 3.0)); + break; + case ActionsClass::Actions::Take_Source_Screenshot: + ActionsClass::TakeSourceScreenshot(hook->source); + break; + case ActionsClass::Actions::Pause_Recording: + ActionsClass::PauseRecording(); + break; + case ActionsClass::Actions::Enable_Source_Filter: + ActionsClass::EnableSourceFilter(); + break; + case ActionsClass::Actions::Disable_Source_Filter: + ActionsClass::DisableSourceFilter(); + break; + case ActionsClass::Actions::Toggle_Start_Stop_Recording: + ActionsClass::StartStopRecording(); + break; + case ActionsClass::Actions::Toggle_Start_Stop_Replay_Buffer: + ActionsClass::StartStopReplayBuffer(); + break; + case ActionsClass::Actions::Resume_Recording: + ActionsClass::ResumeRecording(); + break; + case ActionsClass::Actions::Save_Replay_Buffer: + ActionsClass::SaveReplayBuffer(); + break; + case ActionsClass::Actions::Set_Current_Profile: + ActionsClass::SetCurrentProfile(hook->profile); + break; + case ActionsClass::Actions::Toggle_Source_Filter: + ActionsClass::ToggleSourceFilter(); + break; + case ActionsClass::Actions::Set_Text_GDIPlus_Text: + ActionsClass::SetTextGDIPlusText(hook->string_override); + break; + case ActionsClass::Actions::Set_Browser_Source_URL: + ActionsClass::SetBrowserSourceURL(hook->string_override); + break; + case ActionsClass::Actions::Reload_Browser_Source: + ActionsClass::ReloadBrowserSource(); + break; + case ActionsClass::Actions::Set_Sync_Offset: + ActionsClass::SetSyncOffset(hook->media_source, + (int64_t)MidiVal); + break; + case ActionsClass::Actions::Set_Source_Rotation: + ActionsClass::SetSourceRotation(); + break; + case ActionsClass::Actions::Set_Source_Position: + ActionsClass::SetSourcePosition(); + break; + case ActionsClass::Actions::Set_Gain_Filter: + ActionsClass::SetGainFilter(); + break; + case ActionsClass::Actions::Set_Opacity: + ActionsClass::SetOpacity(); + break; + case ActionsClass::Actions::Set_Source_Scale: + ActionsClass::SetSourceScale(); + break; + }; +} diff --git a/src/actions.h b/src/actions.h new file mode 100644 index 00000000..2cfbb7e0 --- /dev/null +++ b/src/actions.h @@ -0,0 +1,190 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "midi-agent.h" +#include "obs-midi.h" + +class ActionsClass : public QObject { + Q_OBJECT +public: + enum class Actions { + Disable_Preview, + Disable_Source_Filter, + Enable_Preview, + Enable_Source_Filter, + Next_Media, + Pause_Recording, + Play_Pause_Media, + Previous_Media, + Reset_Scene_Item, + Reset_Stats, + Restart_Media, + Scrub_Media, + Set_Audio_Monitor_Type, + Set_Current_Scene, + Set_Current_Scene_Collection, + Set_Current_Transition, + Set_Gain_Filter, + Set_Media_Time, + Set_Mute, + Set_Preview_Scene, + Set_Scene_Item_Crop, + Set_Scene_Item_Position, + Set_Scene_Item_Render, + Set_Scene_Item_Transform, + Set_Scene_Transition_Override, + Set_Source_Filter_Visibility, + Set_Source_Name, + Set_Source_Settings, + Set_Sync_Offset, + Set_Transition_Duration, + Set_Volume, + Start_Recording, + Start_Replay_Buffer, + Start_Streaming, + Stop_Media, + Stop_Recording, + Stop_Replay_Buffer, + Stop_Streaming, + Studio_Mode, + Take_Source_Screenshot, + Toggle_Mute, + Toggle_Source_Filter, + Toggle_Start_Stop_Streaming, + Toggle_Start_Stop_Recording, + Toggle_Start_Stop_Replay_Buffer, + Do_Transition, + Unpause_Recording, + Resume_Recording, + Save_Replay_Buffer, + Set_Current_Profile, + Set_Text_GDIPlus_Text, + Set_Browser_Source_URL, + Reload_Browser_Source, + Set_Source_Scale, + Set_Source_Rotation, + Set_Source_Position, + Set_Opacity + }; + Q_ENUM(Actions) + enum class obs_event_type { + SourceVolumeChanged, + SwitchScenes, + TransitionBegin, + TransitionEnd, + SourceMuteStateChanged, + SourceRenamed, + Exiting, + SourceDestroyed, + }; + QList AllActions_raw = { + Actions::Disable_Preview, + Actions::Disable_Source_Filter, + Actions::Enable_Preview, + Actions::Enable_Source_Filter, + Actions::Next_Media, + Actions::Pause_Recording, + Actions::Play_Pause_Media, + Actions::Previous_Media, + Actions::Reset_Scene_Item, + Actions::Reset_Stats, + Actions::Restart_Media, + Actions::Set_Audio_Monitor_Type, + Actions::Set_Current_Scene, + Actions::Set_Current_Transition, + Actions::Set_Gain_Filter, + Actions::Set_Media_Time, + Actions::Set_Mute, + Actions::Set_Scene_Item_Crop, + Actions::Set_Scene_Item_Position, + Actions::Set_Scene_Item_Render, + Actions::Set_Scene_Item_Transform, + Actions::Set_Scene_Transition_Override, + Actions::Set_Source_Filter_Visibility, + Actions::Set_Source_Name, + Actions::Set_Source_Settings, + Actions::Set_Sync_Offset, + Actions::Set_Volume, + Actions::Start_Recording, + Actions::Start_Replay_Buffer, + Actions::Start_Streaming, + Actions::Stop_Media, + Actions::Stop_Recording, + Actions::Stop_Replay_Buffer, + Actions::Stop_Streaming, + Actions::Studio_Mode, + Actions::Take_Source_Screenshot, + Actions::Toggle_Mute, + Actions::Toggle_Source_Filter, + Actions::Toggle_Start_Stop_Streaming, + Actions::Do_Transition, + Actions::Unpause_Recording}; + Q_ENUM(obs_event_type) + + static QString action_to_string(const Actions &enumval); + static Actions string_to_action(const QString &string); + + static QString event_to_string(const obs_event_type &enumval); + static obs_event_type string_to_event(const QString &string); + + void SetCurrentScene(QString sceneName); + void SetPreviewScene(QString sceneName); + void SetCurrentSceneCollection(QString sceneCollection); + void ResetSceneItem(QString sceneName, QString itemName); + void TransitionToProgram(); + void TransitionToProgram(QString transitionName, + int transitionDuration = 300); + void SetCurrentTransition(QString name); + void SetTransitionDuration(int duration); // can also be used with cc + + void SetSourceVisibility(); // doesn't exist?? + void ToggleSourceVisibility(); //doesn't exist? + + void ToggleMute(QString sourceName); + void SetMute(QString sourceName, bool mute); + + void StartStopStreaming(); + void StartStreaming(); + void StopStreaming(); + + void StartStopRecording(); + void StartRecording(); + void StopRecording(); + void PauseRecording(); + void ResumeRecording(); + + void StartStopReplayBuffer(); + void StartReplayBuffer(); + void StopReplayBuffer(); + void SaveReplayBuffer(); + + void SetCurrentProfile(QString profileName); + void SetTextGDIPlusText(QString text); + void SetBrowserSourceURL(QString url); + void ReloadBrowserSource(); + void TakeSourceScreenshot(QString source); + void EnableSourceFilter(); + void DisableSourceFilter(); + void ToggleSourceFilter(); + + // CC ACTIONS + void SetVolume(QString source, float volume); + void SetSyncOffset(QString sourceName, int64_t sourceSyncOffset); + void SetSourcePosition(); + void SetSourceRotation(); + void SetSourceScale(); + void SetGainFilter(); + void SetOpacity(); + + void do_obs_action(MidiHook *hook, int MidiVal, + ActionsClass::Actions action); +};