In Dittytoy, each loop runs in a separate thread/worker. All filters chained to the loop run, by default, in the same thread.
Sometimes, however, you want to run a filter in a separate thread. The most likely situation is that you want to process the output of multiple loops through the same filter. You can enforce this by creating a filter with the createShared() method (instead of create()).
You can connect a (shared) filter to other (shared) filters using the connect method.
dittytoy.net/syntax#filters
#dittytoy #tutorial
Log in to post a comment.
// #12 Shared Filters. DittyToy 2022.
// The MIT License.
//
// https://dittytoy.net/ditty/ccd086456e
//
// In Dittytoy, each loop runs in a separate thread/worker. All filters chained to the loop run, by default,
// in the same thread.
//
// Sometimes, however, you want to run a filter in a separate thread. This can be for performance reasons
// (although each additional thread also gives additional overhead). However, the most likely situation is
// that you want to process the output of multiple loops through the same filter. You can enforce this by
// creating a filter with the createShared() method (instead of create()).
//
// You can connect a (shared) filter to other (shared) filters using the connect method.
//
// Note 1! After you connect a loop to a shared filter, you cannot connect it to subsequent filters.
// Note 2! Using shared filters will increase latency a bit.
//
// https://dittytoy.net/syntax#filters
//
// Compressor by athibaul - https://dittytoy.net/ditty/9d16ed4102
const compressor = filter.def(class {
constructor(options) {
this.gain = 1;
}
process(input, options) {
let sigdb = 10 * Math.log10(input[0]**2 + input[1]**2);
let overdb = sigdb - options.threshold;
if(overdb < 0) {
overdb = 0;
}
let target_gaindb = overdb * (1 - options.ratio)/options.ratio;
let target_gain = 10 ** (target_gaindb / 20);
let time = this.gain > target_gain ? options.attack : options.release;
let a0 = clamp01(2.3 * ditty.dt / time); // 'time' to go to within 10% of the final value
this.gain = lerp(this.gain, target_gain, a0);
let gr = 20 * Math.log10(this.gain);
return [input[0] * this.gain, input[1] * this.gain];
}
}, {threshold:-18, ratio:2, attack:0.005, release:0.2});
// create a shared filter
let sharedCompressor = compressor.createShared();
// synth and loops
const kick = synth.def( (phase, env, tick, options) => Math.sin(phase * 2 * Math.PI * (1.5 - tick * 4)) * env.value, { amp: 4 } );
const tri = synth.def( (phase, env, tick, options) => { const v = (phase % 1) * 4; return (v < 2 ? v - 1 : 3 - v) * env.value }, { attack: 0.0125, release: 0.25 });
ditty.bpm = 140;
loop( (loopCount) => {
kick.play(c3, { attack: 0.025, release: 0.05 });
}, { sync: 1, name: 'kick' })
.connect( sharedCompressor ); // connect loop to shared filter
loop( (loopCount) => {
scale(c3, scales.minor_pentatonic, 1).forEach( note => {
tri.play(note, { pan: Math.random() * 2 - 1 });
sleep(0.25);
});
}, { name: 'melody' })
.connect( sharedCompressor ); // connect another loop to the same shared filter