-
Notifications
You must be signed in to change notification settings - Fork 293
/
speakClient.js
105 lines (97 loc) · 3.07 KB
/
speakClient.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
93
94
95
96
97
98
99
100
101
102
103
104
var speakWorker;
try {
speakWorker = new Worker('speakWorker.js');
} catch(e) {
console.log('speak.js warning: no worker support');
}
function speak(text, args) {
var PROFILE = 1;
function parseWav(wav) {
function readInt(i, bytes) {
var ret = 0;
var shft = 0;
while (bytes) {
ret += wav[i] << shft;
shft += 8;
i++;
bytes--;
}
return ret;
}
if (readInt(20, 2) != 1) throw 'Invalid compression code, not PCM';
if (readInt(22, 2) != 1) throw 'Invalid number of channels, not 1';
return {
sampleRate: readInt(24, 4),
bitsPerSample: readInt(34, 2),
samples: wav.subarray(44)
};
}
function playHTMLAudioElement(wav) {
function encode64(data) {
var BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var PAD = '=';
var ret = '';
var leftchar = 0;
var leftbits = 0;
for (var i = 0; i < data.length; i++) {
leftchar = (leftchar << 8) | data[i];
leftbits += 8;
while (leftbits >= 6) {
var curr = (leftchar >> (leftbits-6)) & 0x3f;
leftbits -= 6;
ret += BASE[curr];
}
}
if (leftbits == 2) {
ret += BASE[(leftchar&3) << 4];
ret += PAD + PAD;
} else if (leftbits == 4) {
ret += BASE[(leftchar&0xf) << 2];
ret += PAD;
}
return ret;
}
document.getElementById("audio").innerHTML=("<audio id=\"player\" src=\"data:audio/x-wav;base64,"+encode64(wav)+"\">");
document.getElementById("player").play();
}
function playAudioDataAPI(data) {
try {
var output = new Audio();
output.mozSetup(1, data.sampleRate);
var num = data.samples.length;
var buffer = data.samples;
var f32Buffer = new Float32Array(num);
for (var i = 0; i < num; i++) {
var value = buffer[i<<1] + (buffer[(i<<1)+1]<<8);
if (value >= 0x8000) value |= ~0x7FFF;
f32Buffer[i] = value / 0x8000;
}
output.mozWriteAudio(f32Buffer);
return true;
} catch(e) {
return false;
}
}
function handleWav(wav) {
var startTime = Date.now();
var data = parseWav(wav); // validate the data and parse it
// TODO: try playAudioDataAPI(data), and fallback if failed
playHTMLAudioElement(wav);
if (PROFILE) console.log('speak.js: wav processing took ' + (Date.now()-startTime).toFixed(2) + ' ms');
}
if (args && args.noWorker) {
// Do everything right now. speakGenerator.js must have been loaded.
var startTime = Date.now();
var wav = generateSpeech(text, args);
if (PROFILE) console.log('speak.js: processing took ' + (Date.now()-startTime).toFixed(2) + ' ms');
handleWav(wav);
} else {
// Call the worker, which will return a wav that we then play
var startTime = Date.now();
speakWorker.onmessage = function(event) {
if (PROFILE) console.log('speak.js: worker processing took ' + (Date.now()-startTime).toFixed(2) + ' ms');
handleWav(event.data);
};
speakWorker.postMessage({ text: text, args: args });
}
}