当前位置: 首页 > news >正文

Windows软件插件-写mp3

下载本插件
本插件将PCM音频流编码为MP3,写入MP3音频文件。插件类型为DLL,可以在win32和MFC程序中使用。

使用方法

首先,加载本“写mp3”DLL,获得DLL模块句柄。

	HMODULE hMp3 = LoadLibrary(L"写mp3.dll");//加载写mp3 DLL

获取所有导出函数的地址。

struct WAV_INIT //mp3初始化结构
{WCHAR* Path;//输出文件路径int FormatType;//压缩方式。仅支持PCM(值1),ALAW(值6)WORD nChannels;//声道数DWORD SamplesPerSec;//音频采样率
};
typedef int(__cdecl *MYPROC_VOID)();
typedef int(__cdecl *MYPROC_MP3_Init)(int index, MP3_INIT init);
typedef int(__cdecl *MYPROC_ISample)(int index, BYTE* pB, LONG len);
typedef int(__cdecl *MYPROC_IVOID)(int index);
MYPROC_VOID MP3_Create=NULL;
MYPROC_MP3_Init MP3_Init=NULL;
MYPROC_ISample MP3_Sample=NULL;
MYPROC_IVOID MP3_Run=NULL;
MYPROC_IVOID MP3_Stop=NULL;
MYPROC_IVOID MP3_Exit=NULL;
MYPROC_IVOID MP3_GetState=NULL;
MYPROC_VOID MP3_DestroyAll=NULLif (hMp3){MP3_Create= (MYPROC_VOID)GetProcAddress(hMp3, "Create");//“创建类对象”函数MP3_Init=(MYPROC_MP3_Init)GetProcAddress(hMp3, "Init");//“初始化指定类对象”函数MP3_Sample=(MYPROC_ISample)GetProcAddress(hMp3, "WriteSample");//“写样本”函数MP3_Run = (MYPROC_IVOID)GetProcAddress(hMp3, "Run");//“运行”函数MP3_Stop = (MYPROC_IVOID)GetProcAddress(hMp3, "Stop");//“停止”函数MP3_Exit = (MYPROC_IVOID)GetProcAddress(hMp3, "Exit");//“退出”函数MP3_GetState = (MYPROC_IVOID)GetProcAddress(hMp3, "GetState");//“获取指定对象状态”函数MP3_DestroyAll = (MYPROC_VOID)GetProcAddress(hMp3, "DestroyAll");//“销毁所有对象”函数}

调用“创建”函数,创建一个写mp3类对象。对象在DLL内部,外部无法直接访问。只能通过对象的索引和DLL导出函数,调用该对象的函数。

		MP3_Create();//创建写mp3类对象。可以多次调用该函数,以创建多个对象;函数返回对象索引,索引从0开始

提供初始化参数,调用“初始化”函数。初始化函数创建了写mp3线程。在线程中,创建样本队列,创建mp3编码器,为编码器指定输出输入媒体类型;创建mp3文件,写mp3文件头。

		MP3_INIT Mp3Init;Mp3Init.Path = L"D:\\文件名.mp3";Mp3Init.nChannels = 2;//声道数Mp3Init.SamplesPerSec = 48000;//采样率。仅支持48000,44100, 32000采样率MP3_Init(0, Mp3Init);//参数1为“写mp3类对象”索引。函数初始化第1个对象

调用“运行”函数。函数调用后,可以写入样本。

		MP3_Run(0);//运行第1个对象

反复调用“写样本”函数,函数将样本添加到样本队列。样本大小须小于或等于1M。函数的参数1为对象索引;参数2,提供包含PCM音频数据的样本缓冲区指针;参数3,样本的字节大小。

		MP3_Sample(0,pB,len);//pB类型为BYTE*,len类型为LONG

调用“停止”函数,可以暂停写入样本。即使此时“写样本”函数仍在调用,也不会将样本写入mp3文件。

		MP3_Stop(0);

调用“停止”和“退出”函数。结束写样本。退出写mp3线程。

		MP3_Stop(0);MP3_Exit(0);

在程序退出时,销毁写mp3对象。

		MP3_DestroyAll();//函数可以销毁创建的所有对象

“写mp3”DLL的全部代码

创建DLL项目时,需指定“在共享DLL中使用MFC”。
Mp3Writer.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>#include "mfapi.h"
#include "mftransform.h"
#include "mferror.h"
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfuuid")#ifndef  SAFE_RELEASE
#define SAFE_RELEASEtemplate <class T> void SafeRelease(T** ppT)
{if (*ppT){(*ppT)->Release();*ppT = NULL;}
}#endif //SAFE_RELEASEstruct MP3_INIT {WCHAR* Path;//输出文件路径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 Mp3Writer
{
public:Mp3Writer();~Mp3Writer();HANDLE hStop = NULL;//“停止”事件句柄HANDLE hExit = NULL;//“退出”事件句柄HANDLE hReady = NULL;MP3_INIT mInit;//初始化结构对象HANDLE hThread = NULL;//线程句柄CQueue* pQueue = NULL;//样本队列BOOL mRun = FALSE;BOOL Init(MP3_INIT init);//初始化函数CString FilePath;//wav输出文件路径void WriteSample(BYTE* pB, LONG len);void Run();void Stop();void Exit();
};

Mp3Writer.cpp

#include "Mp3Writer.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;
}Mp3Writer::Mp3Writer()
{hStop = CreateEvent(NULL, TRUE, TRUE, NULL);hExit = CreateEvent(NULL, TRUE, FALSE, NULL);hReady = CreateEvent(NULL, FALSE, FALSE, NULL);//创建“线程初始化完成”事件,自动重置,初始无信号
}Mp3Writer::~Mp3Writer()
{CloseHandle(hStop); CloseHandle(hExit); CloseHandle(hReady);
}HRESULT GetOutput(IMFTransform *pMP3Encoder, CFile* pF)//获取编码器输出
{
CreateOutBuffer:IMFMediaBuffer* pMFOutBuffer = NULL;HRESULT hr = MFCreateMemoryBuffer(1000000, &pMFOutBuffer);//创建输出媒体基础缓冲区,大小1Mif (hr != S_OK || pMFOutBuffer == NULL)//如果创建失败{Sleep(1); goto CreateOutBuffer;//再次创建}BYTE* pD = NULL;hr = pMFOutBuffer->Lock(&pD, NULL, NULL);
CreateOutSample:IMFSample* pMFOutSample = NULL;hr = MFCreateSample(&pMFOutSample);//创建输出媒体基础样本if (hr != S_OK || pMFOutSample == NULL)//如果创建失败{Sleep(1); goto CreateOutSample;//再次创建}hr = pMFOutSample->AddBuffer(pMFOutBuffer);//添加缓冲区到媒体基础样本MFT_OUTPUT_DATA_BUFFER OD;OD.dwStreamID = NULL;OD.pSample = pMFOutSample;//须为MP3编码器指定输出样本OD.dwStatus = 0;OD.pEvents = NULL;DWORD status = 0;hr = pMP3Encoder->ProcessOutput(MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER, 1, &OD, &status);//获取MP3编码器输出数据。MP3编码器将输出数据,输出到刚才创建的输出样本的缓冲区内if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)//如果MFT需要更多的输入数据,此时已不可获取输出{SafeRelease(&pMFOutBuffer); SafeRelease(&pMFOutSample); SafeRelease(&OD.pEvents);//释放媒体基础缓冲区,媒体基础样return S_OK;}if (hr == S_OK)//如果成功获得输出数据{DWORD L;hr = pMFOutSample->GetTotalLength(&L);//获取MP3编码器输出样本有效数据长度pF->Write(pD, L);//将样本数据写入MP3文件}SafeRelease(&pMFOutBuffer); SafeRelease(&pMFOutSample); SafeRelease(&OD.pEvents);//释放媒体基础缓冲区,媒体基础样本goto CreateOutBuffer;//再次获取输出,直到不可获取为止
}DWORD WINAPI WriterThread(LPVOID lp)
{Mp3Writer* pWriter = (Mp3Writer*)lp;HRESULT hr = MFStartup(MF_VERSION);//初始化媒体基础if (hr != S_OK){MessageBox(NULL, L"初始化媒体基础失败", L"写MP3", MB_OK); return 0;}IMFTransform *pMP3Encoder = NULL;GUID CLSID_Mp3Mft = { 0x11103421, 0x354c, 0x4cca, 0xa7, 0xa3, 0x1a, 0xff, 0x9a, 0x5b, 0x67, 0x01 };//MP3编码器的类标识符hr = CoCreateInstance(CLSID_Mp3Mft, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMP3Encoder));//创建MP3音频编码器(媒体基础转换)if (hr != S_OK){MessageBox(NULL, L"MP3音频编码器创建失败", L"写MP3", MB_OK); return 0;}IMFMediaType *pOutType = NULL;//输出媒体类型int i = 0;while (hr = pMP3Encoder->GetOutputAvailableType(NULL, i, &pOutType) == S_OK)//获取MP3编码器所有允许的输出媒体类型{i++;UINT32 nch;hr = pOutType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &nch);//声道数UINT32 nSample;hr = pOutType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &nSample);//采样率if (nch == pWriter->mInit.nChannels && nSample == pWriter->mInit.SamplesPerSec)break;//如果声道数,采样率匹配;结束循环SafeRelease(&pOutType);}hr = pMP3Encoder->SetOutputType(NULL, pOutType, 0);//设置MP3编码器输出媒体类型SafeRelease(&pOutType);IMFMediaType* pInType = NULL;hr = pMP3Encoder->GetInputAvailableType(NULL, 0, &pInType);//获取允许的输入媒体类型hr = pMP3Encoder->SetInputType(NULL, pInType, 0); //设置MP3编码器输入媒体类型SafeRelease(&pInType);pWriter->pQueue = new CQueue(1000000 + 4, pWriter->hExit);//创建队列CFile F;F.Open(pWriter->FilePath, CFile::modeCreate | CFile::modeWrite);char chID[3] = { 'I','D','3' };F.Write("ID3", 3);//写标识BYTE byte = 3;F.Write(&byte, 1);//写版本号:ID3V2.3byte = 0;F.Write(&byte, 1);//写副版本号F.Write(&byte, 1);//写标志DWORD mSize = 0;F.Write(&mSize, 4);//写标签大小BYTE* pS = new BYTE[1000000]; LONG len;SetEvent(pWriter->hReady);//发送“线程初始化完成”信号Agan:DWORD mStop = WaitForSingleObject(pWriter->hStop, 0);if (mStop != WAIT_OBJECT_0)//如果“停止”无信号{BOOL BReduce = pWriter->pQueue->Reduce(pS, len);//从队列读取样本if (BReduce)//如果读取样本成功{CreateBuffer:IMFMediaBuffer* pMFBuffer = NULL;hr = MFCreateMemoryBuffer(len, &pMFBuffer);//创建媒体基础缓冲区if (hr != S_OK || pMFBuffer == NULL)//如果创建失败{Sleep(1); goto CreateBuffer;//再次创建}BYTE* pData = NULL;hr = pMFBuffer->Lock(&pData, NULL, NULL);//锁定媒体基础缓冲区CopyMemory(pData, pS, len);//复制数据到媒体基础样本缓冲区hr = pMFBuffer->Unlock();//解锁媒体基础缓冲区hr = pMFBuffer->SetCurrentLength((DWORD)len);//设置媒体基础缓冲区的数据长度CreateSample:IMFSample* pMFSample = NULL;hr = MFCreateSample(&pMFSample);//创建媒体基础样本if (hr != S_OK || pMFSample == NULL)//如果创建失败{Sleep(1); goto CreateSample;//再次创建}hr = pMFSample->AddBuffer(pMFBuffer);//添加缓冲区到媒体基础样本RePut:hr = pMP3Encoder->ProcessInput(NULL, pMFSample, 0);//向MP3音频编码器传递输入数据if (hr == S_OK)//如果传递输入成功{SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放媒体基础缓冲区,媒体基础样本}if (MF_E_NOTACCEPTING == hr)//如果不可以传递输入。MF_E_NOTACCEPTING表示已不能接收更多输入{GetOutput(pMP3Encoder, &F);//获取编码器输出goto RePut;//传递输入失败时,需将此次的样本再次传递到输入}}}DWORD mExit = WaitForSingleObject(pWriter->hExit, 0);if (mExit == WAIT_OBJECT_0)//有“退出”信号{F.Close();Sleep(200);delete[] pS;delete pWriter->pQueue;MFShutdown();//关闭媒体基础return 0;}goto Agan;
}BOOL Mp3Writer::Init(MP3_INIT init)
{DWORD dw = WaitForSingleObject(hThread, 0);if (dw == WAIT_TIMEOUT)return FALSE;//如果线程已存在,返回if (init.SamplesPerSec != 48000 && init.SamplesPerSec != 44100 && init.SamplesPerSec != 32000){MessageBox(NULL, L"采样率仅允许48000, 44100, 32000", L"写MP3", MB_OK); return FALSE;}mInit = init;FilePath = init.Path;ResetEvent(hExit);//设置“退出”无信号SetEvent(hStop);//设置“停止”有信号ResetEvent(hReady); //设置“线程初始化完成”无信号hThread = CreateThread(NULL, 0, WriterThread, this, 0, NULL);//创建写mp3线程WaitForSingleObject(hReady, INFINITE);//等待“初始化完成”信号mRun = TRUE;return TRUE;
}void Mp3Writer::WriteSample(BYTE* pB, LONG len)
{DWORD dw = WaitForSingleObject(hStop, 0);//检测“停止”信号if (dw == WAIT_OBJECT_0)//如果“停止”有信号,不将样本加入队列return;if (pQueue)pQueue->Add(pB, len);
}void Mp3Writer::Run()
{ResetEvent(hStop);//设置“停止”无信号
}void Mp3Writer::Stop()
{SetEvent(hStop);//设置“停止”有信号
}void Mp3Writer::Exit()
{mRun = FALSE;SetEvent(hStop);//设置“停止”有信号SetEvent(hExit);//设置“退出”有信号WaitForSingleObject(hThread, INFINITE);//等待线程退出
}Mp3Writer* pMp3Writer[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个类对象pMp3Writer[mNu] = new Mp3Writer();return mNu;}__declspec(dllexport) int __cdecl Init(int index, MP3_INIT Init)//初始化指定对象{if (pMp3Writer[index]->Init(Init))return 1;return 0;}__declspec(dllexport) int __cdecl WriteSample(int index, BYTE* pB, LONG len)//指定对象写样本{pMp3Writer[index]->WriteSample(pB, len);return 1;}__declspec(dllexport) int __cdecl GetState(int index)//获取指定对象状态{return (int)pMp3Writer[index]->mRun;}__declspec(dllexport) int __cdecl Run(int index)//运行指定对象{pMp3Writer[index]->Run();return 0;}__declspec(dllexport) int __cdecl Stop(int index)//停止指定对象{pMp3Writer[index]->Stop();return 0;}__declspec(dllexport) int __cdecl Exit(int index)//退出指定对象{pMp3Writer[index]->Exit();return 0;}__declspec(dllexport) int __cdecl DestroyAll()//销毁所有对象{for (int i = 0; i <= mNu; i++){delete pMp3Writer[i];}return 0;}#ifdef __cplusplus
}
#endif

