forked from s-macke/SAM
-
Notifications
You must be signed in to change notification settings - Fork 65
/
player.es6
148 lines (134 loc) · 3.54 KB
/
player.es6
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import {text2Uint8Array, Uint32ToUint8Array, Uint16ToUint8Array} from '../util/util.es6';
/**
*
* @param {AudioContext} context
* @param {Float32Array} audiobuffer
*
* @return {Promise}
*/
const Play = (context, audiobuffer) => {
let abort;
let promise = new Promise((resolve, reject) => {
let source = context.createBufferSource();
let soundBuffer = context.createBuffer(1, audiobuffer.length, 22050);
let buffer = soundBuffer.getChannelData(0);
for(let i=0; i<audiobuffer.length; i++) {
buffer[i] = audiobuffer[i];
}
source.buffer = soundBuffer;
source.connect(context.destination);
source.onended = () => {
resolve(true);
};
abort = reason => {
source.disconnect();
reject(reason);
};
source.start(0);
});
promise.abort = abort;
return promise;
}
let context = null;
/**
* Play an audio buffer.
*
* @param {Float32Array} audiobuffer
*
* @return {Promise}
*/
export const PlayBuffer = (audiobuffer) => {
if (null === context) {
context = new AudioContext();
}
if (!context) {
if (process.env.NODE_ENV === 'development') {
throw new Error('No player available!');
}
throw new Error();
}
return Play(context, audiobuffer);
}
/**
* Convert a Uint8Array wave buffer to a Float32Array WaveBuffer
*
* @param {Uint8Array} buffer
*
* @return {Float32Array}
*/
export const Uint8ArrayToFloat32Array = (buffer) => {
const audio = new Float32Array(buffer.length);
for(let i=0; i < buffer.length; i++) {
audio[i] = (buffer[i] - 128) / 256;
}
return audio
}
/**
* Converts a Uint8Array buffer to a Uint8Array wave buffer
*
* @param {Uint8Array} audiobuffer
*
* @return {Uint8Array}
*/
export const ToWavBuffer = (audiobuffer) => {
// Calculate buffer size.
const realbuffer = new Uint8Array(
4 + // "RIFF"
4 + // uint32 filesize
4 + // "WAVE"
4 + // "fmt "
4 + // uint32 fmt length
2 + // uint16 fmt
2 + // uint16 channels
4 + // uint32 sample rate
4 + // uint32 bytes per second
2 + // uint16 block align
2 + // uint16 bits per sample
4 + // "data"
4 + // uint32 chunk length
audiobuffer.length
);
let pos=0;
const write = (buffer) => {
realbuffer.set(buffer, pos);
pos+=buffer.length;
};
//RIFF header
write(text2Uint8Array('RIFF')); // chunkID
write(Uint32ToUint8Array(audiobuffer.length + 12 + 16 + 8 - 8)); // ChunkSize
write(text2Uint8Array('WAVE')); // riffType
//format chunk
write(text2Uint8Array('fmt '));
write(Uint32ToUint8Array(16)); // ChunkSize
write(Uint16ToUint8Array(1)); // wFormatTag - 1 = PCM
write(Uint16ToUint8Array(1)); // channels
write(Uint32ToUint8Array(22050)); // samplerate
write(Uint32ToUint8Array(22050)); // bytes/second
write(Uint16ToUint8Array(1)); // blockalign
write(Uint16ToUint8Array(8)); // bits per sample
//data chunk
write(text2Uint8Array('data'));
write(Uint32ToUint8Array(audiobuffer.length)); // buffer length
write(audiobuffer);
return realbuffer;
};
/**
*
* @param {Uint8Array} audiobuffer
*
* @return void
*/
export const RenderBuffer = (audiobuffer) => {
const filename = "sam.wav";
const blob = new Blob([ToWavBuffer(audiobuffer)], { type: "audio/vnd.wave" });
const url = (window.URL || window.webkitURL);
const fileURL = url.createObjectURL(blob);
const a = document.createElement('a');
a.href = fileURL;
a.target = '_blank';
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
url.revokeObjectURL(fileURL);
}