Apply EQ on the output of the speech synthesis only when using the Pico

TTS engine.
This commit is contained in:
Jean-Michel Trivi
2010-01-11 14:04:50 -08:00
parent 9b0b155e60
commit 0320f8bdfb
2 changed files with 76 additions and 22 deletions

View File

@@ -41,8 +41,6 @@
#define FILTER_TRANSITION_FREQ 1100.0f // in Hz
#define FILTER_SHELF_SLOPE 1.0f // Q
#define FILTER_GAIN 5.5f // linear gain
// such a huge gain is justified by how much energy in the low frequencies is "wasted" at the output
// of the synthesis. The low shelving filter removes it, leaving room for amplification.
#define USAGEMODE_PLAY_IMMEDIATELY 0
#define USAGEMODE_WRITE_TO_FILE 1
@@ -80,13 +78,19 @@ double out0;// y[n]
double out1;// y[n-1]
double out2;// y[n-2]
static float fFilterLowshelfAttenuation = FILTER_LOWSHELF_ATTENUATION;
static float fFilterTransitionFreq = FILTER_TRANSITION_FREQ;
static float fFilterShelfSlope = FILTER_SHELF_SLOPE;
static float fFilterGain = FILTER_GAIN;
static bool bUseFilter = false;
void initializeEQ() {
amp = float(pow(10.0, FILTER_LOWSHELF_ATTENUATION / 40.0));
w = 2.0 * M_PI * (FILTER_TRANSITION_FREQ / DEFAULT_TTS_RATE);
amp = float(pow(10.0, fFilterLowshelfAttenuation / 40.0));
w = 2.0 * M_PI * (fFilterTransitionFreq / DEFAULT_TTS_RATE);
sinw = float(sin(w));
cosw = float(cos(w));
beta = float(sqrt(amp)/FILTER_SHELF_SLOPE);
beta = float(sqrt(amp)/fFilterShelfSlope);
// initialize low-shelf parameters
b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw));
@@ -96,9 +100,9 @@ void initializeEQ() {
a1 = 2.0F * ((amp-1.0F) + ((amp+1.0F)*cosw));
a2 = -((amp+1.0F) + ((amp-1.0F)*cosw) - (beta*sinw));
m_fa = FILTER_GAIN * b0/a0;
m_fb = FILTER_GAIN * b1/a0;
m_fc = FILTER_GAIN * b2/a0;
m_fa = fFilterGain * b0/a0;
m_fb = fFilterGain * b1/a0;
m_fc = fFilterGain * b2/a0;
m_fd = a1/a0;
m_fe = a2/a0;
}
@@ -284,7 +288,9 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
if (bufferSize > 0) {
prepAudioTrack(pJniData, pForAfter->streamType, rate, (AudioSystem::audio_format)format, channel);
if (pJniData->mAudioOut) {
applyFilter((int16_t*)wav, bufferSize/2);
if (bUseFilter) {
applyFilter((int16_t*)wav, bufferSize/2);
}
pJniData->mAudioOut->write(wav, bufferSize);
memset(wav, 0, bufferSize);
//LOGV("AudioTrack wrote: %d bytes", bufferSize);
@@ -300,7 +306,9 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
return TTS_CALLBACK_HALT;
}
if (bufferSize > 0){
applyFilter((int16_t*)wav, bufferSize/2);
if (bUseFilter) {
applyFilter((int16_t*)wav, bufferSize/2);
}
fwrite(wav, 1, bufferSize, pForAfter->outputFile);
memset(wav, 0, bufferSize);
}
@@ -334,23 +342,50 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
// ----------------------------------------------------------------------------
static void
static int
android_tts_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter,
jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope)
{
int result = TTS_SUCCESS;
bUseFilter = applyFilter;
if (applyFilter) {
fFilterLowshelfAttenuation = attenuationInDb;
fFilterTransitionFreq = freqInHz;
fFilterShelfSlope = slope;
fFilterGain = filterGain;
if (fFilterShelfSlope != 0.0f) {
initializeEQ();
} else {
LOGE("Invalid slope, can't be null");
result = TTS_FAILURE;
}
}
return result;
}
// ----------------------------------------------------------------------------
static int
android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
jobject weak_this, jstring nativeSoLib)
{
int result = TTS_FAILURE;
bUseFilter = false;
SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
prepAudioTrack(pJniStorage,
DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
const char *nativeSoLibNativeString =
env->GetStringUTFChars(nativeSoLib, 0);
const char *nativeSoLibNativeString = env->GetStringUTFChars(nativeSoLib, 0);
void *engine_lib_handle = dlopen(nativeSoLibNativeString,
RTLD_NOW | RTLD_LOCAL);
if (engine_lib_handle == NULL) {
LOGE("android_tts_SynthProxy_native_setup(): engine_lib_handle == NULL");
// TODO report error so the TTS can't be used
} else {
TtsEngine *(*get_TtsEngine)() =
reinterpret_cast<TtsEngine* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
@@ -362,18 +397,19 @@ android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
Mutex::Autolock l(engineMutex);
pJniStorage->mNativeSynthInterface->init(ttsSynthDoneCB);
}
result = TTS_SUCCESS;
}
// we use a weak reference so the SynthProxy object can be garbage collected.
pJniStorage->tts_ref = env->NewGlobalRef(weak_this);
// save the JNI resources so we can use them (and free them) later
env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData,
(int)pJniStorage);
initializeEQ();
env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, (int)pJniStorage);
env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
return result;
}
@@ -842,9 +878,13 @@ static JNINativeMethod gMethods[] = {
(void*)android_tts_SynthProxy_shutdown
},
{ "native_setup",
"(Ljava/lang/Object;Ljava/lang/String;)V",
"(Ljava/lang/Object;Ljava/lang/String;)I",
(void*)android_tts_SynthProxy_native_setup
},
{ "native_setLowShelf",
"(ZFFFF)I",
(void*)android_tts_SynthProxy_setLowShelf
},
{ "native_finalize",
"(I)V",
(void*)android_tts_SynthProxy_native_finalize

View File

@@ -32,6 +32,15 @@ import java.lang.ref.WeakReference;
@SuppressWarnings("unused")
public class SynthProxy {
// Default parameters of a filter to be applied when using the Pico engine.
// Such a huge filter gain is justified by how much energy in the low frequencies is "wasted" at
// the output of the synthesis. The low shelving filter removes it, leaving room for
// amplification.
private final static float PICO_FILTER_GAIN = 5.5f; // linear gain
private final static float PICO_FILTER_LOWSHELF_ATTENUATION = -18.0f; // in dB
private final static float PICO_FILTER_TRANSITION_FREQ = 1100.0f; // in Hz
private final static float PICO_FILTER_SHELF_SLOPE = 1.0f; // Q
//
// External API
//
@@ -40,8 +49,11 @@ public class SynthProxy {
* Constructor; pass the location of the native TTS .so to use.
*/
public SynthProxy(String nativeSoLib) {
Log.v(TtsService.SERVICE_TAG, "TTS is loading " + nativeSoLib);
boolean applyFilter = nativeSoLib.toLowerCase().contains("pico");
Log.v(TtsService.SERVICE_TAG, "about to load "+ nativeSoLib + ", applyFilter="+applyFilter);
native_setup(new WeakReference<SynthProxy>(this), nativeSoLib);
native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
}
/**
@@ -161,8 +173,10 @@ public class SynthProxy {
*/
private int mJniData = 0;
private native final void native_setup(Object weak_this,
String nativeSoLib);
private native final int native_setup(Object weak_this, String nativeSoLib);
private native final int native_setLowShelf(boolean applyFilter, float filterGain,
float attenuationInDb, float freqInHz, float slope);
private native final void native_finalize(int jniData);