'Small Devices'에 해당되는 글 125건

  1. 2018.09.06 [Tinker Board S] AI 스피커 만들어보기 #5 : AI스피커 만들어 보기
반응형

본 체험 제품은 아이씨뱅큐(주)에서 진행하는 무상 체험단 활동으로 작성한 것입니다.


 이전 포스팅에서는 AI 스피커를 만들기 위한 구성 요소들을 알아봤습니다. 이번 포스팅에서는 이 구성 요소들을 엮어서 간단한 AI 스피커를 만들어 보겠습니다. 유튜브에서 특정 가수들에 대한 노래를 검색해서 플레이 해주는 간단한 기능을 구현해 보겠습니다. 기본 프로그램 언어는 node.js 로 설명 하겠습니다.


1. 음성명령 수신 (Hot-Word Detection -> STT)

AI 스피커에서 음성 명령은 일반적으로 Hot-Word Detection 이후 발화된 내용으로 명령을 내립니다. 예를 들어 "오케이 구글 유튜브에서 싸이 노래 들려줘" 에서 "오케이 구글"은 Hot-Word 이고 "구글 유튜브에서 싸이 노래 들려줘"가 음성명령이 되는 것이지요. 이전 포스팅 중에서 다음의 두 포스팅이 관련됩니다.


- [Tinker Board S] AI 스피커 만들어보기 #1: Hot-word Detection

- [Tinker Board S] AI 스피커 만들어보기 #2: 음성인식 해보기


마이크를 어떻게 사용할 것인지에 따라 2가지 동작이 있을 수 있습니다. 1) 마이크 입력을 Hot-Word Detection 모듈에 연결, 2) 마이크 입력을 음성인식 모듈에 연결. 일단 기본적으로는 Hot-Word Detection 모듈에서 마이크를 점유하다가, Hot-Word Detection 이 되면 음성인식 모듈에 전달하는 프로그램을 작성해 보겠습니다. 다음은 Hot-Word Detection 대기 상태로 있다가 Hot-Word가 Detection 되면 음성인식을 수행하고 화면에 출력하는 node.js 프로그램 입니다. 음성인식은 구글을 이용하도록 하겠습니다.

//for recording microphone

const record = require('node-record-lpcm16');

const startMic=()=>{

return record.start({

sampleRateHertz:16000,

threshold: 0,

verbose: false,

recordProgram:'arecord',

silence:'10.0'

});

};

let micMode=0;//0->hot-word detection, 1-> stt


//for hot-word detection

const Detector = require('snowboy').Detector;

const Models = require('snowboy').Models;

const models = new Models();

models.add({

file: 'Mirae.pmdl',

sensitivity: '0.5',

hotwords : 'snowboy'

});

const detector = new Detector({

resource: "resources/common.res",

models: models,

audioGain: 2.0,

applyFrontend: true

});

detector.on('silence', function () { });

detector.on('sound', function (buffer) { });

detector.on('error', function () {

console.log('error');

});

detector.on('hotword', function (index, hotword, buffer) {

console.log('hotword detected. start stt', index, hotword);

micMode=1; //---1)

});


//for stt

const speech=require('@google-cloud/speech');

const projectId='YOUR_PROJECT_ID';

const keyfile='YOUR_JSON_KEY_PATH';

const client=new speech.SpeechClient({

projectId:projectId,

keyFilename:keyfile

});

const request={

config:{

encoding:'LINEAR16',

sampleRateHertz:16000,

languageCode:'ko-KR'

},

interimResults:false

};

let recognizeStream=null;

const startStt=()=>{

recognizeStream = client.streamingRecognize(request);

recognizeStream.on('error', (error)=>{

console.log('recognizeStream Error:'+error);

recognizeStream.end();

micMode=0; //---2)

});

recognizeStream.on('data', (data) =>{

//console.log('date received:'+JSON.stringify(data));

if(data.results[0] && data.results[0].alternatives[0]){

let sayData= data.results[0].alternatives[0].transcript

console.log(sayData);

recognizeStream.end();

recognizeStream=null;

micMode=0; //---2)

}

}

);

};


//start service

let mic=startMic();

mic.on('data',(data)=>{

if(micMode===0){//hot-word detection mode

detector.write(data); //---3)

} else {//stt mode

console.log('say~');

if(recognizeStream===null) startStt();

if(recognizeStream!==null) recognizeStream.write(data); //---4)

}

});


1) HotWord가 Detect 되면 micMode를 1로 변경해서 마이크 입력을 STT에 전달하도록 합니다.( 4)번에서 STT모듈에 마이크 입력 전달)

2) STT 결과가 나오면 micMode를 0으로 변경하여 Hot-Word Detection 모드로 전환합니다.

3) Hot-Word Detector에 마이크 데이터를 write 합니다.

4) STT모듈에 마이크 데이터를 write 합니다.


이전 포스팅 예제와의 차이는 micMode에 따라서 마이크 데이터를 write 하는 대상이 달라진 다는 것이고 mic의 pipe/unpipe stream 메소드 대신 write를 사용했다는 차이가 있습니다. 다음은 실행 결과 입니다.



2. 음성 명령 해석

음성인식 되고 나서는 그 인식된 결과를 해석해야 합니다. 예를 들어 "유튜브에서 싸이 노래 들려줘" 라는 음성인식 결과는 "유튜브에서", "싸이" "노래 들려줘"로 분해되고 각각 TargetMedia=Youtube, TargetArtist=싸이, TargetAction=PlayMusic 으로 명령어 실행을 위한 단위로 해석되어야 하는 것이지요. 이전 포스팅 중 다음의 포스팅이 관련 됩니다.


- [Tinker Board S] AI 스피커 만들어보기 #4: 텍스트 해석 해보기


앞에 음성 명령어 수신 이후 그 결과에 대해서 dialogflow의 API를 이용해서 해석 결과까지 받아 보겠습니다. 이전 코드에 다음을 추가합니다. 입력되는 queryText에 대해서 dialogflow API의 detectIntent 를 호출하도록 queryDialog라는 함수로 감싸 줍니다. (나머지는 상기 포스팅 예제와 같습니다.)

//for dialog

const languageCode='ko-KR';

const dialogflow=require('dialogflow');

const sessionId = 'YOUR_PROJECT_ID';

const credentials_file_path = 'YOUR_JSON_KEY_PATH';

const sessionClient = new dialogflow.SessionsClient({

projectId, keyFilename: credentials_file_path,

});

const sessionPath=sessionClient.sessionPath(projectId,sessionId);

const queryDialog=(queryText)=>{

const request={

session:sessionPath,

queryInput:{

text:{

text:queryText,

languageCode:languageCode,

},

},

};

sessionClient.detectIntent(request).then((responses) => {

//console.log('Detected intent:'+JSON.stringify(responses));

const queryResult=responses[0].queryResult;

const action=queryResult.action;

const TargetAction=queryResult.parameters.fields.TargetAction;

const TargetArtist=queryResult.parameters.fields.TargetArtist;

const TargetMedia=queryResult.parameters.fields.TargetMedia;

const fulfillmentText=queryResult.fulfillmentText;

console.log('action:'+action+

' TargetAction:'+TargetAction.stringValue+

' TargetArtist:'+TargetArtist.stringValue+

' TargetMedia:'+TargetMedia.stringValue+ 

' Fullfillment:'+fulfillmentText);

}).catch((err) => {

console.error('ERROR:', err);

});

};

그리고 startStt 함수에서 음성인식된 결과를 queryDialog에 전달할 수 있도록 다음과 같이 변경합니다.

const startStt=()=>{

recognizeStream = client.streamingRecognize(request);

recognizeStream.on('error', (error)=>{

console.log('recognizeStream Error:'+error);

recognizeStream.end();

micMode=0;

});

recognizeStream.on('data', (data) =>{

//console.log('date received:'+JSON.stringify(data));

if(data.results[0] && data.results[0].alternatives[0]){

let sayData= data.results[0].alternatives[0].transcript

//console.log(sayData);

queryDialog(sayData);

recognizeStream.end();

recognizeStream=null;

micMode=0;

}

}

);

};

Hot-Word Detection 이후 "유튜브에서 싸이 노래 틀어줘"라고 발화 하게 되면 다음과 같은 결과를 출력합니다.



3. Youtube 음악 실행

Youtube는 youtube-dl이라는 모듈을 이용해서 플레이 해보도록 하겠습니다. (구글의 Youtube Data API를 이용해도 됩니다. 과금 등이 정확히 파악이 안되서 무료로 이용 가능한 youtube-dl 모듈을 이용하도록 하겠습니다.) 다음과 같이 youtube-dl 을 설치합니다.


$ sudo apt-get install youtube-dl


