C++基于Windows的音频采集

C++基于Windows的音频采集

#include "stdafx.h"

#include <time.h>
#include <windows.h>
#include<mmsystem.h>
#include<mmreg.h>
#pragma  comment(lib, "winmm.lib")

struct RIFF_HEADER//文件类型
{
    char szRiffID[4]; // 'R','I','F','F'
    DWORD dwRiffSize;//DWORD最大可容纳4GB的文件大小=FILE_LENGTH-8(即减去此数据结构大小)
    char szRiffFormat[4]; // 'W','A','V','E'
};

struct WAVE_FORMAT
{
    WORD wFormatTag;
    WORD wChannels;
    DWORD dwSamplesPerSec;
    DWORD dwAvgBytesPerSec;
    WORD wBlockAlign;
    WORD wBitsPerSample;
};

struct FMT_BLOCK
{
    char szFmtID[4]; // 'f','m','t',' '
    DWORD dwFmtSize;
    WAVE_FORMAT wavFormat;
};

struct DATA_BLOCK
{
    char szDataID[4]; // 'd','a','t','a'
    DWORD dwDataSize;
};

//struct FACT_BLOCK//可先项 一般可不用
//{
//    char szFactID[4]; // 'f','a','c','t'
//    DWORD dwFactSize;
//};

WAVEFORMATEX pwfx; //声音格式


#define MAX_RUN_COUNT 4
#define BUFFER_COUNT 4
#define MAX_BUFF_SOUNDSIZE 10240
FILE *fptmp;
CHAR m_cBufferIn[BUFFER_COUNT][MAX_BUFF_SOUNDSIZE];
WAVEHDR m_pWaveHdrIn[BUFFER_COUNT];
HWAVEIN phwi; //设备句柄

bool is_stop = false;


void WaveInitFormat(LPWAVEFORMATEX m_WaveFormat, WORD nCh, DWORD nSampleRate, WORD BitsPerSample)
{
    m_WaveFormat->wFormatTag = WAVE_FORMAT_PCM;
    m_WaveFormat->nChannels = nCh;
    m_WaveFormat->nSamplesPerSec = nSampleRate;
    m_WaveFormat->nAvgBytesPerSec = nSampleRate * nCh * BitsPerSample / 8;
    m_WaveFormat->nBlockAlign = m_WaveFormat->nChannels * BitsPerSample / 8;
    m_WaveFormat->wBitsPerSample = BitsPerSample;
    m_WaveFormat->cbSize = 0;
}

DWORD CALLBACK MicCallback(HWAVEIN hwavein, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
    switch (uMsg)
    {
    case WIM_OPEN: //打开设备时消息,在此期间我们可以进行一些初始化工作
        printf("\n设备已经打开...\n");
        break;

    case WIM_DATA: //当缓存已满或者停止录音时的消息,处理这个消息可以对缓存进行重新分配,实现不限长度录音
    {
        WAVEHDR*p = ((PWAVEHDR)dwParam1); // 这就是采集到的数据指针
        DWORD len = p->dwBytesRecorded; // 这就是采集到的数据长度
       

        int i = p->dwUser;
        if (is_stop) //停止
        {
            waveInUnprepareHeader(phwi, p, sizeof(WAVEHDR)); //释放
            return 0;
        }
        
        printf("\n缓冲区%d存满...\n", p->dwUser);
        fwrite(&m_cBufferIn[i], 1, p->dwBytesRecorded, fptmp);
        waveInUnprepareHeader(phwi, p, sizeof(WAVEHDR)); //释放
        


        p->lpData = m_cBufferIn[i];
        p->dwBufferLength = MAX_BUFF_SOUNDSIZE;
        p->dwBytesRecorded = 0;
        p->dwUser = i;
        p->dwFlags = 0;
        waveInPrepareHeader(phwi, p, sizeof(WAVEHDR)); //准备内存块录音
        waveInAddBuffer(phwi, p, sizeof(WAVEHDR)); //增加内存块

        break;
    }
    case WIM_CLOSE: //关闭录音设备时的消息
        printf("\n设备已经关闭...\n");
        break;
    default:
        break;
    }
    return 0;
}

