mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-10-03 05:43:50 +02:00
Fix WAV files with embedded ID3 metadata returning empty tags (#110)
* Read ID3v2 tags from WAV file * remove js import Co-authored-by: David P. <[email protected]> * revert slicing in wave-demuxer.ts and add id3.ts for further sharing * Add `writeId3Tag` to the `WavOutputFormat` constructor options * add more context in private and public APIs that it's non-standard * compare actual values in the test with real file * remove unnecesarry double slice requesting * trim test audio file (i tried my best to preserve ID3, including artwork) * Couple of code cleanups * const -> using * Fix linting errors --------- Co-authored-by: David P. <[email protected]>
This commit is contained in:
co-authored by
David P.
parent
cab8319051
commit
6aee4e6a6a
@@ -6,16 +6,13 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import { isIso88591Compatible, textEncoder } from '../misc';
|
||||
import { Writer } from '../writer';
|
||||
import {
|
||||
computeMp3FrameSize,
|
||||
encodeSynchsafe,
|
||||
getXingOffset,
|
||||
KILOBIT_RATES,
|
||||
XING,
|
||||
} from '../../shared/mp3-misc';
|
||||
import { Id3V2TextEncoding } from './mp3-reader';
|
||||
|
||||
export type XingFrameData = {
|
||||
mpegVersionId: number;
|
||||
@@ -39,28 +36,11 @@ export class Mp3Writer {
|
||||
|
||||
constructor(private writer: Writer) {}
|
||||
|
||||
writeU8(value: number) {
|
||||
this.helper[0] = value;
|
||||
this.writer.write(this.helper.subarray(0, 1));
|
||||
}
|
||||
|
||||
writeU16(value: number) {
|
||||
this.helperView.setUint16(0, value, false);
|
||||
this.writer.write(this.helper.subarray(0, 2));
|
||||
}
|
||||
|
||||
writeU32(value: number) {
|
||||
this.helperView.setUint32(0, value, false);
|
||||
this.writer.write(this.helper.subarray(0, 4));
|
||||
}
|
||||
|
||||
writeAscii(text: string) {
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
this.helper[i] = text.charCodeAt(i);
|
||||
}
|
||||
this.writer.write(this.helper.subarray(0, text.length));
|
||||
}
|
||||
|
||||
writeXingFrame(data: XingFrameData) {
|
||||
const startPos = this.writer.getPos();
|
||||
|
||||
@@ -137,113 +117,4 @@ export class Mp3Writer {
|
||||
);
|
||||
this.writer.seek(startPos + frameSize);
|
||||
}
|
||||
|
||||
writeSynchsafeU32(value: number) {
|
||||
this.writeU32(encodeSynchsafe(value));
|
||||
}
|
||||
|
||||
writeIsoString(text: string) {
|
||||
const bytes = new Uint8Array(text.length + 1);
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
bytes[i] = text.charCodeAt(i);
|
||||
}
|
||||
bytes[text.length] = 0x00;
|
||||
this.writer.write(bytes);
|
||||
}
|
||||
|
||||
writeUtf8String(text: string) {
|
||||
const utf8Data = textEncoder.encode(text);
|
||||
this.writer.write(utf8Data);
|
||||
this.writeU8(0x00);
|
||||
}
|
||||
|
||||
writeId3V2TextFrame(frameId: string, text: string) {
|
||||
const useIso88591 = isIso88591Compatible(text);
|
||||
const textDataLength = useIso88591 ? text.length : textEncoder.encode(text).byteLength;
|
||||
const frameSize = 1 + textDataLength + 1;
|
||||
|
||||
this.writeAscii(frameId);
|
||||
this.writeSynchsafeU32(frameSize);
|
||||
this.writeU16(0x0000);
|
||||
|
||||
this.writeU8(useIso88591 ? Id3V2TextEncoding.ISO_8859_1 : Id3V2TextEncoding.UTF_8);
|
||||
if (useIso88591) {
|
||||
this.writeIsoString(text);
|
||||
} else {
|
||||
this.writeUtf8String(text);
|
||||
}
|
||||
}
|
||||
|
||||
writeId3V2LyricsFrame(lyrics: string) {
|
||||
const useIso88591 = isIso88591Compatible(lyrics);
|
||||
const shortDescription = '';
|
||||
const frameSize = 1 + 3 + shortDescription.length + 1 + lyrics.length + 1;
|
||||
|
||||
this.writeAscii('USLT');
|
||||
this.writeSynchsafeU32(frameSize);
|
||||
this.writeU16(0x0000);
|
||||
|
||||
this.writeU8(useIso88591 ? Id3V2TextEncoding.ISO_8859_1 : Id3V2TextEncoding.UTF_8);
|
||||
this.writeAscii('und');
|
||||
|
||||
if (useIso88591) {
|
||||
this.writeIsoString(shortDescription);
|
||||
this.writeIsoString(lyrics);
|
||||
} else {
|
||||
this.writeUtf8String(shortDescription);
|
||||
this.writeUtf8String(lyrics);
|
||||
}
|
||||
}
|
||||
|
||||
writeId3V2CommentFrame(comment: string) {
|
||||
const useIso88591 = isIso88591Compatible(comment);
|
||||
const textDataLength = useIso88591 ? comment.length : textEncoder.encode(comment).byteLength;
|
||||
const shortDescription = '';
|
||||
const frameSize = 1 + 3 + shortDescription.length + 1 + textDataLength + 1;
|
||||
|
||||
this.writeAscii('COMM');
|
||||
this.writeSynchsafeU32(frameSize);
|
||||
this.writeU16(0x0000);
|
||||
|
||||
this.writeU8(useIso88591 ? Id3V2TextEncoding.ISO_8859_1 : Id3V2TextEncoding.UTF_8);
|
||||
this.writeU8(0x75); // 'u'
|
||||
this.writeU8(0x6E); // 'n'
|
||||
this.writeU8(0x64); // 'd'
|
||||
|
||||
if (useIso88591) {
|
||||
this.writeIsoString(shortDescription);
|
||||
this.writeIsoString(comment);
|
||||
} else {
|
||||
this.writeUtf8String(shortDescription);
|
||||
this.writeUtf8String(comment);
|
||||
}
|
||||
}
|
||||
|
||||
writeId3V2ApicFrame(mimeType: string, pictureType: number, description: string, imageData: Uint8Array) {
|
||||
const useIso88591 = isIso88591Compatible(mimeType) && isIso88591Compatible(description);
|
||||
const descriptionDataLength = useIso88591 ? description.length : textEncoder.encode(description).byteLength;
|
||||
const frameSize = 1 + mimeType.length + 1 + 1 + descriptionDataLength + 1 + imageData.byteLength;
|
||||
|
||||
this.writeAscii('APIC');
|
||||
this.writeSynchsafeU32(frameSize);
|
||||
this.writeU16(0x0000);
|
||||
|
||||
this.writeU8(useIso88591 ? Id3V2TextEncoding.ISO_8859_1 : Id3V2TextEncoding.UTF_8);
|
||||
|
||||
if (useIso88591) {
|
||||
this.writeIsoString(mimeType);
|
||||
} else {
|
||||
this.writeUtf8String(mimeType);
|
||||
}
|
||||
|
||||
this.writeU8(pictureType);
|
||||
|
||||
if (useIso88591) {
|
||||
this.writeIsoString(description);
|
||||
} else {
|
||||
this.writeUtf8String(description);
|
||||
}
|
||||
|
||||
this.writer.write(imageData);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user