1use super::MediaTrack;
2use crate::{
3 require_non_null_panic, AutomationMode, Bpm, InputMonitoringMode, PlaybackSpeedFactor,
4 ReaperNormalizedFxParamValue, ReaperPanValue, ReaperVersion, ReaperVolumeValue,
5 TrackFxChainType, TrackFxLocation, TryFromRawError,
6};
7
8use reaper_low;
9use reaper_low::raw;
10use std::borrow::Cow;
11
12use std::ffi::CStr;
13use std::fmt::Debug;
14use std::os::raw::c_void;
15use std::panic::RefUnwindSafe;
16use std::ptr::null_mut;
17
18pub trait MediumReaperControlSurface: RefUnwindSafe + Debug {
26 fn get_type_string(&self) -> Option<Cow<'static, CStr>> {
32 None
33 }
34
35 fn get_desc_string(&self) -> Option<Cow<'static, CStr>> {
41 None
42 }
43
44 fn get_config_string(&self) -> Option<Cow<'static, CStr>> {
48 None
49 }
50
51 fn close_no_reset(&self) {}
55
56 fn run(&mut self) {}
60
61 fn set_track_list_change(&self) {}
65
66 fn set_surface_volume(&self, _args: SetSurfaceVolumeArgs) {}
68
69 fn set_surface_pan(&self, _args: SetSurfacePanArgs) {}
71
72 fn set_surface_mute(&self, _args: SetSurfaceMuteArgs) {}
74
75 fn set_surface_selected(&self, _args: SetSurfaceSelectedArgs) {}
77
78 fn set_surface_solo(&self, _args: SetSurfaceSoloArgs) {}
82
83 fn set_surface_rec_arm(&self, _args: SetSurfaceRecArmArgs) {}
85
86 fn set_play_state(&self, _args: SetPlayStateArgs) {}
88
89 fn set_repeat_state(&self, _args: SetRepeatStateArgs) {}
91
92 fn set_track_title(&self, _args: SetTrackTitleArgs) {}
94
95 fn get_touch_state(&self, _args: GetTouchStateArgs) -> bool {
96 false
97 }
98
99 fn set_auto_mode(&self, _args: SetAutoModeArgs) {}
101
102 fn reset_cached_vol_pan_states(&self) {}
104
105 fn on_track_selection(&self, _args: OnTrackSelectionArgs) {}
107
108 fn is_key_down(&self, _args: IsKeyDownArgs) -> bool {
110 false
111 }
112
113 unsafe fn extended(&self, _args: ExtendedArgs) -> i32 {
126 0
127 }
128
129 fn ext_set_input_monitor(&self, _args: ExtSetInputMonitorArgs) -> i32 {
131 0
132 }
133
134 fn ext_set_fx_param(&self, _args: ExtSetFxParamArgs) -> i32 {
139 0
140 }
141
142 fn ext_set_fx_param_rec_fx(&self, _args: ExtSetFxParamArgs) -> i32 {
146 0
147 }
148
149 fn ext_set_fx_enabled(&self, _args: ExtSetFxEnabledArgs) -> i32 {
151 0
152 }
153
154 fn ext_set_send_volume(&self, _args: ExtSetSendVolumeArgs) -> i32 {
156 0
157 }
158
159 fn ext_set_send_pan(&self, _args: ExtSetSendPanArgs) -> i32 {
161 0
162 }
163
164 fn ext_set_focused_fx(&self, _args: ExtSetFocusedFxArgs) -> i32 {
166 0
167 }
168
169 fn ext_set_last_touched_fx(&self, _args: ExtSetLastTouchedFxArgs) -> i32 {
171 0
172 }
173
174 fn ext_set_fx_open(&self, _args: ExtSetFxOpenArgs) -> i32 {
176 0
177 }
178
179 fn ext_set_fx_change(&self, _args: ExtSetFxChangeArgs) -> i32 {
181 0
182 }
183
184 fn ext_set_bpm_and_play_rate(&self, _args: ExtSetBpmAndPlayRateArgs) -> i32 {
186 0
187 }
188}
189
190#[derive(Copy, Clone, PartialEq, Debug)]
191pub struct SetSurfaceVolumeArgs {
192 pub track: MediaTrack,
193 pub volume: ReaperVolumeValue,
194}
195
196#[derive(Copy, Clone, PartialEq, Debug)]
197pub struct SetSurfacePanArgs {
198 pub track: MediaTrack,
199 pub pan: ReaperPanValue,
200}
201
202#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
203pub struct SetSurfaceMuteArgs {
204 pub track: MediaTrack,
205 pub is_mute: bool,
206}
207
208#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
209pub struct SetSurfaceSelectedArgs {
210 pub track: MediaTrack,
211 pub is_selected: bool,
212}
213
214#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
215pub struct SetSurfaceSoloArgs {
216 pub track: MediaTrack,
217 pub is_solo: bool,
218}
219
220#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
221pub struct SetSurfaceRecArmArgs {
222 pub track: MediaTrack,
223 pub is_armed: bool,
224}
225
226#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
227pub struct SetPlayStateArgs {
228 pub is_playing: bool,
229 pub is_paused: bool,
230 pub is_recording: bool,
231}
232
233#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
234pub struct SetRepeatStateArgs {
235 pub is_enabled: bool,
236}
237
238#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
239pub struct SetTrackTitleArgs<'a> {
240 pub track: MediaTrack,
241 pub name: &'a CStr,
242}
243
244#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
245pub struct GetTouchStateArgs {
246 pub track: MediaTrack,
247 pub is_pan: bool,
248}
249
250#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
251pub struct SetAutoModeArgs {
252 pub mode: AutomationMode,
253}
254
255#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
256pub struct OnTrackSelectionArgs {
257 pub track: MediaTrack,
258}
259
260#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
261pub struct IsKeyDownArgs {
262 pub key: ModKey,
263}
264
265#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
266pub struct ExtendedArgs {
267 call: i32,
269 parm_1: *mut c_void,
270 parm_2: *mut c_void,
271 parm_3: *mut c_void,
272}
273
274#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
275pub struct ExtSetInputMonitorArgs {
276 pub track: MediaTrack,
277 pub mode: InputMonitoringMode,
278}
279
280#[derive(Copy, Clone, PartialEq, Debug)]
281pub struct ExtSetFxParamArgs {
282 pub track: MediaTrack,
283 pub fx_index: u32,
284 pub param_index: u32,
285 pub param_value: ReaperNormalizedFxParamValue,
286}
287
288#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
289pub struct ExtSetFxEnabledArgs {
290 pub track: MediaTrack,
291 pub fx_location: VersionDependentTrackFxLocation,
292 pub is_enabled: bool,
293}
294
295#[derive(Copy, Clone, PartialEq, Debug)]
296pub struct ExtSetSendVolumeArgs {
297 pub track: MediaTrack,
298 pub send_index: u32,
299 pub volume: ReaperVolumeValue,
300}
301
302#[derive(Copy, Clone, PartialEq, Debug)]
303pub struct ExtSetSendPanArgs {
304 pub track: MediaTrack,
305 pub send_index: u32,
306 pub pan: ReaperPanValue,
307}
308
309#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
310pub struct ExtSetFocusedFxArgs {
311 pub fx_location: Option<QualifiedFxLocation>,
312}
313
314#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
315pub struct ExtSetLastTouchedFxArgs {
316 pub fx_location: Option<QualifiedFxLocation>,
317}
318
319#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
320pub struct ExtSetFxOpenArgs {
321 pub track: MediaTrack,
322 pub fx_location: VersionDependentTrackFxLocation,
323 pub is_open: bool,
324}
325
326#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
327pub struct ExtSetFxChangeArgs {
328 pub track: MediaTrack,
329 pub fx_chain_type: Option<TrackFxChainType>,
332}
333
334#[derive(Copy, Clone, PartialEq, Debug)]
335pub struct ExtSetBpmAndPlayRateArgs {
336 pub tempo: Option<Bpm>,
337 pub play_rate: Option<PlaybackSpeedFactor>,
338}
339
340#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
342pub enum ModKey {
343 Shift,
345 Control,
347 Menu,
349 Custom(u32),
352}
353
354impl ModKey {
355 pub fn try_from_raw(value: i32) -> Result<ModKey, TryFromRawError<i32>> {
357 if value < 0 {
358 return Err(TryFromRawError::new("couldn't convert to mod key", value));
359 };
360 let value = value as u32;
361 use ModKey::*;
362 let key = match value {
363 raw::VK_SHIFT => Shift,
364 raw::VK_CONTROL => Control,
365 raw::VK_MENU => Menu,
366 _ => Custom(value),
367 };
368 Ok(key)
369 }
370}
371
372#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
374pub struct QualifiedFxLocation {
375 pub track: MediaTrack,
377 pub fx_location: VersionDependentFxLocation,
379}
380
381#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
383pub enum VersionDependentFxLocation {
384 TakeFx {
388 item_index: u32,
390 fx_index: u32,
392 },
393 TrackFx(VersionDependentTrackFxLocation),
395}
396
397#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
399pub enum VersionDependentTrackFxLocation {
400 Old(u32),
404 New(TrackFxLocation),
408}
409
410#[derive(Debug)]
411pub(crate) struct DelegatingControlSurface {
412 delegate: Box<dyn MediumReaperControlSurface>,
413 supports_detection_of_input_fx: bool,
415 supports_detection_of_input_fx_in_set_fx_change: bool,
416}
417
418impl DelegatingControlSurface {
419 pub fn new(
420 delegate: impl MediumReaperControlSurface + 'static,
421 reaper_version: &ReaperVersion,
422 ) -> DelegatingControlSurface {
423 let reaper_version_5_95: ReaperVersion = ReaperVersion::new("5.95");
424 DelegatingControlSurface {
425 delegate: Box::new(delegate),
426 supports_detection_of_input_fx: reaper_version >= &reaper_version_5_95,
428 supports_detection_of_input_fx_in_set_fx_change: reaper_version >= &reaper_version_5_95,
430 }
431 }
432
433 unsafe fn get_as_qualified_fx_ref(
434 &self,
435 media_track_ptr: *mut c_void,
436 media_item_ptr: *mut c_void,
437 fx_index_ptr: *mut c_void,
438 ) -> Option<QualifiedFxLocation> {
439 if media_track_ptr.is_null() {
440 return None;
441 }
442 Some(QualifiedFxLocation {
443 track: require_non_null_panic(media_track_ptr as *mut raw::MediaTrack),
444 fx_location: if media_item_ptr.is_null() {
445 VersionDependentFxLocation::TrackFx(
446 self.get_as_version_dependent_track_fx_ref(fx_index_ptr),
447 )
448 } else {
449 VersionDependentFxLocation::TakeFx {
450 item_index: deref_as::<i32>(media_item_ptr).expect("media item pointer is null")
451 as u32,
452 fx_index: deref_as::<i32>(fx_index_ptr).expect("FX index pointer is null")
453 as u32,
454 }
455 },
456 })
457 }
458
459 unsafe fn get_as_version_dependent_track_fx_ref(
460 &self,
461 ptr: *mut c_void,
462 ) -> VersionDependentTrackFxLocation {
463 let fx_index = deref_as::<i32>(ptr).expect("FX index is null");
464 if self.supports_detection_of_input_fx {
465 VersionDependentTrackFxLocation::New(
466 TrackFxLocation::try_from_raw(fx_index).expect("weird FX index"),
467 )
468 } else {
469 VersionDependentTrackFxLocation::Old(fx_index as u32)
470 }
471 }
472}
473
474#[allow(non_snake_case)]
475impl reaper_low::IReaperControlSurface for DelegatingControlSurface {
476 fn GetTypeString(&self) -> *const i8 {
477 self.delegate
478 .get_type_string()
479 .map(|o| o.as_ptr())
480 .unwrap_or(null_mut())
481 }
482
483 fn GetDescString(&self) -> *const i8 {
484 self.delegate
485 .get_desc_string()
486 .map(|o| o.as_ptr())
487 .unwrap_or(null_mut())
488 }
489
490 fn GetConfigString(&self) -> *const i8 {
491 self.delegate
492 .get_config_string()
493 .map(|o| o.as_ptr())
494 .unwrap_or(null_mut())
495 }
496
497 fn CloseNoReset(&self) {
498 self.delegate.close_no_reset()
499 }
500
501 fn Run(&mut self) {
502 self.delegate.run()
503 }
504
505 fn SetTrackListChange(&self) {
506 self.delegate.set_track_list_change()
507 }
508
509 fn SetSurfaceVolume(&self, trackid: *mut raw::MediaTrack, volume: f64) {
510 self.delegate.set_surface_volume(SetSurfaceVolumeArgs {
511 track: require_non_null_panic(trackid),
512 volume: ReaperVolumeValue(volume),
513 })
514 }
515
516 fn SetSurfacePan(&self, trackid: *mut raw::MediaTrack, pan: f64) {
517 self.delegate.set_surface_pan(SetSurfacePanArgs {
518 track: require_non_null_panic(trackid),
519 pan: ReaperPanValue(pan),
520 })
521 }
522
523 fn SetSurfaceMute(&self, trackid: *mut raw::MediaTrack, mute: bool) {
524 self.delegate.set_surface_mute(SetSurfaceMuteArgs {
525 track: require_non_null_panic(trackid),
526 is_mute: mute,
527 })
528 }
529
530 fn SetSurfaceSelected(&self, trackid: *mut raw::MediaTrack, selected: bool) {
531 self.delegate.set_surface_selected(SetSurfaceSelectedArgs {
532 track: require_non_null_panic(trackid),
533 is_selected: selected,
534 })
535 }
536
537 fn SetSurfaceSolo(&self, trackid: *mut raw::MediaTrack, solo: bool) {
538 self.delegate.set_surface_solo(SetSurfaceSoloArgs {
539 track: require_non_null_panic(trackid),
540 is_solo: solo,
541 })
542 }
543
544 fn SetSurfaceRecArm(&self, trackid: *mut raw::MediaTrack, recarm: bool) {
545 self.delegate.set_surface_rec_arm(SetSurfaceRecArmArgs {
546 track: require_non_null_panic(trackid),
547 is_armed: recarm,
548 })
549 }
550
551 fn SetPlayState(&self, play: bool, pause: bool, rec: bool) {
552 self.delegate.set_play_state(SetPlayStateArgs {
553 is_playing: play,
554 is_paused: pause,
555 is_recording: rec,
556 })
557 }
558
559 fn SetRepeatState(&self, rep: bool) {
560 self.delegate
561 .set_repeat_state(SetRepeatStateArgs { is_enabled: rep })
562 }
563
564 fn SetTrackTitle(&self, trackid: *mut raw::MediaTrack, title: *const i8) {
565 self.delegate.set_track_title(SetTrackTitleArgs {
566 track: require_non_null_panic(trackid),
567 name: unsafe { CStr::from_ptr(title) },
568 })
569 }
570
571 fn GetTouchState(&self, trackid: *mut raw::MediaTrack, isPan: i32) -> bool {
572 self.delegate.get_touch_state(GetTouchStateArgs {
573 track: require_non_null_panic(trackid),
574 is_pan: isPan != 0,
575 })
576 }
577
578 fn SetAutoMode(&self, mode: i32) {
579 self.delegate.set_auto_mode(SetAutoModeArgs {
580 mode: AutomationMode::try_from_raw(mode).expect("unknown automation mode"),
581 })
582 }
583
584 fn ResetCachedVolPanStates(&self) {
585 self.delegate.reset_cached_vol_pan_states()
586 }
587
588 fn OnTrackSelection(&self, trackid: *mut raw::MediaTrack) {
589 self.delegate.on_track_selection(OnTrackSelectionArgs {
590 track: require_non_null_panic(trackid),
591 })
592 }
593
594 fn IsKeyDown(&self, key: i32) -> bool {
595 self.delegate.is_key_down(IsKeyDownArgs {
596 key: ModKey::try_from_raw(key).expect("unknown key code"),
597 })
598 }
599
600 fn Extended(
601 &self,
602 call: i32,
603 parm1: *mut c_void,
604 parm2: *mut c_void,
605 parm3: *mut c_void,
606 ) -> i32 {
607 let result = unsafe {
608 match call as u32 {
610 raw::CSURF_EXT_SETINPUTMONITOR => {
611 let recmon: i32 = deref_as(parm2).expect("recmon pointer is null");
612 self.delegate.ext_set_input_monitor(ExtSetInputMonitorArgs {
613 track: require_non_null_panic(parm1 as *mut raw::MediaTrack),
614 mode: InputMonitoringMode::try_from_raw(recmon)
615 .expect("unknown input monitoring mode"),
616 })
617 }
618 raw::CSURF_EXT_SETFXPARAM | raw::CSURF_EXT_SETFXPARAM_RECFX => {
619 let fxidx_and_paramidx: i32 =
620 deref_as(parm2).expect("fx/param index pointer is null");
621 let normalized_value: f64 = deref_as(parm3).expect("value pointer is null");
622 let fx_index = (fxidx_and_paramidx >> 16) & 0xffff;
623 let param_index = fxidx_and_paramidx & 0xffff;
624 let args = ExtSetFxParamArgs {
625 track: require_non_null_panic(parm1 as *mut raw::MediaTrack),
626 fx_index: fx_index as u32,
627 param_index: param_index as u32,
628 param_value: ReaperNormalizedFxParamValue::new(normalized_value),
629 };
630 match call as u32 {
631 raw::CSURF_EXT_SETFXPARAM => self.delegate.ext_set_fx_param(args),
632 raw::CSURF_EXT_SETFXPARAM_RECFX => {
633 self.delegate.ext_set_fx_param_rec_fx(args)
634 }
635 _ => unreachable!(),
636 }
637 }
638 raw::CSURF_EXT_SETFOCUSEDFX => {
639 self.delegate.ext_set_focused_fx(ExtSetFocusedFxArgs {
640 fx_location: self.get_as_qualified_fx_ref(parm1, parm2, parm3),
641 })
642 }
643 raw::CSURF_EXT_SETLASTTOUCHEDFX => {
644 self.delegate
645 .ext_set_last_touched_fx(ExtSetLastTouchedFxArgs {
646 fx_location: self.get_as_qualified_fx_ref(parm1, parm2, parm3),
647 })
648 }
649 raw::CSURF_EXT_SETFXOPEN => self.delegate.ext_set_fx_open(ExtSetFxOpenArgs {
650 track: require_non_null_panic(parm1 as *mut raw::MediaTrack),
651 fx_location: self.get_as_version_dependent_track_fx_ref(parm2),
652 is_open: interpret_as_bool(parm3),
653 }),
654 raw::CSURF_EXT_SETFXENABLED => {
655 if parm1.is_null() {
656 0
658 } else {
659 self.delegate.ext_set_fx_enabled(ExtSetFxEnabledArgs {
660 track: require_non_null_panic(parm1 as *mut raw::MediaTrack),
661 fx_location: self.get_as_version_dependent_track_fx_ref(parm2),
662 is_enabled: interpret_as_bool(parm3),
663 })
664 }
665 }
666 raw::CSURF_EXT_SETSENDVOLUME => {
667 self.delegate.ext_set_send_volume(ExtSetSendVolumeArgs {
668 track: require_non_null_panic(parm1 as *mut raw::MediaTrack),
669 send_index: deref_as::<i32>(parm2).expect("send index pointer is null")
670 as u32,
671 volume: deref_as(parm3).expect("volume pointer is null"),
672 })
673 }
674 raw::CSURF_EXT_SETSENDPAN => self.delegate.ext_set_send_pan(ExtSetSendPanArgs {
675 track: require_non_null_panic(parm1 as *mut raw::MediaTrack),
676 send_index: deref_as::<i32>(parm2).expect("send index pointer is null") as u32,
677 pan: deref_as(parm3).expect("pan pointer is null"),
678 }),
679 raw::CSURF_EXT_SETFXCHANGE => self.delegate.ext_set_fx_change(ExtSetFxChangeArgs {
680 track: require_non_null_panic(parm1 as *mut raw::MediaTrack),
681 fx_chain_type: {
682 if self.supports_detection_of_input_fx_in_set_fx_change {
683 let flags = parm2 as usize as u32;
684 let fx_chain_type = if (flags & 1) == 1 {
685 TrackFxChainType::InputFxChain
686 } else {
687 TrackFxChainType::NormalFxChain
688 };
689 Some(fx_chain_type)
690 } else {
691 None
692 }
693 },
694 }),
695 raw::CSURF_EXT_SETBPMANDPLAYRATE => {
696 self.delegate
697 .ext_set_bpm_and_play_rate(ExtSetBpmAndPlayRateArgs {
698 tempo: deref_as(parm1),
699 play_rate: deref_as(parm2),
700 })
701 }
702 _ => 0,
703 }
704 };
705 if result != 0 {
706 return result;
708 }
709 unsafe {
710 self.delegate.extended(ExtendedArgs {
711 call,
712 parm_1: parm1,
713 parm_2: parm2,
714 parm_3: parm3,
715 })
716 }
717 }
718}
719
720unsafe fn deref_as<T: Copy>(ptr: *mut c_void) -> Option<T> {
721 if ptr.is_null() {
722 return None;
723 }
724 let ptr = ptr as *mut T;
725 Some(*ptr)
726}
727
728unsafe fn interpret_as_bool(ptr: *mut c_void) -> bool {
729 !ptr.is_null()
730}