Android 12 Upgrade - TextToSpeech setPitch() and setSpeechRate() not working - android

My Java app uses TextToSpeech, with setPitch() and setSpeechRate(). Since the recent upgrade to Android 12 API 32, these set methods no longer work. This is on some Samsung devices, and Google Pixel 5a. They worked before the upgrade, and have no effect now (although still return a "0" result).
I'm using the deprecated speak API:
int speak (String text, int queueMode, HashMap<String, String> params)
It looks like setPitch and setSpeechRate have been broken by the upgrade. On one device I tried it immediately before and immediately after upgrading, with no change to the app.
Perhaps the new API still works:
public int speak (CharSequence text, int queueMode, Bundle params, String utteranceId)
I haven't tried it yet, but thought I'd flag this and check for others' experiences straight away. Does anyone else have this problem, or any suggestions?
EDITS: Using the newer API does the same.
Using an emulator on API 32 it all works OK. Using a Galaxy S21 or Galaxy S22 on API 32 we get the problem. Both are using the com.google.android.tts TTS engine.
Curiously, it ignores setSpeechRate() unless it's over 2.0.
I've created an empty app with just TTS. SpeechRates from 0.2 to 2.0 speak as if they're 1.0. A speechRate of 2.1 speaks about double speed, as it should.
My code is below for reference:
void initTTS() {
tts = new TextToSpeech (this, status -> {
Log.d("MyApp", "TTS Init complete. status:" + status);
// All of these speak at the same rate, as if they are 1.0
speak(0.2f);
speak(1.0f);
speak(2.0f);
// This speaks much faster, sounds like 2.1
speak(2.1f);
});
}
void speak(Float speechRate) {
tts.setSpeechRate(speechRate);
final Bundle params = new Bundle();
params.putFloat(TextToSpeech.Engine.KEY_PARAM_VOLUME,1);
tts.speak("This is my voice", TextToSpeech.QUEUE_ADD, params, "1");
}
If you'd like to try this and report the result, please do. Don't forget to mention which API level you are on, and which device please.

Our applications had the same problem. Speech rate is now always 1 or more than 2. And on different phones Samsung, Xiaomi. And on different versions of OS 10.12. It looks like a problem with updating the voice package.

Related

Android: Possible maximum number of audiosessions or effect engines for audio? (EQ, REV...)

for awhile now, I am working on a media playing app. In this app, I also programmed a little 5 Band EQ using mainly this code:
try
{
AppPreferencesClass ap = new AppPreferencesClass(ctx);
if (Activity_Player.eq != null)
{
Activity_Player.eq.Dispose();
}
Activity_Player.eq = new Android.Media.Audiofx.Equalizer(0, Activity_Player.mediaPlayerSessionId);
Activity_Player.eq.SetEnabled(true);
await Task.Run(() =>
{
if (Activity_Player.EqActive)
{
if (ap.getAwesomeSound())
{
Activity_Player.eq.SetBandLevel(0, Convert.ToInt16(Activity_Equalizer.awesomesound0));
Activity_Player.eq.SetBandLevel(1, Convert.ToInt16(Activity_Equalizer.awesomesound1));
Activity_Player.eq.SetBandLevel(2, Convert.ToInt16(Activity_Equalizer.awesomesound2));
Activity_Player.eq.SetBandLevel(3, Convert.ToInt16(Activity_Equalizer.awesomesound3));
Activity_Player.eq.SetBandLevel(4, Convert.ToInt16(Activity_Equalizer.awesomesound4));
}
else
{
Activity_Player.eq.SetBandLevel(0, Convert.ToInt16(ap.getEQ0()));
Activity_Player.eq.SetBandLevel(1, Convert.ToInt16(ap.getEQ1()));
Activity_Player.eq.SetBandLevel(2, Convert.ToInt16(ap.getEQ2()));
Activity_Player.eq.SetBandLevel(3, Convert.ToInt16(ap.getEQ3()));
Activity_Player.eq.SetBandLevel(4, Convert.ToInt16(ap.getEQ4()));
}
}
});
}
catch
{
}
For many days, this worked just fine but out of NO WHERE, the catch block sometimes gets activated. But only occasionally.On other times, try works fine but there are just no more changes to the audio being played. This is odd enough, since I never changed anything on this code after it starting working.
I then tried another phone (Samsung S4) on my code and the eq worked just perfectly.
So this got me googleing and I think I might have heard that there can only be as many audiosession IDs after you just would run out. I tested and the audio session ID used here is somewhere at 74,000.
So this could be an issue I thought but this would easialy be tested because I already had this very app running in the google play store just an older version of it. I am 100 percent positive, that in this version the EQ worked on my phone. Otherwise I would have not uploaded that version.
Anyway, I downloaded my old app from the play store and here we go:
It doesnt work anymore. The EQ in the old version also has simply NO effect on the audio. While ofcourse on my other phones this old version works perfectly.
Before I am going to reset my own personal phone I wanted to ask you guys if this could be infact the case.
Another thing is, that I am using many static variables in order to get the EQ to work right. Actually, the variable EQ itself is static. Do maybe static variables sort of leave a "trace" behind and maybe I have set the eq up just "too" many times? Although I am disposing of the object before intialising it again (see in my code).
Summing up:
1.) Can there maybe be a maxmium number of EQ or AudioSessionIDs and I have passed those?
2.) Can creating static variables over and over again in my code cause a memory leak so big, even deinstalling the app doesnt do anything?
Thank you!
This is the error log:
11-20 12:16:43.736 E/AudioEffect(16990): set(): AudioFlinger could not create effect, status: -38
11-20 12:16:43.736 E/AudioEffects-JNI(16990): Error setting AudioEffect
11-20 12:16:43.737 E/AudioEffect-JAVA(16990): Error code -19 when initializing AudioEffect.
Thread started: #311-20 12:16:43.745 V/MediaPlayerNative(16990): unrecognized message: (6, 0, 0)
After 2 days of googeling and trying evetything out, here is the issue:
NOT CALLING RELEASE() will have you eventually have to REBOOT your phone. It wont allow too many instances of an EQ to be set.
Solution:
if (eq != null)
{
eq.Release();
}