//录音函数
void Start()
{
    //获取声卡数
    int count = waveInGetNumDevs();//1
    printf("\n音频输入数量:%d\n", count);

    //获取声音输入设备
    WAVEINCAPS waveIncaps;
    MMRESULT mmResult = waveInGetDevCaps(0, &waveIncaps, sizeof(WAVEINCAPS));//2
    printf("\n音频输入设备:%s\n", waveIncaps.szPname);

    //如果正确获取到输入设备
    if (MMSYSERR_NOERROR == mmResult)
    {
        /// HWAVEIN phwi; //设备句柄
        //  WAVEFORMATEX pwfx;
        WaveInitFormat(&pwfx, 1, 8000, 8);
        printf("\n请求打开音频输入设备");
        printf("\n采样参数:单声道 8kHz 8bit\n");
        //打开录音设备
        mmResult = waveInOpen(&phwi, WAVE_MAPPER, &pwfx, (DWORD)(MicCallback), NULL, CALLBACK_FUNCTION);//3

        //如果正确打开了设备
        if (MMSYSERR_NOERROR == mmResult)
        {
            for (int i = 0; i < BUFFER_COUNT; i++)
            {
                m_pWaveHdrIn[i].lpData = m_cBufferIn[i];
                m_pWaveHdrIn[i].dwBufferLength = MAX_BUFF_SOUNDSIZE;
                m_pWaveHdrIn[i].dwBytesRecorded = 0;
                m_pWaveHdrIn[i].dwUser = i;
                m_pWaveHdrIn[i].dwFlags = 0;
                mmResult = waveInPrepareHeader(phwi, &m_pWaveHdrIn[i], sizeof(WAVEHDR));//4
                printf("\n准备缓冲区%d\n", i + 1);
            }

            //数据头准备完成
            if (MMSYSERR_NOERROR == mmResult)
            {
                for (int i = 0; i < BUFFER_COUNT; i++)
                {
                    mmResult = waveInAddBuffer(phwi, &m_pWaveHdrIn[i], sizeof(WAVEHDR));//5
                    printf("\n将缓冲区%d加入音频输入设备\n", i + 1);
                }

                //数据块准备完成
                if (MMSYSERR_NOERROR == mmResult)
                {
                    //开始录音
                    mmResult = waveInStart(phwi);//6
                    printf("\n请求开始录音\n");
                }
            }
        }
    }
}

void Stop()
{
    is_stop = true;
    Sleep(3000); //等待录音完成

    printf("Start Stop\n");
    waveInStop(phwi);

    printf("Start Reset\n");
    waveInReset(phwi);

    printf("Start Close\n");
    waveInClose(phwi);

    fclose(fptmp);
}

void Save()
{
    long len = 0;
    char *buf = NULL;
    FILE *fp;
    if (fopen_s(&fp, "temp.dat", "rb") == 0)
    {
        fseek(fp, 0, SEEK_END);;
        len = ftell(fp);
        fseek(fp, 0, SEEK_SET);
        buf = new char[len + 1];
        memset(buf, 0, sizeof(char)*(len + 1));
        fread(buf, len, 1, fp);
        fclose(fp);
    }
    if (buf == NULL)
    {
        printf("save error\n");
        return;
    }

    if (fopen_s(&fp, "output.wav", "wb") == 0)
    {
        RIFF_HEADER riff;
        WAVE_FORMAT wform;
        FMT_BLOCK fmt;
        //FACT_BLOCK fact;
        DATA_BLOCK data;
        memset(&riff, 0, sizeof(RIFF_HEADER));
        memset(&wform, 0, sizeof(WAVE_FORMAT));
        memset(&fmt, 0, sizeof(FMT_BLOCK));
        //memset(&fact, 0, sizeof(FACT_BLOCK));
        memset(&data, 0, sizeof(DATA_BLOCK));

        fmt.wavFormat.wFormatTag = WAVE_FORMAT_PCM;//1
        fmt.wavFormat.dwAvgBytesPerSec = pwfx.nAvgBytesPerSec;
        fmt.wavFormat.dwSamplesPerSec = pwfx.nSamplesPerSec;
        fmt.wavFormat.wBitsPerSample = pwfx.wBitsPerSample;
        fmt.wavFormat.wBlockAlign = pwfx.nBlockAlign;
        fmt.wavFormat.wChannels = pwfx.nChannels;

        memcpy(fmt.szFmtID, "fmt ", 4);
        fmt.dwFmtSize = 16;//一般情况下为16,如有附加信息为18
        memcpy(data.szDataID, "data", 4);
        data.dwDataSize = len;


        riff.dwRiffSize = (len + sizeof(FMT_BLOCK) + sizeof(DATA_BLOCK));
        memcpy(riff.szRiffFormat, "WAVE", 4);
        memcpy(riff.szRiffID, "RIFF", 4);

        fwrite(&riff, sizeof(RIFF_HEADER), 1, fp);//写RIFF_HEADER
        fwrite(&fmt, sizeof(FMT_BLOCK), 1, fp);//写FMT_BLOCK
        fwrite(&data, sizeof(DATA_BLOCK), 1, fp);//写数据头 DATA_BLOCK

        fwrite(buf, len, 1, fp);

        fclose(fp);
    }
}


DWORD WINAPI TimeThread(LPVOID wPame)
{
    Sleep(10000);
    return 0;
}


int main(void)
{
    errno_t errno;
    errno = fopen_s(&fptmp, "temp.dat", "wb");
    if (errno != 0)
    {
        printf("临时文件创建失败..\n");
        return 0;
    }

    Start();

    HANDLE hThread = CreateThread(NULL, NULL, TimeThread, NULL, NULL, NULL);
    WaitForSingleObject(hThread, INFINITE); //等待计时线程执行完成

    Stop();
    Save();

    system("pause");
    return 0;
}

把HDC保存成图片

把HDC保存成图片,做法如下:

先调用CopyDCToBitmap把HDC转成HBITMAP, 然后调用 SaveBmp或SaveImg