Node에서 플레이를 하기 위해서는 vlc 를 설치해야 합니다. 다음과 같이 vlc를 설치합니다.


$ sudo apt-get install vlc


node.js에서 이용하기 위해서는 youtube-dl 모듈과 cvlc 모듈을 설치해야 합니다. 다음과 같이 설치합니다.


$npm install youtube-dl

$npm install cvlc


youtube-dl 은 https://www.youtube.com/watch?[-----] 로 시작하는 특정 youtube 영상의 재생 URL을 조회해줍니다. (Youtube 영상 실행시 보이는 URL입니다.) 조회시 Format을 지정해 줄 수 있습니다. 이 포스팅에서는 Audio 만 다운로드 하도록 하겠습니다. 다. Format은 251을 이용하도록 하겠습니다. 251은 다음과 같이 다운로드 됩니다.


Youtube 영상 URL을 알면 다음의 코드로 Play할수 있는 URL을 추출할 수 있습니다.

function getPlayRealUrl(inUrl){

        const ytoptions=['--format=251'];

        const url=inUrl;

        return new Promise((resolve,reject)=>{

                youtubedl.getInfo(url,ytoptions,function(err,info){

                        console.log('title:'+info.title);

                        console.log('url:'+info.url);

                        resolve({title:info.title,url:info.url});

                });

        });

};


이 URL을 가지고 cvlc 모듈을 이용해서 Tinker Board에서 실행 가능합니다. 문제는 특정 키워드들에 대해서 Youtube에서 조회하는 것입니다. 다행이도 Youtube는 웹 기반으로 잘 연결-조직되어 있습니다. search url에 대해서 get 요청을 하고 이 요청을 파싱하면 플레이 URL들을 뽑아 낼 수 있습니다. 다음은 특정 키워드에 대해서 Youtube에서 검색되는 Youtube Play Url을 추출해 내는 코드 입니다. request라는 HTTP client 모듈을 이용하였습니다.

function getYoutubeSearchList(inQuery){

        const queryText=inQuery;

        return new Promise((resolve,reject)=>{

                console.log('QueryText:'+queryText);

                const searchurl='https://www.youtube.com/results?search_query=';

                const queryUrl=encodeURI(searchurl+queryText);

                request(queryUrl,(err,res,body)=>{

                        let splitByWatch=body.split('href=\"\/watch?');

                        let isFirst=true;

                        let urlList=[];

                        splitByWatch.forEach((splitText)=>{

                                if(isFirst===true) isFirst=false;

                                else {

                                        let splitByQuot=splitText.split('"');

                                        urlList.push(splitByQuot[0]);

                                }

                        });

                        resolve(urlList);

                });

        });

};


다음은 이들을 조합하여 특정 키워드에 대해서 Youtube 검색 결과 중 최상위 영상의 Audio를 플레이하는 코드입니다. playYoutubeByText("싸이")와 같이 호출하면 Youtube의 싸이 검색 결과 최상위 영상의 Audio를 출력합니다.

const request = require('request');

const youtubedl=require('youtube-dl');

const Cvlc=require('cvlc');

let player=new Cvlc();

function getYoutubeSearchList(inQuery){

        const queryText=inQuery;

        return new Promise((resolve,reject)=>{

                console.log('QueryText:'+queryText);

                const searchurl='https://www.youtube.com/results?search_query=';

                const queryUrl=encodeURI(searchurl+queryText);

                request(queryUrl,(err,res,body)=>{

                        let splitByWatch=body.split('href=\"\/watch?');

                        let isFirst=true;

                        let urlList=[];

                        splitByWatch.forEach((splitText)=>{

                                if(isFirst===true) isFirst=false;

                                else {

                                        let splitByQuot=splitText.split('"');

                                        urlList.push(splitByQuot[0]);

                                }

                        });

                        resolve(urlList);

                });

        });

};

function getPlayRealUrl(inUrl){

        const url=inUrl;

        const ytoptions=['--format=251'];

        return new Promise((resolve,reject)=>{

                youtubedl.getInfo(url,ytoptions,function(err,info){

                        console.log('title:'+info.title);

                        console.log('url:'+info.url);

                        resolve({title:info.title,url:info.url});

                });

        });

};

const youtubePlayBaseUrl='https://www.youtube.com/watch?';

async function playYoutubeByText(inQuery){

        let urls=await getYoutubeSearchList(inQuery);

        let targetUrl=youtubePlayBaseUrl+urls[0];

        console.log('yt url:'+targetUrl);

        let realPlayUrl=await getPlayRealUrl(targetUrl);

        console.log('title:'+realPlayUrl.title+' url:'+realPlayUrl.url);

        player.play(realPlayUrl.url,()=>{

                console.log('Music Played');

        });

};

playYoutubeByText('싸이'); 



4. 해석 결과 실행

해석된 결과는 두가지 로 실행이 됩니다. 즉, 해석된 결과를 TTS로 안내하고 Intent에 대한 매핑된 코드를 실행하는 것이지요. 이전 포스팅 중 다음의 포스팅이 관련 됩니다.


 - [Tinker Board S] AI 스피커 만들어보기 #3: 음성합성 해보기


TTS안내는 Fullfillment 의 Text로 전달합니다. 통일성을 위해서 google tts를 이용하도록 하겠습니다. "2. 음성 명령 해석" 의 작성 코드에 google tts를 붙여서 Fullfillment를 TTS로 실행하고, TargetAction에 따라서 Youtube를 실행-중지하는 프로그램을 작성해 보도록 하겠습니다. 다음의 함수들을 추가합니다.

//for tts

const textToSpeech=require('@google-cloud/text-to-speech');

const Cvlc=require('cvlc');

const fs=require('fs');

const playertts=new Cvlc();

const ttsclient=new textToSpeech.TextToSpeechClient({

        projectId:projectId,

        keyFilename:keyfile

});

const playTTS=(ttsText,callback)=>{

        const request={

                input:{text:ttsText},

                voice:{languageCode:'ko-KR',ssmlGender:'FEMALE'},

                audioConfig:{audioEncoding:'LINEAR16'},

        };

        ttsclient.synthesizeSpeech(request,(err,response)=>{

                if(err){

                        console.log('TTS ERROR:'+err);

                        return;

                };

                const playTime=(response.audioContent.length-40)/(16000*2);

                console.log('playTime:'+playTime);

                fs.writeFileSync('./tts.wav',response.audioContent,'binary');

                playertts.play('./tts.wav');

                callback(playTime);

        });

};


//for youtube

const httprequest = require('request');

const youtubedl=require('youtube-dl');

const player=new Cvlc();

function getYoutubeSearchList(inQuery){

        const queryText=inQuery;

        return new Promise((resolve,reject)=>{

                console.log('QueryText:'+queryText);

                const searchurl='https://www.youtube.com/results?search_query=';

                const queryUrl=encodeURI(searchurl+queryText);

                httprequest(queryUrl,(err,res,body)=>{

                        let splitByWatch=body.split('href=\"\/watch?');

                        let isFirst=true;

                        let urlList=[];

                        splitByWatch.forEach((splitText)=>{

                                if(isFirst===true) isFirst=false;

                                else {

                                        let splitByQuot=splitText.split('"');

                                        urlList.push(splitByQuot[0]);

                                }

                        });

                        resolve(urlList);

                });

        });

};

function getPlayRealUrl(inUrl){

        const url=inUrl;

        const ytoptions=['--format=251'];

        return new Promise((resolve,reject)=>{

                youtubedl.getInfo(url,ytoptions,function(err,info){

                        console.log('title:'+info.title);

                        console.log('url:'+info.url);

                        resolve({title:info.title,url:info.url});

                });

        });

};

const youtubePlayBaseUrl='https://www.youtube.com/watch?';

async function playYoutubeByText(inQuery){

        console.log('inQuery:'+inQuery);

        let urls=await getYoutubeSearchList(inQuery);

        let targetUrl=youtubePlayBaseUrl+urls[0];

        console.log('yt url:'+targetUrl);

        let realPlayUrl=await getPlayRealUrl(targetUrl);

        console.log('title:'+realPlayUrl.title+' url:'+realPlayUrl.url);

        player.file_url='';

        player.play(realPlayUrl.url,()=>{

                console.log('Music Played');

        });

}; 


음악 재생은 TTS 재생 이후 진행 되어야 하여 playTTS 함수에 PCM 데이터의 크기로 재생 시간을 계산하여 콜백으로 넘겨주도록 하였습니다. 


