-
-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Midi functions draft #66
base: master
Are you sure you want to change the base?
Conversation
midi_get_ppq_pos_start_of_measure midi_get_ppq_pos_end_of_measure midi_get_proj_qn_from_ppq_pos midi_get_ppq_pos_from_proj_qn midi_get_proj_time_from_ppq_pos midi_get_ppq_pos_from_proj_time every PPQ function is unsafe, because of take requirement Fixing naming convencions Co-authored-by: helgoboss <[email protected]> rebasing to master Replaced all mut to ptr
also introduced the new struct `TimeMapQNToMeasuresResult` for storing bar start\end positions renamed to time_map (without 2) and again
This reverts commit 59f2716cfcdf7efe543aaac796d2242b8ad8981b.
Example: fn test_midi_events() {
let rpr = Reaper::get().medium_reaper();
let pr = reaper_medium::ProjectContext::CurrentProject;
let track = rpr.get_track(pr, 2).unwrap();
let item = get_track_items(track).expect("should be item")[0];
unsafe {
let take = rpr.get_active_take(item).expect("should be take");
assert_true(
rpr.take_is_midi(&take),
"Take is MIDI",
"Take should be MIDI",
);
let events = rpr
.midi_get_all_events_iter(take, 500)
.expect("should be iterator");
println!(
"Events from iterator: {:#?}",
events.collect::<Vec<SourceMidiEvent>>()
);
}
} Out:
|
Now, I'm pretty sure in iterators. And again thinking of adding original single-event functions, as additional tool for self-check, and because more complex iterators through raw midi lays out of medium-level tasks. Considering the naming: I've thought at first to name the original functions as they are, and functions with iterators like |
Now it can hold various messages and still can represent raw bytes with `RawMidiMessage`
So, the main message types from `helgoboss_midi` now covered
OK. Now the idea looks fine for me. Please, check if you like this API and realization, and I'll finish other I've checked, that everything works OK with real REAPER, just need to fix this in reaper-rs integration test. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't have the time to review all of your code yet but here's a start.
track: MediaTrack, | ||
starttime: f64, | ||
endtime: f64, | ||
time_in_qn: bool, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Taking a bool is not very self-explaining in this case (see here for design rationale):
reaper.create_midi_item_in_proj(track, 0.0, 10.0, true);
I think I would prefer this one:
reaper.create_midi_item_in_proj(track, PositionInQuarterNotes(0.0)..PositionInQuarterNotes(10.0));
This would also make sense because we already have newtypes PositionInQuarterNotes
and PositionInSeconds
which are used consistently across the medium-level API. In real-world use cases, chances are you already have PositionInQuarterNotes
values ready, so this would get quite terse.
It should be achievable by having:
pub unsafe fn create_midi_item_in_proj(&self, track: MediaTrack, range: R) -> ReaperFunctionResult<MediaItem>
where
R: Into<TimeRange>,
UsageScope: MainThreadOnly,
{
...
}
pub enum TimeRange {
Seconds(Range<PositionInSeconds>),
QuarterNotes(Range<PositionInQuarterNotes>),
}
impl From<Range<PositionInSeconds>> for TimeRange {
...
}
impl From<Range<PositionInQuarterNotes>> for TimeRange {
...
}
Alternatively, we could also start by taking a TimeRange
directly, without the magic Into
conversions (and add the magic later if it's desired):
reaper.create_midi_item_in_proj(track, TimeRange::QuarterNotes(PositionInQuarterNotes(0.0)..PositionInQuarterNotes(10.0)));
But a bit too much repetition for my taste.
BTW, Rust has inclusive and exclusive ranges. Although it wouldn't make any different for us because we don't want to iterate the range, I think it would be good to communicate the intention. If I understand REAPER correctly, the end time always represents something exclusive. The item actually ends before that end position. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, in this case we again face the problem of what medium-level should do, and what not...
For my personal tase, I found, that std
already has awesome type, that solves 90% of ambiguity and boiler-plate: std:time::Duration
. While one think of a Position in the therm of «Duration from the start», it removes any leaks in terminology. Alongside, it leaves interpretation of low-level API f64 arguments not on the type system, but on concrete wrapper-function realization. And, if, occasionally, someone made a mistake in interpreting float time
as seconds, while it is not — the change will not affect next-level users.
More than, it implements accurate arithmetic operations as position comparison and holding the length of events. It naturally converts from\into f64, it is fast and supports all the derive traits, like Copy, Hash, Ord, Eq
etc.
P.S. So, For my personal use I just wrapped it in my struct for the case, and implemented a couple of from\into traits.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same thing I thought, when used a Color
. NativeColor
and RGB color is just a leaking abstraction, that not really helps in code usage. So, again, for myself I've made just Color
with from_native
and into_native
methods, that take and return f64.
}) | ||
} | ||
|
||
/// returns false if there are no plugins on the track that support MIDI programs, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs are not ready for prime-time yet but this is something that we can polish at the end, more important things right now.
/// or if all programs have been enumerated | ||
pub fn enum_track_midi_program_names( | ||
&self, | ||
track: i32, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a first time reader of this function, I don't know what these parameters mean, e.g. why i32 instead of u32? What does a negative value mean? This is something we should clarify by using newtypes or enums.
track_index: u32, | ||
pitch: u32, | ||
channel: u32, | ||
) -> Option<&'a ReaperStr> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whenever you return a string that's owned by REAPER and can lose validity, use continuation-passing style. See enum_project_markers_3
for example.
I've just built an iterator, that returns arbitrary "generic" midi events with PPQ position and all flags unpacked.
Then the decision is needed what to do:
midi_set_all_evts
and make everything clean