Check and Windows preview / check (patina) (push) Successful in 3m59s
Check and Windows preview / check (rivet) (push) Successful in 2m2s
Check and Windows preview / previews (push) Successful in 1m8s
Independent plugin release / plan (push) Successful in 7s
Check and Windows preview / windows (push) Successful in 2m41s
Independent plugin release / build (push) Successful in 3m0s
Independent plugin release / publish (push) Successful in 9s
Rivet 0.4.0 tells a motif as a story. A timeline of blocks with roles (statement, answer, departure, return, rest) replaces tasks, pins and branches. Rivet writes each block from a motif and joins the seams with a cut, breath, pickup or approach. - Motifs of 1-8 bars in a bin of 16; storyboards up to 32 bars. - Meters 2/4, 3/4, 4/4, 5/4, 6/8, 7/8 and 12/8 for import, writing, audition and export. Phrygian, harmonic minor and Mixolydian scales. - Per-block options, Another take and Keep; templates for new boards. - Editor rebuilt: motif bin, timeline, large piano roll, inspector, tabs for generation, harmony and sound roles, 32-step undo/redo. - Sessions from 0.1-0.3 open as a storyboard from their source motif.
125 lines
3.6 KiB
Rust
125 lines
3.6 KiB
Rust
use rivet_app::{AudioEngine, Shared};
|
|
use rivet_core::{Note, Phrase, BAR, PPQ};
|
|
|
|
fn clip(pitch: u8, length: u32) -> Phrase {
|
|
Phrase {
|
|
length,
|
|
notes: vec![Note {
|
|
start: 0,
|
|
duration: length,
|
|
pitch,
|
|
velocity: 90,
|
|
..Default::default()
|
|
}],
|
|
}
|
|
}
|
|
#[test]
|
|
fn looping_and_mode_changes_release_and_retrigger_notes() {
|
|
let shared = Shared::default();
|
|
let mut a = AudioEngine::default();
|
|
a.player.set_sample_rate(8000.).unwrap();
|
|
shared.preview(&clip(60, PPQ)).unwrap();
|
|
a.begin_block(&shared);
|
|
let first = a.frame(120., true);
|
|
assert_eq!(first.on, Some((60, 90)));
|
|
assert_eq!(first.off, None);
|
|
let mut repeated = false;
|
|
for _ in 0..4001 {
|
|
let frame = a.frame(120., true);
|
|
if frame.off.is_some() {
|
|
assert_eq!(frame.off, Some(60));
|
|
assert_eq!(frame.on, Some((60, 90)));
|
|
repeated = true;
|
|
break;
|
|
}
|
|
}
|
|
assert!(repeated);
|
|
let muted = a.frame(120., false);
|
|
assert_eq!(muted.off, Some(60));
|
|
assert_eq!(muted.on, None);
|
|
assert_eq!(a.frame(120., true).on, Some((60, 90)));
|
|
shared.stop();
|
|
a.begin_block(&shared);
|
|
let stopped = a.frame(120., true);
|
|
assert_eq!(stopped.off, Some(60));
|
|
assert_eq!(stopped.on, None);
|
|
}
|
|
#[test]
|
|
fn adjacent_same_pitch_notes_have_separate_sample_accurate_attacks() {
|
|
let shared = Shared::default();
|
|
shared
|
|
.looping
|
|
.store(false, std::sync::atomic::Ordering::Release);
|
|
let mut a = AudioEngine::default();
|
|
a.player.set_sample_rate(8000.).unwrap();
|
|
let p = Phrase {
|
|
length: PPQ,
|
|
notes: vec![
|
|
Note {
|
|
start: 0,
|
|
duration: PPQ / 2,
|
|
pitch: 60,
|
|
velocity: 90,
|
|
..Default::default()
|
|
},
|
|
Note {
|
|
start: PPQ / 2,
|
|
duration: PPQ / 2,
|
|
pitch: 60,
|
|
velocity: 70,
|
|
..Default::default()
|
|
},
|
|
],
|
|
};
|
|
shared.preview(&p).unwrap();
|
|
a.begin_block(&shared);
|
|
let mut events = Vec::new();
|
|
for frame in 0i32..4003 {
|
|
let e = a.frame(120., true);
|
|
if e.off.is_some() || e.on.is_some() {
|
|
events.push((frame, e.off, e.on));
|
|
}
|
|
}
|
|
assert_eq!(events.len(), 3);
|
|
assert_eq!(events[0], (0, None, Some((60, 90))));
|
|
assert!((events[1].0 - 2000).abs() <= 1);
|
|
assert_eq!(events[1].1, Some(60));
|
|
assert_eq!(events[1].2, Some((60, 70)));
|
|
assert!((events[2].0 - 4000).abs() <= 1);
|
|
assert_eq!(events[2].1, Some(60));
|
|
assert_eq!(events[2].2, None);
|
|
}
|
|
#[test]
|
|
fn queued_audition_switches_at_the_next_bar_line_and_stop_cancels_it() {
|
|
let shared = Shared::default();
|
|
let mut a = AudioEngine::default();
|
|
a.player.set_sample_rate(8000.).unwrap();
|
|
shared.preview(&clip(60, 4 * BAR)).unwrap();
|
|
a.begin_block(&shared);
|
|
for _ in 0..100 {
|
|
a.frame(120., true);
|
|
}
|
|
a.end_block(&shared);
|
|
shared.audition(&clip(67, 4 * BAR)).unwrap();
|
|
a.begin_block(&shared);
|
|
let mut switched = None;
|
|
for index in 100i32..16003 {
|
|
let f = a.frame(120., true);
|
|
if f.on == Some((67, 90)) {
|
|
assert_eq!(f.off, Some(60));
|
|
switched = Some(index);
|
|
break;
|
|
}
|
|
}
|
|
assert!((switched.unwrap() - 16000).abs() <= 1);
|
|
a.end_block(&shared);
|
|
shared.audition(&clip(72, 2 * BAR)).unwrap();
|
|
a.begin_block(&shared);
|
|
shared.stop();
|
|
a.begin_block(&shared);
|
|
let f = a.frame(120., true);
|
|
assert_eq!(f.off, Some(67));
|
|
assert_eq!(f.on, None);
|
|
assert!(!a.player.is_playing());
|
|
}
|