@remotion/media-utils 4.0.392 → 4.0.393
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/package.json +6 -6
- package/dist/get-partial-media-data-2.d.ts +0 -7
- package/dist/get-partial-media-data-2.js +0 -104
- package/dist/get-partial-media-data.d.ts +0 -7
- package/dist/get-partial-media-data.js +0 -105
- package/dist/get-partial-wave-data.d.ts +0 -12
- package/dist/get-partial-wave-data.js +0 -40
- package/dist/probe-wave-file.d.ts +0 -12
- package/dist/probe-wave-file.js +0 -95
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"url": "https://github.com/remotion-dev/remotion/tree/main/packages/media-utils"
|
|
4
4
|
},
|
|
5
5
|
"name": "@remotion/media-utils",
|
|
6
|
-
"version": "4.0.
|
|
6
|
+
"version": "4.0.393",
|
|
7
7
|
"description": "Utilities for working with media files",
|
|
8
8
|
"main": "dist/index.js",
|
|
9
9
|
"sideEffects": false,
|
|
@@ -18,17 +18,17 @@
|
|
|
18
18
|
"url": "https://github.com/remotion-dev/remotion/issues"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@remotion/media-parser": "4.0.
|
|
22
|
-
"@remotion/webcodecs": "4.0.
|
|
23
|
-
"remotion": "4.0.
|
|
24
|
-
"mediabunny": "1.
|
|
21
|
+
"@remotion/media-parser": "4.0.393",
|
|
22
|
+
"@remotion/webcodecs": "4.0.393",
|
|
23
|
+
"remotion": "4.0.393",
|
|
24
|
+
"mediabunny": "1.27.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"react": ">=16.8.0",
|
|
28
28
|
"react-dom": ">=16.8.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@remotion/eslint-config-internal": "4.0.
|
|
31
|
+
"@remotion/eslint-config-internal": "4.0.393",
|
|
32
32
|
"eslint": "9.19.0"
|
|
33
33
|
},
|
|
34
34
|
"keywords": [
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPartialMediaData = void 0;
|
|
4
|
-
const media_parser_1 = require("@remotion/media-parser");
|
|
5
|
-
const webcodecs_1 = require("@remotion/webcodecs");
|
|
6
|
-
const getPartialMediaData = async ({ src, fromSeconds, toSeconds, channelIndex, signal, }) => {
|
|
7
|
-
const controller = (0, media_parser_1.mediaParserController)();
|
|
8
|
-
// Collect audio samples
|
|
9
|
-
const audioSamples = [];
|
|
10
|
-
// Abort if the signal is already aborted
|
|
11
|
-
if (signal.aborted) {
|
|
12
|
-
throw new Error('Operation was aborted');
|
|
13
|
-
}
|
|
14
|
-
try {
|
|
15
|
-
if (fromSeconds > 0) {
|
|
16
|
-
controller.seek(fromSeconds);
|
|
17
|
-
}
|
|
18
|
-
await (0, media_parser_1.parseMedia)({
|
|
19
|
-
src,
|
|
20
|
-
controller,
|
|
21
|
-
onAudioTrack: ({ track }) => {
|
|
22
|
-
if (!track) {
|
|
23
|
-
throw new Error('No audio track found');
|
|
24
|
-
}
|
|
25
|
-
const audioDecoder = (0, webcodecs_1.createAudioDecoder)({
|
|
26
|
-
track,
|
|
27
|
-
onFrame: (sample) => {
|
|
28
|
-
if (signal.aborted) {
|
|
29
|
-
sample.close();
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
// For multi-channel audio, we need to handle channels properly
|
|
33
|
-
const { numberOfChannels } = sample;
|
|
34
|
-
const samplesPerChannel = sample.numberOfFrames;
|
|
35
|
-
let data;
|
|
36
|
-
if (numberOfChannels === 1) {
|
|
37
|
-
// Mono audio
|
|
38
|
-
data = new Float32Array(sample.allocationSize({ format: 'f32', planeIndex: 0 }));
|
|
39
|
-
sample.copyTo(data, { format: 'f32', planeIndex: 0 });
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
// Multi-channel audio: extract specific channel
|
|
43
|
-
const allChannelsData = new Float32Array(sample.allocationSize({ format: 'f32', planeIndex: 0 }));
|
|
44
|
-
sample.copyTo(allChannelsData, { format: 'f32', planeIndex: 0 });
|
|
45
|
-
// Extract the specific channel (interleaved audio)
|
|
46
|
-
data = new Float32Array(samplesPerChannel);
|
|
47
|
-
for (let i = 0; i < samplesPerChannel; i++) {
|
|
48
|
-
data[i] = allChannelsData[i * numberOfChannels + channelIndex];
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
audioSamples.push(data);
|
|
52
|
-
sample.close();
|
|
53
|
-
},
|
|
54
|
-
onError(error) {
|
|
55
|
-
throw error;
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
|
-
// Listen for abort signal
|
|
59
|
-
const onAbort = () => {
|
|
60
|
-
controller.abort();
|
|
61
|
-
if (audioDecoder) {
|
|
62
|
-
audioDecoder.close();
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
66
|
-
return async (sample) => {
|
|
67
|
-
if (signal.aborted) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
// Convert timestamp using the track's timescale
|
|
71
|
-
const time = sample.timestamp / track.timescale;
|
|
72
|
-
console.log(time);
|
|
73
|
-
// Stop immediately when we reach our target time
|
|
74
|
-
if (time >= toSeconds) {
|
|
75
|
-
// abort media parsing, we reached the point where we want to stop
|
|
76
|
-
controller.abort();
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
// Decode the sample using the sample directly
|
|
80
|
-
await audioDecoder.waitForQueueToBeLessThan(10);
|
|
81
|
-
// we're waiting for the queue above anyway, enqueue in sync mode
|
|
82
|
-
audioDecoder.decode(sample);
|
|
83
|
-
};
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
catch (err) {
|
|
88
|
-
const isAbortedByTimeCutoff = (0, media_parser_1.hasBeenAborted)(err);
|
|
89
|
-
// Don't throw if we stopped the parsing ourselves
|
|
90
|
-
if (!isAbortedByTimeCutoff && !signal.aborted) {
|
|
91
|
-
throw err;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// Simply concatenate all audio data since windowing handles the time ranges
|
|
95
|
-
const totalSamples = audioSamples.reduce((sum, sample) => sum + sample.length, 0);
|
|
96
|
-
const result = new Float32Array(totalSamples);
|
|
97
|
-
let offset = 0;
|
|
98
|
-
for (const audioSample of audioSamples) {
|
|
99
|
-
result.set(audioSample, offset);
|
|
100
|
-
offset += audioSample.length;
|
|
101
|
-
}
|
|
102
|
-
return result;
|
|
103
|
-
};
|
|
104
|
-
exports.getPartialMediaData = getPartialMediaData;
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPartialMediaData = void 0;
|
|
4
|
-
const media_parser_1 = require("@remotion/media-parser");
|
|
5
|
-
const worker_1 = require("@remotion/media-parser/worker");
|
|
6
|
-
const webcodecs_1 = require("@remotion/webcodecs");
|
|
7
|
-
const getPartialMediaData = async ({ src, fromSeconds, toSeconds, channelIndex, signal, }) => {
|
|
8
|
-
const controller = (0, media_parser_1.mediaParserController)();
|
|
9
|
-
// Collect audio samples
|
|
10
|
-
const audioSamples = [];
|
|
11
|
-
// Abort if the signal is already aborted
|
|
12
|
-
if (signal.aborted) {
|
|
13
|
-
throw new Error('Operation was aborted');
|
|
14
|
-
}
|
|
15
|
-
try {
|
|
16
|
-
if (fromSeconds > 0) {
|
|
17
|
-
controller.seek(fromSeconds);
|
|
18
|
-
}
|
|
19
|
-
await (0, worker_1.parseMediaOnWebWorker)({
|
|
20
|
-
src,
|
|
21
|
-
controller,
|
|
22
|
-
onAudioTrack: ({ track }) => {
|
|
23
|
-
if (!track) {
|
|
24
|
-
throw new Error('No audio track found');
|
|
25
|
-
}
|
|
26
|
-
const audioDecoder = (0, webcodecs_1.createAudioDecoder)({
|
|
27
|
-
track,
|
|
28
|
-
onFrame: (sample) => {
|
|
29
|
-
if (signal.aborted) {
|
|
30
|
-
sample.close();
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
// For multi-channel audio, we need to handle channels properly
|
|
34
|
-
const { numberOfChannels } = sample;
|
|
35
|
-
const samplesPerChannel = sample.numberOfFrames;
|
|
36
|
-
let data;
|
|
37
|
-
if (numberOfChannels === 1) {
|
|
38
|
-
// Mono audio
|
|
39
|
-
data = new Float32Array(sample.allocationSize({ format: 'f32', planeIndex: 0 }));
|
|
40
|
-
sample.copyTo(data, { format: 'f32', planeIndex: 0 });
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
// Multi-channel audio: extract specific channel
|
|
44
|
-
const allChannelsData = new Float32Array(sample.allocationSize({ format: 'f32', planeIndex: 0 }));
|
|
45
|
-
sample.copyTo(allChannelsData, { format: 'f32', planeIndex: 0 });
|
|
46
|
-
// Extract the specific channel (interleaved audio)
|
|
47
|
-
data = new Float32Array(samplesPerChannel);
|
|
48
|
-
for (let i = 0; i < samplesPerChannel; i++) {
|
|
49
|
-
data[i] = allChannelsData[i * numberOfChannels + channelIndex];
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
audioSamples.push(data);
|
|
53
|
-
sample.close();
|
|
54
|
-
},
|
|
55
|
-
onError(error) {
|
|
56
|
-
throw error;
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
// Listen for abort signal
|
|
60
|
-
const onAbort = () => {
|
|
61
|
-
controller.abort();
|
|
62
|
-
if (audioDecoder) {
|
|
63
|
-
audioDecoder.close();
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
signal.addEventListener('abort', onAbort, { once: true });
|
|
67
|
-
return async (sample) => {
|
|
68
|
-
if (signal.aborted) {
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
// Convert timestamp using the track's timescale
|
|
72
|
-
const time = sample.timestamp / track.timescale;
|
|
73
|
-
// Stop immediately when we reach our target time
|
|
74
|
-
if (time >= toSeconds) {
|
|
75
|
-
// abort media parsing, we reached the point where we want to stop
|
|
76
|
-
controller.abort();
|
|
77
|
-
await audioDecoder.flush();
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
// Decode the sample using the sample directly
|
|
81
|
-
await audioDecoder.waitForQueueToBeLessThan(10);
|
|
82
|
-
// we're waiting for the queue above anyway, enqueue in sync mode
|
|
83
|
-
audioDecoder.decode(sample);
|
|
84
|
-
};
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
catch (err) {
|
|
89
|
-
const isAbortedByTimeCutoff = (0, media_parser_1.hasBeenAborted)(err);
|
|
90
|
-
// Don't throw if we stopped the parsing ourselves
|
|
91
|
-
if (!isAbortedByTimeCutoff && !signal.aborted) {
|
|
92
|
-
throw err;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
// Simply concatenate all audio data since windowing handles the time ranges
|
|
96
|
-
const totalSamples = audioSamples.reduce((sum, sample) => sum + sample.length, 0);
|
|
97
|
-
const result = new Float32Array(totalSamples);
|
|
98
|
-
let offset = 0;
|
|
99
|
-
for (const audioSample of audioSamples) {
|
|
100
|
-
result.set(audioSample, offset);
|
|
101
|
-
offset += audioSample.length;
|
|
102
|
-
}
|
|
103
|
-
return result;
|
|
104
|
-
};
|
|
105
|
-
exports.getPartialMediaData = getPartialMediaData;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare const getPartialWaveData: ({ dataOffset, src, bitsPerSample, channelIndex, sampleRate, fromSeconds, toSeconds, blockAlign, fileSize, signal, }: {
|
|
2
|
-
dataOffset: number;
|
|
3
|
-
src: string;
|
|
4
|
-
bitsPerSample: number;
|
|
5
|
-
channelIndex: number;
|
|
6
|
-
sampleRate: number;
|
|
7
|
-
fromSeconds: number;
|
|
8
|
-
toSeconds: number;
|
|
9
|
-
blockAlign: number;
|
|
10
|
-
fileSize: number;
|
|
11
|
-
signal: AbortSignal;
|
|
12
|
-
}) => Promise<Float32Array<ArrayBuffer>>;
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPartialWaveData = void 0;
|
|
4
|
-
const fetch_with_cors_catch_1 = require("./fetch-with-cors-catch");
|
|
5
|
-
const probe_wave_file_1 = require("./probe-wave-file");
|
|
6
|
-
const getPartialWaveData = async ({ dataOffset, src, bitsPerSample, channelIndex, sampleRate, fromSeconds, toSeconds, blockAlign, fileSize, signal, }) => {
|
|
7
|
-
const startByte = dataOffset + Math.floor(fromSeconds * sampleRate) * blockAlign;
|
|
8
|
-
const endByte = Math.min(fileSize, (dataOffset + Math.floor(toSeconds * sampleRate)) * blockAlign) - 1;
|
|
9
|
-
const response = await (0, fetch_with_cors_catch_1.fetchWithCorsCatch)(src, {
|
|
10
|
-
headers: {
|
|
11
|
-
range: `bytes=${startByte}-${endByte}`,
|
|
12
|
-
},
|
|
13
|
-
signal,
|
|
14
|
-
});
|
|
15
|
-
if (response.status === 416) {
|
|
16
|
-
throw new Error(`Tried to read bytes ${startByte}-${endByte} from ${src}, but the response status code was 416 "Range Not Satisfiable". Were too many bytes requested? The file is ${fileSize} bytes long.`);
|
|
17
|
-
}
|
|
18
|
-
if (response.status !== 206) {
|
|
19
|
-
throw new Error(`Tried to read bytes ${startByte}-${endByte} from ${src}, but the response status code was ${response.status} (expected was 206). This means the server might not support returning a partial response.`);
|
|
20
|
-
}
|
|
21
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
22
|
-
const uintArray = new Uint8Array(arrayBuffer);
|
|
23
|
-
const samples = new Float32Array(uintArray.length / blockAlign);
|
|
24
|
-
for (let i = 0; i < uintArray.length; i += blockAlign) {
|
|
25
|
-
const sampleStart = i + channelIndex * (bitsPerSample / 8);
|
|
26
|
-
let sample;
|
|
27
|
-
if (bitsPerSample === 16) {
|
|
28
|
-
sample = (0, probe_wave_file_1.getInt16AsFloat)(uintArray, sampleStart);
|
|
29
|
-
}
|
|
30
|
-
else if (bitsPerSample === 8) {
|
|
31
|
-
sample = (0, probe_wave_file_1.getInt8AsFloat)(uintArray, sampleStart);
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
throw new Error(`Unsupported bits per sample: ${bitsPerSample}`);
|
|
35
|
-
}
|
|
36
|
-
samples[i / blockAlign] = sample;
|
|
37
|
-
}
|
|
38
|
-
return samples;
|
|
39
|
-
};
|
|
40
|
-
exports.getPartialWaveData = getPartialWaveData;
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export declare const getInt16AsFloat: (bytes: Uint8Array, offset: number) => number;
|
|
2
|
-
export declare const getInt8AsFloat: (bytes: Uint8Array, offset: number) => number;
|
|
3
|
-
export type WaveProbe = {
|
|
4
|
-
dataOffset: number;
|
|
5
|
-
bitsPerSample: number;
|
|
6
|
-
numberOfChannels: number;
|
|
7
|
-
sampleRate: number;
|
|
8
|
-
blockAlign: number;
|
|
9
|
-
fileSize: number;
|
|
10
|
-
durationInSeconds: number;
|
|
11
|
-
};
|
|
12
|
-
export declare const probeWaveFile: (src: string, probeSize?: number) => Promise<WaveProbe>;
|
package/dist/probe-wave-file.js
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.probeWaveFile = exports.getInt8AsFloat = exports.getInt16AsFloat = void 0;
|
|
4
|
-
const fetch_with_cors_catch_1 = require("./fetch-with-cors-catch");
|
|
5
|
-
const getUint32 = (bytes, offset) => {
|
|
6
|
-
const val1 = bytes[offset + 3];
|
|
7
|
-
const val2 = bytes[offset + 2];
|
|
8
|
-
const val3 = bytes[offset + 1];
|
|
9
|
-
const val4 = bytes[offset];
|
|
10
|
-
return (val1 << 24) | (val2 << 16) | (val3 << 8) | val4;
|
|
11
|
-
};
|
|
12
|
-
const getUint16 = (bytes, offset) => {
|
|
13
|
-
const val1 = bytes[offset + 1];
|
|
14
|
-
const val2 = bytes[offset];
|
|
15
|
-
return (val1 << 8) | val2;
|
|
16
|
-
};
|
|
17
|
-
const getInt16AsFloat = (bytes, offset) => {
|
|
18
|
-
if (offset >= bytes.length) {
|
|
19
|
-
throw new Error(`Tried to read a 16-bit integer from offset ${offset} but the array length is ${bytes.length}`);
|
|
20
|
-
}
|
|
21
|
-
const val1 = bytes[offset + 1];
|
|
22
|
-
const val2 = bytes[offset];
|
|
23
|
-
const int16 = (val1 << 8) | val2;
|
|
24
|
-
return ((int16 << 16) >> 16) / 32768;
|
|
25
|
-
};
|
|
26
|
-
exports.getInt16AsFloat = getInt16AsFloat;
|
|
27
|
-
const getInt8AsFloat = (bytes, offset) => {
|
|
28
|
-
if (offset >= bytes.length) {
|
|
29
|
-
throw new Error(`Tried to read an 8-bit integer from offset ${offset} but the array length is ${bytes.length}`);
|
|
30
|
-
}
|
|
31
|
-
return bytes[offset] / 128;
|
|
32
|
-
};
|
|
33
|
-
exports.getInt8AsFloat = getInt8AsFloat;
|
|
34
|
-
const probeWaveFile = async (src, probeSize = 1024) => {
|
|
35
|
-
const response = await (0, fetch_with_cors_catch_1.fetchWithCorsCatch)(src, {
|
|
36
|
-
headers: {
|
|
37
|
-
range: `bytes=0-${probeSize - 1}`,
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
if (response.status === 416) {
|
|
41
|
-
throw new Error(`Tried to read bytes 0-1024 from ${src}, but the response status code was 416 "Range Not Satisfiable". Is the file at least 256 bytes long?`);
|
|
42
|
-
}
|
|
43
|
-
if (response.status !== 206) {
|
|
44
|
-
throw new Error(`Tried to read bytes 0-1024 from ${src}, but the response status code was ${response.status} (expected was 206). This means the server might not support returning a partial response.`);
|
|
45
|
-
}
|
|
46
|
-
const buffer = await response.arrayBuffer();
|
|
47
|
-
const uintArray = new Uint8Array(buffer);
|
|
48
|
-
const shouldBeRiff = new TextDecoder().decode(uintArray.slice(0, 4));
|
|
49
|
-
if (shouldBeRiff !== 'RIFF') {
|
|
50
|
-
throw new Error('getPartialAudioData() requires a WAVE file, but the first bytes are not RIFF. ');
|
|
51
|
-
}
|
|
52
|
-
const size = getUint32(uintArray, 4);
|
|
53
|
-
const shouldBeWAVE = new TextDecoder().decode(uintArray.slice(8, 12));
|
|
54
|
-
if (shouldBeWAVE !== 'WAVE') {
|
|
55
|
-
throw new Error('getPartialAudioData() requires a WAVE file, but the bytes 8-11 are not "WAVE". ');
|
|
56
|
-
}
|
|
57
|
-
const shouldBeFmt = new TextDecoder().decode(uintArray.slice(12, 16));
|
|
58
|
-
if (shouldBeFmt !== 'fmt ') {
|
|
59
|
-
throw new Error('getPartialAudioData() requires a WAVE file, but the bytes 12-15 are not "fmt ". ');
|
|
60
|
-
}
|
|
61
|
-
// const chunkSize = toUint32(uintArray.slice(16, 20));
|
|
62
|
-
const audioFormat = getUint16(uintArray, 20);
|
|
63
|
-
if (audioFormat !== 1) {
|
|
64
|
-
throw new Error('getPartialAudioData() supports only a WAVE file with PCM audio format, but the audio format is not PCM. ');
|
|
65
|
-
}
|
|
66
|
-
const numberOfChannels = getUint16(uintArray, 22);
|
|
67
|
-
const sampleRate = getUint32(uintArray, 24);
|
|
68
|
-
const blockAlign = getUint16(uintArray, 32);
|
|
69
|
-
const bitsPerSample = getUint16(uintArray, 34);
|
|
70
|
-
let offset = 36;
|
|
71
|
-
const shouldBeDataOrList = new TextDecoder().decode(uintArray.slice(offset, offset + 4));
|
|
72
|
-
if (shouldBeDataOrList === 'LIST') {
|
|
73
|
-
const listSize = getUint32(uintArray, 40);
|
|
74
|
-
offset += listSize;
|
|
75
|
-
offset += 8;
|
|
76
|
-
}
|
|
77
|
-
if (offset + 4 > probeSize) {
|
|
78
|
-
return (0, exports.probeWaveFile)(src, offset + 4);
|
|
79
|
-
}
|
|
80
|
-
const shouldBeData = new TextDecoder().decode(uintArray.slice(offset, offset + 4));
|
|
81
|
-
if (shouldBeData !== 'data') {
|
|
82
|
-
throw new Error(`getPartialAudioData() requires a WAVE file, but the bytes ${offset}-${offset + 4} are not "data". `);
|
|
83
|
-
}
|
|
84
|
-
const dataSize = getUint32(uintArray, offset + 4);
|
|
85
|
-
return {
|
|
86
|
-
dataOffset: offset + 8,
|
|
87
|
-
bitsPerSample,
|
|
88
|
-
numberOfChannels,
|
|
89
|
-
sampleRate,
|
|
90
|
-
blockAlign,
|
|
91
|
-
fileSize: size,
|
|
92
|
-
durationInSeconds: dataSize / (sampleRate * blockAlign),
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
exports.probeWaveFile = probeWaveFile;
|