1
0
Fork 0

Add a Transport struct with conversion methods

We'll add this to the ProcessContext in a bit.
This commit is contained in:
Robbert van der Helm 2022-03-04 14:25:38 +01:00
parent 99f97978a9
commit a483dbc6a6

View file

@ -80,6 +80,60 @@ pub trait GuiContext: Send + Sync + 'static {
unsafe fn raw_default_normalized_param_value(&self, param: ParamPtr) -> f32;
}
/// Information about the plugin's transport. Depending on the plugin API and the host not all
/// fields may be available.
pub struct Transport {
/// Whether the transport is currently running.
pub playing: bool,
/// Whether recording is enabled in the project.
pub recording: bool,
/// Whether the pre-roll is currently active, if the plugin API reports this information.
pub preroll_active: Option<bool>,
/// The sample rate in Hertz. Also passed in
/// [`Plugin::initialize()`][crate::prelude::Plugin::initialize()], so if you need this then you
/// can also store that value.
pub sample_rate: f32,
/// The proejct's tempo in beats per minute.
pub tempo: Option<f64>,
/// The time signature's numerator.
pub time_sig_numerator: Option<i32>,
/// The time signature's denominator.
pub time_sig_denominator: Option<i32>,
// XXX: VST3 also has a continuous time in samples that ignores loops, but we can't reconstruct
// something similar in CLAP so it may be best to just ignore that so you can't rely on it
/// The position in the song in samples. Can be used to calculate the time in seconds if needed.
pub(crate) pos_samples: Option<i64>,
/// The position in the song in quarter notes. Can be used to calculate the time in samples if
/// needed.
pub(crate) pos_seconds: Option<f64>,
/// The position in the song in quarter notes. Can be calculated from the time in seconds and
/// the tempo if needed.
pub(crate) pos_beats: Option<f64>,
/// The last bar's start position in beats. Can be calculated from the beat position and time
/// signature if needed.
pub(crate) bar_start_pos_beats: Option<f64>,
/// The number of the bar at `bar_start_pos_beats`. This starts at 0 for the very first bar at
/// the start of the song. Can be calculated from the beat position and time signature if
/// needed.
pub(crate) bar_number: Option<i32>,
/// The loop range in samples, if the loop is active and this information is available. None of
/// the plugin API docs mention whether this is exclusive or inclusive, but just assume that the
/// end is exclusive. Can be calulcated from the other loop range information if needed.
pub(crate) loop_range_samples: Option<(i64, i64)>,
/// The loop range in seconds, if the loop is active and this information is available. None of
/// the plugin API docs mention whether this is exclusive or inclusive, but just assume that the
/// end is exclusive. Can be calulcated from the other loop range information if needed.
pub(crate) loop_range_seconds: Option<(f64, f64)>,
/// The loop range in quarter notes, if the loop is active and this information is available.
/// None of the plugin API docs mention whether this is exclusive or inclusive, but just assume
/// that the end is exclusive. Can be calulcated from the other loop range information if
/// needed.
pub(crate) loop_range_beats: Option<(f64, f64)>,
}
/// A convenience helper for setting parameter values. Any changes made here will be broadcasted to
/// the host and reflected in the plugin's [`Params`][crate::param::internals::Params] object. These
/// functions should only be called from the main thread.
@ -87,6 +141,170 @@ pub struct ParamSetter<'a> {
context: &'a dyn GuiContext,
}
// TODO: These conversions have not really been tested yet, there might be an error in there somewhere
impl Transport {
/// The position in the song in samples. Will be calculated from other information if needed.
pub fn pos_samples(&self) -> Option<i64> {
match (
self.pos_samples,
self.pos_seconds,
self.pos_beats,
self.tempo,
) {
(Some(pos_samples), _, _, _) => Some(pos_samples),
(_, Some(pos_seconds), _, _) => {
Some((pos_seconds * self.sample_rate as f64).round() as i64)
}
(_, _, Some(pos_beats), Some(tempo)) => {
Some((pos_beats / tempo * 60.0 * self.sample_rate as f64).round() as i64)
}
(_, _, _, _) => None,
}
}
/// The position in the song in quarter notes. Will be calculated from other information if
/// needed.
pub fn pos_seconds(&self) -> Option<f64> {
match (
self.pos_samples,
self.pos_seconds,
self.pos_beats,
self.tempo,
) {
(_, Some(pos_seconds), _, _) => Some(pos_seconds),
(Some(pos_samples), _, _, _) => Some(pos_samples as f64 / self.sample_rate as f64),
(_, _, Some(pos_beats), Some(tempo)) => Some(pos_beats / tempo * 60.0),
(_, _, _, _) => None,
}
}
/// The position in the song in quarter notes. Will be calculated from other information if
/// needed.
pub fn pos_beats(&self) -> Option<f64> {
match (
self.pos_samples,
self.pos_seconds,
self.pos_beats,
self.tempo,
) {
(_, _, Some(pos_beats), _) => Some(pos_beats),
(_, Some(pos_seconds), _, Some(tempo)) => Some(pos_seconds / 60.0 * tempo),
(Some(pos_samples), _, _, Some(tempo)) => {
Some(pos_samples as f64 / self.sample_rate as f64 / 60.0 * tempo)
}
(_, _, _, _) => None,
}
}
/// The last bar's start position in beats. Will be calculated from other information if needed.
pub fn bar_start_pos_beats(&self) -> Option<f64> {
if self.bar_start_pos_beats.is_some() {
return self.bar_start_pos_beats;
}
match (
self.time_sig_numerator,
self.time_sig_denominator,
self.pos_beats(),
) {
(Some(time_sig_numerator), Some(time_sig_denominator), Some(pos_beats)) => {
let quarter_note_bar_length =
time_sig_numerator as f64 / time_sig_denominator as f64 * 4.0;
Some(pos_beats.div_euclid(quarter_note_bar_length))
}
(_, _, _) => None,
}
}
/// The number of the bar at `bar_start_pos_beats`. This starts at 0 for the very first bar at
/// the start of the song. Will be calculated from other information if needed.
pub fn bar_number(&self) -> Option<i32> {
if self.bar_number.is_some() {
return self.bar_number;
}
match (
self.time_sig_numerator,
self.time_sig_denominator,
self.pos_beats(),
) {
(Some(time_sig_numerator), Some(time_sig_denominator), Some(pos_beats)) => {
let quarter_note_bar_length =
time_sig_numerator as f64 / time_sig_denominator as f64 * 4.0;
Some((pos_beats / quarter_note_bar_length).floor() as i32)
}
(_, _, _) => None,
}
}
/// The loop range in samples, if the loop is active and this information is available. None of
/// the plugin API docs mention whether this is exclusive or inclusive, but just assume that the
/// end is exclusive. Will be calculated from other information if needed.
pub fn loop_range_samples(&self) -> Option<(i64, i64)> {
match (
self.loop_range_samples,
self.loop_range_seconds,
self.loop_range_beats,
self.tempo,
) {
(Some(loop_range_samples), _, _, _) => Some(loop_range_samples),
(_, Some((start_seconds, end_seconds)), _, _) => Some((
((start_seconds * self.sample_rate as f64).round() as i64),
((end_seconds * self.sample_rate as f64).round() as i64),
)),
(_, _, Some((start_beats, end_beats)), Some(tempo)) => Some((
(start_beats / tempo * 60.0 * self.sample_rate as f64).round() as i64,
(end_beats / tempo * 60.0 * self.sample_rate as f64).round() as i64,
)),
(_, _, _, _) => None,
}
}
/// The loop range in seconds, if the loop is active and this information is available. None of
/// the plugin API docs mention whether this is exclusive or inclusive, but just assume that the
/// end is exclusive. Will be calculated from other information if needed.
pub fn loop_range_seconds(&self) -> Option<(f64, f64)> {
match (
self.loop_range_samples,
self.loop_range_seconds,
self.loop_range_beats,
self.tempo,
) {
(_, Some(loop_range_seconds), _, _) => Some(loop_range_seconds),
(Some((start_samples, end_samples)), _, _, _) => Some((
start_samples as f64 / self.sample_rate as f64,
end_samples as f64 / self.sample_rate as f64,
)),
(_, _, Some((start_beats, end_beats)), Some(tempo)) => {
Some((start_beats / tempo * 60.0, end_beats / tempo * 60.0))
}
(_, _, _, _) => None,
}
}
/// The loop range in quarter notes, if the loop is active and this information is available.
/// None of the plugin API docs mention whether this is exclusive or inclusive, but just assume
/// that the end is exclusive. Will be calculated from other information if needed.
pub fn loop_range_beats(&self) -> Option<(f64, f64)> {
match (
self.loop_range_samples,
self.loop_range_seconds,
self.loop_range_beats,
self.tempo,
) {
(_, _, Some(loop_range_beats), _) => Some(loop_range_beats),
(_, Some((start_seconds, end_seconds)), _, Some(tempo)) => {
Some((start_seconds / 60.0 * tempo, end_seconds / 60.0 * tempo))
}
(Some((start_samples, end_samples)), _, _, Some(tempo)) => Some((
start_samples as f64 / self.sample_rate as f64 / 60.0 * tempo,
end_samples as f64 / self.sample_rate as f64 / 60.0 * tempo,
)),
(_, _, _, _) => None,
}
}
}
impl<'a> ParamSetter<'a> {
pub fn new(context: &'a dyn GuiContext) -> Self {
Self { context }