前回からの続きで、Javascripソースの記述について説明します。
2. wavファイル生成Javascripソースの記述
app.jsを作成します。ExpressやEJSについては、偉大な先達の方々の知見1や、公式サイト、BluemixにDeployするためのportの設定については手順3の記述を参考にしています。なお、'STTで音声認識Javascriptソースが動作するURLを記述'には、後述の音声認識処理サーバーのURLを設定します。
ところで、コメント部分は、クロスオリジン HTTP リクエストの制限解除を試みた設定ですが、有効化できていません。不要ですが、敢えて、残しています。また、CDNとしてajaxを使うためのタグがあります。結果としてajaxは使っていないので不要ですが、これまた、敢えて残しています。
:app.js
var express = require('express');
var ejs = require("ejs");
var app = express();
// app.use(function(req, res, next) {
// res.setHeader("Access-Control-Allow-Origin", "https://stream.watsonplatform.net");
// res.setHeader("Access-Control-Allow-Methods", "POST, GET, DELETE, PUT");
// res.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type");
// res.setHeader("Access-Control-Allow-Credentials", true);
// return next();
// });
app.engine('ejs',ejs.renderFile);
app.get("/", function(req, res){
res.render('test.ejs',
{title: 'Test Page' ,
content: 'this is test.'});
});
//var server = app.listen(3000, function(){
// console.log('Server is running!');
//})
var server = app.listen(process.env.PORT || 3000, function() {
console.log('Listening on port %d', server.address().port);
});
次に、test.ejsを作成します。EJSの仕組みについては、
偉大な先達の方々の知見1を、WebRTC、wavファイルの生成については、知見2、知見3を参考にしています(参考というより、そのモノです)。ソース中の、'STTで音声認識Javascriptソースが動作するURLを記述'の部分に、後述のBluemixでDeployにおける、cf push mysttで得られたurlsの値を設定します。
test.ejs
<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="content-type"
content="text/html; charset=UTF-8">
<title><%=title %></title>
<style>
body { font-size:12pt; color:#000066; }
h1 { font-size:18pt; background-color:#DDDDFF; }
pre { background-color:#EEEEEE; }
</style>
</head>
<body>
<header>
<h1><%=title %></h1>
</header>
<article>
<%-content %>
</article>
<button id="StartRecording">StartRecording</button>
<button id="StopRecording">StopRecording</button>
<button id="StartPlaying">StartPlaying</button>
<button id="GenerateWAV">GenerateWAV</button>
<button id="DownloadWAV">DownloadWAV</button>
<button id="Upload">Upload</button>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script>
window.Recorder = function(audioContext, bufferSize){
var o = this;
o.audioContext = audioContext;
o.bufferSize = bufferSize || 4096;
}
Recorder.prototype = {
audioContext : '',
bufferSize : '',
audioBufferArray : [],
stream : '',
recording : function(stream){
var o = this;
o.stream = stream;
var mediaStreamSource =
o.audioContext.createMediaStreamSource(stream);
var scriptProcessor =
o.audioContext.createScriptProcessor(o.bufferSize, 1, 1);
mediaStreamSource.connect(scriptProcessor);
o.audioBufferArray = [];
scriptProcessor.onaudioprocess = function(event){
var channel = event.inputBuffer.getChannelData(0);
var buffer = new Float32Array(o.bufferSize);
for (var i = 0; i < o.bufferSize; i++) {
buffer[i] = channel[i];
}
o.audioBufferArray.push(buffer);
}
//この接続でonaudioprocessが起動
scriptProcessor.connect(o.audioContext.destination);
o.scriptProcessor = scriptProcessor;
},
recStart : function(){
var o = this;
if(o.stream){
o.recording(o.stream);
}
else{
navigator.getUserMedia(
{video: false, audio: true},
function(stream){o.recording(stream)},
function(err){
console.log(err.name ? err.name : err);
}
);
}
},
recStop : function(){
var o = this;
o.scriptProcessor.disconnect();
if(o.stream){
o.stream.stop();
o.stream = null;
}
},
getAudioBufferArray : function(){
var o = this;
return o.audioBufferArray
},
getAudioBuffer : function(){
var o = this;
var buffer = o.audioContext.createBuffer(
1,
o.audioBufferArray.length * o.bufferSize,
o.audioContext.sampleRate
);
var channel = buffer.getChannelData(0);
for (var i = 0; i < o.audioBufferArray.length; i++) {
for (var j = 0; j < o.bufferSize; j++) {
channel[i * o.bufferSize + j] = o.audioBufferArray[i][j];
}
}
return buffer;
}
}
window.exportWAV = function(audioData, sampleRate) {
var encodeWAV = function(samples, sampleRate) {
var buffer = new ArrayBuffer(44 + samples.length * 2);
var view = new DataView(buffer);
var writeString = function(view, offset, string) {
for (var i = 0; i < string.length; i++){
view.setUint8(offset + i, string.charCodeAt(i));
}
};
var floatTo16BitPCM = function(output, offset, input) {
for (var i = 0; i < input.length; i++, offset += 2){
var s = Math.max(-1, Math.min(1, input[i]));
output.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
}
};
writeString(view, 0, 'RIFF'); // RIFFヘッダ
view.setUint32(4, 32 + samples.length * 2, true); // これ以降のファイルサイズ
writeString(view, 8, 'WAVE'); // WAVEヘッダ
writeString(view, 12, 'fmt '); // fmtチャンク
view.setUint32(16, 16, true); // fmtチャンクのバイト数
view.setUint16(20, 1, true); // フォーマットID
view.setUint16(22, 1, true); // チャンネル数
view.setUint32(24, sampleRate, true); // サンプリングレート
view.setUint32(28, sampleRate * 2, true); // データ速度
view.setUint16(32, 2, true); // ブロックサイズ
view.setUint16(34, 16, true); // サンプルあたりのビット数
writeString(view, 36, 'data'); // dataチャンク
view.setUint32(40, samples.length * 2, true); // 波形データのバイト数
floatTo16BitPCM(view, 44, samples); // 波形データ
return view;
};
var mergeBuffers = function(audioData) {
var sampleLength = 0;
for (var i = 0; i < audioData.length; i++) {
sampleLength += audioData[i].length;
}
var samples = new Float32Array(sampleLength);
var sampleIdx = 0;
for (var i = 0; i < audioData.length; i++) {
for (var j = 0; j < audioData[i].length; j++) {
samples[sampleIdx] = audioData[i][j];
sampleIdx++;
}
}
return samples;
};
var dataview = encodeWAV(mergeBuffers(audioData), sampleRate);
var audioBlob = new Blob([dataview], { type: 'audio/wav' });
return audioBlob;
};
navigator.getUserMedia =
navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia ||
navigator.msGetUserMedia;
window.URL =
window.URL ||
window.webkitURL ||
window.mozURL ||
window.msURL;
window.AudioContext =
window.AudioContext||
window.webkitAudioContext;
//以下、アプリのためのロジック
var audioContext = new AudioContext();
var recorder = new Recorder(audioContext);
var localurl = null;
function startRecording() {
recorder.recStart(); // 録音開始
};
function stopRecording() {
recorder.recStop(); // 録音停止
recorder.getAudioBufferArray(); //音声配列データの取得
recorder.getAudioBuffer(); //AudioBuffer の取得
};
function startPlaying() {
var src = audioContext.createBufferSource();
src.buffer = recorder.getAudioBuffer();
src.connect(audioContext.destination);
src.start()
};
function generateWAV() {
var blob = exportWAV(recorder.getAudioBufferArray(), audioContext.sampleRate)
localurl = URL.createObjectURL(blob);
console.log(localurl);
};
//querySelectorでは、#をつけることで、idがbtnStartMonitoringの要素を取得している(つまり、Startボタン)
document.querySelector("#StartRecording").onclick = startRecording; // Wire up start button
document.querySelector("#StopRecording").onclick = stopRecording; // Wire up stop button
document.querySelector("#StartPlaying").onclick = startPlaying; //
document.querySelector("#GenerateWAV").onclick = generateWAV; //
document.querySelector("#DownloadWAV").onclick = function() {
console.log(localurl);
return location.href = localurl;
};
document.querySelector("#Upload").onclick = function() {
console.log('Upload');
return location.href = 'STTで音声認識Javascriptソースが動作するURLを記述';
};
</script>
</body>
</html>