accelr8 3.8.1 → 3.8.2
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/chunks/{app-7YGYMFSZ.js → app-SYLCS3QX.js} +123 -63
- package/dist/chunks/{chunk-UTWXIX5N.js → chunk-7JSMON7M.js} +1 -1
- package/dist/chunks/{client-3RLQY3ZB.js → client-BKQLCKVT.js} +1 -1
- package/dist/chunks/{server-V56IHFUH.js → server-IRLRECZ7.js} +1 -1
- package/dist/cli.js +4 -4
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
DEFAULT_SERVER,
|
|
9
9
|
VERSION
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-7JSMON7M.js";
|
|
11
11
|
|
|
12
12
|
// src/radio/app.ts
|
|
13
13
|
import tk from "terminal-kit";
|
|
@@ -318,7 +318,7 @@ var matrix = {
|
|
|
318
318
|
frame(tMs, w, h) {
|
|
319
319
|
const out = new Array(w * h);
|
|
320
320
|
const t = tMs / 1e3;
|
|
321
|
-
const
|
|
321
|
+
const CH2 = "01<>+*=~^:;|/\\ACELR8#$%&?!";
|
|
322
322
|
for (let x = 0; x < w; x++) {
|
|
323
323
|
let h0 = 0, l0 = 1, h1 = 0, l1 = 1;
|
|
324
324
|
for (let d = 0; d < 2; d++) {
|
|
@@ -345,7 +345,7 @@ var matrix = {
|
|
|
345
345
|
continue;
|
|
346
346
|
}
|
|
347
347
|
const rate = 1.5 + 4 * hash2(x, y);
|
|
348
|
-
const ch =
|
|
348
|
+
const ch = CH2[Math.floor(hashU(x * 31 + y * 57 + Math.floor(t * rate) * 7919) * CH2.length)];
|
|
349
349
|
const isHead = q0 > 0 && d0 < 1 || q1 > 0 && d1 < 1;
|
|
350
350
|
out[y * w + x] = isHead ? { ch, color: col(200 + 55 * q, 252, 252) } : { ch, color: col(18 + 40 * q, 55 + 155 * q, 60 + 195 * q) };
|
|
351
351
|
}
|
|
@@ -5345,123 +5345,183 @@ function renderOverlay(ctx, opts) {
|
|
|
5345
5345
|
}
|
|
5346
5346
|
|
|
5347
5347
|
// src/radio/audio.ts
|
|
5348
|
-
import { spawn as spawn2 } from "node:child_process";
|
|
5348
|
+
import { spawn as spawn2, spawnSync } from "node:child_process";
|
|
5349
|
+
var SR = 44100;
|
|
5350
|
+
var CH = 2;
|
|
5349
5351
|
function nextAudioAction(sched, serverNowMs, mem, driftBudgetMs = 1500) {
|
|
5350
5352
|
const pos = nowPlaying(sched, serverNowMs);
|
|
5351
5353
|
if (!pos || !pos.item.url) return { kind: "none" };
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5354
|
+
if (pos.item.id !== mem.playingTrackId || !mem.procAlive) return { kind: "play", track: pos.item, offsetMs: pos.offsetMs };
|
|
5355
|
+
const implied = mem.spawnedOffsetMs + (serverNowMs - mem.spawnedAtServerMs);
|
|
5356
|
+
if (Math.abs(implied - pos.offsetMs) > driftBudgetMs) return { kind: "play", track: pos.item, offsetMs: pos.offsetMs };
|
|
5357
|
+
return { kind: "none" };
|
|
5358
|
+
}
|
|
5359
|
+
function applyGain(buf, fromGain, target, rampPerSample = 1 / 1400) {
|
|
5360
|
+
let g = fromGain;
|
|
5361
|
+
for (let i = 0; i + 1 < buf.length; i += 2) {
|
|
5362
|
+
if (g < target) g = g + rampPerSample > target ? target : g + rampPerSample;
|
|
5363
|
+
else if (g > target) g = g - rampPerSample < target ? target : g - rampPerSample;
|
|
5364
|
+
let s = Math.round(buf.readInt16LE(i) * g);
|
|
5365
|
+
if (s > 32767) s = 32767;
|
|
5366
|
+
else if (s < -32768) s = -32768;
|
|
5367
|
+
buf.writeInt16LE(s, i);
|
|
5355
5368
|
}
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5369
|
+
return g;
|
|
5370
|
+
}
|
|
5371
|
+
function decoderArgs(url, offsetMs) {
|
|
5372
|
+
const args = ["-hide_banner", "-loglevel", "error", "-re"];
|
|
5373
|
+
if (offsetMs > 0) args.push("-ss", (offsetMs / 1e3).toFixed(3));
|
|
5374
|
+
args.push("-i", url, "-f", "s16le", "-ar", String(SR), "-ac", String(CH), "-");
|
|
5375
|
+
return args;
|
|
5376
|
+
}
|
|
5377
|
+
var chFlagCache = null;
|
|
5378
|
+
function ffplayStereoFlag(ffplayPath) {
|
|
5379
|
+
if (chFlagCache) return chFlagCache;
|
|
5380
|
+
let modern = true;
|
|
5381
|
+
try {
|
|
5382
|
+
const out = spawnSync(ffplayPath, ["-hide_banner", "-version"], { encoding: "utf8", timeout: 3e3 });
|
|
5383
|
+
const m = ((out.stdout || "") + (out.stderr || "")).match(/version\s+n?(\d+)\.(\d+)/i);
|
|
5384
|
+
if (m) {
|
|
5385
|
+
const major = +m[1], minor = +m[2];
|
|
5386
|
+
modern = major > 5 || major === 5 && minor >= 1;
|
|
5387
|
+
}
|
|
5388
|
+
} catch {
|
|
5359
5389
|
}
|
|
5360
|
-
|
|
5390
|
+
chFlagCache = modern ? ["-ch_layout", "stereo"] : ["-ac", String(CH)];
|
|
5391
|
+
return chFlagCache;
|
|
5361
5392
|
}
|
|
5362
|
-
function
|
|
5363
|
-
|
|
5393
|
+
function sinkArgs(ffplayPath) {
|
|
5394
|
+
return [
|
|
5364
5395
|
"-hide_banner",
|
|
5365
5396
|
"-loglevel",
|
|
5366
5397
|
"error",
|
|
5367
5398
|
"-nodisp",
|
|
5368
|
-
"-
|
|
5369
|
-
"
|
|
5370
|
-
|
|
5399
|
+
"-sync",
|
|
5400
|
+
"audio",
|
|
5401
|
+
"-infbuf",
|
|
5402
|
+
"-f",
|
|
5403
|
+
"s16le",
|
|
5404
|
+
"-ar",
|
|
5405
|
+
String(SR),
|
|
5406
|
+
...ffplayStereoFlag(ffplayPath),
|
|
5407
|
+
"-i",
|
|
5408
|
+
"-"
|
|
5371
5409
|
];
|
|
5372
|
-
if (offsetMs > 0) args.push("-ss", (offsetMs / 1e3).toFixed(3));
|
|
5373
|
-
args.push("-i", url);
|
|
5374
|
-
return args;
|
|
5375
5410
|
}
|
|
5376
5411
|
var SyncedAudio = class {
|
|
5377
|
-
|
|
5412
|
+
sink = null;
|
|
5413
|
+
decoder = null;
|
|
5378
5414
|
mem = { playingTrackId: null, spawnedAtServerMs: 0, spawnedOffsetMs: 0, procAlive: false };
|
|
5379
5415
|
opts = null;
|
|
5380
5416
|
timer = null;
|
|
5381
|
-
|
|
5382
|
-
//
|
|
5417
|
+
gain = 0.7;
|
|
5418
|
+
// current (ramping) gain
|
|
5419
|
+
target = 0.7;
|
|
5420
|
+
// desired volume (0..1)
|
|
5383
5421
|
muted = false;
|
|
5384
5422
|
start(opts) {
|
|
5385
5423
|
this.stop();
|
|
5386
5424
|
this.opts = opts;
|
|
5425
|
+
this.startSink();
|
|
5387
5426
|
this.tick();
|
|
5388
5427
|
this.timer = setInterval(() => this.tick(), 1e3);
|
|
5389
5428
|
}
|
|
5390
|
-
/** Swap in a new schedule (worker pushed a new playlist) without a restart. */
|
|
5391
5429
|
setSchedule(sched) {
|
|
5392
5430
|
if (this.opts) this.opts.schedule = sched;
|
|
5393
5431
|
}
|
|
5394
5432
|
get volume() {
|
|
5395
|
-
return this.
|
|
5433
|
+
return Math.round(this.target * 100);
|
|
5396
5434
|
}
|
|
5397
5435
|
get isMuted() {
|
|
5398
5436
|
return this.muted;
|
|
5399
5437
|
}
|
|
5400
|
-
|
|
5401
|
-
|
|
5438
|
+
get nowTrackId() {
|
|
5439
|
+
return this.mem.playingTrackId;
|
|
5440
|
+
}
|
|
5441
|
+
/** Smooth: just move the target; the per-chunk gain ramp glides to it. */
|
|
5402
5442
|
setVolume(v) {
|
|
5403
|
-
this.
|
|
5404
|
-
|
|
5443
|
+
this.target = Math.max(0, Math.min(100, Math.round(v))) / 100;
|
|
5444
|
+
this.muted = false;
|
|
5405
5445
|
}
|
|
5406
|
-
/** Toggle mute. Muting stops the process (no CPU); unmuting resyncs + resumes. */
|
|
5407
5446
|
toggleMute() {
|
|
5408
5447
|
this.muted = !this.muted;
|
|
5409
|
-
if (this.muted) {
|
|
5410
|
-
this.killProc();
|
|
5411
|
-
this.mem.playingTrackId = null;
|
|
5412
|
-
this.mem.procAlive = false;
|
|
5413
|
-
} else this.tick();
|
|
5414
5448
|
return this.muted;
|
|
5415
5449
|
}
|
|
5416
|
-
|
|
5450
|
+
// gain ramps to 0 / back, no cut
|
|
5451
|
+
effTarget() {
|
|
5452
|
+
return this.muted ? 0 : this.target;
|
|
5453
|
+
}
|
|
5454
|
+
startSink() {
|
|
5417
5455
|
if (!this.opts) return;
|
|
5418
|
-
const
|
|
5419
|
-
|
|
5456
|
+
const sink = spawn2(this.opts.ffplayPath, sinkArgs(this.opts.ffplayPath), { stdio: ["pipe", "ignore", "pipe"] });
|
|
5457
|
+
this.sink = sink;
|
|
5458
|
+
sink.stdin?.on("error", () => {
|
|
5459
|
+
});
|
|
5460
|
+
sink.stdin?.on("drain", () => this.decoder?.stdout?.resume());
|
|
5461
|
+
let err = "";
|
|
5462
|
+
sink.stderr?.on("data", (d) => {
|
|
5463
|
+
err = (err + d.toString()).slice(-200);
|
|
5464
|
+
});
|
|
5465
|
+
sink.on("exit", () => {
|
|
5466
|
+
if (this.sink !== sink) return;
|
|
5467
|
+
this.sink = null;
|
|
5468
|
+
if (err) this.opts?.onError?.(err.split("\n").pop() || err);
|
|
5469
|
+
if (this.opts) {
|
|
5470
|
+
this.startSink();
|
|
5471
|
+
this.mem.procAlive = false;
|
|
5472
|
+
}
|
|
5473
|
+
});
|
|
5420
5474
|
}
|
|
5421
5475
|
tick() {
|
|
5422
|
-
if (!this.opts
|
|
5476
|
+
if (!this.opts) return;
|
|
5477
|
+
if (!this.sink) this.startSink();
|
|
5423
5478
|
const action = nextAudioAction(this.opts.schedule, this.opts.serverNow(), this.mem);
|
|
5424
|
-
if (action.kind === "play") this.
|
|
5479
|
+
if (action.kind === "play") this.spawnDecoder(action.track, action.offsetMs);
|
|
5425
5480
|
}
|
|
5426
|
-
|
|
5481
|
+
spawnDecoder(track, offsetMs) {
|
|
5427
5482
|
if (!this.opts) return;
|
|
5428
|
-
this.
|
|
5429
|
-
const
|
|
5430
|
-
this.
|
|
5483
|
+
this.killDecoder();
|
|
5484
|
+
const dec = spawn2(this.opts.ffmpegPath, decoderArgs(track.url, offsetMs), { stdio: ["ignore", "pipe", "ignore"] });
|
|
5485
|
+
this.decoder = dec;
|
|
5431
5486
|
this.mem = { playingTrackId: track.id, spawnedAtServerMs: this.opts.serverNow(), spawnedOffsetMs: offsetMs, procAlive: true };
|
|
5432
5487
|
this.opts.onTrack?.(track);
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5488
|
+
dec.stdout?.on("data", (chunk) => {
|
|
5489
|
+
if (this.decoder !== dec) return;
|
|
5490
|
+
this.gain = applyGain(chunk, this.gain, this.effTarget());
|
|
5491
|
+
const sink = this.sink;
|
|
5492
|
+
if (!sink || !sink.stdin || sink.stdin.destroyed) return;
|
|
5493
|
+
const ok = sink.stdin.write(chunk);
|
|
5494
|
+
if (ok === false) dec.stdout?.pause();
|
|
5436
5495
|
});
|
|
5437
|
-
|
|
5438
|
-
this.mem.procAlive = false;
|
|
5439
|
-
this.opts?.onError?.(e.message);
|
|
5496
|
+
dec.on("error", () => {
|
|
5497
|
+
if (this.decoder === dec) this.mem.procAlive = false;
|
|
5440
5498
|
});
|
|
5441
|
-
|
|
5442
|
-
if (this.
|
|
5443
|
-
this.mem.procAlive = false;
|
|
5444
|
-
if (err) this.opts?.onError?.(err.split("\n").pop() || err);
|
|
5499
|
+
dec.on("exit", () => {
|
|
5500
|
+
if (this.decoder === dec) this.mem.procAlive = false;
|
|
5445
5501
|
});
|
|
5446
5502
|
}
|
|
5447
|
-
|
|
5448
|
-
if (this.
|
|
5503
|
+
killDecoder() {
|
|
5504
|
+
if (this.decoder) {
|
|
5449
5505
|
try {
|
|
5450
|
-
this.
|
|
5506
|
+
this.decoder.kill("SIGKILL");
|
|
5451
5507
|
} catch {
|
|
5452
5508
|
}
|
|
5453
|
-
this.
|
|
5509
|
+
this.decoder = null;
|
|
5454
5510
|
}
|
|
5455
5511
|
}
|
|
5456
|
-
get nowTrackId() {
|
|
5457
|
-
return this.mem.playingTrackId;
|
|
5458
|
-
}
|
|
5459
5512
|
stop() {
|
|
5460
5513
|
if (this.timer) {
|
|
5461
5514
|
clearInterval(this.timer);
|
|
5462
5515
|
this.timer = null;
|
|
5463
5516
|
}
|
|
5464
|
-
this.
|
|
5517
|
+
this.killDecoder();
|
|
5518
|
+
if (this.sink) {
|
|
5519
|
+
try {
|
|
5520
|
+
this.sink.kill("SIGKILL");
|
|
5521
|
+
} catch {
|
|
5522
|
+
}
|
|
5523
|
+
this.sink = null;
|
|
5524
|
+
}
|
|
5465
5525
|
this.mem = { playingTrackId: null, spawnedAtServerMs: 0, spawnedOffsetMs: 0, procAlive: false };
|
|
5466
5526
|
}
|
|
5467
5527
|
};
|
|
@@ -5606,9 +5666,9 @@ function startBackground(opts) {
|
|
|
5606
5666
|
} else {
|
|
5607
5667
|
visuals = new Showrunner(ALL_SCENES);
|
|
5608
5668
|
}
|
|
5609
|
-
if (tools.ffplayPath) {
|
|
5669
|
+
if (tools.ffmpegPath && tools.ffplayPath) {
|
|
5610
5670
|
audio = new SyncedAudio();
|
|
5611
|
-
audio.start({ ffplayPath: tools.ffplayPath, schedule: state.schedule, serverNow });
|
|
5671
|
+
audio.start({ ffplayPath: tools.ffplayPath, ffmpegPath: tools.ffmpegPath, schedule: state.schedule, serverNow });
|
|
5612
5672
|
}
|
|
5613
5673
|
}
|
|
5614
5674
|
function makeScreen() {
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
DEFAULT_SERVER,
|
|
4
4
|
PORT,
|
|
5
5
|
VERSION
|
|
6
|
-
} from "./chunks/chunk-
|
|
6
|
+
} from "./chunks/chunk-7JSMON7M.js";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
9
|
var HELP = `
|
|
@@ -53,7 +53,7 @@ async function main() {
|
|
|
53
53
|
console.error(`invalid port: ${argVal(args, "--port")}`);
|
|
54
54
|
process.exit(1);
|
|
55
55
|
}
|
|
56
|
-
const { runServer } = await import("./chunks/server-
|
|
56
|
+
const { runServer } = await import("./chunks/server-IRLRECZ7.js");
|
|
57
57
|
runServer(port);
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
@@ -67,7 +67,7 @@ async function main() {
|
|
|
67
67
|
console.error("accelr8 radio needs an interactive terminal (TTY).");
|
|
68
68
|
process.exit(1);
|
|
69
69
|
}
|
|
70
|
-
const { runRadio } = await import("./chunks/app-
|
|
70
|
+
const { runRadio } = await import("./chunks/app-SYLCS3QX.js");
|
|
71
71
|
runRadio({
|
|
72
72
|
name: argVal(args, "--name"),
|
|
73
73
|
server: argVal(args, "--server"),
|
|
@@ -90,7 +90,7 @@ async function main() {
|
|
|
90
90
|
const { cachedUpdate, refreshUpdateCache } = await import("./chunks/update-UEPNGB6Y.js");
|
|
91
91
|
refreshUpdateCache();
|
|
92
92
|
const update = cachedUpdate(VERSION);
|
|
93
|
-
const { runClient } = await import("./chunks/client-
|
|
93
|
+
const { runClient } = await import("./chunks/client-BKQLCKVT.js");
|
|
94
94
|
runClient({
|
|
95
95
|
name: argVal(args, "--name"),
|
|
96
96
|
server: argVal(args, "--server"),
|