BOOL SaveBmp (HBITMAP hBitmap, CString FileName)
{
    HDC   hDC;
    //当前分辨率下每象素所占字节数  
    int   iBits;
    //位图中每象素所占字节数  
    WORD  wBitCount;
    //定义调色板大小,位图中像素字节大小,位图文件大小,写入文件字节数
    DWORD dwPaletteSize = 0, dwBmBitsSize = 0, dwDIBSize = 0, dwWritten = 0;
    //位图属性结构    
    BITMAP   Bitmap;
    //位图文件头结构  
    BITMAPFILEHEADER   bmfHdr;
    //位图信息头结构    
    BITMAPINFOHEADER   bi;
    //指向位图信息头结构      
    LPBITMAPINFOHEADER lpbi;
    //定义文件,分配内存句柄,调色板句柄    
    HANDLE  fh, hDib, hPal, hOldPal = NULL;

    //计算位图文件每个像素所占字节数    
    hDC = CreateDC(_T("DISPLAY"), NULL, NULL , NULL);
    iBits = GetDeviceCaps( hDC, BITSPIXEL) * GetDeviceCaps (hDC , PLANES);
    DeleteDC( hDC);
    if (iBits <= 1) wBitCount = 1;
    else if (iBits <= 4) wBitCount = 4;
    else if (iBits <= 8) wBitCount = 8;
    else wBitCount = 24;

    GetObject(hBitmap, sizeof(Bitmap), (LPSTR)& Bitmap);
    bi.biSize = sizeof( BITMAPINFOHEADER);
    bi.biWidth = Bitmap. bmWidth;
    bi.biHeight = Bitmap. bmHeight;
    bi.biPlanes = 1;
    bi.biBitCount = wBitCount;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrImportant = 0;
    bi.biClrUsed = 0;

    dwBmBitsSize = (( Bitmap. bmWidth * wBitCount + 31) / 32) * 4 * Bitmap.bmHeight;

    //   为位图内容分配内存    
    hDib = GlobalAlloc( GHND, dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER ));
    lpbi = (LPBITMAPINFOHEADER) GlobalLock(hDib );
    *lpbi = bi;

    //   处理调色板      
    hPal = GetStockObject( DEFAULT_PALETTE);
    if (hPal)
    {
        hDC = :: GetDC( NULL);
        //hDC   =   m_pDc->GetSafeHdc();  
        hOldPal = :: SelectPalette(hDC , (HPALETTE)hPal, FALSE);
        RealizePalette(hDC );
    }
    //   获取该调色板下新的像素值    
    GetDIBits( hDC, hBitmap, 0, ( UINT)Bitmap .bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER )
        + dwPaletteSize, (BITMAPINFO *)lpbi , DIB_RGB_COLORS);

    //   恢复调色板      
    if (hOldPal)
    {
        :: SelectPalette(hDC , (HPALETTE)hOldPal, TRUE);
        RealizePalette(hDC );
        :: ReleaseDC(NULL , hDC );
    }

    //   创建位图文件      
    fh = CreateFile( FileName, GENERIC_WRITE, 0, NULL , CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN , NULL);

    if (fh == INVALID_HANDLE_VALUE)
        return FALSE;

    //   设置位图文件头    
    bmfHdr.bfType = 0x4D42;   //   ;BM;
    dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof( BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize ;
    bmfHdr.bfSize = dwDIBSize;
    bmfHdr.bfReserved1 = 0;
    bmfHdr.bfReserved2 = 0;
    bmfHdr.bfOffBits = ( DWORD)sizeof (BITMAPFILEHEADER) + (DWORD)sizeof (BITMAPINFOHEADER) + dwPaletteSize;
    //   写入位图文件头
    WriteFile( fh, ( LPSTR)&bmfHdr , sizeof (BITMAPFILEHEADER), &dwWritten, NULL);
    //   写入位图文件其余内容
    WriteFile( fh, ( LPSTR)lpbi , dwDIBSize , &dwWritten , NULL);
    //   清除
    GlobalUnlock( hDib);
    GlobalFree( hDib);
    CloseHandle( fh);

    return TRUE;
}


// 保存成JPG文件
BOOL SaveImg (HBITMAP hBitmap, CString imgFile)
{
    BITMAP bm;
    int width, height;
    GetObject(hBitmap, sizeof(BITMAP), & bm);
    width = bm.bmWidth;
    height = bm.bmHeight;
    CDC memDC;
    memDC.CreateCompatibleDC( NULL);
    memDC.SelectObject(hBitmap);

    CImage image;
    image.Create(width, height, 32);
    BitBlt( image. GetDC(), 0, 0, width, height, memDC. m_hDC, 0, 0, SRCCOPY );

    HRESULT hResult = image. Save(imgFile );
    //image.Save(_T("new.bmp"), Gdiplus::ImageFormatBMP);   // 保存成bmp

    image.ReleaseDC();
    memDC.DeleteDC();
    return SUCCEEDED( hResult);
}


