agdi 3.3.6 → 3.3.7

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.
Files changed (36) hide show
  1. package/README.md +13 -0
  2. package/dist/APEv2Parser-EU45AV6X.js +14 -0
  3. package/dist/AiffParser-FOX7GQ42.js +189 -0
  4. package/dist/AsfParser-HD5CSGIO.js +610 -0
  5. package/dist/DsdiffParser-OJREDMBI.js +188 -0
  6. package/dist/DsfParser-2YL4ARJ7.js +110 -0
  7. package/dist/FlacParser-IVV4RYF6.js +15 -0
  8. package/dist/MP4Parser-NLX4A2YN.js +1140 -0
  9. package/dist/MatroskaParser-4OEK43GF.js +654 -0
  10. package/dist/MpegParser-PXKEUF2B.js +642 -0
  11. package/dist/MusepackParser-54QGYRLY.js +322 -0
  12. package/dist/OggParser-KRV5QCGZ.js +435 -0
  13. package/dist/WavPackParser-XPZSQFVS.js +203 -0
  14. package/dist/WaveParser-27IS2RAI.js +294 -0
  15. package/dist/chunk-2B4QMSZW.js +311 -0
  16. package/dist/chunk-3JKZUGPJ.js +70 -0
  17. package/dist/chunk-4VNS5WPM.js +42 -0
  18. package/dist/chunk-65JVFJ3X.js +729 -0
  19. package/dist/chunk-6OKLAJRQ.js +0 -0
  20. package/dist/chunk-AGSFUVRG.js +439 -0
  21. package/dist/chunk-GD35BJSH.js +177 -0
  22. package/dist/chunk-HNLU36CC.js +702 -0
  23. package/dist/{chunk-M2FF7ETI.js → chunk-J6OLLWVT.js} +1 -1
  24. package/dist/chunk-LREP5CZP.js +146 -0
  25. package/dist/chunk-M54HVABG.js +34 -0
  26. package/dist/{chunk-S45VXJEO.js → chunk-OPFFFAQC.js} +19 -1
  27. package/dist/chunk-VGOIHW7D.js +1529 -0
  28. package/dist/chunk-YIHDW7JC.js +314 -0
  29. package/dist/config-D3QBUN2Y.js +13 -0
  30. package/dist/{config-ZFU7TSU2.js → config-K2XM6D4Z.js} +3 -2
  31. package/dist/{event-bus-Q3WCETQQ.js → event-bus-MO5SFUME.js} +1 -0
  32. package/dist/index.js +3251 -1199
  33. package/dist/lib-2XISBYT3.js +144950 -0
  34. package/dist/lib-HCGLI2GJ.js +4161 -0
  35. package/dist/{telemetry-service-OHU5NKON.js → telemetry-service-76YPOPDM.js} +8 -4
  36. package/package.json +5 -2
@@ -0,0 +1,642 @@
1
+ import {
2
+ AbstractID3Parser
3
+ } from "./chunk-3JKZUGPJ.js";
4
+ import "./chunk-65JVFJ3X.js";
5
+ import "./chunk-2B4QMSZW.js";
6
+ import "./chunk-YIHDW7JC.js";
7
+ import {
8
+ EndOfStreamError
9
+ } from "./chunk-HNLU36CC.js";
10
+ import "./chunk-LREP5CZP.js";
11
+ import {
12
+ getBitAllignedNumber,
13
+ isBitSet,
14
+ stripNulls
15
+ } from "./chunk-GD35BJSH.js";
16
+ import {
17
+ INT16_BE,
18
+ StringType,
19
+ UINT16_BE,
20
+ UINT32_BE,
21
+ UINT8,
22
+ Uint8ArrayType,
23
+ makeUnexpectedFileContentError,
24
+ require_src
25
+ } from "./chunk-VGOIHW7D.js";
26
+ import {
27
+ __toESM
28
+ } from "./chunk-4VNS5WPM.js";
29
+
30
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mpeg/MpegParser.js
31
+ var import_debug = __toESM(require_src(), 1);
32
+
33
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mpeg/ReplayGainDataFormat.js
34
+ var ReplayGain = {
35
+ len: 2,
36
+ get: (buf, off) => {
37
+ const gain_type = getBitAllignedNumber(buf, off, 0, 3);
38
+ const sign = getBitAllignedNumber(buf, off, 6, 1);
39
+ const gain_adj = getBitAllignedNumber(buf, off, 7, 9) / 10;
40
+ if (gain_type > 0) {
41
+ return {
42
+ type: getBitAllignedNumber(buf, off, 0, 3),
43
+ origin: getBitAllignedNumber(buf, off, 3, 3),
44
+ adjustment: sign ? -gain_adj : gain_adj
45
+ };
46
+ }
47
+ return void 0;
48
+ }
49
+ };
50
+
51
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mpeg/ExtendedLameHeader.js
52
+ var ExtendedLameHeader = {
53
+ len: 27,
54
+ get: (buf, off) => {
55
+ const track_peak = UINT32_BE.get(buf, off + 2);
56
+ return {
57
+ revision: getBitAllignedNumber(buf, off, 0, 4),
58
+ vbr_method: getBitAllignedNumber(buf, off, 4, 4),
59
+ lowpass_filter: 100 * UINT8.get(buf, off + 1),
60
+ track_peak: track_peak === 0 ? null : track_peak / 2 ** 23,
61
+ track_gain: ReplayGain.get(buf, 6),
62
+ album_gain: ReplayGain.get(buf, 8),
63
+ music_length: UINT32_BE.get(buf, off + 20),
64
+ music_crc: UINT8.get(buf, off + 24),
65
+ header_crc: UINT16_BE.get(buf, off + 24)
66
+ };
67
+ }
68
+ };
69
+
70
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mpeg/XingTag.js
71
+ var InfoTagHeaderTag = new StringType(4, "ascii");
72
+ var LameEncoderVersion = new StringType(6, "ascii");
73
+ var XingHeaderFlags = {
74
+ len: 4,
75
+ get: (buf, off) => {
76
+ return {
77
+ frames: isBitSet(buf, off, 31),
78
+ bytes: isBitSet(buf, off, 30),
79
+ toc: isBitSet(buf, off, 29),
80
+ vbrScale: isBitSet(buf, off, 28)
81
+ };
82
+ }
83
+ };
84
+ async function readXingHeader(tokenizer) {
85
+ const flags = await tokenizer.readToken(XingHeaderFlags);
86
+ const xingInfoTag = { numFrames: null, streamSize: null, vbrScale: null };
87
+ if (flags.frames) {
88
+ xingInfoTag.numFrames = await tokenizer.readToken(UINT32_BE);
89
+ }
90
+ if (flags.bytes) {
91
+ xingInfoTag.streamSize = await tokenizer.readToken(UINT32_BE);
92
+ }
93
+ if (flags.toc) {
94
+ xingInfoTag.toc = new Uint8Array(100);
95
+ await tokenizer.readBuffer(xingInfoTag.toc);
96
+ }
97
+ if (flags.vbrScale) {
98
+ xingInfoTag.vbrScale = await tokenizer.readToken(UINT32_BE);
99
+ }
100
+ const lameTag = await tokenizer.peekToken(new StringType(4, "ascii"));
101
+ if (lameTag === "LAME") {
102
+ await tokenizer.ignore(4);
103
+ xingInfoTag.lame = {
104
+ version: await tokenizer.readToken(new StringType(5, "ascii"))
105
+ };
106
+ const match = xingInfoTag.lame.version.match(/\d+.\d+/g);
107
+ if (match !== null) {
108
+ const majorMinorVersion = match[0];
109
+ const version = majorMinorVersion.split(".").map((n) => Number.parseInt(n, 10));
110
+ if (version[0] >= 3 && version[1] >= 90) {
111
+ xingInfoTag.lame.extended = await tokenizer.readToken(ExtendedLameHeader);
112
+ }
113
+ }
114
+ }
115
+ return xingInfoTag;
116
+ }
117
+
118
+ // ../../node_modules/.pnpm/music-metadata@11.12.0/node_modules/music-metadata/lib/mpeg/MpegParser.js
119
+ var debug = (0, import_debug.default)("music-metadata:parser:mpeg");
120
+ var MpegContentError = class extends makeUnexpectedFileContentError("MPEG") {
121
+ };
122
+ var maxPeekLen = 1024;
123
+ var MPEG4 = {
124
+ /**
125
+ * Audio Object Types
126
+ */
127
+ AudioObjectTypes: [
128
+ "AAC Main",
129
+ "AAC LC",
130
+ // Low Complexity
131
+ "AAC SSR",
132
+ // Scalable Sample Rate
133
+ "AAC LTP"
134
+ // Long Term Prediction
135
+ ],
136
+ /**
137
+ * Sampling Frequencies
138
+ * https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Sampling_Frequencies
139
+ */
140
+ SamplingFrequencies: [
141
+ 96e3,
142
+ 88200,
143
+ 64e3,
144
+ 48e3,
145
+ 44100,
146
+ 32e3,
147
+ 24e3,
148
+ 22050,
149
+ 16e3,
150
+ 12e3,
151
+ 11025,
152
+ 8e3,
153
+ 7350,
154
+ null,
155
+ null,
156
+ -1
157
+ ]
158
+ /**
159
+ * Channel Configurations
160
+ */
161
+ };
162
+ var MPEG4_ChannelConfigurations = [
163
+ void 0,
164
+ ["front-center"],
165
+ ["front-left", "front-right"],
166
+ ["front-center", "front-left", "front-right"],
167
+ ["front-center", "front-left", "front-right", "back-center"],
168
+ ["front-center", "front-left", "front-right", "back-left", "back-right"],
169
+ ["front-center", "front-left", "front-right", "back-left", "back-right", "LFE-channel"],
170
+ ["front-center", "front-left", "front-right", "side-left", "side-right", "back-left", "back-right", "LFE-channel"]
171
+ ];
172
+ var MpegFrameHeader = class _MpegFrameHeader {
173
+ constructor(buf, off) {
174
+ this.bitrateIndex = null;
175
+ this.sampRateFreqIndex = null;
176
+ this.padding = null;
177
+ this.privateBit = null;
178
+ this.channelModeIndex = null;
179
+ this.modeExtension = null;
180
+ this.isOriginalMedia = null;
181
+ this.version = null;
182
+ this.bitrate = null;
183
+ this.samplingRate = null;
184
+ this.frameLength = 0;
185
+ this.versionIndex = getBitAllignedNumber(buf, off + 1, 3, 2);
186
+ this.layer = _MpegFrameHeader.LayerDescription[getBitAllignedNumber(buf, off + 1, 5, 2)];
187
+ if (this.versionIndex > 1 && this.layer === 0) {
188
+ this.parseAdtsHeader(buf, off);
189
+ } else {
190
+ this.parseMpegHeader(buf, off);
191
+ }
192
+ this.isProtectedByCRC = !isBitSet(buf, off + 1, 7);
193
+ }
194
+ calcDuration(numFrames) {
195
+ return this.samplingRate == null ? null : numFrames * this.calcSamplesPerFrame() / this.samplingRate;
196
+ }
197
+ calcSamplesPerFrame() {
198
+ return _MpegFrameHeader.samplesInFrameTable[this.version === 1 ? 0 : 1][this.layer];
199
+ }
200
+ calculateSideInfoLength() {
201
+ if (this.layer !== 3)
202
+ return 2;
203
+ if (this.channelModeIndex === 3) {
204
+ if (this.version === 1) {
205
+ return 17;
206
+ }
207
+ if (this.version === 2 || this.version === 2.5) {
208
+ return 9;
209
+ }
210
+ } else {
211
+ if (this.version === 1) {
212
+ return 32;
213
+ }
214
+ if (this.version === 2 || this.version === 2.5) {
215
+ return 17;
216
+ }
217
+ }
218
+ return null;
219
+ }
220
+ calcSlotSize() {
221
+ return [null, 4, 1, 1][this.layer];
222
+ }
223
+ parseMpegHeader(buf, off) {
224
+ this.container = "MPEG";
225
+ this.bitrateIndex = getBitAllignedNumber(buf, off + 2, 0, 4);
226
+ this.sampRateFreqIndex = getBitAllignedNumber(buf, off + 2, 4, 2);
227
+ this.padding = isBitSet(buf, off + 2, 6);
228
+ this.privateBit = isBitSet(buf, off + 2, 7);
229
+ this.channelModeIndex = getBitAllignedNumber(buf, off + 3, 0, 2);
230
+ this.modeExtension = getBitAllignedNumber(buf, off + 3, 2, 2);
231
+ this.isCopyrighted = isBitSet(buf, off + 3, 4);
232
+ this.isOriginalMedia = isBitSet(buf, off + 3, 5);
233
+ this.emphasis = getBitAllignedNumber(buf, off + 3, 7, 2);
234
+ this.version = _MpegFrameHeader.VersionID[this.versionIndex];
235
+ this.channelMode = _MpegFrameHeader.ChannelMode[this.channelModeIndex];
236
+ this.codec = `MPEG ${this.version} Layer ${this.layer}`;
237
+ const bitrateInKbps = this.calcBitrate();
238
+ if (!bitrateInKbps) {
239
+ throw new MpegContentError("Cannot determine bit-rate");
240
+ }
241
+ this.bitrate = bitrateInKbps * 1e3;
242
+ this.samplingRate = this.calcSamplingRate();
243
+ if (this.samplingRate == null) {
244
+ throw new MpegContentError("Cannot determine sampling-rate");
245
+ }
246
+ }
247
+ parseAdtsHeader(buf, off) {
248
+ debug("layer=0 => ADTS");
249
+ this.version = this.versionIndex === 2 ? 4 : 2;
250
+ this.container = `ADTS/MPEG-${this.version}`;
251
+ const profileIndex = getBitAllignedNumber(buf, off + 2, 0, 2);
252
+ this.codec = "AAC";
253
+ this.codecProfile = MPEG4.AudioObjectTypes[profileIndex];
254
+ debug(`MPEG-4 audio-codec=${this.codec}`);
255
+ const samplingFrequencyIndex = getBitAllignedNumber(buf, off + 2, 2, 4);
256
+ this.samplingRate = MPEG4.SamplingFrequencies[samplingFrequencyIndex];
257
+ debug(`sampling-rate=${this.samplingRate}`);
258
+ const channelIndex = getBitAllignedNumber(buf, off + 2, 7, 3);
259
+ this.mp4ChannelConfig = MPEG4_ChannelConfigurations[channelIndex];
260
+ debug(`channel-config=${this.mp4ChannelConfig ? this.mp4ChannelConfig.join("+") : "?"}`);
261
+ this.frameLength = getBitAllignedNumber(buf, off + 3, 6, 2) << 11;
262
+ }
263
+ calcBitrate() {
264
+ if (this.bitrateIndex === 0 || // free
265
+ this.bitrateIndex === 15) {
266
+ return null;
267
+ }
268
+ if (this.version && this.bitrateIndex) {
269
+ const codecIndex = 10 * Math.floor(this.version) + this.layer;
270
+ return _MpegFrameHeader.bitrate_index[this.bitrateIndex][codecIndex];
271
+ }
272
+ return null;
273
+ }
274
+ calcSamplingRate() {
275
+ if (this.sampRateFreqIndex === 3 || this.version === null || this.sampRateFreqIndex == null)
276
+ return null;
277
+ return _MpegFrameHeader.sampling_rate_freq_index[this.version][this.sampRateFreqIndex];
278
+ }
279
+ };
280
+ MpegFrameHeader.SyncByte1 = 255;
281
+ MpegFrameHeader.SyncByte2 = 224;
282
+ MpegFrameHeader.VersionID = [2.5, null, 2, 1];
283
+ MpegFrameHeader.LayerDescription = [0, 3, 2, 1];
284
+ MpegFrameHeader.ChannelMode = ["stereo", "joint_stereo", "dual_channel", "mono"];
285
+ MpegFrameHeader.bitrate_index = {
286
+ 1: { 11: 32, 12: 32, 13: 32, 21: 32, 22: 8, 23: 8 },
287
+ 2: { 11: 64, 12: 48, 13: 40, 21: 48, 22: 16, 23: 16 },
288
+ 3: { 11: 96, 12: 56, 13: 48, 21: 56, 22: 24, 23: 24 },
289
+ 4: { 11: 128, 12: 64, 13: 56, 21: 64, 22: 32, 23: 32 },
290
+ 5: { 11: 160, 12: 80, 13: 64, 21: 80, 22: 40, 23: 40 },
291
+ 6: { 11: 192, 12: 96, 13: 80, 21: 96, 22: 48, 23: 48 },
292
+ 7: { 11: 224, 12: 112, 13: 96, 21: 112, 22: 56, 23: 56 },
293
+ 8: { 11: 256, 12: 128, 13: 112, 21: 128, 22: 64, 23: 64 },
294
+ 9: { 11: 288, 12: 160, 13: 128, 21: 144, 22: 80, 23: 80 },
295
+ 10: { 11: 320, 12: 192, 13: 160, 21: 160, 22: 96, 23: 96 },
296
+ 11: { 11: 352, 12: 224, 13: 192, 21: 176, 22: 112, 23: 112 },
297
+ 12: { 11: 384, 12: 256, 13: 224, 21: 192, 22: 128, 23: 128 },
298
+ 13: { 11: 416, 12: 320, 13: 256, 21: 224, 22: 144, 23: 144 },
299
+ 14: { 11: 448, 12: 384, 13: 320, 21: 256, 22: 160, 23: 160 }
300
+ };
301
+ MpegFrameHeader.sampling_rate_freq_index = {
302
+ 1: { 0: 44100, 1: 48e3, 2: 32e3 },
303
+ 2: { 0: 22050, 1: 24e3, 2: 16e3 },
304
+ 2.5: { 0: 11025, 1: 12e3, 2: 8e3 }
305
+ };
306
+ MpegFrameHeader.samplesInFrameTable = [
307
+ /* Layer I II III */
308
+ [0, 384, 1152, 1152],
309
+ // MPEG-1
310
+ [0, 384, 1152, 576]
311
+ // MPEG-2(.5
312
+ ];
313
+ var FrameHeader = {
314
+ len: 4,
315
+ get: (buf, off) => {
316
+ return new MpegFrameHeader(buf, off);
317
+ }
318
+ };
319
+ function getVbrCodecProfile(vbrScale) {
320
+ return `V${Math.floor((100 - vbrScale) / 10)}`;
321
+ }
322
+ var MpegParser = class extends AbstractID3Parser {
323
+ constructor() {
324
+ super(...arguments);
325
+ this.frameCount = 0;
326
+ this.syncFrameCount = -1;
327
+ this.totalDataLength = 0;
328
+ this.bitrates = [];
329
+ this.offset = 0;
330
+ this.frame_size = 0;
331
+ this.calculateEofDuration = false;
332
+ this.samplesPerFrame = null;
333
+ this.buf_frame_header = new Uint8Array(4);
334
+ this.mpegOffset = null;
335
+ this.syncPeek = {
336
+ buf: new Uint8Array(maxPeekLen),
337
+ len: 0
338
+ };
339
+ }
340
+ /**
341
+ * Called after ID3 headers have been parsed
342
+ */
343
+ async postId3v2Parse() {
344
+ this.metadata.setFormat("lossless", false);
345
+ this.metadata.setAudioOnly();
346
+ try {
347
+ let quit = false;
348
+ while (!quit) {
349
+ await this.sync();
350
+ quit = await this.parseCommonMpegHeader();
351
+ }
352
+ } catch (err) {
353
+ if (err instanceof EndOfStreamError) {
354
+ debug("End-of-stream");
355
+ if (this.calculateEofDuration) {
356
+ if (this.samplesPerFrame !== null) {
357
+ const numberOfSamples = this.frameCount * this.samplesPerFrame;
358
+ this.metadata.setFormat("numberOfSamples", numberOfSamples);
359
+ if (this.metadata.format.sampleRate) {
360
+ const duration = numberOfSamples / this.metadata.format.sampleRate;
361
+ debug(`Calculate duration at EOF: ${duration} sec.`, duration);
362
+ this.metadata.setFormat("duration", duration);
363
+ }
364
+ }
365
+ }
366
+ } else {
367
+ throw err;
368
+ }
369
+ }
370
+ }
371
+ /**
372
+ * Called after file has been fully parsed, this allows, if present, to exclude the ID3v1.1 header length
373
+ */
374
+ finalize() {
375
+ const format = this.metadata.format;
376
+ const hasID3v1 = !!this.metadata.native.ID3v1;
377
+ if (this.mpegOffset !== null) {
378
+ if (format.duration && this.tokenizer.fileInfo.size) {
379
+ const mpegSize = this.tokenizer.fileInfo.size - this.mpegOffset - (hasID3v1 ? 128 : 0);
380
+ if (format.codecProfile && format.codecProfile[0] === "V") {
381
+ this.metadata.setFormat("bitrate", mpegSize * 8 / format.duration);
382
+ }
383
+ }
384
+ if (this.tokenizer.fileInfo.size && format.codecProfile === "CBR") {
385
+ const mpegSize = this.tokenizer.fileInfo.size - this.mpegOffset - (hasID3v1 ? 128 : 0);
386
+ if (this.frame_size !== null && this.samplesPerFrame !== null) {
387
+ const numberOfSamples = Math.round(mpegSize / this.frame_size) * this.samplesPerFrame;
388
+ this.metadata.setFormat("numberOfSamples", numberOfSamples);
389
+ if (format.sampleRate && !format.duration) {
390
+ const duration = numberOfSamples / format.sampleRate;
391
+ debug("Calculate CBR duration based on file size: %s", duration);
392
+ this.metadata.setFormat("duration", duration);
393
+ }
394
+ }
395
+ }
396
+ }
397
+ }
398
+ async sync() {
399
+ let gotFirstSync = false;
400
+ while (true) {
401
+ let bo = 0;
402
+ this.syncPeek.len = await this.tokenizer.peekBuffer(this.syncPeek.buf, { length: maxPeekLen, mayBeLess: true });
403
+ if (this.syncPeek.len <= 163) {
404
+ throw new EndOfStreamError();
405
+ }
406
+ while (true) {
407
+ if (gotFirstSync && (this.syncPeek.buf[bo] & 224) === 224) {
408
+ this.buf_frame_header[0] = MpegFrameHeader.SyncByte1;
409
+ this.buf_frame_header[1] = this.syncPeek.buf[bo];
410
+ await this.tokenizer.ignore(bo);
411
+ debug(`Sync at offset=${this.tokenizer.position - 1}, frameCount=${this.frameCount}`);
412
+ if (this.syncFrameCount === this.frameCount) {
413
+ debug(`Re-synced MPEG stream, frameCount=${this.frameCount}`);
414
+ this.frameCount = 0;
415
+ this.frame_size = 0;
416
+ }
417
+ this.syncFrameCount = this.frameCount;
418
+ return;
419
+ }
420
+ gotFirstSync = false;
421
+ bo = this.syncPeek.buf.indexOf(MpegFrameHeader.SyncByte1, bo);
422
+ if (bo === -1) {
423
+ if (this.syncPeek.len < this.syncPeek.buf.length) {
424
+ throw new EndOfStreamError();
425
+ }
426
+ await this.tokenizer.ignore(this.syncPeek.len);
427
+ break;
428
+ }
429
+ ++bo;
430
+ gotFirstSync = true;
431
+ }
432
+ }
433
+ }
434
+ /**
435
+ * Combined ADTS & MPEG (MP2 & MP3) header handling
436
+ * @return {Promise<boolean>} true if parser should quit
437
+ */
438
+ async parseCommonMpegHeader() {
439
+ if (this.frameCount === 0) {
440
+ this.mpegOffset = this.tokenizer.position - 1;
441
+ }
442
+ await this.tokenizer.peekBuffer(this.buf_frame_header.subarray(1), { length: 3 });
443
+ let header;
444
+ try {
445
+ header = FrameHeader.get(this.buf_frame_header, 0);
446
+ } catch (err) {
447
+ await this.tokenizer.ignore(1);
448
+ if (err instanceof Error) {
449
+ this.metadata.addWarning(`Parse error: ${err.message}`);
450
+ return false;
451
+ }
452
+ throw err;
453
+ }
454
+ await this.tokenizer.ignore(3);
455
+ this.metadata.setFormat("container", header.container);
456
+ this.metadata.setFormat("codec", header.codec);
457
+ this.metadata.setFormat("lossless", false);
458
+ this.metadata.setFormat("sampleRate", header.samplingRate);
459
+ this.frameCount++;
460
+ return header.version !== null && header.version >= 2 && header.layer === 0 ? this.parseAdts(header) : this.parseAudioFrameHeader(header);
461
+ }
462
+ /**
463
+ * @return {Promise<boolean>} true if parser should quit
464
+ */
465
+ async parseAudioFrameHeader(header) {
466
+ this.metadata.setFormat("numberOfChannels", header.channelMode === "mono" ? 1 : 2);
467
+ this.metadata.setFormat("bitrate", header.bitrate);
468
+ if (this.frameCount < 20 * 1e4) {
469
+ debug("offset=%s MP%s bitrate=%s sample-rate=%s", this.tokenizer.position - 4, header.layer, header.bitrate, header.samplingRate);
470
+ }
471
+ const slot_size = header.calcSlotSize();
472
+ if (slot_size === null) {
473
+ throw new MpegContentError("invalid slot_size");
474
+ }
475
+ const samples_per_frame = header.calcSamplesPerFrame();
476
+ debug(`samples_per_frame=${samples_per_frame}`);
477
+ const bps = samples_per_frame / 8;
478
+ if (header.bitrate !== null && header.samplingRate != null) {
479
+ const fsize = bps * header.bitrate / header.samplingRate + (header.padding ? slot_size : 0);
480
+ this.frame_size = Math.floor(fsize);
481
+ }
482
+ this.audioFrameHeader = header;
483
+ if (header.bitrate !== null) {
484
+ this.bitrates.push(header.bitrate);
485
+ }
486
+ if (this.frameCount === 1) {
487
+ this.offset = FrameHeader.len;
488
+ await this.skipSideInformation();
489
+ return false;
490
+ }
491
+ if (this.frameCount === 4) {
492
+ if (this.areAllSame(this.bitrates)) {
493
+ this.samplesPerFrame = samples_per_frame;
494
+ this.metadata.setFormat("codecProfile", "CBR");
495
+ if (this.tokenizer.fileInfo.size)
496
+ return true;
497
+ } else if (this.metadata.format.duration) {
498
+ return true;
499
+ }
500
+ if (!this.options.duration) {
501
+ return true;
502
+ }
503
+ }
504
+ if (this.options.duration && this.frameCount === 4) {
505
+ this.samplesPerFrame = samples_per_frame;
506
+ this.calculateEofDuration = true;
507
+ }
508
+ this.offset = 4;
509
+ if (header.isProtectedByCRC) {
510
+ await this.parseCrc();
511
+ return false;
512
+ }
513
+ await this.skipSideInformation();
514
+ return false;
515
+ }
516
+ async parseAdts(header) {
517
+ const buf = new Uint8Array(3);
518
+ await this.tokenizer.readBuffer(buf);
519
+ header.frameLength += getBitAllignedNumber(buf, 0, 0, 11);
520
+ this.totalDataLength += header.frameLength;
521
+ this.samplesPerFrame = 1024;
522
+ if (header.samplingRate !== null) {
523
+ const framesPerSec = header.samplingRate / this.samplesPerFrame;
524
+ const bytesPerFrame = this.frameCount === 0 ? 0 : this.totalDataLength / this.frameCount;
525
+ const bitrate = 8 * bytesPerFrame * framesPerSec + 0.5;
526
+ this.metadata.setFormat("bitrate", bitrate);
527
+ debug(`frame-count=${this.frameCount}, size=${header.frameLength} bytes, bit-rate=${bitrate}`);
528
+ }
529
+ await this.tokenizer.ignore(header.frameLength > 7 ? header.frameLength - 7 : 1);
530
+ if (this.frameCount === 3) {
531
+ this.metadata.setFormat("codecProfile", header.codecProfile);
532
+ if (header.mp4ChannelConfig) {
533
+ this.metadata.setFormat("numberOfChannels", header.mp4ChannelConfig.length);
534
+ }
535
+ if (this.options.duration) {
536
+ this.calculateEofDuration = true;
537
+ } else {
538
+ return true;
539
+ }
540
+ }
541
+ return false;
542
+ }
543
+ async parseCrc() {
544
+ await this.tokenizer.ignore(INT16_BE.len);
545
+ this.offset += INT16_BE.len;
546
+ return this.skipSideInformation();
547
+ }
548
+ async skipSideInformation() {
549
+ if (this.audioFrameHeader) {
550
+ const sideinfo_length = this.audioFrameHeader.calculateSideInfoLength();
551
+ if (sideinfo_length !== null) {
552
+ await this.tokenizer.readToken(new Uint8ArrayType(sideinfo_length));
553
+ this.offset += sideinfo_length;
554
+ await this.readXtraInfoHeader();
555
+ return;
556
+ }
557
+ }
558
+ }
559
+ async readXtraInfoHeader() {
560
+ const headerTag = await this.tokenizer.readToken(InfoTagHeaderTag);
561
+ this.offset += InfoTagHeaderTag.len;
562
+ switch (headerTag) {
563
+ case "Info":
564
+ this.metadata.setFormat("codecProfile", "CBR");
565
+ return this.readXingInfoHeader();
566
+ case "Xing": {
567
+ const infoTag = await this.readXingInfoHeader();
568
+ if (infoTag.vbrScale !== null) {
569
+ const codecProfile = getVbrCodecProfile(infoTag.vbrScale);
570
+ this.metadata.setFormat("codecProfile", codecProfile);
571
+ }
572
+ return null;
573
+ }
574
+ case "Xtra":
575
+ break;
576
+ case "LAME": {
577
+ const version = await this.tokenizer.readToken(LameEncoderVersion);
578
+ if (this.frame_size !== null && this.frame_size >= this.offset + LameEncoderVersion.len) {
579
+ this.offset += LameEncoderVersion.len;
580
+ this.metadata.setFormat("tool", `LAME ${version}`);
581
+ await this.skipFrameData(this.frame_size - this.offset);
582
+ return null;
583
+ }
584
+ this.metadata.addWarning("Corrupt LAME header");
585
+ break;
586
+ }
587
+ }
588
+ const frameDataLeft = this.frame_size - this.offset;
589
+ if (frameDataLeft < 0) {
590
+ this.metadata.addWarning(`Frame ${this.frameCount}corrupt: negative frameDataLeft`);
591
+ } else {
592
+ await this.skipFrameData(frameDataLeft);
593
+ }
594
+ return null;
595
+ }
596
+ /**
597
+ * Ref: http://gabriel.mp3-tech.org/mp3infotag.html
598
+ * @returns {Promise<string>}
599
+ */
600
+ async readXingInfoHeader() {
601
+ const offset = this.tokenizer.position;
602
+ const infoTag = await readXingHeader(this.tokenizer);
603
+ this.offset += this.tokenizer.position - offset;
604
+ if (infoTag.lame) {
605
+ this.metadata.setFormat("tool", `LAME ${stripNulls(infoTag.lame.version)}`);
606
+ if (infoTag.lame.extended) {
607
+ this.metadata.setFormat("trackPeakLevel", infoTag.lame.extended.track_peak);
608
+ if (infoTag.lame.extended.track_gain) {
609
+ this.metadata.setFormat("trackGain", infoTag.lame.extended.track_gain.adjustment);
610
+ }
611
+ if (infoTag.lame.extended.album_gain) {
612
+ this.metadata.setFormat("albumGain", infoTag.lame.extended.album_gain.adjustment);
613
+ }
614
+ this.metadata.setFormat("duration", infoTag.lame.extended.music_length / 1e3);
615
+ }
616
+ }
617
+ if (infoTag.streamSize && this.audioFrameHeader && infoTag.numFrames !== null) {
618
+ const duration = this.audioFrameHeader.calcDuration(infoTag.numFrames);
619
+ this.metadata.setFormat("duration", duration);
620
+ debug("Get duration from Xing header: %s", this.metadata.format.duration);
621
+ return infoTag;
622
+ }
623
+ const frameDataLeft = this.frame_size - this.offset;
624
+ await this.skipFrameData(frameDataLeft);
625
+ return infoTag;
626
+ }
627
+ async skipFrameData(frameDataLeft) {
628
+ if (frameDataLeft < 0)
629
+ throw new MpegContentError("frame-data-left cannot be negative");
630
+ await this.tokenizer.ignore(frameDataLeft);
631
+ }
632
+ areAllSame(array) {
633
+ const first = array[0];
634
+ return array.every((element) => {
635
+ return element === first;
636
+ });
637
+ }
638
+ };
639
+ export {
640
+ MpegContentError,
641
+ MpegParser
642
+ };