6 releases
0.1.6 | Mar 15, 2020 |
---|---|
0.1.5 | Dec 12, 2019 |
#352 in Audio
Used in chord-composer
34KB
406 lines
Music-Timer
Music Timer is a crate with music time and counting utilities featuring a callback performance engine to help with triggering events in music time. Written in Rust.
Its aims are to allow for an easy interface to trigger events in music time. This crate does not feature any threading.
Performance engine
To create a performance engine with a time signature and bpm call music_timer::create_performance_engine
.
let mut performer = music_timer::create_performance_engine(3, 4, 155.0);
Create a struct
with the trait MusicTimerState
and pass it into the engine's pulse. The callbacks will be triggered in music time.
struct PerformanceState;
impl MusicTimerState for PerformanceState {
fn on_beat_interval(&mut self, current_time: &MusicTime) {
// Do something on the beat interval
}
fn on_beat(&mut self, current_time: &MusicTime) {
// Do something on the beat
}
fn on_bar(&mut self, current_time: &MusicTime) {
// Do something on the bar
}
}
let mut performer_state = PerformanceState{};
let mut performer = music_timer::create_performance_engine(3, 4, 155.0);
performer.pulse(&mut performer_state);
Future work
- Support un orthodox time signatures e.g 3/5.
- Explore the need for higher resolution beat intervals.
Example
use music_timer::{
music_time::MusicTime,
music_timer_engine::MusicTimerState,
};
struct PerformanceState {
is_playing: bool,
performance_end: MusicTime,
events: Vec<MusicTime>,
event_head: usize,
}
impl MusicTimerState for PerformanceState {
fn on_beat_interval(&mut self, current_time: &MusicTime) {
let event_triggered =
self.event_head < self.events.len() && *current_time == self.events[self.event_head];
// Advance the event head
if event_triggered {
self.event_head += 1;
}
// Print out esoteric data
println!(
"{:02}.{}.{} = {}",
current_time.get_bar(),
current_time.get_beat(),
current_time.get_beat_interval(),
event_triggered
);
// Check to end the performance
self.is_playing = *current_time < self.performance_end;
}
fn on_beat(&mut self, _current_time: &MusicTime) {
// Do something on the beat
}
fn on_bar(&mut self, _current_time: &MusicTime) {
// Do something on the bar
}
}
fn main() {
use std::thread;
// Create the performer_state with bunch of events
let mut performer_state = PerformanceState {
is_playing: true,
performance_end: MusicTime::new(4, 3, 8),
events: vec![
MusicTime::new(1, 1, 1),
MusicTime::new(2, 2, 5),
MusicTime::new(4, 3, 8),
],
event_head: 0,
};
// Run our main loop
let mut performer = music_timer::create_performance_engine(3, 4, 155.0);
// We can set the delay to be half the trigger target. This will give
// us a reasonable cycle speed with enough buffer to keep an accurate time.
// This of course is not needed if the application is managing thread sleeping.
// The shorter the sleep duration of the thread, the more accurate the
// time triggering will be. In most cases setting the sleep to 60fps is recommended for
// < 180bpm @ 4/4.
let sleep_duration = performer.get_beat_interval_duration() / 2;
println!("SLEEP_DURATION: {:?}", sleep_duration);
while performer_state.is_playing {
// Pass in our performance state to trigger our on event callback functions
performer.pulse(&mut performer_state);
thread::sleep(sleep_duration);
}
}