Canardoux / flutter_sound

Flutter plugin for sound. Audio recorder and player.
Mozilla Public License 2.0
877 stars 573 forks source link

[BUG] Muting OFF on Android #583

Open efraespada opened 3 years ago

efraespada commented 3 years ago

Flutter Sound Version :

Severity

Platforms you faced the error

Logs

When recording:

I/AudioManager(29542): In stopBluetoothSco(), calling application: com.landa.app
I/AudioManager(29542): In setBluetoothScoOn(), on: false, calling application: com.landa.app
I/AudioManager(29542): In setSpeakerphoneOn(), on: true, calling application: com.landa.app
I/AudioManager(29542): In setSpeakerphoneOn(), on: true, calling application: com.landa.app
I/AudioManager(29542): In setBluetoothScoOn(), on: false, calling application: com.landa.app
I/AudioManager(29542): In stopBluetoothSco(), calling application: com.landa.app

When playing the sound:

I/flutter (29542): FS:---> openAudioSession 
I/AudioManager(29542): In stopBluetoothSco(), calling application: com.landa.app
I/AudioManager(29542): In setBluetoothScoOn(), on: false, calling application: com.landa.app
I/AudioManager(29542): In setSpeakerphoneOn(), on: true, calling application: com.landa.app
I/AudioManager(29542): In setSpeakerphoneOn(), on: true, calling application: com.landa.app
I/AudioManager(29542): In setBluetoothScoOn(), on: false, calling application: com.landa.app
I/AudioManager(29542): In stopBluetoothSco(), calling application: com.landa.app
I/flutter (29542): FS:---> channelMethodCallHandler : openAudioSessionCompleted
I/flutter (29542): FS:<--- channelMethodCallHandler : openAudioSessionCompleted
I/flutter (29542): FS:<--- openAudioSession 
I/flutter (29542): FS:---> setSubscriptionDuration 
I/flutter (29542): FS:<---- setSubscriptionDuration 
I/flutter (29542): FS:---> startPlayer 
I/flutter (29542): FS:---> stop 
I/flutter (29542): FS:<--- stop 
I/flutter (29542): FS:---> _convert 
I/flutter (29542): FS:---> needToConvert 
I/flutter (29542): FS:<--- needToConvert 
I/flutter (29542): FS:<--- _convert 
I/flutter (29542): FS:<--- startPlayer 
D/FlautoPlayer(29542): mediaPlayer prepared and started
I/flutter (29542): FS:---> channelMethodCallHandler : startPlayerCompleted
I/flutter (29542): FS:<--- channelMethodCallHandler : startPlayerCompleted
I/flutter (29542): audioPlayerListener 0:00:00.000000 0:00:02.560000
I/flutter (29542): audioPlayerListener 0:00:00.345000 0:00:02.560000
I/flutter (29542): audioPlayerListener 0:00:00.846000 0:00:02.560000
I/flutter (29542): audioPlayerListener 0:00:01.348000 0:00:02.560000
I/flutter (29542): audioPlayerListener 0:00:01.849000 0:00:02.560000
I/flutter (29542): audioPlayerListener 0:00:02.349000 0:00:02.560000
D/FlautoPlayer(29542): Playback completed.
V/MediaPlayer(29542): resetDrmState:  mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
V/MediaPlayer(29542): cleanDrmObj: mDrmObj=null mDrmSessionId=null
V/MediaPlayer(29542): resetDrmState:  mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
V/MediaPlayer(29542): cleanDrmObj: mDrmObj=null mDrmSessionId=null
I/flutter (29542): FS:---> channelMethodCallHandler : audioPlayerFinishedPlaying
I/flutter (29542): FS:---> audioPlayerFinished
I/flutter (29542): FS:---> stopPlayer 
I/flutter (29542): FS:---> stop 
I/flutter (29542): FS:---> closeAudioSession 
I/flutter (29542): FS:---> stop 
I/flutter (29542): FS:<--- audioPlayerFinished
I/flutter (29542): FS:<--- channelMethodCallHandler : audioPlayerFinishedPlaying
I/flutter (29542): FS:<--- stop 
I/flutter (29542): FS:<--- stopPlayer 
I/flutter (29542): FS:<--- stop 
I/flutter (29542): FS:<--- closeAudioSession 

Describe the bug Some Android devices record as expected but when playing this record file, no sound is heard. If I try to play this record file on other devices, there is no sound either.

Additional context**

This problem is faced in a BQ X2. Moto G9 works fine. No problem found in iOS.

Larpoux commented 3 years ago

Which codec ?

Larpoux commented 3 years ago

Do you use a bluetooth microphone ? A bluetooth Headset ?

efraespada commented 3 years ago

Using the Codec.aacADTS. The Bluetooth is off (but has the same behavior with Bluetooth on).

Larpoux commented 3 years ago

For what I see in your logs, it seems that a correct Audio File was recorded. It plays correctly with a callback every 300 ms.

If there is nos sound, it should be because of a blank record, and not a bad record. Is that true ?

Larpoux commented 3 years ago

Is the recorded length correct ?

efraespada commented 3 years ago

It is correct. Those records have a correct length, so as you say a blank record.

Larpoux commented 3 years ago

OK. So the problem is not in the recorder nor the player. The problem is because for an unknown reason, the microphone is not correctly linked to the player.

Perhaps your Android device does not allow opening the microphone (But I think we would have an error when starting the player). If you are sure that the record is blank (playing it elsewhere), you should look (and play) with the 'device:' parameter of openAudioSession() or setAudiofocus().

Larpoux commented 3 years ago

I suggest that you leave the default values for all the parameters of openAudioSession(), both for the player and the recorder

Larpoux commented 3 years ago

And do not use setAudioFocus() during these first tests.

Larpoux commented 3 years ago

AudioSessions are flaw. You can read this. I will work soon on this subject.

efraespada commented 3 years ago

I confirm it is a blank record because this device can play other records (from other devices).

Right now I'm not using the setAudioFocus method. This is my code:

if (!recordingAudio) {
  rRecorder = FlutterSoundRecorder();
  audioPath = await createTempAudioFileName();
  var outputFile = File(audioPath);
  await rRecorder.openAudioSession();
  await rRecorder.setSubscriptionDuration(Duration(milliseconds: 500));
  recordProgress = rRecorder.onProgress.listen((event) {
    recordDuration = event.duration;
    refreshScreen();
  });
  await rRecorder.startRecorder(
    toFile: audioPath,
    codec: Codec.aacADTS,
  );

  refreshScreen();
}

The same configuration is defined for the player.

I will check the openAudioSession params.

Thanks for your work ๐Ÿ˜ƒ

Larpoux commented 3 years ago