HBITMAP CopyDCToBitmap (HDC hScrDC, LPRECT lpRect)
{
    HDC  hMemDC;
    //   屏幕和内存设备描述表  
    HBITMAP hBitmap, hOldBitmap;
    //   位图句柄  
    int  nX, nY, nX2, nY2;
    //   选定区域坐标  
    int  nWidth, nHeight;
    //   位图宽度和高度  

    //   确保选定区域不为空矩形  
    if (IsRectEmpty( lpRect))
        return   NULL;

    //   获得选定区域坐标  
    nX = lpRect->left;
    nY = lpRect->top;
    nX2 = lpRect->right;
    nY2 = lpRect->bottom;
    nWidth = nX2 - nX;
    nHeight = nY2 - nY;
    //   为屏幕设备描述表创建兼容的内存设备描述表  
    hMemDC = CreateCompatibleDC( hScrDC);
    //   创建一个与屏幕设备描述表兼容的位图,函数需要与CreateCompatibleDC配合使用  
    hBitmap = CreateCompatibleBitmap( hScrDC, nWidth, nHeight);
    //   把新位图选到内存设备描述表中  
    hOldBitmap = ( HBITMAP)SelectObject (hMemDC , hBitmap );
    //   把屏幕设备描述表拷贝到内存设备描述表中
    //   StretchBlt函数从源矩形中复制一个位图到目标矩形,必要时按目前目标设备设置的模式进行图像的拉伸或压缩。
    StretchBlt( hMemDC, 0, 0, nWidth, nHeight, hScrDC, nX, nY, nWidth, nHeight, SRCCOPY);  // SRCCOPY
    //BitBlt(hMemDC,   0,   0,   nWidth,   nHeight,hScrDC,   nX,   nY,   SRCCOPY);  
    //   得到屏幕位图的句柄    

    hBitmap = (HBITMAP) SelectObject(hMemDC , hOldBitmap );
    //   清除    

    //DeleteDC(hScrDC);  
    DeleteDC( hMemDC);
    DeleteObject( hOldBitmap);
    //   返回位图句柄  
    return   hBitmap;
}

HPSocket中SendSmallFile的使用

HP–Socket 是一套通用的高性能 TCP/UDP/HTTP 通信框架,这里记录的是通过 HP–Socket 发送小文件的功能,代码如下:

p-> GetCurrentImage();

CFile file (_T("$shs.jpg"), CFile::modeRead );
auto uLen = file. GetLength();
file. Close();

TPkgHeader * header = new TPkgHeader ();
header-> seq = NET_SSH_SCREEN;
header-> body_len = uLen;

WSABUF buf ;
buf. len = sizeof (TPkgHeader );
buf. buf = (CHAR *)header ;

p-> m_pSocket-> GetTcpPullClientPtr()->SendSmallFile (_T("$shs.jpg"), &buf, NULL );

VC跨进程操作SysListview32控件

VC跨进程操作SysListview32控件,代码如下:

bool CTaskManager::StockCodeIsExist(CString stockCode)
{
    HWND hMain = CWndHelper::GetMainWnd();
    HWND hListview = ::FindWindowEx(hMain, NULL, _T("SysListView32"), NULL);
    ASSERT(hListview);


    //listview的列头句柄
    int headerhwnd = ::SendMessage(hListview, LVM_GETHEADER, 0, 0);

    //总行数
    int rows = ::SendMessage(hListview, LVM_GETITEMCOUNT, 0, 0);
    //列表列数
    int cols = ::SendMessage((HWND)headerhwnd, HDM_GETITEMCOUNT, 0, 0);

    DWORD ProcessID = NULL;
    DWORD ThreadID = GetWindowThreadProcessId(hListview, &ProcessID);

    //打开并插入进程
    HANDLE hProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_QUERY_INFORMATION, FALSE, ProcessID);
    //申请代码的内存区
    LPVOID pointer = (LVITEM*)VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);

    bool result = false;
    for (int i = 0; i < rows; i++)
    {
        int j = 0;
        // for (int j = 0; j < cols; j++)
        {
            LVITEM vItem;
            vItem.mask = LVIF_TEXT;    //说明pszText是有效的
            vItem.iItem = i;        //行号
            vItem.iSubItem = j;        //列号
            vItem.cchTextMax = 512;    //所能存储的最大的文本为256字节
            LPWSTR pItem = NULL;
            //申请内存空间
            pItem = (LPWSTR)VirtualAllocEx(hProcess, NULL, 512, MEM_COMMIT, PAGE_READWRITE);
            vItem.pszText = pItem;

            WriteProcessMemory(hProcess, pointer, &vItem, sizeof(LVITEM), NULL);
            ::SendMessage(hListview, LVM_GETITEMW, (WPARAM)i, (LPARAM)pointer);
            char ItemBuf[512];
            memset(ItemBuf, 0, 512);
            ReadProcessMemory(hProcess, pItem, ItemBuf, 512, NULL);

            CString strItemText; //i行j列的文本值
            strItemText.Format(_T("%s"), ItemBuf);
            if (strItemText == stockCode)
            {
                //选中行
                vItem.state = LVIS_SELECTED | LVIS_FOCUSED | LVIS_ACTIVATING;
                vItem.stateMask = LVIS_SELECTED;
                WriteProcessMemory(hProcess, pointer, &vItem, sizeof(LVITEM), NULL);
                SendMessage(hListview, LVM_SETITEMSTATE, (WPARAM)i, (LPARAM)pointer);

                result = true;
            }

            //释放内存空间
            VirtualFreeEx(hProcess, pItem, 0, MEM_RELEASE);
        }
    }

    //释放内存空间
    VirtualFreeEx(hProcess, pointer, 0, MEM_RELEASE);//在其它进程中释放申请的虚拟内存空间,MEM_RELEASE方式很彻底,完全回收
    CloseHandle(hProcess);//关闭打开的进程对象
    return result;
}

