LoginSignup
6
3

More than 5 years have passed since last update.

IBM Bluemix/Watson Speech To Text/Node.jsでApplication開発(2)

Last updated at Posted at 2016-05-15

前回からの続きで、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>

3. STTで音声認識Javascriptソースの記述に続きます。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3