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

📄 wavefile.cs

📁 功能:基于windows mobile 的地图查看器。使用vs2005开发
💻 CS
字号:
using System;
using System.IO;
using System.Runtime.InteropServices;
//using System.Threading;
using System.Diagnostics;

namespace GXSoundLibrary
{
	/// <summary>
	/// Implements the functionality required to load and play a .wav file.
	/// </summary>
	public class WaveFile : IDisposable
	{
		/// <summary>
		/// Number of data buffers used for file streaming.
		/// </summary>
		protected const int kNumBuffers = 2;

		/// <summary>
		/// Handle to the waveform audio output device associated with this file.
		/// </summary>
		public int hwo { get { return m_hwo.ToInt32(); } }
		protected IntPtr m_hwo = IntPtr.Zero;

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

		/// <summary>
		/// WAVEHDR's used to stream the data from this file.
		/// </summary>
		protected Wave.WAVEHDR[] m_whdr = null;

		/// <summary>
		/// Specifies if the file is loaded.
		/// </summary>
		protected bool m_loaded;

		/// <summary>
		/// Specifies the size of the buffers used to stream this file.
		/// </summary>
		protected int m_bufferSize;

		/// <summary>
		/// Specifies the number of blocks in the data file.
		/// </summary>
		protected int m_numBlocks;

		/// <summary>
		/// Specifies the block that is currently waiting to be loaded from the
		/// file.
		/// </summary>
		protected int m_curBlock;

		/// <summary>
		/// The BinaryReader being used to stream the wave file.
		/// </summary>
		protected BinaryReader m_rdr = null;

		/// <summary>
		/// Specifies if the file is done being played.
		/// </summary>
		public bool Done { get { return !m_playing; } }
		protected bool m_playing = false;

		/// <summary>
		/// Specifies the length of the audio data in milliseconds.
		/// </summary>
		public uint Milliseconds { get { return 1000 * m_dataLength / m_wfmt.nAvgBytesPerSec; } }

		/// <summary>
		/// Length of the data in bytes.
		/// </summary>
		protected uint m_dataLength;

		/// <summary>
		/// Create an instance of a wave file.
		/// </summary>
		public WaveFile()
		{
			m_wfmt = new Wave.WAVEFORMATEX();

			m_whdr = new Wave.WAVEHDR[kNumBuffers];
			for (int i = 0; i < kNumBuffers; i++)
			{
				m_whdr[i] = new Wave.WAVEHDR();
			}

			m_numBlocks = 0;
			m_curBlock = 0;

			m_loaded = false;
		}

		/// <summary>
		/// Load a .wav file and initialize data buffers for playback.
		/// </summary>
		/// <param name="curDevice">Current wavefore audio device</param>
		/// <param name="fileName">File to be loaded</param>
		/// <param name="hwnd">Handle to owner control</param>
		/// <param name="bufferSize">Total size of buffers to use, if this is 0
		/// then the buffer will contain the entire file (no streaming)</param>
		/// <returns>Error condition of load attempt</returns>
		public Wave.MMSYSERR Load(uint curDevice, string fileName, IntPtr hwnd, int bufferSize)
		{
			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.SeekTo(strm);
			m_dataLength = m_wfmt.Read(m_rdr);

			Wave.MMSYSERR result = WaveOut.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.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);

			m_numBlocks = (int)(m_dataLength / m_bufferSize);
			if (m_dataLength % m_bufferSize != 0)
				m_numBlocks++;

			result = ReadBuffer(0);
			if (result != Wave.MMSYSERR.NOERROR)
				return result;

			if (m_numBlocks == 1)
			{
				m_rdr.Close();
				strm.Close();
				m_rdr = null;
			}

			m_loaded = true;

			return Wave.MMSYSERR.NOERROR;
		}

		/// <summary>
		/// Reload a .wav file.  This method will re-use as many resources
		/// as possible from a previous load.  If the audio was not being
		/// streamed then no reloading needs to be done and this method will
		/// simply prepare the buffer for playback.
		/// </summary>
		/// <param name="curDevice">Current wavefore audio device</param>
		/// <param name="hwnd">Handle of owner control</param>
		/// <returns>Error condition of reload attempt</returns>
		protected Wave.MMSYSERR Reload(uint curDevice, IntPtr hwnd)
		{
			Wave.MMSYSERR result = WaveOut.waveOutOpen(ref m_hwo, curDevice, m_wfmt, hwnd, 0, Wave.CALLBACK_WINDOW);
			if (result != Wave.MMSYSERR.NOERROR)
				return result;

			for (int i = 0; i < kNumBuffers; i++)
			{
				m_whdr[i].Reinit();
			}

			if (m_numBlocks > 1)
			{
				m_wfmt.Skip(m_rdr.BaseStream);

				result = ReadBuffer(0);
				if (result != Wave.MMSYSERR.NOERROR)
					return result;
			}
			else
			{
				if ((m_whdr[0].dwFlags & Wave.WHDR_PREPARED) == 0)
				{
					return WaveOut.waveOutPrepareHeader(m_hwo, m_whdr[0], (uint)Marshal.SizeOf(m_whdr[0]));
				}
			}

			return Wave.MMSYSERR.NOERROR;
		}

