Not that I can play an mp3 file, let's find a way to build a simple TTS speech
As we live in a connected world, why not leveraging the power out there on the web to overtake hardware limitations?
I found an interesting site here
that provides a text-to-speech web service
To convert a text programmatically from NodeJS, the URL to invoke is the following one
var mp3url='http://tts-api.com/tts.mp3?q='+encodeURIComponent(text);
Given this url, we can use NodeJS's http.get to download the mp3 file. Here is the code
function getMp3(mp3url, filename, cb) { console.log("Getting url " + mp3url); http.get(mp3url, function(response) { if (response.statusCode > 300 && response.statusCode < 400 && response.headers.location) { console.log("Redirect detected"); // The location for some (most) redirects will only contain the path, not the hostname; // detect this and add the host to the path. if (url.parse(response.headers.location).hostname) { // Hostname included; make request to response.headers.location console.log("Redirecting to " + response.headers.location); getMp3(response.headers.location,filename, cb); } else { // Hostname not included; get host from requested URL (url.parse()) and prepend to location. } // Otherwise no redirect; capture the response as normal } else { response.setEncoding('binary'); response.on('data', function (chunk) { console.log("Data " + chunk.length); _data =_data.concat(chunk); }); //the whole response has been recieved, so we just print it out here response.on('end', function () { console.log("End " + _data.length); var f = fs.createWriteStream(filename); f.write(new Buffer(_data, 'binary')); f.end(); cb(); }); } }); };
Here are two kind of magic:
- Redirection: when the above-mentioned URL is requested, the request is redirected to a URL like this one
http://media.tts-api.com/1e4e888ac66f8dd41e00c5a7ac36a32a9950d271.mp3
NodeJS's http.get does not provided redirection-following capabilities, we have to implement the logic using the following lines of code
if (response.statusCode > 300 && response.statusCode < 400 && response.headers.location) { console.log("Redirect detected"); // The location for some (most) redirects will only contain the path, not the hostname; // detect this and add the host to the path. if (url.parse(response.headers.location).hostname) { // Hostname included; make request to response.headers.location console.log("Redirecting to " + response.headers.location); getMp3(response.headers.location, cb); } else { // Hostname not included; get host from requested URL (url.parse()) and prepend to location. }
- by default, NodeJS filesystem assumes data received after a GET is a text. In this case, we are receiving binary data, so we have to let NodeJS know we want it to treat data as an octect stream. This is accomplished by means of the following instruction
response.setEncoding('binary');
Finally, we have to instruct NodeJS to parse for a specific string from on serial port
yunPort = new serialPort.SerialPort('/dev/ttyATH0', { baudrate: 115200 }); yunPort.on('data', function(data) { console.log('data ' + data); if (_ws) { _data = _data + data; if (_data.indexOf("\r\n") > 0) { // check if this is a PLAY command if (_data.substring(0, 5) == "PLAY") { var text = _data.substring(6); var mp3url='http://tts-api.com/tts.mp3?q='+encodeURIComponent(text); var filename = '/mnt/sda1/' + text.replace(' ', '_') + '.mp3'; getMp3(text, filename, function() { var exec = require('child_process').exec; exec('/usr/bin/madplay ' + filename); }); } else _ws.send(_data); _data = ""; } } });
Now, on the Atmel side I can simply write
Serial.println("hello world");
to make MagicHat speak! That's Internet power!
Top Comments