android – Flutter: Why is my app giving an error when trying to play back an audio file?

I have an app that records audio when the user taps a button. It writes a temporary file to the mobile device, then runs some FFMPEG code to change the pitch.

The normal way a user would use it is:

  1. Tap the button
  2. Record the audio (10 seconds)
  3. After recording is done, play back the audio with the pitch modified

This works fine when I do it once, but when I do it a second time (after playback has completely finished), I can record another audio file but my console says there was a source error for playback. I’m not sure why it can do it once but not twice. I feel as though the issue might be something with async/await. I don’t fully understand when to use it so I try to avoid it, but Android Studio forced me to use it for the line final List<FileSystemEntity> files = await dir.list().toList(); and then I added it everywhere else that I thought kinda made sense but honestly it’s a mess…

void startTapRecording() {
    print('##MyApp## startTapRecording: 1');
    controller?.stop();
    controller?.reset();
    cancelTimer();
    print('##MyApp## startTapRecording: 2');
    stopPlaying();
    stopRecording();

    final String filename = getNewMyAppAudioFileID();

    setState(() {
      currentMyAppFilename = filename;
    });

    startRecordingTimer();

    print('##MyApp## startTapRecording: 3');

    startRecording(MyAppsInProgressFileDir.path + currentMyAppFilename);

    controller?.forward();

    print('##MyApp## startTapRecording: 4 (finished)');
}

void startRecording(String filename) async {
    print('##MyApp## startRecording: 1');
    // Request Microphone permission if needed

    await cleanupTempAudioFiles();

    print('##MyApp## startRecording: 2');

    PermissionStatus status = await Permission.microphone.request();
    if (status != PermissionStatus.granted) {
      throw RecordingPermissionException("Microphone permission not granted");
    }

    await myRecorder.startRecorder(toFile: filename); 

    print('##MyApp## startRecording: 3 (finished)');
}


Future<void> cleanupTempAudioFiles() async {
    print('##MyApp## cleanupTempAudioFiles: 1');

    final dir = Directory(MyAppsInProgressFileDir.path);
    final List<FileSystemEntity> files = await dir.list().toList();

    print('##MyApp## cleanupTempAudioFiles: 2 ');
    files.forEach((file) async {
      print('##MyApp## cleanupTempAudioFiles: file.path=" + file.path);
      if (file.path == MyAppsInProgressFileDir.path + currentMyAppFilename) {
        await file.delete();
        print("##MyApp## cleanupTempAudioFiles: file deleted');
      }

      if (file.path == MyAppsInProgressFileDir.path + currentMyAppFilename + 'High.mp3') {
        await file.delete();
        print('##MyApp## cleanupTempAudioFiles: file deleted High.mp3');
      }

      if (file.path == MyAppsInProgressFileDir.path + currentMyAppFilename + 'Low.mp3') {
        await file.delete();
        print('##MyApp## cleanupTempAudioFiles: file deleted Low.mp3');
      }
    });
    print('##MyApp## cleanupTempAudioFiles: 3 (finished)');
}

Logcat output, filtered on ##MyApp##:

##MyApp## startTapRecording: 1
##MyApp## startTapRecording: 2
##MyApp## startTapRecording: 3
##MyApp## startRecording: 1
##MyApp## cleanupTempAudioFiles: 1
##MyApp## startTapRecording: 4 (finished)
##MyApp## cleanupTempAudioFiles: 2 
##MyApp## cleanupTempAudioFiles: file.path = /data/user/0/com.example.MyApp_3/code_cache/MyApp/MyAppAudioFiles/MyAppsInProgress/MyAppAudioFile1
##MyApp## cleanupTempAudioFiles: file.path = /data/user/0/com.example.MyApp_3/code_cache/MyApp/MyAppAudioFiles/MyAppsInProgress/MyAppAudioFile1High.mp3
##MyApp## cleanupTempAudioFiles: 3 (finished)
##MyApp## startRecording: 2
##MyApp## cleanupTempAudioFiles: file deleted
##MyApp## cleanupTempAudioFiles: file deleted High.mp3
##MyApp## startRecording: 3 (finished)
##MyApp## handleTimeout sendableMyAppExists: 1
##MyApp## runFFMPEGHighLow
##MyApp## startTapRecording: 1
##MyApp## startTapRecording: 2
##MyApp## startTapRecording: 3
##MyApp## startRecording: 1
##MyApp## cleanupTempAudioFiles: 1
##MyApp## startTapRecording: 4 (finished)
##MyApp## cleanupTempAudioFiles: 2 
##MyApp## cleanupTempAudioFiles: file.path = /data/user/0/com.example.MyApp_3/code_cache/MyApp/MyAppAudioFiles/MyAppsInProgress/MyAppAudioFile1
##MyApp## cleanupTempAudioFiles: file.path = /data/user/0/com.example.MyApp_3/code_cache/MyApp/MyAppAudioFiles/MyAppsInProgress/MyAppAudioFile1High.mp3
##MyApp## cleanupTempAudioFiles: 3 (finished)
##MyApp## startRecording: 2
##MyApp## cleanupTempAudioFiles: file deleted
##MyApp## cleanupTempAudioFiles: file deleted High.mp3
##MyApp## startRecording: 3 (finished)
##MyApp## handleTimeout sendableMyAppExists: 1
##MyApp## runFFMPEGHighLow

The error in the console:

E/ExoPlayerImplInternal(20582): Playback error
E/ExoPlayerImplInternal(20582):   com.google.android.exoplayer2.ExoPlaybackException: Source error
E/ExoPlayerImplInternal(20582):       at com.google.android.exoplayer2.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:624)
E/ExoPlayerImplInternal(20582):       at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:594)
E/ExoPlayerImplInternal(20582):       at android.os.Handler.dispatchMessage(Handler.java:102)
E/ExoPlayerImplInternal(20582):       at android.os.Looper.loopOnce(Looper.java:226)
E/ExoPlayerImplInternal(20582):       at android.os.Looper.loop(Looper.java:313)
E/ExoPlayerImplInternal(20582):       at android.os.HandlerThread.run(HandlerThread.java:67)
E/ExoPlayerImplInternal(20582):   Caused by: com.google.android.exoplayer2.source.UnrecognizedInputFormatException: None of the available extractors (Mp3Extractor, FlvExtractor, FlacExtractor, WavExtractor, FragmentedMp4Extractor, Mp4Extractor, AmrExtractor, PsExtractor, OggExtractor, TsExtractor, MatroskaExtractor, AdtsExtractor, Ac3Extractor, Ac4Extractor, JpegExtractor) could read the stream.
E/ExoPlayerImplInternal(20582):       at com.google.android.exoplayer2.source.BundledExtractorsAdapter.init(BundledExtractorsAdapter.java:92)
E/ExoPlayerImplInternal(20582):       at com.google.android.exoplayer2.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1025)
E/ExoPlayerImplInternal(20582):       at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:409)
E/ExoPlayerImplInternal(20582):       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
E/ExoPlayerImplInternal(20582):       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
E/ExoPlayerImplInternal(20582):       at java.lang.Thread.run(Thread.java:920)
E/AudioPlayer(20582): TYPE_SOURCE: None of the available extractors (Mp3Extractor, FlvExtractor, FlacExtractor, WavExtractor, FragmentedMp4Extractor, Mp4Extractor, AmrExtractor, PsExtractor, OggExtractor, TsExtractor, MatroskaExtractor, AdtsExtractor, Ac3Extractor, Ac4Extractor, JpegExtractor) could read the stream.
I/ExoPlayerImpl(20582): Release cc7756c [ExoPlayerLib/2.15.0] [o1q, SM-G991U, samsung, 31] [goog.exo.core]
E/flutter (20582): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: (0) Source error
E/flutter (20582): #0      AudioPlayer._load (package:just_audio/just_audio.dart:798:9)
E/flutter (20582): <asynchronous suspension>
E/flutter (20582): #1      AudioPlayer._setPlatformActive.setPlatform (package:just_audio/just_audio.dart:1365:28)
E/flutter (20582): <asynchronous suspension>
E/flutter (20582): 

Leave a Comment