Waveform Audio — waveOutWrite дает прерывистый звук

Я пытаюсь создать программу на C ++ с Аудио библиотека Waveform это будет воспроизведение AudioFrames (необработанные аудиоданные, каждый кадр состоит из около 1920 байтов), предоставленные другой программой (сейчас я просто имитирую это, читая файл как AudioFrames). Изменение кода из эта тема Мне удалось создать класс SoundPlayer, который выполняет эту работу, но вывод, который я получаю, очень прерывистый. Это становится лучше с большими размерами кадров, но даже с кадрами размером 96000 байт звук по-прежнему глючит каждую секунду или около того (и мне нужно, чтобы кадры тоже были намного меньше).

Как я могу исправить эту проблему?

Вот это тестовый файл, который я использую. А вот и сам код:

#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);
}

1

Решение

Вы должны читать данные с диска во время воспроизведения звука, а не между буферами!

Если вы не можете прочитать весь файл сразу, вы должны изменить 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 звонки во что-то, что может обеспечить куски в лучшем виде.

2

Другие решения

После дня, когда я попытался выяснить, как начать воспроизведение аудио, основываясь только на документации, которую я нашел этот отличный учебник. Если кто-то нашел эту тему, пытаясь создать воспроизведение звука с использованием аудио Waveform, это очень хороший ориентир (несомненно, намного лучше, чем мой глючный код выше).

Что касается моего кода, я подозреваю, что он не работает должным образом, поскольку предполагается, что очередь AudioFrames должна храниться с использованием waveOutWrite () по крайней мере, с несколькими кадрами в любое время, чтобы избежать ситуации, когда звуковой карте придется ждать другого AudioFrame.

1

По вопросам рекламы [email protected]