I'm using pocketsphynx library on Android for keyword spotting, and it serves its purpose great. However, when I am trying to release microphone to make use of it in another component of the app, I am unable to do so. I get the following error in logcat:
12-11 10:19:55.827 255-30344/? I/AudioFlinger_Threads: AudioFlinger's thread 0xa6cc0000 ready to run
12-11 10:19:55.849 255-31292/? W/APM::AudioPolicyManager: startInput(17675) preempting low-priority input 17671
12-11 10:19:55.859 255-29587/? W/AudioALSAStreamManager: -routingInputDevice(), input_device == AUDIO_DEVICE_NONE(0x0), return
12-11 10:19:55.924 27054-1900/? W/AudioRecord: dead IAudioRecord, creating a new one from obtainBuffer()
12-11 10:19:55.925 255-30344/? W/AudioALSAStreamManager: -routingInputDevice(), input_device == current_input_device(0x80000004), return
12-11 10:19:55.927 255-30346/? I/AudioFlinger_Threads: AudioFlinger's thread 0xaa880000 ready to run
12-11 10:19:55.930 255-31292/? W/APM::AudioPolicyManager: stopInput() unknown input 17671
12-11 10:19:55.930 255-31292/? W/APM::AudioPolicyManager: releaseInput() releasing unknown input 17671
12-11 10:19:55.931 255-953/? W/APM::AudioPolicyManager: startInput(AUDIO_SOURCE_HOTWORD) invalid, already started high-priority input 17675
12-11 10:19:55.931 27054-1900/? W/AudioRecord: restoreRecord_l() failed status -38
12-11 10:19:55.934 27054-1900/? E/AudioRecord-JNI: Error -38 during AudioRecord native read
12-11 10:19:55.951 27054-1900/? E/AudioProvider: audioRecord read failed
com.google.android.apps.gsa.shared.exception.GsaIOException: Error code: 393221 | not open
at com.google.android.libraries.assistant.hotword.a.a(SourceFile:67)
at com.google.android.libraries.assistant.hotword.b.run(SourceFile:30)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:423)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
This happens when I'm attempting to use getUserMedia
from WebView
. Before doing so, I am calling
recognizer.cancel();
from my Activity, but that doesn't seem to help.
I also tried using .stop()
, .removeListener(...)
APIs, but the issue still remains. Please suggest if there is a way to release a hold on a mic that pocketsphinx is making, so that other components of the app can use mic again.
Any help is appreciated.
UPDATE 1: Added more logs and looked at Android code for AudioPolicyManager. It seems like two processes (31292
and 953
) were trying to compete by doing startInput
call, and one (31292
) shut down another (953
). Obviously, I may be misinterpreting the log.
UPDATE 2: I looked into pocketsphynx SpeechRecognizer.shutdown
method and found that this is the method that actually releases the AudioRecord
object. By trial-end-error I managed to figure out how to call shutdown
and not crash the app (it has to happen in onResult, after all the listeners are removed). After that, my another component started to work. I did not yet have a chance to confirm whether or not I can re-start keyword spotting after shutdown
. If re-initialization is required after that (which takes a lot of time), that's not going to be an acceptable workaround.
UPDATE 3: I confirmed that shutdown
is not necessary to release a hold on a mic. I managed to make my other component after just calling stop
API. The key seems to be that stopping pocketsphinx takes a bit of time, and the other component should wait for that. The event I used, that worked for me was onResult
. At that point another component is already able to get a hold of a new AudioRecord
. I still haven't tried re-starting pocketsphinx after that.
Alright, I got it to work. Calling SpeechRecognizer.shutdown
is indeed unnecessary. I was able to use SpeechRecognizer.stop
API while making sure to wait for the last result to come in in onResult
callback of my RecognitionListener
implementation. After that I am able to call getUserMedia
browser API and it successfully gets a hold of a mic.
onResult
code in my RecognitionListener
implementation:
recognizer.removeListener(listener);
I also was able to confirm that re-starting recognition is easy, I just needed to do the following:
recognizer.addListener(listener);
recognizer.addKeyphraseSearch(KWS_SEARCH, KEYPHRASE);
recognizer.startListening(KWS_SEARCH);
Note: I am stopping the recognizer in a separate thread similarly to how recognizer was instantiated, but I have not confirmed whether or not it is necessary.