Closed helgoboss closed 1 year ago
The test actually succeeds (there's some non-deterministic issue with the integration test execution on Linux).
Sorry that it took so long, I finally got around to trying this branch in my VST. I'm calling it like this:
if let TypeSpecificPluginContext::Vst(ctx) =
reaper.plugin_context().type_specific()
{
unsafe {
let mut my_track: NonNull<MediaTrack> = ctx
.request_containing_track(
NonNull::new(host.raw_effect()).expect("NonNull"),
)
.expect("track fx");
// clear track before inserting midi file
for i in 0 .. reaper
.low()
.CountTrackMediaItems(my_track.as_mut())
{
let media_item = reaper
.low()
.GetTrackMediaItem(my_track.as_mut(), i);
let res = reaper.low().DeleteTrackMediaItem(
my_track.as_mut(),
media_item,
);
info!("DeleteTrackMediaItem: {:?}", res);
}
let track_idx = (0 .. reaper
.count_tracks(ProjectContext::CurrentProject))
.find(|&track_idx| {
reaper
.get_track(
ProjectContext::CurrentProject,
track_idx,
)
.expect("get_track") == my_track
})
.expect("track_idx");
// insert midi file
let _ = reaper
.insert_media(
&midi_path,
InsertMediaMode::AddToTrackAtIndex(track_idx),
InsertMediaFlag::TryToMatchTempo1X
| InsertMediaFlag::StretchLoopToFitTimeSelection
| InsertMediaFlag::ForceLoopRegardlessOfGlobalPreference,
)
.map_err(|e| error!("insert_media: {}", e));
}
}
But I ran into some issues:
Even though I pass the right track_idx
and file path, it's not inserting the file as midi item on the track: Instead, this call creates a new track but the midi item does not appear on it. What's noteworthy though: The new track is named after the midi file, just like when dragging a midi file from explorer into Reaper. (The Reaper track name is taken from the name of the first named track inside the multitrack midi file.)
So it is reading the midi file! And when I try to rename the midi file while this Reaper session is open, Explorer complains that the file is locked in Reaper. So it's also locking it.
(Which is bad for my use case of continuously re-generating this midi file from my livecoding application. Any idea how to make it not lock it? Btw, if I drop a midi file from Explorer into Reaper, it doesn't lock it!)
And any idea why the midi file doesn't appear as a media item on the track? (The track is empty afterwards.)
(Btw, track_idx
is correct, it's removing the media items of the correct track, and DeleteTrackMediaItem
returns true
.)
Another issue I encountered: I use the hotwatch
crate to watch the midi file to auto-reload it whenever it changes and I get this panic:
thread 'unnamed' panicked at 'called main-thread-only function from wrong thread': C:\Users\me.cargo\git\checkouts\reaper-rs-d32fed07113e6874\5eee308\main\medium\src\reaper.rs:4020
But it's not being called from the audio thread, but from the newly spawned background watcher thread.
(hotwatch
also has a blocking API but I can't use that because I need to be able to terminate the watcher thread in the Drop
impl of my plugin, which isn't possible with hotwatch's blocking API.)
When I was using my own Reaper API bindings, I never had problems calling them from a new thread.
So with reaper-rs, shouldn't it also be allowed to call these functions from any non-audio thread? :)
But I ran into some issues: Even though I pass the right
track_idx
and file path, it's not inserting the file as midi item on the track: Instead, this call creates a new track but the midi item does not appear on it. What's noteworthy though: The new track is named after the midi file, just like when dragging a midi file from explorer into Reaper. (The Reaper track name is taken from the name of the first named track inside the multitrack midi file.) So it is reading the midi file! And when I try to rename the midi file while this Reaper session is open, Explorer complains that the file is locked in Reaper. So it's also locking it. (Which is bad for my use case of continuously re-generating this midi file from my livecoding application. Any idea how to make it not lock it? Btw, if I drop a midi file from Explorer into Reaper, it doesn't lock it!) And any idea why the midi file doesn't appear as a media item on the track? (The track is empty afterwards.) (Btw,track_idx
is correct, it's removing the media items of the correct track, andDeleteTrackMediaItem
returnstrue
.)
This phenomena is probably more related to the way this particular REAPER function works, not reaper-rs. I would like to give you more hints but I'm not familiar with this function. I think it's best if you experiment a bit with this in ReaScript/Lua first to get a feeling how the function works. That's what I always do if I want to get to know a REAPER function (since most of them are not well documented). If this doesn't get you further, writing Justin an email is usually the way to go.
And if you found the answer, adding some reaper-rs method documentation would be awesome :)
Another issue I encountered: I use the
hotwatch
crate to watch the midi file to auto-reload it whenever it changes and I get this panic:thread 'unnamed' panicked at 'called main-thread-only function from wrong thread': C:\Users\me.cargo\git\checkouts\reaper-rs-d32fed07113e6874\5eee308\main\medium\src\reaper.rs:4020
But it's not being called from the audio thread, but from the newly spawned background watcher thread. (
hotwatch
also has a blocking API but I can't use that because I need to be able to terminate the watcher thread in theDrop
impl of my plugin, which isn't possible with hotwatch's blocking API.) When I was using my own Reaper API bindings, I never had problems calling them from a new thread. So with reaper-rs, shouldn't it also be allowed to call these functions from any non-audio thread? :)
Most REAPER functions must only be called from the main thread, otherwise it's undefined behavior and can even lead to crashes. There are some functions which may be called from other threads as well but it's the exception (and inserting a media item is most likely not one of these exceptions). reaper-rs embraces safety and therefore checks proactively if it's the correct thread - in order to spare you from situations in which everything works on your machine, but only most of the time and maybe not on other machines. Happened to me a lot!
The usual way I approach this is to schedule something for execution on the main thread (by sending closures via channel to a ControlSurface
and executing them in the run
method). This is something you need for sure when writing multi-threaded REAPER plug-ins. In ReaLearn I have channels all over the place.
This phenomena is probably more related to the way this particular REAPER function works, not reaper-rs. I would like to give you more hints but I'm not familiar with this function. I think it's best if you experiment a bit with this in ReaScript/Lua first to get a feeling how the function works. That's what I always do if I want to get to know a REAPER function (since most of them are not well documented).
I tested InsertMedia
from lua and it worked, but it always opens this popup:
I didn't find a way to suppress this popup.. But in the manual of this tool it says:
Which I thought implies that there is a way to suppress this popup when calling InsertMedia
(used for "MIDI CH mode").
But it seems to always call it with 1
as flag arg (insert on new track), without any other flags:
https://github.com/daniellumertz/DanielLumertz-Scripts/blob/f3bffa2bfd07a075ea0c4c7a678acb9d19b7975f/MIDI%20Transfer/main.lua#L208
But I found something useful in that manual:
and
So it seems that's the only solution, to not permanently lock the inserted midi file. Then, when Reaper's window is not focused and it's not playing, it will unlock the midi file, which allows my livecoding application to replace the file without this popup showing up. But it's still not optimal, because the midi file can't be replaced while Reaper is playing when it's not focused..
Maybe I can get InsertMedia
to work such that it behaves like drag&drop (so that it doesn't lock the file, with the above setting set to "Import midi files as midi items (in project)").
But then I'd have to find a way to suppress that popup. Do you know if there's a way (maybe through a hidden setting)? :)
If this doesn't get you further, writing Justin an email is usually the way to go.
You mean sending to support@cockos.com
? I tried that a while ago but never got a response. Or do you mean a different email address?
The usual way I approach this is to schedule something for execution on the main thread (by sending closures via channel to a ControlSurface and executing them in the run method). This is something you need for sure when writing multi-threaded REAPER plug-ins. In ReaLearn I have channels all over the place.
Thanks, I'll try that. Btw, is there any way to run things periodically on the main thread without having a control surface?
(We used to have Plugin::idle()
in vst-rs
but removed it because the opcode was deprecated and wasn't being called by all hosts. And Editor::idle()
depends on having a GUI.)
This phenomena is probably more related to the way this particular REAPER function works, not reaper-rs. I would like to give you more hints but I'm not familiar with this function. I think it's best if you experiment a bit with this in ReaScript/Lua first to get a feeling how the function works. That's what I always do if I want to get to know a REAPER function (since most of them are not well documented).
I tested
InsertMedia
from lua and it worked, but it always opens this popup:I didn't find a way to suppress this popup.. But in the manual of this tool it says:
Which I thought implies that there is a way to suppress this popup when calling
InsertMedia
(used for "MIDI CH mode"). But it seems to always call it with1
as flag arg (insert on new track), without any other flags: https://github.com/daniellumertz/DanielLumertz-Scripts/blob/f3bffa2bfd07a075ea0c4c7a678acb9d19b7975f/MIDI%20Transfer/main.lua#L208But I found something useful in that manual:
and
So it seems that's the only solution, to not permanently lock the inserted midi file. Then, when Reaper's window is not focused and it's not playing, it will unlock the midi file, which allows my livecoding application to replace the file without this popup showing up. But it's still not optimal, because the midi file can't be replaced while Reaper is playing when it's not focused..
Maybe I can get
InsertMedia
to work such that it behaves like drag&drop (so that it doesn't lock the file, with the above setting set to "Import midi files as midi items (in project)"). But then I'd have to find a way to suppress that popup. Do you know if there's a way (maybe through a hidden setting)? :)
Not aware of that. If this InsertMedia
stuff is too restrictive, then it might be time for some of the more low-level functions (inserting single events directly into the MediaItem). But as we discussed before, it will need much thought to lift those to medium-level API level. Maybe you can gain some experience by trying the low-level methods - this is what REAPER C++ devs have to put up with all the time ;)
If this doesn't get you further, writing Justin an email is usually the way to go.
You mean sending to
support@cockos.com
? I tried that a while ago but never got a response. Or do you mean a different email address?
https://forums.cockos.com/showpost.php?p=134239&postcount=8
The usual way I approach this is to schedule something for execution on the main thread (by sending closures via channel to a ControlSurface and executing them in the run method). This is something you need for sure when writing multi-threaded REAPER plug-ins. In ReaLearn I have channels all over the place.
Thanks, I'll try that. Btw, is there any way to run things periodically on the main thread without having a control surface? (We used to have
Plugin::idle()
invst-rs
but removed it because the opcode was deprecated and wasn't being called by all hosts. AndEditor::idle()
depends on having a GUI.)
Control surface is the way to go. That's REAPER's way to let you participate in the main loop. And it works brilliantly, so I think there's no need for an alternative.
@Boscop Looks I was wrong with the last statement. There might be another way to run something periodically which I was not aware of: Search for "timer" in https://github.com/justinfrankel/reaper-sdk/blob/main/sdk/reaper_plugin.h. I guess that - apart from the fact that all it needs is a function pointer - it's not much different from the IReaperControlSurface
Run()
method.
Thanks, I'm doing everything in ControlSurface::run
now and it's not crashing on startup anymore, and everything works fine now :)
Btw, does it run at a constant framerate?
It turns out that with InsertMedia
there's no way to suppress this popup:
So I'm doing it this way instead:
pub unsafe fn reaper_insert_midi_file(
reaper: &Reaper,
track: MediaTrack,
midi_path: impl AsRef<Path>,
) {
let insert_pos_sec = 0.;
let path_str_c = CString::new(midi_path.as_ref().to_string_lossy().as_bytes()).unwrap();
let reaper = reaper.low();
let item = reaper.AddMediaItemToTrack(track.as_ptr());
let take = reaper.AddTakeToMediaItem(item);
let source = reaper.PCM_Source_CreateFromFile(path_str_c.as_ptr());
let mut is_qn: bool = false;
let mut length = reaper.GetMediaSourceLength(source, &mut is_qn as *mut _);
if is_qn {
let pos_qn = reaper.TimeMap2_timeToQN(null_mut(), insert_pos_sec);
length = reaper.TimeMap2_QNToTime(null_mut(), pos_qn + length) - insert_pos_sec;
}
reaper.SetMediaItemTake_Source(take, source);
reaper.SetMediaItemInfo_Value(item, cstr_const!(D_POSITION), insert_pos_sec);
reaper.SetMediaItemInfo_Value(item, cstr_const!(D_LENGTH), length);
reaper.UpdateArrange();
}
So I guess you could merge this PR (and implement InsertMediaMode::to_raw
).
Even though I'm not using this function for this use case, the interpretation of flags seems correct.
I played around with it in cfillion's iReaScript lua console that I installed from ReaPack.
My above function works well, the only disadvantage is that I'd have to manually construct the tempo map from the tempo meta events in the midi file and create the tempo markers using the extension API :/ There doesn't seem a way to import the midi file while also importing its tempo map and suppressing this popup.. (I have a lot of tempo micro-variations in my livecoded midi songs, to make them more "organic".)
Available on master.
@boscop This works already but the mode/flags parameters are not yet evaluated because I'm unsure about the structure. There are clearly some flags that can be combined (and therefore should be represented via bitflags) but also some that totally exclude each other and therefore would make more sense as an enum. So far I tried to model it a bit by looking at the REAPER doc for this function:
I put the modes which I think are exclusive into
InsertMediaMode
and the flags which I think can be combined intoInsertMediaFlag
- but I've no idea if this is completely correct. Did you experiment with the function a bit and can maybe improve it?