sfxr.js
Version:
8-bit sound effects generator based on sfxr
104 lines (103 loc) • 3.87 kB
JavaScript
/**
* RIFFWAVE.ts - Audio encoder for HTML5 <audio> elements (TypeScript port)
* Original: Copyleft 2011 by Pedro Ladaria <pedro.ladaria at Gmail dot com>
* TypeScript port: Modern rewrite with proper typing
*
* Public Domain
*
* Notes:
* - 8 bit data is unsigned: 0..255
* - 16 bit data is signed: -32,768..32,767
*/
const FastBase64 = {
chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encLookup: [],
Init() {
for (let i = 0; i < 4096; i++) {
this.encLookup[i] = this.chars[i >> 6] + this.chars[i & 0x3F];
}
},
Encode(src) {
let len = src.length;
let dst = '';
let i = 0;
let n;
while (len > 2) {
n = (src[i] << 16) | (src[i + 1] << 8) | src[i + 2];
dst += this.encLookup[n >> 12] + this.encLookup[n & 0xFFF];
len -= 3;
i += 3;
}
if (len > 0) {
const n1 = (src[i] & 0xFC) >> 2;
let n2 = (src[i] & 0x03) << 4;
if (len > 1)
n2 |= (src[++i] & 0xF0) >> 4;
dst += this.chars[n1];
dst += this.chars[n2];
if (len == 2) {
let n3 = (src[i++] & 0x0F) << 2;
n3 |= (src[i] & 0xC0) >> 6;
dst += this.chars[n3];
}
if (len == 1)
dst += '=';
dst += '=';
}
return dst;
}
};
FastBase64.Init();
export class RiffWave {
constructor(data) {
this.data = [];
this.wav = [];
this.dataURI = '';
this.header = {
chunkId: [0x52, 0x49, 0x46, 0x46], // "RIFF"
chunkSize: 0, // 36+SubChunk2Size
format: [0x57, 0x41, 0x56, 0x45], // "WAVE"
subChunk1Id: [0x66, 0x6d, 0x74, 0x20], // "fmt "
subChunk1Size: 16, // 16 for PCM
audioFormat: 1, // PCM = 1
numChannels: 1, // Mono = 1, Stereo = 2
sampleRate: 8000, // 8000, 44100...
byteRate: 0, // SampleRate*NumChannels*BitsPerSample/8
blockAlign: 0, // NumChannels*BitsPerSample/8
bitsPerSample: 8, // 8 bits = 8, 16 bits = 16
subChunk2Id: [0x64, 0x61, 0x74, 0x61], // "data"
subChunk2Size: 0 // data size
};
if (data) {
this.Make(data);
}
}
u32ToArray(i) {
return [i & 0xFF, (i >> 8) & 0xFF, (i >> 16) & 0xFF, (i >> 24) & 0xFF];
}
u16ToArray(i) {
return [i & 0xFF, (i >> 8) & 0xFF];
}
split16bitArray(data) {
const r = [];
let j = 0;
const len = data.length;
for (let i = 0; i < len; i++) {
r[j++] = data[i] & 0xFF;
r[j++] = (data[i] >> 8) & 0xFF;
}
return r;
}
Make(data) {
this.data = data;
this.header.byteRate = (this.header.sampleRate * this.header.numChannels * this.header.bitsPerSample) >> 3;
this.header.blockAlign = (this.header.numChannels * this.header.bitsPerSample) >> 3;
this.header.subChunk2Size = this.data.length;
this.header.chunkSize = 36 + this.header.subChunk2Size;
this.wav = this.header.chunkId.concat(this.u32ToArray(this.header.chunkSize), this.header.format, this.header.subChunk1Id, this.u32ToArray(this.header.subChunk1Size), this.u16ToArray(this.header.audioFormat), this.u16ToArray(this.header.numChannels), this.u32ToArray(this.header.sampleRate), this.u32ToArray(this.header.byteRate), this.u16ToArray(this.header.blockAlign), this.u16ToArray(this.header.bitsPerSample), this.header.subChunk2Id, this.u32ToArray(this.header.subChunk2Size), this.data);
this.dataURI = 'data:audio/wav;base64,' + FastBase64.Encode(this.wav);
}
}
// For backward compatibility
export const RIFFWAVE = RiffWave;
export default RiffWave;