⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 waveout.cs

📁 功能:基于windows mobile 的地图查看器。使用vs2005开发
💻 CS
📖 第 1 页 / 共 3 页
字号:
using System;
using System.Runtime.InteropServices;
using System.IO;
using Microsoft.WindowsCE.Forms;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;

namespace PInvokeLibrary
{
	/// <summary>
	/// Encapsulates Waveform Audio Interface playback functions and provides a simple
	/// interface for playing audio.
	/// </summary>
	public class WaveOut : IDisposable
	{
		/// <summary>
		/// Supplies an inteface for loading and playing a .wav file.
		/// </summary>
		public class WaveFile : IDisposable
		{
			/// <summary>
			/// Hardware interface instance for this wave file.
			/// </summary>
			protected IntPtr m_hwo = IntPtr.Zero;

			/// <summary>
			/// Instance of WAVEFORMATEX associated with this file.
			/// </summary>
			protected Wave.WAVEFORMATEX m_wfmt = null;

			/// <summary>
			/// Buffers used to read and play wave data.
			/// </summary>
			protected Wave.WAVEHDR[] m_whdr = null;

			/// <summary>
			/// Size of each buffer used for playback
			/// </summary>
			protected int m_bufferSize;

			/// <summary>
			/// Number of buffers needed for playback
			/// </summary>
			protected int m_numBlocks;

			/// <summary>
			/// Current buffer being read
			/// </summary>
			protected int m_curBlock;

			/// <summary>
			/// BinaryRead used to access the streaming audio file.
			/// </summary>
			protected BinaryReader m_rdr = null;

			/// <summary>
			/// Determines if file is done playing.
			/// </summary>
			public bool Done { get { return !m_playing; } }
			protected bool m_playing = false;

			/// <summary>
			/// Length, in milliseconds, of the audio file.
			/// </summary>
			public uint Milliseconds { get { return 1000 * m_dataLength / m_wfmt.nAvgBytesPerSec; } }
			protected uint m_dataLength;

			/// <summary>
			/// Create an instance of a wave file.  Allocate the two buffers that will
			/// be used for streaming audio.
			/// </summary>
			public WaveFile()
			{
				m_whdr = new Wave.WAVEHDR[2];
			}

			/// <summary>
			/// Play a wave file.
			/// </summary>
			/// <param name="curDevice">Hardware device to use for playback</param>
			/// <param name="fileName">Name of file to play</param>
			/// <param name="hwnd">Handle to a message window to use for messaging</param>
			/// <param name="bufferSize">Size of streaming buffers, a 0 value specifies
			/// that the buffer should be created big enough to fit the entire file</param>
			/// <param name="volLeft">Left channel volume level</param>
			/// <param name="volRight">Right channel volume level</param>
			/// <returns>MMSYSERR.NOERROR if successful</returns>
			public Wave.MMSYSERR Play(uint curDevice, String fileName, IntPtr hwnd, int bufferSize, ushort volLeft, ushort volRight)
			{
				if (m_playing)
					return Wave.MMSYSERR.NOERROR;

				if (!File.Exists(fileName))
					return Wave.MMSYSERR.ERROR;

				FileInfo fi = new FileInfo(fileName);
				if ((fi.Attributes & FileAttributes.ReadOnly) != 0)
					fi.Attributes -= FileAttributes.ReadOnly;

				FileStream strm = new FileStream(fileName, FileMode.Open);
				if (strm == null)
					return Wave.MMSYSERR.ERROR;

				m_rdr = new BinaryReader(strm);
				if (m_rdr == null)
					return Wave.MMSYSERR.ERROR;

				m_wfmt = new Wave.WAVEFORMATEX();
				m_wfmt.SeekTo(strm);

				// Read in the WAVEFORMATEX structure and attempt to open the
				// device for playback.
				m_wfmt.Read(m_rdr);

				Wave.MMSYSERR result = waveOutOpen(ref m_hwo, curDevice, m_wfmt, hwnd, 0, Wave.CALLBACK_WINDOW);
				if (result != Wave.MMSYSERR.NOERROR)
					return result;

				m_dataLength = (uint)(m_rdr.BaseStream.Length - Wave.WAVEFORMATEX.WF_OFFSET_DATA);

				if (bufferSize == 0)
					m_bufferSize = (int)m_dataLength;
				else
					m_bufferSize = bufferSize / 2;

				if (m_bufferSize % m_wfmt.nBlockAlign != 0)
					m_bufferSize += m_wfmt.nBlockAlign - (m_bufferSize % m_wfmt.nBlockAlign);

				// Determine the number of buffer reads required to play the entire
				// file
				m_numBlocks = (int)(m_dataLength / m_bufferSize);
				if ((m_numBlocks * m_bufferSize) < m_dataLength)
					m_numBlocks++;

				m_whdr[0] = new Wave.WAVEHDR();
				m_whdr[1] = new Wave.WAVEHDR();

				// Read in the first buffer
				result = ReadBuffer(0);
				if (result != Wave.MMSYSERR.NOERROR)
					return result;

				// If the entire file fits in the buffer then close the file
				if (m_numBlocks == 1)
				{
					m_rdr.BaseStream.Close();
					m_rdr.Close();
					m_rdr = null;
				}

				SetVolume(volLeft, volRight);

				// Start playback of the first buffer
				result = waveOutWrite(m_hwo, m_whdr[0], (uint)Marshal.SizeOf(m_whdr[0]));
				if (result != Wave.MMSYSERR.NOERROR)
					return result;

				m_curBlock = 0;

				// Create the second buffer.  If the audio is being streamed, this will
				// be the next audio block, otherwise it will be padding
				Thread loadThread = new Thread(new ThreadStart(LoadBuffer));
				loadThread.Start();

				m_playing = true;

				return Wave.MMSYSERR.NOERROR;
			}

