TextToSpeech, playEarcon, and .wav files

In one of my applications, I have an activity in which speech is synthesized by alphanumeric reference lines, a letter / number by letter / number, for example, "ABC123" sounds like "Ay, bee, sea, one two three." Since this is a limited set of sounds, I thought it would be nice to let the TTS engine work without an Internet connection by playing pre-recorded .wav numbers and letters using the playEarcon method.

I placed all 36 wav files in the res / raw folder and matched resource identifiers with letters when initializing the TTS engine. This works well, however .apk is now much larger as wav files are stored uncompressed in apk. I would like to reduce apk size.

The answer to another question says that wav files are excluded from compression. (I don’t understand why, since they usually fade to about 40% of the original). Checking the internal elements of apk, this seems to be true.

Since the resource file extension is not mentioned in the code, I tried to rename wavs, differently .waw, .abc, .spc. All of them are compressed, but, unfortunately, the playEarcon method does not cause sound when called if the extension is not equal to .wav.

In short, I would like to force the TTS engine to play files without the wav extension, or to convince it to compress WAV files.

All offers will be gratefully received. For what it's worth, I'm posting the smallest example demo code below. My work files are called gb_a.wav, gb_b.wav, etc. If the extension is changed, they stop sounding.

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 = "0123456789"; 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 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); } } 

. .

+6
source share
2 answers
  • Why wav files did not try to compress: according to wikipedia, a wav file is a container for data. Most often it is used for uncompressed PCM sound, but can also be used to store data compressed using various codecs (you can find possible values ​​for wFormatTags (type for saved data), for example here ).
  • You can save your resource on the local file system and use addEarcon(String earcon, String filename) instead of addEarcon(String earcon, String packagename, int resourceId) .
  • You can use aapt with the -0 wav cmd line switch to make apk with wav files excluded from compressed types.
+1
source

You should try to convert the wav file to an ogg file, then you will get the maximum compression speed for the sound file.

0
source

Source: https://habr.com/ru/post/908518/


All Articles