2023年7月29日发(作者:)
最近的项目中,要做这样一个功能,在服务器上可以通过播放器针对不同的音箱设备来播放不同的音乐,就像大学里英语角一样,校广播台对不同的活动中心播放的不同的英文广播。当然我们的项目功能类似,但是却也没校园广播那么复杂,校园广播使用了很好的音频设备,我们得控制成本,所以我们的解决方案是:在服务器上安装一块多通道的专业声卡(录音棚用来录音)或者是安装多块普通的声卡。这样我们在播放音乐的时候可以选择输出的声卡,播放界面如下:
如何去做这样一个播放器呢,.net framework中是不是有这个接口,网上找了一下资料,发现有两种方法:
第一种方法是调用win32下的的API函数去播放文件,代码:
using System;
using c;
using ;
using pServices;
namespace HardWaresOperation
{
public class SpeakerBeeper
{
//文件资源
private string SoundSource = @"C:Documents and SettingsAdministrator桌面";
private string SoundSource; public SpeakerBeeper(string _SoundSource)
{
SoundSource = _SoundSource;
}
///
/// 检查声卡,播放声音
///
/// 声音文件
///
public bool SpeakerBeep()
{
if (tGetNumDevs() != 0)
{
und(SoundSource, ,
_FILENAME | _ASYNC);
return true;
}
else
{
return false;
}
}
}
// 这是辅助的文件
public class SBHelper
{
public enum PlaySoundFlags : int
{
SND_SYNC = 0x0000,//同步
SND_ASYNC = 0x0001,//异步
SND_NODEFAULT = 0x0002,//未找到文件默认为静音
SND_MEMORY = 0x0004,//声音文件来自内存
SND_LOOP = 0x0008, //循环播放
SND_NOSTOP = 0x0010,//不停止目前的播放
SND_NOWAIT = 0x00002000,//当播放器忙碌时不等待
SND_ALIAS = 0x00010000, //为已注册的别名时
SND_ALIAS_ID = 0x00110000, //别名为ID
SND_FILENAME = 0x00020000, //文件名
SND_RESOURCE = 0x00040004 //资源名
}
[DllImport("", EntryPoint = "waveOutGetNumDevs")]
//waveOutGetNumdevs()方法
//当机器有声卡时返回1 //没有声卡返回0
public static extern int waveOutGetNumDevs();
[DllImport("")]
//SoundSource声音文件
//参数hmod是应用程序的实例句柄
//psFlag播放模式
public static extern bool PlaySound(string SoundSource, IntPtr hmod, PlaySoundFlags
psFlag);
}
}
但是发现waveOutGetNumDevs()函数只是用来返回机器是否有声卡,并没有得到机器的声卡设备的列表,知识对当前默认的设备进行播放,达不到要求。
第二种方法就是利用dirextx sdk开发包中的API来开发,这个开发包里面有个DierctSound可以用来开发,在网上找了好久他的中文开发文档,大致看了一遍,就决定动手做了。
一、首先是对DirectSound中的类我需要的调用的函数进行了封装,封装成两个结构体,一个枚举,一个静态类,一个播放类,类图如下:
其中DeviceInfo结构体是硬件信息也是就是声卡的硬件信息,DevId表示硬件的ID号,Description是对硬件的描述,这些信息都是通过DirectSound从声卡的驱动程序中读取出来的。
///
/// 设备信息 ///
public struct DeviceInfo
{
public Guid DevId;
public string Description;
}
DeviceStatus结构体记录播放器的状态,isLooping表示是否循环播放,isPlaying表示正处于播放还是停止,isTerminated表示播放终止。
///
/// 播放状态
///
public struct DeviceStatus
{
///
/// 是否播放
///
public bool isPlaying;
///
///是否循环播放
///
public bool isLooping;
///
/// 是否终止
///
public bool isTerminated;
}
PlayFlags枚举指定播放器是正常播放还是循环播放。
///
/// 设置播放的方式
///
public enum PlayFlags
{
///
/// 正常播放
///
Default,
///
/// 循环播放
///
Looping
};
静态类QBDeviceHelper中的方法GetDeviceList()用来获得机器的声卡列表。 public static class QBDeviceHelper
{
///
/// 获得设备列表(返回设备信息)
///
///
public static List
{
List
DevicesCollection myDevices = new DevicesCollection();//获得所有声卡列表
foreach (DeviceInformation dev in myDevices)
{
DeviceInfo dData = new DeviceInfo();
= Guid;
ption = ption;
(dData);
}
return devList;
}
}
播放器类QBAudioCtrl是用来控制播放器的播放,停止等动作。
public class QBAudioCtrl
{
SecondaryBuffer sbuffer = null;
public QBAudioCtrl(Guid DevId, string FileName)
{
try
{
//根据ID建立设备对象
Device dev = new Device(DevId);
//设置设备的优先级
perativeLevel((new ()),
ty);
//设置缓存的属性
BufferDescription bfDes = new BufferDescription();
lPan = true;
lVolume = true;
lFrequency = true;
Focus = true;
//创建二级缓存
sbuffer = new SecondaryBuffer(FileName, bfDes, dev);
= 0;//声音最大
= 0;//混合声道 ncy = sPerSecond;//以正常速度播放
}
catch (Exception e)
{
throw new Exception(e);
}
}
///
/// 播放
///
/// 播放方式(正常,循环)
public void Dev_Play(PlayFlags PlayStyle)
{
//开始播放
if (sbuffer != null)
{
if (g == true)
();
if (PlayStyle == t)
(0, t);//正常播放
else if (PlayStyle == g)
(0, g);//循环播放
}
}
//暂停
public void Dev_Stop()
{
if (sbuffer != null)
();
}
//关闭
public void Dev_Dispose()
{
e();
}
///
/// 获取播放状态
///
///
public DeviceStatus GetDeviceStatus()
{ DeviceStatus thisStatus = new DeviceStatus();
if (sbuffer != null)
{
BufferStatus devStates = ;
thisStatus = new DeviceStatus();
ing = g;//是否循环播放
ing = g;//是否播放
inated = ated;//是否终止
}
return thisStatus;
}
}
以上把基本的类写好了,下面就可以开始编写前台代码了。先来个截图
直接上代码吧
using System;
using c;
using entModel;
using ;
using g;
using ;
using ;
using ;
using QBAudioLib;
namespace QBAudio
{
public partial class Form2 : Form
{
QBAudioCtrl qbCtrl;
DeviceStatus devStauts;
PlayFlags thisFlags;
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)//初始化
{
//获得所有的音频设备
List
foreach (DeviceInfo dev in devInfo)
{
cBox_();
}
ags = t;
}
private void btn_play_Click(object sender, EventArgs e)//播放
{
try
{
_Play(ags);
devStauts = iceStatus();
lbl_ = "playing:" + ng() + "---looping:" +
ing + "---Terminated:" + inated;
}
catch (Exception ex)
{
(e);
}
}
private void btn_stop_Click(object sender, EventArgs e)//停止
{
try
{
_Stop();
devStauts = iceStatus();
lbl_ = "playing:" + ng() + "---looping:" +
ing + "---Terminated:" + inated;
}
catch (Exception ex)
{
(e);
}
}
private void btn_File_Click(object sender, EventArgs e)//选择播放文件
{
OpenFileDialog filedg = new OpenFileDialog();
= "音视频文件(*.wav)|*.wav";
if (alog() == )
{
if (me != "" || me != )
{
txt_ = me;
}
}
}
private void cBox_Devices_SelectedIndexChanged(object sender, EventArgs e)//选择声卡
{
try
{
//thisDev = (DeviceInfo)cBox_edItem;
Guid devID = (Guid)cBox_edItem;
string fileName = txt_();
if (fileName == null || fileName == )
{
("请选择播放的文件...");
return;
}
qbCtrl = new QBAudioCtrl(devID, fileName);
devStauts = iceStatus();
lbl_ = "playing:" + ng() + "---looping:" + ing + "---Terminated:" + inated;
}
catch (Exception ex)
{
(e);
}
}
private void btn_pause_Click(object sender, EventArgs e)//暂停
{
try
{
_Dispose();
}
catch (Exception ex)
{
(e);
}
}
private void radioButton1_CheckedChanged(object sender, EventArgs e)//设置正常播放
{
if (d)
{
thisFlags = t;
btn_play_Click(sender, e);
}
}
private void radioButton2_CheckedChanged(object sender, EventArgs e)//设置循环播放
{
if (d)
{
thisFlags = g;
btn_play_Click(sender, e);
}
}
}
}
但上面的例子中,只能播放单一文件,如果要是多个文件选择多个声卡同时播放呢?想到使用多线程来实现,再次封装多线程播放类
AudioPalyer类,有三个事件,当播放时触发OnAudioPlay事件,当循环播放时触发OnLoopingPlay事件,当正常播放时触发OnNormalPlay事件,代码如下:
using System;
using c;
using ;
using ;
using QBAudioLib;
using ing;
namespace QBAudioThread
{
public struct playInfo
{
public Guid devId;
public string fileName;
public PlayFlags thisFlag;
}
public class AudioEventArgs : EventArgs
{
public playInfo AudioPlayInfo;
}
public class AudioPlayer
{
QBAudioCtrl qbCtrl;
DeviceStatus devStauts;
playInfo thisInfo;
public delegate void DefEventHandler(object sender,AudioEventArgs args);
public event DefEventHandler OnAudioPlay;//开始播放事件 public event EventHandler OnNormalPaly;//默认正常播放时事件
public event EventHandler OnLoopingPaly;//循环播放时事件
public AudioPlayer(playInfo iPlay)
{
thisInfo = iPlay;
}
public void Audio_Play()
{
(this);
}
}
}
界面实现:qbCtrl = new QBAudioCtrl(, me);
_Play(ag);
devStauts = iceStatus();
//开始播放激发的事件
if (ing == true)
{
AudioEventArgs thisArgs = new AudioEventArgs();
layInfo = thisInfo;
OnAudioPlay(this, thisArgs);
}
if (ing == false)
{//默认播放激发的事件
OnNormalPaly(this, new EventArgs());
}
else
{//循环播放激发的事件
OnLoopingPaly(this, new EventArgs());
}
(this);
(1000);
代码:
using System;
using c;
using entModel;
using ;
using g;
using ;
using ;
using ;
using QBAudioLib;
using ing;
namespace QBAudioThread
{
public partial class Form2 : Form
{
PlayFlags thisFlags;
DataTable dt = new DataTable();
Thread threadPlay;
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
//获得所有的音频设备
List
foreach (DeviceInfo dev in devInfo)
{
cBox_(new KeyValuePair
ption));
}
cBox_edIndex = 0;
ags = t;
("devId", typeof(Guid));
("fileName", typeof(string));
("thisFlag", typeof(PlayFlags));
urce = dt;
}
private void btn_play_Click(object sender, EventArgs e)//播放
{
try
{
int i = 0;
foreach (DataGridViewRow row in )
{
if ((bool)[0].FormattedValue == true)
{
playInfo thisPlay = new playInfo();
= (Guid)["devId"].Value;
me = ["fileName"].ng();
ag = (PlayFlags)["thisFlag"].Value;
AudioPlayer myPlayer = new AudioPlayer(thisPlay);
oPlay += new
ntHandler(myPlayer_OnAudioPlay); ingPaly += new
EventHandler(myPlayer_OnLoopingPaly);
alPaly += new
EventHandler(myPlayer_OnNormalPaly);
threadPlay = new Thread(new ThreadStart(_Play));
= "player_" + ng();
();
ground = true;
i++;
}
}
}
catch (Exception ex)
{
(e);
}
}
void myPlayer_OnAudioPlay(object sender,AudioEventArgs args)
{
playInfo myplay = (playInfo)layInfo;
string msg = ng() + "rn正在播放rn" + me +
"rn";
(msg);
}
void myPlayer_OnNormalPaly(object sender, EventArgs e)
{
("Normal");
}
void myPlayer_OnLoopingPaly(object sender, EventArgs e)
{
//播放一分钟后终止线程
("Looping");
}
private void btn_File_Click(object sender, EventArgs e)//选择播放文件
{
OpenFileDialog filedg = new OpenFileDialog();
= "音视频文件(*.wav)|*.wav";
if (alog() == )
{
if (me != "" || me != )
{
txt_ = me;
}
}
}
private void cBox_Devices_SelectedIndexChanged(object sender, EventArgs e)//选择声卡
{
}
private void radioButton1_CheckedChanged(object sender, EventArgs e)//选择默认播放
{
if (d)
{
thisFlags = t;
}
}
private void radioButton2_CheckedChanged(object sender, EventArgs e)//选择循环播放
{
if (d)
{
thisFlags = g;
}
}
private void btn_add_Click(object sender, EventArgs e)//添加进列表
{
DataRow dr = (); dr["devId"] = ((KeyValuePair
dr["fileName"] = txt_();
dr["thisFlag"] = thisFlags;
(dr);
urce = dt;
}
private void btn_clear_Click(object sender, EventArgs e)//清除列表
{
}
}
}
();
urce = dt;
发布者:admin,转转请注明出处:http://www.yc00.com/web/1690645168a384994.html
评论列表(0条)