@mudah-cli/audio 0.8.0 → 0.9.0
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.
- package/LICENSE +21 -0
- package/README.md +37 -0
- package/dist/clock.d.ts +26 -0
- package/dist/clock.js +51 -0
- package/dist/clock.js.map +1 -0
- package/dist/detect.js +1 -1
- package/dist/detect.js.map +1 -1
- package/dist/device.js +25 -3
- package/dist/device.js.map +1 -1
- package/dist/fft.d.ts +14 -0
- package/dist/fft.js +122 -0
- package/dist/fft.js.map +1 -0
- package/dist/index.d.ts +12 -4
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/mic.d.ts +26 -0
- package/dist/mic.js +78 -0
- package/dist/mic.js.map +1 -0
- package/dist/mix.d.ts +17 -0
- package/dist/mix.js +58 -0
- package/dist/mix.js.map +1 -0
- package/dist/mp3.d.ts +21 -0
- package/dist/mp3.js +190 -0
- package/dist/mp3.js.map +1 -0
- package/dist/quantize.d.ts +6 -0
- package/dist/quantize.js +10 -0
- package/dist/quantize.js.map +1 -0
- package/dist/reactive.d.ts +21 -0
- package/dist/reactive.js +50 -0
- package/dist/reactive.js.map +1 -0
- package/dist/tune.d.ts +21 -0
- package/dist/tune.js +91 -0
- package/dist/tune.js.map +1 -0
- package/package.json +17 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mudah Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @mudah-cli/audio
|
|
2
|
+
|
|
3
|
+
Play PCM through the OS mixer from a Mudah app (streaming output and one-shot WAV/MP3).
|
|
4
|
+
|
|
5
|
+
**Optional.** The `@mudah-cli/mudah` umbrella does not depend on this package, and apps that
|
|
6
|
+
never import it never load an audio backend. Kitty cannot carry audio — this never writes to the
|
|
7
|
+
terminal.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
npm install @mudah-cli/audio
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Backends are auto-detected: `audify` (optional peer) for native output, otherwise
|
|
16
|
+
`pw-play` / `paplay` / `aplay`, otherwise silent. Set `MUDAH_AUDIO=silent` to force silence.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { AudioOut } from '@mudah-cli/audio';
|
|
22
|
+
|
|
23
|
+
const out = await AudioOut.open();
|
|
24
|
+
out.write(new Int16Array(/* interleaved PCM */));
|
|
25
|
+
out.dispose();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Also ships `decodeWav` / `decodeMp3`, a tone-sequence DSL (`parseTune`), a `BpmClock`, channel
|
|
29
|
+
mixing + ducking, a beat-grid quantizer, and a microphone source with FFT band extraction
|
|
30
|
+
(`createMicrophone`, `FftBands`) plus an audio-reactive bridge for `@mudah-cli/vgpu`.
|
|
31
|
+
|
|
32
|
+
## Links
|
|
33
|
+
|
|
34
|
+
- [Full documentation](https://github.com/thesimonharms/mudah#readme)
|
|
35
|
+
- [tone example](https://github.com/thesimonharms/mudah/tree/main/examples/tone) ·
|
|
36
|
+
[melody example](https://github.com/thesimonharms/mudah/tree/main/examples/melody)
|
|
37
|
+
- [License: MIT](LICENSE)
|
package/dist/clock.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface BpmClockOptions {
|
|
2
|
+
bpm?: number;
|
|
3
|
+
/** Clock source in milliseconds. Used only as a fallback when `tick` is not driven. */
|
|
4
|
+
now?: () => number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Beat/tempo clock. `tick(dtMs)` advances time; `now()` is the phase inside
|
|
8
|
+
* the current beat (0..1). `onBeat` fires each time the integer beat increases.
|
|
9
|
+
*/
|
|
10
|
+
export declare class BpmClock {
|
|
11
|
+
bpm: number;
|
|
12
|
+
private elapsedMs;
|
|
13
|
+
private lastBeat;
|
|
14
|
+
private readonly listeners;
|
|
15
|
+
constructor(options?: BpmClockOptions);
|
|
16
|
+
/** Phase of the current beat, 0 inclusive to 1 exclusive. */
|
|
17
|
+
now(): number;
|
|
18
|
+
/** Whole beats elapsed since start (0-based). */
|
|
19
|
+
get beat(): number;
|
|
20
|
+
onBeat(fn: (beat: number) => void): () => void;
|
|
21
|
+
/** Advance the clock and fire beat listeners when a new beat starts. */
|
|
22
|
+
tick(dtMs: number): void;
|
|
23
|
+
reset(): void;
|
|
24
|
+
private beatPosition;
|
|
25
|
+
private flushBeats;
|
|
26
|
+
}
|
package/dist/clock.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Beat/tempo clock. `tick(dtMs)` advances time; `now()` is the phase inside
|
|
3
|
+
* the current beat (0..1). `onBeat` fires each time the integer beat increases.
|
|
4
|
+
*/
|
|
5
|
+
export class BpmClock {
|
|
6
|
+
bpm;
|
|
7
|
+
elapsedMs = 0;
|
|
8
|
+
lastBeat = -1;
|
|
9
|
+
listeners = new Set();
|
|
10
|
+
constructor(options = {}) {
|
|
11
|
+
this.bpm = options.bpm ?? 120;
|
|
12
|
+
}
|
|
13
|
+
/** Phase of the current beat, 0 inclusive to 1 exclusive. */
|
|
14
|
+
now() {
|
|
15
|
+
const beat = this.beatPosition();
|
|
16
|
+
const frac = beat - Math.floor(beat);
|
|
17
|
+
return frac < 0 ? 0 : frac;
|
|
18
|
+
}
|
|
19
|
+
/** Whole beats elapsed since start (0-based). */
|
|
20
|
+
get beat() {
|
|
21
|
+
return Math.floor(this.beatPosition());
|
|
22
|
+
}
|
|
23
|
+
onBeat(fn) {
|
|
24
|
+
this.listeners.add(fn);
|
|
25
|
+
return () => {
|
|
26
|
+
this.listeners.delete(fn);
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Advance the clock and fire beat listeners when a new beat starts. */
|
|
30
|
+
tick(dtMs) {
|
|
31
|
+
this.elapsedMs += dtMs;
|
|
32
|
+
this.flushBeats();
|
|
33
|
+
}
|
|
34
|
+
reset() {
|
|
35
|
+
this.elapsedMs = 0;
|
|
36
|
+
this.lastBeat = -1;
|
|
37
|
+
}
|
|
38
|
+
beatPosition() {
|
|
39
|
+
const beatMs = 60_000 / Math.max(this.bpm, 1e-6);
|
|
40
|
+
return this.elapsedMs / beatMs;
|
|
41
|
+
}
|
|
42
|
+
flushBeats() {
|
|
43
|
+
const current = Math.floor(this.beatPosition());
|
|
44
|
+
while (this.lastBeat < current) {
|
|
45
|
+
this.lastBeat += 1;
|
|
46
|
+
for (const listener of this.listeners)
|
|
47
|
+
listener(this.lastBeat);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=clock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clock.js","sourceRoot":"","sources":["../src/clock.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,MAAM,OAAO,QAAQ;IACnB,GAAG,CAAS;IACJ,SAAS,GAAG,CAAC,CAAC;IACd,QAAQ,GAAG,CAAC,CAAC,CAAC;IACL,SAAS,GAAG,IAAI,GAAG,EAA0B,CAAC;IAE/D,YAAY,OAAO,GAAoB,EAAE;QACvC,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC;IAChC,CAAC;IAED,6DAA6D;IAC7D,GAAG;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;QACjC,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrC,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7B,CAAC;IAED,iDAAiD;IACjD,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,CAAC,EAA0B;QAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,IAAI,CAAC,IAAY;QACf,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;QACvB,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC;IACrB,CAAC;IAEO,YAAY;QAClB,MAAM,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC;IACjC,CAAC;IAEO,UAAU;QAChB,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,CAAC;YAC/B,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;YACnB,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS;gBAAE,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;CACF"}
|
package/dist/detect.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
1
|
import { existsSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
const requireFromHere = createRequire(fileURLToPath(import.meta.url));
|
package/dist/detect.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,eAAe,GAAG,aAAa,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtE,MAAM,WAAW,GAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AAEzE,MAAM,UAAU,eAAe,CAAC,GAAG,GAAsB,OAAO,CAAC,GAAG;IAClE,IAAI,GAAG,CAAC,gBAAgB,CAAC,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;IACrB,OAAO,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC;QACH,eAAe,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,GAAG,GAAsB,OAAO,CAAC,GAAG,EACpC,QAA4B;IAE5B,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAI,GAAsB,YAAY,EAAE;IACpE,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CACzB,GAAG,GAAsB,OAAO,CAAC,GAAG,EACpC,QAA4B;IAE5B,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,gBAAgB,EAAE,CAAC;IAElC,IAAI,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAClD,CAAC;IACD,IAAI,MAAM;QAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC5D,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC5E,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAClD,CAAC"}
|
package/dist/device.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { SilentBackend } from './backends/silent.js';
|
|
2
1
|
import { NativeBackend } from './backends/native.js';
|
|
2
|
+
import { SilentBackend } from './backends/silent.js';
|
|
3
3
|
import { SpawnBackend } from './backends/spawn.js';
|
|
4
|
-
import { findSpawnTool, pathDirsFrom,
|
|
4
|
+
import { detectAudio, findSpawnTool, pathDirsFrom, silentRequested } from './detect.js';
|
|
5
|
+
import { decodeMp3, looksLikeMp3 } from './mp3.js';
|
|
5
6
|
import { decodeWav } from './wav.js';
|
|
6
7
|
/**
|
|
7
8
|
* PCM output through the OS mixer. Kitty cannot carry audio; this never
|
|
@@ -97,7 +98,14 @@ export class AudioOut {
|
|
|
97
98
|
this.backend.dispose();
|
|
98
99
|
}
|
|
99
100
|
normalize(source) {
|
|
100
|
-
|
|
101
|
+
if (!(source instanceof Uint8Array)) {
|
|
102
|
+
return remapChannels(source.samples, source.channels, this.channels);
|
|
103
|
+
}
|
|
104
|
+
if (looksLikeMp3(source) && !looksLikeWav(source)) {
|
|
105
|
+
const decoded = decodeMp3(source);
|
|
106
|
+
return remapChannels(floatToS16(decoded.samples), decoded.channels, this.channels);
|
|
107
|
+
}
|
|
108
|
+
const clip = decodeWav(source);
|
|
101
109
|
return remapChannels(clip.samples, clip.channels, this.channels);
|
|
102
110
|
}
|
|
103
111
|
toDeviceS16(pcm) {
|
|
@@ -146,6 +154,20 @@ function f32le(samples) {
|
|
|
146
154
|
view.setFloat32(i * 4, (samples[i] ?? 0) / 32768, true);
|
|
147
155
|
return out;
|
|
148
156
|
}
|
|
157
|
+
function looksLikeWav(bytes) {
|
|
158
|
+
if (bytes.byteLength < 12)
|
|
159
|
+
return false;
|
|
160
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
161
|
+
return view.getUint32(0, true) === 0x46464952 && view.getUint32(8, true) === 0x45564157;
|
|
162
|
+
}
|
|
163
|
+
function floatToS16(samples) {
|
|
164
|
+
const out = new Int16Array(samples.length);
|
|
165
|
+
for (let i = 0; i < samples.length; i++) {
|
|
166
|
+
const x = samples[i] ?? 0;
|
|
167
|
+
out[i] = Math.max(-32768, Math.min(32767, Math.round(x * 32767)));
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
149
171
|
function remapChannels(samples, from, to) {
|
|
150
172
|
if (from === to)
|
|
151
173
|
return Int16Array.from(samples);
|
package/dist/device.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device.js","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"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"}
|
package/dist/fft.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface FftBandResult {
|
|
2
|
+
readonly bass: number;
|
|
3
|
+
readonly mid: number;
|
|
4
|
+
readonly high: number;
|
|
5
|
+
readonly energy: number;
|
|
6
|
+
readonly bands: number[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* FFT-derived bass / mid / high / energy plus 8 logarithmic bands.
|
|
10
|
+
* Values are 0..1-ish magnitudes for typical -1..1 PCM.
|
|
11
|
+
*/
|
|
12
|
+
export declare class FftBands {
|
|
13
|
+
static fromSamples(samples: Float32Array, sampleRate?: number): FftBandResult;
|
|
14
|
+
}
|
package/dist/fft.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
const BAND_COUNT = 8;
|
|
2
|
+
/**
|
|
3
|
+
* FFT-derived bass / mid / high / energy plus 8 logarithmic bands.
|
|
4
|
+
* Values are 0..1-ish magnitudes for typical -1..1 PCM.
|
|
5
|
+
*/
|
|
6
|
+
// biome-ignore lint/complexity/noStaticOnlyClass: exported namespace-style API (`FftBands.fromSamples`).
|
|
7
|
+
export class FftBands {
|
|
8
|
+
static fromSamples(samples, sampleRate = 44100) {
|
|
9
|
+
if (samples.length === 0) {
|
|
10
|
+
return { bass: 0, mid: 0, high: 0, energy: 0, bands: zeros(BAND_COUNT) };
|
|
11
|
+
}
|
|
12
|
+
const energy = rms(samples);
|
|
13
|
+
const spectrum = magnitudeSpectrum(samples);
|
|
14
|
+
const nyquist = sampleRate / 2;
|
|
15
|
+
const binHz = nyquist / Math.max(spectrum.length, 1);
|
|
16
|
+
const bass = bandMean(spectrum, binHz, 20, 250);
|
|
17
|
+
const mid = bandMean(spectrum, binHz, 250, 2000);
|
|
18
|
+
const high = bandMean(spectrum, binHz, 2000, 12_000);
|
|
19
|
+
const bands = logBands(spectrum, binHz, BAND_COUNT);
|
|
20
|
+
return { bass, mid, high, energy, bands };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function zeros(n) {
|
|
24
|
+
return Array.from({ length: n }, () => 0);
|
|
25
|
+
}
|
|
26
|
+
function rms(samples) {
|
|
27
|
+
let sum = 0;
|
|
28
|
+
for (let i = 0; i < samples.length; i++) {
|
|
29
|
+
const x = samples[i] ?? 0;
|
|
30
|
+
sum += x * x;
|
|
31
|
+
}
|
|
32
|
+
return Math.sqrt(sum / samples.length);
|
|
33
|
+
}
|
|
34
|
+
function nextPow2(n) {
|
|
35
|
+
let p = 1;
|
|
36
|
+
while (p < n)
|
|
37
|
+
p <<= 1;
|
|
38
|
+
return p;
|
|
39
|
+
}
|
|
40
|
+
function magnitudeSpectrum(samples) {
|
|
41
|
+
const n = nextPow2(samples.length);
|
|
42
|
+
const real = new Float64Array(n);
|
|
43
|
+
const imag = new Float64Array(n);
|
|
44
|
+
for (let i = 0; i < samples.length; i++)
|
|
45
|
+
real[i] = samples[i] ?? 0;
|
|
46
|
+
fft(real, imag);
|
|
47
|
+
const half = n / 2;
|
|
48
|
+
const mags = new Float64Array(half);
|
|
49
|
+
const norm = 2 / n;
|
|
50
|
+
for (let i = 0; i < half; i++) {
|
|
51
|
+
const re = real[i] ?? 0;
|
|
52
|
+
const im = imag[i] ?? 0;
|
|
53
|
+
mags[i] = Math.sqrt(re * re + im * im) * norm;
|
|
54
|
+
}
|
|
55
|
+
return mags;
|
|
56
|
+
}
|
|
57
|
+
function fft(real, imag) {
|
|
58
|
+
const n = real.length;
|
|
59
|
+
for (let i = 1, j = 0; i < n; i++) {
|
|
60
|
+
let bit = n >> 1;
|
|
61
|
+
for (; (j & bit) !== 0; bit >>= 1)
|
|
62
|
+
j ^= bit;
|
|
63
|
+
j ^= bit;
|
|
64
|
+
if (i < j) {
|
|
65
|
+
const re = real[i] ?? 0;
|
|
66
|
+
const im = imag[i] ?? 0;
|
|
67
|
+
real[i] = real[j] ?? 0;
|
|
68
|
+
imag[i] = imag[j] ?? 0;
|
|
69
|
+
real[j] = re;
|
|
70
|
+
imag[j] = im;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
for (let len = 2; len <= n; len <<= 1) {
|
|
74
|
+
const ang = (-2 * Math.PI) / len;
|
|
75
|
+
const wlenRe = Math.cos(ang);
|
|
76
|
+
const wlenIm = Math.sin(ang);
|
|
77
|
+
const half = len >> 1;
|
|
78
|
+
for (let i = 0; i < n; i += len) {
|
|
79
|
+
let wRe = 1;
|
|
80
|
+
let wIm = 0;
|
|
81
|
+
for (let j = 0; j < half; j++) {
|
|
82
|
+
const uRe = real[i + j] ?? 0;
|
|
83
|
+
const uIm = imag[i + j] ?? 0;
|
|
84
|
+
const vr = real[i + j + half] ?? 0;
|
|
85
|
+
const vi = imag[i + j + half] ?? 0;
|
|
86
|
+
const vRe = vr * wRe - vi * wIm;
|
|
87
|
+
const vIm = vr * wIm + vi * wRe;
|
|
88
|
+
real[i + j] = uRe + vRe;
|
|
89
|
+
imag[i + j] = uIm + vIm;
|
|
90
|
+
real[i + j + half] = uRe - vRe;
|
|
91
|
+
imag[i + j + half] = uIm - vIm;
|
|
92
|
+
const nextRe = wRe * wlenRe - wIm * wlenIm;
|
|
93
|
+
wIm = wRe * wlenIm + wIm * wlenRe;
|
|
94
|
+
wRe = nextRe;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function bandMean(spectrum, binHz, low, high) {
|
|
100
|
+
const start = Math.max(0, Math.floor(low / Math.max(binHz, 1e-9)));
|
|
101
|
+
const end = Math.min(spectrum.length, Math.ceil(high / Math.max(binHz, 1e-9)));
|
|
102
|
+
if (end <= start)
|
|
103
|
+
return 0;
|
|
104
|
+
let sum = 0;
|
|
105
|
+
for (let i = start; i < end; i++)
|
|
106
|
+
sum += spectrum[i] ?? 0;
|
|
107
|
+
return sum / (end - start);
|
|
108
|
+
}
|
|
109
|
+
function logBands(spectrum, binHz, count) {
|
|
110
|
+
const maxHz = Math.max(binHz * spectrum.length, 1);
|
|
111
|
+
const minHz = Math.max(binHz, 20);
|
|
112
|
+
const out = [];
|
|
113
|
+
for (let i = 0; i < count; i++) {
|
|
114
|
+
const t0 = i / count;
|
|
115
|
+
const t1 = (i + 1) / count;
|
|
116
|
+
const low = minHz * (maxHz / minHz) ** t0;
|
|
117
|
+
const high = minHz * (maxHz / minHz) ** t1;
|
|
118
|
+
out.push(bandMean(spectrum, binHz, low, high));
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=fft.js.map
|
package/dist/fft.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fft.js","sourceRoot":"","sources":["../src/fft.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
1
|
+
export { spawnCommand } from './backends/spawn-command.js';
|
|
2
|
+
export { BpmClock, type BpmClockOptions } from './clock.js';
|
|
3
|
+
export { canResolveAudify, detectAudio, findSpawnTool, silentRequested, } from './detect.js';
|
|
4
|
+
export { AudioOut } from './device.js';
|
|
5
|
+
export { type FftBandResult, FftBands } from './fft.js';
|
|
6
|
+
export { type CreateMicrophoneOptions, createMicrophone, type MicrophoneSource, } from './mic.js';
|
|
7
|
+
export { duck, type MixChannelsOptions, mixChannels } from './mix.js';
|
|
8
|
+
export { type DecodedMp3, type DecodeMp3Options, decodeMp3, looksLikeMp3, type Mp3Spawn, } from './mp3.js';
|
|
9
|
+
export { type BeatSubdivision, quantize } from './quantize.js';
|
|
10
|
+
export { createReactiveBridge, type ReactiveBridge, type ReactiveBridgeOptions, type ReactiveEvent, type ReactiveListener, } from './reactive.js';
|
|
11
|
+
export { midiToHz, type Note, parsePitch, parseTune, type Tune, type TuneDurationUnit, } from './tune.js';
|
|
5
12
|
export type { AudioBackendKind, AudioClip, AudioDetection, AudioFormat, AudioOutOptions, PlaySource, SpawnTool, } from './types.js';
|
|
13
|
+
export { decodeWav, encodeWav, } from './wav.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { spawnCommand } from './backends/spawn-command.js';
|
|
2
|
+
export { BpmClock } from './clock.js';
|
|
3
|
+
export { canResolveAudify, detectAudio, findSpawnTool, silentRequested, } from './detect.js';
|
|
4
|
+
export { AudioOut } from './device.js';
|
|
5
|
+
export { FftBands } from './fft.js';
|
|
6
|
+
export { createMicrophone, } from './mic.js';
|
|
7
|
+
export { duck, mixChannels } from './mix.js';
|
|
8
|
+
export { decodeMp3, looksLikeMp3, } from './mp3.js';
|
|
9
|
+
export { quantize } from './quantize.js';
|
|
10
|
+
export { createReactiveBridge, } from './reactive.js';
|
|
11
|
+
export { midiToHz, parsePitch, parseTune, } from './tune.js';
|
|
3
12
|
export { decodeWav, encodeWav, } from './wav.js';
|
|
4
|
-
export { spawnCommand, } from './backends/spawn-command.js';
|
|
5
13
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAwB,MAAM,YAAY,CAAC;AAC5D,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAsB,QAAQ,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAEL,gBAAgB,GAEjB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,IAAI,EAA2B,WAAW,EAAE,MAAM,UAAU,CAAC;AACtE,OAAO,EAGL,SAAS,EACT,YAAY,GAEb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAwB,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EACL,oBAAoB,GAKrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,QAAQ,EAER,UAAU,EACV,SAAS,GAGV,MAAM,WAAW,CAAC;AAUnB,OAAO,EACL,SAAS,EACT,SAAS,GACV,MAAM,UAAU,CAAC"}
|
package/dist/mic.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface MicrophoneSource {
|
|
2
|
+
read(): Float32Array;
|
|
3
|
+
close(): void;
|
|
4
|
+
readonly sampleRate: number;
|
|
5
|
+
}
|
|
6
|
+
export type MicSpawn = (command: string, args: readonly string[]) => {
|
|
7
|
+
status: number | null;
|
|
8
|
+
stdout: Buffer | string;
|
|
9
|
+
error?: Error;
|
|
10
|
+
} | null;
|
|
11
|
+
export interface CreateMicrophoneOptions {
|
|
12
|
+
/** Injected capture. Wins over OS capture. */
|
|
13
|
+
read?: () => Float32Array;
|
|
14
|
+
sampleRate?: number;
|
|
15
|
+
/**
|
|
16
|
+
* Open a live OS capture (`arecord` / `rec` / `sox`). Default on when not
|
|
17
|
+
* inside Vitest and `MUDAH_MIC` is not `0`.
|
|
18
|
+
*/
|
|
19
|
+
live?: boolean;
|
|
20
|
+
spawn?: MicSpawn;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Live microphone handle. Tests inject `read`. Production opens arecord/sox
|
|
24
|
+
* when `live` is true.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createMicrophone(options?: CreateMicrophoneOptions): MicrophoneSource;
|
package/dist/mic.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Live microphone handle. Tests inject `read`. Production opens arecord/sox
|
|
4
|
+
* when `live` is true.
|
|
5
|
+
*/
|
|
6
|
+
export function createMicrophone(options = {}) {
|
|
7
|
+
const sampleRate = options.sampleRate ?? 44100;
|
|
8
|
+
let closed = false;
|
|
9
|
+
if (options.read) {
|
|
10
|
+
const read = options.read;
|
|
11
|
+
return {
|
|
12
|
+
sampleRate,
|
|
13
|
+
read() {
|
|
14
|
+
if (closed)
|
|
15
|
+
return new Float32Array(0);
|
|
16
|
+
return read();
|
|
17
|
+
},
|
|
18
|
+
close() {
|
|
19
|
+
closed = true;
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const live = options.live ?? (process.env['VITEST'] === undefined && process.env['MUDAH_MIC'] !== '0');
|
|
24
|
+
const spawn = options.spawn ?? defaultMicSpawn;
|
|
25
|
+
const tool = live ? pickMicTool(spawn) : undefined;
|
|
26
|
+
return {
|
|
27
|
+
sampleRate,
|
|
28
|
+
read() {
|
|
29
|
+
if (closed)
|
|
30
|
+
return new Float32Array(0);
|
|
31
|
+
if (tool === undefined)
|
|
32
|
+
return new Float32Array(0);
|
|
33
|
+
const result = spawn(tool.command, tool.args(sampleRate));
|
|
34
|
+
if (result === null || result.error || result.status !== 0)
|
|
35
|
+
return new Float32Array(0);
|
|
36
|
+
const bytes = result.stdout instanceof Buffer ? result.stdout : Buffer.from(result.stdout);
|
|
37
|
+
if (bytes.byteLength < 4)
|
|
38
|
+
return new Float32Array(0);
|
|
39
|
+
const aligned = bytes.byteLength - (bytes.byteLength % 4);
|
|
40
|
+
return new Float32Array(bytes.buffer, bytes.byteOffset, aligned / 4);
|
|
41
|
+
},
|
|
42
|
+
close() {
|
|
43
|
+
closed = true;
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function pickMicTool(spawn) {
|
|
48
|
+
const candidates = [
|
|
49
|
+
{
|
|
50
|
+
command: 'arecord',
|
|
51
|
+
args: (rate) => ['-f', 'FLOAT_LE', '-r', String(rate), '-c', '1', '-t', 'raw', '-d', '1', '-q'],
|
|
52
|
+
},
|
|
53
|
+
{ command: 'rec', args: (rate) => ['-q', '-t', 'f32', '-r', String(rate), '-c', '1', '-'] },
|
|
54
|
+
{ command: 'sox', args: (rate) => ['-d', '-t', 'f32', '-r', String(rate), '-c', '1', '-'] },
|
|
55
|
+
];
|
|
56
|
+
for (const tool of candidates) {
|
|
57
|
+
const probe = spawn(tool.command, ['--version']);
|
|
58
|
+
if (probe !== null && probe.error === undefined)
|
|
59
|
+
return tool;
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
function defaultMicSpawn(command, args) {
|
|
64
|
+
try {
|
|
65
|
+
const result = spawnSync(command, [...args], {
|
|
66
|
+
encoding: 'buffer',
|
|
67
|
+
timeout: 3_000,
|
|
68
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
69
|
+
});
|
|
70
|
+
if (result.error)
|
|
71
|
+
return { status: result.status, stdout: Buffer.alloc(0), error: result.error };
|
|
72
|
+
return { status: result.status, stdout: result.stdout ?? Buffer.alloc(0) };
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=mic.js.map
|
package/dist/mic.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mic.js","sourceRoot":"","sources":["../src/mic.ts"],"names":[],"mappings":"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"}
|
package/dist/mix.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface MixChannelsOptions {
|
|
2
|
+
/**
|
|
3
|
+
* Per-buffer duck amount 0..1. Buffer `i` is attenuated by the peak of
|
|
4
|
+
* the other buffers when `duck[i]` is set.
|
|
5
|
+
*/
|
|
6
|
+
duck?: number[];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Sum interleaved buffers (zero-padded to the longest). Optional per-buffer
|
|
10
|
+
* ducking against the rest of the mix.
|
|
11
|
+
*/
|
|
12
|
+
export declare function mixChannels(buffers: Float32Array[], options?: MixChannelsOptions): Float32Array;
|
|
13
|
+
/**
|
|
14
|
+
* Attenuate `target` when `sidechain` is loud. `amount` 0 leaves the target
|
|
15
|
+
* unchanged; 1 silences it at full sidechain peak.
|
|
16
|
+
*/
|
|
17
|
+
export declare function duck(target: Float32Array, sidechain: Float32Array, amount: number): Float32Array;
|
package/dist/mix.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sum interleaved buffers (zero-padded to the longest). Optional per-buffer
|
|
3
|
+
* ducking against the rest of the mix.
|
|
4
|
+
*/
|
|
5
|
+
export function mixChannels(buffers, options = {}) {
|
|
6
|
+
const length = buffers.reduce((max, buf) => Math.max(max, buf.length), 0);
|
|
7
|
+
const out = new Float32Array(length);
|
|
8
|
+
const amounts = options.duck ?? [];
|
|
9
|
+
for (let b = 0; b < buffers.length; b++) {
|
|
10
|
+
const buf = buffers[b];
|
|
11
|
+
if (buf === undefined)
|
|
12
|
+
continue;
|
|
13
|
+
const amount = amounts[b];
|
|
14
|
+
const source = amount !== undefined && amount !== 0 ? duck(buf, othersPeak(buffers, b, length), amount) : buf;
|
|
15
|
+
for (let i = 0; i < source.length; i++) {
|
|
16
|
+
out[i] = (out[i] ?? 0) + (source[i] ?? 0);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return out;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Attenuate `target` when `sidechain` is loud. `amount` 0 leaves the target
|
|
23
|
+
* unchanged; 1 silences it at full sidechain peak.
|
|
24
|
+
*/
|
|
25
|
+
export function duck(target, sidechain, amount) {
|
|
26
|
+
const peak = peakAbs(sidechain);
|
|
27
|
+
const gain = Math.max(0, 1 - Math.min(1, amount) * Math.min(1, peak));
|
|
28
|
+
const out = new Float32Array(target.length);
|
|
29
|
+
for (let i = 0; i < target.length; i++)
|
|
30
|
+
out[i] = (target[i] ?? 0) * gain;
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
function peakAbs(samples) {
|
|
34
|
+
let peak = 0;
|
|
35
|
+
for (let i = 0; i < samples.length; i++) {
|
|
36
|
+
const a = Math.abs(samples[i] ?? 0);
|
|
37
|
+
if (a > peak)
|
|
38
|
+
peak = a;
|
|
39
|
+
}
|
|
40
|
+
return peak;
|
|
41
|
+
}
|
|
42
|
+
function othersPeak(buffers, skip, length) {
|
|
43
|
+
const side = new Float32Array(length);
|
|
44
|
+
for (let b = 0; b < buffers.length; b++) {
|
|
45
|
+
if (b === skip)
|
|
46
|
+
continue;
|
|
47
|
+
const buf = buffers[b];
|
|
48
|
+
if (buf === undefined)
|
|
49
|
+
continue;
|
|
50
|
+
for (let i = 0; i < buf.length; i++) {
|
|
51
|
+
const a = Math.abs(buf[i] ?? 0);
|
|
52
|
+
if (a > (side[i] ?? 0))
|
|
53
|
+
side[i] = a;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return side;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=mix.js.map
|
package/dist/mix.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mix.js","sourceRoot":"","sources":["../src/mix.ts"],"names":[],"mappings":"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"}
|
package/dist/mp3.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface DecodedMp3 {
|
|
2
|
+
readonly sampleRate: number;
|
|
3
|
+
readonly channels: number;
|
|
4
|
+
/** Interleaved PCM. Real samples when `decoded` is true, else silence of the estimated duration. */
|
|
5
|
+
readonly samples: Float32Array;
|
|
6
|
+
readonly duration: number;
|
|
7
|
+
readonly frames: number;
|
|
8
|
+
/** True when an OS decoder (ffmpeg) produced PCM. */
|
|
9
|
+
readonly decoded: boolean;
|
|
10
|
+
}
|
|
11
|
+
export type Mp3Spawn = (command: string, args: readonly string[], input: Uint8Array) => Uint8Array | null;
|
|
12
|
+
export interface DecodeMp3Options {
|
|
13
|
+
/** Injected process runner. Tests stub ffmpeg. */
|
|
14
|
+
spawn?: Mp3Spawn;
|
|
15
|
+
}
|
|
16
|
+
export declare function looksLikeMp3(bytes: Uint8Array): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Decode MP3 to PCM. Tries `ffmpeg` for real samples; falls back to
|
|
19
|
+
* header-scanned silence when no decoder is available.
|
|
20
|
+
*/
|
|
21
|
+
export declare function decodeMp3(buffer: Uint8Array, options?: DecodeMp3Options): DecodedMp3;
|
package/dist/mp3.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
/** [unused, Layer I, Layer II, Layer III] kbps. */
|
|
3
|
+
const BITRATE_V1 = [
|
|
4
|
+
[0, 0, 0, 0],
|
|
5
|
+
[0, 32, 32, 32],
|
|
6
|
+
[0, 64, 48, 40],
|
|
7
|
+
[0, 96, 56, 48],
|
|
8
|
+
[0, 128, 64, 56],
|
|
9
|
+
[0, 160, 80, 64],
|
|
10
|
+
[0, 192, 96, 80],
|
|
11
|
+
[0, 224, 112, 96],
|
|
12
|
+
[0, 256, 128, 112],
|
|
13
|
+
[0, 288, 160, 128],
|
|
14
|
+
[0, 320, 192, 160],
|
|
15
|
+
[0, 352, 224, 192],
|
|
16
|
+
[0, 384, 256, 224],
|
|
17
|
+
[0, 416, 320, 256],
|
|
18
|
+
[0, 448, 384, 320],
|
|
19
|
+
[0, 0, 0, 0],
|
|
20
|
+
];
|
|
21
|
+
const BITRATE_V2 = [
|
|
22
|
+
[0, 0, 0, 0],
|
|
23
|
+
[0, 32, 8, 8],
|
|
24
|
+
[0, 48, 16, 16],
|
|
25
|
+
[0, 56, 24, 24],
|
|
26
|
+
[0, 64, 32, 32],
|
|
27
|
+
[0, 80, 40, 40],
|
|
28
|
+
[0, 96, 48, 48],
|
|
29
|
+
[0, 112, 56, 56],
|
|
30
|
+
[0, 128, 64, 64],
|
|
31
|
+
[0, 144, 80, 80],
|
|
32
|
+
[0, 160, 96, 96],
|
|
33
|
+
[0, 176, 112, 112],
|
|
34
|
+
[0, 192, 128, 128],
|
|
35
|
+
[0, 224, 144, 144],
|
|
36
|
+
[0, 256, 160, 160],
|
|
37
|
+
[0, 0, 0, 0],
|
|
38
|
+
];
|
|
39
|
+
const SR_V1 = [44100, 48000, 32000];
|
|
40
|
+
const SR_V2 = [22050, 24000, 16000];
|
|
41
|
+
const SR_V25 = [11025, 12000, 8000];
|
|
42
|
+
export function looksLikeMp3(bytes) {
|
|
43
|
+
if (bytes.byteLength >= 3 && bytes[0] === 0x49 && bytes[1] === 0x44 && bytes[2] === 0x33) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return findMpegHeader(bytes, 0) !== undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Decode MP3 to PCM. Tries `ffmpeg` for real samples; falls back to
|
|
50
|
+
* header-scanned silence when no decoder is available.
|
|
51
|
+
*/
|
|
52
|
+
export function decodeMp3(buffer, options = {}) {
|
|
53
|
+
const headers = scanMp3(buffer);
|
|
54
|
+
const spawn = options.spawn ?? (process.env['VITEST'] !== undefined ? () => null : defaultFfmpegSpawn);
|
|
55
|
+
const pcm = spawn('ffmpeg', [
|
|
56
|
+
'-hide_banner',
|
|
57
|
+
'-loglevel',
|
|
58
|
+
'error',
|
|
59
|
+
'-i',
|
|
60
|
+
'pipe:0',
|
|
61
|
+
'-f',
|
|
62
|
+
'f32le',
|
|
63
|
+
'-ac',
|
|
64
|
+
String(headers.channels),
|
|
65
|
+
'-ar',
|
|
66
|
+
String(headers.sampleRate),
|
|
67
|
+
'pipe:1',
|
|
68
|
+
], buffer);
|
|
69
|
+
if (pcm !== null && pcm.byteLength >= 4) {
|
|
70
|
+
const aligned = pcm.byteLength - (pcm.byteLength % 4);
|
|
71
|
+
const samples = new Float32Array(pcm.buffer, pcm.byteOffset, aligned / 4);
|
|
72
|
+
return {
|
|
73
|
+
sampleRate: headers.sampleRate,
|
|
74
|
+
channels: headers.channels,
|
|
75
|
+
samples,
|
|
76
|
+
duration: samples.length / headers.channels / headers.sampleRate,
|
|
77
|
+
frames: headers.frames,
|
|
78
|
+
decoded: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
sampleRate: headers.sampleRate,
|
|
83
|
+
channels: headers.channels,
|
|
84
|
+
samples: new Float32Array(headers.samplesPerChannel * headers.channels),
|
|
85
|
+
duration: headers.samplesPerChannel / headers.sampleRate,
|
|
86
|
+
frames: headers.frames,
|
|
87
|
+
decoded: false,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function scanMp3(buffer) {
|
|
91
|
+
let offset = skipId3v2(buffer);
|
|
92
|
+
let sampleRate = 0;
|
|
93
|
+
let channels = 0;
|
|
94
|
+
let frames = 0;
|
|
95
|
+
let samplesPerChannel = 0;
|
|
96
|
+
while (offset + 4 <= buffer.byteLength) {
|
|
97
|
+
const header = parseMpegHeader(buffer, offset);
|
|
98
|
+
if (header === undefined) {
|
|
99
|
+
offset += 1;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (sampleRate === 0) {
|
|
103
|
+
sampleRate = header.sampleRate;
|
|
104
|
+
channels = header.channels;
|
|
105
|
+
}
|
|
106
|
+
frames += 1;
|
|
107
|
+
samplesPerChannel += header.samplesPerFrame;
|
|
108
|
+
offset += header.frameLength;
|
|
109
|
+
}
|
|
110
|
+
if (frames === 0 || sampleRate === 0 || channels === 0) {
|
|
111
|
+
throw new Error('[audio] MP3 payload has no MPEG frames.');
|
|
112
|
+
}
|
|
113
|
+
return { sampleRate, channels, frames, samplesPerChannel };
|
|
114
|
+
}
|
|
115
|
+
function defaultFfmpegSpawn(command, args, input) {
|
|
116
|
+
try {
|
|
117
|
+
const result = spawnSync(command, [...args], {
|
|
118
|
+
input: Buffer.from(input),
|
|
119
|
+
encoding: 'buffer',
|
|
120
|
+
timeout: 8_000,
|
|
121
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
122
|
+
});
|
|
123
|
+
if (result.error || result.status !== 0 || result.stdout === null)
|
|
124
|
+
return null;
|
|
125
|
+
const stdout = result.stdout;
|
|
126
|
+
return stdout.byteLength > 0 ? new Uint8Array(stdout) : null;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function skipId3v2(bytes) {
|
|
133
|
+
if (bytes.byteLength < 10)
|
|
134
|
+
return 0;
|
|
135
|
+
if (bytes[0] !== 0x49 || bytes[1] !== 0x44 || bytes[2] !== 0x33)
|
|
136
|
+
return 0;
|
|
137
|
+
const size = ((bytes[6] ?? 0) & 0x7f) * 0x200000 +
|
|
138
|
+
((bytes[7] ?? 0) & 0x7f) * 0x4000 +
|
|
139
|
+
((bytes[8] ?? 0) & 0x7f) * 0x80 +
|
|
140
|
+
((bytes[9] ?? 0) & 0x7f);
|
|
141
|
+
return 10 + size;
|
|
142
|
+
}
|
|
143
|
+
function findMpegHeader(bytes, start) {
|
|
144
|
+
for (let i = start; i + 4 <= bytes.byteLength; i++) {
|
|
145
|
+
if (parseMpegHeader(bytes, i) !== undefined)
|
|
146
|
+
return i;
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
function parseMpegHeader(bytes, offset) {
|
|
151
|
+
const b0 = bytes[offset] ?? 0;
|
|
152
|
+
const b1 = bytes[offset + 1] ?? 0;
|
|
153
|
+
const b2 = bytes[offset + 2] ?? 0;
|
|
154
|
+
const b3 = bytes[offset + 3] ?? 0;
|
|
155
|
+
if (b0 !== 0xff || (b1 & 0xe0) !== 0xe0)
|
|
156
|
+
return undefined;
|
|
157
|
+
const versionBits = (b1 >> 3) & 0x03;
|
|
158
|
+
const layerBits = (b1 >> 1) & 0x03;
|
|
159
|
+
if (versionBits === 1 || layerBits === 0)
|
|
160
|
+
return undefined;
|
|
161
|
+
const bitrateIndex = (b2 >> 4) & 0x0f;
|
|
162
|
+
const srIndex = (b2 >> 2) & 0x03;
|
|
163
|
+
const padding = (b2 >> 1) & 0x01;
|
|
164
|
+
if (bitrateIndex === 0 || bitrateIndex === 15 || srIndex === 3)
|
|
165
|
+
return undefined;
|
|
166
|
+
const mpeg1 = versionBits === 3;
|
|
167
|
+
const mpeg25 = versionBits === 0;
|
|
168
|
+
const sampleRate = (mpeg1 ? SR_V1 : mpeg25 ? SR_V25 : SR_V2)[srIndex];
|
|
169
|
+
if (sampleRate === undefined)
|
|
170
|
+
return undefined;
|
|
171
|
+
const layer = layerBits === 3 ? 1 : layerBits === 2 ? 2 : 3;
|
|
172
|
+
const table = mpeg1 ? BITRATE_V1 : BITRATE_V2;
|
|
173
|
+
const bitrate = table[bitrateIndex]?.[layer] ?? 0;
|
|
174
|
+
if (bitrate === 0)
|
|
175
|
+
return undefined;
|
|
176
|
+
const samplesPerFrame = layer === 1 ? 384 : layer === 2 ? 1152 : mpeg1 ? 1152 : 576;
|
|
177
|
+
let frameLength;
|
|
178
|
+
if (layer === 1) {
|
|
179
|
+
frameLength = Math.floor((12 * bitrate * 1000) / sampleRate + padding) * 4;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
const coeff = layer === 3 && !mpeg1 ? 72 : 144;
|
|
183
|
+
frameLength = Math.floor((coeff * bitrate * 1000) / sampleRate) + padding;
|
|
184
|
+
}
|
|
185
|
+
if (frameLength < 4)
|
|
186
|
+
return undefined;
|
|
187
|
+
const channels = ((b3 >> 6) & 0x03) === 3 ? 1 : 2;
|
|
188
|
+
return { sampleRate, channels, frameLength, samplesPerFrame };
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=mp3.js.map
|
package/dist/mp3.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mp3.js","sourceRoot":"","sources":["../src/mp3.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type BeatSubdivision = 4 | 8 | 16;
|
|
2
|
+
/**
|
|
3
|
+
* Snap `timeSec` to the nearest beat-grid position.
|
|
4
|
+
* `subdivision` 4 = quarter, 8 = eighth, 16 = sixteenth.
|
|
5
|
+
*/
|
|
6
|
+
export declare function quantize(timeSec: number, bpm: number, subdivision?: BeatSubdivision): number;
|
package/dist/quantize.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snap `timeSec` to the nearest beat-grid position.
|
|
3
|
+
* `subdivision` 4 = quarter, 8 = eighth, 16 = sixteenth.
|
|
4
|
+
*/
|
|
5
|
+
export function quantize(timeSec, bpm, subdivision = 4) {
|
|
6
|
+
const beatSec = 60 / Math.max(bpm, 1e-6);
|
|
7
|
+
const step = beatSec * (4 / subdivision);
|
|
8
|
+
return Math.round(timeSec / step) * step;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=quantize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quantize.js","sourceRoot":"","sources":["../src/quantize.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAe,EAAE,GAAW,EAAE,WAAW,GAAoB,CAAC;IACrF,MAAM,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzC,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type FftBandResult } from './fft.js';
|
|
2
|
+
export type ReactiveEvent = 'beat' | 'energy';
|
|
3
|
+
export type ReactiveListener = (value: number) => void;
|
|
4
|
+
export interface ReactiveBridge {
|
|
5
|
+
push(samples: Float32Array): FftBandResult;
|
|
6
|
+
bands(): number[];
|
|
7
|
+
energy(): number;
|
|
8
|
+
on(event: ReactiveEvent, fn: ReactiveListener): () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface ReactiveBridgeOptions {
|
|
11
|
+
sampleRate?: number;
|
|
12
|
+
/** Energy rise ratio that counts as a beat. Default 1.4. */
|
|
13
|
+
beatRatio?: number;
|
|
14
|
+
/** Minimum energy to emit a beat. Default 0.08. */
|
|
15
|
+
beatFloor?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Audio-reactive event bridge for `@mudah-cli/vgpu` `bindAudioReactive`.
|
|
19
|
+
* `push` runs FFT band extraction and emits `'energy'` / `'beat'`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function createReactiveBridge(options?: ReactiveBridgeOptions): ReactiveBridge;
|
package/dist/reactive.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { FftBands } from './fft.js';
|
|
2
|
+
/**
|
|
3
|
+
* Audio-reactive event bridge for `@mudah-cli/vgpu` `bindAudioReactive`.
|
|
4
|
+
* `push` runs FFT band extraction and emits `'energy'` / `'beat'`.
|
|
5
|
+
*/
|
|
6
|
+
export function createReactiveBridge(options = {}) {
|
|
7
|
+
const sampleRate = options.sampleRate ?? 44100;
|
|
8
|
+
const beatRatio = options.beatRatio ?? 1.4;
|
|
9
|
+
const beatFloor = options.beatFloor ?? 0.08;
|
|
10
|
+
let last = { bass: 0, mid: 0, high: 0, energy: 0, bands: [] };
|
|
11
|
+
let prevEnergy = 0;
|
|
12
|
+
let cooldown = 0;
|
|
13
|
+
const listeners = {
|
|
14
|
+
beat: new Set(),
|
|
15
|
+
energy: new Set(),
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
push(samples) {
|
|
19
|
+
last = FftBands.fromSamples(samples, sampleRate);
|
|
20
|
+
for (const fn of listeners.energy)
|
|
21
|
+
fn(last.energy);
|
|
22
|
+
const rising = last.energy > prevEnergy * beatRatio && last.energy >= beatFloor;
|
|
23
|
+
if (rising && cooldown <= 0) {
|
|
24
|
+
for (const fn of listeners.beat)
|
|
25
|
+
fn(last.energy);
|
|
26
|
+
cooldown = 3;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
cooldown = Math.max(0, cooldown - 1);
|
|
30
|
+
}
|
|
31
|
+
prevEnergy = last.energy;
|
|
32
|
+
return last;
|
|
33
|
+
},
|
|
34
|
+
bands() {
|
|
35
|
+
if (last.bands.length > 0)
|
|
36
|
+
return last.bands;
|
|
37
|
+
return [last.bass, last.mid, last.high];
|
|
38
|
+
},
|
|
39
|
+
energy() {
|
|
40
|
+
return last.energy;
|
|
41
|
+
},
|
|
42
|
+
on(event, fn) {
|
|
43
|
+
listeners[event].add(fn);
|
|
44
|
+
return () => {
|
|
45
|
+
listeners[event].delete(fn);
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=reactive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactive.js","sourceRoot":"","sources":["../src/reactive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,QAAQ,EAAE,MAAM,UAAU,CAAC;AAqBxD;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAO,GAA0B,EAAE;IACtE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,KAAK,CAAC;IAC/C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,GAAG,CAAC;IAC3C,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,IAAI,CAAC;IAC5C,IAAI,IAAI,GAAkB,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IAC7E,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAM,SAAS,GAAiD;QAC9D,IAAI,EAAE,IAAI,GAAG,EAAE;QACf,MAAM,EAAE,IAAI,GAAG,EAAE;KAClB,CAAC;IAEF,OAAO;QACL,IAAI,CAAC,OAAqB;YACxB,IAAI,GAAG,QAAQ,CAAC,WAAW,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;YACjD,KAAK,MAAM,EAAE,IAAI,SAAS,CAAC,MAAM;gBAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,GAAG,UAAU,GAAG,SAAS,IAAI,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC;YAChF,IAAI,MAAM,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;gBAC5B,KAAK,MAAM,EAAE,IAAI,SAAS,CAAC,IAAI;oBAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACjD,QAAQ,GAAG,CAAC,CAAC;YACf,CAAC;iBAAM,CAAC;gBACN,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC;YACvC,CAAC;YACD,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC;YACzB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK;YACH,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC,KAAK,CAAC;YAC7C,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,CAAC;QACD,MAAM;YACJ,OAAO,IAAI,CAAC,MAAM,CAAC;QACrB,CAAC;QACD,EAAE,CAAC,KAAoB,EAAE,EAAoB;YAC3C,SAAS,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACzB,OAAO,GAAG,EAAE;gBACV,SAAS,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC9B,CAAC,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/tune.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type TuneDurationUnit = 'sec' | 'beat';
|
|
2
|
+
/** One pitch or a rest. `duration` is seconds when `unit` is `sec`, beats (quarter = 1) otherwise. */
|
|
3
|
+
export interface Note {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly midi: number | null;
|
|
6
|
+
readonly duration: number;
|
|
7
|
+
readonly unit: TuneDurationUnit;
|
|
8
|
+
}
|
|
9
|
+
export interface Tune {
|
|
10
|
+
readonly notes: readonly Note[];
|
|
11
|
+
readonly source: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Parse a tone-sequence DSL.
|
|
15
|
+
*
|
|
16
|
+
* - `C4:0.25 D4:0.25 rest:0.5` — duration in seconds
|
|
17
|
+
* - `C4 8n, D4 8n` — note values (`4n` quarter, `8n` eighth, dotted `8n.`)
|
|
18
|
+
*/
|
|
19
|
+
export declare function parseTune(text: string): Tune;
|
|
20
|
+
export declare function parsePitch(token: string): number;
|
|
21
|
+
export declare function midiToHz(midi: number): number;
|
package/dist/tune.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const LETTERS = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };
|
|
2
|
+
const NOTE_VALUES = {
|
|
3
|
+
'1n': 4,
|
|
4
|
+
'2n': 2,
|
|
5
|
+
'4n': 1,
|
|
6
|
+
'8n': 0.5,
|
|
7
|
+
'16n': 0.25,
|
|
8
|
+
'32n': 0.125,
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Parse a tone-sequence DSL.
|
|
12
|
+
*
|
|
13
|
+
* - `C4:0.25 D4:0.25 rest:0.5` — duration in seconds
|
|
14
|
+
* - `C4 8n, D4 8n` — note values (`4n` quarter, `8n` eighth, dotted `8n.`)
|
|
15
|
+
*/
|
|
16
|
+
export function parseTune(text) {
|
|
17
|
+
const notes = [];
|
|
18
|
+
const tokens = tokenize(text);
|
|
19
|
+
let i = 0;
|
|
20
|
+
while (i < tokens.length) {
|
|
21
|
+
const token = tokens[i] ?? '';
|
|
22
|
+
const colon = token.indexOf(':');
|
|
23
|
+
if (colon > 0) {
|
|
24
|
+
notes.push(makeNote(token.slice(0, colon), token.slice(colon + 1)));
|
|
25
|
+
i += 1;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
const next = tokens[i + 1];
|
|
29
|
+
if (next !== undefined && isDurationToken(next)) {
|
|
30
|
+
notes.push(makeNote(token, next));
|
|
31
|
+
i += 2;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
throw new Error(`[audio] Tune token "${token}" needs a duration (C4:0.25 or C4 8n).`);
|
|
35
|
+
}
|
|
36
|
+
return { notes, source: text };
|
|
37
|
+
}
|
|
38
|
+
export function parsePitch(token) {
|
|
39
|
+
const match = /^([A-G])([#b]?)(-?\d+)$/.exec(token);
|
|
40
|
+
if (match === null)
|
|
41
|
+
throw new Error(`[audio] Bad pitch ${token}.`);
|
|
42
|
+
const letter = match[1] ?? 'C';
|
|
43
|
+
const accidental = match[2] ?? '';
|
|
44
|
+
const octave = Number(match[3]);
|
|
45
|
+
const semis = LETTERS[letter] ?? 0;
|
|
46
|
+
const shift = accidental === '#' ? 1 : accidental === 'b' ? -1 : 0;
|
|
47
|
+
return (octave + 1) * 12 + semis + shift;
|
|
48
|
+
}
|
|
49
|
+
export function midiToHz(midi) {
|
|
50
|
+
return 440 * 2 ** ((midi - 69) / 12);
|
|
51
|
+
}
|
|
52
|
+
function tokenize(text) {
|
|
53
|
+
const stripped = text
|
|
54
|
+
.split(/\r?\n/)
|
|
55
|
+
.map((line) => line
|
|
56
|
+
.replace(/#.*$/, '')
|
|
57
|
+
.replace(/\/\/.*$/, '')
|
|
58
|
+
.trim())
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.join(' ');
|
|
61
|
+
return stripped.split(/[\s,]+/).filter(Boolean);
|
|
62
|
+
}
|
|
63
|
+
function isRest(token) {
|
|
64
|
+
const lower = token.toLowerCase();
|
|
65
|
+
return lower === 'rest' || lower === 'r' || token === '-';
|
|
66
|
+
}
|
|
67
|
+
function isDurationToken(token) {
|
|
68
|
+
return parseDuration(token) !== undefined;
|
|
69
|
+
}
|
|
70
|
+
function parseDuration(token) {
|
|
71
|
+
if (/^\d+(?:\.\d+)?$/.test(token))
|
|
72
|
+
return { duration: Number(token), unit: 'sec' };
|
|
73
|
+
const dotted = token.endsWith('.');
|
|
74
|
+
const core = dotted ? token.slice(0, -1) : token;
|
|
75
|
+
const beats = NOTE_VALUES[core];
|
|
76
|
+
if (beats === undefined)
|
|
77
|
+
return undefined;
|
|
78
|
+
return { duration: dotted ? beats * 1.5 : beats, unit: 'beat' };
|
|
79
|
+
}
|
|
80
|
+
function makeNote(pitchToken, durationToken) {
|
|
81
|
+
const parsed = parseDuration(durationToken);
|
|
82
|
+
if (parsed === undefined) {
|
|
83
|
+
throw new Error(`[audio] Bad duration "${durationToken}". Use seconds or 4n/8n/16n.`);
|
|
84
|
+
}
|
|
85
|
+
if (isRest(pitchToken)) {
|
|
86
|
+
return { name: 'rest', midi: null, duration: parsed.duration, unit: parsed.unit };
|
|
87
|
+
}
|
|
88
|
+
const midi = parsePitch(pitchToken);
|
|
89
|
+
return { name: pitchToken, midi, duration: parsed.duration, unit: parsed.unit };
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=tune.js.map
|
package/dist/tune.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tune.js","sourceRoot":"","sources":["../src/tune.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mudah-cli/audio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Play PCM through the OS mixer from a Mudah app (streaming output and one-shot WAV). Optional: apps that never import it never load an audio backend.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"author": "Mudah Contributors",
|
|
8
|
+
"homepage": "https://github.com/thesimonharms/mudah#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/thesimonharms/mudah.git",
|
|
12
|
+
"directory": "packages/audio"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"cli",
|
|
16
|
+
"terminal",
|
|
17
|
+
"audio",
|
|
18
|
+
"pcm",
|
|
19
|
+
"pipewire",
|
|
20
|
+
"alsa",
|
|
21
|
+
"typescript"
|
|
22
|
+
],
|
|
7
23
|
"engines": {
|
|
8
24
|
"node": ">=26"
|
|
9
25
|
},
|