feat(rivet): storyboard composition with flexible lengths and meters
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
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.
This commit is contained in:
1 parent
abd37fd4fe
commit
f1366280ff
37 files changed
+3763
-2549
No files matched your search
@@ -1,15 +1,15 @@
|
||||
//! Shared application state and bounded control messages. The audio callback never locks a session.
|
||||
use crossbeam_queue::ArrayQueue;
|
||||
use rivet_core::{
|
||||
Audition, Candidate, Error, EventBatch, MessageKind, MidiMessage, Performance, PerformanceClip,
|
||||
PerformancePlayer, Result, Session, SECTION,
|
||||
Audition, Error, EventBatch, MessageKind, MidiMessage, Performance, PerformanceClip,
|
||||
PerformancePlayer, Result, Session,
|
||||
};
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Write},
|
||||
path::{Path, PathBuf},
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||
atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering},
|
||||
Arc, Mutex, RwLock,
|
||||
},
|
||||
};
|
||||
@@ -34,17 +34,17 @@ pub struct Shared {
|
||||
pub queued: AtomicBool,
|
||||
pub playing: AtomicBool,
|
||||
pub playhead: AtomicU64,
|
||||
/// Bar length in ticks; queued auditions switch on the next bar line.
|
||||
pub bar: AtomicU32,
|
||||
pub commands: ArrayQueue<PlayRequest>,
|
||||
pub busy: AtomicBool,
|
||||
results: Mutex<Vec<Completion>>,
|
||||
}
|
||||
pub enum Output {
|
||||
Imported(rivet_midi::MidiFile),
|
||||
Generated(Vec<Candidate>),
|
||||
Exported(PathBuf),
|
||||
Saved(PathBuf),
|
||||
Loaded(Box<Session>),
|
||||
LibraryLoaded(Vec<rivet_core::Gesture>),
|
||||
Canceled,
|
||||
}
|
||||
pub struct Completion {
|
||||
@@ -65,6 +65,7 @@ impl Default for Shared {
|
||||
queued: AtomicBool::new(false),
|
||||
playing: AtomicBool::new(false),
|
||||
playhead: AtomicU64::new(0),
|
||||
bar: AtomicU32::new(rivet_core::BAR),
|
||||
commands: ArrayQueue::new(2),
|
||||
busy: AtomicBool::new(false),
|
||||
results: Mutex::new(Vec::new()),
|
||||
@@ -87,6 +88,7 @@ impl Shared {
|
||||
self.midi
|
||||
.store(s.audition == Audition::Midi, Ordering::Release);
|
||||
self.looping.store(s.looping, Ordering::Release);
|
||||
self.bar.store(s.settings.meter.bar(), Ordering::Release);
|
||||
self.revision.fetch_add(1, Ordering::AcqRel);
|
||||
}
|
||||
pub fn replace(&self, mut session: Session) -> Result<()> {
|
||||
@@ -107,10 +109,7 @@ impl Shared {
|
||||
"Session changed while this operation was running. Try again.".into(),
|
||||
));
|
||||
}
|
||||
if current.source != session.source
|
||||
|| current.settings != session.settings
|
||||
|| current.harmony != session.harmony
|
||||
|| current.candidates != session.candidates
|
||||
if current.settings.meter != session.settings.meter
|
||||
|| current.performance != session.performance
|
||||
|| current.audition != session.audition
|
||||
{
|
||||
@@ -130,8 +129,9 @@ impl Shared {
|
||||
};
|
||||
let settings = state.performance.clone();
|
||||
let compensate = state.audition == Audition::Midi;
|
||||
let meter = state.settings.meter;
|
||||
drop(state);
|
||||
Performance::compile(phrase, &settings, tempo, compensate)?.clip()
|
||||
Performance::compile(phrase, &settings, tempo, compensate, meter)?.clip()
|
||||
}
|
||||
pub fn preview(&self, phrase: &rivet_core::Phrase) -> Result<()> {
|
||||
let epoch = self.epoch.load(Ordering::Acquire);
|
||||
@@ -279,9 +279,8 @@ impl AudioEngine {
|
||||
if request.quantized && self.player.is_playing() {
|
||||
let now = self.player.absolute_tick();
|
||||
let lead = request.clip.lead_in as f64;
|
||||
let boundary = ((now + lead) / SECTION as f64)
|
||||
.floor()
|
||||
.mul_add(SECTION as f64, SECTION as f64);
|
||||
let bar = shared.bar.load(Ordering::Acquire).max(1) as f64;
|
||||
let boundary = ((now + lead) / bar).floor().mul_add(bar, bar);
|
||||
self.pending = Some((boundary - lead, request.clip));
|
||||
} else {
|
||||
self.player.play(request.clip);
|
||||
|
||||
Reference in new issue
Block a user