Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9440bce8 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

Propagate optional audio stream type to the creation of the audio resources

to enable the playback of TTS content on various stream types.
parent c4ca4206
Loading
Loading
Loading
Loading
+24 −18
Original line number Original line Diff line number Diff line
@@ -33,6 +33,8 @@
#define DEFAULT_TTS_FORMAT      AudioSystem::PCM_16_BIT
#define DEFAULT_TTS_FORMAT      AudioSystem::PCM_16_BIT
#define DEFAULT_TTS_NB_CHANNELS 1
#define DEFAULT_TTS_NB_CHANNELS 1
#define DEFAULT_TTS_BUFFERSIZE  1024
#define DEFAULT_TTS_BUFFERSIZE  1024
// TODO use the TTS stream type when available
#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC


#define USAGEMODE_PLAY_IMMEDIATELY 0
#define USAGEMODE_PLAY_IMMEDIATELY 0
#define USAGEMODE_WRITE_TO_FILE    1
#define USAGEMODE_WRITE_TO_FILE    1
@@ -46,10 +48,12 @@ struct fields_t {
    jmethodID   synthProxyMethodPost;
    jmethodID   synthProxyMethodPost;
};
};


// structure to hold the data that is used each time the TTS engine has synthesized more data
struct afterSynthData_t {
struct afterSynthData_t {
    jint jniStorage;
    jint jniStorage;
    int  usageMode;
    int  usageMode;
    FILE* outputFile;
    FILE* outputFile;
    AudioSystem::stream_type streamType;
};
};


// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
@@ -62,6 +66,7 @@ class SynthProxyJniStorage {
        jobject                   tts_ref;
        jobject                   tts_ref;
        TtsEngine*                mNativeSynthInterface;
        TtsEngine*                mNativeSynthInterface;
        AudioTrack*               mAudioOut;
        AudioTrack*               mAudioOut;
        AudioSystem::stream_type  mStreamType;
        uint32_t                  mSampleRate;
        uint32_t                  mSampleRate;
        AudioSystem::audio_format mAudFormat;
        AudioSystem::audio_format mAudFormat;
        int                       mNbChannels;
        int                       mNbChannels;
@@ -73,6 +78,7 @@ class SynthProxyJniStorage {
            tts_ref = NULL;
            tts_ref = NULL;
            mNativeSynthInterface = NULL;
            mNativeSynthInterface = NULL;
            mAudioOut = NULL;
            mAudioOut = NULL;
            mStreamType = DEFAULT_TTS_STREAM_TYPE;
            mSampleRate = DEFAULT_TTS_RATE;
            mSampleRate = DEFAULT_TTS_RATE;
            mAudFormat  = DEFAULT_TTS_FORMAT;
            mAudFormat  = DEFAULT_TTS_FORMAT;
            mNbChannels = DEFAULT_TTS_NB_CHANNELS;
            mNbChannels = DEFAULT_TTS_NB_CHANNELS;
@@ -97,34 +103,33 @@ class SynthProxyJniStorage {
            }
            }
        }
        }


        void createAudioOut(uint32_t rate, AudioSystem::audio_format format,
        void createAudioOut(AudioSystem::stream_type streamType, uint32_t rate,
                int channel) {
                AudioSystem::audio_format format, int channel) {
            mSampleRate = rate;
            mSampleRate = rate;
            mAudFormat  = format;
            mAudFormat  = format;
            mNbChannels = channel;
            mNbChannels = channel;


            // TODO use the TTS stream type
            mStreamType = streamType;
            int streamType = AudioSystem::MUSIC;


            // retrieve system properties to ensure successful creation of the
            // retrieve system properties to ensure successful creation of the
            // AudioTrack object for playback
            // AudioTrack object for playback
            int afSampleRate;
            int afSampleRate;
            if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
            if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
                afSampleRate = 44100;
                afSampleRate = 44100;
            }
            }
            int afFrameCount;
            int afFrameCount;
            if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
            if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
                afFrameCount = 2048;
                afFrameCount = 2048;
            }
            }
            uint32_t afLatency;
            uint32_t afLatency;
            if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
            if (AudioSystem::getOutputLatency(&afLatency, mStreamType) != NO_ERROR) {
                afLatency = 500;
                afLatency = 500;
            }
            }
            uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
            uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
            if (minBufCount < 2) minBufCount = 2;
            if (minBufCount < 2) minBufCount = 2;
            int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
            int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;


            mAudioOut = new AudioTrack(streamType, rate, format, channel,
            mAudioOut = new AudioTrack(mStreamType, rate, format, channel,
                    minFrameCount > 4096 ? minFrameCount : 4096,
                    minFrameCount > 4096 ? minFrameCount : 4096,
                    0, 0, 0, 0); // not using an AudioTrack callback
                    0, 0, 0, 0); // not using an AudioTrack callback


