SimformSolutionsPvtLtd / audio_waveforms

Use this plugin to generate waveforms while recording audio in any file formats supported by given encoders or from audio files. We can use gestures to scroll through the waveforms or seek to any position while playing audio and also style waveforms
https://pub.dev/packages/audio_waveforms
MIT License
275 stars 141 forks source link

Player can only play the recorded sound once on Android phone. #295

Open jdevp opened 4 months ago

jdevp commented 4 months ago

Describe the bug I'm trying to record a sound and play it back. It works on the recording part but it only allows me to play the sound once.

To Reproduce Steps to reproduce the behavior:

  1. Initialize Player Controller: playerController = PlayerController();
  2. Record sound (successfully)
  3. Prepare the player:
getPlayerReady() async{
    String directory = (await getApplicationDocumentsDirectory()).path;
    String dir="$directory/audio";
    print ("$dir/$uKey.aac");
    await playerController!.preparePlayer(
      path: '$dir/$uKey.aac',
      shouldExtractWaveform: true,
      noOfSamples: 100,
      volume: 1.0,
    );
  }
  1. Play the recorded sound but it plays only first time.
IconButton(
            icon: const Icon(  Icons.play_circle,
              color: Colors.green,
            ),
            onPressed: ()async{

              print(playerController!.playerState);
              // the first time, the playerState shows Initialized. At end of play, t shows Stopped
              if(playerController!.playerState.isStopped) {
                 print("try to play it again but failed");
                 await playerController!.seekTo(0);

                 await playerController!.startPlayer(finishMode: FinishMode.stop);
                 return;
              }
              playerController!.playerState == PlayerState.playing
                  ? await playerController!.pausePlayer()
                  : await playerController!.startPlayer(finishMode: FinishMode.stop);
            }
        ),

Expected behavior It should be played every time.

Smartphone (please complete the following information):

Screenshots If applicable, add screenshots to help explain your problem.

Additional context Add any other context about the problem here.

baraazain commented 4 months ago

i face the same probleme i used await controller.startPlayer(finishMode: FinishMode.pause); and it work if you are stop the player you can't run the player again

class AudioWavesMessage extends StatefulWidget { final Message message;

const AudioWavesMessage({super.key, required this.message});

@override State createState() => _AudioWavesMessageState(); }

class _AudioWavesMessageState extends State { PlayerController controller = PlayerController(); // Initialise bool isPlaying = false;

  @override
  initState() {
    initialPlayer();
    super.initState();
  }

  @override
  void dispose() {
    stopPlayer();
    controller.dispose();
    super.dispose();
  }

`` initialPlayer() async { await controller.preparePlayer( path: widget.message.files![0].path, shouldExtractWaveform: true, noOfSamples: 50, volume: 1.0, ); controller.onCompletion.listen((state) { setState(() { pausePlayer(); }); }); }

startPlayer() async { try { await controller.startPlayer(finishMode: FinishMode.pause); setState(() { isPlaying = true; }); } catch (e) { print('Error start player: $e'); } }

pausePlayer() async { try { await controller.pausePlayer(); setState(() { isPlaying = false; }); } catch (e) { print('Error pausing player: $e'); } }

stopPlayer() async { try { await controller.stopPlayer(); setState(() { isPlaying = false; }); } catch (e) { print('Error stop player: $e'); } }

@override build(BuildContext context) { return SizedBox( width: 300, child: Row( children: [ InkWell( onTap: () { isPlaying ? pausePlayer() : startPlayer(); }, child: Icon( isPlaying ? Icons.pause : Icons.play_arrow, color: Colors.white, )), const SizedBox( width: 10, ), Expanded( child: AudioFileWaveforms( size: Size(MediaQuery.of(context).size.width, 40.0), playerController: controller, enableSeekGesture: isPlaying ? true : false, waveformType: WaveformType.fitWidth, waveformData: const [], playerWaveStyle: const PlayerWaveStyle( fixedWaveColor: Colors.white, liveWaveColor: DesignColors.green, spacing: 6, seekLineColor: Colors.grey, scaleFactor: 200), padding: EdgeInsets.zero, margin: EdgeInsets.zero, continuousWaveform: true, ), ), ], ), ); } }

Ujas-Majithiya commented 4 months ago

@jdevp startPlayer has the parameter finishMode which you can use to change behaviour when audio ends playing. For your use case, you may use FinishMode.pause. See its documentation for more info.

ebwood commented 2 months ago

@jdevp startPlayer has the parameter finishMode which you can use to change behaviour when audio ends playing. For your use case, you may use FinishMode.pause. See its documentation for more info.

FinishMode.pause can play again for same audio file, but if i reuse the PlayerController for different file, FinishMode.pause is not working in android. iOS is no such problem.

Ujas-Majithiya commented 1 month ago

Reopening for further investigation

ysgy1121 commented 1 week ago
fun preparePlayer(
        result: MethodChannel.Result,
        path: String?,
        volume: Float?,
        frequency: Long?,
    ) {
        if (path != null) {
            frequency?.let {
                updateFrequency = it
            }
            val uri = Uri.parse(path)
            val mediaItem = MediaItem.fromUri(uri)
            player = ExoPlayer.Builder(appContext).build()
            player?.addMediaItem(mediaItem)
            player?.prepare()
            playerListener = object : Player.Listener {
                override fun onPlayerStateChanged(isReady: Boolean, state: Int) {
                    if (!isPlayerPrepared) {
                        if (state == Player.STATE_READY) {
                            player?.volume = volume ?: 1F
                            isPlayerPrepared = true
                            result.success(true)
                        }
                    }
                    if (state == Player.STATE_ENDED) {
                        val args: MutableMap<String, Any?> = HashMap()
                        when (finishMode) {
                            FinishMode.Loop -> {
                                player?.seekTo(0)
                                player?.play()
                                args[Constants.finishType] = 0
                            }
                            FinishMode.Pause -> {
                                player?.seekTo(0)
                                player?.playWhenReady = false
                                stopListening()
                                args[Constants.finishType] = 1
                            }
                            else -> {
                                player?.stop()
                                player?.release()
                                player = null
                                isPlayerPrepared = false
                                stopListening()
                                args[Constants.finishType] = 2
                            }
                        }
                        args[Constants.playerKey] = key
                        methodChannel.invokeMethod(
                            Constants.onDidFinishPlayingAudio,
                            args
                        )
                    }
                }
            }
            player?.addListener(playerListener!!)
        } else {
            result.error(Constants.LOG_TAG, "path to audio file or unique key can't be null", "")
        }
    }

I encountered the same issue while finishMode was set to stop, and I resolved the problem by modifying the code[/audio_waveforms/android/src/main/kotlin/com/simform/audio_waveforms/AudioPlayer.kt] as described.

add 'isPlayerPrepared = false'

igorfastronicorrea commented 5 days ago

same problem, my state is stopped e when I try .startPlayer() sound not play.