Mp3播放器源码赏析及问题

sxldfang 2010-11-12 10:16:27

问题:在一个线程中成功搜索一Mp3播放时,界面无法显示播放进度。如何来正确显示进度呢?

==============================================================================

Form1.cs 窗体 -> 显示Mp3的播放进度

窗体上有一“命令按钮Button”、“进度条trackBar”及“定时器Timer”


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace netMp3Player
{
public partial class Form1 : Form
{
mciMusicPlayer mp3Player; // 播放音乐的对象
searchAndPlay findPlay = new searchAndPlay(); // 查找并播放音乐

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
// 成功“找到音乐网址并播放”后执行的事件处理程序
findPlay.findPlayOk += new FindFileEventHandler(findPlay_findPlayOk);
}

private void button1_Click(object sender, EventArgs e)
{
findPlay.Search();
}

void findPlay_findPlayOk(mciMusicPlayer mp)
{
// 此时检测 mp 对象,一切正常,将其赋值给 mp3Player
this.Invoke(new System.Action<object>(delegate
{
mp3Player = mp;
trackBar1.Maximum = mp3Player.Length;
timer1.Interval = 1000;
timer1.Enabled = true;
}), 1);
}

private void timer1_Tick(object sender, EventArgs e)
{
// 这里对 mp3Player 对象的操作是错误的,mp3Player.Position总是0,Why?
trackBar1.Value = mp3Player.Position;
}
}
}




searchAndPlay.cs 类文件 -> 用于从Internet 网上搜索Mp3并播放


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace netMp3Player
{
delegate void FindFileEventHandler(mciMusicPlayer mp); // 事件委托

class searchAndPlay
{
public event FindFileEventHandler findPlayOk; // 声明“事件”
mciMusicPlayer mp = new mciMusicPlayer();

public void Search() // 要保证该方法快速返回,所以启动另一线程执行“查找并播放”这一耗时的任务
{
Thread doWork = new Thread(findAndPlay);
doWork.SetApartmentState(ApartmentState.STA);
doWork.IsBackground = true;
doWork.Start();
}

void findAndPlay() // 耗时的任务
{
Worker();
if (findPlayOk != null) // 若定义有事件处理程序
{
findPlayOk(mp); // 则执行事件处理程序
}
}

void Worker()
{
// 可能要化很长事件才能完成的工作,// 实际播放的文件会来自网络
mp.Open(@"e:\mp3\情人.mp3");
if (mp.Status == mciMusicPlayer.PlayStatus.Stopped)
{
mp.Play();
}
}
}
}




mciMusicPlayer.cs 类文件 -> 用于播放 Mp3


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace netMp3Player
{
class mciMusicPlayer
{
[DllImport("winmm.dll", EntryPoint = "mciSendString", CharSet = CharSet.Auto)]
public static extern int mciSendString(string lpstrCommand, string lpstrReturnString, int uReturnLength, int hwndCallback);

[DllImport("winmm.dll", EntryPoint = "mciGetErrorString", CharSet = CharSet.Auto)]
public static extern int mciGetErrorString(int errCode, string lpstrReturnString, int uReturnLength);

[DllImport("winmm.dll", EntryPoint = "waveOutSetVolume", CharSet = CharSet.Auto)]
public static extern int waveOutSetVolume(uint deviceID, uint Volume); //参数为uint

public enum PlayStatus // 播放状态
{
Playing, Paused, Stopped, NoPlayFile
}

private static int bm = 0;
private string Name = ""; // 存放播放的文件名
private string buff = "................................................................................................................................";
private int errCode; // 执行mciSendString返回的错误码
private string device;

public mciMusicPlayer()
{
++bm;
device = "music" + bm;
}

public string Message // 根据错误码返回错误信息字符串
{
get
{
mciGetErrorString(errCode, buff, buff.Length);
int p = buff.IndexOf('\0');
return buff.Substring(0, p);
}
}

public bool Open(string fileName) // 打开一个要播放的文件
{
bool b = false;
try
{
if (Status != PlayStatus.NoPlayFile) Close();
errCode = mciSendString("open \"" + fileName + "\" alias " + device, buff, buff.Length, 0);
b = errCode == 0;
}
finally
{
if (b) Name = fileName;
else Name = string.Empty;
}
return b;
}

public void Play() // 播放 - playing
{
errCode = mciSendString("play " + device, buff, buff.Length, 0); // play NetMp3 repeat 循环播放
}

public void Stop() // 停止 - stopped
{
Position = 0; // mciSendString("stop NetMp3", buff, buff.Length, 0);
}

public void Pause() // 状态将变为 - paused
{
errCode = mciSendString("pause " + device, buff, buff.Length, 0);
}

public void Continue() // 状态将变为 - playing
{
errCode = mciSendString("resume " + device, buff, buff.Length, 0);
}

public void Close() // 状态将变为 - 空
{
errCode = mciSendString("close " + device, buff, buff.Length, 0);
//errCode = mciSendString("close all", buff, buff.Length, 0);
}

public string FileName // 播放的文件
{
get
{
return Name;
}
set
{
Open(value);
}
}

public PlayStatus Status // 获取当前状态: playing / paused / stopped / 空
{
get
{
if (string.IsNullOrEmpty(Name)) return PlayStatus.NoPlayFile;
PlayStatus status;
errCode = mciSendString("status " + device + " mode", buff, buff.Length, 0);
int p = buff.IndexOf('\0');
string s = buff.Substring(0, p);
switch (s)
{
case "playing": status = PlayStatus.Playing; break;
case "paused": status = PlayStatus.Paused; break;
default: status = PlayStatus.Stopped; break;
}
return status;
}
}


public int Length // 歌曲总长度(毫秒)
{
get
{
errCode = mciSendString("status " + device + " length", buff, buff.Length, 0);
int p = buff.IndexOf('\0');
string s = buff.Substring(0, p);
if (string.IsNullOrEmpty(s)) return 0;
return int.Parse(s);
}
}

public int Position // 播放位置(毫秒)
{
get
{
errCode = mciSendString("status " + device + " position", buff, buff.Length, 0);
int p = buff.IndexOf('\0');
string s = buff.Substring(0, p);
if (string.IsNullOrEmpty(s)) return 0;
return int.Parse(s);
}
set
{
errCode = mciSendString("seek " + device + " to " + value, buff, buff.Length, 0);
}
}

public int Volume // 音量 0 ~ 1000
{
get
{
errCode = mciSendString("status " + device + " volume", buff, buff.Length, 0);
int p = buff.IndexOf('\0');
string s = buff.Substring(0, p);
if (string.IsNullOrEmpty(s)) return 0;
return int.Parse(s);
}
set
{
errCode = mciSendString("setaudio " + device + " volume to " + value, buff, buff.Length, 0);
}
}

public uint LeftRightVolume // 分别控制左右声道的音量
{
set
{
waveOutSetVolume(0, value); // 0xffffffff,高2字节控制右声道,低2字节控制左声道。
}
}
}
}

All Over
...全文
731 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
hongdongfang 2010-11-12
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 hongdongfang 的回复:]
关注,能播放比如 “http://dm717.v.iask.com/fs/709/1/95a3476cc7b048660c9325abb7ac8a0f39795345/mp3/9_2268456.mp3”这样的Mp3吗?
[/Quote]

试了一下,还真能播放,就是进度条不会动,等着看楼下的解答!
bloodish 2010-11-12
  • 打赏
  • 举报
回复
PInvoke感觉有点问题
[DllImport("winmm.dll", EntryPoint = "mciSendString", CharSet = CharSet.Auto)]
public static extern int mciSendString(string lpstrCommand, string lpstrReturnString, int uReturnLength, int hwndCallback);


string lpstrReturnString 改成 StringBuilder lpstrReturnString


你再试试
hongdongfang 2010-11-12
  • 打赏
  • 举报
回复
关注,能播放比如 “http://dm717.v.iask.com/fs/709/1/95a3476cc7b048660c9325abb7ac8a0f39795345/mp3/9_2268456.mp3”这样的Mp3吗?
sxldfang 2010-11-12
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 wg_0126 的回复:]
你有这块的显示吗
[/Quote]

实际上是利用百度的Mp3来进行处理的。怕内容多,不敢往这里放,这里仅是简单的模型
sxldfang 2010-11-12
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 vpoint2009 的回复:]
是不是线程跟主线程之间通信的问题?
[/Quote]

好像不是这么简单。觉得不应该错的地方却错啦,很是郁闷
wg_0126 2010-11-12
  • 打赏
  • 举报
回复
你有这块的显示吗
vpoint2009 2010-11-12
  • 打赏
  • 举报
回复
是不是线程跟主线程之间通信的问题?
无心雨云 2010-11-12
  • 打赏
  • 举报
回复
没人来看
sxldfang 2010-11-12
  • 打赏
  • 举报
回复
是有点太不方便啦,还有很多的控制,比如:暂停、继续、停止、播放、音量 等等,都按上面的方法来控制,很麻烦,且速度跟不上(每秒才会检测一次),提高检测频率会影响整体性能。不知还有无其他的方法。

在searchAndPlay.cs 类文件中,mciMusicPlayer类型的变量 mp 在不在线程中,访问的结果咋会差别这么大呢?
hongdongfang 2010-11-12
  • 打赏
  • 举报
回复
这样子吧,可能有点变态,o(∩_∩)o...哈哈


int newPos=-1;

void findPlay_findPlayOk(mciMusicPlayer mp)
{
if (newPos >= 0)
{
mp.Position = newPos;
mp.Play();
newPos = -1;
}
int l = mp.Length;
int p = mp.Position;
this.Invoke(new System.Action<object>(delegate
{
trackBar1.Maximum = l;
trackBar1.Value = p;

}), 1);
}

private void button2_Click(object sender, EventArgs e)
{
newPos = 40000; // 设置新的播放位置
}
sxldfang 2010-11-12
  • 打赏
  • 举报
回复
注意:直接通过 findPlay 对象进行操作是不对的

比如:下面的代码,显示歌曲长度是0,显然是不对的。

private void button2_Click(object sender, EventArgs e)
{
this.Text=findPlay.mp.Length.ToString();
}
sxldfang 2010-11-12
  • 打赏
  • 举报
回复
谢谢 hongdongfang

上面的确是不错,但还存在新的问题:

mp3Player = mp;

若不能正确执行,就缺少对 mp 的引用,若想调整播放的位置,也就是设置:

mp3Player.Position=5000; // 5秒的位置

就不能操作了,又该如何处理呢?

hongdongfang 2010-11-12
  • 打赏
  • 举报
回复
不用定时器,这样改一下:

searchAndPlay.cs 类文件
---------------------------------------


void findAndPlay() // 耗时的任务
{
Worker();
if (findPlayOk != null) // 若定义有事件处理程序
{
while (mp.Position < mp.Length)
{
Thread.Sleep(1000);
findPlayOk(mp); // 则执行事件处理程序
}
}
}



Form1.cs 窗体 -> 显示Mp3的播放进度---------------------------------------------

void findPlay_findPlayOk(mciMusicPlayer mp)
{
// 此时检测 mp 对象,一切正常,将其赋值给 mp3Player
int l = mp.Length;
int p = mp.Position;
this.Invoke(new System.Action<object>(delegate
{
// mp3Player = mp; // 这里赋值后就错,为什么会错,我也不清楚
trackBar1.Maximum = l;
trackBar1.Value = p;

}), 1);
}



mp3Player = mp; // 这里赋值后就错,为什么会错,我也不清楚,这不是传个引用吗?
sxldfang 2010-11-12
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 hongdongfang 的回复:]
C# code


private void button2_Click(object sender, EventArgs e)
{
mp3Player.Open(@"http://dm717.v.iask.com/fs/709/1/95a3476cc7b048660c9325abb7ac8a0f39795345/mp3/9_2268456.mp3");
if (mp3……
[/Quote]

这样是没问题,但当从网络加载MP3时,可能会因速度过慢而造成界面假死啊
mabiJay 2010-11-12
  • 打赏
  • 举报
回复
汗 ... ...
hongdongfang 2010-11-12
  • 打赏
  • 举报
回复


private void button2_Click(object sender, EventArgs e)
{
mp3Player.Open(@"http://dm717.v.iask.com/fs/709/1/95a3476cc7b048660c9325abb7ac8a0f39795345/mp3/9_2268456.mp3");
if (mp3Player.Status == mciMusicPlayer.PlayStatus.Stopped)
{
mp3Player.Play();
trackBar1.Maximum = mp3Player.Length;
timer1.Interval = 1000;
timer1.Enabled = true;
}
}

楼主可这样试试,音乐可以播放,进度条也可动啦!

110,578

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