录音与播放(WaveIn And WaveOut)

#define _CRT_SECURE_NO_WARNINGS        //避免时间函数报错

#include "CAdd_Recording.h"            //系统自动生成的,啥内容都没有,空的。
#include <iostream>
#include <windows.h>
#include <ctime>

#pragma comment(lib, "winmm.lib")

time_t t = time(0);

static DWORD hasRecorded = 0;    //记录已录入的数据大小
static BYTE* file = (BYTE*)malloc(sizeof(BYTE) * 512);
static FILE* f;

static BOOL flgRecored=TRUE;    //true:继续录制,false:停止录制

class MyClass
{
public:
    MyClass();
    ~MyClass();
    std::string strDataName="ICBC";
    int data=666;
private:
};

MyClass::MyClass()
{}

MyClass::~MyClass()
{}

//声明回调函数
void CALLBACK waveInProc(HWAVEIN hwi, // 设备句柄
    UINT uMsg, // 消息
    DWORD_PTR dwInstance, // 对象
    DWORD_PTR dwParam1, // 参数1
    DWORD_PTR dwParam2); // 参数2

//声明存储函数
void SaveRecord();

char* WideCharToMultiByte(wchar_t* pWCStrKey)//2023.04.27 新增WChar*转char*
{
	//第一次调用确认转换后单字节字符串的长度,用于开辟空间
	int pSize = WideCharToMultiByte(CP_OEMCP, 0, pWCStrKey, wcslen(pWCStrKey), NULL, 0, NULL, NULL);
	char* pCStrKey = new char[pSize + 1];
	//第二次调用将双字节字符串转换成单字节字符串
	WideCharToMultiByte(CP_OEMCP, 0, pWCStrKey, wcslen(pWCStrKey), pCStrKey, pSize, NULL, NULL);
	pCStrKey[pSize] = '\0';
	return pCStrKey;

	//如果想要转换成string,直接赋值即可
		//string pKey = pCStrKey;
}//定义回调函数 //何时回调此函数(过程)? void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { MyClass* user = (MyClass*)dwInstance; WAVEHDR* head_L = (WAVEHDR*)dwParam1; switch (uMsg) { case WIM_CLOSE: printf("停止录音..\n"); SaveRecord(); break; case WIM_DATA: //缓冲区满的时候,触发 {//1.Sent when the device driver is finished with a data block //2.sent using the waveInAddBuffer function. printf("缓存区已满!\n"); //printf("用户数据:(%s,%d)\n", user->strDataName, user->data); std::cout << "用户数据:" <<"名字 " +user->strDataName << ",代号 "<<user->data << std::endl; //缓存池信息 DWORD bufLength = head_L->dwBufferLength; DWORD bytesReced = head_L->dwBytesRecorded; //已录入的数据的大小 hasRecorded += bytesReced; //存储.pcm方法 No.1: if (f) { fwrite(head_L->lpData, head_L->dwBytesRecorded, 1, f); } //扩充缓存 //malloc函数对没有分配过的内存块直接进行分配(初始化内存)。 //realloc函数是在已经分配好的内存块重新进行分配(格式化内存)。 file = (BYTE*)realloc(file, hasRecorded * sizeof(BYTE)); if (file) { memcpy(&file[hasRecorded - bytesReced], head_L->lpData, bytesReced); struct tm* now = localtime(&t); printf("简报:%d:%d:%d", now->tm_hour, now->tm_min, now->tm_sec); printf("已储存:%d byte \n", hasRecorded); } if (flgRecored) {//若继续录制,则增添缓存 waveInAddBuffer(hwi, head_L, sizeof(WAVEHDR)); struct tm* now = localtime(&t); printf("addBuf:%d:%d:%d \n", now->tm_hour, now->tm_min, now->tm_sec); } }//C++中的case里,若要初始化变量,必须使用{} break;
case WIM_OPEN:
        printf("成功打开录音设备\n");
        break;

    default:
        break;
    }
}
//将数据写入指定的文件或输入/输出 (I/O) 设备。
//BOOL WriteFile(
//    [in]                HANDLE       hFile,
//    [in]                LPCVOID      lpBuffer,
//    [in]                DWORD        nNumberOfBytesToWrite,
//    [out, optional]     LPDWORD      lpNumberOfBytesWritten,
//    [in, out, optional] LPOVERLAPPED lpOverlapped
//);

