Skip to content

Commit

Permalink
Playtime: Display warning messages if MIDI-learn can't work
Browse files Browse the repository at this point in the history
  • Loading branch information
helgoboss committed Mar 11, 2024
1 parent c1e18d6 commit b3bc633
Show file tree
Hide file tree
Showing 16 changed files with 113 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};

#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum InfoEvent {
pub enum GlobalInfoEvent {
AutoAddedController(AutoAddedControllerEvent),
PlaytimeActivationSucceeded,
PlaytimeActivationFailed,
Expand Down
10 changes: 10 additions & 0 deletions api/src/runtime/instance_info_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use serde::{Deserialize, Serialize};

#[derive(Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum InstanceInfoEvent {
/// If attempting to MIDI-learn but the track is either not armed or the input monitoring mode
/// is not suitable.
MidiLearnFromFxInputButTrackNotArmed,
MidiLearnFromFxInputButTrackHasAudioInput,
}
7 changes: 5 additions & 2 deletions api/src/runtime/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
mod preset;
pub use preset::*;

mod info_event;
pub use info_event::*;
mod global_info_event;
pub use global_info_event::*;

mod instance_info_event;
pub use instance_info_event::*;

mod reaper;
pub use reaper::*;
Expand Down
39 changes: 30 additions & 9 deletions main/src/application/unit_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ use crate::domain::{
CompartmentSettings, CompoundMappingSource, ControlContext, ControlInput, DomainEvent,
DomainEventHandler, ExtendedProcessorContext, FeedbackAudioHookTask, FeedbackOutput,
FeedbackRealTimeTask, FinalSourceFeedbackValue, GroupId, GroupKey, IncomingCompoundSourceValue,
InfoEvent, InputDescriptor, InstanceId, LastTouchedTargetFilter, MainMapping, MappingId,
MappingKey, MappingMatchedEvent, MessageCaptureEvent, MidiControlInput, NormalMainTask,
OscFeedbackTask, ParamSetting, PluginParams, ProcessorContext, ProjectionFeedbackValue,
QualifiedMappingId, RealearnControlSurfaceMainTask, RealearnTarget, ReaperTarget,
ReaperTargetType, SharedInstance, SharedUnit, SourceFeedbackEvent,
InputDescriptor, InstanceId, InternalInfoEvent, LastTouchedTargetFilter, MainMapping,
MappingId, MappingKey, MappingMatchedEvent, MessageCaptureEvent, MidiControlInput,
NormalMainTask, OscFeedbackTask, ParamSetting, PluginParams, ProcessorContext,
ProjectionFeedbackValue, QualifiedMappingId, RealearnControlSurfaceMainTask, RealearnTarget,
ReaperTarget, ReaperTargetType, SharedInstance, SharedUnit, SourceFeedbackEvent,
StayActiveWhenProjectInBackground, Tag, TargetControlEvent, TargetTouchEvent,
TargetValueChangedEvent, Unit, UnitContainer, UnitId, VirtualControlElementId, VirtualFx,
VirtualSource, VirtualSourceValue, LUA_MIDI_SCRIPT_SOURCE_RUNTIME_NAME,
Expand All @@ -26,7 +26,7 @@ use base::{Global, NamedChannelSender, SenderToNormalThread, SenderToRealTimeThr
use derivative::Derivative;
use enum_map::EnumMap;

use reaper_high::Reaper;
use reaper_high::{Reaper, Track};
use rx_util::Notifier;
use rxrust::prelude::*;
use slog::{debug, trace};
Expand All @@ -41,7 +41,8 @@ use itertools::Itertools;
use realearn_api::persistence::{
FxDescriptor, MappingModification, TargetTouchCause, TrackDescriptor,
};
use reaper_medium::RecordingInput;
use realearn_api::runtime::{GlobalInfoEvent, InstanceInfoEvent};
use reaper_medium::{InputMonitoringMode, RecordingInput};
use std::error::Error;
use std::fmt;
use std::rc::{Rc, Weak};
Expand All @@ -58,7 +59,8 @@ pub trait SessionUi {
fn mapping_matched(&self, event: MappingMatchedEvent);
fn handle_target_control(&self, event: TargetControlEvent);
fn handle_source_feedback(&self, event: SourceFeedbackEvent);
fn handle_info_event(&self, event: &InfoEvent);
fn handle_internal_info_event(&self, event: &InternalInfoEvent);
fn handle_external_info_event(&self, event: InstanceInfoEvent);
fn handle_everything_changed(&self, unit_model: &UnitModel);
fn handle_unit_name_changed(&self);
fn handle_global_control_and_feedback_state_changed(&self);
Expand Down Expand Up @@ -1741,6 +1743,25 @@ impl UnitModel {
reenable_control_after_touched: bool,
ignore_sources: Vec<CompoundMappingSource>,
) -> Result<(), &'static str> {
// Warn if settings are not good
if self.control_input.get().is_midi_fx_input() {
if let Some(track) = self.processor_context.track() {
if !track
.recording_input()
.is_some_and(|i| matches!(i, RecordingInput::Midi { .. }))
{
self.ui().handle_external_info_event(
InstanceInfoEvent::MidiLearnFromFxInputButTrackHasAudioInput,
);
} else if !track.is_armed(false)
|| track.input_monitoring_mode() != InputMonitoringMode::Normal
{
self.ui().handle_external_info_event(
InstanceInfoEvent::MidiLearnFromFxInputButTrackNotArmed,
);
}
}
}
let allow_virtual_sources = mapping_id.compartment != CompartmentKind::Controller;
let osc_arg_index_hint = {
let mapping = self
Expand Down Expand Up @@ -2695,7 +2716,7 @@ impl DomainEventHandler for WeakUnitModel {
match event {
Info(evt) => {
let s = session.try_borrow()?;
s.ui().handle_info_event(evt);
s.ui().handle_internal_info_event(evt);
}
ConditionsChanged => {
let s = session.try_borrow()?;
Expand Down
2 changes: 1 addition & 1 deletion main/src/domain/control_surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ impl<EH: DomainEventHandler> RealearnControlSurfaceMiddleware<EH> {
helgoboss_allocator::undesired_allocation_count();
if current_undesired_allocation_count != self.last_undesired_allocation_count {
self.last_undesired_allocation_count = current_undesired_allocation_count;
let event = &crate::domain::InfoEvent::UndesiredAllocationCountChanged;
let event = &crate::domain::InternalInfoEvent::UndesiredAllocationCountChanged;
for p in self.main_processors.borrow_mut().iter_mut() {
p.process_info_event(event);
}
Expand Down
4 changes: 2 additions & 2 deletions main/src/domain/eventing.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::domain::{
CompartmentKind, CompoundMappingTarget, ControlLogContext, ControlLogEntry, FeedbackLogEntry,
InfoEvent, MappingId, MessageCaptureResult, PluginParamIndex, PluginParams,
InternalInfoEvent, MappingId, MessageCaptureResult, PluginParamIndex, PluginParams,
ProjectionFeedbackValue, QualifiedMappingId, RawParamValue,
};
use base::hash_util::NonCryptoHashSet;
Expand All @@ -22,7 +22,7 @@ pub enum DomainEvent<'a> {
},
UpdatedAllParameters(PluginParams),
TargetValueChanged(TargetValueChangedEvent<'a>),
Info(&'a InfoEvent),
Info(&'a InternalInfoEvent),
ProjectionFeedback(ProjectionFeedbackValue),
MappingMatched(MappingMatchedEvent),
HandleTargetControl(TargetControlEvent),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#[derive(Debug)]
pub enum InfoEvent {
pub enum InternalInfoEvent {
UndesiredAllocationCountChanged,
}
4 changes: 4 additions & 0 deletions main/src/domain/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ impl ControlInput {
pub fn is_midi_device(self) -> bool {
matches!(self, ControlInput::Midi(MidiControlInput::Device(_)))
}

pub fn is_midi_fx_input(self) -> bool {
matches!(self, ControlInput::Midi(MidiControlInput::FxInput))
}
}

impl Default for ControlInput {
Expand Down
4 changes: 2 additions & 2 deletions main/src/domain/main_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::domain::{
FeedbackLogEntry, FeedbackOutput, FeedbackRealTimeTask, FeedbackResolution,
FeedbackSendBehavior, FinalRealFeedbackValue, FinalSourceFeedbackValue,
GlobalControlAndFeedbackState, GroupId, HitInstructionContext, HitInstructionResponse,
InfoEvent, InstanceId, IoUpdatedEvent, KeyMessage, MainMapping, MainSourceMessage,
InstanceId, InternalInfoEvent, IoUpdatedEvent, KeyMessage, MainMapping, MainSourceMessage,
MappingActivationEffect, MappingControlResult, MappingId, MappingInfo, MessageCaptureEvent,
MessageCaptureResult, MidiControlInput, MidiDestination, MidiScanResult, NoopLogger,
NormalRealTimeTask, OrderedMappingIdSet, OrderedMappingMap, OscDeviceId, OscFeedbackTask,
Expand Down Expand Up @@ -1847,7 +1847,7 @@ impl<EH: DomainEventHandler> MainProcessor<EH> {
&& self.basics.settings.control_input == ControlInput::Osc(*device_id)
}

pub fn process_info_event(&mut self, evt: &InfoEvent) {
pub fn process_info_event(&mut self, evt: &InternalInfoEvent) {
self.basics
.event_handler
.handle_event_ignoring_error(DomainEvent::Info(evt));
Expand Down
4 changes: 2 additions & 2 deletions main/src/domain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ pub use lua_support::*;
mod lua_module_container;
pub use lua_module_container::*;

mod info_event;
pub use info_event::*;
mod internal_info_event;
pub use internal_info_event::*;

mod instance;
pub use instance::*;
Expand Down
14 changes: 8 additions & 6 deletions main/src/infrastructure/plugin/backbone_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ use realearn_api::persistence::{
FxDescriptor, MidiControllerConnection, MidiInputPort, MidiOutputPort, TargetTouchCause,
TrackDescriptor, TrackFxChain,
};
use realearn_api::runtime::{AutoAddedControllerEvent, InfoEvent};
use realearn_api::runtime::{AutoAddedControllerEvent, GlobalInfoEvent};
use reaper_high::{
ChangeEvent, CrashInfo, Fx, Guid, MiddlewareControlSurface, Project, Reaper, Track,
};
Expand Down Expand Up @@ -2608,9 +2608,9 @@ impl LicenseManagerEventHandler for BackboneLicenseManagerEventHandler {
playtime_clip_engine::ClipEngine::get().handle_changed_licenses(source.licenses());
// Send a notification to the app (if it wants to display "success")
let info_event = if success {
InfoEvent::PlaytimeActivationSucceeded
GlobalInfoEvent::PlaytimeActivationSucceeded
} else {
InfoEvent::PlaytimeActivationFailed
GlobalInfoEvent::PlaytimeActivationFailed
};
shell.proto_hub().notify_about_global_info_event(info_event);
// Give all Playtime instances a chance to load previously unloaded matrices
Expand Down Expand Up @@ -2834,9 +2834,11 @@ async fn maybe_create_controller_for_device(
.save_controller(controller)?;
BackboneShell::get()
.proto_hub()
.notify_about_global_info_event(InfoEvent::AutoAddedController(AutoAddedControllerEvent {
controller_id: outcome.id,
}));
.notify_about_global_info_event(GlobalInfoEvent::AutoAddedController(
AutoAddedControllerEvent {
controller_id: outcome.id,
},
));
Ok(())
}

Expand Down
2 changes: 1 addition & 1 deletion main/src/infrastructure/plugin/unit_shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ impl UnitShell {
);
let shared_unit_model = Rc::new(RefCell::new(unit_model));
let weak_session = Rc::downgrade(&shared_unit_model);
let unit_panel = UnitPanel::new(weak_session.clone(), instance_panel.clone());
let unit_panel = UnitPanel::new(instance_id, weak_session.clone(), instance_panel.clone());
shared_unit_model
.borrow_mut()
.set_ui(Rc::downgrade(&unit_panel));
Expand Down
10 changes: 8 additions & 2 deletions main/src/infrastructure/proto/ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ use crate::infrastructure::server::data::get_controller_routing;
use realearn_api::runtime::{ControllerPreset, LicenseInfo, MainPreset, ValidatedLicense};

impl occasional_instance_update::Update {
pub fn info_event(event: realearn_api::runtime::InstanceInfoEvent) -> Self {
let json =
serde_json::to_string(&event).expect("couldn't represent instance info event as JSON");
Self::InfoEvent(json)
}

pub fn settings(instance_shell: &InstanceShell) -> Self {
let settings = instance_shell.settings();
let json =
Expand Down Expand Up @@ -66,9 +72,9 @@ impl occasional_global_update::Update {
Self::ArrangementPlayState(ArrangementPlayState::from_engine(play_state).into())
}

pub fn info_event(event: realearn_api::runtime::InfoEvent) -> Self {
pub fn info_event(event: realearn_api::runtime::GlobalInfoEvent) -> Self {
let json =
serde_json::to_string(&event).expect("couldn't represent main info event as JSON");
serde_json::to_string(&event).expect("couldn't represent global info event as JSON");
Self::InfoEvent(json)
}

Expand Down
5 changes: 4 additions & 1 deletion main/src/infrastructure/proto/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -856,7 +856,7 @@ pub struct GetOccasionalInstanceUpdatesReply {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct OccasionalInstanceUpdate {
#[prost(oneof = "occasional_instance_update::Update", tags = "1, 2, 3")]
#[prost(oneof = "occasional_instance_update::Update", tags = "1, 2, 3, 4")]
pub update: ::core::option::Option<occasional_instance_update::Update>,
}
/// Nested message and enum types in `OccasionalInstanceUpdate`.
Expand All @@ -873,6 +873,9 @@ pub mod occasional_instance_update {
/// Everything within the instance has changed (e.g. instance data load).
#[prost(bool, tag = "3")]
EverythingHasChanged(bool),
/// Info event as JSON.
#[prost(string, tag = "4")]
InfoEvent(::prost::alloc::string::String),
}
}
#[allow(clippy::derive_partial_eq_without_eq)]
Expand Down
14 changes: 12 additions & 2 deletions main/src/infrastructure/proto/hub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::infrastructure::proto::{
OccasionalInstanceUpdate, OccasionalInstanceUpdateBatch, OccasionalUnitUpdateBatch,
ProtoRequestHandler, ProtoSenders, QualifiedOccasionalUnitUpdate,
};
use realearn_api::runtime::InfoEvent;
use realearn_api::runtime::{GlobalInfoEvent, InstanceInfoEvent};
use reaper_high::ChangeEvent;

#[derive(Debug)]
Expand Down Expand Up @@ -43,7 +43,17 @@ impl ProtoHub {
))
}

pub fn notify_about_global_info_event(&self, info_event: InfoEvent) {
pub fn notify_about_instance_info_event(
&self,
instance_id: InstanceId,
info_event: InstanceInfoEvent,
) {
self.send_occasional_instance_updates(instance_id, || {
[occasional_instance_update::Update::info_event(info_event)]
});
}

pub fn notify_about_global_info_event(&self, info_event: GlobalInfoEvent) {
self.send_occasional_global_updates(|| {
[occasional_global_update::Update::info_event(info_event)]
});
Expand Down
28 changes: 22 additions & 6 deletions main/src/infrastructure/ui/unit_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ use crate::application::{
use crate::base::when;
use crate::domain::ui_util::format_tags_as_csv;
use crate::domain::{
CompartmentKind, InfoEvent, MappingId, MappingMatchedEvent, ProjectionFeedbackValue,
QualifiedMappingId, SourceFeedbackEvent, TargetControlEvent, TargetValueChangedEvent,
CompartmentKind, InstanceId, InternalInfoEvent, MappingId, MappingMatchedEvent,
ProjectionFeedbackValue, QualifiedMappingId, SourceFeedbackEvent, TargetControlEvent,
TargetValueChangedEvent,
};
use crate::infrastructure::plugin::{update_auto_units_async, BackboneShell};
use crate::infrastructure::server::http::{
Expand All @@ -27,6 +28,8 @@ use crate::infrastructure::ui::util::{header_panel_height, parse_tags_from_csv};
use anyhow::Context;
use base::{Global, SoundPlayer};
use helgoboss_allocator::undesired_allocation_count;
use playtime_api::runtime::InfoEvent;
use realearn_api::runtime::{GlobalInfoEvent, InstanceInfoEvent};
use rxrust::prelude::*;
use std::rc::{Rc, Weak};
use swell_ui::{DialogUnits, Point, SharedView, View, ViewContext, WeakView, Window};
Expand All @@ -37,6 +40,7 @@ type _MainPanel = UnitPanel;
/// The complete ReaLearn panel containing everything.
#[derive(Debug)]
pub struct UnitPanel {
instance_id: InstanceId,
view: ViewContext,
session: WeakUnitModel,
instance_panel: WeakView<InstancePanel>,
Expand All @@ -49,13 +53,15 @@ pub struct UnitPanel {

impl UnitPanel {
pub fn new(
instance_id: InstanceId,
session: WeakUnitModel,
instance_panel: WeakView<InstancePanel>,
) -> SharedView<Self> {
let panel_manager = IndependentPanelManager::new(session.clone());
let panel_manager = Rc::new(RefCell::new(panel_manager));
let state = SharedMainState::default();
let main_panel = Self {
instance_id,
view: Default::default(),
state: state.clone(),
session: session.clone(),
Expand Down Expand Up @@ -309,17 +315,23 @@ impl UnitPanel {
self.handle_affected_own(affected);
}

fn handle_info_event(self: SharedView<Self>, event: &InfoEvent) {
fn handle_internal_info_event(self: SharedView<Self>, event: &InternalInfoEvent) {
if !self.is_open() {
return;
}
match event {
InfoEvent::UndesiredAllocationCountChanged => {
InternalInfoEvent::UndesiredAllocationCountChanged => {
self.invalidate_status_2_text();
}
}
}

fn handle_external_info_event(self: SharedView<Self>, event: InstanceInfoEvent) {
BackboneShell::get()
.proto_hub()
.notify_about_instance_info_event(self.instance_id, event);
}

fn handle_unit_name_changed(&self) {
if let Ok(shell) = self.instance_panel().shell() {
// At the time when this method is called, the unit is still borrowed, so the proto hub can't create
Expand Down Expand Up @@ -563,8 +575,12 @@ impl SessionUi for Weak<UnitPanel> {
upgrade_panel(self).handle_affected(affected, initiator);
}

fn handle_info_event(&self, event: &InfoEvent) {
upgrade_panel(self).handle_info_event(event);
fn handle_internal_info_event(&self, event: &InternalInfoEvent) {
upgrade_panel(self).handle_internal_info_event(event);
}

fn handle_external_info_event(&self, event: InstanceInfoEvent) {
upgrade_panel(self).handle_external_info_event(event);
}

fn handle_everything_changed(&self, unit_model: &UnitModel) {
Expand Down

0 comments on commit b3bc633

Please sign in to comment.