Skip to content

Commit

Permalink
Merge 0e3e93b into f3f2d1e
Browse files Browse the repository at this point in the history
  • Loading branch information
emuell authored Jul 3, 2024
2 parents f3f2d1e + 0e3e93b commit 1d73755
Show file tree
Hide file tree
Showing 20 changed files with 677 additions and 237 deletions.
10 changes: 5 additions & 5 deletions benches/benchmarks/rhythm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ pub fn run(c: &mut Criterion) {
pub fn seek(c: &mut Criterion) {
let mut group = c.benchmark_group("Rust Phrase");
let phrase = create_phrase();
let samples_per_sec = phrase.time_base().samples_per_sec;
let samples_per_sec = phrase.time_base().samples_per_sec as SampleTime;
let seek_step = 10;
let seek_time = 60 * 60;
group.bench_function("Seek", |b| {
Expand All @@ -202,10 +202,10 @@ pub fn seek(c: &mut Criterion) {
phrase
},
|mut phrase| {
let mut sample_time = samples_per_sec as SampleTime;
while sample_time < (seek_time * samples_per_sec) as SampleTime {
phrase.seek_until_time(sample_time);
sample_time += seek_step * samples_per_sec as SampleTime;
let mut sample_time = samples_per_sec;
while sample_time < seek_time * samples_per_sec {
phrase.advance_until_time(sample_time);
sample_time += seek_step * samples_per_sec;
}
},
criterion::BatchSize::SmallInput,
Expand Down
10 changes: 5 additions & 5 deletions benches/benchmarks/scripted.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ pub fn run(c: &mut Criterion) {
pub fn seek(c: &mut Criterion) {
let mut group = c.benchmark_group("Scripted Phrase");
let phrase = create_phrase();
let samples_per_sec = phrase.time_base().samples_per_sec;
let samples_per_sec = phrase.time_base().samples_per_sec as SampleTime;
let seek_step = 10;
let seek_time = 60 * 60;
group.bench_function("Seek", |b| {
Expand All @@ -191,10 +191,10 @@ pub fn seek(c: &mut Criterion) {
phrase
},
|mut phrase| {
let mut sample_time = samples_per_sec as SampleTime;
while sample_time < (seek_time * samples_per_sec) as SampleTime {
phrase.seek_until_time(sample_time);
sample_time += seek_step * samples_per_sec as SampleTime;
let mut sample_time = samples_per_sec;
while sample_time < seek_time * samples_per_sec {
phrase.advance_until_time(sample_time);
sample_time += seek_step * samples_per_sec;
}
},
criterion::BatchSize::SmallInput,
Expand Down
124 changes: 91 additions & 33 deletions src/bindings/callback.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{borrow::Cow, fmt::Debug};
use std::{borrow::Cow, collections::HashMap, fmt::Debug};

use mlua::prelude::*;

Expand Down Expand Up @@ -71,18 +71,18 @@ pub fn add_lua_callback_error(name: &str, err: &LuaError) {
/// with values before it's called.
///
/// Errors from callbacks should be handled by calling `self.handle_error` so external clients
/// can deal with them later, as apropriate.
/// can deal with them later, as appropriate.
///
/// By memorizing the original generator function and environment, it also can be reset to its
/// initial state by calling the original generator function again to fetch a new freshly
/// initialized function.
///
/// TODO: Upvalues of generators or simple functions could actuially be collected and restored
/// TODO: Upvalues of generators or simple functions could actually be collected and restored
/// too, but this uses debug functionality and may break some upvalues.
#[derive(Debug, Clone)]
pub(crate) struct LuaCallback {
environment: Option<LuaOwnedTable>,
context: LuaOwnedTable,
context: LuaOwnedAnyUserData,
generator: Option<LuaOwnedFunction>,
function: LuaOwnedFunction,
initialized: bool,
Expand All @@ -96,8 +96,15 @@ impl LuaCallback {

/// Create a new Callback from an owned lua function.
pub fn with_owned(lua: &Lua, function: LuaOwnedFunction) -> LuaResult<Self> {
// create an empty context and memorize the function without calling it
let context = lua.create_table()?.into_owned();
// create and register the callback context
LuaCallbackContext::register(lua)?;
let context = lua
.create_any_userdata(LuaCallbackContext {
values: HashMap::new(),
external_values: HashMap::new(),
})?
.into_owned();
// and memorize the function without calling it
let environment = function.to_ref().environment().map(LuaTable::into_owned);
let generator = None;
let initialized = false;
Expand All @@ -112,27 +119,31 @@ impl LuaCallback {

/// Sets the emitter time base context for the callback.
pub fn set_context_time_base(&mut self, time_base: &BeatTimeBase) -> LuaResult<()> {
let table = self.context.to_ref();
table.raw_set("beats_per_min", time_base.beats_per_min)?;
table.raw_set("beats_per_bar", time_base.beats_per_bar)?;
table.raw_set("samples_per_sec", time_base.samples_per_sec)?;
let values = &mut self.context.borrow_mut::<LuaCallbackContext>()?.values;
values.insert(b"beats_per_min", time_base.beats_per_min as LuaNumber);
values.insert(b"beats_per_min", time_base.beats_per_min as LuaNumber);
values.insert(b"beats_per_bar", time_base.beats_per_bar as LuaNumber);
values.insert(b"samples_per_sec", time_base.samples_per_sec as LuaNumber);
Ok(())
}

/// Sets external emitter context for the callback.
pub fn set_context_external_data(&mut self, data: &[(Cow<str>, f64)]) -> LuaResult<()> {
let table = self.context.to_ref();
let external_values = &mut self
.context
.borrow_mut::<LuaCallbackContext>()?
.external_values;
for (key, value) in data {
table.raw_set(key as &str, *value)?;
external_values.insert(key.to_string(), *value as LuaNumber);
}
Ok(())
}

/// Sets the pulse value emitter context for the callback.
pub fn set_context_pulse_value(&mut self, pulse: PulseIterItem) -> LuaResult<()> {
let table = self.context.to_ref();
table.raw_set("pulse_value", pulse.value)?;
table.raw_set("pulse_time", pulse.step_time)?;
let values = &mut self.context.borrow_mut::<LuaCallbackContext>()?.values;
values.insert(b"pulse_value", pulse.value as LuaNumber);
values.insert(b"pulse_time", pulse.step_time as LuaNumber);
Ok(())
}

Expand All @@ -142,16 +153,16 @@ impl LuaCallback {
pulse_step: usize,
pulse_time_step: f64,
) -> LuaResult<()> {
let table = self.context.to_ref();
table.raw_set("pulse_step", pulse_step + 1)?;
table.raw_set("pulse_time_step", pulse_time_step)?;
let values = &mut self.context.borrow_mut::<LuaCallbackContext>()?.values;
values.insert(b"pulse_step", (pulse_step + 1) as LuaNumber);
values.insert(b"pulse_time_step", pulse_time_step as LuaNumber);
Ok(())
}

/// Sets the step emitter context for the callback.
pub fn set_context_step(&mut self, step: usize) -> LuaResult<()> {
let table = self.context.to_ref();
table.raw_set("step", step + 1)?;
let values = &mut self.context.borrow_mut::<LuaCallbackContext>()?.values;
values.insert(b"step", (step + 1) as LuaNumber);
Ok(())
}

Expand All @@ -162,10 +173,10 @@ impl LuaCallback {
step: usize,
step_length: f64,
) -> LuaResult<()> {
let table = self.context.to_ref();
table.raw_set("channel", channel + 1)?;
table.raw_set("step", step + 1)?;
table.raw_set("step_length", step_length)?;
let values = &mut self.context.borrow_mut::<LuaCallbackContext>()?.values;
values.insert(b"channel", (channel + 1) as LuaNumber);
values.insert(b"step", (step + 1) as LuaNumber);
values.insert(b"step_length", step_length as LuaNumber);
Ok(())
}

Expand Down Expand Up @@ -221,34 +232,45 @@ impl LuaCallback {
Ok(())
}

/// Name of the inner function for errors. Usually will be an annonymous function.
/// Name of the inner function for errors. Usually will be an anonymous function.
pub fn name(&self) -> String {
self.function
.to_ref()
.info()
.name
.unwrap_or("annonymous function".to_string())
.unwrap_or("anonymous function".to_string())
}

/// Invoke the Lua function callback or generator.
/// Returns true if the callback is a generator.
///
/// To test this, the callback must have run at least once, so it returns None if it never has.
pub fn is_stateful(&self) -> Option<bool> {
if self.initialized {
Some(self.generator.is_some())
} else {
None
}
}

/// Invoke the Lua function or generator and return its result as LuaValue.
pub fn call(&mut self) -> LuaResult<LuaValue> {
self.call_with_arg(LuaValue::Nil)
}

/// Invoke the Lua function callback or generator with an additional argument.
/// Invoke the Lua function or generator with an additional argument and return its result as LuaValue.
pub fn call_with_arg<'lua, A: IntoLua<'lua> + Clone>(
&'lua mut self,
arg: A,
) -> LuaResult<LuaValue<'lua>> {
if self.initialized {
self.function.call((self.context.to_ref(), arg))
self.function.call((&self.context, arg))
} else {
self.initialized = true;
let result = {
// HACK: don't borrow self here, so we can borrow mut again to assign the generator function
// see https://stackoverflow.com/questions/73641155/how-to-force-rust-to-drop-a-mutable-borrow
let function = unsafe { &*(&self.function as *const LuaOwnedFunction) };
function.call::<_, LuaValue>((self.context.to_ref(), arg.clone()))?
function.call::<_, LuaValue>((&self.context, arg.clone()))?
};
if let Some(inner_function) = result.as_function().cloned().map(|f| f.into_owned()) {
// function returned a function -> is a generator. use the inner function instead.
Expand All @@ -259,8 +281,7 @@ impl LuaCallback {
.map(LuaTable::into_owned);
self.environment = environment;
self.generator = Some(std::mem::replace(&mut self.function, inner_function));
self.function
.call::<_, LuaValue>((self.context.to_ref(), arg))
self.function.call::<_, LuaValue>((&self.context, arg))
} else {
// function returned some value. use this function directly.
self.environment = None;
Expand Down Expand Up @@ -288,7 +309,7 @@ impl LuaCallback {
// then fetch a new fresh function from the generator
let value = function_generator
.to_ref()
.call::<_, LuaValue>(self.context.to_ref())?;
.call::<_, LuaValue>(&self.context)?;
if let Some(function) = value.as_function() {
self.function = function.clone().into_owned();
} else {
Expand All @@ -305,6 +326,43 @@ impl LuaCallback {
}
}

// -------------------------------------------------------------------------------------------------

/// Memorizes an optional set of values that are passed along as context with the callback.
#[derive(Debug, Clone)]
struct LuaCallbackContext {
values: HashMap<&'static [u8], LuaNumber>,
external_values: HashMap<String, LuaNumber>,
}

impl LuaCallbackContext {
fn register(lua: &Lua) -> LuaResult<()> {
// NB: registering for a specific engine is faster than implementing the UserData impl
// See https://github.com/mlua-rs/mlua/discussions/283
lua.register_userdata_type::<LuaCallbackContext>(|reg| {
reg.add_meta_field_with("__index", |lua| {
lua.create_function(
|lua, (this, key): (LuaUserDataRef<LuaCallbackContext>, LuaString)| {
if let Some(value) = this.values.get(key.as_bytes()) {
// fast path (string bytes)
value.into_lua(lua)
} else if let Some(value) =
this.external_values.get(key.to_string_lossy().as_ref())
{
// slower path (string )
value.into_lua(lua)
} else {
Err(mlua::Error::RuntimeError(
"no such field in context".to_string(),
))
}
},
)
})
})
}
}

// --------------------------------------------------------------------------------------------------

#[cfg(test)]
Expand Down
10 changes: 9 additions & 1 deletion src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,15 @@ pub trait EventIter: Debug {
/// Returns an optional stack of event iter items, which should be emitted for the given pulse.
fn run(&mut self, pulse: PulseIterItem, emit_event: bool) -> Option<Vec<EventIterItem>>;

/// Create a new cloned instance of this event iter. This actualy is a clone(), wrapped into
/// Move iterator with the given pulse value forward without generating an event.
///
/// This can be used to optimize iterator skipping in some EventIter implementations, but by
/// default calls `run` and simply discards the generated event return value.
fn advance(&mut self, pulse: PulseIterItem, emit_event: bool) {
let _ = self.run(pulse, emit_event);
}

/// Create a new cloned instance of this event iter. This actually is a clone(), wrapped into
/// a `Box<dyn EventIter>`, but called 'duplicate' to avoid conflicts with possible
/// Clone impls.
fn duplicate(&self) -> Box<dyn EventIter>;
Expand Down
14 changes: 10 additions & 4 deletions src/event/cycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ impl CycleEventIter {
}

/// Generate a note event from a single cycle event, applying mappings if necessary
fn note_events(&mut self, event: CycleEvent) -> Result<Vec<Option<NoteEvent>>, String> {
fn map_note_event(&mut self, event: CycleEvent) -> Result<Vec<Option<NoteEvent>>, String> {
let mut note_events = {
if let Some(note_events) = self.mappings.get(event.string()) {
// apply custom note mappings
Expand All @@ -215,7 +215,7 @@ impl CycleEventIter {

/// Generate next batch of events from the next cycle run.
/// Converts cycle events to note events and flattens channels into note columns.
fn generate_events(&mut self) -> Vec<EventIterItem> {
fn generate(&mut self) -> Vec<EventIterItem> {
// run the cycle event generator
let events = {
match self.cycle.generate() {
Expand All @@ -232,7 +232,7 @@ impl CycleEventIter {
for event in channel_events.into_iter() {
let start = event.span().start();
let length = event.span().length();
match self.note_events(event) {
match self.map_note_event(event) {
Ok(note_events) => {
if !note_events.is_empty() {
timed_note_events.add(channel_index, start, length, note_events);
Expand Down Expand Up @@ -261,12 +261,18 @@ impl EventIter for CycleEventIter {

fn run(&mut self, _pulse: PulseIterItem, emit_event: bool) -> Option<Vec<EventIterItem>> {
if emit_event {
Some(self.generate_events())
Some(self.generate())
} else {
None
}
}

fn advance(&mut self, _pulse: PulseIterItem, emit_event: bool) {
if emit_event {
self.cycle.advance();
}
}

fn duplicate(&self) -> Box<dyn EventIter> {
Box::new(self.clone())
}
Expand Down
12 changes: 8 additions & 4 deletions src/event/fixed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,17 @@ impl EventIter for FixedEventIter {
return None;
}
let event = self.events[self.event_index].clone();
self.event_index += 1;
if self.event_index >= self.events.len() {
self.event_index = 0;
}
self.event_index = (self.event_index + 1) % self.events.len();
Some(vec![EventIterItem::new(event)])
}

fn advance(&mut self, _pulse: PulseIterItem, emit_event: bool) {
if !emit_event || self.events.is_empty() {
return;
}
self.event_index = (self.event_index + 1) % self.events.len();
}

fn duplicate(&self) -> Box<dyn EventIter> {
Box::new(self.clone())
}
Expand Down
16 changes: 6 additions & 10 deletions src/event/mutated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,13 @@ impl EventIter for MutatedEventIter {
}

fn run(&mut self, _pulse: PulseIterItem, emit_event: bool) -> Option<Vec<EventIterItem>> {
if emit_event {
let event = self.events[self.event_index].clone();
self.events[self.event_index] = Self::mutate(event.clone(), &mut self.map);
self.event_index += 1;
if self.event_index >= self.events.len() {
self.event_index = 0;
}
Some(vec![EventIterItem::new(event)])
} else {
None
if !emit_event || self.events.is_empty() {
return None;
}
let event = self.events[self.event_index].clone();
self.events[self.event_index] = Self::mutate(event.clone(), &mut self.map);
self.event_index = (self.event_index + 1) % self.events.len();
Some(vec![EventIterItem::new(event)])
}

fn duplicate(&self) -> Box<dyn EventIter> {
Expand Down
Loading

0 comments on commit 1d73755

Please sign in to comment.