From a4bd94aec0f07234230296764259f1971691de79 Mon Sep 17 00:00:00 2001 From: Benjamin Klum Date: Mon, 26 Aug 2024 14:22:56 +0200 Subject: [PATCH] #1124 Small refactoring --- main/src/domain/real_time_processor.rs | 107 ++------------------ main/src/domain/targets/midi_send_target.rs | 99 ++++++++++++++++-- 2 files changed, 103 insertions(+), 103 deletions(-) diff --git a/main/src/domain/real_time_processor.rs b/main/src/domain/real_time_processor.rs index 9d2bd824..062bea2b 100644 --- a/main/src/domain/real_time_processor.rs +++ b/main/src/domain/real_time_processor.rs @@ -3,11 +3,11 @@ use crate::domain::{ ControlEventTimestamp, ControlLogEntry, ControlLogEntryKind, ControlMainTask, ControlMode, ControlOptions, FeedbackSendBehavior, LifecycleMidiMessage, LifecyclePhase, MappingCore, MappingId, MatchOutcome, MidiClockCalculator, MidiEvent, MidiMessageClassification, - MidiScanResult, MidiScanner, MidiSendTarget, MidiTransformationContainer, - NormalRealTimeToMainThreadTask, OrderedMappingMap, OwnedIncomingMidiMessage, - PartialControlMatch, PersistentMappingProcessingState, QualifiedMappingId, - RealTimeCompoundMappingTarget, RealTimeControlContext, RealTimeMapping, RealTimeReaperTarget, - SampleOffset, SendMidiDestination, UnitId, VirtualSourceValue, WeakRealTimeInstance, + MidiScanResult, MidiScanner, MidiTransformationContainer, NormalRealTimeToMainThreadTask, + OrderedMappingMap, OwnedIncomingMidiMessage, PartialControlMatch, + PersistentMappingProcessingState, QualifiedMappingId, RealTimeCompoundMappingTarget, + RealTimeControlContext, RealTimeMapping, RealTimeReaperTarget, SampleOffset, UnitId, + VirtualSourceValue, WeakRealTimeInstance, }; use helgoboss_learn::{ControlValue, MidiSourceValue, ModeControlResult, RawMidiEvent}; use helgoboss_midi::{ @@ -1479,8 +1479,7 @@ fn process_real_mapping_in_real_time( value: control_value, }) => { let hit_result = match reaper_target { - RealTimeReaperTarget::SendMidi(t) => real_time_target_send_midi( - t, + RealTimeReaperTarget::SendMidi(t) => t.midi_send_target_send_midi_in_rt_thread( args.caller, control_value, args.midi_feedback_output, @@ -1539,90 +1538,6 @@ fn process_real_mapping_in_real_time( false } -// TODO-high CONTINUE Also keep this more local to SendMidiTarget, just like ClipTransportTarget. -#[allow(clippy::too_many_arguments)] -fn real_time_target_send_midi( - t: &mut MidiSendTarget, - caller: Caller, - control_value: ControlValue, - midi_feedback_output: Option, - log_options: LogOptions, - main_task_sender: &SenderToNormalThread, - rt_feedback_sender: &SenderToRealTimeThread, - value_event: MidiEvent, - transformation_container: &mut Option<&mut MidiTransformationContainer>, -) -> Result<(), &'static str> { - let v = control_value.to_absolute_value()?; - // This is a type of mapping that we should process right here because we want to - // send a MIDI message and this needs to happen in the audio thread. - // Going to the main thread and back would be such a waste! - let raw_midi_event = t.pattern().to_concrete_midi_event(v); - match t.destination() { - SendMidiDestination::FxOutput | SendMidiDestination::FeedbackOutput => { - let midi_destination = match caller { - Caller::Vst(_) => match t.destination() { - SendMidiDestination::FxOutput => MidiDestination::FxOutput, - SendMidiDestination::FeedbackOutput => { - midi_feedback_output.ok_or("no feedback output set")? - } - _ => unreachable!(), - }, - Caller::AudioHook => match t.destination() { - SendMidiDestination::FxOutput => MidiDestination::FxOutput, - SendMidiDestination::FeedbackOutput => { - midi_feedback_output.ok_or("no feedback output set")? - } - _ => unreachable!(), - }, - }; - match midi_destination { - MidiDestination::FxOutput => { - match caller { - Caller::Vst(_) => { - send_raw_midi_to_fx_output( - raw_midi_event.bytes(), - value_event.offset(), - caller, - ); - } - Caller::AudioHook => { - // We can't send to FX output here directly. Need to wait until VST processing - // starts (same processing cycle). - rt_feedback_sender.send_complaining( - FeedbackRealTimeTask::NonAllocatingFxOutputFeedback(raw_midi_event), - ); - } - } - } - MidiDestination::Device(dev_id) => { - MidiOutputDevice::new(dev_id).with_midi_output( - |mo| -> Result<(), &'static str> { - let mo = mo.ok_or("couldn't open MIDI output device")?; - mo.send_msg(raw_midi_event, SendMidiTime::Instantly); - Ok(()) - }, - )?; - } - }; - } - SendMidiDestination::DeviceInput => { - if let Some(container) = transformation_container { - container.push(raw_midi_event); - } - } - } - // We end up here only if the message was successfully sent - t.set_artificial_value(v); - if log_options.output_logging_enabled { - permit_alloc(|| { - main_task_sender.send_complaining(ControlMainTask::LogTargetOutput { - event: Box::new(raw_midi_event), - }); - }); - } - Ok(()) -} - fn forward_control_to_main_processor( sender: &SenderToNormalThread, compartment: CompartmentKind, @@ -1691,7 +1606,7 @@ fn control_main_mappings_virtual( match_outcome } -fn send_raw_midi_to_fx_output(bytes: &[u8], offset: SampleOffset, caller: Caller) { +pub fn send_raw_midi_to_fx_output(bytes: &[u8], offset: SampleOffset, caller: Caller) { let host = match caller { Caller::Vst(h) => h, _ => return, @@ -1848,10 +1763,10 @@ fn flatten_control_midi_event(evt: ControlEvent>) -> Contr } #[derive(Copy, Clone)] -struct LogOptions { - virtual_input_logging_enabled: bool, - output_logging_enabled: bool, - target_control_logging_enabled: bool, +pub struct LogOptions { + pub virtual_input_logging_enabled: bool, + pub output_logging_enabled: bool, + pub target_control_logging_enabled: bool, } impl LogOptions { diff --git a/main/src/domain/targets/midi_send_target.rs b/main/src/domain/targets/midi_send_target.rs index 66a9e6a3..8a082822 100644 --- a/main/src/domain/targets/midi_send_target.rs +++ b/main/src/domain/targets/midi_send_target.rs @@ -1,14 +1,19 @@ use crate::domain::{ - CompartmentKind, ControlContext, ExtendedProcessorContext, FeedbackAudioHookTask, - FeedbackOutput, FeedbackRealTimeTask, HitResponse, MappingControlContext, MidiDestination, - RealTimeReaperTarget, RealearnTarget, ReaperTarget, ReaperTargetType, SendMidiDestination, - TargetCharacter, TargetSection, TargetTypeDef, UnresolvedReaperTargetDef, DEFAULT_TARGET, + real_time_processor, Caller, CompartmentKind, ControlContext, ControlMainTask, + ExtendedProcessorContext, FeedbackAudioHookTask, FeedbackOutput, FeedbackRealTimeTask, + HitResponse, LogOptions, MappingControlContext, MidiDestination, MidiEvent, + MidiTransformationContainer, RealTimeReaperTarget, RealearnTarget, ReaperTarget, + ReaperTargetType, SendMidiDestination, TargetCharacter, TargetSection, TargetTypeDef, + UnresolvedReaperTargetDef, DEFAULT_TARGET, }; -use base::NamedChannelSender; +use base::{NamedChannelSender, SenderToNormalThread, SenderToRealTimeThread}; use helgoboss_learn::{ create_raw_midi_events_singleton, AbsoluteValue, ControlType, ControlValue, Fraction, MidiSourceValue, RawMidiPattern, Target, UnitValue, }; +use helgobox_allocator::permit_alloc; +use reaper_high::MidiOutputDevice; +use reaper_medium::SendMidiTime; use std::convert::TryInto; #[derive(Debug)] @@ -61,8 +66,88 @@ impl MidiSendTarget { self.destination } - pub fn set_artificial_value(&mut self, value: AbsoluteValue) { - self.artificial_value = value; + pub fn midi_send_target_send_midi_in_rt_thread( + &mut self, + caller: Caller, + control_value: ControlValue, + midi_feedback_output: Option, + log_options: LogOptions, + main_task_sender: &SenderToNormalThread, + rt_feedback_sender: &SenderToRealTimeThread, + value_event: MidiEvent, + transformation_container: &mut Option<&mut MidiTransformationContainer>, + ) -> Result<(), &'static str> { + let v = control_value.to_absolute_value()?; + // This is a type of mapping that we should process right here because we want to + // send a MIDI message and this needs to happen in the audio thread. + // Going to the main thread and back would be such a waste! + let raw_midi_event = self.pattern().to_concrete_midi_event(v); + match self.destination() { + SendMidiDestination::FxOutput | SendMidiDestination::FeedbackOutput => { + let midi_destination = match caller { + Caller::Vst(_) => match self.destination() { + SendMidiDestination::FxOutput => MidiDestination::FxOutput, + SendMidiDestination::FeedbackOutput => { + midi_feedback_output.ok_or("no feedback output set")? + } + _ => unreachable!(), + }, + Caller::AudioHook => match self.destination() { + SendMidiDestination::FxOutput => MidiDestination::FxOutput, + SendMidiDestination::FeedbackOutput => { + midi_feedback_output.ok_or("no feedback output set")? + } + _ => unreachable!(), + }, + }; + match midi_destination { + MidiDestination::FxOutput => { + match caller { + Caller::Vst(_) => { + real_time_processor::send_raw_midi_to_fx_output( + raw_midi_event.bytes(), + value_event.offset(), + caller, + ); + } + Caller::AudioHook => { + // We can't send to FX output here directly. Need to wait until VST processing + // starts (same processing cycle). + rt_feedback_sender.send_complaining( + FeedbackRealTimeTask::NonAllocatingFxOutputFeedback( + raw_midi_event, + ), + ); + } + } + } + MidiDestination::Device(dev_id) => { + MidiOutputDevice::new(dev_id).with_midi_output( + |mo| -> Result<(), &'static str> { + let mo = mo.ok_or("couldn't open MIDI output device")?; + mo.send_msg(raw_midi_event, SendMidiTime::Instantly); + Ok(()) + }, + )?; + } + }; + } + SendMidiDestination::DeviceInput => { + if let Some(container) = transformation_container { + container.push(raw_midi_event); + } + } + } + // We end up here only if the message was successfully sent + self.artificial_value = v; + if log_options.output_logging_enabled { + permit_alloc(|| { + main_task_sender.send_complaining(ControlMainTask::LogTargetOutput { + event: Box::new(raw_midi_event), + }); + }); + } + Ok(()) } fn control_type_and_character_simple(&self) -> (ControlType, TargetCharacter) {