am 0950c5de: Merge change 21226 into donut

Merge commit '0950c5de864d1ad83ed96efc5c2d1569b4d36188' into eclair

* commit '0950c5de864d1ad83ed96efc5c2d1569b4d36188':
  Fix bug 2046705 where the output of the speech synthesizer is too low.
This commit is contained in:
Jean-Michel Trivi
2009-08-18 08:44:51 -07:00
committed by Android Git Automerger

View File

@@ -26,16 +26,25 @@
#include <android_runtime/AndroidRuntime.h> #include <android_runtime/AndroidRuntime.h>
#include <tts/TtsEngine.h> #include <tts/TtsEngine.h>
#include <media/AudioTrack.h> #include <media/AudioTrack.h>
#include <math.h>
#include <dlfcn.h> #include <dlfcn.h>
#define DEFAULT_TTS_RATE 16000 #define DEFAULT_TTS_RATE 16000
#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 2048
// TODO use the TTS stream type when available // TODO use the TTS stream type when available
#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC #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_PLAY_IMMEDIATELY 0
#define USAGEMODE_WRITE_TO_FILE 1 #define USAGEMODE_WRITE_TO_FILE 1
@@ -56,6 +65,79 @@ struct afterSynthData_t {
AudioSystem::stream_type streamType; AudioSystem::stream_type streamType;
}; };
// ----------------------------------------------------------------------------
// 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; static fields_t javaTTSFields;
@@ -198,12 +280,13 @@ static tts_callback_status ttsSynthDoneCB(void *& userdata, uint32_t rate,
if (wav == NULL) { if (wav == NULL) {
delete pForAfter; delete pForAfter;
LOGI("Null: speech has completed"); LOGV("Null: speech has completed");
} }
if (bufferSize > 0) { if (bufferSize > 0) {
prepAudioTrack(pJniData, pForAfter->streamType, rate, (AudioSystem::audio_format)format, channel); prepAudioTrack(pJniData, pForAfter->streamType, rate, (AudioSystem::audio_format)format, channel);
if (pJniData->mAudioOut) { if (pJniData->mAudioOut) {
applyFilter((int16_t*)wav, bufferSize/2);
pJniData->mAudioOut->write(wav, bufferSize); pJniData->mAudioOut->write(wav, bufferSize);
memset(wav, 0, bufferSize); memset(wav, 0, bufferSize);
//LOGV("AudioTrack wrote: %d bytes", 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) { } else if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
LOGV("Save to file"); //LOGV("Save to file");
if (wav == NULL) { if (wav == NULL) {
delete pForAfter; delete pForAfter;
LOGV("Null: speech has completed"); LOGV("Null: speech has completed");
return TTS_CALLBACK_HALT; return TTS_CALLBACK_HALT;
} }
if (bufferSize > 0){ if (bufferSize > 0){
applyFilter((int16_t*)wav, bufferSize/2);
fwrite(wav, 1, bufferSize, pForAfter->outputFile); fwrite(wav, 1, bufferSize, pForAfter->outputFile);
memset(wav, 0, bufferSize); memset(wav, 0, bufferSize);
} }
@@ -289,6 +373,8 @@ android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData,
(int)pJniStorage); (int)pJniStorage);
initializeEQ();
env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString); env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
} }
@@ -479,6 +565,8 @@ android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
return result; return result;
} }
initializeFilter();
Mutex::Autolock l(engineMutex); Mutex::Autolock l(engineMutex);
// Retrieve audio parameters before writing the file header // Retrieve audio parameters before writing the file header
@@ -583,6 +671,8 @@ android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
return result; return result;
} }
initializeFilter();
Mutex::Autolock l(engineMutex); Mutex::Autolock l(engineMutex);
SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;