			/// <summary>
			/// Read in the specified buffer.
			/// </summary>
			/// <param name="bufIndex">Index of buffer to use for the read</param>
			/// <returns>MMSYSERR.NOERROR if successful</returns>
			protected Wave.MMSYSERR ReadBuffer(int bufIndex)
			{
				uint readLength = (uint)m_bufferSize;
				if (bufIndex < m_numBlocks)
				{
					uint remainingDataLength = (uint)(m_rdr.BaseStream.Length - m_rdr.BaseStream.Position);
					if (m_bufferSize > remainingDataLength)
						readLength = remainingDataLength;
				}

				// Read in the next block of data
				Wave.MMSYSERR result = m_whdr[bufIndex].Read(m_rdr, readLength, m_wfmt.nBlockAlign);
				if (result != Wave.MMSYSERR.NOERROR)
					return result;

				// If the header is not prepared then prepare it
				if ((m_whdr[bufIndex].dwFlags & Wave.WAVEHDR.WHDR_PREPARED) == 0)
				{
					return waveOutPrepareHeader(m_hwo, m_whdr[bufIndex], (uint)Marshal.SizeOf(m_whdr[bufIndex]));
				}

				return Wave.MMSYSERR.NOERROR;
			}

			/// <summary>
			/// Create an empty buffer to append to the end of the sound.  This protects
			/// the playback because the system will sometimes continue reading after the
			/// BlockDone method is called.
			/// </summary>
			/// <param name="bufIndex">Index of buffer to be created</param>
			/// <returns>MMSYSERR.NOERROR is successful</returns>
			protected Wave.MMSYSERR CreateBuffer(int bufIndex)
			{
				Wave.MMSYSERR result = m_whdr[bufIndex].Init((uint)m_bufferSize, true);
				if (result != Wave.MMSYSERR.NOERROR)
					return result;

				if ((m_whdr[bufIndex].dwFlags & Wave.WAVEHDR.WHDR_PREPARED) == 0)
				{
					return waveOutPrepareHeader(m_hwo, m_whdr[bufIndex], (uint)Marshal.SizeOf(m_whdr[bufIndex]));
				}

				return Wave.MMSYSERR.NOERROR;
			}

			/// <summary>
			/// Load a buffer.  If there are blocks left to be read, then data will
			/// be read into the buffer that is not being played.  Otherwise, the buffer
			/// will be filled with 0's for padding to detect the end of playback.
			/// </summary>
			public void LoadBuffer()
			{
				int readBuffer = (m_curBlock + 3) % 2;

				lock(m_whdr[readBuffer])
				{
					if (m_curBlock == m_numBlocks - 1)
						CreateBuffer(readBuffer);
					else
						ReadBuffer(readBuffer);

					waveOutWrite(m_hwo, m_whdr[readBuffer], (uint)Marshal.SizeOf(m_whdr[readBuffer]));
				}
			}

			/// <summary>
			/// Called by the MessageWindow when the buffer currently being played has
			/// finished.  This method starts the loading of the next block on a
			/// separate thread.  If the current block is the last one then playback is
			/// stopped.
			/// </summary>
			public void BlockDone()
			{
				m_curBlock++;

				if (m_curBlock < m_numBlocks)
				{
					Debug.Assert((m_whdr[(m_curBlock + 2) % 2].dwFlags & Wave.WAVEHDR.WHDR_INQUEUE) != 0,
						"ERROR: A sound block finished before the subsequent block was written.");

					Thread loadThread = new Thread(new ThreadStart(LoadBuffer));
					loadThread.Start();
				}
				else if (m_curBlock == m_numBlocks)
				{
					Stop();
				}
			}

			/// <summary>
			/// Stop playing the current file and clean up all resources.
			/// </summary>
			public void Stop()
			{
				waveOutReset(m_hwo);

				m_playing = false;

				if (m_rdr != null)
				{
					m_rdr.BaseStream.Close();
					m_rdr.Close();
					m_rdr = null;
				}

				for (int i = 0; i < 2; i++)
				{
					if (m_whdr[i] != null)
					{
						lock(m_whdr[i])
						{
							if (m_hwo != IntPtr.Zero)
								waveOutUnprepareHeader(m_hwo, m_whdr[i], (uint)Marshal.SizeOf(m_whdr[i]));

							m_whdr[i].Dispose();
							m_whdr[i] = null;
						}
					}
				}

				if (m_hwo != IntPtr.Zero)
					waveOutClose(m_hwo);

				m_hwo = IntPtr.Zero;
				m_wfmt = null;

			}

			/// <summary>
			/// Resume the playback of a paused sound.
			/// </summary>
			/// <returns>MMSYSERR.NOERROR if successful</returns>
			public Wave.MMSYSERR Resume()
			{
				return waveOutRestart(m_hwo);
			}

			/// <summary>
			/// Pause the playback of a sound.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -