Search code examples
javascriptnode.jsnpmaudioaudio-streaming

How can I pause a specific function in NodeJS?


Okay, so I've been trying to do this for a long time but I just can't find a solution. I'm building a personal Voice Assistant that only records when a hotword is detected, and everything until here works fine. To record the audio, I'm using the npm package node-record-lcpm16. I can't seem to find a solution to pause or stop(and start again) the recording. On the npm website of the audiorecorder there is a function specified that says recording.stop() but it doesn't work for me. My code right now is:


const recorder = require('node-record-lpcm16');
const fs = require('file-system');
const speech = require('@google-cloud/speech');
const say = require('say');
const notifier = require('node-notifier');
const Bumblebee = require('bumblebee-hotword-node');
const { setTimeout } = require('timers');
const { record } = require('node-record-lpcm16');
const bumblebee = new Bumblebee;
const voice = 'Microsoft Zira Desktop';


bumblebee.addHotword('computer');

const config = {
  encoding: 'LINEAR16',
  sampleRateHertz: 16000,
  languageCode: 'en-US',
};

const request = {
  config,
  interimResults: false,
};


const client = new speech.SpeechClient();




const recognizeStream = client
  .streamingRecognize(request)
  .on('error', console.error)
  .on('data', data => findfunction(data.results[0].alternatives[0].transcript)
  );

const recording = recorder.record({
  sampleRateHertz: 16000,
  threshold: 0, 
  recorder: 'sox',
  silence: '5.0',
  })
  .stream().on('error', console.error); //Here is the Recorder, and I can't actually stop it and that's my problem.

recording.pipe(recognizeStream);

bumblebee.on('hotword', function(hotword){
  console.log('Hotword detected:', hotword);  // It does these actions as soon as the hotword is detected
  recording.pipe(recognizeStream);
  setTimeout(function stop(){
    recording.pipe(fs.createWriteStream("\\\\.\\NUL")),
    console.log('Stopped Recording.')
  }, 5000);
});


console.log('Computer initiated.');

bumblebee.start();
//Everything down from here is just what do to with the translated speech, it doesn't play a role in my problem.

function findfunction(Data){
  let findFunction = Data;
  console.log(Data);


  if(findFunction.includes('time')){
    whattimeisit(findFunction);
  };

  if(findFunction.includes('day')){
    whatdateisit(findFunction);
  };

  if(findFunction.includes('thank you')){
    thankyou();
  };

  if(findFunction.includes('remind')){
    setatimer(findFunction);
  };

};

function whattimeisit(timeString){
  const date = new Date();
  const time = date.toLocaleTimeString();
  say.speak(`It's currently ${time}.`, voice);
  console.log(`It's currently ${time}.`);
};

function whatdateisit(dateString){
  const date = new Date();
  const currentDate = date.toLocaleDateString();
  say.speak(`It's currently ${currentDate}.`, voice);
  console.log(`It's currently ${currentDate}.`);
};

function thankyou(){
  say.speak("You're welcome!", voice);
  console.log("You're welcome!");
};

function setatimer(timerString){
  const timer = timerString.replace(/\D/g, '');
  setTimeout(function stop() {notifier.notify({title: 'Computer', message: 'Your timer ran out!', icon: './computericon1.png'})} , timer * 60000);
  if(timer == 1){
    say.speak(`Set a timer for ${timer} minute.`, voice);
    console.log(`Set a timer for ${timer} minute.`);
  }else{
    say.speak(`Set a timer for ${timer} minutes.`, voice);
    console.log(`Set a timer for ${timer} minutes.`);
  };
};

Any help would be greatly appreciated!


Solution

  • I've played about with your code.. it's definitely a fun project to play with!

    I would suggest maybe just modifying the code to record to a buffer, then send that to the google speech recognition engine.

    The reason recording.stop() was probably not working for you is that you were calling it on the stream. If we separate the recording and recordingStream variables we can control the flow better.

    I've updated the code so when we get the hotword, we stop recording, recognize the speech, then start recording again.

    const recorder = require('node-record-lpcm16');
    const Bumblebee = require('bumblebee-hotword-node');
    
    const say = require('say');
    const voice = 'Microsoft Zira Desktop';
    
    const speech = require('@google-cloud/speech');
    
    let chunks = null;
    let recording = null;
    
    function startRecording() {
        console.log("listening...");
        chunks = [];
        recording = recorder.record({
            sampleRateHertz: 16000,
            threshold: 0, 
            recorder: 'sox',
            silence: '5.0',
        })
    
        const recordingStream = recording.stream();
        recordingStream.on('error', () => {});
    
        // Tune this to ensure we only send the last few seconds of audio to google..
        const maxChunks = 10;
        recordingStream.on('data', (chunk) => {
            chunks.push(chunk);
            // keep the number of chunks below a reasonable limit...
            if (chunks.length > maxChunks) {
                chunks = chunks.slice(-maxChunks);
            }
        });
        recordingStream.on('end', async () => {
            // Create a buffer from our recording, it should only be a few seconds long.
            const audioBuffer = Buffer.concat(chunks);
            console.log("Chunk count:", chunks.length);
            await recognizeSpeech(audioBuffer);
            startRecording();
        });
    }
    
    async function recognizeSpeech(audioBuffer) {
        console.log(`recognizeSpeech: Converting audio buffer to text (${audioBuffer.length} bytes)...`)
        const client = new speech.SpeechClient();
        const request = {
            config: { encoding: 'LINEAR16', sampleRateHertz: 16000, languageCode: 'en-US'},
            audio: { content: audioBuffer.toString("base64") }
        };
        // Convert our audio to text.
        const response = await client.recognize(request)    
        findfunction(response[0].results[0].alternatives[0].transcript);
    }
    
    startRecording();
    startBumblebee();
    
    function startBumblebee() {
        const bumblebee = new Bumblebee();
        bumblebee.addHotword('computer');
        bumblebee.on('hotword', function(hotword){
            console.log('Hotword detected:', hotword);
            setTimeout(() => {
                console.log('Stopping recording...');
                recording.stop()
            }, 2000);
        });
        bumblebee.start( );
    }
    
    // Nothing changed from here...
    function findfunction(Data){
        let findFunction = Data;
        console.log(Data);
    
    
        if(findFunction.includes('time')){
        whattimeisit(findFunction);
        };
    
        if(findFunction.includes('day')){
        whatdateisit(findFunction);
        };
    
        if(findFunction.includes('thank you')){
        thankyou();
        };
    
        if(findFunction.includes('remind')){
        setatimer(findFunction);
        };
    
    };
    
    function whattimeisit(timeString){
        const date = new Date();
        const time = date.toLocaleTimeString();
        say.speak(`It's currently ${time}.`, voice);
        console.log(`It's currently ${time}.`);
    };
    
    function whatdateisit(dateString){
        const date = new Date();
        const currentDate = date.toLocaleDateString();
        say.speak(`It's currently ${currentDate}.`, voice);
        console.log(`It's currently ${currentDate}.`);
    };
    
    function thankyou(){
        say.speak("You're welcome!", voice);
        console.log("You're welcome!");
    };
    
    function setatimer(timerString){
        const timer = timerString.replace(/\D/g, '');
        setTimeout(function stop() {notifier.notify({title: 'Computer', message: 'Your timer ran out!', icon: './computericon1.png'})} , timer * 60000);
        if(timer == 1){
        say.speak(`Set a timer for ${timer} minute.`, voice);
        console.log(`Set a timer for ${timer} minute.`);
        }else{
        say.speak(`Set a timer for ${timer} minutes.`, voice);
        console.log(`Set a timer for ${timer} minutes.`);
        };
    };