Speech to Text: Piping microphone stream to Watson STT with NodeJS - node.js

I am currently trying to send a microphone stream to Watson STT service but for some reason, the Watson service is not receiving the stream (I'm guessing) so I get the error "Error: No speech detected for 30s".
Note that I have streamed a .wav file to Watson and I have also tested piping micInputStream to my local files so I know both are at least set up correctly. I am fairly new to NodeJS / javascript so I'm hoping the error might be obvious.
const fs = require('fs');
const mic = require('mic');
var SpeechToTextV1 = require('watson-developer-cloud/speech-to-text/v1');
var speechToText = new SpeechToTextV1({
iam_apikey: '{key_here}',
url: 'https://stream.watsonplatform.net/speech-to-text/api'
});
var params = {
content_type: 'audio/l16; rate=44100; channels=2',
interim_results: true
};
const micParams = {
rate: 44100,
channels: 2,
debug: false,
exitOnSilence: 6
}
const micInstance = mic(micParams);
const micInputStream = micInstance.getAudioStream();
micInstance.start();
console.log('Watson is listening, you may speak now.');
// Create the stream.
var recognizeStream = speechToText.recognizeUsingWebSocket(params);
// Pipe in the audio.
var textStream = micInputStream.pipe(recognizeStream).setEncoding('utf8');
textStream.on('data', user_speech_text => console.log('Watson hears:', user_speech_text));
textStream.on('error', e => console.log(`error: ${e}`));
textStream.on('close', e => console.log(`close: ${e}`));

Conclusion: In the end, I am not entirely sure what was wrong with the code. I'm guessing it had something to do with the mic package. I ended up scrapping the package and using "Node-audiorecorder" instead for my audio stream https://www.npmjs.com/package/node-audiorecorder
Note: This module requires you to install SoX and it must be available in your $PATH. http://sox.sourceforge.net/
Updated Code: For anyone wondering what my final code looks like here you go. Also a big shoutout to NikolayShmyrev for trying to help me with my code!
Sorry for the heavy comments but for new projects I like to make sure I know what every line is doing.
// Import module.
var AudioRecorder = require('node-audiorecorder');
var fs = require('fs');
var SpeechToTextV1 = require('watson-developer-cloud/speech-to-text/v1');
/******************************************************************************
* Configuring STT
*******************************************************************************/
var speechToText = new SpeechToTextV1({
iam_apikey: '{your watson key here}',
url: 'https://stream.watsonplatform.net/speech-to-text/api'
});
var recognizeStream = speechToText.recognizeUsingWebSocket({
content_type: 'audio/wav',
interim_results: true
});
/******************************************************************************
* Configuring the Recording
*******************************************************************************/
// Options is an optional parameter for the constructor call.
// If an option is not given the default value, as seen below, will be used.
const options = {
program: 'rec', // Which program to use, either `arecord`, `rec`, or `sox`.
device: null, // Recording device to use.
bits: 16, // Sample size. (only for `rec` and `sox`)
channels: 2, // Channel count.
encoding: 'signed-integer', // Encoding type. (only for `rec` and `sox`)
rate: 48000, // Sample rate.
type: 'wav', // Format type.
// Following options only available when using `rec` or `sox`.
silence: 6, // Duration of silence in seconds before it stops recording.
keepSilence: true // Keep the silence in the recording.
};
const logger = console;
/******************************************************************************
* Create Streams
*******************************************************************************/
// Create an instance.
let audioRecorder = new AudioRecorder(options, logger);
//create timeout (so after 10 seconds it stops feel free to remove this)
setTimeout(function() {
audioRecorder.stop();
}, 10000);
// This line is for saving the file locally as well (Strongly encouraged for testing)
const fileStream = fs.createWriteStream("test.wav", { encoding: 'binary' });
// Start stream to Watson STT Remove .pipe(process.stdout) if you dont want translation printed to console
audioRecorder.start().stream().pipe(recognizeStream).pipe(process.stdout);
//Create another stream to save locally
audioRecorder.stream().pipe(fileStream);
//Finally pipe translation to transcription file
recognizeStream.pipe(fs.createWriteStream('./transcription.txt'));

Related

REST API with node-rtsp-stream NPM

I am trying to stream a RTSP in HTML5 pages using node-rtsp-stream NPM. Here I can see the live stream in HTML page. But the thing is when I try to do REST API with this it throws TypeError: stream is not a constructor. when I call my post method first time its working properly. when I try to do the same again it throws error.
here is my API:
RTSPRouter.post('/getPreview', (req, res) => {
// stream.mpeg1Muxer.kill();
stream = new stream({
name: 'name',
streamUrl: req.body.RTSPURL,
wsPort: 9999,
ffmpegOptions: {
'-r': 30
}
})
res.send(stream)
})
API for Kill :
RTSPRouter.get('/killPreview', (req, res) => {
process.kill(req.body.pid1)
stream.prototype.stop()// this method also not working
})
Even I killed the stream alone using the PID it's throwing the same error.
Kindly help me to fix this problem, thanks in advance!
you're getting a typeError because you haven't imported Stream.
Your code should look like this:
Stream = require("node-rtsp-stream");
stream = new Stream({
name: "name",
streamUrl: "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov",
wsPort: 9999,
ffmpegOptions: {
// options ffmpeg flags
"-stats": "", // an option with no neccessary value uses a blank string
"-r": 30, // options with required values specify the value after the key
},
});
res.send(stream);
Also don't forget to run npm install node-rtsp-stream.
After a few days I found the answer for this question.
You should not create stream like this:
var stream = require('node-rtsp-stream')
stream = new stream({
name: 'name',
streamUrl: req.body.RTSPURL,
wsPort: 9999,
ffmpegOptions: {
'-r': 30
}
})
Instead of this, try the below code:
var stream = require('node-rtsp-stream')
var streamObj;
streamObj = new stream({
name: 'name',
streamUrl: req.body.RTSP,
wsPort: 9999,
ffmpegOptions: { // options ffmpeg flags
'-stats': '', // an option with no neccessary value uses a blank string
'-r': 30 // options with required values specify the value after the key
}
})
The actual error is that I had imported a stream with the name stream, and tried to initiate the stream with same variable name stream. You have to use a different variable name to initialize the stream. In my case I used streamobj.

How to solve output underflow error using naudiodon / portaudio?

I am writing a small node.js program that will be able to play wav sound files on a chosen audio device.
The sound starts well but it is stoped before the end of the file.
Here is my code :
const fs = require("fs");
const wav = require("wav");
const portAudio = require("naudiodon");
const ao = new portAudio.AudioIO({
outOptions: {
channelCount: 2,
sampleFormat: portAudio.SampleFormat24Bit,
sampleRate: 44100,
}
});
const name = "myfile.wav";
const file = fs.createReadStream(`./sounds/${name}`);
const reader = new wav.Reader();
reader.on("format", () => {
reader.pipe(ao);
ao.start();
});
file.pipe(reader);
process.on("SIGINT", ao.quit);
When I modify the highWaterMark option of fs.createReadStream, it slightly change the cut position in the sound but it never goes until the end of it.
I always get a portAudio status - output underflow log error.
Thanks for any help !
I have been experiencing a similar error, and my solution was to manually write to the AudioIO stream instead of using the pipe commands.
So instead of
reader.on("format", () => {
reader.pipe(ao);
ao.start();
});
You would use
ao.start();
reader.on("data",chunk=>ao.write(chunk));
Output underflow is generally not an issue, but to avoid it I initialised a new instance of PortAudio before playing every file, however that is only applicable if you don't care about slight latency.

change author of mp3 file

I'd like to set the author of a mp3 file based on the data sent by the user.
So far, I managed to get the data sent by the user (hardly an exploit anyway), but I can't manage to change the author of the file. I've tried using both node-id3 package and ffmetadata package, as suggested by this answer, but none of this worked.
The node-id3 approach
Here is a part of code that I wrote for the node-id3 approach, and whille the tags shown in readTags are indeed the ones that I added in the update method, it doesn't change the author of the file when I read it on my computer (with itunes) or on my android phone (with samsung music), which means this approach doesn't work.
const NodeID3 = require('node-id3')
//getting the filename and fileAuthor, as well as creating the file on the server
let tags = {
title: filename,
composer: fileAuthor,
artist: fileAuthor,
remixArtist: fileAuthor,
conductor: fileAuthor,
originalArtist: fileAuthor,
}
let success = NodeID3.update(tags, toBeDownloadedFilePath)
console.log(success)
let readTags = NodeID3.read(toBeDownloadedFilePath)
console.log(readTags)
the ffmetadata approach
Here is the same part written with the ffmetadata approach :
const ffmetadata = require("ffmetadata");
//getting the filename and fileAuthor, as well as creating the file on the server
let tags = {
artist: fileAuthor,
}
ffmetadata.write(toBeDownloadedFilePath, tags, function(err) {
if (err) console.error("Error writing metadata", err);
else console.log("Data written");
});
and with this approach, I'm getting the error :
[mp3 # 0x7f90f8000000] Format mp3 detected only with low score of 1, misdetection possible!
[mp3 # 0x7f90f8000000] Failed to read frame size: Could not seek to 1026.
music1.mp3: Invalid argument
(music1.mp3 being my filename), and my mp3 file is perfectly recognised by all audio reader with which I tested it.
Thank you very much for your help.
So I finally found where was the problem (at least with the node-id3 approach) :
to better understand it, I'll add some details to the creating the file on server step.
Here was my unfunctional code :
const NodeID3 = require('node-id3')
const fs = require('fs-extra'); // file system
fs.ensureFileSync(toBeDownloadedFilePath); //because I need to create the file if it doesn't exist
const toBeDownloadedFile = fs.createWriteStream(toBeDownloadedFilePath);
//the way the audio stream is created isn't relevant, but if you're interested, it's a youtube stream, as per https://www.npmjs.com/package/youtube-audio-stream
let fileWriteStream = audioStream.pipe(toBeDownloadedFile)
let tags = {
title: filename,
composer: fileAuthor,
artist: fileAuthor,
remixArtist: fileAuthor,
conductor: fileAuthor,
originalArtist: fileAuthor,
}
let success = NodeID3.update(tags, toBeDownloadedFilePath)
console.log(success)
let readTags = NodeID3.read(toBeDownloadedFilePath)
console.log(readTags)
And the problem was that my tags were written but immediately deleted by the audiostream.pipe
the solution was therefore quite simple, and I ended up with this code :
const NodeID3 = require('node-id3')
const fs = require('fs-extra'); // file system
fs.ensureFileSync(toBeDownloadedFilePath); //because I need to create the file if it doesn't exist
const toBeDownloadedFile = fs.createWriteStream(toBeDownloadedFilePath);
let fileWriteStream = audiSstream.pipe(toBeDownloadedFile)
fileWriteStream.on('finish', () => {
let tags = {
title: filename,
artist: fileAuthor,
}
NodeID3.update(tags, toBeDownloadedFilePath)
//any additional action, in my case, send the file for a download
})
Hope this helps people with similar problem.

Nodejs PDF KIT, pdf file is in use by application

Sample code I have tried:
var PDFDocument = require('pdfkit');
var fs = require('fs');
var doc = new PDFDocument;
doc.pipe(fs.createWriteStream('test.pdf'));
doc.text('hello how r u');
var x= true;
doc.end();
while(x){
console.log("***");
}
This code is not releasing the test.pdf and thereby I am not able to open the file.
How do I get the file released so that it can be used by rest of the application?
This issue has been discussed on github. The recommended way of doing something when writing to the stream is finished is to watch the finish event on the stream:
stream.on( 'finish', function() { ..... } );

Why append rather than write when using knox / node.js to grab file from Amazon s3

I'm experimenting with the knox module for node.js as a way of managing some small files in an Amazon S3 bucket. Everything works fine stand-alone: I can upload a file, download a file, etc. However, I want to be able to download a file on recurring schedule. When I modify the code to run on an interval, I'm getting the downloaded file appending to the previous instance instead of overwriting.
I'm not sure if I've made a mistake in the file write code or in the knox handling code. I've tried several different write approaches (writeFile, writeStream, etc.) and I've looked at the knox source code. Nothing obvious to me stands out as a problem. Here's the code I'm using:
knox = require('knox');
fs = require('fs');
var downFile = DOWNFILE;
var downTxt = '';
var timer = INTERVAL;
var path = S3PATH + downFile;
setInterval(function()
{
var s3client = knox.createClient(
{
key: '********************',
secret: '**********************************',
bucket: '********'
});
s3client.get(path).on('response', function(response)
{
response.setEncoding('ascii');
response.on('data', function(chunk)
{
downTxt += chunk;
});
response.on('end', function()
{
fs.writeFileSync(downFile, downTxt, 'ascii');
});
}).end();
},
timer);
The problem is with your placement of var downTxt = '';. That is the only place you set downTxt to blank, so every time you retrieve more data, you add it to the data that you got in the previous request because you never clear the data from the previous request. The simplest fix is to move that line to just before the setEncoding line.
However, the way you are processing the data is unnecessarily complicated. Try something like this instead. You don't need to recreate the client every time, and setting the encoding will just break things if you are downloading non-text files, and it won't make a difference with text files. Next, you shouldn't manually collect the data, you can immediately start writing it to the file as you receive it. Lastly, since request is a standard stream, you don't need to monitor the 'data' event because you can just use pipe.
var knox = require('knox'),
fs = require('fs'),
downFile = DOWNFILE,
timer = INTERVAL,
path = S3PATH + downFile,
s3client = knox.createClient({
key: '********************',
secret: '**********************************',
bucket: '********'
});
(function downloadFile() {
var str = fs.createWriteStream(downFile);
s3client.get(path).pipe(str);
str.on('close', function() {
setTimeout(downloadFile, timer);
});
})();

Resources