Text to speech pronouncing numbers like "4th", "8ths", or "2nd"

A while back I wrote some code that would convert a Double into a String, where the string was formatted as a readable fraction.
For an example
4.75 => "4 and 3 4ths"
1.5 => "1 and 1 half"
1.33 => "1 and 1 3rd"
The majority of numbers are pronounced as intended with a few notable exceptions. Instead of the text "4ths" being pronounced as "fourths" it is pronounced "four tee ache ess". Here is an example demonstrating this.
//this works
tts.speak("1 and 3 fourths", TextToSpeech.QUEUE_FLUSH, null);
//this works
tts.speak("1 and 1 3rd", TextToSpeech.QUEUE_FLUSH, null);
//this works
tts.speak("1 and 1 4th", TextToSpeech.QUEUE_FLUSH, null);
//this does not work
tts.speak("1 and 3 4ths", TextToSpeech.QUEUE_FLUSH, null);
//this does not work
tts.speak("1 and 3 4thes", TextToSpeech.QUEUE_FLUSH, null);
//this does not work
tts.speak("1 and 3 4th-s", TextToSpeech.QUEUE_FLUSH, null);
The strangest this is that this worked fine about a year back when I first wrote the code, the "ths" postfix was pronounced as one might expect. Perhaps I am mistaken on that point...
Regardless, the issue seems to be that numbers followed by 2 letters are read like a complete word, while numbers followed by 3 or more are read like a series of digits instead. I could add to the complexity of the algorithm by substituting all the numbers for their word counterparts however the longer I work at this the more I begin to think that I am reinventing the wheel. The API did not seem to denote a way of specifying pronunciation for the speak() method. Am I missing something?
This sort of behavior is going to vary between TextToSpeech Engines -- the Google TTS engine, for example, will behave differently than, say, the SVOX PICO (emulator < API 24) engine... so it's not your fault how each engine behaves slightly differently... and if there are any pronunciation controls, then the engine is responsible for supplying them directly to the end user via settings.
You're probably just testing on a different engine than you were before... or even an update to the same engine.
You could just test some major engines like Samsung, Google, and PICO and try to find a common denominator of behavior. I suspect that you're right: spelling out the words is the best option in this case.
You can specify what engine you want to use as the last argument (String) of the TextToSpeech constructor, and you can see what engines are installed on any particular device by going to (home\settings\language&locale\TTS) or in code like this:
private ArrayList<String> whatEnginesAreInstalled(Context context) {
final Intent ttsIntent = new Intent();
ttsIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
final PackageManager pm = context.getPackageManager();
final List<ResolveInfo> list = pm.queryIntentActivities(ttsIntent, PackageManager.GET_META_DATA);
ArrayList<String> installedEngineNames = new ArrayList<>();
for (ResolveInfo r : list) {
String engineName = r.activityInfo.applicationInfo.packageName;
installedEngineNames.add(engineName);
// just logging the version number out of interest
String version = "null";
try {
version = pm.getPackageInfo(engineName,
PackageManager.GET_META_DATA).versionName;
} catch (Exception e) {
Log.i("XXX", "try catch error");
}
Log.i("XXX", "we found an engine: " + engineName);
Log.i("XXX", "version: " + version);
}
return installedEngineNames;
}
As Boober Bunz explained, these features vary from one engine to another. It might get changed with newer versions of engine as well. I would suggest the best option will be to convert everything to words, like Fourths to make it consistent across engines. For a quick fix you can try 4th's as it seems to be more valid word than others you mentioned not working.

