@yume-chan/pcm-player 0.0.20 → 0.0.22
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/CHANGELOG.json +18 -0
- package/CHANGELOG.md +13 -1
- package/README.md +5 -4
- package/esm/index.d.ts +2 -4
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +31 -18
- package/esm/index.js.map +1 -1
- package/esm/worker.js +168 -63
- package/esm/worker.js.map +1 -1
- package/package.json +9 -9
- package/src/index.ts +40 -24
- package/tsconfig.dom.json +9 -8
- package/tsconfig.dom.tsbuildinfo +1 -1
- package/tsconfig.worker.tsbuildinfo +1 -1
- package/worker/worker.ts +229 -101
package/worker/worker.ts
CHANGED
|
@@ -1,80 +1,192 @@
|
|
|
1
|
+
const INPUT_HOP_SIZE = 3000;
|
|
2
|
+
const SCALE = 0.9;
|
|
3
|
+
const OUTPUT_HOP_SIZE = (INPUT_HOP_SIZE * SCALE) | 0;
|
|
4
|
+
|
|
5
|
+
const WINDOW_SIZE = 6000;
|
|
6
|
+
const WINDOW_WEIGHT_TABLE = new Float32Array(WINDOW_SIZE);
|
|
7
|
+
for (let i = 0; i < WINDOW_SIZE / 2; i += 1) {
|
|
8
|
+
const value = Math.sin((i / WINDOW_SIZE) * Math.PI);
|
|
9
|
+
WINDOW_WEIGHT_TABLE[i] = value;
|
|
10
|
+
WINDOW_WEIGHT_TABLE[WINDOW_SIZE - i - 1] = value;
|
|
11
|
+
}
|
|
12
|
+
|
|
1
13
|
abstract class SourceProcessor<T>
|
|
2
14
|
extends AudioWorkletProcessor
|
|
3
15
|
implements AudioWorkletProcessorImpl
|
|
4
16
|
{
|
|
5
|
-
|
|
6
|
-
|
|
17
|
+
channelCount: number;
|
|
18
|
+
#readBuffer: Float32Array;
|
|
19
|
+
|
|
20
|
+
#chunks: T[] = [];
|
|
21
|
+
#chunkSampleCounts: number[] = [];
|
|
22
|
+
#totalSampleCount = 0;
|
|
23
|
+
|
|
24
|
+
#starting = true;
|
|
25
|
+
#speedUp = false;
|
|
26
|
+
#readOffset = 0;
|
|
27
|
+
#inputOffset = 0;
|
|
28
|
+
#outputOffset = 0;
|
|
7
29
|
|
|
8
|
-
|
|
30
|
+
constructor(options: { outputChannelCount?: number[] }) {
|
|
9
31
|
super();
|
|
32
|
+
|
|
33
|
+
this.channelCount = options.outputChannelCount![0]!;
|
|
34
|
+
this.#readBuffer = new Float32Array(this.channelCount);
|
|
35
|
+
|
|
10
36
|
this.port.onmessage = (event) => {
|
|
37
|
+
while (this.#totalSampleCount > 0.35 * 48000) {
|
|
38
|
+
this.#chunks.shift();
|
|
39
|
+
const count = this.#chunkSampleCounts.shift()!;
|
|
40
|
+
this.#totalSampleCount -= count;
|
|
41
|
+
}
|
|
42
|
+
|
|
11
43
|
const data = event.data as ArrayBuffer[];
|
|
12
44
|
const [source, length] = this.createSource(data);
|
|
13
|
-
this.
|
|
14
|
-
this.
|
|
45
|
+
this.#chunks.push(source);
|
|
46
|
+
this.#chunkSampleCounts.push(length);
|
|
47
|
+
this.#totalSampleCount += length;
|
|
48
|
+
|
|
49
|
+
if (!this.#speedUp && this.#totalSampleCount > 0.25 * 48000) {
|
|
50
|
+
this.#speedUp = true;
|
|
51
|
+
this.#readOffset = 0;
|
|
52
|
+
this.#inputOffset = 0;
|
|
53
|
+
this.#outputOffset = 0;
|
|
54
|
+
}
|
|
15
55
|
};
|
|
16
56
|
}
|
|
17
57
|
|
|
18
58
|
protected abstract createSource(data: ArrayBuffer[]): [T, number];
|
|
19
59
|
|
|
20
|
-
process(_inputs: Float32Array[][], outputs: Float32Array[][]) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
60
|
+
process(_inputs: Float32Array[][], [outputs]: Float32Array[][]) {
|
|
61
|
+
if (this.#starting) {
|
|
62
|
+
if (this.#totalSampleCount < 0.1 * 48000) {
|
|
63
|
+
return true;
|
|
64
|
+
} else {
|
|
65
|
+
this.#starting = false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
25
68
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
69
|
+
if (this.#speedUp && this.#totalSampleCount < 0.15 * 48000) {
|
|
70
|
+
this.#speedUp = false;
|
|
71
|
+
this.#starting = true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const outputLength = outputs![0]!.length;
|
|
31
75
|
|
|
32
|
-
|
|
33
|
-
|
|
76
|
+
if (this.#speedUp) {
|
|
77
|
+
for (let i = 0; i < outputLength; i += 1) {
|
|
78
|
+
let totalWeight = 0;
|
|
34
79
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
80
|
+
const firstWindow = Math.max(
|
|
81
|
+
0,
|
|
82
|
+
Math.floor(
|
|
83
|
+
(this.#outputOffset - WINDOW_SIZE) / OUTPUT_HOP_SIZE,
|
|
84
|
+
) + 1,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
let inWindowIndex =
|
|
88
|
+
this.#outputOffset - firstWindow * OUTPUT_HOP_SIZE;
|
|
89
|
+
let inputIndex = firstWindow * INPUT_HOP_SIZE + inWindowIndex;
|
|
90
|
+
|
|
91
|
+
while (inputIndex > 0 && inWindowIndex >= 0) {
|
|
92
|
+
this.#read(inputIndex - this.#readOffset);
|
|
93
|
+
const weight = WINDOW_WEIGHT_TABLE[inWindowIndex]!;
|
|
94
|
+
for (let j = 0; j < this.channelCount; j += 1) {
|
|
95
|
+
outputs![j]![i] += this.#readBuffer[j]! * weight;
|
|
96
|
+
}
|
|
97
|
+
totalWeight += weight;
|
|
98
|
+
|
|
99
|
+
inputIndex += INPUT_HOP_SIZE - OUTPUT_HOP_SIZE;
|
|
100
|
+
inWindowIndex -= OUTPUT_HOP_SIZE;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (totalWeight > 0) {
|
|
104
|
+
for (let j = 0; j < this.channelCount; j += 1) {
|
|
105
|
+
outputs![j]![i] /= totalWeight;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.#outputOffset += 1;
|
|
110
|
+
if (firstWindow > 0) {
|
|
111
|
+
this.#outputOffset -= OUTPUT_HOP_SIZE;
|
|
112
|
+
this.#readOffset -= INPUT_HOP_SIZE;
|
|
113
|
+
this.#inputOffset += (1 - SCALE) * INPUT_HOP_SIZE;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.#inputOffset += outputLength;
|
|
118
|
+
const firstChunkSampleCount = this.#chunkSampleCounts[0]!;
|
|
119
|
+
if (
|
|
120
|
+
firstChunkSampleCount !== undefined &&
|
|
121
|
+
this.#inputOffset >= firstChunkSampleCount
|
|
122
|
+
) {
|
|
123
|
+
this.#chunks.shift();
|
|
124
|
+
this.#chunkSampleCounts.shift();
|
|
125
|
+
this.#totalSampleCount -= firstChunkSampleCount;
|
|
126
|
+
this.#readOffset += firstChunkSampleCount;
|
|
127
|
+
this.#inputOffset -= firstChunkSampleCount;
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
this.#copyChunks(outputs!);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#copyChunks(outputs: Float32Array[]) {
|
|
137
|
+
let outputIndex = 0;
|
|
138
|
+
const outputLength = outputs[0]!.length;
|
|
139
|
+
|
|
140
|
+
while (this.#chunks.length > 0 && outputIndex < outputLength) {
|
|
141
|
+
let source: T | undefined = this.#chunks[0];
|
|
142
|
+
let consumedSampleCount = 0;
|
|
143
|
+
[source, consumedSampleCount, outputIndex] = this.copyChunk(
|
|
39
144
|
source!,
|
|
40
|
-
|
|
41
|
-
outputRight,
|
|
145
|
+
outputs,
|
|
42
146
|
outputLength,
|
|
43
|
-
outputIndex
|
|
147
|
+
outputIndex,
|
|
44
148
|
);
|
|
45
149
|
|
|
46
|
-
|
|
47
|
-
this._sourceSampleCount -= consumedSampleCount;
|
|
48
|
-
sourceIndex -= consumedSampleCount;
|
|
150
|
+
this.#totalSampleCount -= consumedSampleCount;
|
|
49
151
|
|
|
50
152
|
if (source) {
|
|
51
153
|
// Output full
|
|
52
|
-
this
|
|
53
|
-
|
|
154
|
+
this.#chunks[0] = source;
|
|
155
|
+
this.#chunkSampleCounts[0]! -= consumedSampleCount;
|
|
156
|
+
return;
|
|
54
157
|
}
|
|
55
158
|
|
|
56
|
-
this.
|
|
159
|
+
this.#chunks.shift();
|
|
160
|
+
this.#chunkSampleCounts.shift();
|
|
57
161
|
}
|
|
162
|
+
}
|
|
58
163
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
164
|
+
#read(offset: number) {
|
|
165
|
+
for (let i = 0; i < this.#chunks.length; i += 1) {
|
|
166
|
+
const length = this.#chunkSampleCounts[i]!;
|
|
167
|
+
|
|
168
|
+
if (offset < length) {
|
|
169
|
+
this.read(this.#chunks[i]!, offset, this.#readBuffer);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
offset -= length;
|
|
65
174
|
}
|
|
66
175
|
|
|
67
|
-
|
|
176
|
+
this.#readBuffer.fill(0);
|
|
68
177
|
}
|
|
69
178
|
|
|
179
|
+
protected abstract read(
|
|
180
|
+
source: T,
|
|
181
|
+
offset: number,
|
|
182
|
+
target: Float32Array,
|
|
183
|
+
): void;
|
|
184
|
+
|
|
70
185
|
protected abstract copyChunk(
|
|
71
|
-
sourceIndex: number,
|
|
72
|
-
sourceIndexStep: number,
|
|
73
186
|
source: T,
|
|
74
|
-
|
|
75
|
-
outputRight: Float32Array,
|
|
187
|
+
outputs: Float32Array[],
|
|
76
188
|
outputLength: number,
|
|
77
|
-
outputIndex: number
|
|
189
|
+
outputIndex: number,
|
|
78
190
|
): [source: T | undefined, sourceIndex: number, outputIndex: number];
|
|
79
191
|
}
|
|
80
192
|
|
|
@@ -84,135 +196,151 @@ class Int16SourceProcessor
|
|
|
84
196
|
{
|
|
85
197
|
protected override createSource(data: ArrayBuffer[]): [Int16Array, number] {
|
|
86
198
|
const source = new Int16Array(data[0]!);
|
|
87
|
-
return [source, source.length /
|
|
199
|
+
return [source, source.length / this.channelCount];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
protected override read(
|
|
203
|
+
source: Int16Array,
|
|
204
|
+
offset: number,
|
|
205
|
+
target: Float32Array,
|
|
206
|
+
) {
|
|
207
|
+
const sourceOffset = offset * this.channelCount;
|
|
208
|
+
for (let i = 0; i < this.channelCount; i += 1) {
|
|
209
|
+
target[i] = source[sourceOffset + i]! / 0x8000;
|
|
210
|
+
}
|
|
88
211
|
}
|
|
89
212
|
|
|
90
213
|
protected override copyChunk(
|
|
91
|
-
sourceIndex: number,
|
|
92
|
-
sourceIndexStep: number,
|
|
93
214
|
source: Int16Array,
|
|
94
|
-
|
|
95
|
-
outputRight: Float32Array,
|
|
215
|
+
outputs: Float32Array[],
|
|
96
216
|
outputLength: number,
|
|
97
|
-
outputIndex: number
|
|
217
|
+
outputIndex: number,
|
|
98
218
|
): [
|
|
99
219
|
source: Int16Array | undefined,
|
|
100
220
|
sourceIndex: number,
|
|
101
|
-
outputIndex: number
|
|
221
|
+
outputIndex: number,
|
|
102
222
|
] {
|
|
103
223
|
const sourceLength = source.length;
|
|
104
|
-
let
|
|
105
|
-
|
|
106
|
-
while (sourceSampleIndex < sourceLength) {
|
|
107
|
-
outputLeft[outputIndex] = source[sourceSampleIndex]! / 0x8000;
|
|
108
|
-
outputRight[outputIndex] = source[sourceSampleIndex + 1]! / 0x8000;
|
|
224
|
+
let sourceIndex = 0;
|
|
109
225
|
|
|
110
|
-
|
|
111
|
-
|
|
226
|
+
while (sourceIndex < sourceLength) {
|
|
227
|
+
for (let i = 0; i < this.channelCount; i += 1) {
|
|
228
|
+
outputs[i]![outputIndex] = source[sourceIndex]! / 0x8000;
|
|
229
|
+
sourceIndex += 1;
|
|
230
|
+
}
|
|
112
231
|
outputIndex += 1;
|
|
113
232
|
|
|
114
233
|
if (outputIndex === outputLength) {
|
|
115
234
|
return [
|
|
116
|
-
|
|
117
|
-
? source.subarray(
|
|
235
|
+
sourceIndex < sourceLength
|
|
236
|
+
? source.subarray(sourceIndex)
|
|
118
237
|
: undefined,
|
|
119
|
-
sourceIndex,
|
|
238
|
+
sourceIndex / this.channelCount,
|
|
120
239
|
outputIndex,
|
|
121
240
|
];
|
|
122
241
|
}
|
|
123
242
|
}
|
|
124
243
|
|
|
125
|
-
return [undefined, sourceIndex, outputIndex];
|
|
244
|
+
return [undefined, sourceIndex / this.channelCount, outputIndex];
|
|
126
245
|
}
|
|
127
246
|
}
|
|
128
247
|
|
|
129
248
|
class Float32SourceProcessor extends SourceProcessor<Float32Array> {
|
|
130
249
|
protected override createSource(
|
|
131
|
-
data: ArrayBuffer[]
|
|
250
|
+
data: ArrayBuffer[],
|
|
132
251
|
): [Float32Array, number] {
|
|
133
252
|
const source = new Float32Array(data[0]!);
|
|
134
|
-
return [source, source.length /
|
|
253
|
+
return [source, source.length / this.channelCount];
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
protected override read(
|
|
257
|
+
source: Float32Array,
|
|
258
|
+
offset: number,
|
|
259
|
+
target: Float32Array,
|
|
260
|
+
) {
|
|
261
|
+
const sourceOffset = offset * this.channelCount;
|
|
262
|
+
for (let i = 0; i < this.channelCount; i += 1) {
|
|
263
|
+
target[i] = source[sourceOffset + i]!;
|
|
264
|
+
}
|
|
135
265
|
}
|
|
136
266
|
|
|
137
267
|
protected override copyChunk(
|
|
138
|
-
sourceIndex: number,
|
|
139
|
-
sourceIndexStep: number,
|
|
140
268
|
source: Float32Array,
|
|
141
|
-
|
|
142
|
-
outputRight: Float32Array,
|
|
269
|
+
outputs: Float32Array[],
|
|
143
270
|
outputLength: number,
|
|
144
|
-
outputIndex: number
|
|
271
|
+
outputIndex: number,
|
|
145
272
|
): [
|
|
146
273
|
source: Float32Array | undefined,
|
|
147
274
|
sourceIndex: number,
|
|
148
|
-
outputIndex: number
|
|
275
|
+
outputIndex: number,
|
|
149
276
|
] {
|
|
150
277
|
const sourceLength = source.length;
|
|
151
|
-
let
|
|
152
|
-
|
|
153
|
-
while (sourceSampleIndex < sourceLength) {
|
|
154
|
-
outputLeft[outputIndex] = source[sourceSampleIndex]!;
|
|
155
|
-
outputRight[outputIndex] = source[sourceSampleIndex + 1]!;
|
|
278
|
+
let sourceIndex = 0;
|
|
156
279
|
|
|
157
|
-
|
|
158
|
-
|
|
280
|
+
while (sourceIndex < sourceLength) {
|
|
281
|
+
for (let i = 0; i < this.channelCount; i += 1) {
|
|
282
|
+
outputs[i]![outputIndex] = source[sourceIndex]!;
|
|
283
|
+
sourceIndex += 1;
|
|
284
|
+
}
|
|
159
285
|
outputIndex += 1;
|
|
160
286
|
|
|
161
287
|
if (outputIndex === outputLength) {
|
|
162
288
|
return [
|
|
163
|
-
|
|
164
|
-
? source.subarray(
|
|
289
|
+
sourceIndex < sourceLength
|
|
290
|
+
? source.subarray(sourceIndex)
|
|
165
291
|
: undefined,
|
|
166
|
-
sourceIndex,
|
|
292
|
+
sourceIndex / this.channelCount,
|
|
167
293
|
outputIndex,
|
|
168
294
|
];
|
|
169
295
|
}
|
|
170
296
|
}
|
|
171
297
|
|
|
172
|
-
return [undefined, sourceIndex, outputIndex];
|
|
298
|
+
return [undefined, sourceIndex / this.channelCount, outputIndex];
|
|
173
299
|
}
|
|
174
300
|
}
|
|
175
301
|
|
|
176
302
|
class Float32PlanerSourceProcessor extends SourceProcessor<Float32Array[]> {
|
|
177
303
|
protected override createSource(
|
|
178
|
-
data: ArrayBuffer[]
|
|
304
|
+
data: ArrayBuffer[],
|
|
179
305
|
): [Float32Array[], number] {
|
|
180
306
|
const source = data.map((channel) => new Float32Array(channel));
|
|
181
307
|
return [source, source[0]!.length];
|
|
182
308
|
}
|
|
183
309
|
|
|
310
|
+
protected override read(
|
|
311
|
+
source: Float32Array[],
|
|
312
|
+
offset: number,
|
|
313
|
+
target: Float32Array,
|
|
314
|
+
) {
|
|
315
|
+
for (let i = 0; i < target.length; i += 1) {
|
|
316
|
+
target[i] = source[i]![offset]!;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
184
320
|
protected override copyChunk(
|
|
185
|
-
sourceIndex: number,
|
|
186
|
-
sourceIndexStep: number,
|
|
187
321
|
source: Float32Array[],
|
|
188
|
-
|
|
189
|
-
outputRight: Float32Array,
|
|
322
|
+
outputs: Float32Array[],
|
|
190
323
|
outputLength: number,
|
|
191
|
-
outputIndex: number
|
|
324
|
+
outputIndex: number,
|
|
192
325
|
): [
|
|
193
326
|
source: Float32Array[] | undefined,
|
|
194
327
|
sourceIndex: number,
|
|
195
|
-
outputIndex: number
|
|
328
|
+
outputIndex: number,
|
|
196
329
|
] {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
const sourceLength = sourceLeft.length;
|
|
200
|
-
let sourceSampleIndex = sourceIndex | 0;
|
|
201
|
-
|
|
202
|
-
while (sourceSampleIndex < sourceLength) {
|
|
203
|
-
outputLeft[outputIndex] = sourceLeft[sourceSampleIndex]!;
|
|
204
|
-
outputRight[outputIndex] = sourceRight[sourceSampleIndex]!;
|
|
330
|
+
const sourceLength = source[0]!.length;
|
|
331
|
+
let sourceIndex = 0;
|
|
205
332
|
|
|
206
|
-
|
|
207
|
-
|
|
333
|
+
while (sourceIndex < sourceLength) {
|
|
334
|
+
for (let i = 0; i < this.channelCount; i += 1) {
|
|
335
|
+
outputs[i]![outputIndex] = source[i]![sourceIndex]!;
|
|
336
|
+
}
|
|
337
|
+
sourceIndex += 1;
|
|
208
338
|
outputIndex += 1;
|
|
209
339
|
|
|
210
340
|
if (outputIndex === outputLength) {
|
|
211
341
|
return [
|
|
212
|
-
|
|
213
|
-
? source.map((channel) =>
|
|
214
|
-
channel.subarray(sourceSampleIndex)
|
|
215
|
-
)
|
|
342
|
+
sourceIndex < sourceLength
|
|
343
|
+
? source.map((channel) => channel.subarray(sourceIndex))
|
|
216
344
|
: undefined,
|
|
217
345
|
sourceIndex,
|
|
218
346
|
outputIndex,
|
|
@@ -228,5 +356,5 @@ registerProcessor("int16-source-processor", Int16SourceProcessor);
|
|
|
228
356
|
registerProcessor("float32-source-processor", Float32SourceProcessor);
|
|
229
357
|
registerProcessor(
|
|
230
358
|
"float32-planer-source-processor",
|
|
231
|
-
Float32PlanerSourceProcessor
|
|
359
|
+
Float32PlanerSourceProcessor,
|
|
232
360
|
);
|