Windows窗体设计:本地音乐播放

一、引言

  • 音乐播放是生活日常中最常见的娱乐和经常使用的操作,我们应当了解和实现在Windows操作环境下,使用C#语言实现窗体上的音乐播放。 
  • 该博客得到目标是基本实现类似音乐app的音乐播放的一些基本但核心的功能,对控件的学习和实现进行进一步的练习和掌握。

二、开发环境搭建

  • 使用的开发工具:Visual Studio 需要自己配置的库:NAudio,NAudio.Vorbis。这是两个不同的库,作者不一样,要注意!
  • 安装和配置:在Visual Studio中,右键点击你的项目 -> 选择“管理NuGet程序包” -> 搜索“NAudio” -> 点击“安装”,NAudio.Vorbis,安装操作同上。

三、Windows窗体设计基础

  • Windows窗体设计不同于在运行框内直接运行,是设计窗体界面,通过添加各种控件(显示),对于各种控件有不同的动作(如点击),不同的动作可以有不同的方法,再进行代码实现。
  • 在VS页面选择创建新项目,选择Windows窗体应用(.NET Framework)进行创建。
  • 本次博客中使用到的控件主要有:openFiledialog,listBox,axWindowsMediaPlayer,button,label,trackBar等。
  • 简要窗体:

四、音乐播放功能实现

思路讲解:我们进行的是本地的音乐文件播放,并不是网页音乐数据流的播放(后续再发布博客进行实现),所以我们需要本地的音乐文件,对于音乐播放来说,文件的区别在于:ogg文件和其他文件。对于其他文件,比较好处理,可以直接使用axWindowsMediaPlayer进行播放和停止;对于ogg文件需要使用到NAudio和NAudio.Vorbis库的操作进行播放和停止。这part进行两者分开的操作。

  • 4.1 加载音乐文件
    • 对于ogg文件,我们创建控件button并改名为:播放ogg。在该控件内直接实现ogg文件的播放,定义string变量用来接受ogg文件,使用openFiledialog来选择文件,格式如下,并要求了文件格式。使用该种方法来播放ogg文件,用reader来解析ogg文件,再声明waveOut,再添加该reader:waveOut.Init(reader),再进行播放,实现跟axWindowsMediaPlayer一样的功能。
    • private void button5_Click(object sender, EventArgs e)
      {
          string oggFilePath ="" ;
      
          OpenFileDialog openFileDialog= new OpenFileDialog();
          openFileDialog.Filter = "播放音频|*.ogg";
          if(openFileDialog.ShowDialog() == DialogResult.OK)
          {
              oggFilePath=openFileDialog.FileName;
          }
          try
          {
              using (var reader =new VorbisWaveReader(oggFilePath))
              {
                  using (var waveOut = new WaveOutEvent())
                  {
                      waveOut.Init(reader);
                      waveOut.Play();
      
                      // 等待播放完成,或者你可以添加其他逻辑,比如监听播放状态变化事件  
                      while (waveOut.PlaybackState == PlaybackState.Playing)//线程
                      {
                          System.Threading.Thread.Sleep(1000);
                      }
                  }
              }
          }
          catch (Exception ex)
          {
              Console.WriteLine("发生错误: " + ex.Message);
          }
      }

      对于其他格式的音乐播放,我们使用常规方法,先是button打开文件进行添加音乐,将添加的音乐用一个文件数组来接收,并放在listBox中,也就是像音乐软件中的歌单一样。事先先定义两个全局变量,一个用来放音乐文件的,一个是歌单(跟listBox里显示的同步,方便后面获取点击到的歌曲文件)。

    • string[] files;
      List<string> localmusiclist=new List<string>();

    •  private void button1_Click(object sender, EventArgs e)
       {
           
           openFileDialog1.Filter = "选择音频|*mp3;*.wav;*.flac";
           openFileDialog1.Multiselect= true;
           if (files != null)
           {
               Array.Clear(files, 0, files.Length);//范类的约束,置零
           }
           if (openFileDialog1.ShowDialog() == DialogResult.OK)
           {
               //listBox1.Items.Clear();//列表清空
      
                   files = openFileDialog1.FileNames;
      
               string[] array = files;//复制
      
               foreach(string x in array)//添加到歌单中
               {
                   listBox1 .Items.Add(x);
                   localmusiclist.Add(x);
      
               }
           }
       }

  • 4.2 播放和暂停控制
    • 对于该示例中ogg文件是直接一直播放的,只能等播放完了才停止(后面讲解改正),所以我们实现对其他文件的常规播放的播放和暂停处理,我们已经有了listBox(歌单),我们想要做到点击歌单里的任意歌曲都进行播放,我们的思路是实现一个播放音乐的方法musicplay,在listBox里进行调用,我们获取listBox当前点击的文件的下标,用localmusiclist(index)传入给axWindowsMediaPlayer.URL,再axWindowsMediaPlayer.Play;进行播放,代码示例如下:
    • public void musicplay(string filename)//播放
      {
          string extension=Path.GetExtension(filename);//得到它后缀名,可以作扩展,别的文件就可以有分支做别的播放处理
           if (extension == ".ogg")
          {
              Console.WriteLine("this is ogg file.");
          }
           else
          {
               Console.WriteLine("this is not ogg file.");
               axWindowsMediaPlayer1.Ctlcontrols.play();//播放
          }
      }
       private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
       {
           if (localmusiclist.Count > 0)//保证有音乐文件被选中
           {
               axWindowsMediaPlayer1.URL = localmusiclist[listBox1.SelectedIndex];
               musicplay(localmusiclist[listBox1.SelectedIndex]);
               label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[listBox1.SelectedIndex]);
           }
       }

      完成这两部分代码后,在添加了音乐文件(不是ogg)到列表当中去之后,再点击列表当中的任意歌曲,就能进行播放,并且URL每点击都更改,所以当前播放了音乐后,再点击另一首,就会自动停止当前的歌曲,播放选择的歌曲。也就是说axWindowsMediaPlayer.Play是根据它的URL 来播放的。

    • 当然,我们也可以实现停止音乐的button控件,比较简单,直接使用axWindowsMediaPlayer的停止功能,示例如下:

    •  private void button2_Click(object sender, EventArgs e)
       {
      
           axWindowsMediaPlayer1.Ctlcontrols.stop(); 
      
       }

  • 4.3音量控制
    • 音量控制有专门的滑块控件,将其与axWindowsMediaPlayer的音量属性建立连接,就可以通过滑动滑块来控制音量,同时我们也可以将音量显示在一个label控件上,示例如下:
    •         private void trackBar1_Scroll(object sender, EventArgs e)
              {
                  axWindowsMediaPlayer1.settings.volume= trackBar1.Value;
                  label2.Text= trackBar1.Text;
              }

  • 4.4. 下一首/上一首功能
    • 实现下一首或上一首的思路:在一个button控件里,从listBox里获取下一个文件的下标,再用localmusiclist(index)获取文件,将文件给到axWindowsMediaPlayer.URL,再进行播放,此外一个注意点是到达最后一首,即最大下标后,要转为列表第一首,这也符合现实生活。示例如下:
    •  private void button3_Click(object sender, EventArgs e)
       {
           int nextIndex = listBox1.SelectedIndex + 1;//当前下标加一
           if(nextIndex >= localmusiclist.Count)//超过了回到第一首
           {
               nextIndex = 0;
           }
           axWindowsMediaPlayer1.URL = localmusiclist[nextIndex];
           musicplay(axWindowsMediaPlayer1.URL);
           label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[nextIndex]);
           listBox1.SelectedIndex = nextIndex;
       }

      上一首功能与下一首功能类似,不做过多阐述。

