En una de mis aplicaciones tengo una actividad en la que el habla sintetiza cadenas de referencia alfanuméricas, letra/número por letra/número, por ejemplo, "ABC123" suena como "Ay, bee, sea, one two three" . Como se trata de un conjunto limitado de sonidos, pensé que sería bueno habilitar el motor TTS para trabajar sin una conexión a Internet al reproducir archivos .wav pregrabados de los números y letras utilizando el método playEarcon.TextToSpeech, playEarcon y archivos .wav
He colocado todos los 36 archivos wav en la carpeta res/raw y asigné los ID de recursos a las letras al inicializar el motor TTS. Esto funciona bien, sin embargo el .apk ahora es mucho más grande ya que los archivos wav se almacenan sin comprimir en la apk. Me gustaría reducir el tamaño del apk.
En the answer to another question indica que los archivos wav están excluidos de la compresión. (No veo por qué, ya que normalmente se comprimen hasta aproximadamente el 40% del original). Al inspeccionar las partes internas de la aplicación, esto parece ser cierto.
Como la extensión de los archivos de recursos no se menciona en el código, traté de cambiar el nombre de los wavs, a varios .waw, .abc, .spc. Todos estos se comprimen, pero desafortunadamente el método playEarcon no produce ningún sonido cuando se invoca a menos que la extensión sea .wav.
En resumen, me gustaría obligar al motor TTS a reproducir archivos sin una extensión wav, o persuadirlo para que comprima los archivos .wav.
Todas las sugerencias serán recibidas con gratitud. Por lo que vale, estoy publicando el ejemplo de código demostrable más pequeño a continuación. Mis archivos de trabajo se llaman gb_a.wav, gb_b.wav, etc. Si se modifica la extensión, dejan de sonar.
public class WavSpeakerActivity extends Activity implements
RadioGroup.OnCheckedChangeListener, TextToSpeech.OnInitListener {
static final int mGBLetterResIds[] = { R.raw.gb_a, R.raw.gb_b, R.raw.gb_c,
R.raw.gb_d, R.raw.gb_e, R.raw.gb_f, R.raw.gb_g, R.raw.gb_h,
R.raw.gb_i, R.raw.gb_j, R.raw.gb_k, R.raw.gb_l, R.raw.gb_m,
R.raw.gb_n, R.raw.gb_o, R.raw.gb_p, R.raw.gb_q, R.raw.gb_r,
R.raw.gb_s, R.raw.gb_t, R.raw.gb_u, R.raw.gb_v, R.raw.gb_w,
R.raw.gb_x, R.raw.gb_y, R.raw.gb_z };
static final int mGBNumberResIds[] = { R.raw.gb_zero, R.raw.gb_one,
R.raw.gb_two, R.raw.gb_three, R.raw.gb_four, R.raw.gb_five,
R.raw.gb_six, R.raw.gb_seven, R.raw.gb_eight, R.raw.gb_nine };
static final String mGbStr = "GB";
static final String mAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static final String mNumbers = "";
private String mPpackageName = null;
private String mTextToSpeak = null;
private RadioGroup mRadioGroup = null;// two buttons one sets letters, the other numbers
private TextToSpeech mTts = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mTts = new TextToSpeech(this, this);
mRadioGroup = (RadioGroup) findViewById(R.id.radioGroup1);
mRadioGroup.setOnCheckedChangeListener(this);
}
@Override
protected void onResume() {
super.onResume();
RadioGroup rg = (RadioGroup) findViewById(R.id.radioGroup1);
switchText(rg);
mPpackageName = getPackageName();
}
@Override
public void onDestroy() {
// Don't forget to shutdown speech engine
if (mTts != null) {
mTts.stop();
mTts.shutdown();
}
super.onDestroy();
}
private void switchText(RadioGroup rg) {
// select letters or digits as the String to speak
int checkedButton = rg.getCheckedRadioButtonId();
switch (checkedButton) {
case R.id.alphabet:
mTextToSpeak = mAlphabet;
break;
case R.id.numbers:
mTextToSpeak = mNumbers;
break;
}
}
public void myClickHandler(View target) {
// Just the one button has been clicked - the 'Speak' one
String earconKey;
String lang = Locale.UK.getCountry(); // will be "GB", just have UK in this small example
mTts.setLanguage(Locale.UK); // skip error checking for brevity's sake
String text = mTextToSpeak.replaceAll("\\s", "");// remove spaces (if any)
char c;
for (int i = 0; i < text.length(); i++) {
c = text.charAt(i);
if (Character.isLetter(c) || Character.isDigit(c)) {
earconKey = lang + Character.toString(c); // GBA, GBB..GBZ, GB0.. GB9
mTts.playEarcon(earconKey, TextToSpeech.QUEUE_ADD, null);
}
}
}
@Override
public void onInit(int status) {
// doesn't seem we need to check status or setLanguage if we're just playing earcons
mapEarCons(); // map letter/digit sounds to resource ids
}
private void mapEarCons() {
String key;
for (char c = 'A'; c <= 'Z' ; c++){
key = mGbStr + Character.toString(c); // GBA, GBB .. GBZ
mTts.addEarcon(key, mPpackageName, mGBLetterResIds[c - 'A']);// add it
}
for (int i = 0 ; i <= 9; i++){
key = mGbStr + Integer.toString(i); // GB0, GB1 .. GB9
mTts.addEarcon(key, mPpackageName, mGBNumberResIds[i]);
}
}
@Override
public void onCheckedChanged(RadioGroup rg, int arg1) { switchText(rg); }
}
. .
Re your points (1) - es el proceso de compilación de Android que los excluye de la compresión (ver el enlace que cité), quería encontrar una manera de anular esto. (2) Quiero empaquetar los wavs en mi apk (3) ¿Por qué querría excluirlos de la compresión? Ya están excluidos, ese es el punto de esta pregunta: ¡quiero comprimirlos! – NickT
sobre (3), lo siento, mi error. Bueno, creo que puedes descargar las fuentes de herramienta aapt, excluir wav de tipos no comprimidos por defecto, reconstruirlo y hacer tu apt con esta nueva herramienta. Pero no probé esto, por lo que es posible que AssetManager se niegue a cargar dicho recurso. –