前言:
上次介绍了音乐播放器播放.mp3和,ogg文件,现在在该音乐播放器中新增在线播放和音乐下载功能。本文将详细介绍如何用C#为一个现有的音乐播放器添加这两个功能,并对实现过程中的关键代码进行分析。
一、需求分析
首先,我们需要明确在线播放与音乐下载功能的需求:
- 在线播放:用户可以通过输入音乐链接或搜索关键词来播放在线音乐。
- 音乐下载:用户可以选择将在线音乐下载到本地进行保存。
二、技术选型
为了实现上述功能,我们需要用到以下技术:
- C#:作为主要的编程语言,用于编写音乐播放器的逻辑。
- HttpClient:用于发起HTTP请求,获取在线音乐数据。
- NAudio:用于处理音频数据的播放。
- FileStream:用于处理文件写入,实现音乐下载功能。
三、实现步骤
(1)引入命名空间
using System.Diagnostics.Eventing.Reader;
using System.Threading;
using NAudio;
using NAudio.Wave;
using NAudio.Vorbis;
using System.Net;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
这部分代码引入了所需的命名空间,以便在程序中使用相关的类和方法。具体来说:
System.Diagnostics.Eventing.Reader
:这个命名空间包含了用于读取事件日志的类和方法。System.Threading
:这个命名空间包含了多线程编程的相关类和方法,如线程池、锁等。NAudio
:这是一个音频处理库,提供了音频播放、录制、转换等功能。NAudio.Wave
:这是NAudio
库中的一个子命名空间,提供了对WAV格式音频文件的处理功能。NAudio.Vorbis
:这是NAudio
库中的一个子命名空间,提供了对Ogg Vorbis格式音频文件的处理功能。System.Net
:这个命名空间包含了网络编程的相关类和方法,如HTTP请求、FTP上传下载等。System.Text
:这个命名空间包含了文本处理的相关类和方法,如字符串操作、编码解码等。System.Net.Http
:这个命名空间包含了基于HTTP协议的网络编程相关类和方法,如HTTP客户端、HTTP响应等。System.Threading.Tasks
:这个命名空间包含了异步编程的相关类和方法,如任务、异步方法等。System.Text.RegularExpressions
:这个命名空间包含了正则表达式的相关类和方法,用于进行字符串匹配和替换等操作。
(2)设置连接的网站:
private const string audioUrl = "http://tool.liumingye.cn/music/?page=homePage";
这部分代码定义了一个常量字符串audioUrl
,它表示要下载音频文件的网址。这个网址是一个在线音频文件的链接。
(3)点击按键事件处理
private async void button1_Click(object sender, EventArgs e)
{
await DownloadAudioAsync(audioUrl);
}
button1_Click
方法:处理打开文件对话框选择音频文件的事件。当用户点击按钮时,会弹出一个文件选择对话框,用户可以选择一个或多个音频文件。选择的文件路径会被存储在localmusiclist
列表中,并显示在listBox1
列表框中。
button2_Click
方法:处理播放/暂停/停止音频的事件。当用户点击按钮时,会根据当前的状态切换音频的播放状态。
button3_Click
方法:处理下一首音频的事件。当用户点击按钮时,会选择listBox1
列表框中的下一首音频进行播放。
button5_Click
方法:处理下载在线音频文件的事件。当用户点击按钮时,会调用DownloadAudioAsync
方法异步下载在线音频文件。
这里用的例子是button1的点击事件处理方法
(4)下载音乐文件方法
private async Task DownloadAudioAsync(string url)
{
using (var httpClient = new HttpClient())
{
try
{
// 发起 GET 请求并获取响应
using (var response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode(); // 确保响应成功
using (var stream = await response.Content.ReadAsStreamAsync())
{
// 获取文件名,如果服务器没有提供文件名,可以自行指定
var fileName = GetFileName(response);
// 指定保存文件的路径,这里保存在当前用户的下载目录下
var filePath = Path.Combine(@"D:\Desktop", fileName);
// 使用 FileStream 写入文件
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
await stream.CopyToAsync(fileStream);
}
MessageBox.Show("下载完成:" + filePath, "下载成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
catch (Exception ex)
{
MessageBox.Show($"下载失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
这部分代码定义了一个异步方法DownloadAudioAsync
,用于异步下载在线音频文件。它接收一个网址参数,然后使用HttpClient
发起GET请求获取响应。如果响应成功,它将从响应内容中读取音频数据,并将其保存到本地文件中。如果在下载过程中发生异常,将显示错误消息框。具体来说:
httpClient
是一个HttpClient
对象,用于发送HTTP请求和接收HTTP响应。
response
是一个HttpResponseMessage
对象,表示HTTP响应。
stream
是一个Stream
对象,表示HTTP响应的内容流。
fileName
是一个字符串变量,表示要保存的文件名。
filePath
是一个字符串变量,表示要保存的文件路径。
fileStream
是一个FileStream
对象,用于将HTTP响应的内容流写入到本地文件中。
四、完整代码
using System.Diagnostics.Eventing.Reader;
using System.Threading;
using NAudio;
using NAudio.Wave;
using NAudio.Vorbis;
using System.Diagnostics;
using System.Net;
using System.Text;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
namespace WinFormsApp
{
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);//把array里面的所有元素设置为默认,并清空
}
files = openFileDialog1.FileNames;//这个选择多个文件,filename选择一个文件
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];
//axWindowsMediaPlayer1.Ctlcontrols.play();
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);
}
}
}
}
private void axWindowsMediaPlayer1_Enter(object sender, EventArgs e)
{
}
private const string audioUrl = "http://tool.liumingye.cn/music/?page=homePage";
private async void button5_Click(object sender, EventArgs e)
{
await DownloadAudioAsync(audioUrl);
}
private async Task DownloadAudioAsync(string url)
{
using (var httpClient = new HttpClient())
{
try
{
// 发起 GET 请求并获取响应
using (var response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
{
response.EnsureSuccessStatusCode(); // 确保响应成功
using (var stream = await response.Content.ReadAsStreamAsync())
{
// 获取文件名,如果服务器没有提供文件名,可以自行指定
var fileName = GetFileName(response);
// 指定保存文件的路径,这里保存在当前用户的下载目录下
var filePath = Path.Combine(@"D:\Desktop", fileName);
// 使用 FileStream 写入文件
using (var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None))
{
await stream.CopyToAsync(fileStream);
}
MessageBox.Show("下载完成:" + filePath, "下载成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
catch (Exception ex)
{
MessageBox.Show($"下载失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private string GetFileName(HttpResponseMessage response)
{
// 从响应头或 URL 中获取文件名
string fileName = null;
if (!string.IsNullOrEmpty(response.Content.Headers.ContentDisposition?.FileName))
{
fileName = response.Content.Headers.ContentDisposition.FileName;
}
else
{
Uri.TryCreate(response.RequestMessage.RequestUri.ToString(), UriKind.Absolute, out Uri uri);
fileName = Path.GetFileName(uri.LocalPath);
}
return fileName;
}
}
}
五、代码难点分析
-
音频文件格式识别:根据文件扩展名判断音频文件格式,需要处理没有扩展名或无法识别的文件格式。
-
音频播放控制:使用
axWindowsMediaPlayer1
控件播放音频,需要掌握其API使用方法,如URL
属性设置音频文件路径,Ctlcontrols.play()
方法播放音频等。 -
列表框与音频文件关联:通过
listBox1
显示已选择的音频文件列表,需要实现列表框中选中项与音频文件的关联,以便在播放时正确选择对应的音频文件。 -
异步下载:使用
HttpClient
异步下载在线音频文件,需要理解异步编程的概念,如async
和await
关键字的使用,以及Task
类的使用。 -
异常处理:在下载过程中可能会遇到异常,如网络错误、文件写入错误等,需要对异常进行处理,避免程序崩溃。