前言
实现.mp3、.ogg类型音乐文件的播放。
一、功能说明
请编写一个C#程序,实现音乐文件的播放功能,要求如下:
- 程序应能够读取MP3文件和OGG文件,并播放其中的音频。
- 程序应能够处理可能出现的异常,如文件不存在、文件读取错误等。
- 程序应具有良好的用户界面,方便用户进行操作。
- 程序应具有良好的兼容性,能在不同版本的C#中正常运行。
二、窗体设计
音乐播放功能应该包含音乐文件获取、显示音乐列表、显示当前音乐、开始播放、停止播放、切换到下一首、调节音量等功能。
根据上述功能,按照个人喜好完成窗体设计。
如上图
button1修改为“选择歌曲”,用于播放mp3音乐文件。
button2修改为“停止播放”,用于停止音乐文件。
button3修改为“下一曲”,用于切换到下一个音乐文件。
button4修改为“播放ogg”,用于播放ogg音乐文件。
listBox1用于显示音乐播放列表。
label1用于显示当前播放音乐名称。
openFileDialog1用于获取音乐文件。
为了使音乐播放器的界面更加接近现有的播放器,可以在解决方案资源管理器中,添加axWindowsMediaPlayer控件,实现音乐的播放、暂停、音量调节等操作。
添加axWindowsMediaPlayer控件步骤如下:
- 在“工具”中,选择“选择工具箱项”
- 在“选择工具箱项”窗口中,选择“COM组件”
- 在列表中找到“Windows Media Player”并打勾,点击确认
- 工具箱中出现“Windows Media Player”控件,即可使用
axWindowsMediaPlayer控件中已经有音量调节功能,但调节界面太小,可以新引入一个trackBar控件来调节音量。
由于trackBar控件默认样式为水平方向上左右调节,音量调节一般为上下调节,因此可以将属性页面中调节外观的Orientation属性由"Horizontal'改为"Vertical",即可调整为上下调节。
三、代码实现
1、代码分步讲解
(1)添加依赖项NAudio包和NAudio.Vorbis包
NAudio库和NAudio.Vorbis库提供了丰富的音频处理功能,NAudio.Vorbis库中的VorbisWaveReader类能够帮助实现.ogg音频文件的播放。
添加NAudio包和NAudio.Vorbis包步骤如下:
- 在“解决方案资源管理器”中,右键点击项目名称,选择“管理NuGet程序包”
- 在“NuGet包管理器”中搜索“NAudio”
- 点击“NAudio”后显示详细信息,点击“安装”
- 在“预览更改”中点击应用,等待下载即可
- “NAudio.Vorbis”下载方法相同
下载完成后即可在程序中使用:
using NAudio;
using NAudio.Wave;
using NAudio.Vorbis;
(2)定义音频名称和音频列表
首先定义一个字符串,用于储存音频名称。
要想音乐播放器能够实现选取多个音乐文件,需要一个列表localmusiclist,来储存选择的多个文件。
定义一个字符串列表,用于存储多个音频文件。
代码如下:
string[] files;
List<string> localmusiclist = new List<string> { };
(3).mp3音乐播放方法
播放音乐首先要接收需要播放的音乐文件,因此需要出入参数filename。
音乐播放需要用到Windows Media Player控件,则要将这个控件的URL属性设置为这个传入的filename参数。(URL属性用于指定要播放的音频文件的本地路径或网络位置)
URL属性设置完成后,Windows Media Player控件已经准备播放传入的音乐文件。
axWindowsMediaPlayer1.URL = filename;
接下来则需要判断传入文件的类型,是否是正确的音频文件,判断是MP3音乐文件还是OGG音乐文件。
判断文件类型需要将文件的扩展名单独提取出来,通过判断扩展名格式,来区分文件类型。
string extension = Path.GetExtension(filename);
如果是.ogg文件,那么需要别的音乐播放方法来处理这个文件;
如果是,mp3文件,那么开始播放这个音乐文件,调用Windows Media Player控件的Ctlcontrols来播放这个音乐文件:axWindowsMediaPlayer1.Ctlcontrols.play();
否则无法播放。
这里使用WriteLine,在标准的Windows Forms应用程序中不会显示任何内容,能够拒绝播放。
if (extension == ".ogg")
{
Console.WriteLine("这是.ogg文件");
}
else if (extension == ".mp3")
{
axWindowsMediaPlayer1.Ctlcontrols.play();
}
else
{
Console.WriteLine("无法识别的文件格式");
}
至此,.mp3音乐文件播放方法已经完成,以下是完整代码:
private void musicplay(string filename)
{
axWindowsMediaPlayer1.URL = filename;
string extension = Path.GetExtension(filename);
if (extension == ".ogg")
{
Console.WriteLine("这是.ogg文件");
}
else if (extension == ".mp3")
{
axWindowsMediaPlayer1.Ctlcontrols.play();
}
else
{
Console.WriteLine("无法识别的文件格式");
}
}
(4)button1:选择MP3音乐文件
在窗口设计中双击button1,实现选择音乐功能。
这个控件用来选择.mp3音乐文件,因此首先要过滤其余文件,只能选择.mp3文件。
用openFileDialog1.Filter设置openFileDialog1的过滤器,只显示.mp3文件,并且在对话框中显示“选择音频”,代码如下:
openFileDialog1.Filter = "选择音频|*.mp3";
选择音乐文件时的对话框如下:
此时会完全过滤除了.mp3文件以外的所有类型文件(文件夹不会被过滤掉),并且会显示“选择音频”的文字。
如果需要播放的不仅是.mp3类型的音乐文件,还能播放.flac和.wav等类型的音乐文件,还能接着往后添加各种类型的过滤条件。修改代码如下:
openFileDialog1.Filter = "选择音频|*.mp3;*.flac;*.wav";
修改之后即可显示.mp3 .flac .wav这三个类型的音乐文件
在选择文件的时候,我们需要选择多个文件,因此需要代码如下:
openFileDialog1.Multiselect = true;
选择音频文件之后,如果用户点击“确认”按钮,则继续进行下一步,获取音频列表。
想要获取用户选择的音频文件,首先就要清除原来的旧列表。
listBox1中的所有项目和localmusiclist中的所有字符串都需要清除,代码如下:
listBox1.Items.Clear();
localmusiclist.Clear();
不仅需要清除listBox1和localmusiclist中的项目,还需要检查files中是否为空,不为空则要完全清除,否则不能将音频文件传输给files变量。
if (files != null)
{
Array.Clear(files, 0, files.Length);//把array里面的所有元素设置为默认,并清空
}
将files清空之后,我们可以将选取的多个音频文件传给files变量,为了避免foreach循环会直接修改files,创建一个新的数组array,用于存放多个音频文件,代码如下:
files = openFileDialog1.FileNames;
string[] array = files;
再用foreach来遍历数组中的所有元素,并分别存入listBox1的列表和localmusiclist列表中,代码如下:
foreach (string x in array)
{
listBox1.Items.Add(x);
localmusiclist.Add(x);//选择多个音乐文件,能以行显示
}
button1的功能已经完成,完整代码如下:
private void button1_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "选择音频|*.mp3";
openFileDialog1.Multiselect = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
listBox1.Items.Clear();
localmusiclist.Clear();
if (files != null)
{
Array.Clear(files, 0, files.Length);
}
files = openFileDialog1.FileNames;
string[] array = files;
foreach (string x in array)
{
listBox1.Items.Add(x);
localmusiclist.Add(x);
}
}
}
(5)listbox1:显示音频列表
如果选择的音频文件数量大于0,则根据当前listBox1中的索引来确定当前播放的音乐,然后将axWindowsMediaPlayer1控件的URL属性设置为当前播放的音乐文件,以便播放该音乐,代码如下:
axWindowsMediaPlayer1.URL = localmusiclist[listBox1.SelectedIndex];
设置好URL属性值之后,即可调用musicplay音频播放方法。
传递URL参数,实现音频播放,代码如下:
musicplay(axWindowsMediaPlayer1.URL);
开始播放音频之后,label1需要显示当前播放音频文件的名称。
此时可以根据当前正在播放的音频文件的文件路径,获取扩展名以外的音频文件名,赋值给label1的文本Text,则可以实现label1中显示这个除去扩展名以外的音频文件名,代码如下:
label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[listBox1.SelectedIndex]);
完整代码如下:
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (localmusiclist.Count > 0)
{
axWindowsMediaPlayer1.URL = localmusiclist[listBox1.SelectedIndex];
musicplay(axWindowsMediaPlayer1.URL);
label1.Text=
Path.GetFileNameWithoutExtension(localmusiclist[listBox1.SelectedIndex]);
}
}
label1和listBox1的显示效果如下:
(6)button2:音频停止播放
使用axWindowsMediaPlayer控件中的Ctlcontrols来停止播放。
private void button2_Click(object sender, EventArgs e)
{
axWindowsMediaPlayer1.Ctlcontrols.stop();
}
(7)button3:音频切换到下一曲
如果音频文件的数量大于0,那么先将文件列表的索引加1,获取下一个音频文件。
int index = listBox1.SelectedIndex + 1;
但这存在一个问题,如果已经使最后一个音频文件,那么索引加1后,没有音频文件。
按照我们的习惯,播放到最后一个音频文件之后,下一曲应该回到第一曲。因此当索引加1后超出音频文件的数量,则意味着已经到了最后一个音频,需要将索引置为0。代码如下:
if (index >= localmusiclist.Count())
{
index = 0;
}
完成以上操作之后,则可以将新的索引对应的音频文件赋值给axWindowsMediaPlayer控件的URL,调用musicplay方法,实现播放下一曲。代码如下:
axWindowsMediaPlayer1.URL = localmusiclist[index];
musicplay(axWindowsMediaPlayer1.URL);
修改完当前播放的音频之后,还需要修改label1中显示的文本,并将listBox1的选取对象修改为当前播放的音乐。代码如下:
label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[index]);
listBox1.SelectedIndex = index;
button3的功能已经完成,完整代码如下:
private void button3_Click(object sender, EventArgs e)
{
if (localmusiclist.Count > 0)
{
int index = listBox1.SelectedIndex + 1;
if (index >= localmusiclist.Count())
{
index = 0;
}
axWindowsMediaPlayer1.URL = localmusiclist[index];
musicplay(axWindowsMediaPlayer1.URL);
label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[index]);
listBox1.SelectedIndex = index;
}
}
(8)trackBar1:实现音量调节
将trackBar1控件设置为音量调节,代码如下:
private void trackBar1_Scroll(object sender, EventArgs e)
{
axWindowsMediaPlayer1.settings.volume = trackBar1.Value;
}
由于我们习惯将音量大小范围设置为0~100,因此可以调整trackBar1控件的属性,使最大值为100,最小值为0,这样更加接近我们的使用习惯。
(9)button4:播放.ogg音频文件
和播放.mp3音频文件的步骤相同,首先获取音频文件地址。
创建一个新的OpenFileDialog实例,用openFileDialog.Filter过滤其余类型文件,在对话框中选取.ogg文件,用户点击“确认”之后将文件地址赋值给对应的变量oggFilePath。
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "打开音频|*.ogg";
string oggFilePath = "";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
oggFilePath = openFileDialog.FileName;
}
选择好文件之后,就可以播放文件了。使用VorbisWaveReader类来读取oggFilePath指定的 .ogg 音频文件。
再创建一个WaveOutEvent实例,用于播放音频。
初始化outputDevice,使用reader作为音频源,然后使用play函数来播放选取的音频源。
即可实现.ogg音频文件的播放,代码如下:
using (var reader = new VorbisWaveReader(oggFilePath))
{
using (var outputDevice = new WaveOutEvent())
{
outputDevice.Init(reader);
outputDevice.Play();
}
}
为了确认音频当前时刻是否还在播放,可以在播放音频文件之后,使用while循环来检查。
如果还在播放,则线程休眠100毫秒后再次检查。
while (outputDevice.PlaybackState == PlaybackState.Playing)
{
System.Threading.Thread.Sleep(100);
}
之前赋值的outputDevice 和 reader不需要再次进行处理。因为它们被包含在 using 语句块中,因此音频播放完毕后,outputDevice 和 reader 对象会被自动释放 。
完整代码如下:
private void button4_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "打开音频|*.ogg";
string oggFilePath = "";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
oggFilePath = openFileDialog.FileName;
}
using (var reader = new VorbisWaveReader(oggFilePath))
{
using (var outputDevice = new WaveOutEvent())
{
outputDevice.Init(reader);
outputDevice.Play();
while (outputDevice.PlaybackState == PlaybackState.Playing)
{
System.Threading.Thread.Sleep(100);
}
}
}
}
2、完整代码
using System.Diagnostics.Eventing.Reader;
using System.Threading;
using NAudio;
using NAudio.Wave;
using NAudio.Vorbis;
using System.Diagnostics;
namespace WinFormsApp4._23_1
{
public partial class Form1 : Form
{
string[] files;
List<string> localmusiclist = new List<string> { };
public Form1()
{
InitializeComponent();
}
private void musicplay(string filename)
{
axWindowsMediaPlayer1.URL = filename;
string extension = Path.GetExtension(filename);
if (extension == ".ogg") { Console.WriteLine("这是.ogg文件"); }
else if (extension == ".mp3")
{
axWindowsMediaPlayer1.Ctlcontrols.play();
}
else
{
Console.WriteLine("无法识别的文件格式");
}
}
private void button1_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "选择音频|*.mp3";//;*.flac;*.wav
openFileDialog1.Multiselect = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
listBox1.Items.Clear();
localmusiclist.Clear();
if (files != null)
{
Array.Clear(files, 0, files.Length);
}
files = openFileDialog1.FileNames;
string[] array = files;
foreach (string x in array)
{
listBox1.Items.Add(x);
localmusiclist.Add(x);
}
}
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (localmusiclist.Count > 0)
{
axWindowsMediaPlayer1.URL = localmusiclist[listBox1.SelectedIndex];
musicplay(axWindowsMediaPlayer1.URL);
label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[listBox1.SelectedIndex]);
}
}
private void trackBar1_Scroll(object sender, EventArgs e)
{
axWindowsMediaPlayer1.settings.volume = trackBar1.Value;
}
private void button2_Click(object sender, EventArgs e)
{
axWindowsMediaPlayer1.Ctlcontrols.stop();
}
private void button3_Click(object sender, EventArgs e)
{
if (localmusiclist.Count > 0)
{
int index = listBox1.SelectedIndex + 1;
if (index >= localmusiclist.Count())
{
index = 0;
}
axWindowsMediaPlayer1.URL = localmusiclist[index];
//axWindowsMediaPlayer1.Ctlcontrols.play();
musicplay(axWindowsMediaPlayer1.URL);
label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[index]);
listBox1.SelectedIndex = index;
}
}
private void button4_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "打开音频|*.ogg";
string oggFilePath = "";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
oggFilePath = openFileDialog.FileName;
}
using (var reader = new VorbisWaveReader(oggFilePath))
{
using (var outputDevice = new WaveOutEvent())
{
outputDevice.Init(reader);
outputDevice.Play();
while (outputDevice.PlaybackState == PlaybackState.Playing)
{
System.Threading.Thread.Sleep(100);
}
}
}
}
}
}
四、代码分析
1、基本思路
首先确定功能需求,本次实验需要播放.mp3和.ogg文件,并且需要实现基本音乐播放器的功能,包括选择音频文件、停止播放、调整音量、切换下一首等功能。
确定有哪些功能之后即可开始进行窗体设计,界面干净整洁便于用户使用。
设计完成后,将每个控件的功能进行实现即可。
2、难点分析
- 下一曲循环播放:下一曲不能仅仅只是索引加1,还需要考虑到现实生活中,列表的循环播放。
- ogg音频转换:.ogg音频文件播放需要借助NAudio库来播放,需要额外下载并安装第三方库。
- 选取多个音频文件:需要借助list列表来存储多个音频文件,以此来实现多个音频文件的选取,并利用索引来确定当前播放的音乐文件。
下面是gitee的clone地址: