@@ -5,18 +5,20 @@ use crate::domain::{
5
5
use crossbeam_channel:: Receiver ;
6
6
use helgoboss_learn:: OscSource ;
7
7
use reaper_high:: {
8
- ChangeDetectionMiddleware , ControlSurfaceEvent , ControlSurfaceMiddleware , FutureMiddleware ,
9
- MainTaskMiddleware , MeterMiddleware ,
8
+ ChangeDetectionMiddleware , ControlSurfaceEvent , ControlSurfaceMiddleware , FutureMiddleware , Fx ,
9
+ FxParameter , MainTaskMiddleware , MeterMiddleware ,
10
10
} ;
11
11
use reaper_rx:: ControlSurfaceRxMiddleware ;
12
12
use rosc:: { OscMessage , OscPacket } ;
13
13
14
+ use reaper_medium:: { CommandId , ReaperNormalizedFxParamValue } ;
15
+ use rxrust:: prelude:: * ;
14
16
use smallvec:: SmallVec ;
15
17
use std:: collections:: HashMap ;
16
18
17
19
type LearnSourceSender = async_channel:: Sender < ( OscDeviceId , OscSource ) > ;
18
20
19
- const OSC_BULK_SIZE : usize = 32 ;
21
+ const OSC_INCOMING_BULK_SIZE : usize = 32 ;
20
22
21
23
#[ derive( Debug ) ]
22
24
pub struct RealearnControlSurfaceMiddleware < EH : DomainEventHandler > {
@@ -26,6 +28,7 @@ pub struct RealearnControlSurfaceMiddleware<EH: DomainEventHandler> {
26
28
main_processors : Vec < MainProcessor < EH > > ,
27
29
main_task_receiver : Receiver < RealearnControlSurfaceMainTask < EH > > ,
28
30
server_task_receiver : Receiver < RealearnControlSurfaceServerTask > ,
31
+ additional_feedback_event_receiver : Receiver < AdditionalFeedbackEvent > ,
29
32
meter_middleware : MeterMiddleware ,
30
33
main_task_middleware : MainTaskMiddleware ,
31
34
future_middleware : FutureMiddleware ,
@@ -52,6 +55,22 @@ pub enum RealearnControlSurfaceMainTask<EH: DomainEventHandler> {
52
55
StopLearning ,
53
56
}
54
57
58
+ /// Not all events in REAPER are communicated via a control surface, e.g. action invocations.
59
+ #[ derive( Debug ) ]
60
+ pub enum AdditionalFeedbackEvent {
61
+ ActionInvoked ( CommandId ) ,
62
+ FxSnapshotLoaded ( Fx ) ,
63
+ /// Work around REAPER's inability to notify about parameter changes in
64
+ /// monitoring FX by simulating the notification ourselves.
65
+ /// Then parameter learning and feedback works at least for
66
+ /// ReaLearn monitoring FX instances, which is especially
67
+ /// useful for conditional activation.
68
+ RealearnMonitoringFxParameterValueChanged {
69
+ parameter : FxParameter ,
70
+ new_value : ReaperNormalizedFxParamValue ,
71
+ } ,
72
+ }
73
+
55
74
pub enum RealearnControlSurfaceServerTask {
56
75
ProvidePrometheusMetrics ( tokio:: sync:: oneshot:: Sender < String > ) ,
57
76
}
@@ -61,6 +80,7 @@ impl<EH: DomainEventHandler> RealearnControlSurfaceMiddleware<EH> {
61
80
parent_logger : & slog:: Logger ,
62
81
main_task_receiver : Receiver < RealearnControlSurfaceMainTask < EH > > ,
63
82
server_task_receiver : Receiver < RealearnControlSurfaceServerTask > ,
83
+ additional_feedback_event_receiver : Receiver < AdditionalFeedbackEvent > ,
64
84
metrics_enabled : bool ,
65
85
) -> Self {
66
86
let logger = parent_logger. new ( slog:: o!( "struct" => "RealearnControlSurfaceMiddleware" ) ) ;
@@ -71,6 +91,7 @@ impl<EH: DomainEventHandler> RealearnControlSurfaceMiddleware<EH> {
71
91
main_processors : Default :: default ( ) ,
72
92
main_task_receiver,
73
93
server_task_receiver,
94
+ additional_feedback_event_receiver,
74
95
meter_middleware : MeterMiddleware :: new ( logger. clone ( ) ) ,
75
96
main_task_middleware : MainTaskMiddleware :: new (
76
97
logger. clone ( ) ,
@@ -103,6 +124,9 @@ impl<EH: DomainEventHandler> RealearnControlSurfaceMiddleware<EH> {
103
124
104
125
pub fn reset ( & self ) {
105
126
self . change_detection_middleware . reset ( |e| {
127
+ for m in & self . main_processors {
128
+ m. process_control_surface_change_event ( & e) ;
129
+ }
106
130
self . rx_middleware . handle_change ( e) ;
107
131
} ) ;
108
132
// We don't want to execute tasks which accumulated during the "downtime" of Reaper.
@@ -149,7 +173,23 @@ impl<EH: DomainEventHandler> RealearnControlSurfaceMiddleware<EH> {
149
173
}
150
174
}
151
175
}
152
- self . process_osc ( ) ;
176
+ for event in self . additional_feedback_event_receiver . try_iter ( ) . take ( 30 ) {
177
+ if let AdditionalFeedbackEvent :: RealearnMonitoringFxParameterValueChanged {
178
+ parameter,
179
+ ..
180
+ } = & event
181
+ {
182
+ let rx = Global :: control_surface_rx ( ) ;
183
+ rx. fx_parameter_value_changed
184
+ . borrow_mut ( )
185
+ . next ( parameter. clone ( ) ) ;
186
+ rx. fx_parameter_touched . borrow_mut ( ) . next ( parameter. clone ( ) ) ;
187
+ }
188
+ for p in & mut self . main_processors {
189
+ p. process_additional_feedback_event ( & event)
190
+ }
191
+ }
192
+ self . process_incoming_osc_messages ( ) ;
153
193
match & self . state {
154
194
State :: Normal => {
155
195
for p in & mut self . main_processors {
@@ -173,12 +213,17 @@ impl<EH: DomainEventHandler> RealearnControlSurfaceMiddleware<EH> {
173
213
}
174
214
}
175
215
176
- fn process_osc ( & mut self ) {
177
- pub type PacketVec = SmallVec < [ OscPacket ; OSC_BULK_SIZE ] > ;
216
+ fn process_incoming_osc_messages ( & mut self ) {
217
+ pub type PacketVec = SmallVec < [ OscPacket ; OSC_INCOMING_BULK_SIZE ] > ;
178
218
let packets_by_device: SmallVec < [ ( OscDeviceId , PacketVec ) ; 32 ] > = self
179
219
. osc_input_devices
180
220
. iter_mut ( )
181
- . map ( |dev| ( * dev. id ( ) , dev. poll_multiple ( OSC_BULK_SIZE ) . collect ( ) ) )
221
+ . map ( |dev| {
222
+ (
223
+ * dev. id ( ) ,
224
+ dev. poll_multiple ( OSC_INCOMING_BULK_SIZE ) . collect ( ) ,
225
+ )
226
+ } )
182
227
. collect ( ) ;
183
228
for ( dev_id, packets) in packets_by_device {
184
229
match & self . state {
@@ -207,8 +252,17 @@ impl<EH: DomainEventHandler> RealearnControlSurfaceMiddleware<EH> {
207
252
self . change_detection_middleware . process ( event, |e| {
208
253
match & self . state {
209
254
State :: Normal => {
255
+ // This is for feedback processing. No Rx!
256
+ for m in & self . main_processors {
257
+ m. process_control_surface_change_event ( & e) ;
258
+ }
259
+ // The rest is only for upper layers (e.g. UI), not for processing.
210
260
self . rx_middleware . handle_change ( e. clone ( ) ) ;
211
261
if let Some ( target) = ReaperTarget :: touched_from_change_event ( e) {
262
+ // TODO-medium Now we have the necessary framework (AdditionalFeedbackEvent)
263
+ // to also support action, FX snapshot and ReaLearn monitoring FX parameter
264
+ // touching for "Last touched" target and global learning (see
265
+ // LearningTarget state)! Connect the dots!
212
266
DomainGlobal :: get ( ) . set_last_touched_target ( target) ;
213
267
for p in & self . main_processors {
214
268
p. notify_target_touched ( ) ;
0 commit comments