声音处理程序使用 C#,把声音采集程序合并到一块,不用在界面运行两个程序了。图个简洁。以下是改造网上的c#声音采集程序,运行ok。今天晚上加把劲赶出来吧,天晴了,明天要干活了。可以先看一看当时实现的心情:有调试记录记载如下:
16:33 2018-3-20 其实,这只是一个音wu(钨)的传说。(为什么是钨呢?爱迪生的灯泡尝试了多少失败,以此勉励)
10:09 2018-3-25 去噪后,效果明显。可遇见的未来(给自己的孩子看)。一个是发音准确了,一个是噪音控制住了。另外尝试统计后,总结,应再推进一步。程序已经真正的合为一体了,仅有c#表达。距离自己的一堂课(拿得出手)还有些距离。(注:虽然实现了,但是前面已经评价过了,半桶水)
好,下面是程序,c#比起c++,简单多了,如释负重。这个程序的名字叫soundctrlzoom(注:声音控制图像缩放),我们只摘抄采集声音部分,剩下的以后会在项目中再续。
首先,声明: /// <summary>
/// //for audio record
/// </summary>
public const short device = (-1);
public WaveFormat format = new WaveFormat();
private WaveInRecorder m_WIR;
public byte[] m_RecBuffer;
有WaveFormat和WaveInRecorder两个类型不认识,继续
public enum WaveFormats
{
Pcm = 1,
Float = 3
}
[StructLayout(LayoutKind.Sequential)]
public class WaveFormat
{
public short wFormatTag;
public short nChannels;
public int nSamplesPerSec;
public int nAvgBytesPerSec;
public short nBlockAlign;
public short wBitsPerSample;
public short cbSize;
public WaveFormat()//c++中你也可以看到,它是一个固定的选项,波形格式(不懂,我们不改变它)
{
wFormatTag = (short)WaveFormats.Pcm;
nChannels = 1;
nSamplesPerSec = 8000;//仍然是每秒8000的样本采样
nAvgBytesPerSec = 8000;
nBlockAlign = 1;
wBitsPerSample = 8;
cbSize = 0;
}
}
///////////////////再来
// callbacks回调函数被委托替代
public delegate void BufferDoneEventHandler(IntPtr data, int size);
internal class WaveInRecorder : IDisposable//声音采集过程,也封装成类了,c#中思路很清晰,有没有这种感觉?
{
private IntPtr m_WaveIn;
public int bufferSize;
public int bufferCount;
private WaveInBuffer m_Buffers; // linked list
private WaveInBuffer m_CurrentBuffer;
private Thread m_Thread;
private BufferDoneEventHandler m_DoneProc;
private bool m_Finished = false;
//这里使用第二个委托替代回调函数,可以对照着看,加深印象
public WaveNative.WaveDelegate m_BufferProc = new WaveNative.WaveDelegate(WaveInBuffer.WaveInProc);
public static int DeviceCount
{
get { return WaveNative.waveInGetNumDevs(); }
}
public WaveInRecorder(int device, WaveFormat format, int bufferSize, int bufferCount, BufferDoneEventHandler doneProc)
{
m_DoneProc = doneProc;
this.bufferCount = bufferCount;
this.bufferSize = bufferSize;
WaveInHelper.Try(WaveNative.waveInOpen(out m_WaveIn, device, format, m_BufferProc, 0, WaveNative.CALLBACK_FUNCTION));
AllocateBuffers(bufferSize, bufferCount);
for (int i = 0; i < bufferCount; i++)
{
SelectNextBuffer();
m_CurrentBuffer.Record();
}
WaveInHelper.Try(WaveNative.waveInStart(m_WaveIn));
m_Thread = new Thread(new ThreadStart(ThreadProc));
m_Thread.Start();
}
~WaveInRecorder()
{
Dispose();
}
public void Dispose()
{
if (m_Thread != null)
try
{
m_Finished = true;
if (m_WaveIn != IntPtr.Zero)
WaveNative.waveInReset(m_WaveIn);
WaitForAllBuffers();
m_Thread.Join();
m_DoneProc = null;
FreeBuffers();
if (m_WaveIn != IntPtr.Zero)
WaveNative.waveInClose(m_WaveIn);
}
finally
{
m_Thread = null;
m_WaveIn = IntPtr.Zero;
}
GC.SuppressFinalize(this);
}
private void WaitForAllBuffers()
{
WaveInBuffer Buf = m_Buffers;
while (Buf.NextBuffer != m_Buffers)
{
Buf.WaitFor();
Buf = Buf.NextBuffer;
}
}
private void ThreadProc()
{
while (!m_Finished)
{
Advance();
if (m_DoneProc != null && !m_Finished)
m_DoneProc(m_CurrentBuffer.Data, m_CurrentBuffer.Size);
m_CurrentBuffer.Record();
}
}
private void AllocateBuffers(int bufferSize, int bufferCount)
{
FreeBuffers();
if (bufferCount > 0)
{
m_Buffers = new WaveInBuffer(m_WaveIn, bufferSize);
WaveInBuffer Prev = m_Buffers;
try
{
for (int i = 1; i < bufferCount; i++)
{
WaveInBuffer Buf = new WaveInBuffer(m_WaveIn, bufferSize);
Prev.NextBuffer = Buf;
Prev = Buf;
}
}
finally
{
Prev.NextBuffer = m_Buffers;
}
}
}
private void FreeBuffers()
{
m_CurrentBuffer = null;
if (m_Buffers != null)
{
WaveInBuffer First = m_Buffers;
m_Buffers = null;
WaveInBuffer Current = First;
do
{
WaveInBuffer Next = Current.NextBuffer;
Current.Dispose();
Current = Next;
} while (Current != First);
}
}
private void Advance()
{
SelectNextBuffer();
m_CurrentBuffer.WaitFor();
}
private void SelectNextBuffer()
{
m_CurrentBuffer = m_CurrentBuffer == null ? m_Buffers : m_CurrentBuffer.NextBuffer;
}
}
其实可以对比C++版本,更容易理解。是
WaveInBuffer是什么鬼?实质相当于记录声音的内存块,在这里是一个类,封装了对内存块的操作,这不是更好,更安全吗?
internal class WaveInBuffer : IDisposable
{
public WaveInBuffer NextBuffer;
private AutoResetEvent m_RecordEvent = new AutoResetEvent(false);
private IntPtr m_WaveIn;
private WaveNative.WaveHdr m_Header;
private byte[] m_HeaderData;
private GCHandle m_HeaderHandle;
private GCHandle m_HeaderDataHandle;
private bool m_Recording;
internal static void WaveInProc(IntPtr hdrvr, int uMsg, int dwUser, ref WaveNative.WaveHdr wavhdr, int dwParam2)
{
if (uMsg == WaveNative.MM_WIM_DATA)
{
try
{
GCHandle h = (GCHandle)wavhdr.dwUser;
WaveInBuffer buf = (WaveInBuffer)h.Target;
buf.OnCompleted();
}
catch
{
}
}
}
public WaveInBuffer(IntPtr waveInHandle, int size)
{
m_WaveIn = waveInHandle;
m_HeaderHandle = GCHandle.Alloc(m_Header, GCHandleType.Pinned);
m_Header.dwUser = (IntPtr)GCHandle.Alloc(this);
m_HeaderData = new byte[size];
m_HeaderDataHandle = GCHandle.Alloc(m_HeaderData, GCHandleType.Pinned);
m_Header.lpData = m_HeaderDataHandle.AddrOfPinnedObject();
m_Header.dwBufferLength = size;
WaveInHelper.Try(WaveNative.waveInPrepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)));
}
~WaveInBuffer()
{
Dispose();
}
public void Dispose()
{
if (m_Header.lpData != IntPtr.Zero)
{
WaveNative.waveInUnprepareHeader(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header));
m_HeaderHandle.Free();
m_Header.lpData = IntPtr.Zero;
}
m_RecordEvent.Close();
if (m_HeaderDataHandle.IsAllocated)
m_HeaderDataHandle.Free();
GC.SuppressFinalize(this);
}
public int Size
{
get { return m_Header.dwBufferLength; }
}
public IntPtr Data
{
get { return m_Header.lpData; }
}
public bool Record()
{
lock (this)
{
m_RecordEvent.Reset();
m_Recording = WaveNative.waveInAddBuffer(m_WaveIn, ref m_Header, Marshal.SizeOf(m_Header)) == WaveNative.MMSYSERR_NOERROR;
// System.Diagnostics.Debug.Write(string.Format("\n m_WaveIn: {0}, m_Header.dwBytesRecorded: {1}", m_WaveIn, m_Header.dwBytesRecorded));
//m_WaveIn.ToPointer();
return m_Recording;
}
}
public void WaitFor()
{
if (m_Recording)
m_Recording = m_RecordEvent.WaitOne();
else
Thread.Sleep(0);
}
private void OnCompleted()
{
m_RecordEvent.Set();
m_Recording = false;
}
}
WaveNative是什么鬼?实质是封装的微软采集声音的API函数,这里也封装成类了。
internal class WaveNative //: IDisposable
{
// consts
public const int MMSYSERR_NOERROR = 0; // no error
public const int MM_WOM_OPEN = 0x3BB;
public const int MM_WOM_CLOSE = 0x3BC;
public const int MM_WOM_DONE = 0x3BD;
public const int MM_WIM_OPEN = 0x3BE;
public const int MM_WIM_CLOSE = 0x3BF;
public const int MM_WIM_DATA = 0x3C0;
public const int CALLBACK_FUNCTION = 0x00030000; // dwCallback is a FARPROC
// callbacks
public delegate void WaveDelegate(IntPtr hdrvr, int uMsg, int dwUser, ref WaveHdr wavhdr, int dwParam2);
private WaveDelegate m_BufferProc = new WaveDelegate(WaveInBuffer.WaveInProc);
[StructLayout(LayoutKind.Sequential)]
public struct WaveHdr
{
public IntPtr lpData; // pointer to locked data buffer
public int dwBufferLength; // length of data buffer
public int dwBytesRecorded; // used for input only
public IntPtr dwUser; // for client's use
public int dwFlags; // assorted flags (see defines)
public int dwLoops; // loop control counter
public IntPtr lpNext; // PWaveHdr, reserved for driver
public int reserved; // reserved for driver
}
[DllImport("winmm.dll")]
public static extern int waveInOpen(out IntPtr phwi, int uDeviceID, WaveFormat lpFormat, WaveDelegate dwCallback, int dwInstance, int dwFlags);
[DllImport("winmm.dll")]
public static extern int waveInAddBuffer(IntPtr hwi, ref WaveHdr pwh, int cbwh);
[DllImport("winmm.dll")]
public static extern int waveInPrepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
[DllImport("winmm.dll")]
public static extern int waveInUnprepareHeader(IntPtr hWaveIn, ref WaveHdr lpWaveInHdr, int uSize);
[DllImport("winmm.dll")]
public static extern int waveInStart(IntPtr hwi);
[DllImport("winmm.dll")]
public static extern int waveInGetNumDevs();
[DllImport("winmm.dll")]
public static extern int waveInReset(IntPtr hwi);
[DllImport("winmm.dll")]
public static extern int waveInClose(IntPtr hwi);
}
internal class WaveInHelper
{
public static void Try(int err)
{
if (err != WaveNative.MMSYSERR_NOERROR)
throw new Exception(err.ToString());
}
}
好,所有准备工作完成,哎哎哎,有一个委托怎么回事?好,该他上场了:在form中,增加一个按钮用来触发声音采集:
private void startRec_Click(object sender, EventArgs e)
{
m_WIR = new WaveInRecorder(device, format, 1920, 5, new BufferDoneEventHandler(DataArrived));
}
看见这个委托了吧,他的实现就是DataArrived,以下就是实现:先声明一个1920字节的数组,用来接收波形,在form中到来的波形会显示在picturebox1中。
// public byte[] m_RecBuffer;//开头已经声明过了
public void DataArrived(IntPtr data, int size)//此处为委托,不同于DefWndProc201803201439
{
try
{
if (m_RecBuffer == null || m_RecBuffer.Length < size)
{
m_RecBuffer = new byte[size];
}
int _RoiW = 480;//图像分成四段显示480*4=1920
int _RoiH = 768;//也没有什么特殊原因,只是屏幕小,还有其他占用,你也可以一次显示1920,不用如此麻烦
//显示
byte[] cutvalues = new byte[_RoiW * _RoiH * 3];
int bytes = _RoiW * _RoiH * 3;
Bitmap cutPic24 = new Bitmap(_RoiW, _RoiH, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
BitmapData _cutPic = cutPic24.LockBits(new Rectangle(0, 0, _RoiW, _RoiH), ImageLockMode.ReadWrite,
cutPic24.PixelFormat);
IntPtr ptr = _cutPic.Scan0;//得到首地址
int n = 0;
for (int i = 0; i < _RoiH; i++)//图像复原,即由8位图变成24位图像
{
for (int j = 0; j < _RoiW; j++)
{
int m = 3 * (i * _RoiW + j);
if (i <= 255)
{
n = 0 * _RoiW + j;
}
else
{
n = 1 * _RoiW + j;
}
cutvalues[m] = 0;
cutvalues[m + 1] = 0;
cutvalues[m + 2] = 0;
}
}
for (int j = 0; j < _RoiW; j++)
{
int m = 3 * ((m_RecBuffer[j]) * _RoiW + j);
cutvalues[m + 2] = 255;
}
for (int j = 0; j < _RoiW; j++)
{
int m = 3 * ((128) * _RoiW + j);
cutvalues[m] = 255;
cutvalues[m + 1] = 255;
cutvalues[m + 2] = 255;
}//第一段
for (int j = 0; j < _RoiW; j++)
{
int m = 3 * ((m_RecBuffer[j + 480] + 192) * _RoiW + j);
//cutvalues[m] = 0;
//cutvalues[m + 1] = 0;
cutvalues[m + 2] = 255;
}//第二段
for (int j = 0; j < _RoiW; j++)
{
int m = 3 * ((m_RecBuffer[j + 960] + 384) * _RoiW + j);
//cutvalues[m] = 0;
//cutvalues[m + 1] = 0;
cutvalues[m + 2] = 255;
}//第三段
for (int j = 0; j < _RoiW; j++)
{
int m = 3 * ((m_RecBuffer[j + 960+480] + 576) * _RoiW + j);
//cutvalues[m] = 0;
//cutvalues[m + 1] = 0;
cutvalues[m + 2] = 255;
}//第四段
System.Runtime.InteropServices.Marshal.Copy(cutvalues, 0, ptr, _RoiH * _RoiW * 3);
cutPic24.UnlockBits(_cutPic);
pictureBox1.Image = cutPic24;
}
catch (Exception ee)
{
return;
}
}
好,搞定,收工,回头谈采集到的声音波形处理,第一制作,观察波形(现在是人做,以后交给计算机算法),找到特征波形,保存,用来匹配,第二应用,加载特征波形,实时匹配,响应图形缩放,或者开关等,也可以用来声控关闭计算机。
待续(慢慢来!...........)每天一点小改变☺
我的邮箱[email protected];[email protected]
补充:以下是C#采集声音程序运行图(富士通T901,xp),480*4=1920字节: