@manybitsbyte/nesplayer-svelte 0.8.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.
@@ -0,0 +1,10 @@
1
+ interface Props {
2
+ rom?: Uint8Array;
3
+ romName?: string;
4
+ volume?: number;
5
+ muted?: boolean;
6
+ sampleRate?: 48000 | 44100;
7
+ }
8
+ declare const Screen: import("svelte").Component<Props, {}, "volume" | "muted" | "sampleRate">;
9
+ type Screen = ReturnType<typeof Screen>;
10
+ export default Screen;
@@ -0,0 +1 @@
1
+ export { default as Screen } from './components/Screen.svelte';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { default as Screen } from './components/Screen.svelte';
@@ -0,0 +1 @@
1
+ export const version: "0.8.0";
@@ -0,0 +1 @@
1
+ export const version = '0.8.0';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,172 @@
1
+ const RING_SIZE = 4096;
2
+ const PRE_BUFFER = 1024;
3
+ const TARGET_FILL = 1536;
4
+ const SMOOTH = 0.005;
5
+ const RATE_GAIN = 0.000008;
6
+ const MAX_ADJUST = 0.005;
7
+ const FADE_RATE = 0.97;
8
+ const MP_BUF_SIZE = 16384;
9
+
10
+ class NESAudioProcessor extends AudioWorkletProcessor {
11
+ constructor() {
12
+ super();
13
+ this.headerView = null;
14
+ this.ringView = null;
15
+ this.ready = false;
16
+ this.primed = false;
17
+ this.lastSample = 0;
18
+ this.smoothFill = TARGET_FILL;
19
+ this.readBuf = new Float32Array(192);
20
+ this.mpBuf = new Float32Array(MP_BUF_SIZE);
21
+ this.mpHead = 0;
22
+ this.mpTail = 0;
23
+ this.mpMode = false;
24
+ this.mpPrimed = false;
25
+
26
+ this.port.onmessage = (e) => {
27
+ if (e.data.type === 'init') {
28
+ const sab = e.data.sharedBuffer;
29
+ this.headerView = new Int32Array(sab, 0, 2);
30
+ this.ringView = new Float32Array(sab, 8, RING_SIZE);
31
+ this.mpMode = false;
32
+ this.ready = true;
33
+ } else if (e.data.type === 'init-mp') {
34
+ this.mpMode = true;
35
+ this.ready = true;
36
+ } else if (e.data.type === 'push') {
37
+ const s = e.data.samples;
38
+ const avail = MP_BUF_SIZE - (this.mpHead - this.mpTail);
39
+ const count = Math.min(s.length, avail);
40
+ for (let i = 0; i < count; i++) {
41
+ this.mpBuf[(this.mpHead + i) % MP_BUF_SIZE] = s[i];
42
+ }
43
+ this.mpHead += count;
44
+ }
45
+ };
46
+ }
47
+
48
+ process(inputs, outputs) {
49
+ const output = outputs[0];
50
+ if (!output || output.length === 0) { return true; }
51
+
52
+ const left = output[0];
53
+ const right = output.length > 1 ? output[1] : null;
54
+ const frames = left.length;
55
+
56
+ if (!this.ready) {
57
+ left.fill(0);
58
+ if (right) { right.fill(0); }
59
+ return true;
60
+ }
61
+
62
+ if (this.mpMode) {
63
+ return this._processMp(left, right, frames);
64
+ }
65
+
66
+ const writePos = Atomics.load(this.headerView, 0);
67
+ const readPos = Atomics.load(this.headerView, 1);
68
+ const available = (writePos - readPos + RING_SIZE) % RING_SIZE;
69
+
70
+ if (!this.primed) {
71
+ if (available < PRE_BUFFER) {
72
+ left.fill(0);
73
+ if (right) { right.fill(0); }
74
+ return true;
75
+ }
76
+ this.primed = true;
77
+ this.smoothFill = available;
78
+ }
79
+
80
+ if (available === 0) {
81
+ let held = this.lastSample;
82
+ for (let i = 0; i < frames; i++) {
83
+ held *= FADE_RATE;
84
+ left[i] = held;
85
+ if (right) { right[i] = held; }
86
+ }
87
+ this.lastSample = held;
88
+ return true;
89
+ }
90
+
91
+ this.smoothFill += SMOOTH * (available - this.smoothFill);
92
+ const fillError = this.smoothFill - TARGET_FILL;
93
+ const speedAdj = Math.max(-MAX_ADJUST, Math.min(MAX_ADJUST, fillError * RATE_GAIN));
94
+ let readCount = Math.round(frames * (1.0 + speedAdj));
95
+ readCount = Math.max(1, Math.min(readCount, available));
96
+
97
+ const buf = this.readBuf;
98
+ for (let i = 0; i < readCount; i++) {
99
+ buf[i] = this.ringView[(readPos + i) % RING_SIZE];
100
+ }
101
+
102
+ if (readCount === frames) {
103
+ for (let i = 0; i < frames; i++) {
104
+ const s = buf[i];
105
+ left[i] = s;
106
+ if (right) { right[i] = s; }
107
+ }
108
+ } else {
109
+ const ratio = readCount / frames;
110
+ for (let i = 0; i < frames; i++) {
111
+ const srcPos = i * ratio;
112
+ const idx = srcPos | 0;
113
+ const frac = srcPos - idx;
114
+ const s0 = buf[idx];
115
+ const s1 = idx + 1 < readCount ? buf[idx + 1] : s0;
116
+ const s = s0 + frac * (s1 - s0);
117
+ left[i] = s;
118
+ if (right) { right[i] = s; }
119
+ }
120
+ }
121
+
122
+ this.lastSample = left[frames - 1];
123
+ Atomics.store(this.headerView, 1, (readPos + readCount) % RING_SIZE);
124
+ return true;
125
+ }
126
+
127
+ _processMp(left, right, frames) {
128
+ const available = this.mpHead - this.mpTail;
129
+
130
+ if (!this.mpPrimed) {
131
+ if (available < PRE_BUFFER) {
132
+ left.fill(0);
133
+ if (right) { right.fill(0); }
134
+ return true;
135
+ }
136
+ this.mpPrimed = true;
137
+ }
138
+
139
+ if (available === 0) {
140
+ let held = this.lastSample;
141
+ for (let i = 0; i < frames; i++) {
142
+ held *= FADE_RATE;
143
+ left[i] = held;
144
+ if (right) { right[i] = held; }
145
+ }
146
+ this.lastSample = held;
147
+ return true;
148
+ }
149
+
150
+ const toRead = Math.min(frames, available);
151
+ for (let i = 0; i < toRead; i++) {
152
+ const s = this.mpBuf[(this.mpTail + i) % MP_BUF_SIZE];
153
+ left[i] = s;
154
+ if (right) { right[i] = s; }
155
+ }
156
+ this.mpTail += toRead;
157
+
158
+ if (toRead < frames) {
159
+ let held = toRead > 0 ? left[toRead - 1] : this.lastSample;
160
+ for (let i = toRead; i < frames; i++) {
161
+ held *= FADE_RATE;
162
+ left[i] = held;
163
+ if (right) { right[i] = held; }
164
+ }
165
+ }
166
+
167
+ this.lastSample = left[frames - 1];
168
+ return true;
169
+ }
170
+ }
171
+
172
+ registerProcessor('nes-audio-processor', NESAudioProcessor);
@@ -0,0 +1,2 @@
1
+ export default createNESPlayerModule;
2
+ declare function createNESPlayerModule(moduleArg?: {}): Promise<{}>;