그리고 sessionClient.detectIntent(request) 부분에 다음과 같이 TargetAction에 따라서 Youtube를 재생하거나 정지하는 코드를 넣습니다.

          sessionClient.detectIntent(request).then((responses) => {

                        //console.log('Detected intent:'+JSON.stringify(responses));

                        const queryResult=responses[0].queryResult;

                        const action=queryResult.action;

                        const TargetAction=queryResult.parameters.fields.TargetAction;

                        const TargetArtist=queryResult.parameters.fields.TargetArtist;

                        const TargetMedia=queryResult.parameters.fields.TargetMedia;

                        const fulfillmentText=queryResult.fulfillmentText;

                        console.log('action:'+action+

                                                ' TargetAction:'+TargetAction.stringValue+

                                                ' TargetArtist:'+TargetArtist.stringValue+

                                                ' TargetMedia:'+TargetMedia.stringValue+

                                                ' Fullfillment:'+fulfillmentText);

                        if(TargetAction.stringValue==='PlayMusic'){

                                playTTS(fulfillmentText,(waitTime)=>{

                                        player.file_url='';

                                        playYoutubeByText(TargetArtist.stringValue);

                                });

                        } else {

                                player.cmd('stop');

                                playTTS("음악을 중지합니다",(waitTime)=>{

                                });

                        }

        }).catch((err) => {

                        console.error('ERROR:', err);

        });


다음은 실행 결과 입니다. 음악 재생에 약간의 딜레이가 있습니다. 이는 Youtube 검색 파싱, PlayURL가져오는게 시간이 걸리는듯 하네요.


다음은 전체 소스 코드 입니다.

//for recording microphone

const record = require('node-record-lpcm16');

const startMic=()=>{

        return record.start({

                sampleRateHertz:16000,

                threshold: 0,

                verbose: false,

                recordProgram:'arecord',

                silence:'10.0'

        });

};

let micMode=0;//0->hot-word detection, 1-> stt


//for hot-word detection

const Detector = require('snowboy').Detector;

const Models = require('snowboy').Models;

const models = new Models();

models.add({

        file: 'Mirae.pmdl',

        sensitivity: '0.5',

        hotwords : 'snowboy'

});

const detector = new Detector({

        resource: "resources/common.res",

        models: models,

        audioGain: 2.0,

        applyFrontend: true

});

detector.on('silence', function () { });

detector.on('sound', function (buffer) { });

detector.on('error', function () {

        console.log('error');

});

detector.on('hotword', function (index, hotword, buffer) {

        console.log('hotword detected. start stt', index, hotword);

        micMode=1;

});


//for stt

const speech=require('@google-cloud/speech');

const projectId='YOUR_PROJECT_ID';

const keyfile='YOUR_JSON_KEY_FILE';

const client=new speech.SpeechClient({

        projectId:projectId,

        keyFilename:keyfile

});

const request={

        config:{

                encoding:'LINEAR16',

                sampleRateHertz:16000,

                languageCode:'ko-KR'

        },

        interimResults:false

};

let recognizeStream=null;

const startStt=()=>{

        recognizeStream = client.streamingRecognize(request);

        recognizeStream.on('error', (error)=>{

                        console.log('recognizeStream Error:'+error);

                        recognizeStream.end();

                        micMode=0;

        });

        recognizeStream.on('data', (data) =>{

                        //console.log('date received:'+JSON.stringify(data));

                        if(data.results[0] && data.results[0].alternatives[0]){

                                let sayData= data.results[0].alternatives[0].transcript

                                //console.log(sayData);

                                queryDialog(sayData);

                                recognizeStream.end();

                                recognizeStream=null;

                                micMode=0;

                        }

                }

        );

};


//for dialog

const languageCode='ko-KR';

const dialogflow=require('dialogflow');

const sessionId = '123457890';

const credentials_file_path = 'YOUR_JSON_KEY_FILE';

const sessionClient = new dialogflow.SessionsClient({

                projectId, keyFilename: credentials_file_path,

});

const sessionPath=sessionClient.sessionPath(projectId,sessionId);

