diff options
Diffstat (limited to 'packages/TtsService/jni/android_tts_SynthProxy.cpp')
-rw-r--r-- | packages/TtsService/jni/android_tts_SynthProxy.cpp | 96 |
1 files changed, 93 insertions, 3 deletions
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp index a4090cf..1793587 100644 --- a/packages/TtsService/jni/android_tts_SynthProxy.cpp +++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp @@ -26,16 +26,25 @@ #include <android_runtime/AndroidRuntime.h> #include <tts/TtsEngine.h> #include <media/AudioTrack.h> +#include <math.h> #include <dlfcn.h> #define DEFAULT_TTS_RATE 16000 #define DEFAULT_TTS_FORMAT AudioSystem::PCM_16_BIT #define DEFAULT_TTS_NB_CHANNELS 1 -#define DEFAULT_TTS_BUFFERSIZE 1024 +#define DEFAULT_TTS_BUFFERSIZE 2048 // TODO use the TTS stream type when available #define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC +// EQ + BOOST parameters +#define FILTER_LOWSHELF_ATTENUATION -18.0f // in dB +#define FILTER_TRANSITION_FREQ 1100.0f // in Hz +#define FILTER_SHELF_SLOPE 1.0f // Q +#define FILTER_GAIN 6.0f // 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 @@ -57,6 +66,79 @@ struct afterSynthData_t { }; // ---------------------------------------------------------------------------- +// EQ data +double amp; +double w; +double sinw; +double cosw; +double beta; +double a0, a1, a2, b0, b1, b2; +double m_fa, m_fb, m_fc, m_fd, m_fe; +double x0; // x[n] +double x1; // x[n-1] +double x2; // x[n-2] +double out0;// y[n] +double out1;// y[n-1] +double out2;// y[n-2] + +void initializeEQ() { + + amp = float(pow(10.0, FILTER_LOWSHELF_ATTENUATION / 40.0)); + w = 2.0 * M_PI * (FILTER_TRANSITION_FREQ / DEFAULT_TTS_RATE); + sinw = float(sin(w)); + cosw = float(cos(w)); + beta = float(sqrt(amp)/FILTER_SHELF_SLOPE); + + // initialize low-shelf parameters + b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw)); + b1 = 2.0F * amp * ((amp-1.0F) - ((amp+1.0F)*cosw)); + b2 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) - (beta*sinw)); + a0 = (amp+1.0F) + ((amp-1.0F)*cosw) + (beta*sinw); + 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_fd = a1/a0; + m_fe = a2/a0; +} + +void initializeFilter() { + x0 = 0.0f; + x1 = 0.0f; + x2 = 0.0f; + out0 = 0.0f; + out1 = 0.0f; + out2 = 0.0f; +} + +void applyFilter(int16_t* buffer, size_t sampleCount) { + + for (size_t i=0 ; i<sampleCount ; i++) { + + x0 = (double) buffer[i]; + + out0 = (m_fa*x0) + (m_fb*x1) + (m_fc*x2) + (m_fd*out1) + (m_fe*out2); + + x2 = x1; + x1 = x0; + + out2 = out1; + out1 = out0; + + if (out0 > 32767.0f) { + buffer[i] = 32767; + } else if (out0 < -32768.0f) { + buffer[i] = -32768; + } else { + buffer[i] = (int16_t) out0; + } + } +} + + +// ---------------------------------------------------------------------------- static fields_t javaTTSFields; // TODO move to synth member once we have multiple simultaneous engines running @@ -198,12 +280,13 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate, if (wav == NULL) { delete pForAfter; - LOGI("Null: speech has completed"); + LOGV("Null: speech has completed"); } if (bufferSize > 0) { prepAudioTrack(pJniData, pForAfter->streamType, rate, (AudioSystem::audio_format)format, channel); if (pJniData->mAudioOut) { + applyFilter((int16_t*)wav, bufferSize/2); pJniData->mAudioOut->write(wav, bufferSize); memset(wav, 0, bufferSize); //LOGV("AudioTrack wrote: %d bytes", bufferSize); @@ -212,13 +295,14 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate, } } } else if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) { - LOGV("Save to file"); + //LOGV("Save to file"); if (wav == NULL) { delete pForAfter; LOGV("Null: speech has completed"); return TTS_CALLBACK_HALT; } if (bufferSize > 0){ + applyFilter((int16_t*)wav, bufferSize/2); fwrite(wav, 1, bufferSize, pForAfter->outputFile); memset(wav, 0, bufferSize); } @@ -289,6 +373,8 @@ android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz, env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, (int)pJniStorage); + initializeEQ(); + env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString); } @@ -479,6 +565,8 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData, return result; } + initializeFilter(); + Mutex::Autolock l(engineMutex); // Retrieve audio parameters before writing the file header @@ -583,6 +671,8 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData, return result; } + initializeFilter(); + Mutex::Autolock l(engineMutex); SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; |