How to detect programmatically if "Android App" is running in chrome book or in Android phone

Since Google has announced that chromebook also support "Android Application" so I also wanted to support my app on chromebook although it is running fine with few exception which I need to fix.
I want to write code in such a way that that is will execute only for chromebook and will not execute for android phones and tablet.
I have check with Chromebook documentation in android developer site, I didn't get any such API which tell that your app is running in chrome book environment.
Suggestion from ARC Beta documentation did not work:
If you need to check if your app is running on Chrome OS, look for chromium as the android.os.Build.BRAND and android.os.Build.MANUFACTURER.
Both return google on an ASUS Chromebook.
Finally I figure out a way to know if app in running in ARC:
context.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
Jan 15, 2023 Note-- Jump to the bottom of this answer to read how Google has changed their own method for checking YET AGAIN.
(Or keep reading for the history of the ARC check.)
Another method Google uses in their own code (updated several times now from link) is to check if Build.DEVICE ends with "_cheets". I don't know if ending device names like this is some kind of long-term strategy or a fast workaround, but it's also worth a look in addition to dex's proposed solution.
FWIW, since ARCWelder's method is deprecated and there's no official documentation on this (yet), I've also started a discussion in the XDA forums here for people to discuss what works/doesn't work on various devices.
Update 5/18: Looks like the code above was moved and updated, so Google's new ARC check as of May 2018 is here, particularly in this bit:
... } else if (Build.DEVICE != null && Build.DEVICE.matches(ARC_DEVICE_PATTERN)) {
mFormFactor = FORM_FACTOR_ARC;
} else { ...
where ARC_DEVICE_PATTERN is defined as
private static final String ARC_DEVICE_PATTERN = ".+_cheets|cheets_.+";
So it's not just a device ending with _cheets. It can start with cheets_ as well.
Update 8/26/20 -- As of 7 months ago, the source has been moved around from FormFactors.java to FeatureSupport.java. If you were looking for where it went- here it the code as of today.
public static boolean isArc() {
return (Build.DEVICE != null && Build.DEVICE.matches(".+_cheets|cheets_.+"));
}
The test remains the same.
Jan 15, 2023 -- The code has changed again! isArc() is now built into the FeatureUtil class (see commit here) The current version of isArc() :
/** Returns {#code true} if device is an ARC++ device. */
public static boolean isArc() {
return hasAnySystemFeature(ARC_FEATURE, ARC_DEVICE_MANAGEMENT_FEATURE);
}
Where ARC_FEATURE and ARC_DEVICE_MANAGEMENT_FEATURE are defined like this:
public static final String ARC_FEATURE = "org.chromium.arc";
public static final String ARC_DEVICE_MANAGEMENT_FEATURE = "org.chromium.arc.device_management";
the function hasAnySystemFeature() simply checks individual features and returns true if any is true.
Therefore the following might work as a simple standalone check in kotlin (where context is the activity context):
fun isArc(): Boolean {
return ((context.packageManager.hasSystemFeature("org.chromium.arc")) || (context.packageManager.hasSystemFeature("org.chromium.arc.device_management")))
Note this is similar to #dex's answer below, but includes both tests used by the Android source.
Incidentally, from looking at the code linked above you can also check other device characteristics like like isWatch(), isTV(), isAutomotive(), isPC(), isVrHeadset(), isLowRam(), etc. using similar feature checks.
PackageManager pm = context.getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_PC))
// it's a chromebook
I found the solution in Android CTS code.
public static boolean isArc(#NonNull Context context) {
PackageManager pm = context.getPackageManager();
return pm.hasSystemFeature( "org.chromium.arc" ) || pm.hasSystemFeature( "org.chromium.arc.device_management" );
}