		/// <summary>
		/// Play the audio file.  If the file is not already loaded then it will
		/// be loaded at this time.  If the file is loaded then the fileName
		/// parameter is ignored.
		/// </summary>
		/// <param name="curDevice">Current wavefore audio device</param>
		/// <param name="fileName">Name of file to be played/loaded</param>
		/// <param name="hwnd">Handle to owner MessageWindow</param>
		/// <param name="bufferSize">Total buffer size</param>
		/// <param name="volLeft">Left speaker volume level</param>
		/// <param name="volRight">Right speaker volume level</param>
		/// <returns>Error condition of play attempt</returns>
		public Wave.MMSYSERR Play(uint curDevice, string fileName, IntPtr hwnd, int bufferSize, ushort volLeft, ushort volRight)
		{
			if (m_playing)
				Stop();

			Wave.MMSYSERR result;

			if (!m_loaded)
			{
				result = Load(curDevice, fileName, hwnd, bufferSize);
				if (result != Wave.MMSYSERR.NOERROR)
					return result;
			}
			else
			{
				result = Reload(curDevice, hwnd);
				if (result != Wave.MMSYSERR.NOERROR)
					return result;
			}

			SetVolume(volLeft, volRight);

			result = WaveOut.waveOutWrite(m_hwo, m_whdr[0], (uint)Marshal.SizeOf(m_whdr[0]));
			if (result != Wave.MMSYSERR.NOERROR)
				return result;

			//Thread loadThread = new Thread(new ThreadStart(LoadBuffer));
			//loadThread.Start();

			LoadBuffer();
			m_curBlock = 0;

			m_playing = true;

			return Wave.MMSYSERR.NOERROR;
		}

		/// <summary>
		/// Read a buffer from the current stream.
		/// </summary>
		/// <param name="bufIndex">Index of buffer to be read</param>
		/// <returns>Error condition of read attempt</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;
			}

			Wave.MMSYSERR result = m_whdr[bufIndex].Read(m_rdr, readLength);
			if (result != Wave.MMSYSERR.NOERROR)
				return result;

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

			return Wave.MMSYSERR.NOERROR;
		}

		/// <summary>
		/// Create an empty buffer to be used as padding at the end of streaming.
		/// </summary>
		/// <param name="bufIndex">Index of buffer to be used</param>
		/// <returns>Error condition of create attempt</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.WHDR_PREPARED) == 0)
			{
				return WaveOut.waveOutPrepareHeader(m_hwo, m_whdr[bufIndex], (uint)Marshal.SizeOf(m_whdr[bufIndex]));
			}

			return Wave.MMSYSERR.NOERROR;
		}

		/// <summary>
		/// Load the next buffer in the stream.
		/// </summary>
		public void LoadBuffer()
		{
			lock(this)
			{
				if (!m_playing)
					return;

				int readBuffer = (m_curBlock + 3) % 2;

				if (m_curBlock == m_numBlocks - 1)
					CreateBuffer(readBuffer);
				else
					ReadBuffer(readBuffer);

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

		/// <summary>
		/// The currently played block has finished so load the next block into
		/// the next available buffer.
		/// </summary>
		public void BlockDone()
		{
			m_curBlock++;

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

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

		/// <summary>
		/// Stop playback.
		/// </summary>
		public void Stop()
		{
			m_playing = false;

			lock(this)
			{
				WaveOut.waveOutReset(m_hwo);

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

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

				m_hwo = IntPtr.Zero;
			}
		}

		/// <summary>
		/// Free any resources allocated by the wave file.
		/// </summary>
		public void Dispose()
		{
			Stop();

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

			for (int i = 0; i < kNumBuffers; i++)
			{
				m_whdr[i].Dispose();
			}

			m_wfmt = null;
		}

		/// <summary>
		/// Resume paused playback.
		/// </summary>
		/// <returns>Error condition of resume attempt</returns>
		public Wave.MMSYSERR Resume()
		{
			return WaveOut.waveOutRestart(m_hwo);
		}

		/// <summary>
		/// Pause current playback.
		/// </summary>
		/// <returns>Error condition of pause attempt</returns>
		public Wave.MMSYSERR Pause()
		{
			return WaveOut.waveOutPause(m_hwo);
		}

		/// <summary>
		/// Get the current speaker volume.
		/// </summary>
		/// <param name="volLeft">Left speaker volume</param>
		/// <param name="volRight">Right speaker volume</param>
		/// <returns>Error condition of volume attempt</returns>
		public Wave.MMSYSERR GetVolume(ref ushort volLeft, ref ushort volRight)
		{
			uint vol = 0;

			Wave.MMSYSERR result = WaveOut.waveOutGetVolume(m_hwo, ref vol);
			if (result != Wave.MMSYSERR.NOERROR)
				return result;

			volLeft = (ushort)(vol & 0x0000ffff);
			volRight = (ushort)(vol >> 16);

			return Wave.MMSYSERR.NOERROR;
		}

		/// <summary>
		/// Set the volume of this audio playback.
		/// </summary>
		/// <param name="volLeft">Left speaker volume</param>
		/// <param name="volRight">Right speaker volume</param>
		/// <returns>Error condition of volume attempt</returns>
		public Wave.MMSYSERR SetVolume(ushort volLeft, ushort volRight)
		{
			uint vol = ((uint)volLeft & 0x0000ffff) | ((uint)volRight << 16);
			return WaveOut.waveOutSetVolume(m_hwo, vol);
		}
	}
}

⌨️ 快捷键说明

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