Public
Edited
Mar 24, 2023
1 star
Also listed in…
Electronics
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
domain = [lo, hi].map(d => 10 ** d)
Insert cell
lo = 1
Insert cell
hi = 5
Insert cell
settings = ({
...controls,
ab: dampen(controls.ab), // dampen: see https://observablehq.com/@martien/linearize-a-log-movement
at: dampen(controls.at), // dampen: see https://observablehq.com/@martien/linearize-a-log-movement
Cb: n(controls.CB),
Ct: n(controls.CT),
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
bass =
({Cb, ab}) =>
(ƒ, ω = omega(ƒ), p = µ(650) / ((ω * Cb))) =>
(1 + p * (ab)) / (1 + p * (1 - ab))
Insert cell
treble =
({Ct, at}) =>
(ƒ, ω = omega(ƒ), p = ω * Ct * 5500) =>
(1 + p * (at)) / (1 + p * (1 - at))
Insert cell
Insert cell
// single filter containing only one function
B = ({name: "bass", filters: [bass(settings)]})
Insert cell
// single filter containing only one function
T = ({name: "treble", filters: [treble(settings)]})
Insert cell
// composite filter made up of both bass and treble filter
A = ({name: "total", filters: [B, T]})
Insert cell
Insert cell
Insert cell
data = [B, T, A].map(list(frequencies)).flat()
Insert cell
probes = d3.range(settings.steps * (hi - lo) + 1)
Insert cell
// turn frequency index into proper frequency (Hz)
index2ƒ = frequencyAtIndex(lo)(settings.steps)
Insert cell
frequencies = probes.map(index2ƒ)
Insert cell
// take inital value, number of steps, and index
// return corresponding frequency for index
frequencyAtIndex = (initial) => (steps) => (x) => 10 ** (initial + x / steps)
Insert cell
Insert cell
// takes a list of frequencies and a simple or composite filter
// returns a Plot-ready list of all responses for all filters and all frequencies
list =
(frequencies) =>
(filter) =>
frequencies
.map(gain(filter))
.map(objectify(filter))
Insert cell
gain =
(filter) =>
(ƒ) =>
R.pipe(
R.map(dB),
R.sum
)
(gains(filter)(ƒ))
// equivalent to (gains(filter)(ƒ)).map(dB).reduce(sum, 0)
Insert cell
gain(A)(100) // total effect in dB for composite filter AA
Insert cell
// takes a filter and its response
// returns corresponding object
objectify =
({name}) =>
(gain, i) => ({
name,
i,
ƒ: index2ƒ(i),
gain,
})
Insert cell
objectify(A)(15, 2)
Insert cell
// takes a – potentially composite – filter and a frequency
// returns a list of responses
function gains(filter) {
return ((ƒ) => filter.filters.map((f) => ({
"function": f,
"object": gains(f),
})[typeof f](ƒ))
.flat())
}
Insert cell
gains(A)(10) // A is composite filter, so gains returns list of responses, one for each filter component
Insert cell
gains(A)(10).map(dB) // like above, and convert to dB
Insert cell
Insert cell
omega = (ƒ) => 2 * Math.PI * ƒ // unit: rad / s
Insert cell
dB = (x) => 20 * Math.log10(x)
Insert cell
unitCircle = (x) => (2 * x) - 1 // [0, 1] -> [-1, +1]
// equivalent to d3.scaleLinear().domain([0, 1]).range([-1, +1])
Insert cell
unitCircleInvert = (x) => (x + 1) / 2 // [-1, +1] -> [0, 1]
// equivalent to d3.scaleLinear().domain([0, 1]).range([-1, +1]).invert
Insert cell
tanh = ((slope) => (x) => Math.tanh(slope * x))(7) // slop fixed at 7 gives best result in this example
Insert cell
// dampen: see https://observablehq.com/@martien/linearize-a-log-movement
dampen = R.pipe(unitCircle, tanh, unitCircleInvert)
Insert cell
// n, for nano
n = (x) => x * 1e-9
Insert cell
µ = (x) => x * 1e-6;
Insert cell
k = (x) => x * 1e3;
Insert cell
Insert cell
import {columns} from "@martien/inputs"
Insert cell
R = require("ramda")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more