zx-kit 0.36.0 → 0.37.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.
- package/dist/aydump.d.ts +297 -0
- package/dist/aydump.d.ts.map +1 -0
- package/dist/aydump.js +656 -0
- package/dist/aydump.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/aydump.d.ts
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module aydump
|
|
3
|
+
*
|
|
4
|
+
* **Sample-accurate AY register-dump player** — the "sample-accurate
|
|
5
|
+
* AudioWorklet backend" that {@link "ay" | ay.ts} promises on its roadmap.
|
|
6
|
+
* Where `ay.ts` *synthesises* notes (`AYNote[]` → band-limited oscillators),
|
|
7
|
+
* this module *reproduces the hardware*: it feeds a cycle-level emulation of
|
|
8
|
+
* the AY-3-8910 / YM2149 chip a stream of register writes (a **PSG dump**) and
|
|
9
|
+
* renders real PCM — so you can play **actual ZX-scene tunes** in a game.
|
|
10
|
+
*
|
|
11
|
+
* The pipeline is data-driven, exactly like a bitmap font:
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* .psg file (register dump, bundled as data)
|
|
15
|
+
* │ parsePSG()
|
|
16
|
+
* ▼
|
|
17
|
+
* AYDump (flat typed-array frames: who-writes-what-when)
|
|
18
|
+
* │ AYDumpPlayer (applies each frame's writes every sampleRate/50 samples)
|
|
19
|
+
* ▼
|
|
20
|
+
* AYChipCore (16 registers → L/R PCM, HW logic from the datasheet)
|
|
21
|
+
* │ playAYDump() (realtime AudioWorklet) or renderAYDump() (offline)
|
|
22
|
+
* ▼
|
|
23
|
+
* master GainNode (shared with audio.ts / ay.ts — global M mute works for free)
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* **Scope:** v1 is the **PSG** format (a raw register dump — HW-level, unambiguous,
|
|
27
|
+
* dependency-free). Anything from zxart.ee (PT3, STC…) is converted to `.psg`
|
|
28
|
+
* **offline** on a PC (Ay_Emul "save as PSG", `zxtune123 --convert`, Vortex
|
|
29
|
+
* Tracker II). The dump is *data*, not a code dependency — Zero Dependencies stays
|
|
30
|
+
* clean. A native PT3 replay routine over the same {@link AYChipCore} is a future
|
|
31
|
+
* (v2) module.
|
|
32
|
+
*
|
|
33
|
+
* **Authorship:** a PSG dump is a derivative of the original tune. zxart hosting is
|
|
34
|
+
* not a licence — ship a track only with the composer's permission (credit + link).
|
|
35
|
+
*
|
|
36
|
+
* @see {@link "ay" | ay.ts} for note-based music, {@link "audio" | audio.ts} for the beeper.
|
|
37
|
+
*/
|
|
38
|
+
import type { AYStereoMode } from './ay.js';
|
|
39
|
+
/** Chip variant: `ay` = 16-step envelope; `ym` = 32-step envelope (finer). */
|
|
40
|
+
export type AYChipVariant = 'ay' | 'ym';
|
|
41
|
+
/** How to configure the emulated chip. All fields optional — see defaults below. */
|
|
42
|
+
export interface AYChipConfig {
|
|
43
|
+
/** Chip clock in Hz. Default `1_773_400` (ZX 128K). Melodik = `1_750_000`. */
|
|
44
|
+
clockHz?: number;
|
|
45
|
+
/** Chip variant. Default `'ay'`. */
|
|
46
|
+
variant?: AYChipVariant;
|
|
47
|
+
/** Stereo preset. Default `'acb'` (Melodik jack: A left, C centre, B right). */
|
|
48
|
+
stereo?: AYStereoMode;
|
|
49
|
+
/** 16-entry DAC table (level → amplitude). Default zx-kit `AY_VOL`; hook for measured tables. */
|
|
50
|
+
dacTable?: readonly number[];
|
|
51
|
+
}
|
|
52
|
+
/** Ready-made presets for real machines. */
|
|
53
|
+
export declare const AY_MACHINE: {
|
|
54
|
+
/** ZX Spectrum 128K / +2 / +3 — TV speaker, mono. */
|
|
55
|
+
readonly zx128: {
|
|
56
|
+
readonly clockHz: 1773400;
|
|
57
|
+
readonly variant: "ay";
|
|
58
|
+
readonly stereo: "mono";
|
|
59
|
+
};
|
|
60
|
+
/** Didaktik + Melodik add-on — 1.75 MHz, ACB stereo jack. */
|
|
61
|
+
readonly melodik: {
|
|
62
|
+
readonly clockHz: 1750000;
|
|
63
|
+
readonly variant: "ay";
|
|
64
|
+
readonly stereo: "acb";
|
|
65
|
+
};
|
|
66
|
+
/** Pentagon clone — 1.75 MHz, ACB. */
|
|
67
|
+
readonly pentagon: {
|
|
68
|
+
readonly clockHz: 1750000;
|
|
69
|
+
readonly variant: "ay";
|
|
70
|
+
readonly stereo: "acb";
|
|
71
|
+
};
|
|
72
|
+
/** Atari ST — YM2149 at 2 MHz, mono. */
|
|
73
|
+
readonly atariST: {
|
|
74
|
+
readonly clockHz: 2000000;
|
|
75
|
+
readonly variant: "ym";
|
|
76
|
+
readonly stereo: "mono";
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Cycle-level AY-3-8910 / YM2149 emulator: register writes in, PCM samples out.
|
|
81
|
+
* Three square-wave tone channels, a shared 17-bit LFSR noise generator, and the
|
|
82
|
+
* hardware envelope generator (16 or 32 steps). Pure and self-contained so it runs
|
|
83
|
+
* inside an AudioWorklet **and** in a headless test with no `AudioContext`.
|
|
84
|
+
*
|
|
85
|
+
* Named `AYChipCore` (not `AYChip`) so it does not collide with the real-time
|
|
86
|
+
* {@link "ay".AYChip} handle interface exported from `ay.ts`.
|
|
87
|
+
*/
|
|
88
|
+
export declare class AYChipCore {
|
|
89
|
+
private readonly envSteps;
|
|
90
|
+
private readonly dac;
|
|
91
|
+
private readonly regs;
|
|
92
|
+
private readonly toneCnt;
|
|
93
|
+
private readonly toneOut;
|
|
94
|
+
private noiseCnt;
|
|
95
|
+
private lfsr;
|
|
96
|
+
private noiseOut;
|
|
97
|
+
private envCnt;
|
|
98
|
+
private envPos;
|
|
99
|
+
private envAttack;
|
|
100
|
+
private envHolding;
|
|
101
|
+
private envCont;
|
|
102
|
+
private envAlt;
|
|
103
|
+
private envHold;
|
|
104
|
+
private cycleAcc;
|
|
105
|
+
private readonly cyclesPerSample;
|
|
106
|
+
private panL;
|
|
107
|
+
private panR;
|
|
108
|
+
private dcxL;
|
|
109
|
+
private dcyL;
|
|
110
|
+
private dcxR;
|
|
111
|
+
private dcyR;
|
|
112
|
+
/**
|
|
113
|
+
* @param sampleRate Output sample rate (Hz).
|
|
114
|
+
* @param clockHz Chip master clock (Hz).
|
|
115
|
+
* @param envSteps Envelope resolution: 16 (AY) or 32 (YM).
|
|
116
|
+
* @param dac 16-entry DAC amplitude table (level → 0..1).
|
|
117
|
+
* @param stereo Stereo preset (mono / abc / acb).
|
|
118
|
+
*/
|
|
119
|
+
constructor(sampleRate: number, clockHz: number, envSteps: number, dac: readonly number[], stereo: AYStereoMode);
|
|
120
|
+
/** Clears counters, registers, LFSR, envelope and DC state. Call at start / seek. */
|
|
121
|
+
reset(): void;
|
|
122
|
+
/**
|
|
123
|
+
* Applies a demoscene stereo preset. Soft panning (0.75/0.25), so headphones
|
|
124
|
+
* don't get a hard 1/0 split; L+R gain per channel always sums to 1.
|
|
125
|
+
*/
|
|
126
|
+
setStereo(mode: AYStereoMode): void;
|
|
127
|
+
/**
|
|
128
|
+
* Writes a chip register (0..15), masking to the hardware register width.
|
|
129
|
+
* Writing R13 (envelope shape) always restarts the envelope.
|
|
130
|
+
*/
|
|
131
|
+
setRegister(r: number, v: number): void;
|
|
132
|
+
/** Steps the 17-bit LFSR once (feedback = bit0 XOR bit3) and latches the output bit. */
|
|
133
|
+
private stepNoise;
|
|
134
|
+
/** Advances the envelope generator by one step (called when its counter elapses). */
|
|
135
|
+
private envStep;
|
|
136
|
+
/** Current envelope amplitude as a DAC index (0..15). */
|
|
137
|
+
private envLevel;
|
|
138
|
+
/**
|
|
139
|
+
* Renders `count` stereo samples into `left`/`right` starting at `offset`.
|
|
140
|
+
* Box-filters the chip's raw ticks within each output sample (a cheap
|
|
141
|
+
* anti-alias), then DC-blocks each output channel.
|
|
142
|
+
*/
|
|
143
|
+
renderInto(left: Float32Array, right: Float32Array, offset: number, count: number): void;
|
|
144
|
+
/**
|
|
145
|
+
* @internal Test hook — steps the LFSR `n` times, returning each output bit.
|
|
146
|
+
* Locks the noise sequence against a tap/shift typo (see stepNoise).
|
|
147
|
+
*/
|
|
148
|
+
_stepNoiseForTest(n: number): number[];
|
|
149
|
+
/**
|
|
150
|
+
* @internal Test hook — advances the envelope generator `n` times (after an R13
|
|
151
|
+
* write), returning the amplitude level (0..15) after each step.
|
|
152
|
+
*/
|
|
153
|
+
_stepEnvelopeForTest(n: number): number[];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* A parsed PSG register dump — flat typed arrays so it transfers into the
|
|
157
|
+
* AudioWorklet with zero copying and allocates nothing on the audio thread.
|
|
158
|
+
* Frame `f`'s writes are `writeRegs[frameOffsets[f] .. frameOffsets[f+1])`.
|
|
159
|
+
*/
|
|
160
|
+
export interface AYDump {
|
|
161
|
+
/** Frame rate (PSG = 50 Hz). */
|
|
162
|
+
frameRateHz: number;
|
|
163
|
+
/** Number of frames. */
|
|
164
|
+
frameCount: number;
|
|
165
|
+
/** Register index (0..15) per write, all frames concatenated. */
|
|
166
|
+
writeRegs: Uint8Array;
|
|
167
|
+
/** Value per write, parallel to {@link writeRegs}. */
|
|
168
|
+
writeVals: Uint8Array;
|
|
169
|
+
/** Length `frameCount + 1`; last element = `writeRegs.length`. */
|
|
170
|
+
frameOffsets: Uint32Array;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Parses a PSG register dump (the format Ay_Emul / zxtune produce) into an
|
|
174
|
+
* {@link AYDump}. A PSG file is a `"PSG\x1a"` header then a byte stream:
|
|
175
|
+
*
|
|
176
|
+
* - `0xFF` — start a new frame
|
|
177
|
+
* - `0xFE n` — `n × 4` empty frames (chip holds its state)
|
|
178
|
+
* - `0xFD` — end of song (often absent: end-of-data ends the song)
|
|
179
|
+
* - `0x00..0x0F` — register index; the next byte is its value
|
|
180
|
+
* - `0x10..0xFC` — foreign device (MSX / 2nd chip) → skip this byte and the next
|
|
181
|
+
*
|
|
182
|
+
* @throws if the header is wrong or a register/`0xFE` pair is truncated.
|
|
183
|
+
*/
|
|
184
|
+
export declare function parsePSG(bytes: Uint8Array): AYDump;
|
|
185
|
+
/**
|
|
186
|
+
* Drives an {@link AYChipCore} from an {@link AYDump}: applies each frame's
|
|
187
|
+
* register writes on the right sample and fills PCM buffers. Uses a *fractional*
|
|
188
|
+
* samples-per-frame accumulator so a 3-minute tune doesn't drift at exotic rates.
|
|
189
|
+
* Pure — testable with a spy chip, runnable inside the worklet.
|
|
190
|
+
*/
|
|
191
|
+
export declare class AYDumpPlayer {
|
|
192
|
+
private readonly chip;
|
|
193
|
+
private readonly writeRegs;
|
|
194
|
+
private readonly writeVals;
|
|
195
|
+
private readonly frameOffsets;
|
|
196
|
+
private readonly loop;
|
|
197
|
+
private readonly loopFrame;
|
|
198
|
+
private readonly frameCount;
|
|
199
|
+
private readonly samplesPerFrame;
|
|
200
|
+
private frame;
|
|
201
|
+
private frameAcc;
|
|
202
|
+
private ended;
|
|
203
|
+
/**
|
|
204
|
+
* @param chip The chip to drive.
|
|
205
|
+
* @param writeRegs Concatenated register indices (see {@link AYDump}).
|
|
206
|
+
* @param writeVals Concatenated values.
|
|
207
|
+
* @param frameOffsets Frame boundaries (length `frameCount + 1`).
|
|
208
|
+
* @param frameRateHz Frames per second (PSG = 50).
|
|
209
|
+
* @param sampleRate Output sample rate (Hz).
|
|
210
|
+
* @param loop Restart at `loopFrame` after the last frame.
|
|
211
|
+
* @param loopFrame Frame to jump to when looping.
|
|
212
|
+
*/
|
|
213
|
+
constructor(chip: AYChipCore, writeRegs: Uint8Array, writeVals: Uint8Array, frameOffsets: Uint32Array, frameRateHz: number, sampleRate: number, loop: boolean, loopFrame: number);
|
|
214
|
+
private applyFrame;
|
|
215
|
+
/** Fills `left`/`right` completely. Returns `true` once the (non-looping) song has ended. */
|
|
216
|
+
render(left: Float32Array, right: Float32Array): boolean;
|
|
217
|
+
/** Fast-forwards to frame `f` by replaying its register writes without rendering. */
|
|
218
|
+
seekToFrame(f: number): void;
|
|
219
|
+
}
|
|
220
|
+
/** Live handle to a {@link playAYDump} call. */
|
|
221
|
+
export interface AYDumpHandle {
|
|
222
|
+
/** Detach the node and stop playback (irreversible). */
|
|
223
|
+
stop(): void;
|
|
224
|
+
/** Silence via the gain node (the worklet keeps running) — instant. */
|
|
225
|
+
pause(): void;
|
|
226
|
+
/** Restore the pre-pause volume. */
|
|
227
|
+
resume(): void;
|
|
228
|
+
/** Per-track volume, 0..1 (master volume stays with audio.ts). */
|
|
229
|
+
setVolume(v: number): void;
|
|
230
|
+
/** Change the stereo preset live. */
|
|
231
|
+
setStereo(mode: AYStereoMode): void;
|
|
232
|
+
/** `true` while the node is connected and not stopped. */
|
|
233
|
+
readonly playing: boolean;
|
|
234
|
+
/** Called once when a non-looping song finishes. */
|
|
235
|
+
onEnded?: () => void;
|
|
236
|
+
}
|
|
237
|
+
/** Options for {@link playAYDump}. */
|
|
238
|
+
export interface PlayAYDumpOptions extends AYChipConfig {
|
|
239
|
+
/** Loop at `loopFrame` after the last frame. Default `false`. */
|
|
240
|
+
loop?: boolean;
|
|
241
|
+
/** Frame to loop back to. Default `0`. */
|
|
242
|
+
loopFrame?: number;
|
|
243
|
+
/** Initial per-track volume 0..1. Default `1`. */
|
|
244
|
+
volume?: number;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Assembles the AudioWorklet source: the two worklet-safe classes stringified,
|
|
248
|
+
* plus a processor that loads a dump and renders it.
|
|
249
|
+
*
|
|
250
|
+
* Each class is bound to a **fixed name we control** (`const __AYChipCore = …`).
|
|
251
|
+
* This is essential: a bundler (Vite/esbuild) commonly emits a class as an
|
|
252
|
+
* *anonymous* expression (`var AYChipCore = class {…}`), so its `.toString()`
|
|
253
|
+
* has no name. Injected as a bare top-level statement that would be
|
|
254
|
+
* `class {…}` → **SyntaxError: class statement requires a name**. Assigning it to
|
|
255
|
+
* a `const` makes it a valid class expression whatever form `toString()` takes,
|
|
256
|
+
* and neither class references its own name internally, so renaming is safe.
|
|
257
|
+
*
|
|
258
|
+
* Internal; exported only so a test can assemble and evaluate the source.
|
|
259
|
+
*/
|
|
260
|
+
export declare function _buildAYDumpWorkletSource(): string;
|
|
261
|
+
/**
|
|
262
|
+
* Plays a parsed {@link AYDump} in real time through an AudioWorklet, routed to the
|
|
263
|
+
* shared master gain (so global `M` mute / `setMasterVolume` apply for free).
|
|
264
|
+
* Call after {@link initAudio} inside a user gesture — same contract as `playAY`.
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* const dump = await loadPSG('./music/tune.psg')
|
|
268
|
+
* const track = await playAYDump(dump, { loop: true, ...AY_MACHINE.melodik })
|
|
269
|
+
* // later: track.setVolume(0.6); track.stop()
|
|
270
|
+
*/
|
|
271
|
+
export declare function playAYDump(dump: AYDump, opts?: PlayAYDumpOptions): Promise<AYDumpHandle>;
|
|
272
|
+
/** Options for {@link renderAYDump}. */
|
|
273
|
+
export interface RenderAYDumpOptions extends AYChipConfig {
|
|
274
|
+
/** Output sample rate. Default `44100`. */
|
|
275
|
+
sampleRate?: number;
|
|
276
|
+
/** Cap the render length in seconds. Default = the whole song. */
|
|
277
|
+
maxSeconds?: number;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Renders a dump to stereo PCM **without an AudioContext** — deterministic, for
|
|
281
|
+
* unit tests, `AudioWorklet`-less fallbacks, and offline tooling (e.g. PSG → WAV).
|
|
282
|
+
* Beware RAM: a full 3-minute stereo render at 44.1 kHz is ≈ 64 MB — pass
|
|
283
|
+
* `maxSeconds` to bound it.
|
|
284
|
+
*/
|
|
285
|
+
export declare function renderAYDump(dump: AYDump, opts?: RenderAYDumpOptions): {
|
|
286
|
+
left: Float32Array;
|
|
287
|
+
right: Float32Array;
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* Fetches a `.psg` file and parses it into an {@link AYDump}. Network / parse
|
|
291
|
+
* errors propagate so the game decides how to handle them.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* const dump = await loadPSG('./music/tune.psg')
|
|
295
|
+
*/
|
|
296
|
+
export declare function loadPSG(url: string): Promise<AYDump>;
|
|
297
|
+
//# sourceMappingURL=aydump.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aydump.d.ts","sourceRoot":"","sources":["../src/aydump.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAK3C,8EAA8E;AAC9E,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,IAAI,CAAA;AAEvC,oFAAoF;AACpF,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,oCAAoC;IACpC,OAAO,CAAC,EAAE,aAAa,CAAA;IACvB,gFAAgF;IAChF,MAAM,CAAC,EAAE,YAAY,CAAA;IACrB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC7B;AAED,4CAA4C;AAC5C,eAAO,MAAM,UAAU;IACrB,qDAAqD;;;;;;IAErD,6DAA6D;;;;;;IAE7D,sCAAsC;;;;;;IAEtC,wCAAwC;;;;;;CAEO,CAAA;AAUjD;;;;;;;;GAQG;AACH,qBAAa,UAAU;IA6CnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IA7CtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqB;IAG1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAY;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAY;IAGpC,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,QAAQ,CAAI;IAGpB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,OAAO,CAAI;IACnB,OAAO,CAAC,MAAM,CAAI;IAClB,OAAO,CAAC,OAAO,CAAI;IAGnB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IAGxC,OAAO,CAAC,IAAI,CAAkB;IAC9B,OAAO,CAAC,IAAI,CAAkB;IAG9B,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,IAAI,CAAI;IAChB,OAAO,CAAC,IAAI,CAAI;IAEhB;;;;;;OAMG;gBAED,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACE,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,SAAS,MAAM,EAAE,EACvC,MAAM,EAAE,YAAY;IAQtB,qFAAqF;IACrF,KAAK,IAAI,IAAI;IAgBb;;;OAGG;IACH,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAYnC;;;OAGG;IACH,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI;IAsBvC,wFAAwF;IACxF,OAAO,CAAC,SAAS;IAMjB,qFAAqF;IACrF,OAAO,CAAC,OAAO;IAmBf,yDAAyD;IACzD,OAAO,CAAC,QAAQ;IAKhB;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAyExF;;;OAGG;IACH,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAMtC;;;OAGG;IACH,oBAAoB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;CAK1C;AAID;;;;GAIG;AACH,MAAM,WAAW,MAAM;IACrB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,iEAAiE;IACjE,SAAS,EAAE,UAAU,CAAA;IACrB,sDAAsD;IACtD,SAAS,EAAE,UAAU,CAAA;IACrB,kEAAkE;IAClE,YAAY,EAAE,WAAW,CAAA;CAC1B;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CA+ClD;AAMD;;;;;GAKG;AACH,qBAAa,YAAY;IAkBrB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAG7B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAxB5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,KAAK,CAAI;IACjB,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,KAAK,CAAQ;IAErB;;;;;;;;;OASG;gBAEgB,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,UAAU,EACrB,SAAS,EAAE,UAAU,EACrB,YAAY,EAAE,WAAW,EAC1C,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EACD,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM;IAMpC,OAAO,CAAC,UAAU;IAQlB,6FAA6F;IAC7F,MAAM,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO;IA0BxD,qFAAqF;IACrF,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;CAQ7B;AA6BD,gDAAgD;AAChD,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,IAAI,IAAI,IAAI,CAAA;IACZ,uEAAuE;IACvE,KAAK,IAAI,IAAI,CAAA;IACb,oCAAoC;IACpC,MAAM,IAAI,IAAI,CAAA;IACd,kEAAkE;IAClE,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,qCAAqC;IACrC,SAAS,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;CACrB;AAED,sCAAsC;AACtC,MAAM,WAAW,iBAAkB,SAAQ,YAAY;IACrD,iEAAiE;IACjE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,CAiClD;AAiBD;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,iBAAsB,GAAG,OAAO,CAAC,YAAY,CAAC,CAoDlG;AAID,wCAAwC;AACxC,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,2CAA2C;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kEAAkE;IAClE,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,mBAAwB,GAC7B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAuB7C;AAED;;;;;;GAMG;AACH,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1D"}
|
package/dist/aydump.js
ADDED
|
@@ -0,0 +1,656 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module aydump
|
|
3
|
+
*
|
|
4
|
+
* **Sample-accurate AY register-dump player** — the "sample-accurate
|
|
5
|
+
* AudioWorklet backend" that {@link "ay" | ay.ts} promises on its roadmap.
|
|
6
|
+
* Where `ay.ts` *synthesises* notes (`AYNote[]` → band-limited oscillators),
|
|
7
|
+
* this module *reproduces the hardware*: it feeds a cycle-level emulation of
|
|
8
|
+
* the AY-3-8910 / YM2149 chip a stream of register writes (a **PSG dump**) and
|
|
9
|
+
* renders real PCM — so you can play **actual ZX-scene tunes** in a game.
|
|
10
|
+
*
|
|
11
|
+
* The pipeline is data-driven, exactly like a bitmap font:
|
|
12
|
+
*
|
|
13
|
+
* ```
|
|
14
|
+
* .psg file (register dump, bundled as data)
|
|
15
|
+
* │ parsePSG()
|
|
16
|
+
* ▼
|
|
17
|
+
* AYDump (flat typed-array frames: who-writes-what-when)
|
|
18
|
+
* │ AYDumpPlayer (applies each frame's writes every sampleRate/50 samples)
|
|
19
|
+
* ▼
|
|
20
|
+
* AYChipCore (16 registers → L/R PCM, HW logic from the datasheet)
|
|
21
|
+
* │ playAYDump() (realtime AudioWorklet) or renderAYDump() (offline)
|
|
22
|
+
* ▼
|
|
23
|
+
* master GainNode (shared with audio.ts / ay.ts — global M mute works for free)
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* **Scope:** v1 is the **PSG** format (a raw register dump — HW-level, unambiguous,
|
|
27
|
+
* dependency-free). Anything from zxart.ee (PT3, STC…) is converted to `.psg`
|
|
28
|
+
* **offline** on a PC (Ay_Emul "save as PSG", `zxtune123 --convert`, Vortex
|
|
29
|
+
* Tracker II). The dump is *data*, not a code dependency — Zero Dependencies stays
|
|
30
|
+
* clean. A native PT3 replay routine over the same {@link AYChipCore} is a future
|
|
31
|
+
* (v2) module.
|
|
32
|
+
*
|
|
33
|
+
* **Authorship:** a PSG dump is a derivative of the original tune. zxart hosting is
|
|
34
|
+
* not a licence — ship a track only with the composer's permission (credit + link).
|
|
35
|
+
*
|
|
36
|
+
* @see {@link "ay" | ay.ts} for note-based music, {@link "audio" | audio.ts} for the beeper.
|
|
37
|
+
*/
|
|
38
|
+
import { AY_CLOCK, AY_VOL } from './ay.js';
|
|
39
|
+
import { initAudio, getAudioContext, getMasterGain } from './audio.js';
|
|
40
|
+
/** Ready-made presets for real machines. */
|
|
41
|
+
export const AY_MACHINE = {
|
|
42
|
+
/** ZX Spectrum 128K / +2 / +3 — TV speaker, mono. */
|
|
43
|
+
zx128: { clockHz: 1_773_400, variant: 'ay', stereo: 'mono' },
|
|
44
|
+
/** Didaktik + Melodik add-on — 1.75 MHz, ACB stereo jack. */
|
|
45
|
+
melodik: { clockHz: 1_750_000, variant: 'ay', stereo: 'acb' },
|
|
46
|
+
/** Pentagon clone — 1.75 MHz, ACB. */
|
|
47
|
+
pentagon: { clockHz: 1_750_000, variant: 'ay', stereo: 'acb' },
|
|
48
|
+
/** Atari ST — YM2149 at 2 MHz, mono. */
|
|
49
|
+
atariST: { clockHz: 2_000_000, variant: 'ym', stereo: 'mono' },
|
|
50
|
+
};
|
|
51
|
+
// ─── AYChipCore — the chip emulator (§4.2) ───────────────────────────────────────
|
|
52
|
+
//
|
|
53
|
+
// WORKLET-SAFE INVARIANT: this class must not import or reference anything outside
|
|
54
|
+
// itself — its source is stringified via Function.prototype.toString() and injected
|
|
55
|
+
// into an AudioWorklet. It receives every constant (clock, DAC table, …) through the
|
|
56
|
+
// constructor. It uses only worklet globals: Math, Uint8Array, Array. Do not add a
|
|
57
|
+
// module-scope reference here or the worklet will break after minification.
|
|
58
|
+
/**
|
|
59
|
+
* Cycle-level AY-3-8910 / YM2149 emulator: register writes in, PCM samples out.
|
|
60
|
+
* Three square-wave tone channels, a shared 17-bit LFSR noise generator, and the
|
|
61
|
+
* hardware envelope generator (16 or 32 steps). Pure and self-contained so it runs
|
|
62
|
+
* inside an AudioWorklet **and** in a headless test with no `AudioContext`.
|
|
63
|
+
*
|
|
64
|
+
* Named `AYChipCore` (not `AYChip`) so it does not collide with the real-time
|
|
65
|
+
* {@link "ay".AYChip} handle interface exported from `ay.ts`.
|
|
66
|
+
*/
|
|
67
|
+
export class AYChipCore {
|
|
68
|
+
envSteps;
|
|
69
|
+
dac;
|
|
70
|
+
regs = new Uint8Array(16);
|
|
71
|
+
// Tone: per-channel down-counter + square phase.
|
|
72
|
+
toneCnt = [0, 0, 0];
|
|
73
|
+
toneOut = [0, 0, 0];
|
|
74
|
+
// Noise: 17-bit LFSR (seed 1) + counter + current output bit.
|
|
75
|
+
noiseCnt = 0;
|
|
76
|
+
lfsr = 1;
|
|
77
|
+
noiseOut = 1;
|
|
78
|
+
// Envelope generator state.
|
|
79
|
+
envCnt = 0;
|
|
80
|
+
envPos = 0;
|
|
81
|
+
envAttack = false;
|
|
82
|
+
envHolding = false;
|
|
83
|
+
envCont = 0;
|
|
84
|
+
envAlt = 0;
|
|
85
|
+
envHold = 0;
|
|
86
|
+
// Fractional accumulator: chip ticks (clock/8) per output sample.
|
|
87
|
+
cycleAcc = 0;
|
|
88
|
+
cyclesPerSample;
|
|
89
|
+
// Per-channel L/R mix gains (0..1), derived from the stereo preset.
|
|
90
|
+
panL = [0.5, 0.5, 0.5];
|
|
91
|
+
panR = [0.5, 0.5, 0.5];
|
|
92
|
+
// DC-blocker state per output channel (the chip output is unipolar).
|
|
93
|
+
dcxL = 0;
|
|
94
|
+
dcyL = 0;
|
|
95
|
+
dcxR = 0;
|
|
96
|
+
dcyR = 0;
|
|
97
|
+
/**
|
|
98
|
+
* @param sampleRate Output sample rate (Hz).
|
|
99
|
+
* @param clockHz Chip master clock (Hz).
|
|
100
|
+
* @param envSteps Envelope resolution: 16 (AY) or 32 (YM).
|
|
101
|
+
* @param dac 16-entry DAC amplitude table (level → 0..1).
|
|
102
|
+
* @param stereo Stereo preset (mono / abc / acb).
|
|
103
|
+
*/
|
|
104
|
+
constructor(sampleRate, clockHz, envSteps, dac, stereo) {
|
|
105
|
+
this.envSteps = envSteps;
|
|
106
|
+
this.dac = dac;
|
|
107
|
+
// Internal chip tick = 8 master clocks → tone f = clock / (16·TP).
|
|
108
|
+
this.cyclesPerSample = clockHz / 8 / sampleRate;
|
|
109
|
+
this.setStereo(stereo);
|
|
110
|
+
this.reset();
|
|
111
|
+
}
|
|
112
|
+
/** Clears counters, registers, LFSR, envelope and DC state. Call at start / seek. */
|
|
113
|
+
reset() {
|
|
114
|
+
this.regs.fill(0);
|
|
115
|
+
this.toneCnt[0] = this.toneCnt[1] = this.toneCnt[2] = 0;
|
|
116
|
+
this.toneOut[0] = this.toneOut[1] = this.toneOut[2] = 0;
|
|
117
|
+
this.noiseCnt = 0;
|
|
118
|
+
this.lfsr = 1;
|
|
119
|
+
this.noiseOut = 1;
|
|
120
|
+
this.envCnt = 0;
|
|
121
|
+
this.envPos = 0;
|
|
122
|
+
this.envAttack = false;
|
|
123
|
+
this.envHolding = false;
|
|
124
|
+
this.envCont = this.envAlt = this.envHold = 0;
|
|
125
|
+
this.cycleAcc = 0;
|
|
126
|
+
this.dcxL = this.dcyL = this.dcxR = this.dcyR = 0;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Applies a demoscene stereo preset. Soft panning (0.75/0.25), so headphones
|
|
130
|
+
* don't get a hard 1/0 split; L+R gain per channel always sums to 1.
|
|
131
|
+
*/
|
|
132
|
+
setStereo(mode) {
|
|
133
|
+
const L = [0.5, 0.5, 0.5];
|
|
134
|
+
const R = [0.5, 0.5, 0.5];
|
|
135
|
+
const left = (ch) => { L[ch] = 0.75; R[ch] = 0.25; };
|
|
136
|
+
const right = (ch) => { L[ch] = 0.25; R[ch] = 0.75; };
|
|
137
|
+
if (mode === 'abc') {
|
|
138
|
+
left(0);
|
|
139
|
+
right(2);
|
|
140
|
+
} // A left, B centre, C right
|
|
141
|
+
else if (mode === 'acb') {
|
|
142
|
+
left(0);
|
|
143
|
+
right(1);
|
|
144
|
+
} // A left, C centre, B right
|
|
145
|
+
// 'mono' leaves all three centred.
|
|
146
|
+
this.panL = L;
|
|
147
|
+
this.panR = R;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Writes a chip register (0..15), masking to the hardware register width.
|
|
151
|
+
* Writing R13 (envelope shape) always restarts the envelope.
|
|
152
|
+
*/
|
|
153
|
+
setRegister(r, v) {
|
|
154
|
+
r &= 0x0F;
|
|
155
|
+
switch (r) {
|
|
156
|
+
case 1:
|
|
157
|
+
case 3:
|
|
158
|
+
case 5:
|
|
159
|
+
v &= 0x0F;
|
|
160
|
+
break; // tone coarse (12-bit period)
|
|
161
|
+
case 6:
|
|
162
|
+
v &= 0x1F;
|
|
163
|
+
break; // noise period (5-bit)
|
|
164
|
+
case 8:
|
|
165
|
+
case 9:
|
|
166
|
+
case 10:
|
|
167
|
+
v &= 0x1F;
|
|
168
|
+
break; // volume (bit4 = use envelope)
|
|
169
|
+
case 13:
|
|
170
|
+
v &= 0x0F;
|
|
171
|
+
break; // envelope shape
|
|
172
|
+
default:
|
|
173
|
+
v &= 0xFF;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
this.regs[r] = v;
|
|
177
|
+
if (r === 13) {
|
|
178
|
+
// Decode R13: bit3=CONT bit2=ATT bit1=ALT bit0=HOLD.
|
|
179
|
+
this.envCont = (v >> 3) & 1;
|
|
180
|
+
this.envAlt = (v >> 1) & 1;
|
|
181
|
+
this.envHold = v & 1;
|
|
182
|
+
this.envAttack = ((v >> 2) & 1) === 1; // ATT sets the initial ramp direction
|
|
183
|
+
this.envHolding = false;
|
|
184
|
+
this.envPos = 0;
|
|
185
|
+
this.envCnt = 0;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Steps the 17-bit LFSR once (feedback = bit0 XOR bit3) and latches the output bit. */
|
|
189
|
+
stepNoise() {
|
|
190
|
+
const bit = (this.lfsr ^ (this.lfsr >> 3)) & 1;
|
|
191
|
+
this.lfsr = (this.lfsr >> 1) | (bit << 16);
|
|
192
|
+
this.noiseOut = this.lfsr & 1;
|
|
193
|
+
}
|
|
194
|
+
/** Advances the envelope generator by one step (called when its counter elapses). */
|
|
195
|
+
envStep() {
|
|
196
|
+
if (this.envHolding)
|
|
197
|
+
return;
|
|
198
|
+
this.envPos++;
|
|
199
|
+
if (this.envPos >= this.envSteps) {
|
|
200
|
+
if (this.envCont === 0 || this.envHold === 1) {
|
|
201
|
+
if (this.envCont === 1 && this.envHold === 1 && this.envAlt === 1) {
|
|
202
|
+
this.envAttack = !this.envAttack;
|
|
203
|
+
}
|
|
204
|
+
else if (this.envCont === 0) {
|
|
205
|
+
this.envAttack = false; // one-shot shapes always settle at 0
|
|
206
|
+
}
|
|
207
|
+
this.envHolding = true;
|
|
208
|
+
this.envPos = this.envSteps - 1;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
if (this.envAlt === 1)
|
|
212
|
+
this.envAttack = !this.envAttack;
|
|
213
|
+
this.envPos = 0;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/** Current envelope amplitude as a DAC index (0..15). */
|
|
218
|
+
envLevel() {
|
|
219
|
+
const lvl = this.envAttack ? this.envPos : (this.envSteps - 1 - this.envPos);
|
|
220
|
+
return this.envSteps === 32 ? (lvl >> 1) : lvl;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Renders `count` stereo samples into `left`/`right` starting at `offset`.
|
|
224
|
+
* Box-filters the chip's raw ticks within each output sample (a cheap
|
|
225
|
+
* anti-alias), then DC-blocks each output channel.
|
|
226
|
+
*/
|
|
227
|
+
renderInto(left, right, offset, count) {
|
|
228
|
+
const r = this.regs;
|
|
229
|
+
const dac = this.dac;
|
|
230
|
+
const HEADROOM = 0.5; // three channels + soft pan must not clip the bus
|
|
231
|
+
for (let i = 0; i < count; i++) {
|
|
232
|
+
this.cycleAcc += this.cyclesPerSample;
|
|
233
|
+
let ticks = Math.floor(this.cycleAcc);
|
|
234
|
+
this.cycleAcc -= ticks;
|
|
235
|
+
if (ticks < 1)
|
|
236
|
+
ticks = 1;
|
|
237
|
+
let sumL = 0;
|
|
238
|
+
let sumR = 0;
|
|
239
|
+
for (let t = 0; t < ticks; t++) {
|
|
240
|
+
// ── Tone counters ──
|
|
241
|
+
for (let ch = 0; ch < 3; ch++) {
|
|
242
|
+
let tp = ((r[ch * 2 + 1] & 0x0F) << 8) | r[ch * 2];
|
|
243
|
+
if (tp === 0)
|
|
244
|
+
tp = 1;
|
|
245
|
+
if (++this.toneCnt[ch] >= tp) {
|
|
246
|
+
this.toneCnt[ch] = 0;
|
|
247
|
+
this.toneOut[ch] ^= 1;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
// ── Noise (17-bit LFSR) ──
|
|
251
|
+
let np = r[6] & 0x1F;
|
|
252
|
+
if (np === 0)
|
|
253
|
+
np = 1;
|
|
254
|
+
if (++this.noiseCnt >= (np << 1)) {
|
|
255
|
+
this.noiseCnt = 0;
|
|
256
|
+
this.stepNoise();
|
|
257
|
+
}
|
|
258
|
+
// ── Envelope ──
|
|
259
|
+
let ep = (r[12] << 8) | r[11];
|
|
260
|
+
if (ep === 0)
|
|
261
|
+
ep = 1;
|
|
262
|
+
const envStepTicks = this.envSteps === 32 ? ep : (ep << 1);
|
|
263
|
+
if (++this.envCnt >= envStepTicks) {
|
|
264
|
+
this.envCnt = 0;
|
|
265
|
+
this.envStep();
|
|
266
|
+
}
|
|
267
|
+
const envDac = this.envLevel();
|
|
268
|
+
// ── Mixer + amplitude ──
|
|
269
|
+
const dis = r[7];
|
|
270
|
+
for (let ch = 0; ch < 3; ch++) {
|
|
271
|
+
// R7 is active-low: a set bit disables (forces high) that source.
|
|
272
|
+
const toneBit = (this.toneOut[ch] | ((dis >> ch) & 1)) & 1;
|
|
273
|
+
const noiseBit = (this.noiseOut | ((dis >> (ch + 3)) & 1)) & 1;
|
|
274
|
+
const on = toneBit & noiseBit;
|
|
275
|
+
const vreg = r[8 + ch];
|
|
276
|
+
const lvl = (vreg & 0x10) ? envDac : (vreg & 0x0F);
|
|
277
|
+
const s = dac[lvl] * on;
|
|
278
|
+
sumL += s * this.panL[ch];
|
|
279
|
+
sumR += s * this.panR[ch];
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const invT = HEADROOM / ticks;
|
|
283
|
+
const rawL = sumL * invT;
|
|
284
|
+
const rawR = sumR * invT;
|
|
285
|
+
// DC-blocker: y = x - x1 + 0.995·y1 (centre the unipolar output).
|
|
286
|
+
const yL = rawL - this.dcxL + 0.995 * this.dcyL;
|
|
287
|
+
this.dcxL = rawL;
|
|
288
|
+
this.dcyL = yL;
|
|
289
|
+
const yR = rawR - this.dcxR + 0.995 * this.dcyR;
|
|
290
|
+
this.dcxR = rawR;
|
|
291
|
+
this.dcyR = yR;
|
|
292
|
+
left[offset + i] = yL;
|
|
293
|
+
right[offset + i] = yR;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* @internal Test hook — steps the LFSR `n` times, returning each output bit.
|
|
298
|
+
* Locks the noise sequence against a tap/shift typo (see stepNoise).
|
|
299
|
+
*/
|
|
300
|
+
_stepNoiseForTest(n) {
|
|
301
|
+
const out = [];
|
|
302
|
+
for (let i = 0; i < n; i++) {
|
|
303
|
+
this.stepNoise();
|
|
304
|
+
out.push(this.noiseOut);
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* @internal Test hook — advances the envelope generator `n` times (after an R13
|
|
310
|
+
* write), returning the amplitude level (0..15) after each step.
|
|
311
|
+
*/
|
|
312
|
+
_stepEnvelopeForTest(n) {
|
|
313
|
+
const out = [];
|
|
314
|
+
for (let i = 0; i < n; i++) {
|
|
315
|
+
this.envStep();
|
|
316
|
+
out.push(this.envLevel());
|
|
317
|
+
}
|
|
318
|
+
return out;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// ─── parsePSG (§4.4) ─────────────────────────────────────────────────────────────
|
|
322
|
+
/**
|
|
323
|
+
* Parses a PSG register dump (the format Ay_Emul / zxtune produce) into an
|
|
324
|
+
* {@link AYDump}. A PSG file is a `"PSG\x1a"` header then a byte stream:
|
|
325
|
+
*
|
|
326
|
+
* - `0xFF` — start a new frame
|
|
327
|
+
* - `0xFE n` — `n × 4` empty frames (chip holds its state)
|
|
328
|
+
* - `0xFD` — end of song (often absent: end-of-data ends the song)
|
|
329
|
+
* - `0x00..0x0F` — register index; the next byte is its value
|
|
330
|
+
* - `0x10..0xFC` — foreign device (MSX / 2nd chip) → skip this byte and the next
|
|
331
|
+
*
|
|
332
|
+
* @throws if the header is wrong or a register/`0xFE` pair is truncated.
|
|
333
|
+
*/
|
|
334
|
+
export function parsePSG(bytes) {
|
|
335
|
+
if (bytes.length < 16
|
|
336
|
+
|| bytes[0] !== 0x50 || bytes[1] !== 0x53 || bytes[2] !== 0x47 || bytes[3] !== 0x1A) {
|
|
337
|
+
throw new Error('parsePSG: bad header (expected "PSG\\x1a")');
|
|
338
|
+
}
|
|
339
|
+
const regs = [];
|
|
340
|
+
const vals = [];
|
|
341
|
+
const offsets = []; // start index (into regs/vals) of each frame
|
|
342
|
+
const openFrame = () => { offsets.push(regs.length); };
|
|
343
|
+
let i = 16;
|
|
344
|
+
let ended = false;
|
|
345
|
+
while (i < bytes.length && !ended) {
|
|
346
|
+
const b = bytes[i++];
|
|
347
|
+
if (b === 0xFF) {
|
|
348
|
+
openFrame(); // first 0xFF opens frame 0 — no spurious empty frame
|
|
349
|
+
}
|
|
350
|
+
else if (b === 0xFE) {
|
|
351
|
+
if (i >= bytes.length)
|
|
352
|
+
throw new Error('parsePSG: truncated 0xFE run (missing count)');
|
|
353
|
+
const n = bytes[i++];
|
|
354
|
+
for (let k = 0; k < n * 4; k++)
|
|
355
|
+
openFrame();
|
|
356
|
+
}
|
|
357
|
+
else if (b === 0xFD) {
|
|
358
|
+
ended = true;
|
|
359
|
+
}
|
|
360
|
+
else if (b <= 0x0F) {
|
|
361
|
+
if (i >= bytes.length)
|
|
362
|
+
throw new Error('parsePSG: truncated register pair (missing value)');
|
|
363
|
+
if (offsets.length === 0)
|
|
364
|
+
openFrame(); // tolerate a register write before the first 0xFF
|
|
365
|
+
regs.push(b);
|
|
366
|
+
vals.push(bytes[i++]);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
// Foreign device (0x10..0xFC): skip this command byte and its value byte.
|
|
370
|
+
i++;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const frameCount = offsets.length;
|
|
374
|
+
const frameOffsets = new Uint32Array(frameCount + 1);
|
|
375
|
+
frameOffsets.set(offsets);
|
|
376
|
+
frameOffsets[frameCount] = regs.length;
|
|
377
|
+
return {
|
|
378
|
+
frameRateHz: 50,
|
|
379
|
+
frameCount,
|
|
380
|
+
writeRegs: Uint8Array.from(regs),
|
|
381
|
+
writeVals: Uint8Array.from(vals),
|
|
382
|
+
frameOffsets,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
// ─── AYDumpPlayer — pure playback, no Web Audio (§4.5) ────────────────────────────
|
|
386
|
+
//
|
|
387
|
+
// WORKLET-SAFE INVARIANT: like AYChipCore, no imports / no module references.
|
|
388
|
+
/**
|
|
389
|
+
* Drives an {@link AYChipCore} from an {@link AYDump}: applies each frame's
|
|
390
|
+
* register writes on the right sample and fills PCM buffers. Uses a *fractional*
|
|
391
|
+
* samples-per-frame accumulator so a 3-minute tune doesn't drift at exotic rates.
|
|
392
|
+
* Pure — testable with a spy chip, runnable inside the worklet.
|
|
393
|
+
*/
|
|
394
|
+
export class AYDumpPlayer {
|
|
395
|
+
chip;
|
|
396
|
+
writeRegs;
|
|
397
|
+
writeVals;
|
|
398
|
+
frameOffsets;
|
|
399
|
+
loop;
|
|
400
|
+
loopFrame;
|
|
401
|
+
frameCount;
|
|
402
|
+
samplesPerFrame;
|
|
403
|
+
frame = 0;
|
|
404
|
+
frameAcc = 0;
|
|
405
|
+
ended = false;
|
|
406
|
+
/**
|
|
407
|
+
* @param chip The chip to drive.
|
|
408
|
+
* @param writeRegs Concatenated register indices (see {@link AYDump}).
|
|
409
|
+
* @param writeVals Concatenated values.
|
|
410
|
+
* @param frameOffsets Frame boundaries (length `frameCount + 1`).
|
|
411
|
+
* @param frameRateHz Frames per second (PSG = 50).
|
|
412
|
+
* @param sampleRate Output sample rate (Hz).
|
|
413
|
+
* @param loop Restart at `loopFrame` after the last frame.
|
|
414
|
+
* @param loopFrame Frame to jump to when looping.
|
|
415
|
+
*/
|
|
416
|
+
constructor(chip, writeRegs, writeVals, frameOffsets, frameRateHz, sampleRate, loop, loopFrame) {
|
|
417
|
+
this.chip = chip;
|
|
418
|
+
this.writeRegs = writeRegs;
|
|
419
|
+
this.writeVals = writeVals;
|
|
420
|
+
this.frameOffsets = frameOffsets;
|
|
421
|
+
this.loop = loop;
|
|
422
|
+
this.loopFrame = loopFrame;
|
|
423
|
+
this.frameCount = frameOffsets.length - 1;
|
|
424
|
+
this.samplesPerFrame = sampleRate / frameRateHz;
|
|
425
|
+
}
|
|
426
|
+
applyFrame(f) {
|
|
427
|
+
const start = this.frameOffsets[f];
|
|
428
|
+
const end = this.frameOffsets[f + 1];
|
|
429
|
+
for (let k = start; k < end; k++) {
|
|
430
|
+
this.chip.setRegister(this.writeRegs[k], this.writeVals[k]);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/** Fills `left`/`right` completely. Returns `true` once the (non-looping) song has ended. */
|
|
434
|
+
render(left, right) {
|
|
435
|
+
const N = left.length;
|
|
436
|
+
let i = 0;
|
|
437
|
+
while (i < N) {
|
|
438
|
+
if (this.frameAcc <= 0) {
|
|
439
|
+
if (this.frame >= this.frameCount) {
|
|
440
|
+
if (this.loop)
|
|
441
|
+
this.frame = this.loopFrame;
|
|
442
|
+
else
|
|
443
|
+
this.ended = true;
|
|
444
|
+
}
|
|
445
|
+
if (this.ended) {
|
|
446
|
+
for (; i < N; i++) {
|
|
447
|
+
left[i] = 0;
|
|
448
|
+
right[i] = 0;
|
|
449
|
+
}
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
this.applyFrame(this.frame);
|
|
453
|
+
this.frame++;
|
|
454
|
+
this.frameAcc += this.samplesPerFrame;
|
|
455
|
+
}
|
|
456
|
+
let n = Math.min(N - i, Math.ceil(this.frameAcc));
|
|
457
|
+
if (n < 1)
|
|
458
|
+
n = 1;
|
|
459
|
+
this.chip.renderInto(left, right, i, n);
|
|
460
|
+
this.frameAcc -= n;
|
|
461
|
+
i += n;
|
|
462
|
+
}
|
|
463
|
+
return this.ended;
|
|
464
|
+
}
|
|
465
|
+
/** Fast-forwards to frame `f` by replaying its register writes without rendering. */
|
|
466
|
+
seekToFrame(f) {
|
|
467
|
+
this.chip.reset();
|
|
468
|
+
const target = Math.max(0, Math.min(f, this.frameCount));
|
|
469
|
+
for (let k = 0; k < target; k++)
|
|
470
|
+
this.applyFrame(k);
|
|
471
|
+
this.frame = target;
|
|
472
|
+
this.frameAcc = 0;
|
|
473
|
+
this.ended = false;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function resolveConfig(opts) {
|
|
477
|
+
const variant = opts.variant ?? 'ay';
|
|
478
|
+
return {
|
|
479
|
+
clockHz: opts.clockHz ?? AY_CLOCK,
|
|
480
|
+
variant,
|
|
481
|
+
envSteps: variant === 'ym' ? 32 : 16,
|
|
482
|
+
dac: Array.from(opts.dacTable ?? AY_VOL),
|
|
483
|
+
stereo: opts.stereo ?? 'acb',
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
function clamp01(v) {
|
|
487
|
+
return Math.max(0, Math.min(1, v));
|
|
488
|
+
}
|
|
489
|
+
/** Name the worklet processor is registered under. */
|
|
490
|
+
const AYDUMP_PROCESSOR = 'zxkit-aydump';
|
|
491
|
+
/**
|
|
492
|
+
* Assembles the AudioWorklet source: the two worklet-safe classes stringified,
|
|
493
|
+
* plus a processor that loads a dump and renders it.
|
|
494
|
+
*
|
|
495
|
+
* Each class is bound to a **fixed name we control** (`const __AYChipCore = …`).
|
|
496
|
+
* This is essential: a bundler (Vite/esbuild) commonly emits a class as an
|
|
497
|
+
* *anonymous* expression (`var AYChipCore = class {…}`), so its `.toString()`
|
|
498
|
+
* has no name. Injected as a bare top-level statement that would be
|
|
499
|
+
* `class {…}` → **SyntaxError: class statement requires a name**. Assigning it to
|
|
500
|
+
* a `const` makes it a valid class expression whatever form `toString()` takes,
|
|
501
|
+
* and neither class references its own name internally, so renaming is safe.
|
|
502
|
+
*
|
|
503
|
+
* Internal; exported only so a test can assemble and evaluate the source.
|
|
504
|
+
*/
|
|
505
|
+
export function _buildAYDumpWorkletSource() {
|
|
506
|
+
const Chip = '__AYChipCore';
|
|
507
|
+
const Player = '__AYDumpPlayer';
|
|
508
|
+
return `const ${Chip} = ${AYChipCore.toString()};
|
|
509
|
+
const ${Player} = ${AYDumpPlayer.toString()};
|
|
510
|
+
registerProcessor(${JSON.stringify(AYDUMP_PROCESSOR)}, class extends AudioWorkletProcessor {
|
|
511
|
+
constructor() {
|
|
512
|
+
super()
|
|
513
|
+
this.player = null
|
|
514
|
+
this.chip = null
|
|
515
|
+
this.endedPosted = false
|
|
516
|
+
this.port.onmessage = (e) => {
|
|
517
|
+
const d = e.data
|
|
518
|
+
if (d.type === 'load') {
|
|
519
|
+
const chip = new ${Chip}(sampleRate, d.clockHz, d.envSteps, d.dac, d.stereo)
|
|
520
|
+
this.chip = chip
|
|
521
|
+
this.player = new ${Player}(chip,
|
|
522
|
+
new Uint8Array(d.writeRegs), new Uint8Array(d.writeVals),
|
|
523
|
+
new Uint32Array(d.frameOffsets), d.frameRateHz, sampleRate, d.loop, d.loopFrame)
|
|
524
|
+
this.endedPosted = false
|
|
525
|
+
} else if (d.type === 'stereo' && this.chip) {
|
|
526
|
+
this.chip.setStereo(d.mode)
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
process(_inputs, outputs) {
|
|
531
|
+
const out = outputs[0]
|
|
532
|
+
if (!this.player) return true
|
|
533
|
+
const ended = this.player.render(out[0], out[1])
|
|
534
|
+
if (ended && !this.endedPosted) { this.endedPosted = true; this.port.postMessage({ type: 'ended' }) }
|
|
535
|
+
return true
|
|
536
|
+
}
|
|
537
|
+
})`;
|
|
538
|
+
}
|
|
539
|
+
// One-time worklet registration per AudioContext.
|
|
540
|
+
const _workletReady = new WeakSet();
|
|
541
|
+
async function ensureAYDumpWorklet(ctx) {
|
|
542
|
+
if (_workletReady.has(ctx))
|
|
543
|
+
return;
|
|
544
|
+
const src = _buildAYDumpWorkletSource();
|
|
545
|
+
const url = URL.createObjectURL(new Blob([src], { type: 'text/javascript' }));
|
|
546
|
+
try {
|
|
547
|
+
await ctx.audioWorklet.addModule(url);
|
|
548
|
+
}
|
|
549
|
+
finally {
|
|
550
|
+
URL.revokeObjectURL(url);
|
|
551
|
+
}
|
|
552
|
+
_workletReady.add(ctx);
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Plays a parsed {@link AYDump} in real time through an AudioWorklet, routed to the
|
|
556
|
+
* shared master gain (so global `M` mute / `setMasterVolume` apply for free).
|
|
557
|
+
* Call after {@link initAudio} inside a user gesture — same contract as `playAY`.
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* const dump = await loadPSG('./music/tune.psg')
|
|
561
|
+
* const track = await playAYDump(dump, { loop: true, ...AY_MACHINE.melodik })
|
|
562
|
+
* // later: track.setVolume(0.6); track.stop()
|
|
563
|
+
*/
|
|
564
|
+
export async function playAYDump(dump, opts = {}) {
|
|
565
|
+
initAudio();
|
|
566
|
+
const ctx = getAudioContext();
|
|
567
|
+
const master = getMasterGain();
|
|
568
|
+
if (!ctx || !master) {
|
|
569
|
+
throw new Error('playAYDump: call initAudio() inside a user gesture first');
|
|
570
|
+
}
|
|
571
|
+
await ensureAYDumpWorklet(ctx);
|
|
572
|
+
const cfg = resolveConfig(opts);
|
|
573
|
+
const node = new AudioWorkletNode(ctx, AYDUMP_PROCESSOR, { outputChannelCount: [2] });
|
|
574
|
+
const gain = ctx.createGain();
|
|
575
|
+
let volume = clamp01(opts.volume ?? 1);
|
|
576
|
+
gain.gain.value = volume;
|
|
577
|
+
node.connect(gain);
|
|
578
|
+
gain.connect(master);
|
|
579
|
+
// Copy the arrays before transferring their buffers, so the caller's dump stays
|
|
580
|
+
// usable (e.g. to play it again) — the copy is cheap next to zero-copy on the audio thread.
|
|
581
|
+
const wr = dump.writeRegs.slice();
|
|
582
|
+
const wv = dump.writeVals.slice();
|
|
583
|
+
const fo = dump.frameOffsets.slice();
|
|
584
|
+
node.port.postMessage({
|
|
585
|
+
type: 'load',
|
|
586
|
+
clockHz: cfg.clockHz, envSteps: cfg.envSteps, dac: cfg.dac, stereo: cfg.stereo,
|
|
587
|
+
writeRegs: wr, writeVals: wv, frameOffsets: fo,
|
|
588
|
+
frameRateHz: dump.frameRateHz, loop: !!opts.loop, loopFrame: opts.loopFrame ?? 0,
|
|
589
|
+
}, [wr.buffer, wv.buffer, fo.buffer]);
|
|
590
|
+
let playing = true;
|
|
591
|
+
const handle = {
|
|
592
|
+
get playing() { return playing; },
|
|
593
|
+
stop() {
|
|
594
|
+
playing = false;
|
|
595
|
+
try {
|
|
596
|
+
node.disconnect();
|
|
597
|
+
}
|
|
598
|
+
catch { /* already gone */ }
|
|
599
|
+
try {
|
|
600
|
+
gain.disconnect();
|
|
601
|
+
}
|
|
602
|
+
catch { /* already gone */ }
|
|
603
|
+
},
|
|
604
|
+
pause() { gain.gain.value = 0; },
|
|
605
|
+
resume() { gain.gain.value = volume; },
|
|
606
|
+
setVolume(v) { volume = clamp01(v); gain.gain.value = volume; },
|
|
607
|
+
setStereo(mode) { node.port.postMessage({ type: 'stereo', mode }); },
|
|
608
|
+
};
|
|
609
|
+
node.port.onmessage = (e) => {
|
|
610
|
+
if (e.data?.type === 'ended') {
|
|
611
|
+
playing = false;
|
|
612
|
+
handle.onEnded?.();
|
|
613
|
+
}
|
|
614
|
+
};
|
|
615
|
+
return handle;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Renders a dump to stereo PCM **without an AudioContext** — deterministic, for
|
|
619
|
+
* unit tests, `AudioWorklet`-less fallbacks, and offline tooling (e.g. PSG → WAV).
|
|
620
|
+
* Beware RAM: a full 3-minute stereo render at 44.1 kHz is ≈ 64 MB — pass
|
|
621
|
+
* `maxSeconds` to bound it.
|
|
622
|
+
*/
|
|
623
|
+
export function renderAYDump(dump, opts = {}) {
|
|
624
|
+
const sampleRate = opts.sampleRate ?? 44100;
|
|
625
|
+
const cfg = resolveConfig(opts);
|
|
626
|
+
const chip = new AYChipCore(sampleRate, cfg.clockHz, cfg.envSteps, cfg.dac, cfg.stereo);
|
|
627
|
+
const player = new AYDumpPlayer(chip, dump.writeRegs, dump.writeVals, dump.frameOffsets, dump.frameRateHz, sampleRate, false, 0);
|
|
628
|
+
const songSeconds = dump.frameCount / dump.frameRateHz + 0.1;
|
|
629
|
+
const seconds = opts.maxSeconds !== undefined ? Math.min(opts.maxSeconds, songSeconds) : songSeconds;
|
|
630
|
+
const total = Math.max(0, Math.ceil(seconds * sampleRate));
|
|
631
|
+
const left = new Float32Array(total);
|
|
632
|
+
const right = new Float32Array(total);
|
|
633
|
+
const CHUNK = 4096;
|
|
634
|
+
for (let i = 0; i < total;) {
|
|
635
|
+
const n = Math.min(CHUNK, total - i);
|
|
636
|
+
const ended = player.render(left.subarray(i, i + n), right.subarray(i, i + n));
|
|
637
|
+
i += n;
|
|
638
|
+
if (ended)
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
return { left, right };
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Fetches a `.psg` file and parses it into an {@link AYDump}. Network / parse
|
|
645
|
+
* errors propagate so the game decides how to handle them.
|
|
646
|
+
*
|
|
647
|
+
* @example
|
|
648
|
+
* const dump = await loadPSG('./music/tune.psg')
|
|
649
|
+
*/
|
|
650
|
+
export async function loadPSG(url) {
|
|
651
|
+
const res = await fetch(url);
|
|
652
|
+
if (!res.ok)
|
|
653
|
+
throw new Error(`loadPSG: HTTP ${res.status} for ${url}`);
|
|
654
|
+
return parsePSG(new Uint8Array(await res.arrayBuffer()));
|
|
655
|
+
}
|
|
656
|
+
//# sourceMappingURL=aydump.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aydump.js","sourceRoot":"","sources":["../src/aydump.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAA;AAC5B,cAAc,SAAS,CAAA;AACvB,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,SAAS,CAAA;AACvB,cAAc,cAAc,CAAA;AAC5B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,YAAY,CAAA;AAC1B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,iBAAiB,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,mBAAmB,CAAA;AACjC,cAAc,YAAY,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zx-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/zrebec/zx-kit.git"
|
|
@@ -49,5 +49,8 @@
|
|
|
49
49
|
"semantic-release": "^25.0.3",
|
|
50
50
|
"typescript": "^6.0.3",
|
|
51
51
|
"vitest": "^4.1.5"
|
|
52
|
+
},
|
|
53
|
+
"allowScripts": {
|
|
54
|
+
"fsevents@2.3.3": true
|
|
52
55
|
}
|
|
53
56
|
}
|