目录
前言
WindowsMusicPlayer
是一个音乐播放器,能够播放 .mp3、
.wav
和 .ogg
格式的音乐文件。项目使用 C# 和 Windows Forms 构建,结合 Windows Media Player 控件与 NAudio
库处理不同格式的音频文件。并且加入在线音频播放和音乐下载功能,本文将详细介绍如何用C#为一个现有的音乐播放器添加这两个功能,并对实现过程中的关键代码进行分析。
提示:以下是本篇文章正文内容,下面案例可供参考
一、设计思路
1.1 功能需求分析
- 支持 .mp3 和 .wav 本地文件的播放,使用 Windows Media Player 控件。
- 支持 .ogg 本地文件的播放,使用 NAudio.Vorbis 控件。
- 提供播放列表功能,能够选择多个文件进行播放。
- 提供音量控制和播放控制(停止、下一曲等)。
- 提供用户友好的界面,显示当前播放曲目。
- 具备异常处理,能够处理文件不存在、读取错误等问题 。
- 支持在线音频播放和音乐下载功能。
1.2 主要技术与工具
- Windows Forms:用于构建用户界面。
- Windows Media Player 控件:用于播放 .mp3、.flac 和 .wav 文件。
- NAudio.Vorbis 控件:用于播放 .ogg 文件。
- Visual Studio2022:用于开发与调试。
1.3 项目结构
- Form1.cs:主窗体逻辑代码文件。
- Form1.Designer.cs:主窗体设计文件。
- Form1.resx:主窗体资源文件。
二、代码实现
2.1 窗体设计
窗体布局方面,Form1 包含以下主要控件:Button(用于播放ogg文件、选择本地歌曲、停止播放、播放下一曲和播放在线歌曲)、ListBox(用于显示音乐播放列表)、TrackBar(音量滑动条)、Label(用于显示当前播放的曲目)以及 Windows Media Player 控件(用于播放 .mp3和 .wav 文件)。整体布局如图所示:
2.2 具体控件的作用与代码实现
(1)Button控件
选择本地歌曲按钮(button1
): 用于打开右键菜单选择音乐文件。
private void button1_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "选择音频|*.mp3;*.flac;*.wav";
openFileDialog1.Multiselect = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
localmusiclist.Clear();
listBox1.Items.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);
}
}
}
停止播放按钮(button2
): 用于停止当前播放的音乐文件。其功能是停止 Windows Media Player 控件和 OGG 文件的播放。
private void button2_Click(object sender, EventArgs e)
{
axWindowsMediaPlayer1.Ctlcontrols.stop();
}
播放下一曲按钮(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;
}
}
播放ogg按钮(button4
): 用于播放本地列表中的.ogg音乐文件。
private void button4_Click(object sender, EventArgs e)
{
string oggFilePath= "";
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "播放音频|*.ogg";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
oggFilePath = openFileDialog.FileName;
}
using (var vorbisReader = new VorbisWaveReader(oggFilePath))
{
using (var outputDevice = new WaveOutEvent())
{
outputDevice.Init(vorbisReader);
outputDevice.Play();
// 等待播放完成,或者你可以添加其他逻辑,如用户输入来停止播放等。
while (outputDevice.PlaybackState == PlaybackState.Playing)
{
System.Threading.Thread.Sleep(1000);
}
}
}
}
播放在线歌曲按钮(button5
): 用于播放在线音频文件链接中的歌曲,当用户点击按钮时,会调用DownloadAudioAsync
方法异步下载在线音频文件。
private async void button5_Click(object sender, EventArgs e)
{
await PlayOnlineAudio(onlineAudioUrl);
}
private const string onlineAudioUrl = "http://tool.liumingye.cn/music/?page=homePage";
(2)ListBox
控件
音乐播放列表(listBox1
): 用于显示所有选中的音乐文件。当选择某一项时,播放对应的音乐文件。
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]);
}
}
(3)TrackBar 控件
音量滑动条(trackBar1
): 用于音量控制,通过滑动条调整音量百分比,设置音乐播放器的音量。
private void trackBar1_Scroll(object sender, EventArgs e)
{
axWindowsMediaPlayer1.settings.volume=trackBar1.Value;
}
(4)Label 控件
当前曲目标签(label1
): 用于显示当前播放的音乐曲目名称。
2.3 整体代码
using NAudio.Wave;
using NAudio.Vorbis;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Un4seen.Bass;
using System.Net.Http;
namespace WindowsMusic2
{
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 { axWindowsMediaPlayer1.Ctlcontrols.play();}
}
private void button1_Click(object sender, EventArgs e)
{
openFileDialog1.Filter = "选择音频|*.mp3;*.flac;*.wav";
openFileDialog1.Multiselect = true;
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
localmusiclist.Clear();
listBox1.Items.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];
musicplay(axWindowsMediaPlayer1.URL);
label1.Text = Path.GetFileNameWithoutExtension(localmusiclist[index]);
listBox1.SelectedIndex=index;
}
}
private void button4_Click(object sender, EventArgs e)
{
string oggFilePath= "";
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Filter = "播放音频|*.ogg";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
oggFilePath = openFileDialog.FileName;
}
using (var vorbisReader = new VorbisWaveReader(oggFilePath))
{
using (var outputDevice = new WaveOutEvent())
{
outputDevice.Init(vorbisReader);
outputDevice.Play();
// 等待播放完成,或者你可以添加其他逻辑,如用户输入来停止播放等。
while (outputDevice.PlaybackState == PlaybackState.Playing)
{
System.Threading.Thread.Sleep(1000);
}
}
}
/*
using (var reader = new AudioFileReader(oggFilePath))
{
using (var outputDevice = new WaveOutEvent())
{
outputDevice.Init(reader);
outputDevice.Play();
// 等待播放完毕或手动停止
while (outputDevice.PlaybackState == PlaybackState.Playing)
{
System.Threading.Thread.Sleep(100);
}
}
}*/
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
}
private void axWindowsMediaPlayer1_Enter(object sender, EventArgs e)
{
}
private async void DownloadAndPlayButton_Click(object sender, EventArgs e)
{
string url = textBox1.Text;
if (string.IsNullOrEmpty(url))
{
MessageBox.Show("请输入有效的URL!");
return;
}
try
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);
if (response.IsSuccessStatusCode)
{
var audioFileStream = await response.Content.ReadAsStreamAsync();
PlayAudioFromStream(audioFileStream);
}
else
{
MessageBox.Show("无法下载音频文件:" + response.StatusCode);
}
}
}
catch (Exception ex)
{
MessageBox.Show("发生错误:" + ex.Message);
}
}
private void PlayAudioFromStream(Stream audioStream)
{
// 尝试检测音频格式。在这个例子中,我们假设它是WAV或PCM。
IWaveProvider waveProvider = null;
// 如果流是WAV格式,我们可以直接使用WaveFileReader
if (Path.GetExtension(GetContentTypeFromStream(audioStream)).Equals(".wav", StringComparison.OrdinalIgnoreCase))
{
waveProvider = new WaveFileReader(audioStream);
}
// 如果知道PCM参数(如采样率、通道数、位深度),可以直接使用BufferedWaveProvider
else
{
throw new NotSupportedException("不支持的音频格式。");
}
using (var outputDevice = new WaveOutEvent())
{
outputDevice.Init(waveProvider);
outputDevice.Play();
outputDevice.PlaybackStopped += (sender, e) => { /* 处理播放停止事件 */ };
}
// PS:由于outputDevice在using块中,如果尚未释放的话,当播放完成时它会自动被释放
// 因此,不需要在这里调用Dispose或其他清理逻辑
}
private string GetContentTypeFromStream(Stream stream)
{
throw new NotSupportedException("不能直接从流中获取内容类型。");
}
private async void button5_Click(object sender, EventArgs e)
{
await PlayOnlineAudio(onlineAudioUrl);
}
private const string onlineAudioUrl = "http://tool.liumingye.cn/music/?page=homePage";
private async Task PlayOnlineAudio(string audioUrl)
{
try
{
// 使用AudioFileReader从URL读取音频
using (var reader = new NAudio.Wave.AudioFileReader(audioUrl))
{
// 创建音频输出设备
using (var outputDevice = new NAudio.Wave.WaveOutEvent())
{
// 初始化输出设备
outputDevice.Init(reader);
// 播放音频
outputDevice.Play();
// 等待播放完成,这里使用异步等待,以免阻塞UI线程
while (outputDevice.PlaybackState == NAudio.Wave.PlaybackState.Playing)
{
// 这里使用Invoke确保UI线程更新
if (this.InvokeRequired)
{
this.Invoke(new Action(() =>
{
// 这里可以添加更新UI的代码,例如缓冲进度条
}));
}
// 短暂等待后再次检查播放状态
await Task.Delay(100);
}
// 播放完成后,可以在这里添加清理逻辑
}
}
}
catch (Exception ex)
{
// 处理异常,例如网络问题或格式不支持等
MessageBox.Show("播放失败: " + ex.Message);
}
}
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;
}
private void textBox1_TextChanged_1(object sender, EventArgs e)
{
}
}
}
三、运行结果
总结
归纳与总结
在现有的程序基础上,我通过导入第三方库学习了更多控件的使用和互相联系,加深了对C#控件使用的理解。目前程序已经实现了多种常见音乐格式如MP3、OGG以及在线音乐的播放,但仍有一些不足之处:
- 不支持APE、AAC、NCM等部分新型或特有的音乐格式。
- 播放控制功能有限: 当前的播放器缺少一些常见的播放控制功能。
- 音频信息显示不完善: 播放器目前对于音频文件的信息显示比较简单。
改进建议
1.支持更多音频格式:
- 研究并集成第三方解码库,如ffmpeg、libmpg123等,以支持APE、AAC、NCM等格式。
- 如有必要,可尝试自行编写解码器模块,深入学习音频编解码原理。
2.加入高级音频处理功能:
- 利用NAudio等库提供的音频效果处理功能,开发均衡器、混响、压限等音效调节功能。
- 学习数字信号处理(DSP)相关知识,尝试自行实现某些音频效果算法。
3.完善播放控制功能:
- 添加播放速度调节功能,允许用户在0.5x到2x之间改变播放速度。
- 实现AB段循环播放,方便用户反复聆听某个片段。
- 支持显示歌词,并与播放进度同步滚动。