相关文章:

  • 全链路压测实战指南:从理论到高可用架构的终极验证
  • 【Python】在vscode利用pyinstaller中的.spec文件把py项目打包为.exe实现非py环境下使用的操作步骤
  • 【实战教程】从零实现DeepSeek AI多专家协作系统 - Spring Boot+React打造AI专家团队协作平台
  • wps excel将表格输出pdf时所有列在一张纸上
  • 28、动画魔法圣典:Framer Motion 时空奥义全解——React 19 交互动效
  • 智能手表集成测试报告(Integration Test Report)
  • lesson02-PyTorch开发环境安装
  • 游戏行业DDoS攻击类型及防御分析
  • 详细解释api
  • MySQL 迁移至 Doris 最佳实践方案
  • std::deque和std::vector对比
  • 使用Python与正则表达式高效提取Excel中的票号数据
  • CSS 布局系统深度解析:从传统到现代的布局方案
  • 1C:ENTERPRISE 8.3 实用开发者指南-示例和标准技术(Session1-Session3)
  • Android开发-在应用之间共享数据
  • 使用 Modern CMake 构建现代 C++ 项目:target从入门到实践
  • RK3568下QT实现输入框支持虚拟键盘
  • PYTHON训练营DAY26
  • 《Navicat之外的新选择:实测支持国产数据库的SQLynx核心功能解析》
  • 深入理解JavaScript中的闭包:原理、应用与常见问题
  • 习近平向多哥新任领导人致贺电
  • 万科再获深铁集团借款,今年已累计获股东借款近120亿元
  • 国台办:实现祖国完全统一是大势所趋、大义所在、民心所向
  • 陕西河南山西等地将现“干热风”灾害,小麦产区如何防范?
  • 刘永明|在从普及到提高中发展新大众文艺
  • 香港根据《维护国家安全条例》订立附属法例