Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trouble executing nanotts via Node.js child_process.exec #8

Open
clickworkorange opened this issue Sep 27, 2019 · 16 comments
Open

Trouble executing nanotts via Node.js child_process.exec #8

clickworkorange opened this issue Sep 27, 2019 · 16 comments

Comments

@clickworkorange
Copy link

clickworkorange commented Sep 27, 2019

Trying to talk to nanotts from Node.JS i get an error regardless of how I format the command string:

exec("nanotts -c -i \"hello\"", function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts -c -i "hello"
 **error: multiple inputs
exec("nanotts -c -i hello", function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts -c -i hello
 **error: multiple inputs
exec("nanotts -c hello", function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts -c hello
 **error: trailing commandline arguments
exec("nanotts hello", function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts hello
 **error: trailing commandline arguments
exec("nanotts -i \"hello\" -c", function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts hello
 **error: trailing commandline arguments
exec("nanotts -i 'hello' -c", function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts -i 'hello' -c
 **error: multiple inputs

How should I format my command line to avoid this?

@clickworkorange
Copy link
Author

clickworkorange commented Sep 28, 2019

I tried using Node.JS's execFile() method instead, with the arguments supplied as an array:

execFile("nanotts",[
        "-p", 
        "-v", "en-GB", 
        "--pitch", 1, 
        "--speed", 1, 
        "Hello world"
    ], function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts -p -v en-GB --pitch 1 --speed 1 Hello world
 **error: trailing commandline arguments

I also tried with an explicit input argument and quoted string:

execFile("nanotts",[
        "-p", 
        "-v", "en-GB", 
        "--pitch", 1, 
        "--speed", 1, 
        "-i", "\"Hello world\""
    ], function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts -p -v en-GB --pitch 1 --speed 1 -i "Hello world"
 **error: multiple inputs

@clickworkorange
Copy link
Author

clickworkorange commented Sep 28, 2019

Curiously, I can get it to work by commenting out the in_mode check at line 609-612 in main.cpp ...

nanotts/src/main.cpp

Lines 609 to 612 in e856ec3

if ( in_mode != IN_NOT_SET && in_mode != IN_CMDLINE_TRAILING ) {
fprintf( stderr, " **error: trailing commandline arguments\n\n" );
return -4;
}

... and using the syntax in my first execFile() example ...

execFile("nanotts",[
        "-p", 
        "-v", "en-GB", 
        "--pitch", 1, 
        "--speed", 1, 
        "Hello world"
    ], function (err, stdout, stderr) {
...
});
read: 12 bytes from command line
using lang: en-GB
speed: 1.00
pitch: 1.00

@gmn
Copy link
Owner

gmn commented Sep 29, 2019

Hi, The program execution statements you posted work for me on the command-line. It would help me to know what you are trying to do. Are you trying to generate a WAV file, or playback? There are no web-friendly builtins at the moment. Nanotts does require you to specify which way you would like it to produce output. There isn't a default. Perhaps there should be. I don't remember precisely what my reasoning was for designing it this way.

@gmn
Copy link
Owner

gmn commented Sep 29, 2019

I'm sorry, reading closer, I see you are running node.js, not in a web browser. I'll try to repro this and get to the bottom of it. Citing lines 609-612 gets pretty close to the heart of it I think. You can safely leave those lines commented out in your copy to run in the mean-time. I'll post any findings here. Luckily, I'm fairly familiar with Node. :)

@gmn
Copy link
Owner

gmn commented Sep 29, 2019

Oh, one thing I see right off the bat is that if you doexecFile( nanotts , [ "-p", "-v", "en-GB", "--pitch", 1, "--speed", 1, "Hello world"

What will be sent to the command line will be nanotts -p -v en-GB --pitch 1 --speed 1 Hello world

The last phrase needs to be quoted. You could put an escape in like this: execFile(nanotts",[ "-p", "-v", "en-GB", "--pitch", 1, "--speed", 1, "Hello\ world"

Also, the '"' after nanotts looks suspicious.

Best....g

@clickworkorange
Copy link
Author

clickworkorange commented Sep 29, 2019

Hey there :) Many thanks for the swift response!

The last phrase needs to be quoted. You could put an escape in like this: execFile(nanotts",[ "-p", "-v", "en-GB", "--pitch", 1, "--speed", 1, "Hello\ world"

Yes, however if I do quote the string I still get the "trailing commandline arguments" error, unless I comment out lines 609-612:

execFile("nanotts",[
        "-p", 
        "-v", "en-GB", 
        "--pitch", 1, 
        "--speed", 1, 
        "\"Hello\ world\""
    ], function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts -p -v en-GB --pitch 1 --speed 1 "Hello world"
 **error: trailing commandline arguments

And if I make the input explicit with the -i flag, I get (as before):

execFile("nanotts",[
        "-p", 
        "-v", "en-GB", 
        "--pitch", 1, 
        "--speed", 1, 
        "-i", "\"Hello\ world\""
    ], function (err, stdout, stderr) {
...
});
Error: Command failed: nanotts -p -v en-GB --pitch 1 --speed 1 -i "Hello world"
 **error: multiple inputs

But I think you're on to something wrt quotes and escaping; I see in the error message that the command has been called with the "Hello world" string without the escaped space - I would have expected this to be "Hello\ world" instead. Perhaps Node.JS does something to "unescape" the string? Would this cause the "multiple inputs" error? Or has the escape character merely been dropped from the error message?

Also, the '"' after nanotts looks suspicious.

Ooops. That was just a typo in my posts - fixed now.

@clickworkorange
Copy link
Author

clickworkorange commented Sep 29, 2019

I've looked at how @luisivan does it in his Node.JS wrapper for pico2wave

https://github.com/luisivan/node-picotts/blob/8c6b183b884890c8e9422f93036b374942398c8b/index.js

Seems no different to my code when it comes to quoting/escaping the message string. I also found this post on StackOverflow:

But actually... node.js will escape all needed characters for you

That would make a lot of sense, but doesn't explain why I'm having issues...

@clickworkorange
Copy link
Author

In case you're wondering, this is what I'm working on: https://github.com/clickworkorange/node-red-contrib-nano-tts

@clickworkorange
Copy link
Author

clickworkorange commented Oct 3, 2019

Still stuck on this - any help much appreciated!

myString = "Einmal Bier bitte";
execFile("nanotts",[
        "-p", 
        "-v", "de-DE", 
        "--pitch", 0.5, 
        "--speed", 0.8, 
        "--volume", 0.5, 
        "-i", "\"" + myString.replace(/\s/g, "\\ ") + "\""
    ], function (err, stdout, stderr) {
    ...
    }
);
Error: Command failed: nanotts -p -v de-DE --pitch 0.5 --speed 0.8 --volume 0.5 -i "Einmal\ Bier\ bitte"
 **error: multiple inputs

@gmn
Copy link
Owner

gmn commented Oct 4, 2019

Ok, I'm back. I got it to work by doing this. Give it a try. Ymmv, we'll see I guess.

const execFile = require('child_process').execFile;
myString = "Einmal Bier bitte";

var myargs = [ "-w",
        "-v", "de-DE",
        "--pitch", 0.5,
        "--speed", 0.8,
        "--volume", 0.5,
        "-i", "\"Hello World\"" ]

console.log(myargs)
console.log(myargs.join(' '))

execFile( "./nanotts " + myargs.join(" "),[],
    function (err, stdout, stderr) {
        console.log( "got to execution callback" );
        console.log('stdout ' +stdout);
        console.log('stderr ' + stderr);
    }
);

Note: this writes a WAV, not playback. Changing to a -p should work though.

@clickworkorange
Copy link
Author

clickworkorange commented Oct 4, 2019

Hi there! Many thanks for your reply - I know this is a pain, but it is for the greater good :) I've tried joining the arguments into a string, and concatenating this with the command, but unfortunately the execFile method expects the arguments to be supplied as an array, and fails with ENOENT if called this way:

Error: spawn nanotts -p -v es-ES --pitch 1.1 --speed 1.1 --volume 0.8 -i "una cerveza por favor" ENOENT

There is the plain exec Node.JS function, which does take the executable name and its arguments as a single string - however this fails with the all too familiar **error: multiple inputs:

Error: Command failed: nanotts -p -v es-ES --pitch 1.1 --speed 1.1 --volume 0.8 -i "una cerveza por favor"
 **error: multiple inputs

In both cases a copy/paste of the command as listed in the error messages executes without issue. I remain convinced that this is caused by some oddity in how nanotts parses the command line arguments - coupled with how Node.JS passes them on to it - though despite plenty of searching on the web, and plenty of staring at the relevant bits in your main.cpp, I have been unable to figure out what that oddity is. There seems to be rather a lot of code to do with argument parsing, but that's probably just becuase I'm looking at it with untrained eyes!

@rogueadmin
Copy link

rogueadmin commented Oct 18, 2020

This doesn't work when a tty doesn't exist, so if running from a web server or cron job won't work.

I commented out this line in main.cpp line 475

if ( ! isatty(fileno(stdin)) ) {
    in_mode = IN_STDIN;
}

@clickworkorange
Copy link
Author

I commented out this line in main.cpp line 475

So how can we turn this into a proper fix?

@gmn
Copy link
Owner

gmn commented Feb 20, 2021

Ola, your comment tripped my email and got me paying attention again. I'll take a look and try to post something asap. ~g

@amathieson
Copy link

Hi, I am doing a similar thing in PHP, the way I resolved the issue was to pass the text in through stdin by echoing the text through a pipe into nanotts, this is not perfect, but does resolve the issue, I am working on a better implementation for PHP however (will probably just pass directly through pico rather than via nano as nano is simply a wrapper for pico from what I can tell.

echo '$text' | $nano -c -l /bin/lang/lang -v $lang -m | ffmpeg -f s16le -ar 16k -ac 1 -i pipe: tmp/$uuid.wav

@zen2
Copy link

zen2 commented Jun 1, 2021

I got the same problem and finally use a simple bash wrapper as a workaround:

#!/bin/bash
DIR="/home/homeassistant/tts/nanotts"
CMD="nanotts -l $DIR/lang -v fr-FR --volume 0.1 -p"
MSG="$@"
echo "$MSG" | "$DIR"/$CMD 2>/dev/null

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants