@utilix-tech/sdk 0.70.0 → 0.71.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.
- package/CHANGELOG.md +3 -0
- package/README.md +12 -4
- package/dist/{chunk-226EH3ZI.js → chunk-PT4N4UBL.js} +132 -1
- package/dist/index.cjs +131 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/{media-BUvwm4wU.d.cts → media-56zrIBw4.d.cts} +47 -2
- package/dist/{media-BUvwm4wU.d.ts → media-56zrIBw4.d.ts} +47 -2
- package/dist/tools/media.cjs +131 -0
- package/dist/tools/media.d.cts +1 -1
- package/dist/tools/media.d.ts +1 -1
- package/dist/tools/media.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Full release notes for all Utilix surfaces (browser tools, REST API, SDKs, MCP server) live at [utilix.tech/changelog](https://utilix.tech/changelog). This file tracks just this package.
|
|
4
4
|
|
|
5
|
+
## 0.71.0
|
|
6
|
+
- Add `readMidiInfo` (midi-file-info): parse a Standard MIDI File's header and every track chunk to report format, tick division, initial tempo, time signature, and per-track event/note-on counts, plus total duration in seconds accounting for tempo changes.
|
|
7
|
+
|
|
5
8
|
## 0.70.0
|
|
6
9
|
- Add `generateHotp`, `generateTotp` (totp-hotp-generator): generate an HOTP code (RFC 4226) for a counter value, or the current TOTP code (RFC 6238) plus seconds until rotation, from a base32-encoded shared secret, supporting SHA-1/SHA-256/SHA-512 and 6-8 digit codes.
|
|
7
10
|
- Add `calculateTaxEquivalentYield` (tax-equivalent-yield-calculator): convert between a tax-free municipal bond yield and its taxable equivalent given a marginal federal (plus optional state) tax rate, in both directions.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @utilix-tech/sdk
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**692 developer utility functions for Node.js: runs locally, no API key required.**
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@utilix-tech/sdk)
|
|
6
6
|
[](https://www.npmjs.com/package/@utilix-tech/sdk)
|
|
@@ -876,10 +876,10 @@ analyzeString("café"); // { length: 4, codePoints: [...], ... }
|
|
|
876
876
|
|
|
877
877
|
---
|
|
878
878
|
|
|
879
|
-
### `/media`: Image Header Parsing, EXIF, PDF Metadata & Page Dimensions, WAV, ID3, ICO, FLAC, Video &
|
|
879
|
+
### `/media`: Image Header Parsing, EXIF, PDF Metadata & Page Dimensions, WAV, ID3, ICO, FLAC, Video, GIF & MIDI Metadata
|
|
880
880
|
|
|
881
881
|
```ts
|
|
882
|
-
import { readImageInfo, readExifData, readPdfMetadata, readPdfPageDimensions, readWavInfo, readId3Tags, readIcoInfo, readFlacInfo, readVideoInfo, inspectGifFrames } from "@utilix-tech/sdk/media";
|
|
882
|
+
import { readImageInfo, readExifData, readPdfMetadata, readPdfPageDimensions, readWavInfo, readId3Tags, readIcoInfo, readFlacInfo, readVideoInfo, inspectGifFrames, readMidiInfo } from "@utilix-tech/sdk/media";
|
|
883
883
|
|
|
884
884
|
// Reads format, dimensions, bit depth, and alpha channel straight from
|
|
885
885
|
// file bytes: no decoding, no canvas, no image library.
|
|
@@ -945,9 +945,17 @@ readVideoInfo(new Uint8Array(videoBytes));
|
|
|
945
945
|
const gifBytes = await fs.promises.readFile("animation.gif");
|
|
946
946
|
inspectGifFrames(new Uint8Array(gifBytes));
|
|
947
947
|
// { version: "89a", width: 480, height: 270, frameCount: 24, animated: true, loopCount: 0, totalDurationMs: 2400, ... }
|
|
948
|
+
|
|
949
|
+
// Parses a Standard MIDI File's header and every track chunk to report
|
|
950
|
+
// format, tick division, initial tempo, time signature, and per-track
|
|
951
|
+
// event/note-on counts, plus total duration in seconds: no MIDI playback
|
|
952
|
+
// or synthesis.
|
|
953
|
+
const midiBytes = await fs.promises.readFile("song.mid");
|
|
954
|
+
readMidiInfo(new Uint8Array(midiBytes));
|
|
955
|
+
// { format: 1, trackCount: 2, initialTempoBpm: 120, timeSignature: { numerator: 4, denominator: 4 }, durationSeconds: 4, tracks: [...] }
|
|
948
956
|
```
|
|
949
957
|
|
|
950
|
-
`readImageInfo` supports PNG, JPEG, GIF, WebP, and BMP. `readExifData` supports JPEG only. `readWavInfo` supports the canonical RIFF/WAVE container only. `readId3Tags` decodes ID3v2.3/2.4 and ID3v1/1.1 only (ID3v2.2 is detected but not decoded). `readIcoInfo` reads .ico icon files only and rejects .cur cursor files. `readFlacInfo` reads the native FLAC container only, not FLAC-in-Ogg. `readVideoInfo` supports MP4 (ISO BMFF) and WebM (EBML/Matroska) containers only. `inspectGifFrames` reads the GIF87a/89a block structure only; it never decodes pixel data.
|
|
958
|
+
`readImageInfo` supports PNG, JPEG, GIF, WebP, and BMP. `readExifData` supports JPEG only. `readWavInfo` supports the canonical RIFF/WAVE container only. `readId3Tags` decodes ID3v2.3/2.4 and ID3v1/1.1 only (ID3v2.2 is detected but not decoded). `readIcoInfo` reads .ico icon files only and rejects .cur cursor files. `readFlacInfo` reads the native FLAC container only, not FLAC-in-Ogg. `readVideoInfo` supports MP4 (ISO BMFF) and WebM (EBML/Matroska) containers only. `inspectGifFrames` reads the GIF87a/89a block structure only; it never decodes pixel data. `readMidiInfo` reads Standard MIDI Files (format 0/1/2) only.
|
|
951
959
|
|
|
952
960
|
---
|
|
953
961
|
|
|
@@ -9,6 +9,7 @@ __export(media_exports, {
|
|
|
9
9
|
readIcoInfo: () => readIcoInfo,
|
|
10
10
|
readId3Tags: () => readId3Tags,
|
|
11
11
|
readImageInfo: () => readImageInfo,
|
|
12
|
+
readMidiInfo: () => readMidiInfo,
|
|
12
13
|
readPdfMetadata: () => readPdfMetadata,
|
|
13
14
|
readPdfPageDimensions: () => readPdfPageDimensions,
|
|
14
15
|
readVideoInfo: () => readVideoInfo,
|
|
@@ -1306,5 +1307,135 @@ function inspectGifFrames(bytes) {
|
|
|
1306
1307
|
hasTransparency
|
|
1307
1308
|
};
|
|
1308
1309
|
}
|
|
1310
|
+
function readU16BE(bytes, offset) {
|
|
1311
|
+
return bytes[offset] << 8 | bytes[offset + 1];
|
|
1312
|
+
}
|
|
1313
|
+
function readMidiVarLen(bytes, pos) {
|
|
1314
|
+
let value = 0;
|
|
1315
|
+
let i = pos;
|
|
1316
|
+
for (let n = 0; n < 4; n++) {
|
|
1317
|
+
if (i >= bytes.length) return null;
|
|
1318
|
+
const b = bytes[i];
|
|
1319
|
+
value = value << 7 | b & 127;
|
|
1320
|
+
i++;
|
|
1321
|
+
if ((b & 128) === 0) return { value, next: i };
|
|
1322
|
+
}
|
|
1323
|
+
return null;
|
|
1324
|
+
}
|
|
1325
|
+
function readMidiInfo(bytes) {
|
|
1326
|
+
if (bytes.length < 14) return { error: "File is too small to be a valid MIDI file" };
|
|
1327
|
+
if (Buffer.from(bytes.subarray(0, 4)).toString("ascii") !== "MThd") {
|
|
1328
|
+
return { error: 'Not a valid MIDI file (missing "MThd" header chunk)' };
|
|
1329
|
+
}
|
|
1330
|
+
const headerLength = readU32BE(bytes, 4);
|
|
1331
|
+
if (headerLength < 6 || 8 + headerLength > bytes.length) return { error: "Truncated or invalid MThd header chunk" };
|
|
1332
|
+
const format = readU16BE(bytes, 8);
|
|
1333
|
+
if (format !== 0 && format !== 1 && format !== 2) return { error: `Unsupported MIDI format: ${format} (expected 0, 1, or 2)` };
|
|
1334
|
+
const trackCount = readU16BE(bytes, 10);
|
|
1335
|
+
if (trackCount < 1) return { error: "MIDI file declares zero tracks" };
|
|
1336
|
+
const divisionRaw = readU16BE(bytes, 12);
|
|
1337
|
+
const division = (divisionRaw & 32768) !== 0 ? { type: "smpte", framesPerSecond: -(divisionRaw >> 8 << 24 >> 24), ticksPerFrame: divisionRaw & 255 } : { type: "ppq", ticksPerQuarterNote: divisionRaw & 32767 };
|
|
1338
|
+
const tracks = [];
|
|
1339
|
+
const tempoChanges = [];
|
|
1340
|
+
let timeSignature = null;
|
|
1341
|
+
let maxEndTick = 0;
|
|
1342
|
+
let offset = 8 + headerLength;
|
|
1343
|
+
let tracksParsed = 0;
|
|
1344
|
+
while (tracksParsed < trackCount && offset + 8 <= bytes.length) {
|
|
1345
|
+
const chunkId = Buffer.from(bytes.subarray(offset, offset + 4)).toString("ascii");
|
|
1346
|
+
const chunkLength = readU32BE(bytes, offset + 4);
|
|
1347
|
+
const dataStart = offset + 8;
|
|
1348
|
+
const dataEnd = Math.min(dataStart + chunkLength, bytes.length);
|
|
1349
|
+
if (chunkId !== "MTrk") {
|
|
1350
|
+
offset = dataEnd;
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
let pos = dataStart;
|
|
1354
|
+
let runningStatus = 0;
|
|
1355
|
+
let absoluteTick = 0;
|
|
1356
|
+
let eventCount = 0;
|
|
1357
|
+
let noteOnCount = 0;
|
|
1358
|
+
let name = null;
|
|
1359
|
+
while (pos < dataEnd) {
|
|
1360
|
+
const delta = readMidiVarLen(bytes, pos);
|
|
1361
|
+
if (!delta) break;
|
|
1362
|
+
pos = delta.next;
|
|
1363
|
+
absoluteTick += delta.value;
|
|
1364
|
+
if (pos >= dataEnd) break;
|
|
1365
|
+
let statusByte = bytes[pos];
|
|
1366
|
+
if (statusByte & 128) {
|
|
1367
|
+
runningStatus = statusByte;
|
|
1368
|
+
pos++;
|
|
1369
|
+
}
|
|
1370
|
+
statusByte = runningStatus;
|
|
1371
|
+
eventCount++;
|
|
1372
|
+
if (statusByte === 255) {
|
|
1373
|
+
const metaType = bytes[pos];
|
|
1374
|
+
pos++;
|
|
1375
|
+
const len = readMidiVarLen(bytes, pos);
|
|
1376
|
+
if (!len) break;
|
|
1377
|
+
pos = len.next;
|
|
1378
|
+
const data = bytes.subarray(pos, Math.min(pos + len.value, dataEnd));
|
|
1379
|
+
pos += len.value;
|
|
1380
|
+
if (metaType === 81 && data.length >= 3) {
|
|
1381
|
+
tempoChanges.push({ tick: absoluteTick, usPerQuarter: data[0] << 16 | data[1] << 8 | data[2] });
|
|
1382
|
+
} else if (metaType === 88 && data.length >= 2) {
|
|
1383
|
+
if (!timeSignature) timeSignature = { numerator: data[0], denominator: 2 ** data[1] };
|
|
1384
|
+
} else if (metaType === 3 && name === null) {
|
|
1385
|
+
name = Buffer.from(data).toString("utf8").trim() || null;
|
|
1386
|
+
} else if (metaType === 47) {
|
|
1387
|
+
break;
|
|
1388
|
+
}
|
|
1389
|
+
} else if (statusByte === 240 || statusByte === 247) {
|
|
1390
|
+
const len = readMidiVarLen(bytes, pos);
|
|
1391
|
+
if (!len) break;
|
|
1392
|
+
pos = len.next + len.value;
|
|
1393
|
+
} else if (statusByte >= 128) {
|
|
1394
|
+
const type = statusByte & 240;
|
|
1395
|
+
if (type === 192 || type === 208) {
|
|
1396
|
+
pos += 1;
|
|
1397
|
+
} else {
|
|
1398
|
+
if (type === 144 && (bytes[pos + 1] ?? 0) > 0) noteOnCount++;
|
|
1399
|
+
pos += 2;
|
|
1400
|
+
}
|
|
1401
|
+
} else {
|
|
1402
|
+
break;
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
tracks.push({ index: tracksParsed, name, eventCount, noteOnCount });
|
|
1406
|
+
maxEndTick = Math.max(maxEndTick, absoluteTick);
|
|
1407
|
+
offset = dataEnd;
|
|
1408
|
+
tracksParsed++;
|
|
1409
|
+
}
|
|
1410
|
+
if (tracks.length === 0) return { error: "No MTrk track chunks found in this MIDI file" };
|
|
1411
|
+
const DEFAULT_US_PER_QUARTER = 5e5;
|
|
1412
|
+
const initialTempoBpm = Math.round(6e7 / (tempoChanges[0]?.usPerQuarter ?? DEFAULT_US_PER_QUARTER) * 100) / 100;
|
|
1413
|
+
let durationSeconds = null;
|
|
1414
|
+
if (division.type === "smpte") {
|
|
1415
|
+
const ticksPerSecond = division.framesPerSecond * division.ticksPerFrame;
|
|
1416
|
+
durationSeconds = ticksPerSecond > 0 ? Math.round(maxEndTick / ticksPerSecond * 1e3) / 1e3 : null;
|
|
1417
|
+
} else {
|
|
1418
|
+
const sorted = [...tempoChanges].sort((a, b) => a.tick - b.tick);
|
|
1419
|
+
let seconds = 0;
|
|
1420
|
+
let prevTick = 0;
|
|
1421
|
+
let currentUsPerQuarter = DEFAULT_US_PER_QUARTER;
|
|
1422
|
+
for (const change of sorted) {
|
|
1423
|
+
seconds += (change.tick - prevTick) / division.ticksPerQuarterNote * (currentUsPerQuarter / 1e6);
|
|
1424
|
+
prevTick = change.tick;
|
|
1425
|
+
currentUsPerQuarter = change.usPerQuarter;
|
|
1426
|
+
}
|
|
1427
|
+
seconds += (maxEndTick - prevTick) / division.ticksPerQuarterNote * (currentUsPerQuarter / 1e6);
|
|
1428
|
+
durationSeconds = Math.round(seconds * 1e3) / 1e3;
|
|
1429
|
+
}
|
|
1430
|
+
return {
|
|
1431
|
+
format,
|
|
1432
|
+
trackCount: tracks.length,
|
|
1433
|
+
division,
|
|
1434
|
+
initialTempoBpm,
|
|
1435
|
+
timeSignature,
|
|
1436
|
+
durationSeconds,
|
|
1437
|
+
tracks
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1309
1440
|
|
|
1310
|
-
export { inspectGifFrames, media_exports, readExifData, readFlacInfo, readIcoInfo, readId3Tags, readImageInfo, readPdfMetadata, readPdfPageDimensions, readVideoInfo, readWavInfo };
|
|
1441
|
+
export { inspectGifFrames, media_exports, readExifData, readFlacInfo, readIcoInfo, readId3Tags, readImageInfo, readMidiInfo, readPdfMetadata, readPdfPageDimensions, readVideoInfo, readWavInfo };
|
package/dist/index.cjs
CHANGED
|
@@ -24357,6 +24357,7 @@ __export(media_exports, {
|
|
|
24357
24357
|
readIcoInfo: () => readIcoInfo,
|
|
24358
24358
|
readId3Tags: () => readId3Tags,
|
|
24359
24359
|
readImageInfo: () => readImageInfo,
|
|
24360
|
+
readMidiInfo: () => readMidiInfo,
|
|
24360
24361
|
readPdfMetadata: () => readPdfMetadata,
|
|
24361
24362
|
readPdfPageDimensions: () => readPdfPageDimensions,
|
|
24362
24363
|
readVideoInfo: () => readVideoInfo,
|
|
@@ -25654,6 +25655,136 @@ function inspectGifFrames(bytes) {
|
|
|
25654
25655
|
hasTransparency
|
|
25655
25656
|
};
|
|
25656
25657
|
}
|
|
25658
|
+
function readU16BE(bytes, offset) {
|
|
25659
|
+
return bytes[offset] << 8 | bytes[offset + 1];
|
|
25660
|
+
}
|
|
25661
|
+
function readMidiVarLen(bytes, pos) {
|
|
25662
|
+
let value = 0;
|
|
25663
|
+
let i = pos;
|
|
25664
|
+
for (let n = 0; n < 4; n++) {
|
|
25665
|
+
if (i >= bytes.length) return null;
|
|
25666
|
+
const b = bytes[i];
|
|
25667
|
+
value = value << 7 | b & 127;
|
|
25668
|
+
i++;
|
|
25669
|
+
if ((b & 128) === 0) return { value, next: i };
|
|
25670
|
+
}
|
|
25671
|
+
return null;
|
|
25672
|
+
}
|
|
25673
|
+
function readMidiInfo(bytes) {
|
|
25674
|
+
if (bytes.length < 14) return { error: "File is too small to be a valid MIDI file" };
|
|
25675
|
+
if (Buffer.from(bytes.subarray(0, 4)).toString("ascii") !== "MThd") {
|
|
25676
|
+
return { error: 'Not a valid MIDI file (missing "MThd" header chunk)' };
|
|
25677
|
+
}
|
|
25678
|
+
const headerLength = readU32BE(bytes, 4);
|
|
25679
|
+
if (headerLength < 6 || 8 + headerLength > bytes.length) return { error: "Truncated or invalid MThd header chunk" };
|
|
25680
|
+
const format = readU16BE(bytes, 8);
|
|
25681
|
+
if (format !== 0 && format !== 1 && format !== 2) return { error: `Unsupported MIDI format: ${format} (expected 0, 1, or 2)` };
|
|
25682
|
+
const trackCount = readU16BE(bytes, 10);
|
|
25683
|
+
if (trackCount < 1) return { error: "MIDI file declares zero tracks" };
|
|
25684
|
+
const divisionRaw = readU16BE(bytes, 12);
|
|
25685
|
+
const division = (divisionRaw & 32768) !== 0 ? { type: "smpte", framesPerSecond: -(divisionRaw >> 8 << 24 >> 24), ticksPerFrame: divisionRaw & 255 } : { type: "ppq", ticksPerQuarterNote: divisionRaw & 32767 };
|
|
25686
|
+
const tracks = [];
|
|
25687
|
+
const tempoChanges = [];
|
|
25688
|
+
let timeSignature = null;
|
|
25689
|
+
let maxEndTick = 0;
|
|
25690
|
+
let offset = 8 + headerLength;
|
|
25691
|
+
let tracksParsed = 0;
|
|
25692
|
+
while (tracksParsed < trackCount && offset + 8 <= bytes.length) {
|
|
25693
|
+
const chunkId = Buffer.from(bytes.subarray(offset, offset + 4)).toString("ascii");
|
|
25694
|
+
const chunkLength = readU32BE(bytes, offset + 4);
|
|
25695
|
+
const dataStart = offset + 8;
|
|
25696
|
+
const dataEnd = Math.min(dataStart + chunkLength, bytes.length);
|
|
25697
|
+
if (chunkId !== "MTrk") {
|
|
25698
|
+
offset = dataEnd;
|
|
25699
|
+
continue;
|
|
25700
|
+
}
|
|
25701
|
+
let pos = dataStart;
|
|
25702
|
+
let runningStatus = 0;
|
|
25703
|
+
let absoluteTick = 0;
|
|
25704
|
+
let eventCount = 0;
|
|
25705
|
+
let noteOnCount = 0;
|
|
25706
|
+
let name = null;
|
|
25707
|
+
while (pos < dataEnd) {
|
|
25708
|
+
const delta = readMidiVarLen(bytes, pos);
|
|
25709
|
+
if (!delta) break;
|
|
25710
|
+
pos = delta.next;
|
|
25711
|
+
absoluteTick += delta.value;
|
|
25712
|
+
if (pos >= dataEnd) break;
|
|
25713
|
+
let statusByte = bytes[pos];
|
|
25714
|
+
if (statusByte & 128) {
|
|
25715
|
+
runningStatus = statusByte;
|
|
25716
|
+
pos++;
|
|
25717
|
+
}
|
|
25718
|
+
statusByte = runningStatus;
|
|
25719
|
+
eventCount++;
|
|
25720
|
+
if (statusByte === 255) {
|
|
25721
|
+
const metaType = bytes[pos];
|
|
25722
|
+
pos++;
|
|
25723
|
+
const len = readMidiVarLen(bytes, pos);
|
|
25724
|
+
if (!len) break;
|
|
25725
|
+
pos = len.next;
|
|
25726
|
+
const data = bytes.subarray(pos, Math.min(pos + len.value, dataEnd));
|
|
25727
|
+
pos += len.value;
|
|
25728
|
+
if (metaType === 81 && data.length >= 3) {
|
|
25729
|
+
tempoChanges.push({ tick: absoluteTick, usPerQuarter: data[0] << 16 | data[1] << 8 | data[2] });
|
|
25730
|
+
} else if (metaType === 88 && data.length >= 2) {
|
|
25731
|
+
if (!timeSignature) timeSignature = { numerator: data[0], denominator: 2 ** data[1] };
|
|
25732
|
+
} else if (metaType === 3 && name === null) {
|
|
25733
|
+
name = Buffer.from(data).toString("utf8").trim() || null;
|
|
25734
|
+
} else if (metaType === 47) {
|
|
25735
|
+
break;
|
|
25736
|
+
}
|
|
25737
|
+
} else if (statusByte === 240 || statusByte === 247) {
|
|
25738
|
+
const len = readMidiVarLen(bytes, pos);
|
|
25739
|
+
if (!len) break;
|
|
25740
|
+
pos = len.next + len.value;
|
|
25741
|
+
} else if (statusByte >= 128) {
|
|
25742
|
+
const type = statusByte & 240;
|
|
25743
|
+
if (type === 192 || type === 208) {
|
|
25744
|
+
pos += 1;
|
|
25745
|
+
} else {
|
|
25746
|
+
if (type === 144 && (bytes[pos + 1] ?? 0) > 0) noteOnCount++;
|
|
25747
|
+
pos += 2;
|
|
25748
|
+
}
|
|
25749
|
+
} else {
|
|
25750
|
+
break;
|
|
25751
|
+
}
|
|
25752
|
+
}
|
|
25753
|
+
tracks.push({ index: tracksParsed, name, eventCount, noteOnCount });
|
|
25754
|
+
maxEndTick = Math.max(maxEndTick, absoluteTick);
|
|
25755
|
+
offset = dataEnd;
|
|
25756
|
+
tracksParsed++;
|
|
25757
|
+
}
|
|
25758
|
+
if (tracks.length === 0) return { error: "No MTrk track chunks found in this MIDI file" };
|
|
25759
|
+
const DEFAULT_US_PER_QUARTER = 5e5;
|
|
25760
|
+
const initialTempoBpm = Math.round(6e7 / (tempoChanges[0]?.usPerQuarter ?? DEFAULT_US_PER_QUARTER) * 100) / 100;
|
|
25761
|
+
let durationSeconds = null;
|
|
25762
|
+
if (division.type === "smpte") {
|
|
25763
|
+
const ticksPerSecond = division.framesPerSecond * division.ticksPerFrame;
|
|
25764
|
+
durationSeconds = ticksPerSecond > 0 ? Math.round(maxEndTick / ticksPerSecond * 1e3) / 1e3 : null;
|
|
25765
|
+
} else {
|
|
25766
|
+
const sorted = [...tempoChanges].sort((a, b) => a.tick - b.tick);
|
|
25767
|
+
let seconds = 0;
|
|
25768
|
+
let prevTick = 0;
|
|
25769
|
+
let currentUsPerQuarter = DEFAULT_US_PER_QUARTER;
|
|
25770
|
+
for (const change of sorted) {
|
|
25771
|
+
seconds += (change.tick - prevTick) / division.ticksPerQuarterNote * (currentUsPerQuarter / 1e6);
|
|
25772
|
+
prevTick = change.tick;
|
|
25773
|
+
currentUsPerQuarter = change.usPerQuarter;
|
|
25774
|
+
}
|
|
25775
|
+
seconds += (maxEndTick - prevTick) / division.ticksPerQuarterNote * (currentUsPerQuarter / 1e6);
|
|
25776
|
+
durationSeconds = Math.round(seconds * 1e3) / 1e3;
|
|
25777
|
+
}
|
|
25778
|
+
return {
|
|
25779
|
+
format,
|
|
25780
|
+
trackCount: tracks.length,
|
|
25781
|
+
division,
|
|
25782
|
+
initialTempoBpm,
|
|
25783
|
+
timeSignature,
|
|
25784
|
+
durationSeconds,
|
|
25785
|
+
tracks
|
|
25786
|
+
};
|
|
25787
|
+
}
|
|
25657
25788
|
|
|
25658
25789
|
// src/index.ts
|
|
25659
25790
|
var version = "0.1.0";
|
package/dist/index.d.cts
CHANGED
|
@@ -13,7 +13,7 @@ export { c as color } from './color-tPwZCr9H.cjs';
|
|
|
13
13
|
export { c as css } from './css-Cf7AMGM-.cjs';
|
|
14
14
|
export { m as misc } from './misc-CA3N198T.cjs';
|
|
15
15
|
export { a as ai_agent } from './ai_agent-CmrVjvYH.cjs';
|
|
16
|
-
export { m as media } from './media-
|
|
16
|
+
export { m as media } from './media-56zrIBw4.cjs';
|
|
17
17
|
|
|
18
18
|
declare const version = "0.1.0";
|
|
19
19
|
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { c as color } from './color-tPwZCr9H.js';
|
|
|
13
13
|
export { c as css } from './css-Cf7AMGM-.js';
|
|
14
14
|
export { m as misc } from './misc-CA3N198T.js';
|
|
15
15
|
export { a as ai_agent } from './ai_agent-CmrVjvYH.js';
|
|
16
|
-
export { m as media } from './media-
|
|
16
|
+
export { m as media } from './media-56zrIBw4.js';
|
|
17
17
|
|
|
18
18
|
declare const version = "0.1.0";
|
|
19
19
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { media_exports as media } from './chunk-
|
|
1
|
+
export { media_exports as media } from './chunk-PT4N4UBL.js';
|
|
2
2
|
export { units_exports as units } from './chunk-653HDWXW.js';
|
|
3
3
|
export { network_exports as network } from './chunk-ACGCCOCK.js';
|
|
4
4
|
export { api_exports as api } from './chunk-4S63T7Y4.js';
|
|
@@ -224,6 +224,45 @@ interface GifInfoError {
|
|
|
224
224
|
* any LZW-compressed pixel data.
|
|
225
225
|
*/
|
|
226
226
|
declare function inspectGifFrames(bytes: Uint8Array): GifInfo | GifInfoError;
|
|
227
|
+
type MidiDivision = {
|
|
228
|
+
type: 'ppq';
|
|
229
|
+
ticksPerQuarterNote: number;
|
|
230
|
+
} | {
|
|
231
|
+
type: 'smpte';
|
|
232
|
+
framesPerSecond: number;
|
|
233
|
+
ticksPerFrame: number;
|
|
234
|
+
};
|
|
235
|
+
interface MidiTrackInfo {
|
|
236
|
+
index: number;
|
|
237
|
+
name: string | null;
|
|
238
|
+
eventCount: number;
|
|
239
|
+
noteOnCount: number;
|
|
240
|
+
}
|
|
241
|
+
interface MidiTimeSignature {
|
|
242
|
+
numerator: number;
|
|
243
|
+
denominator: number;
|
|
244
|
+
}
|
|
245
|
+
interface MidiInfo {
|
|
246
|
+
format: number;
|
|
247
|
+
trackCount: number;
|
|
248
|
+
division: MidiDivision;
|
|
249
|
+
initialTempoBpm: number;
|
|
250
|
+
timeSignature: MidiTimeSignature | null;
|
|
251
|
+
durationSeconds: number | null;
|
|
252
|
+
tracks: MidiTrackInfo[];
|
|
253
|
+
}
|
|
254
|
+
interface MidiInfoError {
|
|
255
|
+
error: string;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Parse a Standard MIDI File (SMF) header and every track chunk to report
|
|
259
|
+
* format (0/1/2), the tick division, initial tempo, time signature, a
|
|
260
|
+
* per-track event/note-on count, and total duration in seconds. Duration
|
|
261
|
+
* accounts for tempo-change meta events across the whole file when the
|
|
262
|
+
* division is ticks-per-quarter-note; the rarer SMPTE division uses a fixed
|
|
263
|
+
* frames-per-second/ticks-per-frame rate instead.
|
|
264
|
+
*/
|
|
265
|
+
declare function readMidiInfo(bytes: Uint8Array): MidiInfo | MidiInfoError;
|
|
227
266
|
|
|
228
267
|
type media_ExifData = ExifData;
|
|
229
268
|
type media_ExifError = ExifError;
|
|
@@ -239,6 +278,11 @@ type media_Id3TagsError = Id3TagsError;
|
|
|
239
278
|
type media_ImageFormat = ImageFormat;
|
|
240
279
|
type media_ImageInfo = ImageInfo;
|
|
241
280
|
type media_ImageInfoError = ImageInfoError;
|
|
281
|
+
type media_MidiDivision = MidiDivision;
|
|
282
|
+
type media_MidiInfo = MidiInfo;
|
|
283
|
+
type media_MidiInfoError = MidiInfoError;
|
|
284
|
+
type media_MidiTimeSignature = MidiTimeSignature;
|
|
285
|
+
type media_MidiTrackInfo = MidiTrackInfo;
|
|
242
286
|
type media_PdfMetadata = PdfMetadata;
|
|
243
287
|
type media_PdfMetadataError = PdfMetadataError;
|
|
244
288
|
type media_PdfPageDimension = PdfPageDimension;
|
|
@@ -254,12 +298,13 @@ declare const media_readFlacInfo: typeof readFlacInfo;
|
|
|
254
298
|
declare const media_readIcoInfo: typeof readIcoInfo;
|
|
255
299
|
declare const media_readId3Tags: typeof readId3Tags;
|
|
256
300
|
declare const media_readImageInfo: typeof readImageInfo;
|
|
301
|
+
declare const media_readMidiInfo: typeof readMidiInfo;
|
|
257
302
|
declare const media_readPdfMetadata: typeof readPdfMetadata;
|
|
258
303
|
declare const media_readPdfPageDimensions: typeof readPdfPageDimensions;
|
|
259
304
|
declare const media_readVideoInfo: typeof readVideoInfo;
|
|
260
305
|
declare const media_readWavInfo: typeof readWavInfo;
|
|
261
306
|
declare namespace media {
|
|
262
|
-
export { type media_ExifData as ExifData, type media_ExifError as ExifError, type media_FlacInfo as FlacInfo, type media_FlacInfoError as FlacInfoError, type media_GifInfo as GifInfo, type media_GifInfoError as GifInfoError, type media_IcoImageEntry as IcoImageEntry, type media_IcoInfo as IcoInfo, type media_IcoInfoError as IcoInfoError, type media_Id3Tags as Id3Tags, type media_Id3TagsError as Id3TagsError, type media_ImageFormat as ImageFormat, type media_ImageInfo as ImageInfo, type media_ImageInfoError as ImageInfoError, type media_PdfMetadata as PdfMetadata, type media_PdfMetadataError as PdfMetadataError, type media_PdfPageDimension as PdfPageDimension, type media_PdfPageDimensionsResult as PdfPageDimensionsResult, type media_PdfPageOrientation as PdfPageOrientation, type media_VideoInfo as VideoInfo, type media_VideoInfoError as VideoInfoError, type media_WavInfo as WavInfo, type media_WavInfoError as WavInfoError, media_inspectGifFrames as inspectGifFrames, media_readExifData as readExifData, media_readFlacInfo as readFlacInfo, media_readIcoInfo as readIcoInfo, media_readId3Tags as readId3Tags, media_readImageInfo as readImageInfo, media_readPdfMetadata as readPdfMetadata, media_readPdfPageDimensions as readPdfPageDimensions, media_readVideoInfo as readVideoInfo, media_readWavInfo as readWavInfo };
|
|
307
|
+
export { type media_ExifData as ExifData, type media_ExifError as ExifError, type media_FlacInfo as FlacInfo, type media_FlacInfoError as FlacInfoError, type media_GifInfo as GifInfo, type media_GifInfoError as GifInfoError, type media_IcoImageEntry as IcoImageEntry, type media_IcoInfo as IcoInfo, type media_IcoInfoError as IcoInfoError, type media_Id3Tags as Id3Tags, type media_Id3TagsError as Id3TagsError, type media_ImageFormat as ImageFormat, type media_ImageInfo as ImageInfo, type media_ImageInfoError as ImageInfoError, type media_MidiDivision as MidiDivision, type media_MidiInfo as MidiInfo, type media_MidiInfoError as MidiInfoError, type media_MidiTimeSignature as MidiTimeSignature, type media_MidiTrackInfo as MidiTrackInfo, type media_PdfMetadata as PdfMetadata, type media_PdfMetadataError as PdfMetadataError, type media_PdfPageDimension as PdfPageDimension, type media_PdfPageDimensionsResult as PdfPageDimensionsResult, type media_PdfPageOrientation as PdfPageOrientation, type media_VideoInfo as VideoInfo, type media_VideoInfoError as VideoInfoError, type media_WavInfo as WavInfo, type media_WavInfoError as WavInfoError, media_inspectGifFrames as inspectGifFrames, media_readExifData as readExifData, media_readFlacInfo as readFlacInfo, media_readIcoInfo as readIcoInfo, media_readId3Tags as readId3Tags, media_readImageInfo as readImageInfo, media_readMidiInfo as readMidiInfo, media_readPdfMetadata as readPdfMetadata, media_readPdfPageDimensions as readPdfPageDimensions, media_readVideoInfo as readVideoInfo, media_readWavInfo as readWavInfo };
|
|
263
308
|
}
|
|
264
309
|
|
|
265
|
-
export {
|
|
310
|
+
export { readImageInfo as A, readMidiInfo as B, readPdfMetadata as C, readPdfPageDimensions as D, type ExifData as E, type FlacInfo as F, type GifInfo as G, readVideoInfo as H, type IcoImageEntry as I, readWavInfo as J, type MidiDivision as M, type PdfMetadata as P, type VideoInfo as V, type WavInfo as W, type ExifError as a, type FlacInfoError as b, type GifInfoError as c, type IcoInfo as d, type IcoInfoError as e, type Id3Tags as f, type Id3TagsError as g, type ImageFormat as h, type ImageInfo as i, type ImageInfoError as j, type MidiInfo as k, type MidiInfoError as l, media as m, type MidiTimeSignature as n, type MidiTrackInfo as o, type PdfMetadataError as p, type PdfPageDimension as q, type PdfPageDimensionsResult as r, type PdfPageOrientation as s, type VideoInfoError as t, type WavInfoError as u, inspectGifFrames as v, readExifData as w, readFlacInfo as x, readIcoInfo as y, readId3Tags as z };
|
|
@@ -224,6 +224,45 @@ interface GifInfoError {
|
|
|
224
224
|
* any LZW-compressed pixel data.
|
|
225
225
|
*/
|
|
226
226
|
declare function inspectGifFrames(bytes: Uint8Array): GifInfo | GifInfoError;
|
|
227
|
+
type MidiDivision = {
|
|
228
|
+
type: 'ppq';
|
|
229
|
+
ticksPerQuarterNote: number;
|
|
230
|
+
} | {
|
|
231
|
+
type: 'smpte';
|
|
232
|
+
framesPerSecond: number;
|
|
233
|
+
ticksPerFrame: number;
|
|
234
|
+
};
|
|
235
|
+
interface MidiTrackInfo {
|
|
236
|
+
index: number;
|
|
237
|
+
name: string | null;
|
|
238
|
+
eventCount: number;
|
|
239
|
+
noteOnCount: number;
|
|
240
|
+
}
|
|
241
|
+
interface MidiTimeSignature {
|
|
242
|
+
numerator: number;
|
|
243
|
+
denominator: number;
|
|
244
|
+
}
|
|
245
|
+
interface MidiInfo {
|
|
246
|
+
format: number;
|
|
247
|
+
trackCount: number;
|
|
248
|
+
division: MidiDivision;
|
|
249
|
+
initialTempoBpm: number;
|
|
250
|
+
timeSignature: MidiTimeSignature | null;
|
|
251
|
+
durationSeconds: number | null;
|
|
252
|
+
tracks: MidiTrackInfo[];
|
|
253
|
+
}
|
|
254
|
+
interface MidiInfoError {
|
|
255
|
+
error: string;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Parse a Standard MIDI File (SMF) header and every track chunk to report
|
|
259
|
+
* format (0/1/2), the tick division, initial tempo, time signature, a
|
|
260
|
+
* per-track event/note-on count, and total duration in seconds. Duration
|
|
261
|
+
* accounts for tempo-change meta events across the whole file when the
|
|
262
|
+
* division is ticks-per-quarter-note; the rarer SMPTE division uses a fixed
|
|
263
|
+
* frames-per-second/ticks-per-frame rate instead.
|
|
264
|
+
*/
|
|
265
|
+
declare function readMidiInfo(bytes: Uint8Array): MidiInfo | MidiInfoError;
|
|
227
266
|
|
|
228
267
|
type media_ExifData = ExifData;
|
|
229
268
|
type media_ExifError = ExifError;
|
|
@@ -239,6 +278,11 @@ type media_Id3TagsError = Id3TagsError;
|
|
|
239
278
|
type media_ImageFormat = ImageFormat;
|
|
240
279
|
type media_ImageInfo = ImageInfo;
|
|
241
280
|
type media_ImageInfoError = ImageInfoError;
|
|
281
|
+
type media_MidiDivision = MidiDivision;
|
|
282
|
+
type media_MidiInfo = MidiInfo;
|
|
283
|
+
type media_MidiInfoError = MidiInfoError;
|
|
284
|
+
type media_MidiTimeSignature = MidiTimeSignature;
|
|
285
|
+
type media_MidiTrackInfo = MidiTrackInfo;
|
|
242
286
|
type media_PdfMetadata = PdfMetadata;
|
|
243
287
|
type media_PdfMetadataError = PdfMetadataError;
|
|
244
288
|
type media_PdfPageDimension = PdfPageDimension;
|
|
@@ -254,12 +298,13 @@ declare const media_readFlacInfo: typeof readFlacInfo;
|
|
|
254
298
|
declare const media_readIcoInfo: typeof readIcoInfo;
|
|
255
299
|
declare const media_readId3Tags: typeof readId3Tags;
|
|
256
300
|
declare const media_readImageInfo: typeof readImageInfo;
|
|
301
|
+
declare const media_readMidiInfo: typeof readMidiInfo;
|
|
257
302
|
declare const media_readPdfMetadata: typeof readPdfMetadata;
|
|
258
303
|
declare const media_readPdfPageDimensions: typeof readPdfPageDimensions;
|
|
259
304
|
declare const media_readVideoInfo: typeof readVideoInfo;
|
|
260
305
|
declare const media_readWavInfo: typeof readWavInfo;
|
|
261
306
|
declare namespace media {
|
|
262
|
-
export { type media_ExifData as ExifData, type media_ExifError as ExifError, type media_FlacInfo as FlacInfo, type media_FlacInfoError as FlacInfoError, type media_GifInfo as GifInfo, type media_GifInfoError as GifInfoError, type media_IcoImageEntry as IcoImageEntry, type media_IcoInfo as IcoInfo, type media_IcoInfoError as IcoInfoError, type media_Id3Tags as Id3Tags, type media_Id3TagsError as Id3TagsError, type media_ImageFormat as ImageFormat, type media_ImageInfo as ImageInfo, type media_ImageInfoError as ImageInfoError, type media_PdfMetadata as PdfMetadata, type media_PdfMetadataError as PdfMetadataError, type media_PdfPageDimension as PdfPageDimension, type media_PdfPageDimensionsResult as PdfPageDimensionsResult, type media_PdfPageOrientation as PdfPageOrientation, type media_VideoInfo as VideoInfo, type media_VideoInfoError as VideoInfoError, type media_WavInfo as WavInfo, type media_WavInfoError as WavInfoError, media_inspectGifFrames as inspectGifFrames, media_readExifData as readExifData, media_readFlacInfo as readFlacInfo, media_readIcoInfo as readIcoInfo, media_readId3Tags as readId3Tags, media_readImageInfo as readImageInfo, media_readPdfMetadata as readPdfMetadata, media_readPdfPageDimensions as readPdfPageDimensions, media_readVideoInfo as readVideoInfo, media_readWavInfo as readWavInfo };
|
|
307
|
+
export { type media_ExifData as ExifData, type media_ExifError as ExifError, type media_FlacInfo as FlacInfo, type media_FlacInfoError as FlacInfoError, type media_GifInfo as GifInfo, type media_GifInfoError as GifInfoError, type media_IcoImageEntry as IcoImageEntry, type media_IcoInfo as IcoInfo, type media_IcoInfoError as IcoInfoError, type media_Id3Tags as Id3Tags, type media_Id3TagsError as Id3TagsError, type media_ImageFormat as ImageFormat, type media_ImageInfo as ImageInfo, type media_ImageInfoError as ImageInfoError, type media_MidiDivision as MidiDivision, type media_MidiInfo as MidiInfo, type media_MidiInfoError as MidiInfoError, type media_MidiTimeSignature as MidiTimeSignature, type media_MidiTrackInfo as MidiTrackInfo, type media_PdfMetadata as PdfMetadata, type media_PdfMetadataError as PdfMetadataError, type media_PdfPageDimension as PdfPageDimension, type media_PdfPageDimensionsResult as PdfPageDimensionsResult, type media_PdfPageOrientation as PdfPageOrientation, type media_VideoInfo as VideoInfo, type media_VideoInfoError as VideoInfoError, type media_WavInfo as WavInfo, type media_WavInfoError as WavInfoError, media_inspectGifFrames as inspectGifFrames, media_readExifData as readExifData, media_readFlacInfo as readFlacInfo, media_readIcoInfo as readIcoInfo, media_readId3Tags as readId3Tags, media_readImageInfo as readImageInfo, media_readMidiInfo as readMidiInfo, media_readPdfMetadata as readPdfMetadata, media_readPdfPageDimensions as readPdfPageDimensions, media_readVideoInfo as readVideoInfo, media_readWavInfo as readWavInfo };
|
|
263
308
|
}
|
|
264
309
|
|
|
265
|
-
export {
|
|
310
|
+
export { readImageInfo as A, readMidiInfo as B, readPdfMetadata as C, readPdfPageDimensions as D, type ExifData as E, type FlacInfo as F, type GifInfo as G, readVideoInfo as H, type IcoImageEntry as I, readWavInfo as J, type MidiDivision as M, type PdfMetadata as P, type VideoInfo as V, type WavInfo as W, type ExifError as a, type FlacInfoError as b, type GifInfoError as c, type IcoInfo as d, type IcoInfoError as e, type Id3Tags as f, type Id3TagsError as g, type ImageFormat as h, type ImageInfo as i, type ImageInfoError as j, type MidiInfo as k, type MidiInfoError as l, media as m, type MidiTimeSignature as n, type MidiTrackInfo as o, type PdfMetadataError as p, type PdfPageDimension as q, type PdfPageDimensionsResult as r, type PdfPageOrientation as s, type VideoInfoError as t, type WavInfoError as u, inspectGifFrames as v, readExifData as w, readFlacInfo as x, readIcoInfo as y, readId3Tags as z };
|
package/dist/tools/media.cjs
CHANGED
|
@@ -1293,6 +1293,136 @@ function inspectGifFrames(bytes) {
|
|
|
1293
1293
|
hasTransparency
|
|
1294
1294
|
};
|
|
1295
1295
|
}
|
|
1296
|
+
function readU16BE(bytes, offset) {
|
|
1297
|
+
return bytes[offset] << 8 | bytes[offset + 1];
|
|
1298
|
+
}
|
|
1299
|
+
function readMidiVarLen(bytes, pos) {
|
|
1300
|
+
let value = 0;
|
|
1301
|
+
let i = pos;
|
|
1302
|
+
for (let n = 0; n < 4; n++) {
|
|
1303
|
+
if (i >= bytes.length) return null;
|
|
1304
|
+
const b = bytes[i];
|
|
1305
|
+
value = value << 7 | b & 127;
|
|
1306
|
+
i++;
|
|
1307
|
+
if ((b & 128) === 0) return { value, next: i };
|
|
1308
|
+
}
|
|
1309
|
+
return null;
|
|
1310
|
+
}
|
|
1311
|
+
function readMidiInfo(bytes) {
|
|
1312
|
+
if (bytes.length < 14) return { error: "File is too small to be a valid MIDI file" };
|
|
1313
|
+
if (Buffer.from(bytes.subarray(0, 4)).toString("ascii") !== "MThd") {
|
|
1314
|
+
return { error: 'Not a valid MIDI file (missing "MThd" header chunk)' };
|
|
1315
|
+
}
|
|
1316
|
+
const headerLength = readU32BE(bytes, 4);
|
|
1317
|
+
if (headerLength < 6 || 8 + headerLength > bytes.length) return { error: "Truncated or invalid MThd header chunk" };
|
|
1318
|
+
const format = readU16BE(bytes, 8);
|
|
1319
|
+
if (format !== 0 && format !== 1 && format !== 2) return { error: `Unsupported MIDI format: ${format} (expected 0, 1, or 2)` };
|
|
1320
|
+
const trackCount = readU16BE(bytes, 10);
|
|
1321
|
+
if (trackCount < 1) return { error: "MIDI file declares zero tracks" };
|
|
1322
|
+
const divisionRaw = readU16BE(bytes, 12);
|
|
1323
|
+
const division = (divisionRaw & 32768) !== 0 ? { type: "smpte", framesPerSecond: -(divisionRaw >> 8 << 24 >> 24), ticksPerFrame: divisionRaw & 255 } : { type: "ppq", ticksPerQuarterNote: divisionRaw & 32767 };
|
|
1324
|
+
const tracks = [];
|
|
1325
|
+
const tempoChanges = [];
|
|
1326
|
+
let timeSignature = null;
|
|
1327
|
+
let maxEndTick = 0;
|
|
1328
|
+
let offset = 8 + headerLength;
|
|
1329
|
+
let tracksParsed = 0;
|
|
1330
|
+
while (tracksParsed < trackCount && offset + 8 <= bytes.length) {
|
|
1331
|
+
const chunkId = Buffer.from(bytes.subarray(offset, offset + 4)).toString("ascii");
|
|
1332
|
+
const chunkLength = readU32BE(bytes, offset + 4);
|
|
1333
|
+
const dataStart = offset + 8;
|
|
1334
|
+
const dataEnd = Math.min(dataStart + chunkLength, bytes.length);
|
|
1335
|
+
if (chunkId !== "MTrk") {
|
|
1336
|
+
offset = dataEnd;
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
let pos = dataStart;
|
|
1340
|
+
let runningStatus = 0;
|
|
1341
|
+
let absoluteTick = 0;
|
|
1342
|
+
let eventCount = 0;
|
|
1343
|
+
let noteOnCount = 0;
|
|
1344
|
+
let name = null;
|
|
1345
|
+
while (pos < dataEnd) {
|
|
1346
|
+
const delta = readMidiVarLen(bytes, pos);
|
|
1347
|
+
if (!delta) break;
|
|
1348
|
+
pos = delta.next;
|
|
1349
|
+
absoluteTick += delta.value;
|
|
1350
|
+
if (pos >= dataEnd) break;
|
|
1351
|
+
let statusByte = bytes[pos];
|
|
1352
|
+
if (statusByte & 128) {
|
|
1353
|
+
runningStatus = statusByte;
|
|
1354
|
+
pos++;
|
|
1355
|
+
}
|
|
1356
|
+
statusByte = runningStatus;
|
|
1357
|
+
eventCount++;
|
|
1358
|
+
if (statusByte === 255) {
|
|
1359
|
+
const metaType = bytes[pos];
|
|
1360
|
+
pos++;
|
|
1361
|
+
const len = readMidiVarLen(bytes, pos);
|
|
1362
|
+
if (!len) break;
|
|
1363
|
+
pos = len.next;
|
|
1364
|
+
const data = bytes.subarray(pos, Math.min(pos + len.value, dataEnd));
|
|
1365
|
+
pos += len.value;
|
|
1366
|
+
if (metaType === 81 && data.length >= 3) {
|
|
1367
|
+
tempoChanges.push({ tick: absoluteTick, usPerQuarter: data[0] << 16 | data[1] << 8 | data[2] });
|
|
1368
|
+
} else if (metaType === 88 && data.length >= 2) {
|
|
1369
|
+
if (!timeSignature) timeSignature = { numerator: data[0], denominator: 2 ** data[1] };
|
|
1370
|
+
} else if (metaType === 3 && name === null) {
|
|
1371
|
+
name = Buffer.from(data).toString("utf8").trim() || null;
|
|
1372
|
+
} else if (metaType === 47) {
|
|
1373
|
+
break;
|
|
1374
|
+
}
|
|
1375
|
+
} else if (statusByte === 240 || statusByte === 247) {
|
|
1376
|
+
const len = readMidiVarLen(bytes, pos);
|
|
1377
|
+
if (!len) break;
|
|
1378
|
+
pos = len.next + len.value;
|
|
1379
|
+
} else if (statusByte >= 128) {
|
|
1380
|
+
const type = statusByte & 240;
|
|
1381
|
+
if (type === 192 || type === 208) {
|
|
1382
|
+
pos += 1;
|
|
1383
|
+
} else {
|
|
1384
|
+
if (type === 144 && (bytes[pos + 1] ?? 0) > 0) noteOnCount++;
|
|
1385
|
+
pos += 2;
|
|
1386
|
+
}
|
|
1387
|
+
} else {
|
|
1388
|
+
break;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
tracks.push({ index: tracksParsed, name, eventCount, noteOnCount });
|
|
1392
|
+
maxEndTick = Math.max(maxEndTick, absoluteTick);
|
|
1393
|
+
offset = dataEnd;
|
|
1394
|
+
tracksParsed++;
|
|
1395
|
+
}
|
|
1396
|
+
if (tracks.length === 0) return { error: "No MTrk track chunks found in this MIDI file" };
|
|
1397
|
+
const DEFAULT_US_PER_QUARTER = 5e5;
|
|
1398
|
+
const initialTempoBpm = Math.round(6e7 / (tempoChanges[0]?.usPerQuarter ?? DEFAULT_US_PER_QUARTER) * 100) / 100;
|
|
1399
|
+
let durationSeconds = null;
|
|
1400
|
+
if (division.type === "smpte") {
|
|
1401
|
+
const ticksPerSecond = division.framesPerSecond * division.ticksPerFrame;
|
|
1402
|
+
durationSeconds = ticksPerSecond > 0 ? Math.round(maxEndTick / ticksPerSecond * 1e3) / 1e3 : null;
|
|
1403
|
+
} else {
|
|
1404
|
+
const sorted = [...tempoChanges].sort((a, b) => a.tick - b.tick);
|
|
1405
|
+
let seconds = 0;
|
|
1406
|
+
let prevTick = 0;
|
|
1407
|
+
let currentUsPerQuarter = DEFAULT_US_PER_QUARTER;
|
|
1408
|
+
for (const change of sorted) {
|
|
1409
|
+
seconds += (change.tick - prevTick) / division.ticksPerQuarterNote * (currentUsPerQuarter / 1e6);
|
|
1410
|
+
prevTick = change.tick;
|
|
1411
|
+
currentUsPerQuarter = change.usPerQuarter;
|
|
1412
|
+
}
|
|
1413
|
+
seconds += (maxEndTick - prevTick) / division.ticksPerQuarterNote * (currentUsPerQuarter / 1e6);
|
|
1414
|
+
durationSeconds = Math.round(seconds * 1e3) / 1e3;
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
format,
|
|
1418
|
+
trackCount: tracks.length,
|
|
1419
|
+
division,
|
|
1420
|
+
initialTempoBpm,
|
|
1421
|
+
timeSignature,
|
|
1422
|
+
durationSeconds,
|
|
1423
|
+
tracks
|
|
1424
|
+
};
|
|
1425
|
+
}
|
|
1296
1426
|
|
|
1297
1427
|
exports.inspectGifFrames = inspectGifFrames;
|
|
1298
1428
|
exports.readExifData = readExifData;
|
|
@@ -1300,6 +1430,7 @@ exports.readFlacInfo = readFlacInfo;
|
|
|
1300
1430
|
exports.readIcoInfo = readIcoInfo;
|
|
1301
1431
|
exports.readId3Tags = readId3Tags;
|
|
1302
1432
|
exports.readImageInfo = readImageInfo;
|
|
1433
|
+
exports.readMidiInfo = readMidiInfo;
|
|
1303
1434
|
exports.readPdfMetadata = readPdfMetadata;
|
|
1304
1435
|
exports.readPdfPageDimensions = readPdfPageDimensions;
|
|
1305
1436
|
exports.readVideoInfo = readVideoInfo;
|
package/dist/tools/media.d.cts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { E as ExifData, a as ExifError, F as FlacInfo, b as FlacInfoError, G as GifInfo, c as GifInfoError, I as IcoImageEntry, d as IcoInfo, e as IcoInfoError, f as Id3Tags, g as Id3TagsError, h as ImageFormat, i as ImageInfo, j as ImageInfoError, P as PdfMetadata,
|
|
1
|
+
export { E as ExifData, a as ExifError, F as FlacInfo, b as FlacInfoError, G as GifInfo, c as GifInfoError, I as IcoImageEntry, d as IcoInfo, e as IcoInfoError, f as Id3Tags, g as Id3TagsError, h as ImageFormat, i as ImageInfo, j as ImageInfoError, M as MidiDivision, k as MidiInfo, l as MidiInfoError, n as MidiTimeSignature, o as MidiTrackInfo, P as PdfMetadata, p as PdfMetadataError, q as PdfPageDimension, r as PdfPageDimensionsResult, s as PdfPageOrientation, V as VideoInfo, t as VideoInfoError, W as WavInfo, u as WavInfoError, v as inspectGifFrames, w as readExifData, x as readFlacInfo, y as readIcoInfo, z as readId3Tags, A as readImageInfo, B as readMidiInfo, C as readPdfMetadata, D as readPdfPageDimensions, H as readVideoInfo, J as readWavInfo } from '../media-56zrIBw4.cjs';
|
package/dist/tools/media.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { E as ExifData, a as ExifError, F as FlacInfo, b as FlacInfoError, G as GifInfo, c as GifInfoError, I as IcoImageEntry, d as IcoInfo, e as IcoInfoError, f as Id3Tags, g as Id3TagsError, h as ImageFormat, i as ImageInfo, j as ImageInfoError, P as PdfMetadata,
|
|
1
|
+
export { E as ExifData, a as ExifError, F as FlacInfo, b as FlacInfoError, G as GifInfo, c as GifInfoError, I as IcoImageEntry, d as IcoInfo, e as IcoInfoError, f as Id3Tags, g as Id3TagsError, h as ImageFormat, i as ImageInfo, j as ImageInfoError, M as MidiDivision, k as MidiInfo, l as MidiInfoError, n as MidiTimeSignature, o as MidiTrackInfo, P as PdfMetadata, p as PdfMetadataError, q as PdfPageDimension, r as PdfPageDimensionsResult, s as PdfPageOrientation, V as VideoInfo, t as VideoInfoError, W as WavInfo, u as WavInfoError, v as inspectGifFrames, w as readExifData, x as readFlacInfo, y as readIcoInfo, z as readId3Tags, A as readImageInfo, B as readMidiInfo, C as readPdfMetadata, D as readPdfPageDimensions, H as readVideoInfo, J as readWavInfo } from '../media-56zrIBw4.js';
|
package/dist/tools/media.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { inspectGifFrames, readExifData, readFlacInfo, readIcoInfo, readId3Tags, readImageInfo, readPdfMetadata, readPdfPageDimensions, readVideoInfo, readWavInfo } from '../chunk-
|
|
1
|
+
export { inspectGifFrames, readExifData, readFlacInfo, readIcoInfo, readId3Tags, readImageInfo, readMidiInfo, readPdfMetadata, readPdfPageDimensions, readVideoInfo, readWavInfo } from '../chunk-PT4N4UBL.js';
|
|
2
2
|
import '../chunk-MLKGABMK.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utilix-tech/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.71.0",
|
|
4
4
|
"description": "154+ developer utility tools for Node.js: JSON, encoding, hashing, color, CSS, network and more. Runs locally, no API key required.",
|
|
5
5
|
"author": "Utilix <hello@utilix.tech>",
|
|
6
6
|
"license": "MIT",
|