Showing posts with label tts. Show all posts
Showing posts with label tts. Show all posts

Friday, 1 June 2018

It speaks!

Porting TTS to the ESP8266 processor has been on my to-do list for a while now, prompted by a comment on an issue I'd created to remind me. To do this, I realised that I'd have to reacquaint myself with PWM on the ATmega328p.

At its most basic, a PWM waveform is a square-wave with a varying duty-cycle. Both the frequency of the square-wave and the duty-cycle are determined on an AVR by the its built-in timers. PWM on Arduino pin 9 (hardware pin 15, OC1A), for example, is driven by Timer-1, a 16-bit timer.

Several PWM modes are available; TTS chooses the "phase and frequency corrected" option.


The diagram shows how this works to change the duty-cycle of the output pin:
  • the timer counts from BOTTOM to TOP and back down again
  • when it crosses the OCR1 threshold going up, it sets the OC1A output high,
  • when it crosses this threshold on the way down again, it sets the OC1A output low. 
Therefore by dynamically changing OCR1, we can change the "volume" on the output pin.

The final piece of the puzzle is how the frequency of the square wave is determined. Since the timer ticks on every clock cycle, it will take 2*TOP cycles to cycle from BOTTOM to TOP and back. The AVR has a clock frequency of 16MHz and PWM_TOP in the TTS library is defined as 1200/2. This gives a frequency of about 13kHz.

By default PWM on Arduino/ESP8266 has a range of 1023 and frequency of 1kHz. However it provides APIs to change these values. By setting them appropriately, the ESP8266 speaks!

Wemos Mini D1 with TTS amplifier and super-cheap speaker
The picture shows a Wemos Mini D1 connected to a modified version of the simple LM386 amplifier described at the TTS GitHub. (The modifications arose from a Hackaday article on the LM386 and are summarised in the GitHub's README.)

This amplifier sounds surprisingly good, considering its total cost was about €1. Hooking it up to a signal generator and oscilloscope produced the following pretty pictures.

At low volume, the output is a pretty faithful inversion of the input signal (a 1kHz sine wave):

Undistorted Signal
At high volume, the output is clipped by the supply rails (but still sounds pretty good to my untrained ear):
Clipped Signal
These pictures were produced using an Xprotolab Plain, an amazing combination of Oscilloscope, Signal Generator and Protocol Analyzer implemented on an AVR XMEGA. Buy one!
Xprotolab Plain

Sunday, 10 February 2013

Heroic Failures: Voice of TicBot

A project such as Magic Mouth (spotted at Dangerous Prototypes) is a fine example of the surprising power of the ATMega chip. With the addition of a simple audio amplifier and the impressive Webbotlib, your humble 8-bit microcontroller can turn English text into speech!

My first attempt at a project with a similar setup simply read text from the serial port and converted it into speech. (You can find the amplifier circuit schematic and Webbotlib code at Github here, it differs from Magic Mouth mainly by not depending on an external Arduino to supply the text to be spoken.) Here is a picture of it in all its homebrew glory; the amplifier is the little board piggybacking the Arduino clone, the 8-pin chip in the middle is an LM386.



Around the same time, I came across an article about a piece of software which tweeted tics: TicBot. While more Eliza-with-Tourettes than major AI breakthrough, it can be very funny:
Under the influence of Xmas cheer, an idea was born... Voice of TicBot! It would be a humble Arduino connected to Twitter by Ethernet, speaking TicBot's words of wisdom in a robot voice! How hard could that be?

The first obstacle was Twitter's use of OAuth; while this solves the problem of third-party web-application authentication, it is a pain for a device without a traditional UI. Thankfully the good people at supertweet.net have solved this problem, providing an OAuth proxy for Twitter to which devices authenticate themselves using good ol' Basic Auth.

It was then quite straightforward to write a little sketch which polled api.supertweet.net every 10 minutes for TicBot's timeline (using JeeLabs' handy Ethernet driver) and grubbed through the response for the id and text fields; no space for a fancy JSON parser here!

The next problem was how to combine the two applications? One an Arduino sketch (in C++) and the other a Webbotlib application (in C). Neither the option of porting the Ethernet driver to Webbotlib nor that of porting the relevant parts of Webbotlib to Arduino was particularly appealing. Moreover the combined size of the two applications exceeded the flash memory limit of the ATMega-328!

Fortunately a solution turned up in the shape of an Arduino port of Webbotlib's text-to-speech module, TTS. (This was a lucky piece of googling --- my Romanian is non-existent.) It required some work because my amplifier expected its PWM input to come from pin 3 whereas the TTS module emitted it on pin 10, which was needed for the Ethernet SPI. Adding the required functionality for PWM on pin 3 was achieved with the help of a nice online tutorial. An Arduino library updated for version 1.0 and with this functionality and some more examples (including the sketch for VoTB) can be found here.

With these problems solved, Voice of TicBot was finally operational! Unfortunately it was also a failure: the gnomic nature of TicBot's utterances combined with the low quality of the speech rendition made it necessary to visually consult twitter.com whenever output was heard! Here's an example of its output.

(All of this reminds me of a (possibly apocryphal) story about Stephen Hawking's first speech synthesizer: his students were pressed into service as translators, due, one assumes, to their familiarity both with the subject matter and the machine's vocal stylings.)