Exception with Freetts when using kevin or mbrola

2019-01-23 08:31发布

问题:

I am trying to run a program using freetts. I am able to compile the program however I am not able to use kevin or mbrola voices I get the follwing output message at the end

System property "mbrola.base" is undefined. Will not use MBROLA voices.
LINE UNAVAILABLE: Format is pcm_signed 16000.0 Hz 16 bits 1 channel big endian

import javax.speech.*;
import javax.speech.synthesis.*;
import java.util.*;

class freetts {

    public static void main(String[] args) {
        try{ 
            Calendar calendar = new GregorianCalendar();
            String sayTime = "It is " + calendar.get(Calendar.HOUR) + " " + calendar.get(Calendar.MINUTE) + " " + (calendar.get(Calendar.AM_PM)==0 ? "AM":"PM");
            Synthesizer synth = Central.createSynthesizer(null);
            synth.allocate();
            synth.resume();
            synth.speakPlainText(sayTime, null);
            synth.waitEngineState(Synthesizer.QUEUE_EMPTY);
            synth.deallocate();
        }
        catch(Exception e){
            e.printStackTrace();
        }
    }
}

回答1:

It seems that "To enable FreeTTS support for MBROLA, merely copy mbrola/mbrola.jar to lib/mbrola.jar. Then, whenever you run any FreeTTS application, specify the "mbrola.base" directory as a system property:

  java -Dmbrola.base=/home/jim/mbrola -jar bin/FreeTTSHelloWorld.jar mbrola_us1"

I found this at:

http://freetts.sourceforge.net/mbrola/README.html



回答2:

http://workorhobby.blogspot.com/2011/02/java-audio-freetts-line-unavailable.html

A big thanks to the author.


A program based on FreeTTS, the free text-to-speech engine for Java, was getting occasional errors

"LINE UNAVAILABLE: Format is ..."

Turns out there is no Java Exception or other mechanism to detect this error that occurs inside the FreeTTS library. All you get is the message on System.out, so there is no good way to react programatically.

Workaround: Configure the FreeTTS audio player to attempt accessing the audio device more than once until it succeeds. In this example, a short delay of 0.1 seconds is used to not miss an opportunity to grab the audio device; we keep trying for 30 seconds:

System.setProperty("com.sun.speech.freetts.audio.AudioPlayer.openFailDelayMs", "100");
System.setProperty("com.sun.speech.freetts.audio.AudioPlayer.totalOpenFailDelayMs", "30000");

If the audio device is permanently used by another program, there is of course no way to get access. Under Linux, this command will display the ID of the process that is currently holding the audio device, so you can then try to get rid of the offending program:

/sbin/fuser /dev/dsp


回答3:

The second phrase has nothing to do with mbrola, but with a horrendous java linux sound bug that is still not fixed. Check the third post here: https://forums.oracle.com/forums/thread.jspa?threadID=2206163

That is happening because freetts "trusts" the sourcedataline, instead of doing the workaround on that post. The bug is in the jdk, but can be worked around by finding where in freetts that is happening and inserting the workaround & recompiling.

Here is a testcase

package util.speech;

import java.util.Iterator;
import java.util.Locale;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Mixer;
import javax.sound.sampled.SourceDataLine;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

public class VoiceTest {



    public VoiceTest() {
    }

    @BeforeClass
    public static void setUpClass() throws Exception {
    }

    @AfterClass
    public static void tearDownClass() throws Exception {
    }

    @Before
    public void setUp() {

    }

    @After
    public void tearDown() {
    }

    @Test
    public void testDataLineAvailableAndBuggyInJDK() throws LineUnavailableException {
        boolean failedSimpleGetLine = false;
        AudioFormat format = new AudioFormat(44100, 16, 2, true, false);
        SourceDataLine line = null;
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
        try {
            line = (SourceDataLine) AudioSystem.getLine(info);
        } catch (LineUnavailableException e) {
            //ok, at least it says so
            throw e;
        }
        try {
            //if this fails the jdk is very buggy, since it just told us
            //the line was available
            line.open(format);
        } catch (LineUnavailableException e) {
            failedSimpleGetLine = true;
        } finally {
            if (line.isOpen()) {
                line.close();
            }
        }



        //now if this is true, test if it's possible to get a valid sourcedataline
        //or the only bug is adquiring a sourcedataline doesn't throw a lineunavailable
        //exception before open
        Assume.assumeTrue(failedSimpleGetLine);
        line = getSourceDataLine(format);
        if (line == null) {
            return;
        }

        try {
            line.open(format);
        } catch (LineUnavailableException e) {
            //ok then it is consistent, and there is only one bug
            fail("Line Unavailable after being adquired");
        } finally {
            if (line.isOpen()) {
                line.close();
            }
        }
        fail("line available after first test not managing to adquire it");
    }


    private SourceDataLine getSourceDataLine(AudioFormat format) {
        try {
            DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
            for (Mixer.Info mi : AudioSystem.getMixerInfo()) {
                SourceDataLine dataline = null;
                try {
                    Mixer mixer = AudioSystem.getMixer(mi);
                    dataline = (SourceDataLine) mixer.getLine(info);
                    dataline.open(format);
                    dataline.start();
                    return dataline;
                } catch (Exception e) {
                }
                if (dataline != null) {
                    try {
                        dataline.close();
                    } catch (Exception e) {
                    }
                }
            }
        } catch (Exception e) {
        }
        return null;
    }
}


回答4:

I know i am posting it little late, but this may help someone. I tried with both kevin and mbrola, and it worked for me. Please find the code below.

    package com.mani.texttospeech;

import java.beans.PropertyVetoException;
import java.util.Locale;

import javax.speech.AudioException;
import javax.speech.Central;
import javax.speech.EngineException;
import javax.speech.EngineStateError;
import javax.speech.synthesis.Synthesizer;
import javax.speech.synthesis.SynthesizerModeDesc;
import javax.speech.synthesis.Voice;

/**
 *
 * @author Manindar
 */
public class SpeechUtils {

    SynthesizerModeDesc desc;
    Synthesizer synthesizer;
    Voice voice;

    public void init(String voiceName) throws EngineException, AudioException, EngineStateError, PropertyVetoException {
        if (desc == null) {
            System.setProperty("freetts.voices", "com.sun.speech.freetts.en.us.cmu_us_kal.KevinVoiceDirectory");
            desc = new SynthesizerModeDesc(Locale.US);
            Central.registerEngineCentral("com.sun.speech.freetts.jsapi.FreeTTSEngineCentral");
            synthesizer = Central.createSynthesizer(desc);
            synthesizer.allocate();
            synthesizer.resume();
            SynthesizerModeDesc smd = (SynthesizerModeDesc) synthesizer.getEngineModeDesc();
            Voice[] voices = smd.getVoices();
            for (Voice voice1 : voices) {
                if (voice1.getName().equals(voiceName)) {
                    voice = voice1;
                    break;
                }
            }
            synthesizer.getSynthesizerProperties().setVoice(voice);
        }
    }

    public void terminate() throws EngineException, EngineStateError {
        synthesizer.deallocate();
    }

    public void doSpeak(String speakText) throws EngineException, AudioException, IllegalArgumentException, InterruptedException {
        synthesizer.speakPlainText(speakText, null);
        synthesizer.waitEngineState(Synthesizer.QUEUE_EMPTY);
    }

    public static void main(String[] args) throws Exception {
        SpeechUtils su = new SpeechUtils();
        su.init("kevin16");
//        su.init("kevin");
//        su.init("mbrola_us1");
//        su.init("mbrola_us2");
//        su.init("mbrola_us3");
        // high quality
        su.doSpeak("Hi this is Manindar. Welcome to audio world.");
        su.terminate();
    }
}

And add the below dependencies to your pom.xml file.

<dependencies>
        <dependency>
            <groupId>net.sf.sociaal</groupId>
            <artifactId>freetts</artifactId>
            <version>1.2.2</version>
        </dependency>
    </dependencies>

Hope this will be helpful.