helgoboss / reaper-rs

Rust bindings for the REAPER C++ API
MIT License
78 stars 8 forks source link

Investigate "SelectProjectInstance()" for TODO's missing ability to specify ReaProject #47

Open GavinRay97 opened 3 years ago

GavinRay97 commented 3 years ago

Uncertain what this does (never seen it used in code), but if it switches the internal pointer to the current ReaProject for functions that don't take a ReaProject as a param, maybe it could work to quickly swap to another project + back to the original hackily?

https://www.reaper.fm/sdk/reascript/reascripthelp.html#SelectProjectInstance

So maybe we can fix this -> :( // TODO-low reaper::InsertTrackAtIndex unfortunately doesn't allow to specify ReaProject :(

Current impl:

// TODO-low Introduce variant that doesn't notify ControlSurface
pub fn insert_track_at(self, index: u32) -> Track {
    self.complain_if_not_available();
    // TODO-low reaper::InsertTrackAtIndex unfortunately doesn't allow to specify ReaProject :(
    let reaper = Reaper::get().medium_reaper();
    reaper.insert_track_at_index(index, TrackDefaultsBehavior::OmitDefaultEnvAndFx);
    reaper.track_list_update_all_external_surfaces();
    let media_track = reaper.get_track(Proj(self.rea_project), index).unwrap();
    Track::new(media_track, Some(self.rea_project))
}

Naive theoretical impl (if SelectProjectInstance does what I hope):

// TODO-low Introduce variant that doesn't notify ControlSurface
pub fn insert_track_at(self, index: u32, project: Project) -> Track {
    self.complain_if_not_available();

    let reaper = Reaper::get().medium_reaper();
    let current_project = Reaper::get().current_project();

    unsafe {
        reaper.low().SelectProjectInstance(project.rea_project);
    }

    reaper.insert_track_at_index(index, TrackDefaultsBehavior::OmitDefaultEnvAndFx);
    reaper.track_list_update_all_external_surfaces();
    let media_track = reaper.get_track(Proj(self.rea_project), index).unwrap();

    unsafe {
        reaper.low().SelectProjectInstance(current_project.rea_project);
    }

    Track::new(media_track, Some(self.rea_project))
}

🙂

helgoboss commented 3 years ago

Thanks Gavin! That could be a solution. Just would need to make sure that the function doesn't do anything with the GUI, otherwise this could lead to flickering.

GavinRay97 commented 3 years ago

Thanks Gavin! That could be a solution. Just would need to make sure that the function doesn't do anything with the GUI, otherwise this could lead to flickering.

I just tested, it actually does visually swap the project tabs =/

gqh7JMveN9

GavinRay97 commented 3 years ago

Spoke with @cfillion about this, he said calling reaper.PreventUIRefresh() at the beginning and end of the function will prevent the flicker! 😃

proj1, _ = reaper.EnumProjects(0)
proj2, _ = reaper.EnumProjects(1)

reaper.PreventUIRefresh(1)

reaper.SelectProjectInstance(proj2)
t = reaper.GetTrack(0, 0) -- 0 = "current project"
_, name = reaper.GetTrackName(t)
reaper.ShowConsoleMsg("Proj2 track 1 name = " .. name)
reaper.SelectProjectInstance(proj1)

reaper.PreventUIRefresh(-1)

Can probably make this a generic/higher-order function or trait, something roughly like this (excuse poor Rust): https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=67923d3fc92d3776473a9af26e6d3072

fn prevent_ui_refresh<FnType, FnReturn>(F: FnType) -> FnReturn
where
    F: Fn() -> FnReturn
{
    reaper.PreventUIRefresh(1);
    let result = f();
    reaper.PreventUIRefresh(-1);
    return result;
}

fn main() {
    let myfunc = |x: i32| -> i32 { x * x };

    let result = prevent_ui_refresh(|| {
        myfunc(5)
    });

    println!("Result is {}", result);
}

QWMfgg9V5Y