Windows软件插件-写wav
下载本插件
本插件,将PCM音频流写入WAV音频文件。或将PCM音频流压缩为ALAW格式,写入WAV文件。可以创作大文件(超过4字节所能表示的大小)。插件类型为DLL,可以在win32和MFC程序中使用。使用本插件创建的ALAW格式WAV音频文件,Windows播放器不能播放,可以使用其它播放器播放。
使用方法
首先,加载本“写wav”DLL,获得DLL模块句柄。
HMODULE hWav = LoadLibrary(L"写wav.dll");//加载“写wav”DLL模块
获取所有导出函数的地址。
typedef int(__cdecl *MYPROC_VOID)();
typedef int(__cdecl *MYPROC_WAV_Init)(int index, WAV_INIT init);
typedef int(__cdecl *MYPROC_ISample)(int index, BYTE* pB, LONG len);
typedef int(__cdecl *MYPROC_IVOID)(int index);MYPROC_VOID WAV_Create=NULL;
MYPROC_WAV_Init WAV_Init=NULL;
MYPROC_ISample WAV_Sample=NULL;
MYPROC_IVOID WAV_Run=NULL;
MYPROC_IVOID WAV_Stop=NULL;
MYPROC_IVOID WAV_Exit=NULL;
MYPROC_IVOID WAV_GetState=NULL;
MYPROC_VOID WAV_DestroyAll=NULL;if (hWav){WAV_Create=(MYPROC_VOID)GetProcAddress(hWav, "Create");//获取“创建写wav类对象”函数地址WAV_Init=(MYPROC_WAV_Init)GetProcAddress(hWav, "Init");//获取“初始化”函数地址WAV_Sample = (MYPROC_ISample)GetProcAddress(hWav, "WriteSample");//获取“写样本”函数地址WAV_Run=(MYPROC_IVOID)GetProcAddress(hWav, "Run");//“运行”函数WAV_Stop = (MYPROC_IVOID)GetProcAddress(hWav, "Stop");//“停止”函数WAV_Exit = (MYPROC_IVOID)GetProcAddress(hWav, "Exit");//“退出”函数WAV_GetState = (MYPROC_IVOID)GetProcAddress(hWav, "GetState");//“获取状态”函数WAV_DestroyAll=(MYPROC_VOID)GetProcAddress(hWav, "DestroyAll");//“销毁所有类对象”函数}
调用“创建”函数,创建一个写wav类对象。对象在DLL内部,外部无法直接访问。
WAV_Create(); //创建写wav类对象。可以多次调用该函数,以创建多个对象;函数返回对象索引,索引从0开始
提供初始化参数,调用“初始化”函数。初始化函数创建了写wav线程。在线程中,创建wav文件,写wav文件头,格式块。
struct WAV_INIT//“初始化参数”结构
{WCHAR* Path;//输出文件路径int FormatType;//压缩方式。仅支持PCM(值1),ALAW(值6)WORD nChannels;//声道数DWORD SamplesPerSec;//音频采样率
};WAV_INIT WavInit;WavInit.Path = L"D:\\某名称.wav";//提供wav输出文件路径WavInit.FormatType = 1;//PCMWavInit.nChannels = 2;//2声道WavInit.SamplesPerSec = 48000;//采样率48000WAV_Init(0, WavInit);//参数1为“写wav类对象”索引。如果只创建了一个对象,使用索引0初始化该对象,如果有第二个对象,使用索引1
调用“运行”函数。函数调用后,可以写入样本。
WAV_Run(0);//参数0,表示运行第一个类对象
反复调用“写样本”函数,写入样本。函数的参数1为类对象索引;参数2,提供包含wav数据的样本缓冲区指针;参数3,样本的字节大小。
WAV_Sample(0, pB, len);//写wav样本。pB类型为BYTE*,len类型为LONG
调用“停止”函数,可以暂停写入样本。即使此时“写样本”函数仍在调用,也不会将样本写入wav文件。
WAV_Stop(0);//参数0,表示暂停第一个类对象
调用“停止”和“退出”函数。结束写样本,完善wav文件,包括指定文件大小,数据大小。退出写wav线程。
WAV_Stop(0);WAV_Exit(0);
在程序退出时,销毁写wav对象。
WAV_DestroyAll();//函数可以销毁创建的所有对象
“写wav”DLL的全部代码
创建DLL时,需指定“在共享DLL中使用MFC”。
WavWriter.h
#pragma once
#include <SDKDDKVer.h>#define WIN32_LEAN_AND_MEAN // 从 Windows 头中排除极少使用的资料
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // 某些 CString 构造函数将是显式的
#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // 移除对话框中的 MFC 控件支持#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN // 从 Windows 头中排除极少使用的资料
#endif#include <afx.h>
#include <afxwin.h> // MFC 核心组件和标准组件
#include <afxext.h> // MFC 扩展
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h> // MFC 对 Internet Explorer 4 公共控件的支持
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC 对 Windows 公共控件的支持
#endif // _AFX_NO_AFXCMN_SUPPORT// Windows 头文件:
#include <windows.h>struct WAV_INIT
{WCHAR* Path;//输出文件路径int FormatType;//压缩方式。仅支持PCM(值1),ALAW(值6)WORD nChannels;//声道数DWORD SamplesPerSec;//音频采样率
};class CQueue//队列
{
public:CQueue(UINT size, HANDLE hExit);~CQueue();BOOL Add(BYTE* pB, LONG len);BOOL Reduce(BYTE*& pB, LONG& len);UINT mSize;BYTE* pBuffer = NULL;BYTE* pAdd = NULL, *pReduce = NULL;HANDLE hAdd = NULL;HANDLE hReduce = NULL;HANDLE mhExit = NULL;
};class WavWriter
{
public:WavWriter();~WavWriter();WAV_INIT mInit;//初始化结构对象HANDLE hStop = NULL;//“停止”事件句柄HANDLE hExit = NULL;//“退出”事件句柄HANDLE hThread = NULL;//线程句柄HANDLE hReady = NULL;CQueue* pQueue = NULL;//样本队列BOOL mRun = FALSE;void Run();void Stop();void Exit();CString WavFilePath;//wav输出文件路径BOOL Init(WAV_INIT init);//初始化函数void WriteSample(BYTE* pB, LONG len){DWORD dw = WaitForSingleObject(hStop, 0);//检测“停止”信号if (dw == WAIT_OBJECT_0)//如果“停止”有信号,不将样本加入队列return;if (pQueue)pQueue->Add(pB, len);}
};
WavWriter.cpp
#include "WavWriter.h"CQueue::CQueue(UINT size, HANDLE hExit)
{mSize = size;mhExit = hExit;pBuffer = new BYTE[size * 10];pAdd = pReduce = pBuffer;hAdd = CreateSemaphore(NULL, 0, 10, NULL);//创建“已用”信号量,初始计数0,最大计数10hReduce = CreateSemaphore(NULL, 10, 10, NULL);//创建“可用”信号量,初始计数10,最大计数10
}
CQueue::~CQueue()
{delete[] pBuffer; pBuffer = NULL;CloseHandle(hAdd); CloseHandle(hReduce);
}BOOL CQueue::Add(BYTE* pB, LONG len)
{HANDLE h[2] = { hReduce, mhExit };DWORD dw = WaitForMultipleObjects(2, h, FALSE, INFINITE);//“可用”信号量减1,无限期等待if (dw == 1)return FALSE;//如果有“退出”信号,返回。消除阻塞CopyMemory(pAdd, &len, 4); pAdd += 4;CopyMemory(pAdd, pB, len); pAdd += mSize - 4;if (pAdd > pBuffer + mSize * 9)pAdd = pBuffer;LONG Pre;ReleaseSemaphore(hAdd, 1, &Pre);//“已用”信号量加1return TRUE;
}BOOL CQueue::Reduce(BYTE*& pB, LONG& len)
{HANDLE h[2] = { hAdd, mhExit };DWORD dw = WaitForMultipleObjects(2, h, FALSE, INFINITE);//“已用”信号量减1,无限期等待if (dw == 1)return FALSE;//如果有“退出”信号,返回。消除阻塞CopyMemory(&len, pReduce, 4); pReduce += 4;CopyMemory(pB, pReduce, len); pReduce += mSize - 4;if (pReduce > pBuffer + mSize * 9)pReduce = pBuffer;LONG Pre;ReleaseSemaphore(hReduce, 1, &Pre);//“可用”信号量加1return TRUE;
}WavWriter::WavWriter()
{hStop = CreateEvent(NULL, TRUE, TRUE, NULL);hExit = CreateEvent(NULL, TRUE, FALSE, NULL);hReady = CreateEvent(NULL, FALSE, FALSE, NULL);//创建“线程初始化完成”事件,自动重置,初始无信号
}WavWriter::~WavWriter()
{CloseHandle(hReady); CloseHandle(hStop); CloseHandle(hExit);
}unsigned char linear2alaw(int pcm_val);DWORD WINAPI WriterThread(LPVOID lp)
{WavWriter* pWriter = (WavWriter*)lp;pWriter->pQueue = new CQueue(1000000 + 4, pWriter->hExit);CFile F;F.Open(pWriter->WavFilePath, CFile::modeCreate | CFile::modeWrite);F.Write("RF64", 4);DWORD FileSize = UINT32_MAX;F.Write(&FileSize, 4);F.Write("WAVE", 4);F.Write("ds64", 4);DWORD ds64Size = 28;F.Write(&ds64Size, 4);//ds64块大小ULONGLONG dsFileSizePos = F.GetPosition();//记录文件大小位置ULONGLONG dsFileSize = 0;//此时并非实际大小F.Write(&dsFileSize, 8); //文件大小 = 文件总大小 - 8ULONGLONG dsDataSize = 0;//此时并非实际大小F.Write(&dsDataSize, 8);//数据大小ULONGLONG tableSize = 0;F.Write(&tableSize, 8);DWORD xw = 0;F.Write(&xw, 4);//保留F.Write("fmt ", 4);//fmtDWORD fmtSize = 16;F.Write(&fmtSize, 4);//fmt块大小WORD Format;Format = (WORD)pWriter->mInit.FormatType;F.Write(&Format, 2);//音频格式WORD nch = pWriter->mInit.nChannels;F.Write(&nch, 2);//声道数DWORD nSamples = pWriter->mInit.SamplesPerSec;F.Write(&nSamples, 4);//采样率 DWORD nBytes;if (pWriter->mInit.FormatType == 6)//ALAW{nBytes = nSamples * nch;}else//PCM{nBytes = nSamples * nch * 2;}F.Write(&nBytes, 4);//传输率 WORD block;if (pWriter->mInit.FormatType == 6){block = nch;}else//PCM{block = nch * 2;}F.Write(&block, 2);//块对齐 WORD bits;if (pWriter->mInit.FormatType == 6){bits = 8;}else{bits = 16;}F.Write(&bits, 2);//样本位数F.Write("data", 4);//写数据块标识DWORD DataSize = UINT32_MAX;F.Write(&DataSize, 4);ULONGLONG DataStar = F.GetPosition();BYTE* pS = new BYTE[1000000]; LONG len;BYTE* pD = NULL;if (pWriter->mInit.FormatType == 6){pD = new BYTE[500000];}SetEvent(pWriter->hReady);//发送“线程初始化完成”信号Agan:DWORD mStop = WaitForSingleObject(pWriter->hStop, 0);if (mStop != WAIT_OBJECT_0)//如果“停止”无信号{BOOL BReduce = pWriter->pQueue->Reduce(pS, len);//从队列读取样本if (BReduce)//如果读取样本成功{if (pWriter->mInit.FormatType == 6){short sh; BYTE data;for (int i = 0; i < len / 2; i++)//将PCM音频数据转换为ALAW数据{CopyMemory(&sh, pS + i * 2, 2);data = linear2alaw(sh);CopyMemory(pD + i, &data, 1);}F.Write(pD, len / 2);}else//PCM{F.Write(pS, len);}}}DWORD mExit = WaitForSingleObject(pWriter->hExit, 0);if (mExit == WAIT_OBJECT_0)//有“退出”信号{dsDataSize = F.GetPosition() - DataStar;dsFileSize = F.GetPosition() - 8;F.Seek(dsFileSizePos, CFile::begin);//移动文件指针到“文件大小”位置F.Write(&dsFileSize, 8);//写文件大小F.Write(&dsDataSize, 8);//写数据大小tableSize = dsDataSize / block;F.Write(&tableSize, 8);F.Close();Sleep(200);delete[] pS; if(pD)delete[] pD;delete pWriter->pQueue;return 0;}goto Agan;
}BOOL WavWriter::Init(WAV_INIT init)
{DWORD dw = WaitForSingleObject(hThread, 0);if (dw == WAIT_TIMEOUT)return FALSE;//如果线程已存在,返回if (init.FormatType != 1 && init.FormatType != 6){MessageBox(NULL, L"只允许PCM(1)和ALAW(6)", L"写WAV", MB_OK); return FALSE;}mInit = init;WavFilePath = (CString)init.Path;//wav输出文件路径ResetEvent(hExit);//设置“退出”无信号SetEvent(hStop);//设置“停止”有信号ResetEvent(hReady); //设置“线程初始化完成”无信号hThread = CreateThread(NULL, 0, WriterThread, this, 0, NULL);WaitForSingleObject(hReady, INFINITE);//等待“初始化完成”信号mRun = TRUE;return TRUE;
}void WavWriter::Run()
{ResetEvent(hStop);//设置“停止”无信号
}void WavWriter::Stop()
{SetEvent(hStop);//设置“停止”有信号
}void WavWriter::Exit()
{mRun = FALSE;SetEvent(hStop);//设置“停止”有信号SetEvent(hExit);//设置“退出”有信号WaitForSingleObject(hThread, INFINITE);//等待线程退出
}#define QUANT_MASK (0xf)
#define SEG_SHIFT (4) static short seg_end[8] = { 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF };unsigned char _u2a[128] = {1, 1, 2, 2, 3, 3, 4, 4,5, 5, 6, 6, 7, 7, 8, 8,9, 10, 11, 12, 13, 14, 15, 16,17, 18, 19, 20, 21, 22, 23, 24,25, 27, 29, 31, 33, 34, 35, 36,37, 38, 39, 40, 41, 42, 43, 44,46, 48, 49, 50, 51, 52, 53, 54,55, 56, 57, 58, 59, 60, 61, 62,64, 65, 66, 67, 68, 69, 70, 71,72, 73, 74, 75, 76, 77, 78, 79,81, 82, 83, 84, 85, 86, 87, 88,89, 90, 91, 92, 93, 94, 95, 96,97, 98, 99, 100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128
};unsigned char _a2u[128] = {1, 3, 5, 7, 9, 11, 13, 15,16, 17, 18, 19, 20, 21, 22, 23,24, 25, 26, 27, 28, 29, 30, 31,32, 32, 33, 33, 34, 34, 35, 35,36, 37, 38, 39, 40, 41, 42, 43,44, 45, 46, 47, 48, 48, 49, 49,50, 51, 52, 53, 54, 55, 56, 57,58, 59, 60, 61, 62, 63, 64, 64,65, 66, 67, 68, 69, 70, 71, 72,73, 74, 75, 76, 77, 78, 79, 79,80, 81, 82, 83, 84, 85, 86, 87,88, 89, 90, 91, 92, 93, 94, 95,96, 97, 98, 99, 100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127
};static int search(int val, short *table, int size)
{int i;for (i = 0; i < size; i++) {if (val <= *table++)return (i);}return (size);
}unsigned char linear2alaw(int pcm_val)
{int mask;int seg;unsigned char aval;if (pcm_val >= 0){mask = 0xD5;}else{mask = 0x55;pcm_val = -pcm_val - 1;}seg = search(pcm_val, seg_end, 8);if (seg >= 8)return (0x7F ^ mask);else{aval = seg << SEG_SHIFT;if (seg < 2)aval |= (pcm_val >> 4) & QUANT_MASK;elseaval |= (pcm_val >> (seg + 3)) & QUANT_MASK;return (aval ^ mask);}
}WavWriter* pWavWriter[10];//类对象指针数组
int mNu = -1;#ifdef __cplusplus // If used by C++ code,
extern "C" { // we need to export the C interface
#endif__declspec(dllexport) int __cdecl Create()//创建类对象{mNu++;if (mNu > 9)return -1;//最多创建10个类对象pWavWriter[mNu] = new WavWriter();return mNu;}__declspec(dllexport) int __cdecl Init(int index, WAV_INIT WavInit)//初始化指定对象{if (pWavWriter[index]->Init(WavInit))return 1;return 0;}__declspec(dllexport) int __cdecl WriteSample(int index, BYTE* pB, LONG len)//指定对象写样本{pWavWriter[index]->WriteSample(pB, len);return 0;}__declspec(dllexport) int __cdecl GetState(int index)//获取指定对象状态{return (int)pWavWriter[index]->mRun;}__declspec(dllexport) int __cdecl Run(int index)//运行指定对象{pWavWriter[index]->Run();return 0;}__declspec(dllexport) int __cdecl Stop(int index)//停止指定对象{pWavWriter[index]->Stop();return 0;}__declspec(dllexport) int __cdecl Exit(int index)//退出指定对象{pWavWriter[index]->Exit();return 0;}__declspec(dllexport) int __cdecl DestroyAll()//销毁所有对象{for (int i = 0; i <= mNu; i++){delete pWavWriter[i];}return 0;}#ifdef __cplusplus
}
#endif