五、出现的问题和改进思路

  • 我们基本实现了对所有音乐文件的播放和基本功能实现,但我们会发现一些问题:
  • 1.在该示例中,ogg文件点击播放后就一直在播放,而且在其播放过程中不能进行其他的操作。
  • 2.我们不能把ogg文件也放在列表即歌单里,跟其他音乐文件一样的进行播放,停止,下一首等功能,这明显与现实生活我们要求的不同。
  • 改进思路:在播放ogg文件时,不能进行其他操作,是因为播放的方式是单线程的,或者说线程被阻塞,不能进行其他操作,所以我们要改进播放方式,并且我们不想ogg文件是单独播放,所以我们在添加文件的时候,扩展到ogg文件也能添加进列表,再将改进后的播放方式也放在musicplay方法中,再在listBox里点击就可以进行播放。注意:由于ogg文件和其他文件属于两种不同的播放方式,所以播放方式不同,停止方式也不同,所以进行操作时,如在列表中当前播放的是ogg,再选择另一首如MP3,一般都要对现在播放的文件进行后缀判断,再进行停止,不然会出现两首歌都播放的情况。

六、改进后功能的实现

  • 6.1 添加音乐文件 

       两个全局变量不变,对button(添加文件)进行修改,修改后示例如下:

  • private void button1_Click(object sender, EventArgs e)
    {
        openFileDialog1.Filter = "选择音频|*mp3;*.wav;*.flac;*.ogg";
        string extension = Path.GetExtension(openFileDialog1.FileName);
        openFileDialog1.Multiselect= true;
        if (files != null)
        {
            Array.Clear(files, 0, files.Length);//范类的约束,置零
        }
        if (openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            //listBox1.Items.Clear();//列表清空
            if (extension == ".ogg")
            {
                files = openFileDialog1.FileNames;
            }
            else
            {
                files = openFileDialog1.FileNames;
            }
    
            string[] array = files;
    
            foreach(string x in array)
            {
                listBox1 .Items.Add(x);
                localmusiclist.Add(x);
    
            }
        }
    }

  • 6.2.播放和暂停控制

       在播放方法musicplay中,将修改后的多线程的播放ogg方法放入里面,并同时注意:此时musicplay 的返回类型有一点变化。并且我们需要将reader和waveOut设为全局变量。并为了实现播放的停止控制,增加一个全局变量isPlaying。代码如下:

private WaveOutEvent waveOut;
private VorbisWaveReader reader;
private bool isPlaying = false;
 public async void musicplay(string filename)//播放
 {
     string extension=Path.GetExtension(filename);//得到它后缀名,可以作扩展,别的文件就可以有分支做别的播放处理
     if (extension == ".ogg")
     {
         Console.WriteLine("this is ogg file.");
         try
         {
             using (reader = new VorbisWaveReader(filename))
             {
                 using (waveOut = new WaveOutEvent())
                 {
                     waveOut.Init(reader);
                     waveOut.Play();
                     isPlaying = true;

                     await Task.Run(() =>
                     {
                         while (waveOut.PlaybackState == PlaybackState.Playing)
                         {
                             Thread.Sleep(100); // 这里仍然需要Sleep,但它在后台线程上运行  
                         }
                     });
                 }
             }
         }
         catch (Exception ex)
         {
             Console.WriteLine("发生错误: " + ex.Message);
         }
     }
     else
     {
         Console.WriteLine("this is not ogg file.");
         axWindowsMediaPlayer1.Ctlcontrols.play();
         isPlaying=true;
     }
 }

      ogg文件不能通过像axWindowsMediaPlayer那样修改了其URL就直接停止播放了,所以我们要定义一个停止的方法,示例如下:

 private void StopAudio()//ogg文件播放停止方法
 {
     if (isPlaying)
     {
         waveOut.Stop();
         waveOut.Dispose();
         reader.Dispose();
         isPlaying = false;
     }
 }

在listBox里实现点击任何一个首歌,停止当前播放的歌曲,再进行播放选择的歌曲,所以要先进性判断,当前播放的是ogg的话,就停止播放。只需这一个判断即可,因为我们后续的操作是将选择的文件放入URL中,如果当前是其他文件,说明是axWindowsMediaPlayer播放的,那么URL修改了,那么就会停止的。示例如下:

        private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (localmusiclist.Count > 0)//保证有音乐文件被选中
            {
                string extension = Path.GetExtension(axWindowsMediaPlayer1.URL);
                if (isPlaying)
                {
                    if (extension == ".ogg")
                    {
                        StopAudio();
                    }
                }
                axWindowsMediaPlayer1.URL = localmusiclist[listBox1.SelectedIndex];
                musicplay(localmusiclist[listBox1.SelectedIndex]);
                label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[listBox1.SelectedIndex]);
            }
        }

当然也可以点击停止button,修改如下:

        private void button2_Click(object sender, EventArgs e)
        {
            string extension = Path.GetExtension(axWindowsMediaPlayer1.URL);
            if (isPlaying)
            {
                if (extension == ".ogg")
                { 
                    StopAudio();
                }
                else
                {
                    axWindowsMediaPlayer1.Ctlcontrols.stop(); 
                }
            }

        }

6.3.下一首和上一首

也比较简单,就是不同的播放方式,所以要先判断当前播放的是什么文件,是ogg 的话,调用停止方法,再跟之前一样。示例如下:

private void button3_Click(object sender, EventArgs e)
{
    string extension = Path.GetExtension(axWindowsMediaPlayer1.URL);
    if (extension == ".ogg")
    {
        StopAudio();
    }
    int nextIndex = listBox1.SelectedIndex + 1;
    if(nextIndex >= localmusiclist.Count)
    {
        nextIndex = 0;
    }
    axWindowsMediaPlayer1.URL = localmusiclist[nextIndex];
    musicplay(axWindowsMediaPlayer1.URL);
    label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[nextIndex]);
    listBox1.SelectedIndex = nextIndex;
}

上一首功能不再赘述。

改进的主要思路重点:线程的改进,ogg加入进来之后两种播放方式的协调。

七、附录

源代码仓库地址:田扬帅/windows程序设计 仓库里的WindowsMusic1即是源码。

感谢收看!

  • 35
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值