如何在同一页面中播放多个音频文件?

Posted

技术标签:

【中文标题】如何在同一页面中播放多个音频文件?【英文标题】:How to play more than one audio file in the same page in flutter? 【发布时间】:2021-06-02 18:19:56 【问题描述】:

我有一个聊天页面,用户可以在其中将文本、图像、音频和视频作为消息发送给其他用户。除音频和视频外,一切正常。在我的聊天应用程序中,允许用户使用 file_picker 插件选择音频文件。用户选择音频文件后,将其上传到服务器。服务器然后使用套接字事件发回音频文件。我的应用程序侦听套接字事件并根据消息类型生成消息视图。如果是文本消息,则会显示在文本小部件中。如果它是音频消息,它会显示在音频小部件中。 audio_player 插件用于播放音频文件。当用户上传音频时,一段时间后,音频文件会显示给在同一个房间聊天的两个用户。如果它是单个音频文件,一切正常。如果用户在第二个音频中上传另一个音频文件,音频控制器会用最后上传的音频替换我在聊天视图中的所有音频文件。用于音频的代码如下:

    //for audio files (This code has been placed in _ChatPageState)
  AnimationController _animationIconController1;
  AudioCache audioCache;
  AudioPlayer audioPlayer;
  Duration _duration = new Duration();
  Duration _position = new Duration();
  Duration _slider = new Duration(seconds: 0);
  double durationValue;
  bool isSongPlaying = false;
  bool isPlaying = false;

稍后,变量在initState()中被初始化

//for audio inside initState
_position = _slider;
_animationIconController1 = AnimationController(
  vsync: this,
  duration: Duration(milliseconds: 750),
  reverseDuration: Duration(milliseconds: 750),
);
audioPlayer = new AudioPlayer();
audioCache = new AudioCache(fixedPlayer: audioPlayer);
audioPlayer.durationHandler = (d) => setState(() 
  _duration = d;
);

audioPlayer.positionHandler = (p) => setState(() 
  _position = p;
);

接下来,如果用户从套接字监听器接收到一个音频文件,则会显示一个音频小部件,以便用户可以播放该文件:

Widget audioMessage(int index) 
return Container(
  width: MediaQuery.of(context).size.width * 0.6,
  height: 30,
  child: Row(
    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
    children: [
      GestureDetector(
        onTap: () 
          setState(() 
            isPlaying ? _animationIconController1.reverse() : _animationIconController1.forward();
            isPlaying = !isPlaying;
          );
          // Add code to pause and play the music.
          if (!isSongPlaying)
            audioPlayer.play('some hidden link/file/$messages[index].message');
            setState(() 
              isSongPlaying = true;
            );
           else 
            audioPlayer.pause();
            setState(() 
              isSongPlaying = false;
            );
          
        ,
        child: ClipOval(
          child: Container(
            color: Colors.pink[600],
            child: Padding(
              padding: const EdgeInsets.all(8.0),
              child: AnimatedIcon(
                icon: isSongPlaying ==false ? AnimatedIcons.play_pause : AnimatedIcons.pause_play,
                size: 14,
                progress: _animationIconController1,
                color: Colors.white,
              ),
            ),
          ),
        ),
      ),
      Slider(
        activeColor: Colors.white,
        inactiveColor: Colors.grey,
        value: _position.inSeconds.toDouble(),
        max: _duration.inSeconds.toDouble(),
        onChanged: (double value) 
          seekToSeconds(value.toInt());
          value = value;
        ,
      ),
    ],
  ),
);

我认为问题出在我的音频和动画控制器上。它仅针对一名玩家启动。那么我该如何解决这个问题。用户可以上传任意数量的音频。如何为用户上传的尽可能多的音频文件动态创建音频控制器?

【问题讨论】:

【参考方案1】:

因此,我通过将所有音频代码移动到单独的 dart 文件中来实现这一点。音频实现见下文:

  class PlayAudio extends StatefulWidget 
  final String url;
  
  const PlayAudio(Key key, this.url) : super(key: key);

  @override
  _PlayAudiostate createState() => _PlayAudioState();


class _PlayAudioState extends State<PlayAudio> with TickerProviderStateMixin
  //for audio files
  AnimationController _animationIconController1;
  AudioCache audioCache;
  AudioPlayer audioPlayer;
  Duration _duration = new Duration();
  Duration _position = new Duration();
  Duration _slider = new Duration(seconds: 0);
  double durationValue;
  bool isSongPlaying = false;
  bool isPlaying = false;

  @override
  void initState() 
    // TODO: implement initState
    super.initState();
    //for audio inside initState
    _position = _slider;
    _animationIconController1 = new AnimationController(
      vsync: this,
      duration: new Duration(milliseconds: 750),
      reverseDuration: new Duration(milliseconds: 750),
    );
    audioPlayer = new AudioPlayer();
    audioCache = new AudioCache(fixedPlayer: audioPlayer);
    audioPlayer.durationHandler = (d) => setState(() 
      _duration = d;
    );

    audioPlayer.positionHandler = (p) => setState(() 
      _position = p;
    );

    print('audio widget: ' + widget.url);
  

  @override
  void dispose() 
    // TODO: implement dispose
    super.dispose();
    audioPlayer.dispose();
  

  void seekToSeconds(int second) 
    Duration newDuration = Duration(seconds: second);
    audioPlayer.seek(newDuration);
  

  @override
  Widget build(BuildContext context) 
    return Container(
      width: MediaQuery.of(context).size.width * 0.6,
      height: 30,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          GestureDetector(
            onTap: () 
              setState(() 
                isPlaying ? _animationIconController1.reverse() : _animationIconController1.forward();
                isPlaying = !isPlaying;
              );
              // Add code to pause and play the music.
              if (!isSongPlaying)
                audioPlayer.play('$widget.url');
                setState(() 
                  isSongPlaying = true;
                );
               else 
                audioPlayer.pause();
                setState(() 
                  isSongPlaying = false;
                );
              
            ,
            child: ClipOval(
              child: Container(
                color: Colors.pink[600],
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: AnimatedIcon(
                    icon: AnimatedIcons.play_pause,
                    size: 14,
                    progress: _animationIconController1,
                    color: Colors.white,
                  ),
                ),
              ),
            ),
          ),
          Slider(
            activeColor: Colors.white,
            inactiveColor: Colors.grey,
            value: _position.inSeconds.toDouble(),
            max: _duration.inSeconds.toDouble(),
            onChanged: (double value) 
              seekToSeconds(value.toInt());
              value = value;
            ,
          ),
        ],
      ),
    );
  

现在这是一个单独的类。现在,要播放多个音频文件,我所做的就是在需要的地方将上述文件调用到我的 main.dart 中。确保每次要播放新的音频文件时都调用“new”,只需传递一个 url。见下文:

new PlayAudio(url: 'some URL');

希望这有助于在同一页面中播放多个音频文件。仅供参考,我正在使用https://pub.dev/packages/audioplayers

【讨论】:

以上是关于如何在同一页面中播放多个音频文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中连续录制音频并播放同一文件中的音频?

如何在win32编程中,在同一个窗口下播放多个音乐

在 Cordova 中播放音频文件后如何打开页面?

如何在 AVAudioPlayer 中循环播放多个音频文件?

如何同时播放多个音频文件

AudioJS:每页一次只播放 html5 音频