我正在尝试使用Waveform Audio library创建一个C ++程序,该程序将播放另一个程序提供的AudioFrames(原始音频数据,每个帧包含约1920个字节)(现在我只是通过将文件读取为AudioFrames)。从this thread修改代码,我能够使SoundPlayer类完成这项工作,但是我得到的输出非常不稳定。更大的帧尺寸会更好,但是即使具有96000字节的帧,音频仍然每秒钟左右会出现毛刺(我需要这些帧要小得多)。
如何解决此问题?
Here是我正在使用的测试文件。这是代码本身:
#include <windows.h>
#include <iostream>
#pragma comment(lib, "Winmm.lib")
constexpr int FRAME_SIZE_IN_BYTES = 1920;
struct AudioFrame
{
char *Data;
int DataSize;
};
class SoundPlayer
{
public:
SoundPlayer()
{
// Initialize the sound format we will request from sound card
m_waveFormat.wFormatTag = WAVE_FORMAT_PCM; // Uncompressed sound format
m_waveFormat.nChannels = 1; // 1 = Mono, 2 = Stereo
m_waveFormat.wBitsPerSample = 16; // Bits per sample per channel
m_waveFormat.nSamplesPerSec = 48000; // Sample Per Second
m_waveFormat.nBlockAlign = m_waveFormat.nChannels * m_waveFormat.wBitsPerSample / 8;
m_waveFormat.nAvgBytesPerSec = m_waveFormat.nSamplesPerSec * m_waveFormat.nBlockAlign;
m_waveFormat.cbSize = 0;
}
void Play(AudioFrame* af)
{
// Create our "Sound is Done" event
m_done = CreateEvent(0, FALSE, FALSE, 0);
// Open the audio device
if (waveOutOpen(&m_waveOut, 0, &m_waveFormat, (DWORD)m_done, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR)
{
std::cout << "Sound card cannot be opened." << std::endl;
return;
}
// Create the wave header for our sound buffer
m_waveHeader.lpData = af->Data;
m_waveHeader.dwBufferLength = af->DataSize;
m_waveHeader.dwFlags = 0;
m_waveHeader.dwLoops = 0;
// Prepare the header for playback on sound card
if (waveOutPrepareHeader(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error preparing Header!" << std::endl;
return;
}
ResetEvent(m_done); // Reset our Event so it is non-signaled, it will be signaled again with buffer finished
// Play the sound!
if (waveOutWrite(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error writing to sound card!" << std::endl;
return;
}
// Wait until sound finishes playing
if (WaitForSingleObject(m_done, INFINITE) != WAIT_OBJECT_0)
{
std::cout << "Error waiting for sound to finish" << std::endl;
return;
}
// Unprepare our wav header
if (waveOutUnprepareHeader(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error unpreparing header!" << std::endl;
return;
}
// Close the wav device
if (waveOutClose(m_waveOut) != MMSYSERR_NOERROR)
{
std::cout << "Sound card cannot be closed!" << std::endl;
return;
}
// Release our event handle
CloseHandle(m_done);
}
private:
HWAVEOUT m_waveOut; // Handle to sound card output
WAVEFORMATEX m_waveFormat; // The sound format
WAVEHDR m_waveHeader; // WAVE header for our sound data
HANDLE m_done; // Event Handle that tells us the sound has finished being played.
// This is a very efficient way to put the program to sleep
// while the sound card is processing the sound buffer
};
int main()
{
FILE * fileDes;
fopen_s(&fileDes, "Ducksauce.raw", "rb");
if (fileDes == nullptr)
std::cout << "File opening failed.\n";
int bufferSize = FRAME_SIZE_IN_BYTES;
char *buffer = new char[bufferSize];
SoundPlayer sp;
while (fread(buffer, sizeof(char), bufferSize, fileDes) > 0)
{
AudioFrame af;
af.Data = buffer;
af.DataSize = bufferSize;
sp.Play(&af);
}
fclose(fileDes);
delete[] buffer;
return 0;
}
编辑:版本号2。仍然无法正常使用。
#include <windows.h>
#include <iostream>
#pragma comment(lib, "Winmm.lib")
constexpr int FRAME_SIZE_IN_BYTES = 1920;
struct AudioFrame
{
char *Data;
int DataSize;
};
class SoundPlayer
{
public:
SoundPlayer()
{
// Initialize the sound format we will request from sound card
m_waveFormat.wFormatTag = WAVE_FORMAT_PCM; // Uncompressed sound format
m_waveFormat.nChannels = 1; // 1 = Mono, 2 = Stereo
m_waveFormat.wBitsPerSample = 16; // Bits per sample per channel
m_waveFormat.nSamplesPerSec = 48000; // Sample Per Second
m_waveFormat.nBlockAlign = m_waveFormat.nChannels * m_waveFormat.wBitsPerSample / 8;
m_waveFormat.nAvgBytesPerSec = m_waveFormat.nSamplesPerSec * m_waveFormat.nBlockAlign;
m_waveFormat.cbSize = 0;
// Create our "Sound is Done" event
m_done = CreateEvent(0, FALSE, FALSE, 0);
// Open the audio device
if (waveOutOpen(&m_waveOut, 0, &m_waveFormat, (DWORD)m_done, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR)
{
std::cout << "Sound card cannot be opened." << std::endl;
return;
}
}
~SoundPlayer()
{
// Close the wav device
if (waveOutClose(m_waveOut) != MMSYSERR_NOERROR)
{
std::cout << "Sound card cannot be closed!" << std::endl;
return;
}
// Release our event handle
CloseHandle(m_done);
}
void StartPlaying(AudioFrame* af)
{
// Create the wave header for our sound buffer
m_waveHeader.lpData = af->Data;
m_waveHeader.dwBufferLength = af->DataSize;
m_waveHeader.dwFlags = 0;
m_waveHeader.dwLoops = 0;
// Prepare the header for playback on sound card
if (waveOutPrepareHeader(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error preparing Header!" << std::endl;
return;
}
ResetEvent(m_done); // Reset our Event so it is non-signaled, it will be signaled again with buffer finished
// Play the sound!
if (waveOutWrite(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error writing to sound card!" << std::endl;
return;
}
}
void WaitUntilFrameFinishes()
{
// Wait until sound finishes playing
if (WaitForSingleObject(m_done, INFINITE) != WAIT_OBJECT_0)
{
std::cout << "Error waiting for sound to finish" << std::endl;
return;
}
// Unprepare our wav header
if (waveOutUnprepareHeader(m_waveOut, &m_waveHeader, sizeof(m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Error unpreparing header!" << std::endl;
return;
}
}
private:
HWAVEOUT m_waveOut; // Handle to sound card output
WAVEFORMATEX m_waveFormat; // The sound format
WAVEHDR m_waveHeader; // WAVE header for our sound data
HANDLE m_done; // Event Handle that tells us the sound has finished being played.
// This is a very efficient way to put the program to sleep
// while the sound card is processing the sound buffer
};
int main()
{
FILE * fileDes;
fopen_s(&fileDes, "Ducksauce.raw", "rb");
if (fileDes == nullptr)
std::cout << "File opening failed.\n";
int bufferSize = FRAME_SIZE_IN_BYTES;
char *buffer = new char[bufferSize];
SoundPlayer sp;
// Read first time
fread(buffer, sizeof(char), bufferSize, fileDes);
while (true)
{
AudioFrame af;
af.Data = buffer;
af.DataSize = bufferSize;
// Start playing, but don't block
sp.StartPlaying(&af);
// Prepare the next chunk
if (fread(buffer, sizeof(char), bufferSize, fileDes) <= 0)
break;
// Now block the code, waiting with next chunk already loaded
// and ready to be played in the next iteration.
sp.WaitUntilFrameFinishes();
}
fclose(fileDes);
delete[] buffer;
return 0;
}
编辑2:如果我在之前添加此代码,它将起作用:
for (int i = 0; i < 3; i++ )
{
fread(buffer, sizeof(char), bufferSize, fileDes);
af.Data = buffer;
af.DataSize = bufferSize;
sp.StartPlaying(&af);
}
我也做了一些修改:
while (true)
{
// Prepare the next chunk
if (fread(buffer, sizeof(char), bufferSize, fileDes) <= 0)
break;
// Now block the code, waiting with next chunk already loaded
// and ready to be played in the next iteration.
sp.WaitUntilFrameFinishes();
af.Data = buffer;
af.DataSize = bufferSize;
sp.StartPlaying(&af);
}
答案 0 :(得分:2)
您应该在声音播放时从磁盘读取数据,而不是在缓冲区之间!
如果无法一次读取整个文件,则应更改Play
函数,以使其不仅调用WaitForSingleObject
。使用它会使您的代码阻塞,并等到声音停止播放为止。
您需要的是开始播放,然后回到阅读循环,准备下一个缓冲区,然后然后等待音乐结束,例如(在SoundPlayer
中):
void WaitUntilFrameFinishes() {
// Wait until sound finishes playing
if (WaitForSingleObject(m_done, INFINITE) != WAIT_OBJECT_0)
// ... move all the code from Play till the end here
}
然后回到main
循环中:
// Read first frame
fread(buffer, sizeof(char), bufferSize, fileDes);
while (true)
{
AudioFrame af;
af.Data = buffer;
af.DataSize = bufferSize;
// Start playing, but don't block
sp.Play(&af);
// Prepare the next chunk
if (fread(buffer, sizeof(char), bufferSize, fileDes) <= 0) {
break;
// Now block the code, waiting with next chunk already loaded
// and ready to be played in the next iteration.
sp.WaitUntilFrameFinishes();
}
理想情况下,您还可以将fread
调用包装到可以更好地提供块的内容中。
答案 1 :(得分:1)
仅根据文档试图弄清楚音频播放的一天之后,我发现this excellent tutorial。如果有人在尝试使用Waveform音频创建音频播放时找到了该线程,那将是一个很好的参考点(肯定比我上面的错误代码要好得多)。
关于我的代码,我怀疑它无法正常工作,因为应该使用waveOutWrite()来使AudioFrames队列始终保持至少几帧,以防止声卡不得不等待另一个AudioFrame的情况