const queryDialog=(queryText)=>{

        const request={

                session:sessionPath,

                queryInput:{

                        text:{

                                text:queryText,

                                languageCode:languageCode,

                        },

                },

        };

        sessionClient.detectIntent(request).then((responses) => {

                        //console.log('Detected intent:'+JSON.stringify(responses));

                        const queryResult=responses[0].queryResult;

                        const action=queryResult.action;

                        const TargetAction=queryResult.parameters.fields.TargetAction;

                        const TargetArtist=queryResult.parameters.fields.TargetArtist;

                        const TargetMedia=queryResult.parameters.fields.TargetMedia;

                        const fulfillmentText=queryResult.fulfillmentText;

                        console.log('action:'+action+

                                                ' TargetAction:'+TargetAction.stringValue+

                                                ' TargetArtist:'+TargetArtist.stringValue+

                                                ' TargetMedia:'+TargetMedia.stringValue+

                                                ' Fullfillment:'+fulfillmentText);

                        if(TargetAction.stringValue==='PlayMusic'){

                                playTTS(fulfillmentText,(waitTime)=>{

                                        player.file_url='';

                                        playYoutubeByText(TargetArtist.stringValue);

                                });

                        } else {

                                player.cmd('stop');

                                playTTS("음악을 중지합니다",(waitTime)=>{

                                });

                        }

        }).catch((err) => {

                        console.error('ERROR:', err);

        });


};


//for tts

const textToSpeech=require('@google-cloud/text-to-speech');

const Cvlc=require('cvlc');

const fs=require('fs');

const playertts=new Cvlc();

const ttsclient=new textToSpeech.TextToSpeechClient({

        projectId:projectId,

        keyFilename:keyfile

});

const playTTS=(ttsText,callback)=>{

        const request={

                input:{text:ttsText},

                voice:{languageCode:'ko-KR',ssmlGender:'FEMALE'},

                audioConfig:{audioEncoding:'LINEAR16'},

        };

        ttsclient.synthesizeSpeech(request,(err,response)=>{

                if(err){

                        console.log('TTS ERROR:'+err);

                        return;

                };

                const playTime=(response.audioContent.length-40)/(16000*2);

                console.log('playTime:'+playTime);

                fs.writeFileSync('./tts.wav',response.audioContent,'binary');

                playertts.play('./tts.wav');

                callback(playTime);

        });

};


//for youtube

const httprequest = require('request');

const youtubedl=require('youtube-dl');

const player=new Cvlc();

function getYoutubeSearchList(inQuery){

        const queryText=inQuery;

        return new Promise((resolve,reject)=>{

                console.log('QueryText:'+queryText);

                const searchurl='https://www.youtube.com/results?search_query=';

                const queryUrl=encodeURI(searchurl+queryText);

                httprequest(queryUrl,(err,res,body)=>{

                        let splitByWatch=body.split('href=\"\/watch?');

                        let isFirst=true;

                        let urlList=[];

                        splitByWatch.forEach((splitText)=>{

                                if(isFirst===true) isFirst=false;

                                else {

                                        let splitByQuot=splitText.split('"');

                                        urlList.push(splitByQuot[0]);

                                }

                        });

                        resolve(urlList);

                });

        });

};

function getPlayRealUrl(inUrl){

        const url=inUrl;

        const ytoptions=['--format=251'];

        return new Promise((resolve,reject)=>{

                youtubedl.getInfo(url,ytoptions,function(err,info){

                        console.log('title:'+info.title);

                        console.log('url:'+info.url);

                        resolve({title:info.title,url:info.url});

                });

        });

};

const youtubePlayBaseUrl='https://www.youtube.com/watch?';

async function playYoutubeByText(inQuery){

        console.log('inQuery:'+inQuery);

        let urls=await getYoutubeSearchList(inQuery);

        let targetUrl=youtubePlayBaseUrl+urls[0];

        console.log('yt url:'+targetUrl);

        let realPlayUrl=await getPlayRealUrl(targetUrl);

        console.log('title:'+realPlayUrl.title+' url:'+realPlayUrl.url);

        player.file_url='';

        player.play(realPlayUrl.url,()=>{

                console.log('Music Played');

        });

};


//start service

let mic=startMic();

mic.on('data',(data)=>{

        if(micMode===0){//hot-word detection mode

                detector.write(data);

        } else {//stt mode

                console.log('say~');

                if(recognizeStream===null) startStt();

                if(recognizeStream!==null) recognizeStream.write(data);

        }

}); 




지금까지 음성인식-합성-대화(dialog)를 통한 AI 스피커 만든는 예제를 설명하였습니다. dialogflow를 이용해서 GPIO를 이용한 IoT 응용등도 충분히 가능할 것으로 생각 됩니다. 이상 활용기를 마칩니다.


- ASUS 팅커보드 S 공식 구입처: 아이씨뱅큐 http://www.icbanq.com/

- 아이씨뱅큐 공식 카페: http://cafe.naver.com/icbanq

- 아이씨뱅큐 공식 블로그: http://blog.naver.com/icbanq










반응형
Posted by alias
,