//实现存储函数
void SaveRecord()
{//存储为.pcm   
    if (file)
    {   //或者存储方法 No.2:
        //fwrite(file, hasRecorded, 1, f);
    }
}

int main()
{
    f=fopen("audio.pcm", "wb");

    HWAVEIN hWaveIn;            //device:input
    HWAVEOUT hWaveOut;            //deveice:output
    WAVEFORMATEX waveform;        
    byte* pBuffer_Left, * pBuffer_Right;
    WAVEHDR head_L, head_R,head_Out;

    MyClass *user=new MyClass();

#pragma region Init_WaveDriver
    ///音频设备由设备标识符标识。 设备标识符是从系统中存在的设备数隐式确定的。 
    //设备标识符的范围从零到一个小于存在的设备数。 例如,如果系统中有两个波形音频输出设备,则有效的设备标识符为 0 和 1。
    //疑惑:每次运行时,下面的MID、PID和设备设备DriverVersion等都是固定不变的,除了设备名称会变!
    int count = waveInGetNumDevs();
    printf("音频输入数量:%d\n", count);

    WAVEINCAPS waveIncaps;
    MMRESULT mmResult = waveInGetDevCaps(0, &waveIncaps, sizeof(WAVEINCAPS));
    std::cout << "输入设备描述:"<<std::endl;
    std::cout << "设备MID(VID):" << waveIncaps.wMid << std::endl;
    std::cout << "设备PID:" << waveIncaps.wPid << std::endl;
    //std::cout << "设备DriverVersion:" << waveIncaps.vDriverVersion << std::endl;
    std::cout << "设备名称:" << WideCharToMultiByte(waveIncaps.szPname )<< std::endl;
    //std::cout << "设备支持的标准格式:" << waveIncaps.dwFormats << std::endl;
    //std::cout << "设备支持的声道(单声道1,立体声2):" << waveIncaps.wChannels << std::endl<<std::endl;

    count = waveOutGetNumDevs();
    printf("\n音频输出数量:%d\n", count);

    WAVEOUTCAPS waveOutcaps, waveOutcaps2;
    mmResult = waveOutGetDevCaps(0, &waveOutcaps, sizeof(WAVEOUTCAPS));
    std::cout << "输出设备描述:" << std::endl;
    std::cout << "设备MID(VID):" << waveOutcaps.wMid << std::endl;
    std::cout << "设备PID:" << waveOutcaps.wPid << std::endl;
    //std::cout << "设备DriverVersion:" << waveOutcaps.vDriverVersion << std::endl;
std::cout << "设备名称:" << WideCharToMultiByte(waveOutcaps.szPname) << std::endl;  
//std::cout << "设备支持的标准格式:" << waveOutcaps.dwFormats << std::endl;
    //std::cout << "设备支持的声道(单声道1,立体声2):" << waveOutcaps.wChannels << std::endl;
    //std::cout << "设备支持的可选功能:" << waveOutcaps.dwSupport << std::endl<<std::endl;

    mmResult = waveOutGetDevCaps(1, &waveOutcaps2, sizeof(WAVEOUTCAPS));
    std::cout << "\n设备MID(VID):" << waveOutcaps2.wMid << std::endl;
    std::cout << "设备PID:" << waveOutcaps2.wPid << std::endl;
    //std::cout << "设备DriverVersion:" << waveOutcaps2.vDriverVersion << std::endl;
    std::cout << "设备名称:" <<WideCharToMultiByte(waveOutcaps2.szPname) << std::endl; 

//std::cout << "设备支持的标准格式:" << waveOutcaps2.dwFormats << std::endl;
    //std::cout << "设备支持的声道(单声道1,立体声2):" << waveOutcaps2.wChannels << std::endl;
    //std::cout << "设备支持的可选功能:" << waveOutcaps2.dwSupport << std::endl;
#pragma endregion

    //注意初始化的顺序!!!
    waveform.wFormatTag = WAVE_FORMAT_PCM;
    waveform.nChannels = 2;
    waveform.nSamplesPerSec = 44100;
    waveform.wBitsPerSample = 16;    //采样精度

    waveform.nBlockAlign = (waveform.wBitsPerSample * waveform.nChannels) / 8;    //块对齐
    waveform.nAvgBytesPerSec = waveform.nBlockAlign * waveform.nSamplesPerSec;    //平均传输速率:块*频率
    waveform.cbSize = 0;            //额外空间

    pBuffer_Left = new BYTE[1024 * 10000];
    pBuffer_Right = new BYTE[1024 * 10000];
    memset(pBuffer_Left, 0, 1024 * 10000);    //内存置为0;
    memset(pBuffer_Right, 0, 1024 * 10000);    //内存置为0;

    head_L.lpData =(LPSTR) pBuffer_Left;
    head_L.dwBufferLength = 1024 * 10000;
    head_L.dwBytesRecorded = 0;
    head_L.dwUser = 0;
    head_L.dwFlags = 0;
    head_L.dwLoops = 1;        //循环的次数
    
    head_R.lpData = (LPSTR)pBuffer_Right;
    head_R.dwBufferLength = 1024 * 10000;
    head_R.dwBytesRecorded = 0;
    head_R.dwUser = 0;
    head_R.dwFlags = 0;
    head_R.dwLoops = 1;        //循环的次数

    //开始录音
    MMRESULT mRet = waveInOpen(&hWaveIn, WAVE_MAPPER, &waveform, (DWORD_PTR)waveInProc, (DWORD_PTR)user, CALLBACK_FUNCTION);
    waveInPrepareHeader(hWaveIn, &head_L, sizeof(WAVEHDR));
    waveInPrepareHeader(hWaveIn, &head_R, sizeof(WAVEHDR));
    waveInAddBuffer(hWaveIn, &head_L, sizeof(WAVEHDR));
    waveInAddBuffer(hWaveIn, &head_R, sizeof(WAVEHDR));
    waveInStart(hWaveIn);
    
    //结束录音
    getchar();
    flgRecored = FALSE;
    waveInReset(hWaveIn);

    Sleep(500);
    waveInClose(hWaveIn);
    fclose(f);
    //播放
    HANDLE wait = CreateEvent(NULL, 0, 0, NULL);
    waveOutOpen(&hWaveOut, WAVE_MAPPER, &waveform, (DWORD_PTR)wait, 0L, CALLBACK_EVENT);
    // 播放录音
    head_Out.lpData = (LPSTR)file; // 指向buffer
    head_Out.dwBufferLength = hasRecorded; // buffer大小
    head_Out.dwBytesRecorded = hasRecorded;
    head_Out.dwFlags = 0;
    head_Out.dwLoops = 1;
    ResetEvent(wait);
    waveOutPrepareHeader(hWaveOut, &head_Out, sizeof(WAVEHDR));
    waveOutWrite(hWaveOut, &head_Out, sizeof(WAVEHDR));
    /*Sleep(5000);*/
    DWORD dw = WaitForSingleObject(wait, INFINITE);
    if (dw == WAIT_OBJECT_0)
    {
        std::cout << "jieshu" << std::endl;
        return 0;
    }

    system("pause");
    return 0;
}
 
posted @ 2023-03-26 23:00  冲云霄  阅读(3)  评论(0)    收藏  举报