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,5 +1,5 @@
|
||||
//! Explicit sound roles and controller gestures, compiled away from the audio thread.
|
||||
use crate::{err, Note, Phrase, Result, BAR, MAX_PLAYBACK_NOTES, PPQ, SECTION};
|
||||
use crate::{err, Meter, Note, Phrase, Result, MAX_LENGTH, MAX_PLAYBACK_NOTES, PPQ};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::f64::consts::TAU;
|
||||
|
||||
@@ -28,12 +28,14 @@ impl RoleMode {
|
||||
Self::Interlock => "Strong beats and replies",
|
||||
}
|
||||
}
|
||||
pub fn role(self, index: usize, n: &Note) -> usize {
|
||||
/// Which of the two voices plays note `index`. Question/answer alternates bars;
|
||||
/// strong beats follow the meter's pulse.
|
||||
pub fn role(self, index: usize, n: &Note, meter: Meter) -> usize {
|
||||
match self {
|
||||
Self::Solo => 0,
|
||||
Self::Alternating => index % 2,
|
||||
Self::CallResponse => usize::from(n.start % SECTION >= BAR),
|
||||
Self::Interlock => usize::from(!n.start.is_multiple_of(PPQ)),
|
||||
Self::CallResponse => ((n.start / meter.bar()) % 2) as usize,
|
||||
Self::Interlock => usize::from(!meter.strong(n.start)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,6 +226,7 @@ impl Performance {
|
||||
settings: &PerformanceSettings,
|
||||
bpm: f64,
|
||||
compensate: bool,
|
||||
meter: Meter,
|
||||
) -> Result<Self> {
|
||||
phrase.validate(MAX_PLAYBACK_NOTES)?;
|
||||
settings.validate()?;
|
||||
@@ -252,7 +255,7 @@ impl Performance {
|
||||
.notes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, n)| settings.mode.role(*i, n) == role)
|
||||
.filter(|(i, n)| settings.mode.role(*i, n, meter) == role)
|
||||
.map(|(_, n)| n)
|
||||
.collect();
|
||||
if routed.is_empty() && !profile.controller.enabled {
|
||||
@@ -378,7 +381,7 @@ impl Performance {
|
||||
pub fn clip(&self) -> Result<PerformanceClip> {
|
||||
if self.events.len() > MAX_PERFORMANCE_EVENTS
|
||||
|| self.length == 0
|
||||
|| self.length > 8 * BAR
|
||||
|| self.length > MAX_LENGTH
|
||||
|| self.lead_in >= self.length
|
||||
{
|
||||
return Err(err("Invalid performance clip."));
|
||||
|
||||
Reference in new issue
Block a user