I guess that the dbPeakLevel that you get during Recorder.onProgress.listen((event) { is 0. Right ?

efraespada commented 3 years ago

You are right:

I/AudioManager(29542): In stopBluetoothSco(), calling application: com.landa.app
I/AudioManager(29542): In setBluetoothScoOn(), on: false, calling application: com.landa.app
I/AudioManager(29542): In setSpeakerphoneOn(), on: true, calling application: com.landa.app
I/AudioManager(29542): In setSpeakerphoneOn(), on: true, calling application: com.landa.app
I/AudioManager(29542): In setBluetoothScoOn(), on: false, calling application: com.landa.app
I/AudioManager(29542): In stopBluetoothSco(), calling application: com.landa.app
I/flutter (29542): peak level: 0.0
I/flutter (29542): peak level: 0.0
I/flutter (29542): peak level: 0.0
I/flutter (29542): peak level: 0.0
I/flutter (29542): peak level: 0.0
I/flutter (29542): peak level: 0.0
Larpoux commented 3 years ago

The problem is definitely in the opening of the microphone. But I do not see exactly what.

Larpoux commented 3 years ago

Did you try the Flutter Sound Demo or/and the SimpleRecorder example ?

efraespada commented 3 years ago

I've just tested the simple recorder demo and shows the same behavior (BQ X2).

Everything works fine in Moto G9.

Larpoux commented 3 years ago

Efra, I will need your contribution. I am going to build a patched version of Flutter Sound, and you will tell me the result. Probably not this evening, but tomorrow (Spain).

efraespada commented 3 years ago

Perfect, thanks again!

Larpoux commented 3 years ago

Btw : what is the version of your Android device ?

efraespada commented 3 years ago

It is Android 10.

Larpoux commented 3 years ago

I suggest that you give your device to a museum ๐Ÿคฃ . Please look to the SDK requirements

Larpoux commented 3 years ago

I am sorry, I mix Android Version and SDK Version. Android 10 is very new . Sorry.

Larpoux commented 3 years ago

I am just confused because Google does not use any more name for Android versions but number.

Talk with you tomorrow. Have a nice evening, Efra.

efraespada commented 3 years ago

Don't worry. ๐Ÿคฃ

Moto G9 and BQ X2 uses the same Android version, API level 29

Larpoux commented 3 years ago

Good morning Efra. Could you download and test this Patched Version of Flutter Sound ? Tell me if this file is too heavy. I will upload a lighter zip file.

This version :

Thank you for your contribution : this bug is bad, and I really want to fix it. (Several users already complained that the Recorder records nothing, but I did not have enough informations to work on that)

efraespada commented 3 years ago

It is a heavy ZIP, give me some minutes to check it. If you are working in a new branch I can checkout it to test the new changes.

efraespada commented 3 years ago

The ZIP stills downloading (poor connection) but meanwhile I rebooted the device. Now everything works perfectly ๐Ÿ˜ฎ

Larpoux commented 3 years ago

Do you mean that the records are not blanked anymore ?

efraespada commented 3 years ago

That's right. I'm gonna check it on more android devices. I'm doing more tests to see if the problem appears again.

Larpoux commented 3 years ago

๐Ÿ’ฏ : This is really great news. ๐Ÿ‘

Now, I have just to determine which change is necessary in τ (Target SDK, OpenAudioSession() code, Mute off, or setting the audio device.

I will need more help from you for that. Now I am going to lunch. I will be back in 1 hour.

Maybe you can try to change back the "compileSdkVersion" from 29 to 30 in the files :

efraespada commented 3 years ago

Yeah, and it seems a problem with the device system.

If the problem reappears, I'm gonna check the native recorder of the device (I forget to do it before the reboot).

Demo tested (with compileSdkVersion 30 and targetSdkVersion 30) and working in all devices:

Larpoux commented 3 years ago

You have many devices !!!

Larpoux commented 3 years ago

In procedure setAudioDevices() of module FlautoSession.java (tau_core/android/src/... ) I added :

audioManager.setMicrophoneMute(false);

You can try if still OK without this instruction.

Larpoux commented 3 years ago

setAudioFocus() ( not setAudioDevices)

Larpoux commented 3 years ago

At the top of procedure startRecorder() I changed :

        int  audioSource         = tabAudioSource[_audioSource.ordinal()];
        //audioSource =MediaRecorder.AudioSource.MIC; // Just for test

by

        int audioSource = MediaRecorder.AudioSource.MIC; // Just for test // PATCH LARPOUX

You can try reverse back to the previous instruction

Larpoux commented 3 years ago

(Module FlautoRecorder.java in tau_core)

Larpoux commented 3 years ago

And finally you can try to revert back the setAudioFocus() code in module FlautoSession :


    public boolean setAudioFocus(t_AUDIO_FOCUS focus, t_SESSION_CATEGORY category, t_SESSION_MODE sessionMode, int audioFlags, t_AUDIO_DEVICE audioDevice)
    {
        boolean r = true;
        audioManager = ( AudioManager ) Flauto.androidContext.getSystemService( Context.AUDIO_SERVICE );
        if ( Build.VERSION.SDK_INT >= 26 )
        {
            if ( focus != t_AUDIO_FOCUS.abandonFocus && focus != t_AUDIO_FOCUS.doNotRequestFocus && focus != t_AUDIO_FOCUS.requestFocus )
            {
                int focusGain = AudioManager.AUDIOFOCUS_GAIN;

                switch (focus)
                {
                    case requestFocusAndDuckOthers: focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK; ; break;
                    case requestFocusAndKeepOthers: focusGain = AudioManager.AUDIOFOCUS_GAIN; ; break;
                    case requestFocusTransient: focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT; break;
                    case requestFocusTransientExclusive: focusGain = AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE; break;
                    case requestFocusAndInterruptSpokenAudioAndMixWithOthers: focusGain = AudioManager.AUDIOFOCUS_GAIN; break;
                    case requestFocusAndStopOthers: focusGain = AudioManager.AUDIOFOCUS_GAIN; break;
                }
                audioFocusRequest = new AudioFocusRequest.Builder( focusGain )
                    // .setAudioAttributes(mPlaybackAttributes)
                    .build();

                // change the audio input/output device
                switch (audioDevice)
                {
                    case speaker:
                        //if (isBluetoothHeadsetConnected())
                            //audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
                        //else
                            //audioManager.setMode(AudioManager.MODE_NORMAL);
                        audioManager.stopBluetoothSco();
                        audioManager.setBluetoothScoOn(false);
                        audioManager.setSpeakerphoneOn(true);
                        break;
                    case blueTooth:
                    case blueToothA2DP:
                        //audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
                        if (isBluetoothHeadsetConnected())
                        {
                            audioManager.startBluetoothSco();
                            audioManager.setBluetoothScoOn(true);
                        }
                        audioManager.setSpeakerphoneOn(false);
                        break;
                    case earPiece:
                    case headset:
                        //audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
                        audioManager.stopBluetoothSco();
                        audioManager.setBluetoothScoOn(false);
                        audioManager.setSpeakerphoneOn(false);
                }

            }

            if (focus != t_AUDIO_FOCUS.doNotRequestFocus)
            {
                if ( audioFocusRequest == null )
                {
                    audioFocusRequest = new AudioFocusRequest.Builder ( AudioManager.AUDIOFOCUS_GAIN )
                        .build ();
                }

                hasFocus = (focus != t_AUDIO_FOCUS.abandonFocus);
                if (hasFocus)
                    audioManager.requestAudioFocus ( audioFocusRequest );
                else
                    audioManager.abandonAudioFocusRequest ( audioFocusRequest );
            }

            audioManager.setSpeakerphoneOn( (audioFlags &  outputToSpeaker) != 0);
            audioManager.setBluetoothScoOn( (audioFlags & allowBlueTooth) != 0);
            if ((audioFlags & allowBlueTooth) != 0)
                audioManager.startBluetoothSco();
            else
                audioManager.stopBluetoothSco();
            audioManager.setBluetoothA2dpOn(  (audioFlags & allowBlueToothA2DP) != 0 );
            audioManager.setMode( AudioManager.MODE_NORMAL );
        } else
            r = true; // BOF!

        return r;
    }
efraespada commented 3 years ago

That's why I think the microphone "controller/driver" of the BQ X2 could be freeze occasionally.

Okay, I'm testing all these changes.

I have two windows:

I guess I should: 1ยบ Do the changes in the main project. 2ยบ Clean a run the demo project

That's right?

Larpoux commented 3 years ago

Yes, the develop environment that you downloaded is linked to the tau_core project (inside this environment). No external dependencies. You can make your changes and just run the demo.

Larpoux commented 3 years ago

The demo/example will compile the tau_core project. You do not need two windows.

Larpoux commented 3 years ago

Myself, I use the project flutter_sound/example/android/ (a java project) under Android Studio instead of flutter_sound/example/ (a Flutter project) when I want to debug Android.

But you do not really debug so both are OK

efraespada commented 3 years ago

Testing the zip where you did the changes the application crashes when pressing the GO button on BQ M10:

I/flutter (11101): FS:---> openAudioSession 
D/AudioManager(11101): setMicrophoneMute: on = false
E/AndroidRuntime(11101): FATAL EXCEPTION: main
E/AndroidRuntime(11101): Process: com.dooboolab.example, PID: 11101
E/AndroidRuntime(11101): java.lang.NoClassDefFoundError: Failed resolution of: Landroid/media/AudioFocusRequest$Builder;
E/AndroidRuntime(11101):    at com.dooboolab.TauEngine.FlautoSession.setAudioFocus(FlautoSession.java:144)
E/AndroidRuntime(11101):    at com.dooboolab.TauEngine.FlautoPlayer.initializeFlautoPlayer(FlautoPlayer.java:120)
E/AndroidRuntime(11101):    at com.dooboolab.fluttersound.FlutterSoundPlayer.initializeFlautoPlayer(FlutterSoundPlayer.java:174)
E/AndroidRuntime(11101):    at com.dooboolab.fluttersound.FlutterSoundPlayerManager.onMethodCall(FlutterSoundPlayerManager.java:85)
E/AndroidRuntime(11101):    at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:233)
E/AndroidRuntime(11101):    at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:85)
E/AndroidRuntime(11101):    at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:738)
E/AndroidRuntime(11101):    at android.os.MessageQueue.nativePollOnce(Native Method)
E/AndroidRuntime(11101):    at android.os.MessageQueue.next(MessageQueue.java:328)
E/AndroidRuntime(11101):    at android.os.Looper.loop(Looper.java:164)
E/AndroidRuntime(11101):    at android.app.ActivityThread.main(ActivityThread.java:5726)
E/AndroidRuntime(11101):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(11101):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:888)
E/AndroidRuntime(11101):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:749)
E/AndroidRuntime(11101): Caused by: java.lang.ClassNotFoundException: Didn't find class "android.media.AudioFocusRequest$Builder" on path: DexPathList[[zip file "/data/app/com.dooboolab.example-2/base.apk"],nativeLibraryDirectories=[/data/app/com.dooboolab.example-2/lib/arm64, /data/app/com.dooboolab.example-2/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]
E/AndroidRuntime(11101):    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
E/AndroidRuntime(11101):    at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
E/AndroidRuntime(11101):    at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
E/AndroidRuntime(11101):    ... 14 more
E/AndroidRuntime(11101):    Suppressed: java.lang.ClassNotFoundException: android.media.AudioFocusRequest$Builder
E/AndroidRuntime(11101):        at java.lang.Class.classForName(Native Method)
E/AndroidRuntime(11101):        at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
E/AndroidRuntime(11101):        at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
E/AndroidRuntime(11101):        at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
E/AndroidRuntime(11101):        ... 15 more
E/AndroidRuntime(11101):    Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

In BQ X2, Samsung S9 and Moto G9 works fine ๐Ÿ‘Œ๐Ÿผ

efraespada commented 3 years ago

I guess it is normal, BQ M10 uses Android 6 (API level 23) and in the commented code I see:

if ( Build.VERSION.SDK_INT >= 26 )

So forget that crash ๐Ÿ˜… All working ๐Ÿ‘Œ๐Ÿผ

Larpoux commented 3 years ago

hum

Larpoux commented 3 years ago

SDK compatibility on Android is a nightmare

efraespada commented 3 years ago

Remember without editing setAudioFocus method it worked fine in Android 6.

This change also works in all devices: int audioSource = MediaRecorder.AudioSource.MIC

efraespada commented 3 years ago

Sincerely I think everything is ok. The BQ X2 microphone controller was freeze. If it happens again I'll check the native recorder before reboot it.

But right now the released version works fine.

Larpoux commented 3 years ago

Do you mean that we do not have to change anything in V 7.4.14 ? This is bad, because I am sure there is something to fix in this area. You had problem. Several other users had problem too...

Larpoux commented 3 years ago

The minimum I will do, will be to add this instruction to Mute OFF. Actually τ does not handle the muting ON/OFF so this is no problem to ensure that the muting is OFF.

Larpoux commented 3 years ago

And probably I am going to split setAudioFocus() in two procedures :

I am thinking that I cannot wait that the new API will be released. I have actually too many tasks to do before cleaning the API, and I really do not like how setAudiofocus works (this procedure is called during openAudioSession, both for the Recorder and the Player).