📄 wavein.cs
字号:
using System;
using System.Runtime.InteropServices;
using System.IO;
using Microsoft.WindowsCE.Forms;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;
namespace PInvokeLibrary
{
/// <summary>
/// Encapsulates Waveform Audio Interface recording functions and provides a simple
/// interface for recording audio.
/// </summary>
public class WaveIn
{
/// <summary>
/// Supplies an inteface for creating, recording to, and saving a .wav file.
/// </summary>
protected class WaveFile : IDisposable
{
/// <summary>
/// Hardware interface instance for this wave file.
/// </summary>
protected IntPtr m_hwi = IntPtr.Zero;
/// <summary>
/// Instance of the WAVEFORMATEX header for this file.
/// </summary>
protected Wave.WAVEFORMATEX m_wfmt = null;
/// <summary>
/// Buffers used to store recording information.
/// </summary>
protected Wave.WAVEHDR[] m_whdr = null;
/// <summary>
/// Specifies whether the file is inited or not.
/// </summary>
protected bool m_inited = false;
/// <summary>
/// Current block, or buffer in m_whdr, being written.
/// </summary>
protected int m_curBlock;
/// <summary>
/// Maximum number of blocks to be written.
/// </summary>
protected int m_numBlocks;
/// <summary>
/// Size of each record buffer.
/// </summary>
protected uint m_bufferSize;
/// <summary>
/// Maximum size of all record buffers.
/// </summary>
protected uint m_maxDataLength;
/// <summary>
/// Specifies whether or not recording is done.
/// </summary>
public bool Done { get { return !m_recording; } }
protected bool m_recording = false;
/// <summary>
/// Preload the buffers and prepare them for recording.
/// </summary>
/// <param name="curDevice">Device to use for recording</param>
/// <param name="hwnd">Handle to a message window that will receive
/// audio messages.</param>
/// <param name="maxRecordLength_ms">Maximum length of recording</param>
/// <param name="bufferSize">Size of buffers to use for recording. New
/// buffers are added when needed until the maximum length is reached
/// or the recording is stopped.</param>
/// <returns>MMSYSERR.NOERROR if successful</returns>
public Wave.MMSYSERR Preload(uint curDevice, IntPtr hwnd, int maxRecordLength_ms, int bufferSize)
{
// Do not allow recording to be interrupted
if (m_recording)
return Wave.MMSYSERR.ERROR;
// If this file is already initialized then start over
if (m_inited)
{
Stop();
FreeWaveBuffers();
}
// Create an instance of WAVEINCAPS to check if our desired
// format is supported
WAVEINCAPS caps = new WAVEINCAPS();
waveInGetDevCaps(0, caps, caps.Size);
if ((caps.dwFormats & Wave.WAVE_FORMAT_1S08) == 0)
return Wave.MMSYSERR.NOTSUPPORTED;
// Initialize a WAVEFORMATEX structure specifying the desired
// format
m_wfmt = new Wave.WAVEFORMATEX();
m_wfmt.wFormatTag = Wave.WAVEHDR.WAVE_FORMAT_PCM;
m_wfmt.wBitsPerSample = 8;
m_wfmt.nChannels = 2;
m_wfmt.nSamplesPerSec = 11025;
m_wfmt.nAvgBytesPerSec = (uint)(m_wfmt.nSamplesPerSec * m_wfmt.nChannels * (m_wfmt.wBitsPerSample / 8));
m_wfmt.nBlockAlign = (ushort)(m_wfmt.wBitsPerSample * m_wfmt.nChannels / 8);
// Attempt to open the specified device with the desired wave format
Wave.MMSYSERR result = waveInOpen(ref m_hwi, curDevice, m_wfmt, hwnd, 0, Wave.CALLBACK_WINDOW);
if (result != Wave.MMSYSERR.NOERROR)
return result;
if (bufferSize == 0)
return Wave.MMSYSERR.ERROR;
m_bufferSize = (uint)bufferSize;
// Force the buffers to align to nBlockAlign
if (m_bufferSize % m_wfmt.nBlockAlign != 0)
m_bufferSize += m_wfmt.nBlockAlign - (m_bufferSize % m_wfmt.nBlockAlign);
// Determine the number of buffers needed to record the maximum length
m_maxDataLength = (uint)(m_wfmt.nAvgBytesPerSec * maxRecordLength_ms / 1000);
m_numBlocks = (int)(m_maxDataLength / m_bufferSize);
if (m_numBlocks * m_bufferSize < m_maxDataLength)
m_numBlocks++;
// Allocate the list of buffers
m_whdr = new Wave.WAVEHDR[m_numBlocks + 1];
// Allocate and initialize two buffers to start with
m_whdr[0] = new Wave.WAVEHDR();
m_whdr[1] = new Wave.WAVEHDR();
result = InitBuffer(0);
if (result != Wave.MMSYSERR.NOERROR)
return result;
result = InitBuffer(1);
if (result != Wave.MMSYSERR.NOERROR)
return result;
m_curBlock = 0;
m_inited = true;
return Wave.MMSYSERR.NOERROR;
}
/// <summary>
/// Called when the Windows message specifying that a block has finished
/// recording is encountered. It is critical that the application stay
/// one buffer ahead because at the point this function is called, the system
/// has already started writing to the next buffer.
/// </summary>
public void BlockDone()
{
m_curBlock++;
// If the next block is not the padding buffer at the end of the
// recording then initialize another buffer, otherwise stop recording
if (m_curBlock < m_numBlocks)
{
InitBuffer(m_curBlock + 1);
}
else if (m_curBlock == m_numBlocks)
{
Stop();
}
}
/// <summary>
/// Initialize a buffer for recording by allocating a data buffer,
/// preparing the header, and adding it to the read queue.
/// </summary>
/// <param name="bufIndex">Index of buffer to allocate</param>
/// <returns>MMSYSERR.NOERROR if successful</returns>
public Wave.MMSYSERR InitBuffer(int bufIndex)
{
// Determine the size of the buffer to create
uint writeLength = (uint)m_bufferSize;
if (bufIndex < m_numBlocks)
{
uint remainingDataLength = (uint)(m_maxDataLength - bufIndex * m_bufferSize);
if (m_bufferSize > remainingDataLength)
writeLength = remainingDataLength;
}
// If the header is not already instanced then instance it
if (m_whdr[bufIndex] == null)
m_whdr[bufIndex] = new Wave.WAVEHDR();
// Allocate memory if not already allocated
Wave.MMSYSERR result = m_whdr[bufIndex].Init(writeLength, false);
if (result != Wave.MMSYSERR.NOERROR)
return result;
// Prepare the header
result = waveInPrepareHeader(m_hwi, m_whdr[bufIndex], (uint)Marshal.SizeOf(m_whdr[bufIndex]));
if (result != Wave.MMSYSERR.NOERROR)
return result;
// Put the buffer in the queue
return waveInAddBuffer(m_hwi, m_whdr[bufIndex], (uint)Marshal.SizeOf(m_whdr[bufIndex]));
}
/// <summary>
/// Start recording.
/// </summary>
/// <returns>MMSYSERR.NOERROR if successful</returns>
public Wave.MMSYSERR Start()
{
if (!m_inited || m_recording)
return Wave.MMSYSERR.ERROR;
Wave.MMSYSERR result = waveInStart(m_hwi);
if (result != Wave.MMSYSERR.NOERROR)
return result;
m_recording = true;
return Wave.MMSYSERR.NOERROR;
}
/// <summary>
/// Free the buffers allocated for recording. This is not done when
/// recording stops because the user needs a chance to save or reject
/// the recording before trying again.
/// </summary>
private void FreeWaveBuffers()
{
m_inited = false;
if (m_whdr != null)
{
for(int i = 0; i < m_whdr.Length; i++)
{
if (m_whdr[i] != null)
{
waveInUnprepareHeader(m_hwi, m_whdr[i], (uint)Marshal.SizeOf(m_whdr[i]));
m_whdr[i].Dispose();
m_whdr[i] = null;
}
}
m_whdr = null;
}
waveInClose(m_hwi);
m_hwi = IntPtr.Zero;
}
/// <summary>
/// Write individual characters to the BinaryWriter. This is a helper
/// function used by Save. Writing strings does not work because they
/// write a length before the characters.
/// </summary>
/// <param name="wrtr">Destination of write</param>
/// <param name="text">Characters to be written</param>
private void WriteChars(BinaryWriter wrtr, string text)
{
for (int i = 0; i < text.Length; i++)
{
char c = (char)text[i];
wrtr.Write(c);
}
}
/// <summary>
/// Save the current record buffers to the specified file.
/// </summary>
/// <param name="fileName">Name of file to be saved</param>
/// <returns>MMSYSERR.NOERROR if successful</returns>
public Wave.MMSYSERR Save(string fileName)
{
if (!m_inited)
return Wave.MMSYSERR.ERROR;
if (m_recording)
Stop();
FileStream strm = null;
BinaryWriter wrtr = null;
try
{
if (File.Exists(fileName))
{
FileInfo fi = new FileInfo(fileName);
if ((fi.Attributes & FileAttributes.ReadOnly) != 0)
fi.Attributes -= FileAttributes.ReadOnly;
strm = new FileStream(fileName, FileMode.Truncate);
}
else
{
strm = new FileStream(fileName, FileMode.Create);
}
if (strm == null)
return Wave.MMSYSERR.ERROR;
wrtr = new BinaryWriter(strm);
if (wrtr == null)
return Wave.MMSYSERR.ERROR;
// Determine the size of the data, as the total number of bytes
// recorded by each buffer
uint totalSize = 0;
for (int i = 0; i < m_numBlocks; i++)
{
if (m_whdr[i] != null)
totalSize += m_whdr[i].dwBytesRecorded;
}
int chunkSize = (int)(36 + totalSize);
// Write out the header information
WriteChars(wrtr, "RIFF");
wrtr.Write(chunkSize);
WriteChars(wrtr, "WAVEfmt ");
wrtr.Write((int)16);
m_wfmt.Write(wrtr);
WriteChars(wrtr, "data");
wrtr.Write((int)totalSize);
// Write the data recorded to each buffer
for (int i = 0; i < m_numBlocks; i++)
{
if (m_whdr[i] != null)
{
Wave.MMSYSERR result = m_whdr[i].Write(wrtr);
if (result != Wave.MMSYSERR.NOERROR)
return result;
}
}
return Wave.MMSYSERR.NOERROR;
}
finally
{
FreeWaveBuffers();
if (strm != null)
strm.Close();
if (wrtr != null)
wrtr.Close();
}
}
/// <summary>
/// Stop recording. After stopping, the buffers are kept until recording
/// is restarted or the buffers are saved.
/// </summary>
public void Stop()
{
waveInReset(m_hwi);
m_recording = false;
}
/// <summary>
/// Clean up any allocated resources.
/// </summary>
public void Dispose()
{
Stop();
FreeWaveBuffers();
}
}
/// <summary>
/// Defines the MessageWindow used to receive messages from the audio
/// system.
/// </summary>
protected class SoundMessageWindow : MessageWindow
{
public const int MM_WIM_OPEN = 0x3BE;
public const int MM_WIM_CLOSE = 0x3BF;
public const int MM_WIM_DATA = 0x3C0;
// Instance of a recording interface
protected WaveIn m_wi = null;
public SoundMessageWindow(WaveIn wi)
{
m_wi = wi;
}
protected override void WndProc(ref Message msg)
{
switch(msg.Msg)
{
// When this message is encountered, a block is
// done recording, so notify the WaveIn instance.
case MM_WIM_DATA:
{
if (m_wi != null)
m_wi.BlockDone();
}
break;
}
base.WndProc(ref msg);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -