www.pudn.com > SoundDone.rar > WaveOut.cs
using System;
using System.Threading;
using System.Runtime.InteropServices;
namespace WaveLib
{
///
/// 音频输出帮助
///
internal class WaveOutHelper
{
///
/// 抛出指定的异常
///
/// 异常号
public static void Try(int err)
{
if (err != WaveNative.MMSYSERR_NOERROR)
throw new Exception(err.ToString());
}
}
///
/// 缓冲区满时委托
///
public delegate void BufferFillEventHandler(IntPtr data, int size);
///
/// 代表音频输出缓冲区
///
internal class WaveOutBuffer : IDisposable
{
///
/// 缓冲池中的缓冲区
///
public WaveOutBuffer NextBuffer;
///
/// 控制播放状态
///
private AutoResetEvent m_PlayEvent = new AutoResetEvent(false);
///
/// 波形输出设备句柄
///
private IntPtr m_WaveOut;
///
/// WAVE 文件控制块
///
private WaveNative.WaveHdr m_Header;
///
/// 暂存 WAVE 文件的数据
///
private byte[] m_HeaderData;
///
/// 保存 WAVE 文件句柄,用于操作 WAVE 文件(因为是非托管对象)
///
private GCHandle m_HeaderHandle;
///
/// 保存 WAVE 文件数据句柄,用于操作 WAVE 文件数据(因为是非托管对象)
///
private GCHandle m_HeaderDataHandle;
///
/// 该缓冲区的当前播放状态
///
private bool m_Playing;
///
/// 缓冲区空时回调方法
///
/// 设备指针
/// 用户信息
///
///
///
internal static void WaveOutProc(IntPtr hdrvr, int uMsg, int dwUser, ref WaveNative.WaveHdr wavhdr, int dwParam2)
{
if (uMsg == WaveNative.MM_WOM_DONE)
{
try
{
GCHandle h = (GCHandle)wavhdr.dwUser;
WaveOutBuffer buf = (WaveOutBuffer)h.Target;
buf.OnCompleted();
}
catch
{
}
}
}
///
/// 构造函数
///
/// 波形输出设备句柄
/// 缓冲区大小
public WaveOutBuffer(IntPtr waveOutHandle, int size)
{
//保存波形输出设备句柄
m_WaveOut = waveOutHandle;
//获得 WAVE 文件句柄
m_HeaderHandle = GCHandle.Alloc(m_Header, GCHandleType.Pinned);
//将本实例指定为用户数据区
m_Header.dwUser = (IntPtr)GCHandle.Alloc(this);
//为用户数据设置缓冲区
m_HeaderData = new byte[size];
//获得 WAVE 文件数据区句柄
m_HeaderDataHandle = GCHandle.Alloc(m_HeaderData, GCHandleType.Pinned);
//获得数据区的指针
m_Header.lpData = m_HeaderDataHandle.AddrOfPinnedObject();
//设置 WAVE 文件结构--缓冲区大小
m_Header.dwBufferLength = size;
//为播放准备一个波形缓冲区
WaveOutHelper.Try(WaveNative.waveOutPrepareHeader(m_WaveOut, ref m_Header, Marshal.SizeOf(m_Header)));
}
~WaveOutBuffer()
{
Dispose();
}
public void Dispose()
{
if (m_Header.lpData != IntPtr.Zero)
{
WaveNative.waveOutUnprepareHeader(m_WaveOut, ref m_Header, Marshal.SizeOf(m_Header));
m_HeaderHandle.Free();
m_Header.lpData = IntPtr.Zero;
}
m_PlayEvent.Close();
if (m_HeaderDataHandle.IsAllocated)
m_HeaderDataHandle.Free();
GC.SuppressFinalize(this);
}
///
/// 返回缓冲区大小
///
public int Size
{
get { return m_Header.dwBufferLength; }
}
///
/// 获得 WAVE 文件数据区句柄
///
public IntPtr Data
{
get { return m_Header.lpData; }
}
///
/// 播放
///
/// 播放状态
public bool Play()
{
lock(this)
{
m_PlayEvent.Reset();
m_Playing = WaveNative.waveOutWrite(m_WaveOut, ref m_Header, Marshal.SizeOf(m_Header)) == WaveNative.MMSYSERR_NOERROR;
return m_Playing;
}
}
///
/// 阻塞,直到正在播放的缓冲区播放完毕
///
public void WaitFor()
{
if (m_Playing)
{
m_Playing = m_PlayEvent.WaitOne();
}
else
{
Thread.Sleep(0);
}
}
///
/// 确定当前播放已完成
///
public void OnCompleted()
{
m_PlayEvent.Set();
m_Playing = false;
}
}
///
/// 该类实现音频的播放
///
public class WaveOutPlayer : IDisposable
{
///
/// 代表输出设备的名柄
///
private IntPtr m_WaveOut;
///
/// linked list
/// 保存该次播放所用的缓冲池
///
private WaveOutBuffer m_Buffers;
///
/// 当前缓冲区
///
private WaveOutBuffer m_CurrentBuffer;
///
/// 自定义的线程,用于起用播放线程
///
private Thread m_Thread;
///
/// 保存缓冲区空时的处理器
///
private BufferFillEventHandler m_FillProc;
///
/// 播放是否完成
///
private bool m_Finished;
///
/// 用于将所有的位置零
///
private byte m_zero;
///
/// 缓冲区空时的回调方法
///
private WaveNative.WaveDelegate m_BufferProc = new WaveNative.WaveDelegate(WaveOutBuffer.WaveOutProc);
///
/// 检取系统中存在的波形输出设备的数量
///
public static int DeviceCount
{
get { return WaveNative.waveOutGetNumDevs(); }
}
///
/// 构造函数
///
/// 设备号
/// 格式
/// 缓冲区大小
/// 缓冲区个数
/// 缓冲区满时处理器
public WaveOutPlayer(int device, WaveFormat format, int bufferSize, int bufferCount, BufferFillEventHandler fillProc)
{
//根据 WAVE 文件的每个采样需要的 bit 数设置 m_zero 的值
m_zero= format.wBitsPerSample == 8 ? (byte)128 : (byte)0;
//取得缓冲区空时事件
m_FillProc = fillProc;
//设备能否打开
//若能,则返回设备指针
WaveOutHelper.Try(WaveNative.waveOutOpen(out m_WaveOut, device, format, m_BufferProc, 0, WaveNative.CALLBACK_FUNCTION));
//为音频输出分配给定的大小和个数
AllocateBuffers(bufferSize, bufferCount);
//起用播放线程进行播放
m_Thread = new Thread(new ThreadStart(ThreadProc));
m_Thread.Start();
}
~WaveOutPlayer()
{
Dispose();
}
public void Dispose()
{
if (m_Thread != null)
try
{
m_Finished = true;
if (m_WaveOut != IntPtr.Zero)
WaveNative.waveOutReset(m_WaveOut);
m_Thread.Join();
m_FillProc = null;
FreeBuffers();
if (m_WaveOut != IntPtr.Zero)
WaveNative.waveOutClose(m_WaveOut);
}
finally
{
m_Thread = null;
m_WaveOut = IntPtr.Zero;
}
GC.SuppressFinalize(this);
}
///
///
///
private void ThreadProc()
{
while (!m_Finished)
{
Advance();
if (m_FillProc != null && !m_Finished)
m_FillProc(m_CurrentBuffer.Data, m_CurrentBuffer.Size);
else
{
// zero out buffer
byte v = m_zero;
byte[] b = new byte[m_CurrentBuffer.Size];
for (int i = 0; i < b.Length; i++)
b[i] = v;
Marshal.Copy(b, 0, m_CurrentBuffer.Data, b.Length);
}
m_CurrentBuffer.Play();
}
WaitForAllBuffers();
}
///
/// 分配缓冲区
///
/// 缓冲区大小
/// 缓冲区数量
private void AllocateBuffers(int bufferSize, int bufferCount)
{
FreeBuffers();
if (bufferCount > 0)
{
m_Buffers = new WaveOutBuffer(m_WaveOut, bufferSize);
WaveOutBuffer Prev = m_Buffers;
try
{
//建立一个新的循环链表缓冲池
for (int i = 1; i < bufferCount; i++)
{
WaveOutBuffer Buf = new WaveOutBuffer(m_WaveOut, bufferSize);
Prev.NextBuffer = Buf;
Prev = Buf;
}
}
finally
{
Prev.NextBuffer = m_Buffers;
}
}
}
///
/// 清空缓冲区
///
private void FreeBuffers()
{
m_CurrentBuffer = null;
if (m_Buffers != null)
{
WaveOutBuffer First = m_Buffers;
m_Buffers = null;
WaveOutBuffer Current = First;
//循环清空缓冲池中的缓冲区
do
{
WaveOutBuffer Next = Current.NextBuffer;
Current.Dispose();
Current = Next;
} while(Current != First);
}
}
///
/// 将当前缓冲区移动到缓冲池中的下一个缓冲区
///
private void Advance()
{
m_CurrentBuffer= m_CurrentBuffer == null ? m_Buffers : m_CurrentBuffer.NextBuffer;
m_CurrentBuffer.WaitFor();
}
///
/// 循环等待缓冲池中的缓冲区
///
private void WaitForAllBuffers()
{
WaveOutBuffer Buf = m_Buffers;
while (Buf.NextBuffer != m_Buffers)
{
Buf.WaitFor();
Buf = Buf.NextBuffer;
}
}
}
}