Skip to content

Commit 7b1b6ac

Browse files
committed
Persistent value loops
1 parent c7b0905 commit 7b1b6ac

File tree

3 files changed

+81
-67
lines changed

3 files changed

+81
-67
lines changed

caw/examples/live_example.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ fn main() {
3333
.ungated();
3434
let cutoff_hz =
3535
value_looper(cutoff_hz.clone(), clock.clone(), lpf_space.clone())
36+
.persist_with_name("low_pass")
3637
.length(length)
3738
.build();
3839
let env = adsr(gate)

persist/src/lib.rs

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -75,63 +75,26 @@ where
7575
}
7676
}
7777

78-
/// Implement this when there may be multiple different directories where values of the type will
79-
/// be persisted. It's probably more convenient to just use the `&'static str` impl of this trait
80-
/// in such a case.
81-
pub trait Persist<T>
82-
where
83-
T: Serialize + for<'a> Deserialize<'a>,
84-
{
85-
fn name(&self) -> &'static str;
86-
87-
fn save(&self, data: &T, title: impl AsRef<str>) -> anyhow::Result<()> {
88-
save(self.name(), data, title)
89-
}
90-
91-
/// Like `save` but prints a warning on failure rather than returning an error value.
92-
fn save_(&self, data: &T, title: impl AsRef<str>) {
93-
save_(self.name(), data, title)
94-
}
95-
96-
fn load(&self, title: impl AsRef<str>) -> anyhow::Result<T> {
97-
load(self.name(), title)
98-
}
99-
100-
/// Like `load` but prints a warning on failure rather than returning an error value.
101-
fn load_(&self, title: impl AsRef<str>) -> Option<T> {
102-
load_(self.name(), title)
103-
}
104-
}
105-
106-
impl<T> Persist<T> for &'static str
107-
where
108-
T: Serialize + for<'a> Deserialize<'a>,
109-
{
110-
fn name(&self) -> &'static str {
111-
self
112-
}
113-
}
114-
11578
/// Implement this when the type uniquely determines the directory where values of that type will
11679
/// be persisted.
11780
pub trait PersistData: Serialize + for<'a> Deserialize<'a> {
11881
const NAME: &'static str;
11982

12083
fn save(&self, title: impl AsRef<str>) -> anyhow::Result<()> {
121-
Self::NAME.save(self, title)
84+
save(Self::NAME, self, title)
12285
}
12386

12487
/// Like `save` but prints a warning on failure rather than returning an error value.
12588
fn save_(&self, title: impl AsRef<str>) {
126-
Self::NAME.save_(self, title)
89+
save_(Self::NAME, self, title)
12790
}
12891

12992
fn load(title: impl AsRef<str>) -> anyhow::Result<Self> {
130-
Self::NAME.load(title)
93+
load(Self::NAME, title)
13194
}
13295

13396
/// Like `load` but prints a warning on failure rather than returning an error value.
13497
fn load_(title: impl AsRef<str>) -> Option<Self> {
135-
Self::NAME.load_(title)
98+
load_(Self::NAME, title)
13699
}
137100
}

utils/src/looper.rs

Lines changed: 76 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use caw_builder_proc_macros::builder;
22
use caw_core::{Buf, Sig, SigCtx, SigT};
3-
use caw_persist::Persist;
3+
use caw_persist::PersistData;
44
use itertools::izip;
55
use serde::{Deserialize, Serialize};
66

@@ -42,40 +42,45 @@ impl<T> Sequence<T> {
4242
}
4343
}
4444

45-
const KEY_LOOPER_PERSIST: &'static str = "key_looper";
45+
impl<T> PersistData for Sequence<Option<T>>
46+
where
47+
T: Serialize + for<'a> Deserialize<'a>,
48+
{
49+
const NAME: &'static str = "sequence";
50+
}
4651

