@audio/dynamics-ducker 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/audio-module.js +49 -0
  2. package/package.json +5 -3
@@ -0,0 +1,49 @@
1
+ // audio-module manifest — wraps the external-sidechain ducker kernel per @audio/module
2
+ // CONTRACT §channels. The kernel cleanly separates the key input (duckerStream.write(main,
3
+ // side) takes two distinct signals), so this declares a genuine two-bus layout: main +
4
+ // sidechain in, main out — mirrors the compressor pilot's live-param style (compressorGain
5
+ // is a pure per-sample curve, same as compressor.js; only attack/release seed the envelope
6
+ // at construction).
7
+ //
8
+ // NOTE: the audio host only feeds bus 0 today (single-bus process calls) — until it wires
9
+ // up multi-bus routing, `inputs[1]` arrives undefined and this falls back to keying off the
10
+ // main signal itself (self-compression, not true ducking). The declaration is forward-looking
11
+ // and correct per CONTRACT; it just isn't exercised as a real sidechain until the host catches up.
12
+
13
+ import { envelope } from '@audio/dynamics-envelope'
14
+ import { compressorGain } from '@audio/dynamics-compressor'
15
+ import { db2lin, lin2db } from '@audio/dynamics-core'
16
+
17
+ export const ducker = (ctx) => {
18
+ const envs = []
19
+ for (let c = 0, N = ctx.maxChannels ?? 8; c < N; c++) envs.push(envelope({
20
+ sampleRate: ctx.sampleRate,
21
+ attack: ctx.params.attack[0],
22
+ release: ctx.params.release[0],
23
+ }))
24
+ return (inputs, outputs, params) => {
25
+ const main = inputs[0], side = inputs[1], out = outputs[0]
26
+ if (!main || !main.length) return
27
+ const threshold = params.threshold[0], ratio = params.ratio[0]
28
+ const knee = params.knee[0], range = params.range[0]
29
+ for (let c = 0; c < main.length; c++) {
30
+ const x = main[c], y = out[c], env = envs[c]
31
+ const key = side && side[c] ? side[c] : x
32
+ for (let i = 0; i < x.length; i++) {
33
+ let rDb = compressorGain(lin2db(env(key[i])), threshold, ratio, knee)
34
+ if (rDb < range) rDb = range
35
+ y[i] = x[i] * db2lin(rDb)
36
+ }
37
+ }
38
+ }
39
+ }
40
+ ducker.channels = { inputs: [2, 2], outputs: [2] }
41
+ ducker.tail = 0
42
+ ducker.params = {
43
+ threshold: { type: 'number', min: -60, max: 0, default: -30, smoothing: 0.01, unit: 'dB' },
44
+ ratio: { type: 'number', min: 1, max: 20, default: 4 },
45
+ knee: { type: 'number', min: 0, max: 24, default: 6, unit: 'dB' },
46
+ range: { type: 'number', min: -90, max: 0, default: -24, unit: 'dB' },
47
+ attack: { type: 'number', min: 0.1, max: 200, default: 20, unit: 'ms', flags: ['restart'] },
48
+ release: { type: 'number', min: 1, max: 2000, default: 300, unit: 'ms', flags: ['restart'] },
49
+ }
package/package.json CHANGED
@@ -1,16 +1,18 @@
1
1
  {
2
2
  "name": "@audio/dynamics-ducker",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "External-sidechain compressor: main signal's gain is reduced in proportion",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "main": "ducker.js",
8
8
  "exports": {
9
9
  ".": "./ducker.js",
10
- "./package.json": "./package.json"
10
+ "./package.json": "./package.json",
11
+ "./audio-module": "./audio-module.js"
11
12
  },
12
13
  "files": [
13
- "ducker.js"
14
+ "ducker.js",
15
+ "audio-module.js"
14
16
  ],
15
17
  "dependencies": {
16
18
  "@audio/dynamics-core": "^0.1.0",