为啥我使用 WinAPI C++ 录制的声音无法正常播放?
Posted
技术标签:
【中文标题】为啥我使用 WinAPI C++ 录制的声音无法正常播放?【英文标题】:Why is my sound recording with WinAPI C++ not played back properly in audacity?为什么我使用 WinAPI C++ 录制的声音无法正常播放? 【发布时间】:2021-02-02 17:02:34 【问题描述】:我正在尝试从麦克风录制声音,但它变得越来越困难。我已经尝试了几种方法,但它不起作用。我创建了一个仅用于测试的项目,稍后将在更大的项目中实施。以下是相关项目的代码:
#include <iostream>
#include <fstream>
#include <Windows.h>
#include <dshow.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <ks.h>
#include <ksmedia.h>
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfreadwrite")
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "strmbase")
int main()
HRESULT hr = MFStartup(MF_VERSION);
IMFMediaSource* pSoundSource = NULL;
IMFAttributes* pSoundConfig = NULL;
IMFActivate** ppSoundDevices = NULL;
hr = MFCreateAttributes(&pSoundConfig, 1);
if (FAILED(hr))
std::cout << "Failed to create attribute store";
hr = pSoundConfig->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID);
UINT32 count;
hr = MFEnumDeviceSources(pSoundConfig, &ppSoundDevices, &count);
if (FAILED(hr))
std::cout << "Failed to enumerate capture devices";
hr = ppSoundDevices[0]->ActivateObject(IID_PPV_ARGS(&pSoundSource));
if (FAILED(hr))
std::cout << "Failed to connect microphone to source";
IMFSourceReader* pSoundReader;
hr = MFCreateSourceReaderFromMediaSource(pSoundSource, pSoundConfig, &pSoundReader);
if (FAILED(hr))
std::cout << "Failed to create source reader";
/*This part is for getting the audio format that the microphone outputs*/
/*______________________*/
IMFMediaType* pSoundType = NULL;
DWORD dwMediaTypeIndex = 0;
DWORD dwStreamIndex = 0;
hr = pSoundReader->GetNativeMediaType(dwStreamIndex, dwMediaTypeIndex, &pSoundType);
LPVOID soundRepresentation;
pSoundType->GetRepresentation(AM_MEDIA_TYPE_REPRESENTATION, &soundRepresentation);
GUID subSoundType = ((AM_MEDIA_TYPE*)soundRepresentation)->subtype;
BYTE* pbSoundFormat = ((AM_MEDIA_TYPE*)soundRepresentation)->pbFormat;
GUID soundFormatType = ((AM_MEDIA_TYPE*)soundRepresentation)->formattype;
if (soundFormatType == FORMAT_WaveFormatEx) std::cout << 8;
WAVEFORMATEXTENSIBLE* soundFormat = (WAVEFORMATEXTENSIBLE*)pbSoundFormat;
std::cout << std::endl;
std::cout << soundFormat->Format.wFormatTag << std::endl;
std::cout << soundFormat->Format.nChannels << std::endl;
std::cout << soundFormat->Format.nBlockAlign << std::endl;
std::cout << soundFormat->Format.nSamplesPerSec << std::endl;
std::cout << soundFormat->Format.wBitsPerSample << std::endl;
std::cout << soundFormat->Format.cbSize << std::endl;
if (soundFormat->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
std::cout << "IEEE-FLOAT!" << std::endl;
/*_____________________*/
DWORD streamIndex, flags;
LONGLONG llTimeStamp;
IMFSample* pSoundSample;
while (true)
hr = pSoundReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &streamIndex, &flags, &llTimeStamp, &pSoundSample);
if (FAILED(hr))
std::cout << "Failed to get sound from microphone";
if (pSoundSample != NULL)
IMFMediaBuffer* pSoundBuffer;
pSoundSample->ConvertToContiguousBuffer(&pSoundBuffer);
DWORD soundlength;
pSoundBuffer->GetCurrentLength(&soundlength);
unsigned char* sounddata;
hr = pSoundBuffer->Lock(&sounddata, NULL, &soundlength);
if (FAILED(hr))
std::cout << "Failed to get sounddata from buffer";
std::ofstream file;
file.open("C:\\Users\\user\\Documents\\test.raw", std::ios::app);
for (unsigned int i = 0; i < soundlength; i++)
file << sounddata[i];
file.close();
应该确定控制台上打印数据格式的代码部分:
8
65534
1
4
48000
32
22
IEEE-FLOAT!
由此,我确定声音是以 1 通道 32bits 48000Hz IEEE-FLOAT 格式录制的。现在我需要播放这个声音。问题是大多数 API 需要 16 位 PCM 来播放声音。
我尝试将声音转换为 16 位 PCM,但效果不佳。如果你知道如何做到这一点,你能展示一些代码吗?此外,在此处提供的代码中,我将声音附加到没有标题的原始音频文件中。我听说浮点表示介于 1 和 -1 之间,所以我尝试了以下代码进行转换:
void iefloat_to_pcm16(unsigned char* sounddata, std::vector<unsigned char>& newdata, int soundlength)
for (int i = 0; i < soundlength && i + 3 < soundlength; i += 4)
float f;
unsigned char b[] = sounddata[i], sounddata[i + 1], sounddata[i + 2], sounddata[i + 3] ;
memcpy(&f, &b, sizeof(f));
short pcm16 = f * 32767 + 0.5;
newdata.push_back((unsigned char)(pcm16 >> 8));
newdata.push_back((unsigned char)pcm16);
此代码似乎不起作用。
在此之后,我一直在使用 Audacity 和 File > Import > Raw Data,它允许导入原始数据并指定数据的格式。所以我选择了 1 通道 32 位浮点数,48kHZ 并尝试了所有字节序徒劳无功。我对“转换”为 16 位 PCM 的数据做了同样的事情。结果只是大胆的随机噪音。我可以看到我发出噪音的地方有尖峰,其余的都是无声的。但尖峰只是噪音。我在这里做错了吗?
【问题讨论】:
【参考方案1】:音频文件是二进制格式,但您在文件中放置了文本。
file << sounddata[i];
这是一个格式化的插入运算符,它将数据转换为文本表示。请改用file.write()
。
您可能还需要弄乱用于打开流的标志。 C++ 标准 I/O 流不适用于二进制数据。由于您已经在广泛使用 Windows API 对象,您可能只需切换到 CreateFile
/ WriteFile
,其中表面下没有活动的转换方面。
【讨论】:
谢谢,是的,我不得不使用:HANDLE file = CreateFile(L"c:\\users\\user\\documents\\test.raw", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
和 WriteFile(file, sounddata, soundlength, bytesWritten, NULL);
。我现在可以大胆地正确获得声音。我要测试我的转换功能。
我的转换功能似乎可以工作,但我需要指定大端序。这可能是我一直试图修复的 SDL_mixer 的罪魁祸首。再次感谢您的回答。以上是关于为啥我使用 WinAPI C++ 录制的声音无法正常播放?的主要内容,如果未能解决你的问题,请参考以下文章