-
-
Notifications
You must be signed in to change notification settings - Fork 1k
/
get-audio-data.ts
81 lines (68 loc) · 2.01 KB
/
get-audio-data.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import {isRemoteAsset} from './is-remote-asset';
import {pLimit} from './p-limit';
import type {AudioData} from './types';
const metadataCache: {[key: string]: AudioData} = {};
const limit = pLimit(3);
const fetchWithCorsCatch = async (src: string) => {
try {
const response = await fetch(src, {
mode: 'cors',
referrerPolicy: 'no-referrer-when-downgrade',
});
return response;
} catch (err) {
const error = err as Error;
if (
// Chrome
error.message.includes('Failed to fetch') ||
// Safari
error.message.includes('Load failed') ||
// Firefox
error.message.includes('NetworkError when attempting to fetch resource')
) {
throw new TypeError(
`Failed to read from ${src}: ${error.message}. Does the resource support CORS?`,
);
}
throw err;
}
};
type Options = {
sampleRate?: number;
};
const fn = async (src: string, options?: Options): Promise<AudioData> => {
if (metadataCache[src]) {
return metadataCache[src];
}
if (typeof document === 'undefined') {
throw new Error('getAudioData() is only available in the browser.');
}
const audioContext = new AudioContext({
sampleRate: options?.sampleRate ?? 48000,
});
const response = await fetchWithCorsCatch(src);
const arrayBuffer = await response.arrayBuffer();
const wave = await audioContext.decodeAudioData(arrayBuffer);
const channelWaveforms = new Array(wave.numberOfChannels)
.fill(true)
.map((_, channel) => {
return wave.getChannelData(channel);
});
const metadata: AudioData = {
channelWaveforms,
sampleRate: wave.sampleRate,
durationInSeconds: wave.duration,
numberOfChannels: wave.numberOfChannels,
resultId: String(Math.random()),
isRemote: isRemoteAsset(src),
};
metadataCache[src] = metadata;
return metadata;
};
/**
* @description Takes an audio src, loads it and returns data and metadata for the specified source.
* @see [Documentation](https://www.remotion.dev/docs/get-audio-data)
*/
export const getAudioData = (src: string, options?: Options) => {
return limit(fn, src, options);
};