-
Notifications
You must be signed in to change notification settings - Fork 14
/
audio-decode.js
92 lines (83 loc) · 2.75 KB
/
audio-decode.js
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
82
83
84
85
86
87
88
89
90
91
92
/**
* Web-Audio-API decoder
* @module audio-decode
*/
import getType from 'audio-type';
import AudioBufferShim from 'audio-buffer';
const AudioBuffer = globalThis.AudioBuffer || AudioBufferShim;
/**
* Decode an audio buffer.
*
* @param {ArrayBuffer | Uint8Array} buf - The audio data to decode.
* @returns {Promise<AudioBuffer>} A promise that resolves to the decoded audio buffer.
* @throws {Error} Throws an error if the decode target is invalid or if the audio format is not supported.
*/
export default async function audioDecode(buf) {
if (!buf && !(buf.length || buf.buffer)) throw Error('Bad decode target')
buf = new Uint8Array(buf.buffer || buf)
let type = getType(buf);
if (!type) throw Error('Cannot detect audio format');
if (!decoders[type]) throw Error('Missing decoder for ' + type + ' format')
return decoders[type](buf)
};
export const decoders = {
async oga(buf) {
let { decoder } = decoders.oga
if (!decoder) {
let { OggVorbisDecoder } = await import('@wasm-audio-decoders/ogg-vorbis')
await (decoders.oga.decoder = decoder = new OggVorbisDecoder()).ready;
} else await decoder.reset()
return buf && createBuffer(await decoder.decodeFile(buf))
},
async mp3(buf) {
let { decoder } = decoders.mp3
if (!decoder) {
const { MPEGDecoder } = await import('mpg123-decoder')
await (decoders.mp3.decoder = decoder = new MPEGDecoder()).ready;
}
else await decoder.reset()
return buf && createBuffer(await decoder.decode(buf))
},
async flac(buf) {
let { decoder } = decoders.flac
if (!decoder) {
const { FLACDecoder } = await import('@wasm-audio-decoders/flac')
await (decoders.flac.decoder = decoder = new FLACDecoder()).ready
}
else await decoder.reset()
return buf && createBuffer(await decoder.decode(buf))
},
async opus(buf) {
let { decoder } = decoders.opus
if (!decoder) {
const { OggOpusDecoder } = await import('ogg-opus-decoder')
await (decoders.opus.decoder = decoder = new OggOpusDecoder()).ready
}
else await decoder.reset()
return buf && createBuffer(await decoder.decodeFile(buf))
},
async wav(buf) {
let { decode } = decoders.wav
if (!decode) {
let module = await import('node-wav')
decode = decoders.wav.decode = module.default.decode
}
return buf && createBuffer(await decode(buf))
},
async qoa(buf) {
let { decode } = decoders.qoa
if (!decode) {
decoders.qoa.decode = decode = (await import('qoa-format')).decode
}
return buf && createBuffer(await decode(buf))
}
}
function createBuffer({ channelData, sampleRate }) {
let audioBuffer = new AudioBuffer({
sampleRate,
length: channelData[0].length,
numberOfChannels: channelData.length
})
for (let ch = 0; ch < channelData.length; ch++) audioBuffer.getChannelData(ch).set(channelData[ch])
return audioBuffer
}