FreeTTS no audio linux ubuntu - no errors

2019-02-07 01:07发布

问题:

I am running Ubuntu 10.10 using Java 6 and can not get FreeTTS to output any audio. I have tried it now on 3 different computers and even asked a buddy of mine to try it on his Ubuntu PC and he had the same problem. There is absolutly no errors that are displayed, after getting the MBROLA i no longer even get the warning about No MBROLA voices detected. blah blah blah..

Using the same computer I ran a virtual box and started Windows XP, i was actually able to get audio when running the HelloWorld.jar and TTSHelloWorld.jar however the freetts.jar is still silent when I try to input my own text.

Command I use.

java -jar lib/freetts.jar -text Hello

When I hit enter it starts up and used to give me the missing MBROLA warning message but now it just sits there until i CTRL-C to stop it.

I dont understand what I am doing wrong and why nobody else is having this problem, when I expierence it on every computer, well it works somewhat on Windows. Can anyone Help me?

Thanks,

John

回答1:

I'm not sure whether you already managed to solve this one, but I ran into the same problem (Ubuntu 10.10 / JavaSE6). After some investigation of the FreeTTS source I found the culprit, a deadlock, in com.sun.speech.freetts.audio.JavaStreamingAudioPlayer. This deadlock occurs when a Line is opened and the Line is of the type org.classpath.icedtea.pulseaudio.PulseAudioSourceDataLine (which is likely to be the default in Ubuntu 10.10 w JavaSE6). Since you'd always want to open a Line to get audio out, this deadlock will always occur.

The cause of this deadlock lies in the fact that in the JavaStreamingAudioPlayer an assumption is made about Line, namely that all LineListeners will be notified of a LineEvent of type open from the same Thread as Line.open() was called, or after the Line has been opened (and the call to Line.open() can return). This is not the case for the PulseAudioSourceDataLine; it first calls all LineListeners from the PulseAudio event Thread, waits for all of them to return and then returns from the open call. With the JavaStreamingAudioPlayer forcing synchronization around the call of Line.open() and the handling of a specific LineListener which task is to see whether the Line ís actually open, the deadlock occurs.

The workaround I chose for solving this problem is to implement an AudioPlayer which doesn't has this problem. I basically copied JavaStreamingAudioPlayer and altered the synchronization blocks on line 196 and line 646 ( full source for reference : http://www.javadocexamples.com/java_source/com/sun/speech/freetts/audio/JavaStreamingAudioPlayer.java.html ).

___: // This is the actual JavaStreamAudioPlayer source, not the fix
195: ...
196:     synchronized (openLock) {
197:         line.open(format, AUDIO_BUFFER_SIZE); // Blocks due to line 646
198:         try {
199:             openLock.wait();
200:         } catch (InterruptedException ie) {
201:             ie.printStackTrace();
202:     }
203: ...

643: ...
644: public void update(LineEvent event) {
645:     if (event.getType().equals(LineEvent.Type.OPEN)) {
646:         synchronized (openLock) { // Blocks due to line 196
647:             openLock.notifyAll();
648:         }
649:     }
650: }
651: ...

I removed both synchronization blocks and instead of ensuring both parts are mutually excluded I used a Semaphore to signal that the Line is in fact open. Of course this is not really a necessity since the PulseAudioSourceDataLine already guarantees being opened upon returning, but it is more likely to play nice when testing the same code on another platform. I didn't dive into the code long enough to say what is going to happen when you open/close/open the line by multiple Threads at the same time. If you're going to do this you are probably looking at a larger rewrite of the JavaStreamingAudioPlayer ;).

Finally, after you have created your new AudioPlayer you'll have to instruct FreeTTS to use your implementation rather than the default JavaStreamingAudioPlayer. This can be done by using

System.setProperty("com.sun.speech.freetts.voice.defaultAudioPlayer", "classpath.to.your.AudioPlayer");

somewhere early in your code.

Hopefully this all works for you.



回答2:

I am a student who has been trying to make FreeTTS working on its Ubuntu for one week. And finally I found the answer here : thank you so much hakvroot !

Your answer was perfect but you did not put your implementation and this took me quite one hour to understand what was going on in the JavaStreamingAudioPlayer class. To help the other people like me who are not used in "diving" in a completely unknown Java code (I am still a student), I will put here my code and hope it will help other people :) .

First, a more detailed explanation : around line 152, the JavaStreamingAudioPlayer opens a Line. However this operation can require some time so before using it, it wants to check it is opened. In the current implementation, the solution used is to create a LineListener listening to this line and then to sleep (using the wait() method of the threads).

The LineListener will "wake up" the main Thread using a notifyAll() and will do this only when it receives a LineEvent of type "OPEN" which will guarantee that the line has been opened.

However as explained by hakvroot here the problem is that the notification is never sent because of the specific behavior of the DataLine used by Ubuntu.

So I removed the synchronized, wait() and notifyAll() parts of the code but as hakvroot, then your JavaStreamingAudioPlayer might try to use your Line before it is opened : you need to wait for the confirmation with a new mechanism to stop the JavaStreamingAudioPlayer and to wake it up later, when the confirmation arrived.

So I used the Semaphore which havkroot used (see Javadoc for explanations on this lock system) initiated with 1 stack :

  • when the line is opened it acquires one stack (so 0 remains)

  • when it wants to use the line it tries to acquire another (so it is stopped)

  • when the listener gets the event we are looking for, it releases the semaphore

  • this frees the JavaStreamingAudioPlayer who can go for the next part

  • do not forget to release again the semaphore so it has again 1 stack for the next line to open

And here is my code :

Declare a Semaphore variable :

private Semaphore hackSemaphore;

Initiate it in the constructor :

hackSemaphore = new Semaphore(1);

Then the first part to replace (see hakvroot to see where to put it) :

            line = (SourceDataLine) AudioSystem.getLine(info);
            line.addLineListener(new JavaStreamLineListener());

            line.open(format, AUDIO_BUFFER_SIZE);
            hackSemaphore.acquire(); 
            hackSemaphore.acquire(); 
            opened = true;
            hackSemaphore.release();

And the second part :

    public void update(LineEvent event) {
        if (event.getType().equals(LineEvent.Type.OPEN)) {
            hackSemaphore.release();
        }
    }


回答3:

I guess had the same issue on Ubuntu 12.04/OpenJDK-6, the execution get stuck in Voice.allocate() with no errors and no response. I tried using the Oracle/Sun JDK-6 instead of OpenJDK, and it worked fine.

P.S. Nice guide to install SunJDK on Ubuntu and configuring as default http://www.devsniper.com/ubuntu-12-04-install-sun-jdk-6-7/