@@ -142,21 +147,21 @@ class SynthProxyJniStorage {




// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
void prepAudioTrack(SynthProxyJniStorage* pJniData,
void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type streamType,
        uint32_t rate, AudioSystem::audio_format format, int channel)
        uint32_t rate, AudioSystem::audio_format format, int channel) {
{
    // Don't bother creating a new audiotrack object if the current
    // Don't bother creating a new audiotrack object if the current
    // object is already set.
    // object is already initialized with the same audio parameters.
    if ( pJniData->mAudioOut &&
    if ( pJniData->mAudioOut &&
         (rate == pJniData->mSampleRate) &&
         (rate == pJniData->mSampleRate) &&
         (format == pJniData->mAudFormat) &&
         (format == pJniData->mAudFormat) &&
         (channel == pJniData->mNbChannels) ){
         (channel == pJniData->mNbChannels) &&
         (streamType == pJniData->mStreamType) ){
        return;
        return;
    }
    }
    if (pJniData->mAudioOut){
    if (pJniData->mAudioOut){
        pJniData->killAudio();
        pJniData->killAudio();
    }
    }
    pJniData->createAudioOut(rate, format, channel);
    pJniData->createAudioOut(streamType, rate, format, channel);
}
}




@@ -186,7 +191,7 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
        }
        }


        if (bufferSize > 0) {
        if (bufferSize > 0) {
            prepAudioTrack(pJniData, rate, format, channel);
            prepAudioTrack(pJniData, pForAfter->streamType, rate, format, channel);
            if (pJniData->mAudioOut) {
            if (pJniData->mAudioOut) {
                pJniData->mAudioOut->write(wav, bufferSize);
                pJniData->mAudioOut->write(wav, bufferSize);
                //LOGV("AudioTrack wrote: %d bytes", bufferSize);
                //LOGV("AudioTrack wrote: %d bytes", bufferSize);
@@ -241,7 +246,7 @@ android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
    SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
    SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();


    prepAudioTrack(pJniStorage,
    prepAudioTrack(pJniStorage,
            DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
            DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);


    const char *nativeSoLibNativeString =
    const char *nativeSoLibNativeString =
            env->GetStringUTFChars(nativeSoLib, 0);
            env->GetStringUTFChars(nativeSoLib, 0);
@@ -526,7 +531,7 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,


static int
static int
android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
        jstring textJavaString)
        jstring textJavaString, jint javaStreamType)
{
{
    int result = TTS_FAILURE;
    int result = TTS_FAILURE;


@@ -545,6 +550,7 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
    afterSynthData_t* pForAfter = new (afterSynthData_t);
    afterSynthData_t* pForAfter = new (afterSynthData_t);
    pForAfter->jniStorage = jniData;
    pForAfter->jniStorage = jniData;
    pForAfter->usageMode  = USAGEMODE_PLAY_IMMEDIATELY;
    pForAfter->usageMode  = USAGEMODE_PLAY_IMMEDIATELY;
    pForAfter->streamType = (AudioSystem::stream_type) javaStreamType;


    if (pSynthData->mNativeSynthInterface) {
    if (pSynthData->mNativeSynthInterface) {
        const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
        const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
@@ -672,7 +678,7 @@ static JNINativeMethod gMethods[] = {
        (void*)android_tts_SynthProxy_stop
        (void*)android_tts_SynthProxy_stop
    },
    },
    {   "native_speak",
    {   "native_speak",
        "(ILjava/lang/String;)I",
        "(ILjava/lang/String;I)I",
        (void*)android_tts_SynthProxy_speak
        (void*)android_tts_SynthProxy_speak
    },
    },
    {   "native_synthesizeToFile",
    {   "native_synthesizeToFile",
+10 −3
Original line number Original line Diff line number Diff line
@@ -15,6 +15,8 @@
 */
 */
package android.tts;
package android.tts;


import android.media.AudioManager;
import android.media.AudioSystem;
import android.util.Log;
import android.util.Log;
import java.lang.ref.WeakReference;
import java.lang.ref.WeakReference;


@@ -52,8 +54,13 @@ public class SynthProxy {
    /**
    /**
     * Synthesize speech and speak it directly using AudioTrack.
     * Synthesize speech and speak it directly using AudioTrack.
     */
     */
    public int speak(String text) {
    public int speak(String text, int streamType) {
        return native_speak(mJniData, text);
        if ((streamType > -1) && (streamType < AudioSystem.getNumStreamTypes())) {
            return native_speak(mJniData, text, streamType);
        } else {
            Log.e("SynthProxy", "Trying to speak with invalid stream type " + streamType);
            return native_speak(mJniData, text, AudioManager.STREAM_MUSIC);
        }
    }
    }


    /**
    /**
@@ -156,7 +163,7 @@ public class SynthProxy {


    private native final int native_stop(int jniData);
    private native final int native_stop(int jniData);


    private native final int native_speak(int jniData, String text);
    private native final int native_speak(int jniData, String text, int streamType);


    private native final int native_synthesizeToFile(int jniData, String text, String filename);
    private native final int native_synthesizeToFile(int jniData, String text, String filename);


+33 −5
Original line number Original line Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.net.Uri;
@@ -109,6 +110,8 @@ public class TtsService extends Service implements OnCompletionListener {


    private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
    private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
    private static final int MAX_FILENAME_LENGTH = 250;
    private static final int MAX_FILENAME_LENGTH = 250;
    // TODO use the TTS stream type when available
    private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;


    private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
    private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
    private static final String CATEGORY = "android.intent.category.TTS";
    private static final String CATEGORY = "android.intent.category.TTS";
@@ -450,6 +453,7 @@ public class TtsService extends Service implements OnCompletionListener {
                        synth.start();
                        synth.start();
                        return;
                        return;
                    }
                    }
                    int streamType = DEFAULT_STREAM_TYPE;
                    if (params != null){
                    if (params != null){
                        String language = "";
                        String language = "";
                        String country = "";
                        String country = "";
@@ -465,6 +469,12 @@ public class TtsService extends Service implements OnCompletionListener {
                                    country = params.get(i+1);
                                    country = params.get(i+1);
                                } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
                                } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_VARIANT)){
                                    variant = params.get(i+1);
                                    variant = params.get(i+1);
                                } else if (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_STREAM)) {
                                    try {
                                        streamType = Integer.parseInt(params.get(i + 1));
                                    } catch (NumberFormatException e) {
                                        streamType = DEFAULT_STREAM_TYPE;
                                    }
                                }
                                }
                            }
                            }
                        }
                        }
@@ -472,7 +482,7 @@ public class TtsService extends Service implements OnCompletionListener {
                            setLanguage("", language, country, variant);
                            setLanguage("", language, country, variant);
                        }
                        }
                    }
                    }
                    nativeSynth.speak(text);
                    nativeSynth.speak(text, streamType);
                } catch (InterruptedException e) {
                } catch (InterruptedException e) {
                    Log.e("TTS speakInternalOnly", "tryLock interrupted");
                    Log.e("TTS speakInternalOnly", "tryLock interrupted");
                    e.printStackTrace();
                    e.printStackTrace();
@@ -651,8 +661,7 @@ public class TtsService extends Service implements OnCompletionListener {
                    // Utterance is part of the app calling the library
                    // Utterance is part of the app calling the library
                    Context ctx;
                    Context ctx;
                    try {
                    try {
                        ctx = this.createPackageContext(sr.mSourcePackageName,
                        ctx = this.createPackageContext(sr.mSourcePackageName, 0);
                                0);
                    } catch (NameNotFoundException e) {
                    } catch (NameNotFoundException e) {
                        e.printStackTrace();
                        e.printStackTrace();
                        mSpeechQueue.remove(0); // Remove it from the queue and
                        mSpeechQueue.remove(0); // Remove it from the queue and
@@ -675,6 +684,7 @@ public class TtsService extends Service implements OnCompletionListener {
                }
                }
                mPlayer.setOnCompletionListener(this);
                mPlayer.setOnCompletionListener(this);
                try {
                try {
                    mPlayer.setAudioStreamType(getStreamTypeFromParams(currentSpeechItem.mParams));
                    mPlayer.start();
                    mPlayer.start();
                } catch (IllegalStateException e) {
                } catch (IllegalStateException e) {
                    mSpeechQueue.clear();
                    mSpeechQueue.clear();
@@ -695,6 +705,24 @@ public class TtsService extends Service implements OnCompletionListener {
        }
        }
    }
    }


    private int getStreamTypeFromParams(ArrayList<String> paramList) {
        int streamType = DEFAULT_STREAM_TYPE;
        if (paramList == null) {
            return streamType;
        }
        for (int i = 0; i < paramList.size() - 1; i = i + 2) {
            String param = paramList.get(i);
            if ((param != null) && (param.equals(TextToSpeech.Engine.TTS_KEY_PARAM_STREAM))) {
                try {
                    streamType = Integer.parseInt(paramList.get(i + 1));
                } catch (NumberFormatException e) {
                    streamType = DEFAULT_STREAM_TYPE;
                }
            }
        }
        return streamType;
    }

    private void cleanUpPlayer() {
    private void cleanUpPlayer() {
        if (mPlayer != null) {
        if (mPlayer != null) {
            mPlayer.release();
            mPlayer.release();