NEXUS 5 lollipop 5.1 SurfaceFlinger error

Hello there? There is one question.
nexus5 lollipop5.1's issue.
surfaceflinger there is a code that comes in response to the device screen information in ScreenshotClient update by using.
The code looks like the following.
unsigned int sw, sh, xsize, gsize, stride;
... ...
ScreenshotClient sc;
sp<IBinder> display = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
if( sc.update(display,Rect(), sw,sh, false) != NO_ERROR) {
... ...
sc.release();
return 0;
}
stride = sc.getStride();
mapbase = sc.getPixels();
... ...
The above code, in other lollipop version is operating normally. Of course, nexus7 the same version also operate normally.
However, the change in nexus5. My code to reference, but continue to code loop is. And because you try to get to continue the screen. First I code in nexus5 is operating normally. However, when a certain count is, update is not came under the other screen to return the error. More ...! After complete finished my code, re-run, after it is another well, the same phenomenon occurs when it comes to a certain number. If you are ability who know about this?
I'm sorry it is not so good in English.
I had solve. ScreenshotClient sc and sp < IBinder > display should be declared as a global variable. If the class is declared error occurs several times. (example. It declared in the function to be repeated.)

android compatibility. I am confused when using Build.VERSION_CODES

Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + Build.VERSION_CODES.ICE_CREAM_SANDWICH);
I write code like this, I used the sdk4.0 to compile this android program, so it didn't cause compile error. When I run this program in my phone that running android 2.3.4, it run well.
Why? I am confused that version 2.3.4 (api level 10) has Build.VERSION_CODES.ICE_CREAM_SANDWICH property? And when I used sdk2.3.4 will cause compile error.
More
I test some code like these below,
private ScaleGestureDetector mScaleGestureDetector;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
mScaleGestureDetector = new ScaleGestureDetector(this, new MyOnScaleGestureListener());
}
this code will run well on android 1.6 api level 4, but
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR_MR1) {
Log.d(TAG, "getx(0): " + event.getX(0));
}
this program run failed on android 1.6 api level 4.
They both run on android 2.3.4 well.
why? (In ScaleGestureDetector class use the event.getX(0) (since api level 5) too)
I test some code more..
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Fragment f = new Fragment();
}
When I run it on android 1.6 emulator it throw java.lang.VerifyError, but on my phone running android 2.3.4 it throws java.lang.NoClassDefFoundError.
Why??
It's not as strange as it seems. It has to do with how Java treat constant values of primitives. During compilation, the value of the constant is put in the byte code, not a reference to the actual constant.
For example:
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + Build.VERSION_CODES.ICE_CREAM_SANDWICH);
will actucally be translated by the compiler to:
Log.d(TAG, "Build.VERSION_CODES.ICE_CREAM_SANDWICH: " + 14);
so the reference to the actual constant (and class) is removed.
Regarding the code that doesn't run for you, it has to do with that the MotionEvent.getX(int n) method wasn't available until api level 5. Before that, multitouch wasn't supported and thus no need for any other method than getX().
It doesn't matter if you actually call the method that doesn't exist. The error is appearing while the class is being loaded and verified by the platform. You most likely get a VerifyError in the log, since it will discover that you're trying to call a non-existent method during the verification.
On the other hand, if you try to use a class that doesn't exist, you will get a ClassNotFoundException instead. Note that sometimes, a class exists in Android even if the documentation doesn't say so. Some classes existed in early versions of Android but weren't exposed until later. Some have even gone the other way.
So:
Trying to use a class that doesn't exist - ClassNotFoundException
Trying to use a method that doesn't exist on a class that exists - VerifyError
(When it comes to using Fragments, they are available for earlier versions with the standalone Android Support Library)

Categories

Resources