From 171154bddae9570b6aeb4698d75ee3465c6c89be Mon Sep 17 00:00:00 2001 From: Benjamin Klum Date: Thu, 7 Mar 2024 19:55:17 +0100 Subject: [PATCH] App: Move REAPER arrangement transport stuff to main scope - it's independent from Playtime after all - better for event handling --- main/src/domain/control_surface.rs | 4 + .../infrastructure/plugin/backbone_shell.rs | 10 +- main/src/infrastructure/proto/ext.rs | 10 +- main/src/infrastructure/proto/generated.rs | 165 +++++++++++++----- main/src/infrastructure/proto/hub.rs | 24 ++- .../infrastructure/proto/initial_events.rs | 2 + .../proto/initial_playtime_events.rs | 1 - main/src/infrastructure/proto/playtime_ext.rs | 4 - .../proto/playtime_request_handler.rs | 165 +++++++----------- .../infrastructure/proto/request_handler.rs | 38 +++- main/src/infrastructure/proto/service_impl.rs | 11 +- main/src/infrastructure/ui/app/app_library.rs | 3 + 12 files changed, 274 insertions(+), 163 deletions(-) diff --git a/main/src/domain/control_surface.rs b/main/src/domain/control_surface.rs index 7ce0cfec1..78788dd86 100644 --- a/main/src/domain/control_surface.rs +++ b/main/src/domain/control_surface.rs @@ -89,6 +89,8 @@ pub trait ControlSurfaceEventHandler: Debug { diff: &DeviceDiff, device_config_changed: bool, ); + + fn process_reaper_change_events(&self, change_events: &[ChangeEvent]); } pub enum RealearnControlSurfaceMainTask { @@ -394,6 +396,8 @@ impl RealearnControlSurfaceMiddleware { if normal_events.is_empty() && monitoring_fx_events.is_empty() { return; } + self.event_handler + .process_reaper_change_events(&normal_events); for i in self.instances() { i.borrow_mut() .process_control_surface_change_events(&normal_events); diff --git a/main/src/infrastructure/plugin/backbone_shell.rs b/main/src/infrastructure/plugin/backbone_shell.rs index c567561f9..942d0be03 100644 --- a/main/src/infrastructure/plugin/backbone_shell.rs +++ b/main/src/infrastructure/plugin/backbone_shell.rs @@ -66,7 +66,9 @@ use realearn_api::persistence::{ TrackDescriptor, TrackFxChain, }; use realearn_api::runtime::{AutoAddedControllerEvent, InfoEvent}; -use reaper_high::{CrashInfo, Fx, Guid, MiddlewareControlSurface, Project, Reaper, Track}; +use reaper_high::{ + ChangeEvent, CrashInfo, Fx, Guid, MiddlewareControlSurface, Project, Reaper, Track, +}; use reaper_low::{PluginContext, Swell}; use reaper_macros::reaper_extension_plugin; use reaper_medium::{ @@ -2531,6 +2533,12 @@ impl ControlSurfaceEventHandler for BackboneControlSurfaceEventHandler { Ok(()) }); } + + fn process_reaper_change_events(&self, change_events: &[ChangeEvent]) { + BackboneShell::get() + .proto_hub + .send_global_events_caused_by_reaper_change_events(change_events); + } } #[derive(Debug)] diff --git a/main/src/infrastructure/proto/ext.rs b/main/src/infrastructure/proto/ext.rs index 055896997..61a1e8225 100644 --- a/main/src/infrastructure/proto/ext.rs +++ b/main/src/infrastructure/proto/ext.rs @@ -1,7 +1,7 @@ use helgoboss_license_api::persistence::LicenseData; use helgoboss_license_api::runtime::License; use reaper_high::Reaper; -use reaper_medium::ReaperString; +use reaper_medium::{PlayState, ReaperString}; use std::iter; use crate::infrastructure::data::{ @@ -13,8 +13,8 @@ use crate::application::UnitModel; use crate::domain::CompartmentKind; use crate::infrastructure::proto::{ event_reply, occasional_global_update, occasional_instance_update, - qualified_occasional_unit_update, AudioInputChannel, AudioInputChannels, Compartment, - ContinuousColumnUpdate, ContinuousMatrixUpdate, GetContinuousColumnUpdatesReply, + qualified_occasional_unit_update, ArrangementPlayState, AudioInputChannel, AudioInputChannels, + Compartment, ContinuousColumnUpdate, ContinuousMatrixUpdate, GetContinuousColumnUpdatesReply, GetContinuousMatrixUpdatesReply, GetContinuousSlotUpdatesReply, GetOccasionalClipUpdatesReply, GetOccasionalColumnUpdatesReply, GetOccasionalGlobalUpdatesReply, GetOccasionalInstanceUpdatesReply, GetOccasionalMatrixUpdatesReply, @@ -62,6 +62,10 @@ impl qualified_occasional_unit_update::Update { } impl occasional_global_update::Update { + pub fn arrangement_play_state(play_state: PlayState) -> Self { + Self::ArrangementPlayState(ArrangementPlayState::from_engine(play_state).into()) + } + pub fn info_event(event: realearn_api::runtime::InfoEvent) -> Self { let json = serde_json::to_string(&event).expect("couldn't represent main info event as JSON"); diff --git a/main/src/infrastructure/proto/generated.rs b/main/src/infrastructure/proto/generated.rs index 8dbddbee2..aa0e6d41d 100644 --- a/main/src/infrastructure/proto/generated.rs +++ b/main/src/infrastructure/proto/generated.rs @@ -47,7 +47,7 @@ pub mod reply { pub struct CommandRequest { #[prost( oneof = "command_request::Value", - tags = "1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 37, 40, 25, 26, 27, 34, 28, 29, 31, 32, 33, 35, 36, 38, 39, 41, 42, 43, 44, 45" + tags = "1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 37, 40, 25, 26, 27, 34, 28, 29, 31, 32, 33, 35, 36, 38, 39, 41, 42, 43, 44, 45, 46" )] pub value: ::core::option::Option, } @@ -145,6 +145,8 @@ pub mod command_request { SaveCustomCompartmentData(super::SaveCustomCompartmentDataRequest), #[prost(message, tag = "45")] GetOccasionalUnitUpdates(super::GetOccasionalUnitUpdatesRequest), + #[prost(message, tag = "46")] + TriggerGlobal(super::TriggerGlobalRequest), } } /// Envelope for queries. @@ -465,6 +467,12 @@ pub struct DeleteControllerRequest { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct TriggerGlobalRequest { + #[prost(enumeration = "TriggerGlobalAction", tag = "1")] + pub action: i32, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct TriggerMatrixRequest { #[prost(string, tag = "1")] pub matrix_id: ::prost::alloc::string::String, @@ -1049,7 +1057,7 @@ pub struct QualifiedOccasionalTrackUpdate { pub struct OccasionalGlobalUpdate { #[prost( oneof = "occasional_global_update::Update", - tags = "1, 2, 3, 4, 5, 6, 7, 8, 9" + tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10" )] pub update: ::core::option::Option, } @@ -1085,6 +1093,9 @@ pub mod occasional_global_update { /// Contains the license state of Playtime. #[prost(message, tag = "9")] PlaytimeLicenseState(super::LicenseState), + /// Arrangement play state (= REAPER transport play state) + #[prost(enumeration = "super::ArrangementPlayState", tag = "10")] + ArrangementPlayState(i32), } } #[allow(clippy::derive_partial_eq_without_eq)] @@ -1099,7 +1110,7 @@ pub struct LicenseState { pub struct OccasionalMatrixUpdate { #[prost( oneof = "occasional_matrix_update::Update", - tags = "1, 2, 3, 4, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26" + tags = "1, 2, 3, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26" )] pub update: ::core::option::Option, } @@ -1117,9 +1128,6 @@ pub mod occasional_matrix_update { /// Matrix tempo (= REAPER master tempo) #[prost(double, tag = "3")] Tempo(f64), - /// Arrangement play state (= REAPER transport play state) - #[prost(enumeration = "super::ArrangementPlayState", tag = "4")] - ArrangementPlayState(i32), /// Complete persistent data of the matrix has changed, including topology and other settings! /// This contains the complete persistent matrix as JSON. /// @@ -1481,13 +1489,7 @@ impl MatrixVolumeKind { #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] pub enum TriggerMatrixAction { - ArrangementTogglePlayStop = 0, - StopAllClips = 1, - ArrangementPlay = 2, - ArrangementStop = 3, - ArrangementPause = 4, - ArrangementStartRecording = 5, - ArrangementStopRecording = 6, + StopAllClips = 0, Undo = 7, Redo = 8, ToggleClick = 9, @@ -1515,25 +1517,7 @@ impl TriggerMatrixAction { /// (if the ProtoBuf definition does not change) and safe for programmatic use. pub fn as_str_name(&self) -> &'static str { match self { - TriggerMatrixAction::ArrangementTogglePlayStop => { - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_TOGGLE_PLAY_STOP" - } TriggerMatrixAction::StopAllClips => "TRIGGER_MATRIX_ACTION_STOP_ALL_CLIPS", - TriggerMatrixAction::ArrangementPlay => { - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_PLAY" - } - TriggerMatrixAction::ArrangementStop => { - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_STOP" - } - TriggerMatrixAction::ArrangementPause => { - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_PAUSE" - } - TriggerMatrixAction::ArrangementStartRecording => { - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_START_RECORDING" - } - TriggerMatrixAction::ArrangementStopRecording => { - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_STOP_RECORDING" - } TriggerMatrixAction::Undo => "TRIGGER_MATRIX_ACTION_UNDO", TriggerMatrixAction::Redo => "TRIGGER_MATRIX_ACTION_REDO", TriggerMatrixAction::ToggleClick => "TRIGGER_MATRIX_ACTION_TOGGLE_CLICK", @@ -1576,19 +1560,7 @@ impl TriggerMatrixAction { /// Creates an enum from field names used in the ProtoBuf definition. pub fn from_str_name(value: &str) -> ::core::option::Option { match value { - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_TOGGLE_PLAY_STOP" => { - Some(Self::ArrangementTogglePlayStop) - } "TRIGGER_MATRIX_ACTION_STOP_ALL_CLIPS" => Some(Self::StopAllClips), - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_PLAY" => Some(Self::ArrangementPlay), - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_STOP" => Some(Self::ArrangementStop), - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_PAUSE" => Some(Self::ArrangementPause), - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_START_RECORDING" => { - Some(Self::ArrangementStartRecording) - } - "TRIGGER_MATRIX_ACTION_ARRANGEMENT_STOP_RECORDING" => { - Some(Self::ArrangementStopRecording) - } "TRIGGER_MATRIX_ACTION_UNDO" => Some(Self::Undo), "TRIGGER_MATRIX_ACTION_REDO" => Some(Self::Redo), "TRIGGER_MATRIX_ACTION_TOGGLE_CLICK" => Some(Self::ToggleClick), @@ -1626,6 +1598,62 @@ impl TriggerMatrixAction { } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] +pub enum TriggerGlobalAction { + ArrangementTogglePlayStop = 0, + ArrangementPlay = 2, + ArrangementStop = 3, + ArrangementPause = 4, + ArrangementStartRecording = 5, + ArrangementStopRecording = 6, +} +impl TriggerGlobalAction { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + TriggerGlobalAction::ArrangementTogglePlayStop => { + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_TOGGLE_PLAY_STOP" + } + TriggerGlobalAction::ArrangementPlay => { + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_PLAY" + } + TriggerGlobalAction::ArrangementStop => { + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_STOP" + } + TriggerGlobalAction::ArrangementPause => { + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_PAUSE" + } + TriggerGlobalAction::ArrangementStartRecording => { + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_START_RECORDING" + } + TriggerGlobalAction::ArrangementStopRecording => { + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_STOP_RECORDING" + } + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_TOGGLE_PLAY_STOP" => { + Some(Self::ArrangementTogglePlayStop) + } + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_PLAY" => Some(Self::ArrangementPlay), + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_STOP" => Some(Self::ArrangementStop), + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_PAUSE" => Some(Self::ArrangementPause), + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_START_RECORDING" => { + Some(Self::ArrangementStartRecording) + } + "TRIGGER_GLOBAL_ACTION_ARRANGEMENT_STOP_RECORDING" => { + Some(Self::ArrangementStopRecording) + } + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] pub enum Compartment { Controller = 0, Main = 1, @@ -2327,6 +2355,10 @@ pub mod helgobox_service_server { tonic::Status, >; /// General global commands + async fn trigger_global( + &self, + request: tonic::Request, + ) -> std::result::Result, tonic::Status>; async fn set_app_settings( &self, request: tonic::Request, @@ -3090,6 +3122,53 @@ pub mod helgobox_service_server { }; Box::pin(fut) } + "/generated.HelgoboxService/TriggerGlobal" => { + #[allow(non_camel_case_types)] + struct TriggerGlobalSvc(pub Arc); + impl< + T: HelgoboxService, + > tonic::server::UnaryService + for TriggerGlobalSvc { + type Response = super::Empty; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::trigger_global(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let inner = inner.0; + let method = TriggerGlobalSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } "/generated.HelgoboxService/SetAppSettings" => { #[allow(non_camel_case_types)] struct SetAppSettingsSvc(pub Arc); diff --git a/main/src/infrastructure/proto/hub.rs b/main/src/infrastructure/proto/hub.rs index 14ed71062..7ee55dd0e 100644 --- a/main/src/infrastructure/proto/hub.rs +++ b/main/src/infrastructure/proto/hub.rs @@ -6,13 +6,13 @@ use crate::infrastructure::data::{ use crate::infrastructure::plugin::InstanceShell; use crate::infrastructure::proto::helgobox_service_server::HelgoboxServiceServer; use crate::infrastructure::proto::{ - occasional_global_update, occasional_instance_update, qualified_occasional_unit_update, - HelgoboxServiceImpl, OccasionalGlobalUpdate, OccasionalInstanceUpdate, - OccasionalInstanceUpdateBatch, OccasionalUnitUpdateBatch, ProtoRequestHandler, ProtoSenders, - QualifiedOccasionalUnitUpdate, + occasional_global_update, occasional_instance_update, occasional_matrix_update, + qualified_occasional_unit_update, HelgoboxServiceImpl, OccasionalGlobalUpdate, + OccasionalInstanceUpdate, OccasionalInstanceUpdateBatch, OccasionalUnitUpdateBatch, + ProtoRequestHandler, ProtoSenders, QualifiedOccasionalUnitUpdate, }; -use helgoboss_license_api::runtime::License; use realearn_api::runtime::InfoEvent; +use reaper_high::ChangeEvent; #[derive(Debug)] pub struct ProtoHub { @@ -109,6 +109,17 @@ impl ProtoHub { }); } + pub fn send_global_events_caused_by_reaper_change_events(&self, change_events: &[ChangeEvent]) { + self.send_occasional_global_updates(|| { + change_events.iter().filter_map(|event| match event { + ChangeEvent::PlayStateChanged(e) => Some( + occasional_global_update::Update::arrangement_play_state(e.new_value), + ), + _ => None, + }) + }) + } + fn send_occasional_global_updates(&self, create_updates: F) where F: FnOnce() -> I, @@ -674,9 +685,6 @@ mod playtime_impl { ChangeEvent::TrackSelectedChanged(e) => { column_track_update(matrix, &e.track, || Update::selected(e.new_value)) } - ChangeEvent::PlayStateChanged(e) => Some(R::Matrix( - occasional_matrix_update::Update::arrangement_play_state(e.new_value), - )), ChangeEvent::MasterTempoChanged(e) => Some(R::Matrix( occasional_matrix_update::Update::tempo(e.new_value), )), diff --git a/main/src/infrastructure/proto/initial_events.rs b/main/src/infrastructure/proto/initial_events.rs index 7fd3b8029..a72ff8378 100644 --- a/main/src/infrastructure/proto/initial_events.rs +++ b/main/src/infrastructure/proto/initial_events.rs @@ -4,6 +4,7 @@ use crate::infrastructure::proto::{ OccasionalGlobalUpdate, OccasionalInstanceUpdate, QualifiedOccasionalUnitUpdate, }; use crate::infrastructure::server::data::get_controller_routing; +use reaper_high::Reaper; pub fn create_initial_global_updates() -> Vec { use occasional_global_update::Update; @@ -16,6 +17,7 @@ pub fn create_initial_global_updates() -> Vec { let global_updates = [ Update::midi_input_devices(), Update::midi_output_devices(), + Update::arrangement_play_state(Reaper::get().current_project().play_state()), // TODO-high-ms3 Update when changed Update::audio_input_channels(), Update::controller_presets(&BackboneShell::get().controller_preset_manager().borrow()), diff --git a/main/src/infrastructure/proto/initial_playtime_events.rs b/main/src/infrastructure/proto/initial_playtime_events.rs index ead8de599..81f9da78d 100644 --- a/main/src/infrastructure/proto/initial_playtime_events.rs +++ b/main/src/infrastructure/proto/initial_playtime_events.rs @@ -31,7 +31,6 @@ pub fn create_initial_matrix_updates(matrix: Option<&Matrix>) -> Vec Self { - Self::ArrangementPlayState(ArrangementPlayState::from_engine(play_state).into()) - } - pub fn sequencer_play_state(play_state: base::SequencerStatus) -> Self { Self::SequencerPlayState(SequencerPlayState::from_engine(play_state).into()) } diff --git a/main/src/infrastructure/proto/playtime_request_handler.rs b/main/src/infrastructure/proto/playtime_request_handler.rs index aac9d1663..924a1a471 100644 --- a/main/src/infrastructure/proto/playtime_request_handler.rs +++ b/main/src/infrastructure/proto/playtime_request_handler.rs @@ -212,111 +212,80 @@ impl PlaytimeProtoRequestHandler { } pub fn trigger_matrix(&self, req: TriggerMatrixRequest) -> Result, Status> { - let action: TriggerMatrixAction = TriggerMatrixAction::try_from(req.action) + let action = TriggerMatrixAction::try_from(req.action) .map_err(|_| Status::invalid_argument("unknown trigger matrix action"))?; if action == TriggerMatrixAction::CreateMatrix { self.create_matrix(&req.matrix_id) .map_err(|e| Status::not_found(e.to_string()))?; return Ok(Response::new(Empty {})); } - self.handle_matrix_command(&req.matrix_id, |matrix| { - let project = matrix.permanent_project().or_current_project(); - match action { - TriggerMatrixAction::CreateMatrix => { - unreachable!("matrix creation handled above") - } - TriggerMatrixAction::SequencerCleanArrangement => matrix.clean_arrangement(), - TriggerMatrixAction::SequencerWriteToArrangement => { - matrix.write_active_sequence_to_arrangement() - } - TriggerMatrixAction::ArrangementTogglePlayStop => { - if project.is_playing() { - project.stop(); - } else { - project.play(); - } - Ok(()) - } - TriggerMatrixAction::SequencerPlay => { - matrix.play_active_sequence()?; - Ok(()) - } - TriggerMatrixAction::SequencerRecord => { - matrix.record_new_sequence(); - Ok(()) - } - TriggerMatrixAction::SequencerStop => { - matrix.stop_sequencer(); - Ok(()) - } - TriggerMatrixAction::ToggleSilenceMode => { - matrix.toggle_silence_mode(); - Ok(()) - } - TriggerMatrixAction::PlayAllIgnitedClips => { - matrix.play_all_ignited(); - Ok(()) - } - TriggerMatrixAction::StopAllClips => { - matrix.stop(); - Ok(()) - } - TriggerMatrixAction::ArrangementPlay => { - project.play(); - Ok(()) - } - TriggerMatrixAction::ArrangementStop => { - project.stop(); - Ok(()) - } - TriggerMatrixAction::ArrangementPause => { - project.pause(); - Ok(()) - } - TriggerMatrixAction::ArrangementStartRecording => { - Reaper::get().enable_record_in_current_project(); - Ok(()) - } - TriggerMatrixAction::ArrangementStopRecording => { - Reaper::get().disable_record_in_current_project(); - Ok(()) - } - TriggerMatrixAction::Undo => matrix.undo(), - TriggerMatrixAction::Redo => matrix.redo(), - TriggerMatrixAction::ToggleClick => { - matrix.toggle_click(); - Ok(()) - } - TriggerMatrixAction::Panic => { - matrix.panic(); - Ok(()) - } - TriggerMatrixAction::ToggleMute => { - matrix.toggle_mute(); - Ok(()) - } - TriggerMatrixAction::ShowMasterFx => { - matrix.show_master_fx(); - Ok(()) - } - TriggerMatrixAction::ShowMasterRouting => { - matrix.show_master_routing(); - Ok(()) - } - TriggerMatrixAction::TapTempo => { - matrix.tap_tempo(); - Ok(()) - } - TriggerMatrixAction::ToggleLearnSimpleMapping => { - matrix.toggle_learn_source_by_target(SimpleMappingTarget::TriggerMatrix); - Ok(()) - } - TriggerMatrixAction::RemoveSimpleMapping => { - matrix.toggle_learn_source_by_target(SimpleMappingTarget::TriggerMatrix); - Ok(()) - } - TriggerMatrixAction::TriggerSmartRecord => matrix.trigger_smart_record(), + self.handle_matrix_command(&req.matrix_id, |matrix| match action { + TriggerMatrixAction::CreateMatrix => { + unreachable!("matrix creation handled above") + } + TriggerMatrixAction::SequencerCleanArrangement => matrix.clean_arrangement(), + TriggerMatrixAction::SequencerWriteToArrangement => { + matrix.write_active_sequence_to_arrangement() + } + TriggerMatrixAction::SequencerPlay => { + matrix.play_active_sequence()?; + Ok(()) + } + TriggerMatrixAction::SequencerRecord => { + matrix.record_new_sequence(); + Ok(()) + } + TriggerMatrixAction::SequencerStop => { + matrix.stop_sequencer(); + Ok(()) + } + TriggerMatrixAction::ToggleSilenceMode => { + matrix.toggle_silence_mode(); + Ok(()) + } + TriggerMatrixAction::PlayAllIgnitedClips => { + matrix.play_all_ignited(); + Ok(()) + } + TriggerMatrixAction::StopAllClips => { + matrix.stop(); + Ok(()) + } + TriggerMatrixAction::Undo => matrix.undo(), + TriggerMatrixAction::Redo => matrix.redo(), + TriggerMatrixAction::ToggleClick => { + matrix.toggle_click(); + Ok(()) + } + TriggerMatrixAction::Panic => { + matrix.panic(); + Ok(()) + } + TriggerMatrixAction::ToggleMute => { + matrix.toggle_mute(); + Ok(()) + } + TriggerMatrixAction::ShowMasterFx => { + matrix.show_master_fx(); + Ok(()) + } + TriggerMatrixAction::ShowMasterRouting => { + matrix.show_master_routing(); + Ok(()) + } + TriggerMatrixAction::TapTempo => { + matrix.tap_tempo(); + Ok(()) + } + TriggerMatrixAction::ToggleLearnSimpleMapping => { + matrix.toggle_learn_source_by_target(SimpleMappingTarget::TriggerMatrix); + Ok(()) + } + TriggerMatrixAction::RemoveSimpleMapping => { + matrix.toggle_learn_source_by_target(SimpleMappingTarget::TriggerMatrix); + Ok(()) } + TriggerMatrixAction::TriggerSmartRecord => matrix.trigger_smart_record(), }) } diff --git a/main/src/infrastructure/proto/request_handler.rs b/main/src/infrastructure/proto/request_handler.rs index 8742038d1..afd77d68f 100644 --- a/main/src/infrastructure/proto/request_handler.rs +++ b/main/src/infrastructure/proto/request_handler.rs @@ -12,13 +12,14 @@ use crate::infrastructure::proto::{ SetMatrixTempoRequest, SetMatrixTimeSignatureRequest, SetMatrixVolumeRequest, SetRowDataRequest, SetSequenceInfoRequest, SetTrackColorRequest, SetTrackInputMonitoringRequest, SetTrackInputRequest, SetTrackNameRequest, SetTrackPanRequest, - SetTrackVolumeRequest, TriggerClipRequest, TriggerColumnRequest, TriggerMatrixRequest, - TriggerRowRequest, TriggerSequenceRequest, TriggerSlotRequest, TriggerTrackRequest, - HOST_API_VERSION, + SetTrackVolumeRequest, TriggerClipRequest, TriggerColumnRequest, TriggerGlobalAction, + TriggerGlobalRequest, TriggerMatrixAction, TriggerMatrixRequest, TriggerRowRequest, + TriggerSequenceRequest, TriggerSlotRequest, TriggerTrackRequest, HOST_API_VERSION, }; use anyhow::Context; use base::spawn_in_main_thread; use helgoboss_license_api::persistence::LicenseKey; +use reaper_high::Reaper; use crate::domain::{CompartmentKind, UnitId}; use crate::infrastructure::api::convert::from_data; @@ -251,6 +252,37 @@ impl ProtoRequestHandler { }) } + pub fn trigger_global(&self, req: TriggerGlobalRequest) -> Result, Status> { + let action = TriggerGlobalAction::try_from(req.action) + .map_err(|_| Status::invalid_argument("unknown trigger global action"))?; + let project = Reaper::get().current_project(); + match action { + TriggerGlobalAction::ArrangementTogglePlayStop => { + if project.is_playing() { + project.stop(); + } else { + project.play(); + } + } + TriggerGlobalAction::ArrangementPlay => { + project.play(); + } + TriggerGlobalAction::ArrangementStop => { + project.stop(); + } + TriggerGlobalAction::ArrangementPause => { + project.pause(); + } + TriggerGlobalAction::ArrangementStartRecording => { + Reaper::get().enable_record_in_current_project(); + } + TriggerGlobalAction::ArrangementStopRecording => { + Reaper::get().disable_record_in_current_project(); + } + } + Ok(Response::new(Empty {})) + } + pub fn trigger_matrix(&self, req: TriggerMatrixRequest) -> Result, Status> { #[cfg(not(feature = "playtime"))] { diff --git a/main/src/infrastructure/proto/service_impl.rs b/main/src/infrastructure/proto/service_impl.rs index 7f91da284..25a9b853c 100644 --- a/main/src/infrastructure/proto/service_impl.rs +++ b/main/src/infrastructure/proto/service_impl.rs @@ -26,8 +26,8 @@ use crate::infrastructure::proto::{ SetMatrixSettingsRequest, SetMatrixTempoRequest, SetMatrixTimeSignatureRequest, SetMatrixVolumeRequest, SetRowDataRequest, SetTrackColorRequest, SetTrackInputMonitoringRequest, SetTrackInputRequest, SetTrackNameRequest, SetTrackPanRequest, - SetTrackVolumeRequest, TriggerClipRequest, TriggerColumnRequest, TriggerMatrixRequest, - TriggerRowRequest, TriggerSlotRequest, TriggerTrackRequest, + SetTrackVolumeRequest, TriggerClipRequest, TriggerColumnRequest, TriggerGlobalRequest, + TriggerMatrixRequest, TriggerRowRequest, TriggerSlotRequest, TriggerTrackRequest, }; use base::future_util; use futures::{FutureExt, Stream, StreamExt}; @@ -689,6 +689,13 @@ impl helgobox_service_server::HelgoboxService for HelgoboxServiceImpl { self.command_handler .save_custom_compartment_data(request.into_inner()) } + + async fn trigger_global( + &self, + request: Request, + ) -> Result, Status> { + self.command_handler.trigger_global(request.into_inner()) + } } type SyncBoxStream<'a, T> = Pin + Send + Sync + 'a>>; diff --git a/main/src/infrastructure/ui/app/app_library.rs b/main/src/infrastructure/ui/app/app_library.rs index d63be96c6..4da007880 100644 --- a/main/src/infrastructure/ui/app/app_library.rs +++ b/main/src/infrastructure/ui/app/app_library.rs @@ -542,6 +542,9 @@ fn process_command( TriggerMatrix(req) => { handler.trigger_matrix(req)?; } + TriggerGlobal(req) => { + handler.trigger_global(req)?; + } SetMatrixSettings(req) => { handler.set_matrix_settings(req)?; }