IronPython操作数据库

IronPython操作数据库

# -*- coding: utf-8 -*-
import clr
clr.AddReference('System.Data' )
from System .Data import *

conn = SqlClient.SqlConnection("server=42.96.190.81,2433;database=QQRobot;uid=sa;pwd=xinyang0822_xin;pooling=true;Enlist=true;connection lifetime=300;min pool size = 0;max pool size=512;packet size=1000")
conn.Open()

cmd = SqlClient.SqlCommand("select * from AIData where Id=1", conn)
reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)

while reader.Read():
    print reader[0]

reader.Close()
conn.Close()

C#使用Uri.EscapeDataString对URL参数进行编码

System.Web.HttpUtility.UrlEncode

都是对参数进行编码,要像下面这样写:

string data = string.Format(@"{{""ptwebqq"":""{0}"",""clientid"":{1},""psessionid"":""{2}"",""status"":""online""}}", _PTWebQQ, ClientID, PSessionID);
data = "r=" + Uri.EscapeDataString(data);

而不要把r=也编码进去!!!

血的教训,一晚上加一上午都浪费在这上面了!!!!

而python的urllib.parse.urlencode传入的是字典,所以避免了这个坑。

.NET使用UDE.CSharp识别文件编码

.NET中识别文件编码、识别字符集,有一个比较好用的包UDE.CSharp可以实现,这个包是Mozilla出品的,又叫Mozilla Universal Charset Detector。

首先在nuget下载安装UED:

使用样例如下:

private void btnImport_Click(object sender, EventArgs e)
{
    OpenFileDialog dlg = new OpenFileDialog();
    dlg.DefaultExt = ".txt";
    dlg.Filter = "txt文件|*.txt";
    if (dlg.ShowDialog() == System.Windows.Forms. DialogResult.OK)
    {
        _list.Clear();
        string filename = dlg.FileName;

        //使用Mozilla Universal Charset Detector侦测文件编码
        string charset = "UTF-8";
        using ( FileStream fs = File .OpenRead(filename))
        {
            Ude. CharsetDetector cdet = new Ude.CharsetDetector ();
            cdet.Feed(fs);
            cdet.DataEnd();
            if (cdet.Charset != null)
            {
                charset = cdet.Charset;
            }
        }

        string[] lines = File.ReadAllLines(filename, Encoding.GetEncoding(charset));

        foreach ( var line in lines)
        {
            //去重
            if (_list.IndexOf(line) == -1)
            {
                _list.Add(line);
            }
        }
    }
}