music-metadata 10.0.1 → 10.2.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.
Files changed (127) hide show
  1. package/LICENSE.txt +9 -9
  2. package/README.md +535 -520
  3. package/lib/ParserFactory.d.ts +20 -28
  4. package/lib/ParserFactory.js +204 -207
  5. package/lib/aiff/AiffParser.js +20 -19
  6. package/lib/aiff/AiffToken.d.ts +14 -2
  7. package/lib/aiff/AiffToken.js +12 -0
  8. package/lib/apev2/APEv2Parser.d.ts +4 -4
  9. package/lib/apev2/APEv2Parser.js +12 -10
  10. package/lib/apev2/APEv2Token.d.ts +2 -4
  11. package/lib/apev2/APEv2Token.js +0 -3
  12. package/lib/asf/AsfObject.d.ts +7 -16
  13. package/lib/asf/AsfObject.js +8 -34
  14. package/lib/asf/AsfParser.js +17 -10
  15. package/lib/asf/AsfTagMapper.d.ts +1 -1
  16. package/lib/asf/AsfTagMapper.js +3 -2
  17. package/lib/asf/AsfUtil.d.ts +3 -11
  18. package/lib/asf/AsfUtil.js +29 -30
  19. package/lib/asf/GUID.js +6 -9
  20. package/lib/common/BasicParser.d.ts +4 -4
  21. package/lib/common/BasicParser.js +6 -0
  22. package/lib/common/CaseInsensitiveTagMap.d.ts +1 -1
  23. package/lib/common/CombinedTagMapper.d.ts +5 -5
  24. package/lib/common/CombinedTagMapper.js +1 -1
  25. package/lib/common/FourCC.d.ts +1 -1
  26. package/lib/common/GenericTagMapper.d.ts +8 -8
  27. package/lib/common/GenericTagMapper.js +4 -4
  28. package/lib/common/GenericTagTypes.d.ts +5 -4
  29. package/lib/common/GenericTagTypes.js +2 -2
  30. package/lib/common/MetadataCollector.d.ts +9 -9
  31. package/lib/common/MetadataCollector.js +22 -17
  32. package/lib/common/RandomFileReader.d.ts +1 -1
  33. package/lib/common/RandomFileReader.js +1 -1
  34. package/lib/common/RandomUint8ArrayReader.d.ts +1 -1
  35. package/lib/common/Util.d.ts +2 -6
  36. package/lib/common/Util.js +9 -11
  37. package/lib/core.d.ts +7 -9
  38. package/lib/core.js +10 -7
  39. package/lib/dsdiff/DsdiffParser.js +26 -14
  40. package/lib/dsdiff/DsdiffToken.d.ts +2 -2
  41. package/lib/dsdiff/DsdiffToken.js +1 -0
  42. package/lib/dsf/DsfChunk.js +1 -0
  43. package/lib/dsf/DsfParser.js +4 -2
  44. package/lib/ebml/EbmlIterator.d.ts +52 -0
  45. package/lib/ebml/EbmlIterator.js +220 -0
  46. package/lib/ebml/types.d.ts +36 -0
  47. package/lib/ebml/types.js +10 -0
  48. package/lib/flac/FlacParser.d.ts +3 -3
  49. package/lib/flac/FlacParser.js +9 -12
  50. package/lib/id3v1/ID3v1Parser.d.ts +1 -1
  51. package/lib/id3v1/ID3v1Parser.js +7 -4
  52. package/lib/id3v2/AbstractID3Parser.d.ts +1 -1
  53. package/lib/id3v2/AbstractID3Parser.js +2 -1
  54. package/lib/id3v2/FrameParser.d.ts +28 -3
  55. package/lib/id3v2/FrameParser.js +50 -28
  56. package/lib/id3v2/ID3v22TagMapper.d.ts +1 -1
  57. package/lib/id3v2/ID3v24TagMapper.d.ts +2 -1
  58. package/lib/id3v2/ID3v24TagMapper.js +23 -16
  59. package/lib/id3v2/ID3v2Parser.d.ts +2 -2
  60. package/lib/id3v2/ID3v2Parser.js +18 -8
  61. package/lib/iff/index.js +1 -0
  62. package/lib/index.d.ts +5 -6
  63. package/lib/index.js +7 -8
  64. package/lib/lyrics3/Lyrics3.d.ts +1 -1
  65. package/lib/lyrics3/Lyrics3.js +1 -1
  66. package/lib/matroska/MatroskaDtd.d.ts +2 -2
  67. package/lib/matroska/MatroskaDtd.js +247 -239
  68. package/lib/matroska/MatroskaParser.d.ts +10 -24
  69. package/lib/matroska/MatroskaParser.js +120 -205
  70. package/lib/matroska/types.d.ts +12 -46
  71. package/lib/matroska/types.js +0 -9
  72. package/lib/mp4/Atom.d.ts +3 -3
  73. package/lib/mp4/Atom.js +4 -7
  74. package/lib/mp4/AtomToken.js +2 -1
  75. package/lib/mp4/MP4Parser.js +29 -20
  76. package/lib/mp4/MP4TagMapper.d.ts +2 -2
  77. package/lib/mp4/MP4TagMapper.js +1 -1
  78. package/lib/mpeg/ExtendedLameHeader.d.ts +4 -4
  79. package/lib/mpeg/ExtendedLameHeader.js +2 -1
  80. package/lib/mpeg/MpegParser.d.ts +1 -1
  81. package/lib/mpeg/MpegParser.js +145 -96
  82. package/lib/mpeg/ReplayGainDataFormat.d.ts +1 -1
  83. package/lib/mpeg/ReplayGainDataFormat.js +1 -0
  84. package/lib/mpeg/XingTag.d.ts +4 -4
  85. package/lib/mpeg/XingTag.js +5 -4
  86. package/lib/musepack/index.js +1 -0
  87. package/lib/musepack/sv7/BitReader.js +14 -17
  88. package/lib/musepack/sv7/MpcSv7Parser.js +6 -1
  89. package/lib/musepack/sv7/StreamVersion7.js +1 -0
  90. package/lib/musepack/sv8/MpcSv8Parser.js +6 -2
  91. package/lib/musepack/sv8/StreamVersion8.d.ts +1 -1
  92. package/lib/musepack/sv8/StreamVersion8.js +1 -0
  93. package/lib/ogg/Ogg.d.ts +1 -1
  94. package/lib/ogg/Ogg.js +1 -0
  95. package/lib/ogg/OggParser.d.ts +3 -3
  96. package/lib/ogg/OggParser.js +25 -16
  97. package/lib/ogg/opus/Opus.d.ts +1 -1
  98. package/lib/ogg/opus/Opus.js +1 -0
  99. package/lib/ogg/opus/OpusParser.d.ts +4 -4
  100. package/lib/ogg/opus/OpusParser.js +2 -0
  101. package/lib/ogg/speex/Speex.js +1 -0
  102. package/lib/ogg/speex/SpeexParser.d.ts +4 -4
  103. package/lib/ogg/speex/SpeexParser.js +1 -0
  104. package/lib/ogg/theora/Theora.js +1 -0
  105. package/lib/ogg/theora/TheoraParser.d.ts +4 -4
  106. package/lib/ogg/theora/TheoraParser.js +1 -0
  107. package/lib/ogg/vorbis/Vorbis.d.ts +3 -3
  108. package/lib/ogg/vorbis/Vorbis.js +22 -11
  109. package/lib/ogg/vorbis/VorbisDecoder.d.ts +1 -1
  110. package/lib/ogg/vorbis/VorbisDecoder.js +1 -0
  111. package/lib/ogg/vorbis/VorbisParser.d.ts +4 -4
  112. package/lib/ogg/vorbis/VorbisParser.js +3 -2
  113. package/lib/ogg/vorbis/VorbisTagMapper.d.ts +2 -2
  114. package/lib/ogg/vorbis/VorbisTagMapper.js +2 -2
  115. package/lib/riff/RiffChunk.d.ts +3 -3
  116. package/lib/riff/RiffChunk.js +1 -0
  117. package/lib/riff/RiffInfoTagMap.d.ts +1 -1
  118. package/lib/type.d.ts +44 -25
  119. package/lib/wav/BwfChunk.js +1 -0
  120. package/lib/wav/WaveChunk.d.ts +1 -1
  121. package/lib/wav/WaveChunk.js +1 -0
  122. package/lib/wav/WaveParser.js +27 -17
  123. package/lib/wavpack/WavPackParser.d.ts +1 -1
  124. package/lib/wavpack/WavPackParser.js +22 -13
  125. package/lib/wavpack/WavPackToken.d.ts +13 -17
  126. package/lib/wavpack/WavPackToken.js +22 -23
  127. package/package.json +139 -150
@@ -1,6 +1,6 @@
1
1
  import { CaseInsensitiveTagMap } from '../common/CaseInsensitiveTagMap.js';
2
- import { ITag } from "../type.js";
3
- import { INativeMetadataCollector } from "../common/MetadataCollector.js";
2
+ import type { ITag } from "../type.js";
3
+ import type { INativeMetadataCollector } from "../common/MetadataCollector.js";
4
4
  export declare const tagType = "iTunes";
5
5
  export declare class MP4TagMapper extends CaseInsensitiveTagMap {
6
6
  constructor();
@@ -115,7 +115,7 @@ export class MP4TagMapper extends CaseInsensitiveTagMap {
115
115
  case 'rate':
116
116
  tag.value = {
117
117
  source: undefined,
118
- rating: parseFloat(tag.value) / 100
118
+ rating: Number.parseFloat(tag.value) / 100
119
119
  };
120
120
  break;
121
121
  }
@@ -2,7 +2,7 @@
2
2
  * Extended Lame Header
3
3
  */
4
4
  import type { IGetToken } from 'strtok3';
5
- import { IReplayGain } from './ReplayGainDataFormat.js';
5
+ import { type IReplayGain } from './ReplayGainDataFormat.js';
6
6
  /**
7
7
  * LAME Tag, extends the Xing header format
8
8
  * First added in LAME 3.12 for VBR
@@ -12,9 +12,9 @@ export interface IExtendedLameHeader {
12
12
  revision: number;
13
13
  vbr_method: number;
14
14
  lowpass_filter: number;
15
- track_peak?: number;
16
- track_gain: IReplayGain;
17
- album_gain: IReplayGain;
15
+ track_peak?: number | null;
16
+ track_gain?: IReplayGain;
17
+ album_gain?: IReplayGain;
18
18
  music_length: number;
19
19
  music_crc: number;
20
20
  header_crc: number;
@@ -17,7 +17,7 @@ export const ExtendedLameHeader = {
17
17
  revision: common.getBitAllignedNumber(buf, off, 0, 4),
18
18
  vbr_method: common.getBitAllignedNumber(buf, off, 4, 4),
19
19
  lowpass_filter: 100 * Token.UINT8.get(buf, off + 1),
20
- track_peak: track_peak === 0 ? undefined : track_peak / Math.pow(2, 23),
20
+ track_peak: track_peak === 0 ? null : track_peak / 2 ** 23,
21
21
  track_gain: ReplayGain.get(buf, 6),
22
22
  album_gain: ReplayGain.get(buf, 8),
23
23
  music_length: Token.UINT32_BE.get(buf, off + 20),
@@ -26,3 +26,4 @@ export const ExtendedLameHeader = {
26
26
  };
27
27
  }
28
28
  };
29
+ //# sourceMappingURL=ExtendedLameHeader.js.map
@@ -4,7 +4,7 @@ export declare class MpegParser extends AbstractID3Parser {
4
4
  private syncFrameCount;
5
5
  private countSkipFrameData;
6
6
  private totalDataLength;
7
- private audioFrameHeader;
7
+ private audioFrameHeader?;
8
8
  private bitrates;
9
9
  private offset;
10
10
  private frame_size;
@@ -28,7 +28,7 @@ const MPEG4 = {
28
28
  * https://wiki.multimedia.cx/index.php/MPEG-4_Audio#Sampling_Frequencies
29
29
  */
30
30
  SamplingFrequencies: [
31
- 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, undefined, undefined, -1
31
+ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, null, null, -1
32
32
  ]
33
33
  /**
34
34
  * Channel Configurations
@@ -52,6 +52,24 @@ const MPEG4_ChannelConfigurations = [
52
52
  */
53
53
  class MpegFrameHeader {
54
54
  constructor(buf, off) {
55
+ // E(15,12): Bitrate index
56
+ this.bitrateIndex = null;
57
+ // F(11,10): Sampling rate frequency index
58
+ this.sampRateFreqIndex = null;
59
+ // G(9): Padding bit
60
+ this.padding = null;
61
+ // H(8): Private bit
62
+ this.privateBit = null;
63
+ // I(7,6): Channel Mode
64
+ this.channelModeIndex = null;
65
+ // J(5,4): Mode extension (Only used in Joint stereo)
66
+ this.modeExtension = null;
67
+ // L(2): Original
68
+ this.isOriginalMedia = null;
69
+ this.version = null;
70
+ this.bitrate = null;
71
+ this.samplingRate = null;
72
+ this.frameLength = 0;
55
73
  // B(20,19): MPEG Audio versionIndex ID
56
74
  this.versionIndex = common.getBitAllignedNumber(buf, off + 1, 3, 2);
57
75
  // C(18,17): Layer description
@@ -66,7 +84,7 @@ class MpegFrameHeader {
66
84
  this.isProtectedByCRC = !common.isBitSet(buf, off + 1, 7);
67
85
  }
68
86
  calcDuration(numFrames) {
69
- return numFrames * this.calcSamplesPerFrame() / this.samplingRate;
87
+ return this.samplingRate == null ? null : (numFrames * this.calcSamplesPerFrame() / this.samplingRate);
70
88
  }
71
89
  calcSamplesPerFrame() {
72
90
  return MpegFrameHeader.samplesInFrameTable[this.version === 1 ? 0 : 1][this.layer];
@@ -79,7 +97,7 @@ class MpegFrameHeader {
79
97
  if (this.version === 1) {
80
98
  return 17;
81
99
  }
82
- else if (this.version === 2 || this.version === 2.5) {
100
+ if (this.version === 2 || this.version === 2.5) {
83
101
  return 9;
84
102
  }
85
103
  }
@@ -87,10 +105,11 @@ class MpegFrameHeader {
87
105
  if (this.version === 1) {
88
106
  return 32;
89
107
  }
90
- else if (this.version === 2 || this.version === 2.5) {
108
+ if (this.version === 2 || this.version === 2.5) {
91
109
  return 17;
92
110
  }
93
111
  }
112
+ return null;
94
113
  }
95
114
  calcSlotSize() {
96
115
  return [null, 4, 1, 1][this.layer];
@@ -131,9 +150,9 @@ class MpegFrameHeader {
131
150
  }
132
151
  }
133
152
  parseAdtsHeader(buf, off) {
134
- debug(`layer=0 => ADTS`);
153
+ debug("layer=0 => ADTS");
135
154
  this.version = this.versionIndex === 2 ? 4 : 2;
136
- this.container = 'ADTS/MPEG-' + this.version;
155
+ this.container = `ADTS/MPEG-${this.version}`;
137
156
  const profileIndex = common.getBitAllignedNumber(buf, off + 2, 0, 2);
138
157
  this.codec = 'AAC';
139
158
  this.codecProfile = MPEG4.AudioObjectTypes[profileIndex];
@@ -143,19 +162,22 @@ class MpegFrameHeader {
143
162
  debug(`sampling-rate=${this.samplingRate}`);
144
163
  const channelIndex = common.getBitAllignedNumber(buf, off + 2, 7, 3);
145
164
  this.mp4ChannelConfig = MPEG4_ChannelConfigurations[channelIndex];
146
- debug(`channel-config=${this.mp4ChannelConfig.join('+')}`);
165
+ debug(`channel-config=${this.mp4ChannelConfig ? this.mp4ChannelConfig.join('+') : '?'}`);
147
166
  this.frameLength = common.getBitAllignedNumber(buf, off + 3, 6, 2) << 11;
148
167
  }
149
168
  calcBitrate() {
150
169
  if (this.bitrateIndex === 0x00 || // free
151
170
  this.bitrateIndex === 0x0F) { // reserved
152
- return;
171
+ return null;
153
172
  }
154
- const codecIndex = `${Math.floor(this.version)}${this.layer}`;
155
- return MpegFrameHeader.bitrate_index[this.bitrateIndex][codecIndex];
173
+ if (this.version && this.bitrateIndex) {
174
+ const codecIndex = 10 * Math.floor(this.version) + this.layer;
175
+ return MpegFrameHeader.bitrate_index[this.bitrateIndex][codecIndex];
176
+ }
177
+ return null;
156
178
  }
157
179
  calcSamplingRate() {
158
- if (this.sampRateFreqIndex === 0x03)
180
+ if (this.sampRateFreqIndex === 0x03 || this.version === null || this.sampRateFreqIndex == null)
159
181
  return null; // 'reserved'
160
182
  return MpegFrameHeader.sampling_rate_freq_index[this.version][this.sampRateFreqIndex];
161
183
  }
@@ -166,25 +188,25 @@ MpegFrameHeader.VersionID = [2.5, null, 2, 1];
166
188
  MpegFrameHeader.LayerDescription = [0, 3, 2, 1];
167
189
  MpegFrameHeader.ChannelMode = ['stereo', 'joint_stereo', 'dual_channel', 'mono'];
168
190
  MpegFrameHeader.bitrate_index = {
169
- 0x01: { 11: 32, 12: 32, 13: 32, 21: 32, 22: 8, 23: 8 },
170
- 0x02: { 11: 64, 12: 48, 13: 40, 21: 48, 22: 16, 23: 16 },
171
- 0x03: { 11: 96, 12: 56, 13: 48, 21: 56, 22: 24, 23: 24 },
172
- 0x04: { 11: 128, 12: 64, 13: 56, 21: 64, 22: 32, 23: 32 },
173
- 0x05: { 11: 160, 12: 80, 13: 64, 21: 80, 22: 40, 23: 40 },
174
- 0x06: { 11: 192, 12: 96, 13: 80, 21: 96, 22: 48, 23: 48 },
175
- 0x07: { 11: 224, 12: 112, 13: 96, 21: 112, 22: 56, 23: 56 },
176
- 0x08: { 11: 256, 12: 128, 13: 112, 21: 128, 22: 64, 23: 64 },
177
- 0x09: { 11: 288, 12: 160, 13: 128, 21: 144, 22: 80, 23: 80 },
178
- 0x0A: { 11: 320, 12: 192, 13: 160, 21: 160, 22: 96, 23: 96 },
179
- 0x0B: { 11: 352, 12: 224, 13: 192, 21: 176, 22: 112, 23: 112 },
180
- 0x0C: { 11: 384, 12: 256, 13: 224, 21: 192, 22: 128, 23: 128 },
181
- 0x0D: { 11: 416, 12: 320, 13: 256, 21: 224, 22: 144, 23: 144 },
182
- 0x0E: { 11: 448, 12: 384, 13: 320, 21: 256, 22: 160, 23: 160 }
191
+ 1: { 11: 32, 12: 32, 13: 32, 21: 32, 22: 8, 23: 8 },
192
+ 2: { 11: 64, 12: 48, 13: 40, 21: 48, 22: 16, 23: 16 },
193
+ 3: { 11: 96, 12: 56, 13: 48, 21: 56, 22: 24, 23: 24 },
194
+ 4: { 11: 128, 12: 64, 13: 56, 21: 64, 22: 32, 23: 32 },
195
+ 5: { 11: 160, 12: 80, 13: 64, 21: 80, 22: 40, 23: 40 },
196
+ 6: { 11: 192, 12: 96, 13: 80, 21: 96, 22: 48, 23: 48 },
197
+ 7: { 11: 224, 12: 112, 13: 96, 21: 112, 22: 56, 23: 56 },
198
+ 8: { 11: 256, 12: 128, 13: 112, 21: 128, 22: 64, 23: 64 },
199
+ 9: { 11: 288, 12: 160, 13: 128, 21: 144, 22: 80, 23: 80 },
200
+ 10: { 11: 320, 12: 192, 13: 160, 21: 160, 22: 96, 23: 96 },
201
+ 11: { 11: 352, 12: 224, 13: 192, 21: 176, 22: 112, 23: 112 },
202
+ 12: { 11: 384, 12: 256, 13: 224, 21: 192, 22: 128, 23: 128 },
203
+ 13: { 11: 416, 12: 320, 13: 256, 21: 224, 22: 144, 23: 144 },
204
+ 14: { 11: 448, 12: 384, 13: 320, 21: 256, 22: 160, 23: 160 }
183
205
  };
184
206
  MpegFrameHeader.sampling_rate_freq_index = {
185
- 1: { 0x00: 44100, 0x01: 48000, 0x02: 32000 },
186
- 2: { 0x00: 22050, 0x01: 24000, 0x02: 16000 },
187
- 2.5: { 0x00: 11025, 0x01: 12000, 0x02: 8000 }
207
+ 1: { 0: 44100, 1: 48000, 2: 32000 },
208
+ 2: { 0: 22050, 1: 24000, 2: 16000 },
209
+ 2.5: { 0: 11025, 1: 12000, 2: 8000 }
188
210
  };
189
211
  MpegFrameHeader.samplesInFrameTable = [
190
212
  /* Layer I II III */
@@ -201,7 +223,7 @@ const FrameHeader = {
201
223
  }
202
224
  };
203
225
  function getVbrCodecProfile(vbrScale) {
204
- return 'V' + Math.floor((100 - vbrScale) / 10);
226
+ return `V${Math.floor((100 - vbrScale) / 10)}`;
205
227
  }
206
228
  export class MpegParser extends AbstractID3Parser {
207
229
  constructor() {
@@ -211,8 +233,16 @@ export class MpegParser extends AbstractID3Parser {
211
233
  this.countSkipFrameData = 0;
212
234
  this.totalDataLength = 0;
213
235
  this.bitrates = [];
236
+ this.offset = 0;
237
+ this.frame_size = 0;
238
+ this.crc = null;
214
239
  this.calculateEofDuration = false;
240
+ this.samplesPerFrame = null;
215
241
  this.buf_frame_header = new Uint8Array(4);
242
+ /**
243
+ * Number of bytes already parsed since beginning of stream / file
244
+ */
245
+ this.mpegOffset = null;
216
246
  this.syncPeek = {
217
247
  buf: new Uint8Array(maxPeekLen),
218
248
  len: 0
@@ -232,13 +262,17 @@ export class MpegParser extends AbstractID3Parser {
232
262
  }
233
263
  catch (err) {
234
264
  if (err instanceof EndOfStreamError) {
235
- debug(`End-of-stream`);
265
+ debug("End-of-stream");
236
266
  if (this.calculateEofDuration) {
237
- const numberOfSamples = this.frameCount * this.samplesPerFrame;
238
- this.metadata.setFormat('numberOfSamples', numberOfSamples);
239
- const duration = numberOfSamples / this.metadata.format.sampleRate;
240
- debug(`Calculate duration at EOF: ${duration} sec.`, duration);
241
- this.metadata.setFormat('duration', duration);
267
+ if (this.samplesPerFrame !== null) {
268
+ const numberOfSamples = this.frameCount * this.samplesPerFrame;
269
+ this.metadata.setFormat('numberOfSamples', numberOfSamples);
270
+ if (this.metadata.format.sampleRate) {
271
+ const duration = numberOfSamples / this.metadata.format.sampleRate;
272
+ debug(`Calculate duration at EOF: ${duration} sec.`, duration);
273
+ this.metadata.setFormat('duration', duration);
274
+ }
275
+ }
242
276
  }
243
277
  }
244
278
  else {
@@ -251,20 +285,26 @@ export class MpegParser extends AbstractID3Parser {
251
285
  */
252
286
  finalize() {
253
287
  const format = this.metadata.format;
254
- const hasID3v1 = this.metadata.native.hasOwnProperty('ID3v1');
255
- if (format.duration && this.tokenizer.fileInfo.size) {
256
- const mpegSize = this.tokenizer.fileInfo.size - this.mpegOffset - (hasID3v1 ? 128 : 0);
257
- if (format.codecProfile && format.codecProfile[0] === 'V') {
258
- this.metadata.setFormat('bitrate', mpegSize * 8 / format.duration);
288
+ const hasID3v1 = !!this.metadata.native.ID3v1;
289
+ if (this.mpegOffset !== null) {
290
+ if (format.duration && this.tokenizer.fileInfo.size) {
291
+ const mpegSize = this.tokenizer.fileInfo.size - this.mpegOffset - (hasID3v1 ? 128 : 0);
292
+ if (format.codecProfile && format.codecProfile[0] === 'V') {
293
+ this.metadata.setFormat('bitrate', mpegSize * 8 / format.duration);
294
+ }
295
+ }
296
+ else if (this.tokenizer.fileInfo.size && format.codecProfile === 'CBR') {
297
+ const mpegSize = this.tokenizer.fileInfo.size - this.mpegOffset - (hasID3v1 ? 128 : 0);
298
+ if (this.frame_size !== null && this.samplesPerFrame !== null) {
299
+ const numberOfSamples = Math.round(mpegSize / this.frame_size) * this.samplesPerFrame;
300
+ this.metadata.setFormat('numberOfSamples', numberOfSamples);
301
+ if (format.sampleRate) {
302
+ const duration = numberOfSamples / format.sampleRate;
303
+ debug("Calculate CBR duration based on file size: %s", duration);
304
+ this.metadata.setFormat('duration', duration);
305
+ }
306
+ }
259
307
  }
260
- }
261
- else if (this.tokenizer.fileInfo.size && format.codecProfile === 'CBR') {
262
- const mpegSize = this.tokenizer.fileInfo.size - this.mpegOffset - (hasID3v1 ? 128 : 0);
263
- const numberOfSamples = Math.round(mpegSize / this.frame_size) * this.samplesPerFrame;
264
- this.metadata.setFormat('numberOfSamples', numberOfSamples);
265
- const duration = numberOfSamples / format.sampleRate;
266
- debug("Calculate CBR duration based on file size: %s", duration);
267
- this.metadata.setFormat('duration', duration);
268
308
  }
269
309
  }
270
310
  async sync() {
@@ -289,21 +329,17 @@ export class MpegParser extends AbstractID3Parser {
289
329
  this.syncFrameCount = this.frameCount;
290
330
  return; // sync
291
331
  }
292
- else {
293
- gotFirstSync = false;
294
- bo = this.syncPeek.buf.indexOf(MpegFrameHeader.SyncByte1, bo);
295
- if (bo === -1) {
296
- if (this.syncPeek.len < this.syncPeek.buf.length) {
297
- throw new EndOfStreamError();
298
- }
299
- await this.tokenizer.ignore(this.syncPeek.len);
300
- break; // continue with next buffer
301
- }
302
- else {
303
- ++bo;
304
- gotFirstSync = true;
332
+ gotFirstSync = false;
333
+ bo = this.syncPeek.buf.indexOf(MpegFrameHeader.SyncByte1, bo);
334
+ if (bo === -1) {
335
+ if (this.syncPeek.len < this.syncPeek.buf.length) {
336
+ throw new EndOfStreamError();
305
337
  }
338
+ await this.tokenizer.ignore(this.syncPeek.len);
339
+ break; // continue with next buffer
306
340
  }
341
+ ++bo;
342
+ gotFirstSync = true;
307
343
  }
308
344
  }
309
345
  }
@@ -322,8 +358,11 @@ export class MpegParser extends AbstractID3Parser {
322
358
  }
323
359
  catch (err) {
324
360
  await this.tokenizer.ignore(1);
325
- this.metadata.addWarning('Parse error: ' + err.message);
326
- return false; // sync
361
+ if (err instanceof Error) {
362
+ this.metadata.addWarning(`Parse error: ${err.message}`);
363
+ return false; // sync
364
+ }
365
+ throw err;
327
366
  }
328
367
  await this.tokenizer.ignore(3);
329
368
  this.metadata.setFormat('container', header.container);
@@ -331,7 +370,7 @@ export class MpegParser extends AbstractID3Parser {
331
370
  this.metadata.setFormat('lossless', false);
332
371
  this.metadata.setFormat('sampleRate', header.samplingRate);
333
372
  this.frameCount++;
334
- return header.version >= 2 && header.layer === 0 ? this.parseAdts(header) : this.parseAudioFrameHeader(header);
373
+ return header.version !== null && header.version >= 2 && header.layer === 0 ? this.parseAdts(header) : this.parseAudioFrameHeader(header);
335
374
  }
336
375
  /**
337
376
  * @return {Promise<boolean>} true if parser should quit
@@ -349,11 +388,14 @@ export class MpegParser extends AbstractID3Parser {
349
388
  const samples_per_frame = header.calcSamplesPerFrame();
350
389
  debug(`samples_per_frame=${samples_per_frame}`);
351
390
  const bps = samples_per_frame / 8.0;
352
- const fsize = (bps * header.bitrate / header.samplingRate) +
353
- ((header.padding) ? slot_size : 0);
354
- this.frame_size = Math.floor(fsize);
391
+ if (header.bitrate !== null && header.samplingRate != null) {
392
+ const fsize = (bps * header.bitrate / header.samplingRate) + ((header.padding) ? slot_size : 0);
393
+ this.frame_size = Math.floor(fsize);
394
+ }
355
395
  this.audioFrameHeader = header;
356
- this.bitrates.push(header.bitrate);
396
+ if (header.bitrate !== null) {
397
+ this.bitrates.push(header.bitrate);
398
+ }
357
399
  // xtra header only exists in first frame
358
400
  if (this.frameCount === 1) {
359
401
  this.offset = FrameHeader.len;
@@ -388,10 +430,8 @@ export class MpegParser extends AbstractID3Parser {
388
430
  await this.parseCrc();
389
431
  return false;
390
432
  }
391
- else {
392
- await this.skipSideInformation();
393
- return false;
394
- }
433
+ await this.skipSideInformation();
434
+ return false;
395
435
  }
396
436
  async parseAdts(header) {
397
437
  const buf = new Uint8Array(3);
@@ -399,11 +439,13 @@ export class MpegParser extends AbstractID3Parser {
399
439
  header.frameLength += common.getBitAllignedNumber(buf, 0, 0, 11);
400
440
  this.totalDataLength += header.frameLength;
401
441
  this.samplesPerFrame = 1024;
402
- const framesPerSec = header.samplingRate / this.samplesPerFrame;
403
- const bytesPerFrame = this.frameCount === 0 ? 0 : this.totalDataLength / this.frameCount;
404
- const bitrate = 8 * bytesPerFrame * framesPerSec + 0.5;
405
- this.metadata.setFormat('bitrate', bitrate);
406
- debug(`frame-count=${this.frameCount}, size=${header.frameLength} bytes, bit-rate=${bitrate}`);
442
+ if (header.samplingRate !== null) {
443
+ const framesPerSec = header.samplingRate / this.samplesPerFrame;
444
+ const bytesPerFrame = this.frameCount === 0 ? 0 : this.totalDataLength / this.frameCount;
445
+ const bitrate = 8 * bytesPerFrame * framesPerSec + 0.5;
446
+ this.metadata.setFormat('bitrate', bitrate);
447
+ debug(`frame-count=${this.frameCount}, size=${header.frameLength} bytes, bit-rate=${bitrate}`);
448
+ }
407
449
  await this.tokenizer.ignore(header.frameLength > 7 ? header.frameLength - 7 : 1);
408
450
  // Consume remaining header and frame data
409
451
  if (this.frameCount === 3) {
@@ -426,12 +468,16 @@ export class MpegParser extends AbstractID3Parser {
426
468
  return this.skipSideInformation();
427
469
  }
428
470
  async skipSideInformation() {
429
- const sideinfo_length = this.audioFrameHeader.calculateSideInfoLength();
430
- // side information
431
- await this.tokenizer.readToken(new Token.Uint8ArrayType(sideinfo_length));
432
- this.offset += sideinfo_length;
433
- await this.readXtraInfoHeader();
434
- return;
471
+ if (this.audioFrameHeader) {
472
+ const sideinfo_length = this.audioFrameHeader.calculateSideInfoLength();
473
+ if (sideinfo_length !== null) {
474
+ await this.tokenizer.readToken(new Token.Uint8ArrayType(sideinfo_length));
475
+ // side information
476
+ this.offset += sideinfo_length;
477
+ await this.readXtraInfoHeader();
478
+ return;
479
+ }
480
+ }
435
481
  }
436
482
  async readXtraInfoHeader() {
437
483
  const headerTag = await this.tokenizer.readToken(InfoTagHeaderTag);
@@ -440,32 +486,34 @@ export class MpegParser extends AbstractID3Parser {
440
486
  case 'Info':
441
487
  this.metadata.setFormat('codecProfile', 'CBR');
442
488
  return this.readXingInfoHeader();
443
- case 'Xing':
489
+ case 'Xing': {
444
490
  const infoTag = await this.readXingInfoHeader();
445
- const codecProfile = getVbrCodecProfile(infoTag.vbrScale);
446
- this.metadata.setFormat('codecProfile', codecProfile);
491
+ if (infoTag.vbrScale !== null) {
492
+ const codecProfile = getVbrCodecProfile(infoTag.vbrScale);
493
+ this.metadata.setFormat('codecProfile', codecProfile);
494
+ }
447
495
  return null;
496
+ }
448
497
  case 'Xtra':
449
498
  // ToDo: ???
450
499
  break;
451
- case 'LAME':
500
+ case 'LAME': {
452
501
  const version = await this.tokenizer.readToken(LameEncoderVersion);
453
- if (this.frame_size >= this.offset + LameEncoderVersion.len) {
502
+ if (this.frame_size !== null && this.frame_size >= this.offset + LameEncoderVersion.len) {
454
503
  this.offset += LameEncoderVersion.len;
455
- this.metadata.setFormat('tool', 'LAME ' + version);
504
+ this.metadata.setFormat('tool', `LAME ${version}`);
456
505
  await this.skipFrameData(this.frame_size - this.offset);
457
506
  return null;
458
507
  }
459
- else {
460
- this.metadata.addWarning('Corrupt LAME header');
461
- break;
462
- }
508
+ this.metadata.addWarning('Corrupt LAME header');
509
+ break;
510
+ }
463
511
  // ToDo: ???
464
512
  }
465
513
  // ToDo: promise duration???
466
514
  const frameDataLeft = this.frame_size - this.offset;
467
515
  if (frameDataLeft < 0) {
468
- this.metadata.addWarning('Frame ' + this.frameCount + 'corrupt: negative frameDataLeft');
516
+ this.metadata.addWarning(`Frame ${this.frameCount}corrupt: negative frameDataLeft`);
469
517
  }
470
518
  else {
471
519
  await this.skipFrameData(frameDataLeft);
@@ -481,7 +529,7 @@ export class MpegParser extends AbstractID3Parser {
481
529
  const infoTag = await readXingHeader(this.tokenizer);
482
530
  this.offset += this.tokenizer.position - offset;
483
531
  if (infoTag.lame) {
484
- this.metadata.setFormat('tool', 'LAME ' + common.stripNulls(infoTag.lame.version));
532
+ this.metadata.setFormat('tool', `LAME ${common.stripNulls(infoTag.lame.version)}`);
485
533
  if (infoTag.lame.extended) {
486
534
  // this.metadata.setFormat('trackGain', infoTag.lame.extended.track_gain);
487
535
  this.metadata.setFormat('trackPeakLevel', infoTag.lame.extended.track_peak);
@@ -494,7 +542,7 @@ export class MpegParser extends AbstractID3Parser {
494
542
  this.metadata.setFormat('duration', infoTag.lame.extended.music_length / 1000);
495
543
  }
496
544
  }
497
- if (infoTag.streamSize) {
545
+ if (infoTag.streamSize && this.audioFrameHeader && infoTag.numFrames !== null) {
498
546
  const duration = this.audioFrameHeader.calcDuration(infoTag.numFrames);
499
547
  this.metadata.setFormat('duration', duration);
500
548
  debug('Get duration from Xing header: %s', this.metadata.format.duration);
@@ -518,3 +566,4 @@ export class MpegParser extends AbstractID3Parser {
518
566
  });
519
567
  }
520
568
  }
569
+ //# sourceMappingURL=MpegParser.js.map
@@ -51,5 +51,5 @@ declare enum ReplayGainOriginator {
51
51
  *
52
52
  * https://github.com/Borewit/music-metadata/wiki/Replay-Gain-Data-Format
53
53
  */
54
- export declare const ReplayGain: IGetToken<IReplayGain>;
54
+ export declare const ReplayGain: IGetToken<IReplayGain | undefined>;
55
55
  export {};
@@ -64,3 +64,4 @@ export const ReplayGain = {
64
64
  return undefined;
65
65
  }
66
66
  };
67
+ //# sourceMappingURL=ReplayGainDataFormat.js.map
@@ -1,6 +1,6 @@
1
1
  import * as Token from 'token-types';
2
2
  import type { IGetToken, ITokenizer } from 'strtok3';
3
- import { IExtendedLameHeader } from './ExtendedLameHeader.js';
3
+ import { type IExtendedLameHeader } from './ExtendedLameHeader.js';
4
4
  export interface IXingHeaderFlags {
5
5
  frames: boolean;
6
6
  bytes: boolean;
@@ -21,16 +21,16 @@ export interface IXingInfoTag {
21
21
  /**
22
22
  * total bit stream frames from Vbr header data
23
23
  */
24
- numFrames?: number;
24
+ numFrames: number | null;
25
25
  /**
26
26
  * Actual stream size = file size - header(s) size [bytes]
27
27
  */
28
- streamSize?: number;
28
+ streamSize: number | null;
29
29
  toc?: Uint8Array;
30
30
  /**
31
31
  * the number of header data bytes (from original file)
32
32
  */
33
- vbrScale?: number;
33
+ vbrScale: number | null;
34
34
  lame?: {
35
35
  version: string;
36
36
  extended?: IExtendedLameHeader;
@@ -32,7 +32,7 @@ export const XingHeaderFlags = {
32
32
  // */
33
33
  export async function readXingHeader(tokenizer) {
34
34
  const flags = await tokenizer.readToken(XingHeaderFlags);
35
- const xingInfoTag = {};
35
+ const xingInfoTag = { numFrames: null, streamSize: null, vbrScale: null };
36
36
  if (flags.frames) {
37
37
  xingInfoTag.numFrames = await tokenizer.readToken(Token.UINT32_BE);
38
38
  }
@@ -53,9 +53,9 @@ export async function readXingHeader(tokenizer) {
53
53
  version: await tokenizer.readToken(new Token.StringType(5, 'ascii'))
54
54
  };
55
55
  const match = xingInfoTag.lame.version.match(/\d+.\d+/g);
56
- if (match) {
57
- const majorMinorVersion = xingInfoTag.lame.version.match(/\d+.\d+/g)[0]; // e.g. 3.97
58
- const version = majorMinorVersion.split('.').map(n => parseInt(n, 10));
56
+ if (match !== null) {
57
+ const majorMinorVersion = match[0]; // e.g. 3.97
58
+ const version = majorMinorVersion.split('.').map(n => Number.parseInt(n, 10));
59
59
  if (version[0] >= 3 && version[1] >= 90) {
60
60
  xingInfoTag.lame.extended = await tokenizer.readToken(ExtendedLameHeader);
61
61
  }
@@ -63,3 +63,4 @@ export async function readXingHeader(tokenizer) {
63
63
  }
64
64
  return xingInfoTag;
65
65
  }
66
+ //# sourceMappingURL=XingTag.js.map
@@ -28,3 +28,4 @@ class MusepackParser extends AbstractID3Parser {
28
28
  }
29
29
  }
30
30
  export default MusepackParser;
31
+ //# sourceMappingURL=index.js.map
@@ -3,14 +3,14 @@ export class BitReader {
3
3
  constructor(tokenizer) {
4
4
  this.tokenizer = tokenizer;
5
5
  this.pos = 0;
6
- this.dword = undefined;
6
+ this.dword = null;
7
7
  }
8
8
  /**
9
9
  *
10
10
  * @param bits 1..30 bits
11
11
  */
12
12
  async read(bits) {
13
- while (this.dword === undefined) {
13
+ while (this.dword === null) {
14
14
  this.dword = await this.tokenizer.readToken(Token.UINT32_LE);
15
15
  }
16
16
  let out = this.dword;
@@ -19,26 +19,22 @@ export class BitReader {
19
19
  out >>>= (32 - this.pos);
20
20
  return out & ((1 << bits) - 1);
21
21
  }
22
- else {
23
- this.pos -= 32;
24
- if (this.pos === 0) {
25
- this.dword = undefined;
26
- return out & ((1 << bits) - 1);
27
- }
28
- else {
29
- this.dword = await this.tokenizer.readToken(Token.UINT32_LE);
30
- if (this.pos) {
31
- out <<= this.pos;
32
- out |= this.dword >>> (32 - this.pos);
33
- }
34
- return out & ((1 << bits) - 1);
35
- }
22
+ this.pos -= 32;
23
+ if (this.pos === 0) {
24
+ this.dword = null;
25
+ return out & ((1 << bits) - 1);
26
+ }
27
+ this.dword = await this.tokenizer.readToken(Token.UINT32_LE);
28
+ if (this.pos) {
29
+ out <<= this.pos;
30
+ out |= this.dword >>> (32 - this.pos);
36
31
  }
32
+ return out & ((1 << bits) - 1);
37
33
  }
38
34
  async ignore(bits) {
39
35
  if (this.pos > 0) {
40
36
  const remaining = 32 - this.pos;
41
- this.dword = undefined;
37
+ this.dword = null;
42
38
  bits -= remaining;
43
39
  this.pos = 0;
44
40
  }
@@ -48,3 +44,4 @@ export class BitReader {
48
44
  return this.read(remainder);
49
45
  }
50
46
  }
47
+ //# sourceMappingURL=BitReader.js.map
@@ -7,7 +7,9 @@ const debug = initDebug('music-metadata:parser:musepack');
7
7
  export class MpcSv7Parser extends BasicParser {
8
8
  constructor() {
9
9
  super(...arguments);
10
+ this.bitreader = null;
10
11
  this.audioLength = 0;
12
+ this.duration = null;
11
13
  }
12
14
  async parse() {
13
15
  const header = await this.tokenizer.readToken(SV7.Header);
@@ -37,6 +39,9 @@ export class MpcSv7Parser extends BasicParser {
37
39
  // last frame
38
40
  const lastFrameLength = await this.bitreader.read(11);
39
41
  this.audioLength += lastFrameLength;
40
- this.metadata.setFormat('bitrate', this.audioLength / this.duration);
42
+ if (this.duration !== null) {
43
+ this.metadata.setFormat('bitrate', this.audioLength / this.duration);
44
+ }
41
45
  }
42
46
  }
47
+ //# sourceMappingURL=MpcSv7Parser.js.map