How to solve output underflow error using naudiodon / portaudio? - node.js

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.

Related

first m3u8 ts segment not working after mp4 to m3u8 conversion by node js

index.js
const ffmpegPath = require('#ffmpeg-installer/ffmpeg').path;
const ffmpeg = require('fluent-ffmpeg');
const process = require('process');
const args = process.argv.slice(2);
if (args.length !== 4) {
console.error('Incorrect number of arguments');
process.exit(1);
}
const startTime = args[0];
const timeDuration = args[1];
const inputFile = args[2];
const outputFile=args[3];
ffmpeg.setFfmpegPath(ffmpegPath);
ffmpeg(inputFile)
.setStartTime(startTime)
.setDuration(timeDuration)
.output(outputFile)
.outputOptions('-hls_list_size 0')
.on('end', function(err) {
if(!err) { console.log('conversion Done') }
})
.on('error', function(err){
console.log('error: ', err)
}).run();
Here is the index.js and I'm running it by hitting the command on the terminal
node index.js 5 40 ./input.mp4 ./output.m3u8
Here 5 is for starting time and 40 is the time duration in seconds. The process creates m3u8 with ts files but the first ts file isn't getting created properly. It's been created in kb format while all the other files in mb format.
the output_test0 isn't getting generated properly and so that's why while playing the m3u8 file, the first few seconds is just static picture. This issue has been happening with the first ts output only. Any trick on how to fix it?
Following your comment about how it seems to be caused by the use of input seeking instead of output seeking:
Use seek() or seekOutput() instead of setStartTime(). The documentation describes the difference:
seek(time): seek output
Aliases: seekOutput().
Seeks streams before encoding them into the output. This is different from calling seekInput() in that the offset will only apply to one output. This is also slower, as skipped frames will still be decoded (but dropped).
The time argument may be a number (in seconds) or a timestamp string (with format [[hh:]mm:]ss[.xxx]).
ffmpeg('/path/to/file.avi')
.seekInput('1:00')
.output('from-1m30s.avi')
.seek(30)
.output('from-1m40s.avi')
.seek('0:40');
setStartTime() is an alias for seekInput(). From the same documentation:
seekInput(time): set input start time
Alias: setStartTime().
Seeks an input and only start decoding at given time offset.
Note that seek() or seekOutput() should be applied to the output and not on the input as seekInput(), i.e. after output().

Problem with using naudiodon / portaudio?

When I try the "playing audio streaming audio data" from the naudiodon library I only get noise on the speaker. I'm interested in how to get real sound from an app (for example when playing music from youtube). I wonder if the sound is then saved in my case in stream4800.wav?
I wonder what all the dependency I need for the project?
When I just record over a microphone with inOptions: {} I get a successfully saved stream (sound). But when I want to get the sound out of the speakers outOptions: {} then the story becomes unclear to me.
Here is an example of my code:
const portAudio = require('naudiodon');
const wav = require("wav");
const ao = new portAudio.AudioIO({
outOptions: {
channelCount: 2,
sampleFormat: portAudio.SampleFormat64Bit,
sampleRate: 44100,
}
});
const name = "stream4800.wav";
const file = fs.createReadStream(`./${name}`);
const reader = new wav.Reader();
ao.start();
reader.on("data",chunk=>ao.write(chunk));
file.pipe(reader);
Thanks for any help
Hi please check the audio file [stream4800.wav] is Mono or Sterio. I would recommend you to use sterio file with applicable sampleRate will help you out.

Redirect Readable object stdout process to file in node

I use an NPM library to parse markdown to HTML like this:
var Markdown = require('markdown-to-html').Markdown;
var md = new Markdown();
...
md.render('./test', opts, function(err) {
md.pipe(process.stdout)
});
This outputs the result to my terminal as intended.
However, I need the result inside the execution of my node program. I thought about writing the output stream to file and then reading it in at a later time but I can't figure out a way to write the output to a file instead.
I tried to play around var file = fs.createWriteStream('./test.html'); but the node.js streams rather give me headaches than results.
I've also looked into the library's repo and Markdown inherits from Readable via util like this:
var util = require('util');
var Readable = require('stream').Readable;
util.inherits(Markdown, Readable);
Any resources or advice would be highly appreciated. (I would also take another library for parsing the markdown, but this gave me the best results so far)
Actually creating a writable file-stream and piping the markdown to this stream should work just fine. Try it with:
const writeStream = fs.createWriteStream('./output.html');
md.render('./test', opts, function(err) {
md.pipe(writeStream)
});
// in case of errors you should handle them
writeStream.on('error', function (err) {
console.log(err);
});

Speech to Text: Piping microphone stream to Watson STT with NodeJS

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'));

Pipe to stdout and writeable stream

I'm piping a file through a duplex string (courtesy of through) and I'm having trouble printing information to stdout and writing to the file. One or the other works just fine.
var fs = require('fs');
var path = require('path');
var through = require('through'); // easy duplexing, i'm young
catify = new through(function(data){
this.queue(data.toString().replace(/(woof)/gi, 'meow'));
});
var reader = fs.createReadStream('dogDiary.txt'); // woof woof etc.
var writer = fs.createWriteStream(path.normalize('generated/catDiary.txt')); // meow meow etc.
// yay!
reader.pipe(catify).pipe(writer)
// blank file. T_T
reader.pipe(catify).pipe(process.stdout).pipe(writer)
I'm assuming this is because process.stdout is a writeable stream, but I'm not sure how to do what I want (i've tried passing {end: false} to no avail).
Still struggling to wrap my head around streams, so forgive me if i've missed something obvious : )
I think what you want is:
reader.pipe(catify)
catify.pipe(writer)
catify.pipe(process.stdout)
These needed to be separated because pipes return their destinations and not their source.

Resources