flutter_sound icon indicating copy to clipboard operation
flutter_sound copied to clipboard

[BUG]:Crash when play from stream, stream is generate sine wave buffer

Open zoey0820ivy opened this issue 1 year ago • 2 comments

Flutter Sound Version : flutter_sound: ^9.16.3

  • FULL or LITE flavor ?

  • Important: Result of the command : flutter pub deps | grep flutter_sound


Severity

  • Crash ? Yes

  • Result is not what expected ?

  • Cannot build my App ? No

  • Minor issue ?


Platforms you faced the error

  • iOS ? Yes

  • Android ?

  • Flutter Web ?

  • Emulator ?

  • Real device ? Yes


Describe the bug A clear and concise description of what the bug is.

To Reproduce Steps to reproduce the behavior:

  1. Generate sine wave buffer
  2. Play from stream
  3. Wait...
  4. See error

Logs!!!!

(This is very important. Most of the time we cannot do anything if we do not have information on your bug). To activate the logs, you must instantiate your modules with the Log Level set to Level.debug :

final FlutterSoundPlayer _player = FlutterSoundPlayer(logLevel: Level.error);

  Future<void> startPlaying() async {
    try {
      print('_player isOpen:${_player.isOpen()}');
      await _player.startPlayerFromStream(
        codec: Codec.pcm16,
        sampleRate: _sampleRate.toInt(), // _sampleRate = 48000;
        bufferSize: 20480,
        numChannels: 1,
        whenFinished: () {
          FlutterSoundPlayer().logger.i("FINISHED!");
        }
      );
      _isPlaying = true;
      // 定期生成超声波数据并播放
      _playbackTimer =
          Timer.periodic(Duration(milliseconds: 100), (timer) async {
        if (_isPlaying) {
          Uint8List audioData = generateSineWaveBuffer(100);
          print('audioData length:${audioData.length}');
          await _player.feedFromStream(audioData);
        } else {
          timer.cancel();
        }
      });
    } catch (e) {
      _player.logger.e('startPlaying error: $e');
      _isPlaying = false;
    }
  }

  Uint8List generateSineWaveBuffer(int durationMs) {
    int frameCount = ((_sampleRate * durationMs) / 1000).toInt();
    Float32List buffer = Float32List(frameCount);
    double amplitude = 1.0; // [-1, 1] 
    for (int i = 0; i < frameCount; i++) {
      buffer[i] = amplitude * sin(_phase);
      _phase += (2 * pi * _frequency) / _sampleRate; // _frequency =  18000;
      if (_phase >= 2 * pi) {
        _phase -= 2 * pi;
      }
    }
    return Uint8List.view(buffer.buffer);
  }

See this Assertion failed: (waitingBlock == nil), function -[AudioEngine feed:], file FlautoPlayerEngine.mm, line 372.

  • thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT frame #0: 0x000000020eb8f198 libsystem_kernel.dylib__pthread_kill + 8 libsystem_kernel.dylib: -> 0x20eb8f198 <+8>: b.lo 0x20eb8f1b4 ; <+36> 0x20eb8f19c <+12>: stp x29, x30, [sp, #-0x10]! 0x20eb8f1a0 <+16>: mov x29, sp 0x20eb8f1a4 <+20>: bl 0x20eb8ab70 ; cerror_nocancel Target 0: (Runner) stopped.

zoey0820ivy avatar Nov 06 '24 02:11 zoey0820ivy

The problem is that you feed the iOS driver too quickly. You have two choices:

  • play with back pressure (flow control)
  • Play without back pressure (you will feed the flutter sound stream)

the big issue for you is that you send data to the driver every 100ms, without knowing if the driver is ready to accept the data and if the previous buffer has been played.

i think that you should not generate data on a timer, but react on the event "needSomeData". Look to the example "play from stream with back pressure"

Larpoux avatar Nov 06 '24 18:11 Larpoux

Hi @Larpoux I'm facing the same kind of problem where feedFromStream takes forever, and never ends on Ios when there's some type of interruptions. but when I put a timeout on feedFromStream's execution, there is some scenarios where it crashes, do you have any idea on how to fix this? How long feedFromStream is suppose to take to execute?

WNizard avatar Nov 14 '24 14:11 WNizard