@litlab/audx 0.6.0 → 0.8.5
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/dist/bin.js +191 -2
- package/dist/{cc-DgCkkqq8.js → cc-zJdVkWBV.js} +12 -1
- package/dist/index.js +3 -1
- package/dist/react.js +3 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
import * as p from '@clack/prompts';
|
|
2
|
+
import { a as _object_without_properties_loose, _ as _extends } from './cc-zJdVkWBV.js';
|
|
3
3
|
import { existsSync, mkdirSync } from 'node:fs';
|
|
4
4
|
import { readdir, readFile, writeFile, unlink } from 'node:fs/promises';
|
|
5
5
|
import __node_cjsPath, { resolve, join, basename, isAbsolute } from 'node:path';
|
|
6
6
|
import pc from 'picocolors';
|
|
7
|
-
import { _ as _object_without_properties_loose } from './cc-DgCkkqq8.js';
|
|
8
7
|
import __node_cjsModule from 'node:module';
|
|
9
8
|
import __node_cjsUrl from 'node:url';
|
|
10
9
|
|
|
@@ -386,6 +385,10 @@ function parseAddOptions(args) {
|
|
|
386
385
|
options.list = true;
|
|
387
386
|
} else if (arg === "--theme") {
|
|
388
387
|
options.theme = args[++i];
|
|
388
|
+
} else if (arg === "--data") {
|
|
389
|
+
options.data = args[++i];
|
|
390
|
+
} else if (arg === "--tune") {
|
|
391
|
+
options.tune = args[++i];
|
|
389
392
|
} else if (arg && !arg.startsWith("-")) {
|
|
390
393
|
source = arg;
|
|
391
394
|
}
|
|
@@ -399,9 +402,21 @@ async function add(args) {
|
|
|
399
402
|
const { source, options } = parseAddOptions(args);
|
|
400
403
|
p.intro("@litlab/audx add");
|
|
401
404
|
if (!source) {
|
|
405
|
+
if (options.data || options.tune) {
|
|
406
|
+
p.log.error("--data and --tune require a sound name.");
|
|
407
|
+
process.exit(1);
|
|
408
|
+
}
|
|
402
409
|
await addFromRegistry(options);
|
|
403
410
|
return;
|
|
404
411
|
}
|
|
412
|
+
if (options.data) {
|
|
413
|
+
await addEncodedSound(source, options);
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (options.tune) {
|
|
417
|
+
await addTunedSound(source, options);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
405
420
|
if (isLocalSource(source)) {
|
|
406
421
|
await addFromLocal(source, options);
|
|
407
422
|
return;
|
|
@@ -416,6 +431,178 @@ async function add(args) {
|
|
|
416
431
|
}
|
|
417
432
|
await addSoundFromRegistry(source, options);
|
|
418
433
|
}
|
|
434
|
+
const WAVEFORM_CODES = {
|
|
435
|
+
o: "original",
|
|
436
|
+
s: "sine",
|
|
437
|
+
t: "triangle",
|
|
438
|
+
q: "square",
|
|
439
|
+
w: "sawtooth"
|
|
440
|
+
};
|
|
441
|
+
function decodeSoundCustomization(value) {
|
|
442
|
+
const [version, pitch, duration, gain, pan, waveform, ...extra] = value.split(".");
|
|
443
|
+
if (version !== "1" || extra.length > 0 || !pitch || !duration || !gain || !pan || !waveform) {
|
|
444
|
+
throw new Error("unsupported tuning token");
|
|
445
|
+
}
|
|
446
|
+
const settings = {
|
|
447
|
+
pitch: Number.parseInt(pitch, 36) - 12,
|
|
448
|
+
duration: Number.parseInt(duration, 36) / 20,
|
|
449
|
+
gain: Number.parseInt(gain, 36) / 20,
|
|
450
|
+
pan: (Number.parseInt(pan, 36) - 20) / 20,
|
|
451
|
+
waveform: WAVEFORM_CODES[waveform]
|
|
452
|
+
};
|
|
453
|
+
if (!Number.isInteger(settings.pitch) || settings.pitch < -12 || settings.pitch > 12 || !Number.isFinite(settings.duration) || settings.duration < 0.25 || settings.duration > 3 || !Number.isFinite(settings.gain) || settings.gain < 0 || settings.gain > 2 || !Number.isFinite(settings.pan) || settings.pan < -1 || settings.pan > 1 || !settings.waveform) {
|
|
454
|
+
throw new Error("invalid tuning values");
|
|
455
|
+
}
|
|
456
|
+
return settings;
|
|
457
|
+
}
|
|
458
|
+
async function addTunedSound(soundName, options) {
|
|
459
|
+
if (!options.theme) {
|
|
460
|
+
p.log.error("--tune requires --theme <name>.");
|
|
461
|
+
process.exit(1);
|
|
462
|
+
}
|
|
463
|
+
let settings;
|
|
464
|
+
try {
|
|
465
|
+
var _options_tune;
|
|
466
|
+
settings = decodeSoundCustomization((_options_tune = options.tune) != null ? _options_tune : "");
|
|
467
|
+
} catch (err) {
|
|
468
|
+
p.log.error(`Invalid tuning token: ${err instanceof Error ? err.message : String(err)}`);
|
|
469
|
+
process.exit(1);
|
|
470
|
+
}
|
|
471
|
+
const s = p.spinner();
|
|
472
|
+
s.start(`Fetching "${soundName}" from ${options.theme}...`);
|
|
473
|
+
try {
|
|
474
|
+
const theme = await fetchThemeJson(options.theme);
|
|
475
|
+
if (!validateTheme(theme)) throw new Error("invalid theme");
|
|
476
|
+
const match = Object.entries(theme.sounds).find(([name])=>name.toLowerCase() === soundName.toLowerCase());
|
|
477
|
+
if (!match) throw new Error(`sound not found in theme "${options.theme}"`);
|
|
478
|
+
const definition = customizeDefinition(match[1], settings);
|
|
479
|
+
if (!validateSoundDefinition(definition)) {
|
|
480
|
+
throw new Error("theme returned an invalid sound definition");
|
|
481
|
+
}
|
|
482
|
+
s.stop(`Customized "${soundName}" from ${theme.name}`);
|
|
483
|
+
await writeSound(soundName, definition, options);
|
|
484
|
+
p.note(` - ${soundName}`, "Installed customized sound");
|
|
485
|
+
p.outro("Done!");
|
|
486
|
+
} catch (err) {
|
|
487
|
+
s.stop("Failed to install customized sound.");
|
|
488
|
+
p.log.error(err instanceof Error ? err.message : String(err));
|
|
489
|
+
process.exit(1);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
function customizeDefinition(value, settings) {
|
|
493
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
494
|
+
const definition = value;
|
|
495
|
+
if (Array.isArray(definition.layers)) {
|
|
496
|
+
return _extends({}, definition, {
|
|
497
|
+
layers: definition.layers.map((layer)=>customizeDefinition(layer, settings)),
|
|
498
|
+
effects: scaleEffects(definition.effects, settings.duration)
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
const source = customizeSource(definition.source, settings);
|
|
502
|
+
const envelope = definition.envelope && typeof definition.envelope === "object" && !Array.isArray(definition.envelope) ? scaleEnvelope(definition.envelope, settings.duration) : undefined;
|
|
503
|
+
const gain = typeof definition.gain === "number" ? definition.gain : 0.5;
|
|
504
|
+
const delay = typeof definition.delay === "number" ? definition.delay : 0;
|
|
505
|
+
return _extends({}, definition, {
|
|
506
|
+
source,
|
|
507
|
+
envelope,
|
|
508
|
+
gain: clamp(gain * settings.gain, 0, 1)
|
|
509
|
+
}, "panner" in definition ? {} : {
|
|
510
|
+
pan: clamp((typeof definition.pan === "number" ? definition.pan : 0) + settings.pan, -1, 1)
|
|
511
|
+
}, {
|
|
512
|
+
delay: delay * settings.duration,
|
|
513
|
+
effects: scaleEffects(definition.effects, settings.duration)
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
function customizeSource(value, settings) {
|
|
517
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
518
|
+
const source = value;
|
|
519
|
+
const frequencyScale = 2 ** (settings.pitch / 12);
|
|
520
|
+
if (source.type === "sine" || source.type === "triangle" || source.type === "square" || source.type === "sawtooth" || source.type === "wavetable") {
|
|
521
|
+
return _extends({}, source, source.type === "wavetable" || settings.waveform === "original" ? {} : {
|
|
522
|
+
type: settings.waveform
|
|
523
|
+
}, {
|
|
524
|
+
frequency: scaleFrequency(source.frequency, frequencyScale)
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
if (source.type === "sample") {
|
|
528
|
+
return _extends({}, source, {
|
|
529
|
+
detune: (typeof source.detune === "number" ? source.detune : 0) + settings.pitch * 100
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
return source;
|
|
533
|
+
}
|
|
534
|
+
function scaleFrequency(value, scale) {
|
|
535
|
+
if (typeof value === "number") return value * scale;
|
|
536
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
537
|
+
const frequency = value;
|
|
538
|
+
if (typeof frequency.start !== "number" || typeof frequency.end !== "number") {
|
|
539
|
+
return value;
|
|
540
|
+
}
|
|
541
|
+
return {
|
|
542
|
+
start: frequency.start * scale,
|
|
543
|
+
end: frequency.end * scale
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
function scaleEnvelope(envelope, duration) {
|
|
547
|
+
return _extends({}, envelope, {
|
|
548
|
+
attack: (typeof envelope.attack === "number" ? envelope.attack : 0) * duration,
|
|
549
|
+
decay: (typeof envelope.decay === "number" ? envelope.decay : 0) * duration,
|
|
550
|
+
release: (typeof envelope.release === "number" ? envelope.release : 0) * duration
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
function scaleEffects(value, duration) {
|
|
554
|
+
if (!Array.isArray(value)) return undefined;
|
|
555
|
+
return value.map((effect)=>{
|
|
556
|
+
if (!effect || typeof effect !== "object" || Array.isArray(effect)) {
|
|
557
|
+
return effect;
|
|
558
|
+
}
|
|
559
|
+
const item = effect;
|
|
560
|
+
if (item.type === "delay") {
|
|
561
|
+
return _extends({}, item, {
|
|
562
|
+
time: (typeof item.time === "number" ? item.time : 0.25) * duration
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
if (item.type === "reverb") {
|
|
566
|
+
return _extends({}, item, {
|
|
567
|
+
decay: (typeof item.decay === "number" ? item.decay : 0.5) * duration,
|
|
568
|
+
preDelay: (typeof item.preDelay === "number" ? item.preDelay : 0) * duration
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
return item;
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
function clamp(value, min, max) {
|
|
575
|
+
return Math.min(max, Math.max(min, value));
|
|
576
|
+
}
|
|
577
|
+
async function addEncodedSound(soundName, options) {
|
|
578
|
+
let definition;
|
|
579
|
+
try {
|
|
580
|
+
var _options_data;
|
|
581
|
+
definition = decodeSoundDefinition((_options_data = options.data) != null ? _options_data : "");
|
|
582
|
+
} catch (err) {
|
|
583
|
+
p.log.error(`Invalid customized sound data: ${err instanceof Error ? err.message : String(err)}`);
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
if (!validateSoundDefinition(definition)) {
|
|
587
|
+
p.log.error("Invalid customized sound definition.");
|
|
588
|
+
process.exit(1);
|
|
589
|
+
}
|
|
590
|
+
await writeSound(soundName, definition, options);
|
|
591
|
+
p.note(` - ${soundName}`, "Installed customized sound");
|
|
592
|
+
p.outro("Done!");
|
|
593
|
+
}
|
|
594
|
+
function decodeSoundDefinition(value) {
|
|
595
|
+
if (!value) throw new Error("missing value");
|
|
596
|
+
return JSON.parse(Buffer.from(value, "base64url").toString("utf-8"));
|
|
597
|
+
}
|
|
598
|
+
function validateSoundDefinition(value) {
|
|
599
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
600
|
+
const definition = value;
|
|
601
|
+
if (Array.isArray(definition.layers)) {
|
|
602
|
+
return definition.layers.length > 0 && definition.layers.every(validateSoundDefinition);
|
|
603
|
+
}
|
|
604
|
+
return !!definition.source && typeof definition.source === "object" && !Array.isArray(definition.source);
|
|
605
|
+
}
|
|
419
606
|
async function addFromLocal(source, options) {
|
|
420
607
|
const s = p.spinner();
|
|
421
608
|
s.start("Scanning local path for themes...");
|
|
@@ -1147,6 +1334,8 @@ function showHelp() {
|
|
|
1147
1334
|
" -l, --list Preview available themes without installing",
|
|
1148
1335
|
" -y, --yes Skip confirmation prompts",
|
|
1149
1336
|
" --theme <name> Install a specific theme by name",
|
|
1337
|
+
" --data <value> Install an encoded customized sound definition",
|
|
1338
|
+
" --tune <token> Apply compact editor settings (requires --theme)",
|
|
1150
1339
|
"",
|
|
1151
1340
|
"Remove Options:",
|
|
1152
1341
|
" -y, --yes Skip confirmation prompts"
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
function _extends() {
|
|
2
|
+
_extends = Object.assign || function assign(target) {
|
|
3
|
+
for(var i = 1; i < arguments.length; i++){
|
|
4
|
+
var source = arguments[i];
|
|
5
|
+
for(var key in source)if (Object.prototype.hasOwnProperty.call(source, key)) target[key] = source[key];
|
|
6
|
+
}
|
|
7
|
+
return target;
|
|
8
|
+
};
|
|
9
|
+
return _extends.apply(this, arguments);
|
|
10
|
+
}
|
|
11
|
+
|
|
1
12
|
function _object_without_properties_loose(source, excluded) {
|
|
2
13
|
if (source == null) return {};
|
|
3
14
|
var target = {}, sourceKeys = Object.getOwnPropertyNames(source), key, i;
|
|
@@ -10,4 +21,4 @@ function _object_without_properties_loose(source, excluded) {
|
|
|
10
21
|
return target;
|
|
11
22
|
}
|
|
12
23
|
|
|
13
|
-
export {
|
|
24
|
+
export { _extends as _, _object_without_properties_loose as a };
|
package/dist/index.js
CHANGED
|
@@ -1293,7 +1293,9 @@ function createPatchInstance(data) {
|
|
|
1293
1293
|
* @throws {Error} If the network request fails
|
|
1294
1294
|
*/ async function loadPatch(source) {
|
|
1295
1295
|
if (typeof source === "string") {
|
|
1296
|
-
const response = await fetch(source
|
|
1296
|
+
const response = await fetch(source, {
|
|
1297
|
+
cache: "no-store"
|
|
1298
|
+
});
|
|
1297
1299
|
if (!response.ok) throw new Error(`Failed to load patch from ${source}: ${response.status}`);
|
|
1298
1300
|
const data = await response.json();
|
|
1299
1301
|
return createPatchInstance(data);
|
package/dist/react.js
CHANGED
|
@@ -1149,7 +1149,9 @@ function createPatchInstance(data) {
|
|
|
1149
1149
|
* @throws {Error} If the network request fails
|
|
1150
1150
|
*/ async function loadPatch(source) {
|
|
1151
1151
|
if (typeof source === "string") {
|
|
1152
|
-
const response = await fetch(source
|
|
1152
|
+
const response = await fetch(source, {
|
|
1153
|
+
cache: "no-store"
|
|
1154
|
+
});
|
|
1153
1155
|
if (!response.ok) throw new Error(`Failed to load patch from ${source}: ${response.status}`);
|
|
1154
1156
|
const data = await response.json();
|
|
1155
1157
|
return createPatchInstance(data);
|