reaper_medium/lib.rs
1#![doc(html_root_url = "https://docs.rs/reaper-medium/0.1.0")]
2
3//! This crate contains the medium-level API of [reaper-rs](https://github.com/helgoboss/reaper-rs).
4//!
5//! To get started, have a look at the [`Reaper`] struct.
6//!
7//! # General usage hints
8//!
9//! - Whenever you find an identifier in this crate that ends with `index`, you can assume it's a
10//! *zero-based* integer. That means the first index is 0, not 1!
11//!
12//! # Example
13//!
14//! ```no_run
15//! # let reaper = reaper_medium::Reaper::default();
16//! use reaper_medium::ProjectContext::CurrentProject;
17//!
18//! let functions = reaper.functions();
19//! functions.show_console_msg("Hello world from reaper-rs medium-level API!");
20//! let track = functions.get_track(CurrentProject, 0).ok_or("no tracks")?;
21//! unsafe { functions.delete_track(track); }
22//! # Ok::<_, Box<dyn std::error::Error>>(())
23//! ```
24//!
25//! # Design goals
26//!
27//! The ultimate goal of the medium-level API is to provide all functions offered by the low-level
28//! API, but in an idiomatic and type-safe way. The result is still a plain list of functions, where
29//! each function is basically named like its original. Going all object-oriented,
30//! using reactive extensions, introducing a fluid API, finding function names that make more sense
31//! ... all of that is intentionally *out of scope*. The medium-level API is intended to stay close
32//! to the original API. This has the benefit that ReaScript (e.g. Lua) and C++ code seen in forum
33//! threads, blogs and existing extensions can be helpful even for writing plug-ins in Rust.
34//!
35//! # Design principles
36//!
37//! In order to achieve these goals, this API follows a bunch of design principles.
38//!
39//! ## Follow Rust naming conventions
40//!
41//! Most low-level functions and types don't follow the Rust naming conventions. We adjust them
42//! accordingly while still staying as close as possible to the original names.
43//!
44//! ## Use unsigned integers where appropriate
45//!
46//! We don't use signed integers when it's totally clear that a number can never be negative.
47//! Example: [`insert_track_at_index()`](struct.ReaperFunctions.html#method.insert_track_at_index)
48//!
49//! ## Use enums where appropriate
50//!
51//! We want more type safety and more readable code. Enums can contribute to that a lot. Here's how
52//! we use them:
53//!
54//! 1. If the original function uses an integer which represents a limited set of
55//! options that can be easily named, we introduce an enum. Example:
56//! [`get_track_automation_mode()`](struct.ReaperFunctions.html#method.insert_track_at_index),
57//! [`AutomationMode`](enum.AutomationMode.html)
58//!
59//! 2. If the original function uses a string and there's a clear set of predefined
60//! options, we introduce an enum. Example:
61//! [`get_media_track_info_value()`](struct.ReaperFunctions.html#method.get_media_track_info_value),
62//! [`TrackAttributeKey`](enum.TrackAttributeKey.html)
63//!
64//! 3. If the original function uses a bool and the name of the function doesn't give that bool
65//! meaning, introduce an enum. Example:
66//! [`set_current_bpm()`](struct.ReaperFunctions.html#method.set_current_bpm),
67//! [`UndoBehavior`](enum.UndoBehavior.html)
68//!
69//! 4. If the original function can have different mutually exclusive results, introduce an enum.
70//! Example:
71//! [`get_last_touched_fx()`](struct.ReaperFunctions.html#method.get_last_touched_fx),
72//! [`GetLastTouchedFxResult`](enum.GetLastTouchedFxResult.html)
73//!
74//! 5. If the original function has several parameters of which only certain combinations are valid,
75//! introduce an enum for combining those. Example:
76//! [`kbd_on_main_action_ex()`](struct.ReaperFunctions.html#method.kbd_on_main_action_ex),
77//! [`ActionValueChange`](enum.ActionValueChange.html)
78//!
79//! 6. If the original function takes a parameter which describes how another parameter is
80//! interpreted, introduce an enum. Example:
81//! [`csurf_on_pan_change_ex()`](struct.ReaperFunctions.html#method.csurf_on_pan_change_ex),
82//! [`ValueChange`](enum.ValueChange.html)
83//!
84//! 7. If the original function takes an optional value and one cannot conclude from the function
85//! name what a `None` would mean, introduce an enum. Example:
86//! [`count_tracks()`](struct.ReaperFunctions.html#method.count_tracks),
87//! [`ProjectContext`](enum.ProjectContext.html)
88//!
89//! The first design didn't have many enums. Then, with every enum introduced in the medium-level
90//! API, the high-level API code was getting cleaner, more understandable and often even shorter.
91//! More importantly, some API usage bugs suddenly became obvious!
92//!
93//! ## Adjust return types where appropriate
94//!
95//! 1. Use `bool` instead of `i32` as return value type for "yes or no" functions. Example:
96//! [`is_in_real_time_audio()`](struct.ReaperFunctions.html#method.is_in_real_time_audio)
97//! 2. Use return values instead of output parameters. Example:
98//! [`gen_guid()`](struct.ReaperFunctions.html#method.gen_guid)
99//! 3. If a function has multiple results, introduce and return a struct for aggregating them.
100//! Example: [`get_focused_fx()`](struct.ReaperFunctions.html#method.get_focused_fx)
101//! 4. If a function can return a value which represents that something is not present,
102//! return an `Option`. Example:
103//! [`named_command_lookup()`](struct.ReaperFunctions.html#method.named_command_lookup)
104//!
105//! ## Use newtypes where appropriate
106//!
107//! 1. If a value represents an ID, introduce a newtype. Example:
108//! [`CommandId`](struct.CommandId.html)
109//! 2. If a number value is restricted in its value range, represents a mathematical unit or can be
110//! easily confused, consider introducing a meaningful newtype. Example:
111//! [`ReaperVolumeValue`](struct.ReaperVolumeValue.html)
112//!
113//! We *don't* use newtypes for numbers that represent indexes.
114//!
115//! ## Use convenience functions where necessary
116//!
117//! In general, the medium-level API shouldn't have too much additional magic and convenience.
118//! However, there are some low-level functions which are true allrounders. With allrounders it's
119//! often difficult to find accurate signatures and impossible to avoid `unsafe`. Adding multiple
120//! convenience functions can sometimes help with that, at least with making them a *bit* more
121//! safe to use.
122//! Examples:
123//! [`get_set_media_track_info()`](struct.ReaperFunctions.html#method.get_set_media_track_info),
124//! [`plugin_register_add_command_id()`](struct.Reaper.html#method.plugin_register_add_command_id)
125//!
126//! ## Make it easy to work with strings
127//!
128//! - String parameters are used as described in [`ReaperStringArg`](struct.ReaperStringArg.html).
129//! Example: [`string_to_guid()`](struct.ReaperFunctions.html#method.string_to_guid)
130//! - Strings in return positions are dealt with in different ways:
131//! - When returning an owned string, we return `CString` (because that's what comes closest to
132//! the original REAPER API, see [`ReaperStringArg`](struct.ReaperStringArg.html)). Consumers
133//! can easily convert them to regular Rust strings when needed. Example:
134//! [`guid_to_string()`](struct.ReaperFunctions.html#method.guid_to_string)
135//! - When returning a string owned by REAPER and we know that string has a static lifetime, we
136//! return a `&'static CStr`. Example:
137//! [`get_app_version()`](struct.ReaperFunctions.html#method.get_app_version)
138//! - When returning a string owned by REAPER and we can't give it a proper lifetime annotation
139//! (in most cases we can't), we grant the user only temporary access to that string by taking
140//! a closure with a `&CStr` argument which is executed right away. Example:
141//! [`undo_can_undo_2()`](struct.ReaperFunctions.html#method.undo_can_undo_2)
142//! - Strings in enums are often `Cow<CStr>` because we want them to be flexible enough to carry
143//! both owned and borrowed strings.
144//!
145//! ## Use pointer wrappers where appropriate
146//!
147//! When we deal with REAPER, we have to deal with pointers. REAPER often returns pointers and we
148//! can't give them a sane lifetime annotation. Depending on the type of plug-in and the type of
149//! pointer, some are rather static from the perspective of the plug-in and others can come and go
150//! anytime. In any case, just turning them into `'static` references would be plain wrong. At the
151//! same time, annotating them with a bounded lifetime `'a` (correlated to another lifetime) is
152//! often impossible either, because mostly we don't have another lifetime at the disposal which can
153//! serve as frame of reference.
154//!
155//! In most cases the best we can do is passing pointers around. How exactly this is done,
156//! depends on the characteristics of the pointed-to struct and how it is going to be used.
157//!
158//! ### Case 1: Internals not exposed | no vtable
159//!
160//! #### Strategy
161//!
162//! - Use `NonNull` pointers directly
163//! - Make them more accessible by introducing an alias
164//!
165//! #### Explanation
166//!
167//! Such structs are relevant for the consumers *as pointers only*. Because they are
168//! completely opaque (internals not exposed, not even a vtable). We don't create a newtype because
169//! the `NonNull` guarantee is all we need and we will never provide any methods on them (no vtable
170//! emulation, no convenience methods). Using a wrapper just for reasons of symmetry would not be
171//! good because it comes with a cost (more code to write, less substitution possibilities) but in
172//! this case without any benefit.
173//!
174//! #### Examples
175//!
176//! - [`raw::MediaTrack`](../reaper_low/raw/struct.MediaTrack.html) →
177//! [`MediaTrack`](type.MediaTrack.html)
178//! - [`raw::ReaProject`](../reaper_low/raw/struct.ReaProject.html) →
179//! [`ReaProject`](type.ReaProject.html)
180//! - [`raw::MediaItem_Take`](../reaper_low/raw/struct.MediaItem_Take.html) →
181//! [`MediaItemTake`](type.MediaItemTake.html)
182//!
183//! ### Case 2: Internals exposed | no vtable
184//!
185//! #### Strategy
186//!
187//! - *Don't* create an alias for a `NonNull` pointer! In situations where just the pointer is
188//! interesting and not the internals, write `NonNull<...>` everywhere.
189//! - If the consumer shall get access to the internals: Wrap the `NonNull` pointer in a public
190//! newtype. This newtype should expose the internals in a way which is idiomatic for Rust (like
191//! the rest of the medium-level API does).
192//! - If the consumer needs to be able to create such a struct: Provide an idiomatic Rust factory
193//! function. If that's not enough because the raw struct is not completely owned, write an owned
194//! version of that struct, prefixed with `Medium`. Ideally it should wrap the raw struct.
195//!
196//! #### Explanation
197//!
198//! Unlike [`raw::MediaTrack`](../reaper_low/raw/struct.MediaTrack.html) and friends, these
199//! structs are *not* opaque. Still, we need them as pointers and they have the same lifetime
200//! considerations. The difference is that we add type-safe methods to them in order to lift their
201//! members to medium-level API style.
202//!
203//! #### Examples
204//!
205//! - [`raw::KbdSectionInfo`](../reaper_low/raw/struct.KbdSectionInfo.html) →
206//! [`KbdSectionInfo`](struct.KbdSectionInfo.html) & `MediumKdbSectionInfo` (not yet existing)
207//! - [`raw::audio_hook_register_t`](../reaper_low/raw/struct.audio_hook_register_t.html) →
208//! [`AudioHookRegister`](struct.AudioHookRegister.html) &
209//! [`MediumAudioHookRegister`](struct.MediumAudioHookRegister.html)
210//! - [`raw::gaccel_register_t`](../reaper_low/raw/struct.gaccel_register_t.html) → `GaccelRegister`
211//! (not yet existing) & [`MediumGaccelRegister`](struct.MediumGaccelRegister.html)
212//!
213//! ### Case 3: Internals not exposed | vtable
214//!
215//! #### Strategy
216//!
217//! - *Don't* create an alias for a `NonNull` pointer! In situations where just the pointer is
218//! interesting and not the internals, write `NonNull<...>` everywhere.
219//! - If the consumer shall get access to the virtual functions: Wrap `NonNull` pointer in a public
220//! newtype. This newtype should expose the virtual functions in a way which is idiomatic for
221//! Rust. It's intended for the communication from Rust to REAPER. This needs appropriate
222//! companion C code in the low-level API.
223//! - If the consumer needs to be able to provide such a type (for communication from REAPER to
224//! Rust): Create a new trait prefixed with `Medium` which can be implemented by the consumer.
225//! This also needs appropriate companion C code in the low-level API.
226//!
227//! #### Examples
228//!
229//! - [`raw::IReaperControlSurface`](../reaper_low/raw/struct.IReaperControlSurface.html) →
230//! `ReaperControlSurface` (not yet existing) &
231//! [`MediumReaperControlSurface`](struct.MediumReaperControlSurface.html)
232//! - [`raw::midi_Input`](../reaper_low/raw/struct.midi_Input.html) →
233//! [`MidiInput`](struct.MidiInput.html) &
234//! - [`raw::MIDI_eventlist`](../reaper_low/raw/struct.MIDI_eventlist.html) →
235//! [`MidiEventList`](struct.MidiEventList.html) &
236//! - `PCM_source` → `PcmSource` & `MediumPcmSource` (both not yet existing)
237//!
238//! ## Panic/error/safety strategy
239//!
240//! - We panic if a REAPER function is not available, e.g. because it's an older REAPER version.
241//! Rationale: If *all* function signatures would be cluttered up with `Result`s, it would be an
242//! absolute nightmare to use the API. It's also not necessary: The consumer can always check if
243//! the function is there, and mostly it is (see
244//! [`reaper_low::Reaper`](../reaper_low/struct.Reaper.html)).
245//! - We panic when passed parameters don't satisfy documented preconditions which can be easily
246//! satisfied by consumers. Rationale: This represents incorrect API usage.
247//! - Luckily, the need for precondition checks is mitigated by using lots of newtypes and
248//! enums, which don't allow parameters to be out of range in the first place.
249//! Example: [`track_fx_get_fx_name()`](struct.ReaperFunctions.html#method.track_fx_get_fx_name)
250//! - When a function takes pointers, we generally mark it as `unsafe`. Rationale: Pointers can
251//! dangle (e.g. a pointer to a track dangles as soon as that track is removed). Passing a
252//! dangling pointer to a REAPER function can and often will make REAPER crash. Example:
253//! [`delete_track()`](struct.ReaperFunctions.html#method.delete_track)
254//! - That's a bit unfortunate, but unavoidable given the medium-level APIs design goal to stay
255//! close to the original API. The `unsafe` is a hint to the consumer to be extra careful with
256//! those functions.
257//! - The consumer *has* ways to ensure that the passed pointer is valid:
258//!
259//! 1. Using obtained pointers right away instead of caching them (preferred)
260//!
261//! 2. Using [`validate_ptr_2()`](struct.ReaperFunctions.html#method.validate_ptr_2) to
262//! check if the cached pointer is still valid.
263//!
264//! 3. Using a
265//! [hidden control surface](struct.Reaper.html#method.plugin_register_add_csurf_inst)
266//! to be informed whenever e.g. a `MediaTrack` is removed and invalidating the cached
267//! pointer accordingly.
268//! - There's one exception to this: If the parameters passed to the function in question are enough
269//! to check whether the pointer is still valid, we do it, right in that function. If it's
270//! invalid, we panic. We use
271//! [`validate_ptr_2()`](struct.ReaperFunctions.html#method.validate_ptr_2) to check the pointer.
272//! Sadly, for all but project pointers it needs a project context to be able to validate a
273//! pointer. Otherwise we could apply this rule much more. Rationale: This allows us to remove the
274//! `unsafe` (if there was no other reason for it). That's not ideal either but it's far better
275//! than undefined behavior. Failing fast without crashing is one of the main design principles of
276//! *reaper-rs*. Because checking the pointer is an "extra" thing that the medium-level API does,
277//! we also offer an unsafe `_unchecked` variant of the same function, which doesn't do the check.
278//! Example: [`count_tracks()`](struct.ReaperFunctions.html#method.count_tracks) and
279//! [`count_tracks_unchecked()`](struct.ReaperFunctions.html#method.count_tracks_unchecked)
280//! - If a REAPER function can return a value which represents that execution was not successful,
281//! return a `Result`. Example:
282//! [`string_to_guid()`](struct.ReaperFunctions.html#method.string_to_guid)
283//!
284//! Verdict: Making the API completely safe to use can't be done in the medium-level API. But it can
285//! be done in the high-level API because it's not tied to the original REAPER flat function
286//! signatures. For example, there could be a `Track` struct which holds a `ReaProject` pointer,
287//! the track index and the track's GUID. With that combination it's possible to detect reliably
288//! whether a track is still existing. Needless to say, this is far too opinionated for the
289//! medium-level API.
290//!
291//!
292//! ## Try to follow "zero-cost" principle
293//!
294//! If someone uses C++ or Rust instead of just settling with ReaScript, chances are that better
295//! performance is at least one of the reasons. The medium-level API acknowledges that and tries
296//! to be very careful not to introduce possibly performance-harming indirections. In general it
297//! shouldn't do extra stuff. Just the things which are absolutely necessary to reach the design
298//! goals mentioned above. This is essential for code that is intended to be executed in
299//! the real-time audio thread (no heap allocations etc.).
300//!
301//! This is an important principle. It would be bad if it's necessary to reach out to the low-level
302//! API whenever someone wants to do something performance-critical. The low-level API shouldn't
303//! even be considered as a serious Rust API, it's too raw and unsafe for Rust standards.
304//!
305//!
306//! [`Reaper`]: struct.Reaper.html
307
308mod misc_enums;
309pub use misc_enums::*;
310
311mod misc_newtypes;
312pub use misc_newtypes::*;
313
314mod key_enums;
315pub use key_enums::*;
316
317mod fn_traits;
318pub use fn_traits::*;
319
320mod flags;
321pub use flags::*;
322
323mod reaper_pointer;
324pub use reaper_pointer::*;
325
326mod gaccel_register;
327pub use gaccel_register::*;
328
329mod audio_hook_register;
330pub use audio_hook_register::*;
331
332mod infostruct_keeper;
333
334mod control_surface;
335pub use control_surface::*;
336
337mod midi;
338pub use midi::*;
339
340mod reaper;
341pub use reaper::*;
342
343mod reaper_functions;
344pub use reaper_functions::*;
345
346mod util;
347use util::*;
348
349mod string_types;
350pub use string_types::*;
351
352mod recording_input;
353pub use recording_input::*;
354
355mod automation_mode;
356pub use automation_mode::*;
357
358mod message_box;
359pub use message_box::*;
360
361mod ptr_wrappers;
362pub use ptr_wrappers::*;
363
364mod errors;
365pub use errors::*;