4752
/// Driver for saving and loading a sequence state to a file.
48-
pub trait KeyLooperIo<T> {
53+
pub trait LooperIo<T> {
4954
fn load(&self) -> Sequence<Option<T>>;
5055
fn save(&self, sequence: &Sequence<Option<T>>);
5156
}
5257

53-
/// Implementation of `KeyLooperIo` which doesn't actually save or load any data.
54-
pub struct KeyLooperIoNull;
55-
impl<T> KeyLooperIo<T> for KeyLooperIoNull {
58+
/// Implementation of `LooperIo` which doesn't actually save or load any data.
59+
pub struct LooperIoNull;
60+
impl<T> LooperIo<T> for LooperIoNull {
5661
fn load(&self) -> Sequence<Option<T>> {
5762
Sequence::new_with(1, || None)
5863
}
5964

6065
fn save(&self, _sequence: &Sequence<Option<T>>) {}
6166
}
6267

63-
/// Implementation of `KeyLooperIo` which saves state into a file of a given name.
64-
pub struct KeyLooperIoWithName(pub String);
65-
impl<T> KeyLooperIo<T> for KeyLooperIoWithName
68+
/// Implementation of `LooperIo` which saves state into a file of a given name.
69+
pub struct LooperIoWithName(pub String);
70+
impl<T> LooperIo<T> for LooperIoWithName
6671
where
6772
T: Serialize + for<'a> Deserialize<'a>,
6873
{
6974
fn load(&self) -> Sequence<Option<T>> {
70-
if let Some(sequence) = KEY_LOOPER_PERSIST.load_(&self.0) {
75+
if let Some(sequence) = Sequence::load_(&self.0) {
7176
sequence
7277
} else {
7378
Sequence::new_with(1, || None)
7479
}
7580
}
7681

7782
fn save(&self, sequence: &Sequence<Option<T>>) {
78-
KEY_LOOPER_PERSIST.save_(sequence, &self.0)
83+
sequence.save_(&self.0)
7984
}
8085
}
8186

@@ -86,7 +91,7 @@ where
8691
T: SigT<Item = bool>,
8792
C: SigT<Item = bool>,
8893
N: SigT<Item = u32>,
89-
I: KeyLooperIo<X>,
94+
I: LooperIo<X>,
9095
{
9196
sig: S,
9297
last_value: Option<X>,
@@ -105,7 +110,7 @@ where
105110
T: SigT<Item = bool>,
106111
C: SigT<Item = bool>,
107112
N: SigT<Item = u32>,
108-
I: KeyLooperIo<X>,
113+
I: LooperIo<X>,
109114
{
110115
type Item = S::Item;
111116

@@ -154,7 +159,7 @@ where
154159
T: SigT<Item = bool>,
155160
C: SigT<Item = bool>,
156161
N: SigT<Item = u32>,
157-
I: KeyLooperIo<X>,
162+
I: LooperIo<X>,
158163
{
159164
fn new(sig: S, tick: T, clearing: C, length: N, io: I) -> Sig<Self> {
160165
Sig(KeyLooper {
@@ -192,10 +197,10 @@ builder! {
192197
#[generic_name = "N"]
193198
#[default = 16]
194199
length: u32,
195-
#[generic_with_constraint = "KeyLooperIo<V>"]
196-
#[default = KeyLooperIoNull]
200+
#[generic_with_constraint = "LooperIo<V>"]
201+
#[default = LooperIoNull]
197202
#[generic_name = "I"]
198-
io: KeyLooperIoNull,
203+
io: LooperIoNull,
199204
}
200205
}
201206

@@ -206,12 +211,12 @@ where
206211
T: SigT<Item = bool>,
207212
C: SigT<Item = bool>,
208213
N: SigT<Item = u32>,
209-
I: KeyLooperIo<X>,
214+
I: LooperIo<X>,
210215
{
211216
pub fn persist_with_name(
212217
self,
213218
name: impl AsRef<str>,
214-
) -> KeyLooperBuilder<X, S, T, C, N, KeyLooperIoWithName> {
219+
) -> KeyLooperBuilder<X, S, T, C, N, LooperIoWithName> {
215220
let Self {
216221
sig,
217222
trig,
@@ -224,34 +229,37 @@ where
224229
trig,
225230
clearing,
226231
length,
227-
io: KeyLooperIoWithName(name.as_ref().to_string()),
232+
io: LooperIoWithName(format!("key_looper_{}", name.as_ref())),
228233
}
229234
}
230235
}
231236

232-
pub struct ValueLooper<S, T, R, N>
237+
pub struct ValueLooper<S, T, R, N, I>
233238
where
234239
S: SigT,
235240
S::Item: Clone,
236241
T: SigT<Item = bool>,
237242
R: SigT<Item = bool>,
238243
N: SigT<Item = u32>,
244+
I: LooperIo<S::Item>,
239245
{
240246
sig: S,
241247
tick: T,
242248
recording: R,
243249
length: N,
244250
sequence: Sequence<Option<S::Item>>,
245251
buf: Vec<S::Item>,
252+
io: I,
246253
}
247254

248-
impl<S, T, R, N> SigT for ValueLooper<S, T, R, N>
255+
impl<S, T, R, N, I> SigT for ValueLooper<S, T, R, N, I>
249256
where
250257
S: SigT,
251258
S::Item: Clone,
252259
T: SigT<Item = bool>,
253260
R: SigT<Item = bool>,
254261
N: SigT<Item = u32>,
262+
I: LooperIo<S::Item>,
255263
{
256264
type Item = S::Item;
257265

@@ -261,6 +269,7 @@ where
261269
let tick = self.tick.sample(ctx);
262270
let recording = self.recording.sample(ctx);
263271
let length = self.length.sample(ctx);
272+
let mut changed_this_frame = false;
264273
for (sample, tick, recording, length) in izip! {
265274
sig.iter(),
266275
tick.iter(),
@@ -275,32 +284,39 @@ where
275284
let out = match (recording, stored.clone()) {
276285
(true, _) | (_, None) => {
277286
*stored = Some(sample.clone());
287+
changed_this_frame = true;
278288
sample
279289
}
280290
(_, Some(stored)) => stored,
281291
};
282292
self.buf.push(out);
283293
}
294+
if changed_this_frame {
295+
self.io.save(&self.sequence);
296+
}
284297
&self.buf
285298
}
286299
}
287300

288-
impl<S, T, R, N> ValueLooper<S, T, R, N>
301+
impl<S, T, R, N, I> ValueLooper<S, T, R, N, I>
289302
where
290303
S: SigT,
291304
S::Item: Clone,
292305
T: SigT<Item = bool>,
293306
R: SigT<Item = bool>,
294307
N: SigT<Item = u32>,
308+
I: LooperIo<S::Item>,
295309
{
296-
fn new(sig: S, tick: T, recording: R, length: N) -> Sig<Self> {
310+
fn new(sig: S, tick: T, recording: R, length: N, io: I) -> Sig<Self> {
311+
let sequence = io.load();
297312
Sig(ValueLooper {
298313
sig,
299314
tick,
300315
recording,
301316
length,
302-
sequence: Sequence::new_with(1, || None),
317+
sequence,
303318
buf: Vec::new(),
319+
io,
304320
})
305321
}
306322
}
@@ -310,7 +326,7 @@ builder! {
310326
#[constructor_doc = "A looper for values such as knob positions"]
311327
#[generic_setter_type_name = "X"]
312328
#[build_fn = "ValueLooper::new"]
313-
#[build_ty = "Sig<ValueLooper<S, T, R, N>>"]
329+
#[build_ty = "Sig<ValueLooper<S, T, R, N, I>>"]
314330
pub struct ValueLooperBuilder {
315331
#[generic_with_constraint = "SigT"]
316332
#[generic_name = "S"]
@@ -325,5 +341,39 @@ builder! {
325341
#[generic_name = "N"]
326342
#[default = 16]
327343
length: u32,
344+
#[generic_with_constraint = "LooperIo<S::Item>"]
345+
#[default = LooperIoNull]
346+
#[generic_name = "I"]
347+
io: LooperIoNull,
348+
}
349+
}
350+
351+
impl<S, T, R, N, I> ValueLooperBuilder<S, T, R, N, I>
352+
where
353+
S: SigT<Item = f32>,
354+
S::Item: Clone + Serialize + for<'a> Deserialize<'a>,
355+
T: SigT<Item = bool>,
356+
R: SigT<Item = bool>,
357+
N: SigT<Item = u32>,
358+
I: LooperIo<S::Item>,
359+
{
360+
pub fn persist_with_name(
361+
self,
362+
name: impl AsRef<str>,
363+
) -> ValueLooperBuilder<S, T, R, N, LooperIoWithName> {
364+
let Self {
365+
sig,
366+
trig,
367+
recording,
368+
length,
369+
..
370+
} = self;
371+
ValueLooperBuilder {
372+
sig,
373+
trig,
374+
recording,
375+
length,
376+
io: LooperIoWithName(format!("value_looper_{}", name.as_ref())),
377+
}
328378
}
329379
}

0 commit comments

Comments
 (0)