WinAPI: waveInUnprepareHeader - 清除由 waveInPrepareHeader 完成的准备

本文详细介绍了WaveInUnprepareHeader函数的使用方法及其参数意义,包括设备句柄、TWaveHdr结构指针等,并解释了TWaveHdr结构的具体内容及dwFlags标志位的含义。

提示: 
设备写满缓冲区返回给程序后, 须调用此函数;
释放(GlobalFree)缓冲区前, 须调用此函数;
取消一个尚未准备的缓冲区将无效, 但函数返回 0


//声明:
waveInUnprepareHeader(
  hWaveIn: HWAVEIN;      {设备句柄}
  lpWaveInHdr: PWaveHdr; {TWaveHdr 结构的指针}
  uSize: UINT            {TWaveHdr 结构大小}
): MMRESULT;             {成功返回 0; 可能的错误值见下:}

MMSYSERR_INVALHANDLE = 5;  {设备句柄无效}
MMSYSERR_HANDLEBUSY  = 12; {设备已被另一线程使用}
WAVERR_STILLPLAYING  = 33; {缓冲区还在队列中}

//TWaveHdr 是 wavehdr_tag 结构的重定义
wavehdr_tag = record
  lpData: PChar;          {指向波形数据缓冲区}
  dwBufferLength: DWORD;  {波形数据缓冲区的长度}
  dwBytesRecorded: DWORD; {若首部用于输入, 指出缓冲区中的数据量}
  dwUser: DWORD;          {指定用户的32位数据}
  dwFlags: DWORD;         {缓冲区标志}
  dwLoops: DWORD;         {循环播放次数, 仅用于输出缓冲区}
  lpNext: PWaveHdr;       {保留}
  reserved: DWORD;        {保留}
end;

//TWaveHdr 中的 dwFlags 的可选值:
WHDR_DONE      = $00000001; {设备已使用完缓冲区, 并返回给程序}
WHDR_PREPARED  = $00000002; {waveInPrepareHeader 或 waveOutPrepareHeader 已将缓冲区准备好}
WHDR_BEGINLOOP = $00000004; {缓冲区是循环中的第一个缓冲区, 仅用于输出}
WHDR_ENDLOOP   = $00000008; {缓冲区是循环中的最后一个缓冲区, 仅用于输出}
WHDR_INQUEUE   = $00000010; { reserved for driver }
### UG C++二次开发:使用winmm.lib录制音频并保存为WAV文件(多线程实现) 在UG二次开发中,通过`winmm.lib`库实现音频录制并保存为WAV文件,同时使用`_beginthreadex()`创建独立线程避免阻塞主线程,步骤如下: --- #### **1. 包含头文件和库** ```cpp #include <Windows.h> #include <mmsystem.h> #include <process.h> // 多线程支持 #pragma comment(lib, "winmm.lib") ``` --- #### **2. 定义全局变量和结构** ```cpp // 录音设备句柄 HWAVEIN hWaveIn = NULL; // 音频缓冲区结构 WAVEHDR wHdr[2]; // 双缓冲区交替使用 // 文件句柄 HANDLE hFile = INVALID_HANDLE_VALUE; // 线程控制标志 volatile bool g_bRecording = false; ``` --- #### **3. 实现WAV文件头结构** ```cpp struct WAVHeader { char riff[4] = {'R','I','F','F'}; DWORD fileSize; char wave[4] = {'W','A','V','E'}; char fmt[4] = {'f','m','t',' '}; DWORD fmtSize = 16; WORD audioFormat = 1; // PCM格式 WORD channels; DWORD sampleRate; DWORD byteRate; WORD blockAlign; WORD bitsPerSample; char data[4] = {'d','a','t','a'}; DWORD dataSize; }; ``` --- #### **4. 录音线程函数** ```cpp unsigned __stdcall RecordThread(void* pParam) { // 1. 设置音频格式 WAVEFORMATEX wfx; wfx.wFormatTag = WAVE_FORMAT_PCM; wfx.nChannels = 1; // 单声道 wfx.nSamplesPerSec = 44100; // 44.1kHz采样率 wfx.wBitsPerSample = 16; // 16位深度 wfx.nBlockAlign = wfx.nChannels * wfx.wBitsPerSample / 8; wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; wfx.cbSize = 0; // 2. 打开录音设备 if (waveInOpen(&hWaveIn, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) { return 1; // 设备打开失败 } // 3. 初始化缓冲区 for (int i = 0; i < 2; i++) { wHdr[i].lpData = new char[BUFFER_SIZE]; // BUFFER_SIZE=4096 wHdr[i].dwBufferLength = BUFFER_SIZE; wHdr[i].dwFlags = 0; waveInPrepareHeader(hWaveIn, &wHdr[i], sizeof(WAVEHDR)); waveInAddBuffer(hWaveIn, &wHdr[i], sizeof(WAVEHDR)); } // 4. 开始录音 waveInStart(hWaveIn); g_bRecording = true; // 5. 循环处理音频数据 while (g_bRecording) { for (int i = 0; i < 2; i++) { if (wHdr[i].dwFlags & WHDR_DONE) { // 写入WAV文件 DWORD bytesWritten; WriteFile(hFile, wHdr[i].lpData, wHdr[i].dwBytesRecorded, &bytesWritten, NULL); // 重新提交缓冲区 waveInUnprepareHeader(hWaveIn, &wHdr[i], sizeof(WAVEHDR)); waveInPrepareHeader(hWaveIn, &wHdr[i], sizeof(WAVEHDR)); waveInAddBuffer(hWaveIn, &wHdr[i], sizeof(WAVEHDR)); } } Sleep(10); // 避免CPU占用过高 } // 6. 清理资源 waveInStop(hWaveIn); for (int i = 0; i < 2; i++) { waveInUnprepareHeader(hWaveIn, &wHdr[i], sizeof(WAVEHDR)); delete[] wHdr[i].lpData; } waveInClose(hWaveIn); return 0; } ``` --- #### **5. 主线程调用** ```cpp void StartRecording() { // 创建WAV文件 hFile = CreateFile(L"record.wav", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // 写入文件头(预留空间) WAVHeader header; header.channels = 1; header.sampleRate = 44100; header.bitsPerSample = 16; // ...计算其他字段 WriteFile(hFile, &header, sizeof(header), NULL, NULL); // 启动录音线程 HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, RecordThread, NULL, 0, NULL); } void StopRecording() { g_bRecording = false; // 等待线程结束 WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); // 更新WAV文件头(填充实际数据大小) SetFilePointer(hFile, 0, NULL, FILE_BEGIN); // ...重新计算并写入header CloseHandle(hFile); } ``` --- #### **关键注意事项** 1. **双缓冲区机制**:交替使用两个缓冲区确保连续录制 2. **线程安全**: - 使用`volatile bool`控制线程退出 - `_beginthreadex`比`CreateThread`更安全(C运行时库兼容) 3. **资源释放**: - 必须调用`waveInUnprepareHeader`释放资源 - 结束时更新WAV文件头数据大小字段 4. **UG主线程保护**: ```cpp // 在UG菜单回调中调用 extern "C" DllExport void ufusr(char* param, int* retcode, int paramLen) { StartRecording(); // 非阻塞调用 } ``` --- #### **错误处理建议** - 检查API返回值: ```cpp if (waveInAddBuffer(...) != MMSYSERR_NOERROR) { // 处理错误 } ``` - 使用`waveInGetErrorText`获取错误描述 我在使用上述代码时,录出来的.wav语音并不能播放,语音内容时间为零秒,.wav却不是0kb,他有大小,是什么原因?如何修改?
最新发布
09-13
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值