Skip to content

Commit

Permalink
Merge pull request #16 from helgoboss/feature/prevent-feedback-echo
Browse files Browse the repository at this point in the history
Feature/prevent feedback echo
  • Loading branch information
helgoboss authored Aug 19, 2020
2 parents 70a6b90 + 4acb28a commit 8ec431d
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 17 deletions.
6 changes: 5 additions & 1 deletion doc/user-guide.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<table class="table">
<tr>
<td>Last update of text:</td>
<td><code>2020-07-29 (v1.10.0-pre7)</code></td>
<td><code>2020-08-19 (v1.10.0-pre8)</code></td>
</tr>
<tr>
<td>Last update of relevant screenshots:</td>
Expand Down Expand Up @@ -390,6 +390,10 @@ This section provides the following mapping-related settings and functions:
combination with the search function if there are many mappings to keep track of.
- **Control enabled / Feedback enabled:** Use these checkboxes to enable/disable control and/or
feedback for this mapping.
- **Prevent echo feedback:** This checkbox mainly exists for motorized faders that don't like
getting feedback while being moved. If checked, ReaLearn won't send feedback if the target value
change was caused by incoming source events of this mapping. However, it will still send feedback
if the target value change was caused by something else, e.g. a mouse action within REAPER itself.
- **Find in mapping list:** Scrolls the mapping rows panel so that the corresponding mapping row for
this mapping gets visible.

Expand Down
6 changes: 6 additions & 0 deletions main/src/application/mapping_model_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub struct MappingModelData {
target: TargetModelData,
control_is_enabled: bool,
feedback_is_enabled: bool,
prevent_echo_feedback: bool,
}

impl Default for MappingModelData {
Expand All @@ -23,6 +24,7 @@ impl Default for MappingModelData {
target: Default::default(),
control_is_enabled: true,
feedback_is_enabled: true,
prevent_echo_feedback: false,
}
}
}
Expand All @@ -36,6 +38,7 @@ impl MappingModelData {
target: TargetModelData::from_model(&model.target_model, context),
control_is_enabled: model.control_is_enabled.get(),
feedback_is_enabled: model.feedback_is_enabled.get(),
prevent_echo_feedback: model.prevent_echo_feedback.get(),
}
}

Expand All @@ -57,5 +60,8 @@ impl MappingModelData {
model
.feedback_is_enabled
.set_without_notification(self.feedback_is_enabled);
model
.prevent_echo_feedback
.set_without_notification(self.prevent_echo_feedback);
}
}
2 changes: 1 addition & 1 deletion main/src/application/target_model_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ impl TargetModelData {
"Track not found by GUID {} and name {}, falling back to <This>",
guid.to_string_with_braces(),
name.map(|n| format!("\"{}\"", n))
.unwrap_or("-".to_string())
.unwrap_or_else(|| "-".to_string())
)),
}
VirtualTrack::This
Expand Down
2 changes: 1 addition & 1 deletion main/src/domain/main_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ impl ControlSurface for MainProcessor {
}
}
}
// Process feedback tasks
// Process control tasks
let control_tasks: SmallVec<[ControlMainTask; CONTROL_TASK_BULK_SIZE]> = self
.control_task_receiver
.try_iter()
Expand Down
28 changes: 24 additions & 4 deletions main/src/domain/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::domain::{MainProcessorTargetUpdate, Mode, ReaperTarget};
use helgoboss_learn::{ControlValue, MidiSource, MidiSourceValue, Target};
use helgoboss_midi::RawShortMessage;

use std::time::{Duration, Instant};
use uuid::Uuid;

#[derive(Debug)]
Expand All @@ -12,6 +13,7 @@ pub struct ProcessorMapping {
target: Option<ReaperTarget>,
control_is_enabled: bool,
feedback_is_enabled: bool,
prevent_echo_feedback: bool,
}

impl ProcessorMapping {
Expand All @@ -22,6 +24,7 @@ impl ProcessorMapping {
target: Option<ReaperTarget>,
control_is_enabled: bool,
feedback_is_enabled: bool,
prevent_echo_feedback: bool,
) -> ProcessorMapping {
ProcessorMapping {
id,
Expand All @@ -30,6 +33,7 @@ impl ProcessorMapping {
target,
control_is_enabled,
feedback_is_enabled,
prevent_echo_feedback,
}
}

Expand All @@ -46,6 +50,7 @@ impl ProcessorMapping {
self.target.clone(),
self.control_is_enabled,
self.feedback_is_enabled && feedback_is_globally_enabled,
self.prevent_echo_feedback,
);
(real_time_mapping, main_mapping)
}
Expand Down Expand Up @@ -109,6 +114,8 @@ impl RealTimeProcessorMapping {
}
}

const MAX_ECHO_FEEDBACK_DELAY: Duration = Duration::from_millis(20);

#[derive(Debug)]
pub struct MainProcessorMapping {
id: MappingId,
Expand All @@ -117,6 +124,8 @@ pub struct MainProcessorMapping {
target: Option<ReaperTarget>,
control_is_enabled: bool,
feedback_is_enabled: bool,
prevent_echo_feedback: bool,
time_of_last_control: Option<Instant>,
}

impl MainProcessorMapping {
Expand All @@ -125,16 +134,19 @@ impl MainProcessorMapping {
source: MidiSource,
mode: Mode,
target: Option<ReaperTarget>,
control: bool,
feedback: bool,
control_is_enabled: bool,
feedback_is_enabled: bool,
prevent_echo_feedback: bool,
) -> MainProcessorMapping {
MainProcessorMapping {
id,
source,
mode,
target,
control_is_enabled: control,
feedback_is_enabled: feedback,
control_is_enabled,
feedback_is_enabled,
prevent_echo_feedback,
time_of_last_control: None,
}
}

Expand Down Expand Up @@ -174,6 +186,9 @@ impl MainProcessorMapping {
Some(t) => t,
};
if let Some(final_value) = self.mode.control(value, target) {
if self.prevent_echo_feedback {
self.time_of_last_control = Some(Instant::now());
}
target.control(final_value).unwrap();
}
}
Expand All @@ -182,6 +197,11 @@ impl MainProcessorMapping {
if !self.feedback_is_enabled {
return None;
}
if let Some(t) = self.time_of_last_control {
if t.elapsed() <= MAX_ECHO_FEEDBACK_DELAY {
return None;
}
}
let target = match &self.target {
None => return None,
Some(t) => t,
Expand Down
5 changes: 5 additions & 0 deletions main/src/domain/mapping_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub struct MappingModel {
pub name: Prop<String>,
pub control_is_enabled: Prop<bool>,
pub feedback_is_enabled: Prop<bool>,
pub prevent_echo_feedback: Prop<bool>,
pub source_model: MidiSourceModel,
pub mode_model: ModeModel,
pub target_model: TargetModel,
Expand All @@ -27,6 +28,7 @@ impl Clone for MappingModel {
name: self.name.clone(),
control_is_enabled: self.control_is_enabled.clone(),
feedback_is_enabled: self.feedback_is_enabled.clone(),
prevent_echo_feedback: self.prevent_echo_feedback.clone(),
source_model: self.source_model.clone(),
mode_model: self.mode_model.clone(),
target_model: self.target_model.clone(),
Expand All @@ -41,6 +43,7 @@ impl Default for MappingModel {
name: Default::default(),
control_is_enabled: prop(true),
feedback_is_enabled: prop(true),
prevent_echo_feedback: prop(false),
source_model: Default::default(),
mode_model: Default::default(),
target_model: Default::default(),
Expand Down Expand Up @@ -107,6 +110,7 @@ impl MappingModel {
.merge(self.target_model.changed())
.merge(self.control_is_enabled.changed())
.merge(self.feedback_is_enabled.changed())
.merge(self.prevent_echo_feedback.changed())
}
}

Expand All @@ -133,6 +137,7 @@ impl<'a> MappingModelWithContext<'a> {
target,
self.mapping.control_is_enabled.get() && target_conditions_are_met,
self.mapping.feedback_is_enabled.get() && target_conditions_are_met,
self.mapping.prevent_echo_feedback.get(),
)
}

Expand Down
1 change: 1 addition & 0 deletions main/src/infrastructure/common/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ pub mod root {
pub const ID_SOURCE_MIDI_MESSAGE_TYPE_LABEL_TEXT: u32 = 40074;
pub const ID_OK: u32 = 40075;
pub const ID_TARGET_ACTION_LABEL_TEXT: u32 = 40076;
pub const ID_MAPPING_PREVENT_ECHO_FEEDBACK_CHECK_BOX: u32 = 40078;
pub const ID_CMD: u32 = 236;
pub const ID_PSRESTARTWINDOWS: u32 = 2;
pub const ID_PSREBOOTSYSTEM: u32 = 3;
Expand Down
3 changes: 2 additions & 1 deletion main/src/infrastructure/common/realearn.rc
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,15 @@ BEGIN
AUTOCHECKBOX "Slowly approach if jump too big", ID_SETTINGS_SCALE_MODE_CHECK_BOX, 222, 317, 113, 8, 0, WS_EX_LEFT
AUTOCHECKBOX "=> Control enabled", ID_MAPPING_CONTROL_ENABLED_CHECK_BOX, 255, 18, 75, 8, 0, WS_EX_LEFT
AUTOCHECKBOX "<= Feedback enabled", ID_MAPPING_FEEDBACK_ENABLED_CHECK_BOX, 255, 33, 85, 8, 0, WS_EX_LEFT
DEFPUSHBUTTON "Find in mapping list", ID_MAPPING_FIND_IN_LIST_BUTTON, 354, 23, 82, 14, 0, WS_EX_LEFT
DEFPUSHBUTTON "Find in mapping list", ID_MAPPING_FIND_IN_LIST_BUTTON, 347, 30, 88, 14, 0, WS_EX_LEFT
EDITTEXT ID_MAPPING_NAME_EDIT_CONTROL, 39, 23, 198, 14, ES_AUTOHSCROLL | ES_MULTILINE, WS_EX_LEFT
LTEXT "Name", 0, 11, 26, 20, 9, SS_LEFT, WS_EX_LEFT
COMBOBOX ID_SOURCE_MIDI_CLOCK_TRANSPORT_MESSAGE_TYPE_COMBOX_BOX, 48, 96, 120, 15, CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_LEFT
LTEXT "Message", ID_SOURCE_MIDI_MESSAGE_TYPE_LABEL_TEXT, 11, 99, 30, 9, SS_LEFT, WS_EX_LEFT
AUTOCHECKBOX "Rotate", ID_SETTINGS_ROTATE_CHECK_BOX, 124, 317, 37, 8, 0, WS_EX_LEFT
PUSHBUTTON "Pick", ID_TARGET_PICK_ACTION_BUTTON, 410, 95, 26, 14, 0, WS_EX_LEFT
LTEXT "Action name", ID_TARGET_ACTION_LABEL_TEXT, 220, 99, 189, 9, SS_LEFT, WS_EX_LEFT
AUTOCHECKBOX "Prevent echo feedback", ID_MAPPING_PREVENT_ECHO_FEEDBACK_CHECK_BOX, 348, 18, 91, 8, 0, WS_EX_LEFT
END


Expand Down
1 change: 1 addition & 0 deletions main/src/infrastructure/common/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,4 @@
#define ID_SOURCE_MIDI_MESSAGE_TYPE_LABEL_TEXT 40074
#define ID_OK 40075
#define ID_TARGET_ACTION_LABEL_TEXT 40076
#define ID_MAPPING_PREVENT_ECHO_FEEDBACK_CHECK_BOX 40078
23 changes: 23 additions & 0 deletions main/src/infrastructure/ui/mapping_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,14 @@ impl<'a> MutableMappingPanel<'a> {
);
}

fn update_mapping_prevent_echo_feedback(&mut self) {
self.mapping.prevent_echo_feedback.set(
self.view
.require_control(root::ID_MAPPING_PREVENT_ECHO_FEEDBACK_CHECK_BOX)
.is_checked(),
);
}

fn update_mapping_name(&mut self) -> Result<(), &'static str> {
let value = self
.view
Expand Down Expand Up @@ -866,6 +874,7 @@ impl<'a> ImmutableMappingPanel<'a> {
self.invalidate_mapping_name_edit_control();
self.invalidate_mapping_control_enabled_check_box();
self.invalidate_mapping_feedback_enabled_check_box();
self.invalidate_mapping_prevent_echo_feedback_check_box();
self.invalidate_source_controls();
self.invalidate_target_controls();
self.invalidate_mode_controls();
Expand Down Expand Up @@ -907,6 +916,13 @@ impl<'a> ImmutableMappingPanel<'a> {
cb.set_text(format!("{} Feedback enabled", symbols::ARROW_LEFT_SYMBOL));
}

fn invalidate_mapping_prevent_echo_feedback_check_box(&self) {
let cb = self
.view
.require_control(root::ID_MAPPING_PREVENT_ECHO_FEEDBACK_CHECK_BOX);
cb.set_checked(self.mapping.prevent_echo_feedback.get());
}

fn invalidate_source_controls(&self) {
self.invalidate_source_control_appearance();
self.invalidate_source_type_combo_box();
Expand Down Expand Up @@ -1423,6 +1439,10 @@ impl<'a> ImmutableMappingPanel<'a> {
.when_do_sync(self.mapping.feedback_is_enabled.changed(), |view| {
view.invalidate_mapping_feedback_enabled_check_box();
});
self.panel
.when_do_sync(self.mapping.prevent_echo_feedback.changed(), |view| {
view.invalidate_mapping_prevent_echo_feedback_check_box();
});
}

fn register_source_listeners(&self) {
Expand Down Expand Up @@ -2072,6 +2092,9 @@ impl View for MappingPanel {
ID_MAPPING_FEEDBACK_ENABLED_CHECK_BOX => {
self.write(|p| p.update_mapping_feedback_enabled())
}
ID_MAPPING_PREVENT_ECHO_FEEDBACK_CHECK_BOX => {
self.write(|p| p.update_mapping_prevent_echo_feedback())
}
ID_MAPPING_FIND_IN_LIST_BUTTON => {
self.scroll_to_mapping_in_main_panel();
}
Expand Down
18 changes: 9 additions & 9 deletions test/manual/faderport-classic.rpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<REAPER_PROJECT 0.1 "6.12+dev0617/x64" 1597011598
<REAPER_PROJECT 0.1 "6.12+dev0617/x64" 1597042320
RIPPLE 0
GROUPOVERRIDE 0 0 0
AUTOXFADE 1
Expand All @@ -14,8 +14,8 @@
TIMEMODE 1 5 -1 30 0 0 -1
VIDEO_CONFIG 0 0 256
PANMODE 3
CURSOR 0
ZOOM 26.28985354708216 0 0
CURSOR 1
ZOOM 26.28985354708216 478 0
VZOOMEX 6 0
USE_REC_CFG 0
RECMODE 1
Expand Down Expand Up @@ -99,7 +99,7 @@
SHOWINMIX 1 0.6667 0.5 1 0.5 0 0 0
FREEMODE 0
SEL 0
REC 1 5088 1 0 0 0 0
REC 1 -1 1 0 0 0 0
VU 2
TRACKHEIGHT 0 0 0
INQ 0 0 0 0.5 100 0 0 100
Expand All @@ -117,7 +117,7 @@
BYPASS 0 0 0
<VST "VST: ReaControlMIDI (Cockos)" reacontrolmidi.dll 0 "" 1919118692<56535472636D64726561636F6E74726F> ""
ZG1jcu5e7f4AAAAAAAAAAMoAAAABAAAAAAAQAA==
/////wAAAAAAAAAAAAAAAAIAAAAiAAAAAQAAAJAoAAAQAAAAACAAAAAAAAAZAAAAQzpcUkVBUEVSXERhdGFcR00ucmVhYmFuawAAAAABAAAAAQAAAAEAAAAAAAAAAAAA
/////wAAAAAAAAAAAAAAAAIAAAAiAAAAAQAAAJAkAAAQAAAAACAAAAAAAAAZAAAAQzpcUkVBUEVSXERhdGFcR00ucmVhYmFuawAAAAABAAAAAQAAAAEAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAE1ham9yAA0AAAAxMDIwMzQwNTA2MDcAAQAAAAABAAAAAQAAAAMAAAAjAAAAAQAAAAAA
AAAAAAAAAAAAAA==
AAAQAAAA
Expand Down Expand Up @@ -171,7 +171,7 @@
PEAKCOL 18238411
BEAT -1
AUTOMODE 0
VOLPAN 1.69422918866281 -0.936 -1 -1 1
VOLPAN 0.78304335735222 -0.936 -1 -1 1
MUTESOLO 0 0 0
IPHASE 0
PLAYOFFS 0 1
Expand All @@ -192,8 +192,8 @@
MAINSEND 1 0
<FXCHAIN
WNDRECT 720 416 1796 1500
SHOW 0
LASTSEL 0
SHOW 2
LASTSEL 1
DOCKED 0
BYPASS 0 0 0
<VST "VSTi: ReaLearn (Helgoboss)" ReaLearn.dll 0 "" 1751282284<5653546862726C7265616C6561726E00> ""
Expand Down Expand Up @@ -232,7 +232,7 @@
AAAAAAAAAAAAAA==
AAAQAAAA
>
FLOATPOS 0 0 0 0
FLOAT 2228 513 1341 1163
FXID {67786436-D98E-429A-8935-061C5F7DF9AA}
WAK 0 0
>
Expand Down

0 comments on commit 8ec431d

Please sign in to comment.