Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding filter envelopes and filter order selection #692

Merged
merged 40 commits into from
Sep 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ec8616a
Envelope filter and filter order
Bubobubobubobubo Sep 4, 2023
5be7898
New technique using ADSR envelope
Bubobubobubobubo Sep 4, 2023
4319df0
replace odd envelope
Bubobubobubobubo Sep 4, 2023
37842d4
Revert "replace odd envelope"
Bubobubobubobubo Sep 5, 2023
16c8dcf
stable version
Bubobubobubobubo Sep 5, 2023
e597b64
remove test console log statement
Bubobubobubobubo Sep 5, 2023
e2711ba
fix: filter envelopes + simplify
felixroos Sep 7, 2023
75a9746
add missing controls
felixroos Sep 7, 2023
9e4c548
fix: filter clicks
felixroos Sep 8, 2023
606dcc8
use fenv to scale above base cutoff?
felixroos Sep 8, 2023
48753b2
fix last crack?
felixroos Sep 8, 2023
18640d5
acid party
felixroos Sep 8, 2023
c5fb247
Documentation for filter envelope
Bubobubobubobubo Sep 8, 2023
534e3f7
add snapshots
felixroos Sep 9, 2023
bea9a3c
replacing fenv by lpenv, hpenv, bpenv
Bubobubobubobubo Sep 10, 2023
1383af6
documenting filters more + tests
Bubobubobubobubo Sep 10, 2023
7a95baf
Updating documentation
Bubobubobubobubo Sep 10, 2023
4458819
slow down examples + simplify
felixroos Sep 15, 2023
b893239
update snapshot
felixroos Sep 15, 2023
9d5f919
add all filter envelope examples to doc
felixroos Sep 15, 2023
828e475
support negative fenv
felixroos Sep 15, 2023
7e55258
update snapshots
felixroos Sep 15, 2023
4688c0b
format
felixroos Sep 15, 2023
9142fb7
cleaner default filter envelope
felixroos Sep 15, 2023
88c0292
simplify fenv examples
felixroos Sep 15, 2023
9049a8b
simplify fenv
felixroos Sep 15, 2023
ee314e9
snapshot
felixroos Sep 15, 2023
df211c9
anchored fenv, more logical with negative values
felixroos Sep 15, 2023
40de717
fix: don't add frequency
felixroos Sep 15, 2023
7f86eac
simplify
felixroos Sep 15, 2023
7dbcc90
fix: docs after fenv change
felixroos Sep 15, 2023
e959c27
add fanchor
felixroos Sep 15, 2023
735f892
fix: snapshots
felixroos Sep 15, 2023
989dc0a
fix: filters without envelopes
felixroos Sep 15, 2023
b96e809
doc: fenv
felixroos Sep 15, 2023
af6a5b8
keep only lp envelopes as examples
felixroos Sep 15, 2023
a7eb0bb
snapshot
felixroos Sep 15, 2023
5364273
add some filter envelopes here and there
felixroos Sep 15, 2023
e0c424e
update snapshots
felixroos Sep 15, 2023
e3b7d0f
drop some acid in there
felixroos Sep 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
221 changes: 221 additions & 0 deletions packages/core/controls.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,227 @@ const generic_params = [
*
*/
[['cutoff', 'resonance'], 'ctf', 'lpf', 'lp'],

/**
* Sets the lowpass filter envelope modulation depth.
* @name lpenv
* @param {number | Pattern} modulation depth of the lowpass filter envelope between 0 and _n_
* @synonyms lpe
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .lpf(500)
* .lpa(.5)
* .lpenv("<4 2 1 0 -1 -2 -4>/4")
*/
['lpenv', 'lpe'],
/**
* Sets the highpass filter envelope modulation depth.
* @name hpenv
* @param {number | Pattern} modulation depth of the highpass filter envelope between 0 and _n_
* @synonyms hpe
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .hpf(500)
* .hpa(.5)
* .hpenv("<4 2 1 0 -1 -2 -4>/4")
*/
['hpenv', 'hpe'],
/**
* Sets the bandpass filter envelope modulation depth.
* @name bpenv
* @param {number | Pattern} modulation depth of the bandpass filter envelope between 0 and _n_
* @synonyms bpe
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .bpf(500)
* .bpa(.5)
* .bpenv("<4 2 1 0 -1 -2 -4>/4")
*/
['bpenv', 'bpe'],
/**
* Sets the attack duration for the lowpass filter envelope.
* @name lpattack
* @param {number | Pattern} attack time of the filter envelope
* @synonyms lpa
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .lpf(500)
* .lpa("<.5 .25 .1 .01>/4")
* .lpenv(4)
*/
['lpattack', 'lpa'],
/**
* Sets the attack duration for the highpass filter envelope.
* @name hpattack
* @param {number | Pattern} attack time of the highpass filter envelope
* @synonyms hpa
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .hpf(500)
* .hpa("<.5 .25 .1 .01>/4")
* .hpenv(4)
*/
['hpattack', 'hpa'],
/**
* Sets the attack duration for the bandpass filter envelope.
* @name bpattack
* @param {number | Pattern} attack time of the bandpass filter envelope
* @synonyms bpa
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .bpf(500)
* .bpa("<.5 .25 .1 .01>/4")
* .bpenv(4)
*/
['bpattack', 'bpa'],
/**
* Sets the decay duration for the lowpass filter envelope.
* @name lpdecay
* @param {number | Pattern} decay time of the filter envelope
* @synonyms lpd
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .lpf(500)
* .lpd("<.5 .25 .1 0>/4")
* .lps(0.2)
* .lpenv(4)
*/
['lpdecay', 'lpd'],
/**
* Sets the decay duration for the highpass filter envelope.
* @name hpdecay
* @param {number | Pattern} decay time of the highpass filter envelope
* @synonyms hpd
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .hpf(500)
* .hpd("<.5 .25 .1 0>/4")
* .hps(0.2)
* .hpenv(4)
*/
['hpdecay', 'hpd'],
/**
* Sets the decay duration for the bandpass filter envelope.
* @name bpdecay
* @param {number | Pattern} decay time of the bandpass filter envelope
* @synonyms bpd
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .bpf(500)
* .bpd("<.5 .25 .1 0>/4")
* .bps(0.2)
* .bpenv(4)
*/
['bpdecay', 'bpd'],
/**
* Sets the sustain amplitude for the lowpass filter envelope.
* @name lpsustain
* @param {number | Pattern} sustain amplitude of the lowpass filter envelope
* @synonyms lps
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .lpf(500)
* .lpd(.5)
* .lps("<0 .25 .5 1>/4")
* .lpenv(4)
*/
['lpsustain', 'lps'],
/**
* Sets the sustain amplitude for the highpass filter envelope.
* @name hpsustain
* @param {number | Pattern} sustain amplitude of the highpass filter envelope
* @synonyms hps
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .hpf(500)
* .hpd(.5)
* .hps("<0 .25 .5 1>/4")
* .hpenv(4)
*/
['hpsustain', 'hps'],
/**
* Sets the sustain amplitude for the bandpass filter envelope.
* @name bpsustain
* @param {number | Pattern} sustain amplitude of the bandpass filter envelope
* @synonyms bps
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .bpf(500)
* .bpd(.5)
* .bps("<0 .25 .5 1>/4")
* .bpenv(4)
*/
['bpsustain', 'bps'],
/**
* Sets the release time for the lowpass filter envelope.
* @name lprelease
* @param {number | Pattern} release time of the filter envelope
* @synonyms lpr
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .clip(.5)
* .lpf(500)
* .lpenv(4)
* .lpr("<.5 .25 .1 0>/4")
* .release(.5)
*/
['lprelease', 'lpr'],
/**
* Sets the release time for the highpass filter envelope.
* @name hprelease
* @param {number | Pattern} release time of the highpass filter envelope
* @synonyms hpr
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .clip(.5)
* .hpf(500)
* .hpenv(4)
* .hpr("<.5 .25 .1 0>/4")
* .release(.5)
*/
['hprelease', 'hpr'],
/**
* Sets the release time for the bandpass filter envelope.
* @name bprelease
* @param {number | Pattern} release time of the bandpass filter envelope
* @synonyms bpr
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .clip(.5)
* .bpf(500)
* .bpenv(4)
* .bpr("<.5 .25 .1 0>/4")
* .release(.5)
*/
['bprelease', 'bpr'],
/**
* Sets the filter type. The 24db filter is more aggressive. More types might be added in the future.
* @name ftype
* @param {number | Pattern} type 12db (default) or 24db
* @example
* note("<c2 e2 f2 g2>")
* .sound('sawtooth')
* .lpf(500)
* .bpenv(4)
* .ftype("<12db 24db>")
*/
['ftype'],
['fanchor'],
/**
* Applies the cutoff frequency of the **h**igh-**p**ass **f**ilter.
*
Expand Down
47 changes: 43 additions & 4 deletions packages/superdough/helpers.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getAudioContext } from './superdough.mjs';
import { clamp } from './util.mjs';

export function gainNode(value) {
const node = getAudioContext().createGain();
Expand Down Expand Up @@ -66,10 +67,48 @@ export const getADSR = (attack, decay, sustain, release, velocity, begin, end) =
return gainNode;
};

export const getFilter = (type, frequency, Q) => {
const filter = getAudioContext().createBiquadFilter();
export const getParamADSR = (param, attack, decay, sustain, release, min, max, begin, end) => {
const range = max - min;
const peak = min + range;
const sustainLevel = min + sustain * range;
param.setValueAtTime(min, begin);
param.linearRampToValueAtTime(peak, begin + attack);
param.linearRampToValueAtTime(sustainLevel, begin + attack + decay);
param.setValueAtTime(sustainLevel, end);
param.linearRampToValueAtTime(min, end + Math.max(release, 0.1));
};

export function createFilter(
context,
type,
frequency,
Q,
attack,
decay,
sustain,
release,
fenv,
start,
end,
fanchor = 0.5,
) {
const filter = context.createBiquadFilter();
filter.type = type;
filter.frequency.value = frequency;
filter.Q.value = Q;
filter.frequency.value = frequency;

// Apply ADSR to filter frequency
if (!isNaN(fenv) && fenv !== 0) {
const offset = fenv * fanchor;

const min = clamp(2 ** -offset * frequency, 0, 20000);
const max = clamp(2 ** (fenv - offset) * frequency, 0, 20000);

// console.log('min', min, 'max', max);

getParamADSR(filter.frequency, attack, decay, sustain, release, min, max, start, end);
return filter;
}

return filter;
};
}