Нет звука в режиме общих событий WASAPI

Я занимался этим последние 24 часа, но что бы я ни делал, я не могу получить звук через WASAPI (пробовал все 3 IAudioClient интерфейсы, и в итоге я решил придерживаться самого последнего). Я не получаю никаких ошибок, и все выглядит нормально на отладчике. Хех, я думаю, это то, что происходит, когда вы просто читаете документы влево и вправо и взламываете код вместе, не имея ни малейшего понятия о том, что вы делаете.

По большей части, я думаю, что мой код декодера правильный, если я что-то не так понял. Согласно документам libFLAC «параметр буфера» write_callback массив образцов со знаком длины frame-> header.blocksize. Что совпадает с тем, что документы Microsoft должны были сказать по этому поводу

Во всяком случае, вот мой (очень хакерский) код:

Вот как выглядит мой заголовочный файл:

#pragma once
#include <iostream>
#define NOMINMAX
#include <Mmdeviceapi.h>
#include <Audioclient.h>
#include <limits>

#include <FLAC++/decoder.h>

class WASAPIBackend
{
public:
WASAPIBackend();
~WASAPIBackend();
private:
HRESULT hr;
IMMDeviceEnumerator* pEnumerator;
IMMDevice* pDevice;
IAudioClient3* pAudioClient;
IAudioRenderClient* pAudioRenderClient;
WAVEFORMATEX* pMixFormat;
uint32_t defaultPeriodInFrames, fundamentalPeriodInFrames, minPeriodInFrames, maxPeriodInFrames;
};

И вот как выглядит мой файл cpp:

#include "WASAPIBackend.h"
// TODO: Fix this mess.
BYTE* m_audioBuf = nullptr;
int32_t* m_audioFrame = nullptr;
size_t currentFrame = 0;

class FLACDecoder : public FLAC::Decoder::File {
virtual ::FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
virtual void metadata_callback(const ::FLAC__StreamMetadata *metadata);
virtual void error_callback(::FLAC__StreamDecoderErrorStatus status);

};

::FLAC__StreamDecoderWriteStatus FLACDecoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) {
// Audio frame: size of an audio frame is the sample size multiplied by the number of channels in the stream.

//memmove(m_audioFrame, buffer[0], frame->header.blocksize);
// printf("Uniplemented - %llu\n", frame->header.channels * frame->header.blocksize);
m_audioFrame = new int32_t[frame->header.blocksize * frame->header.channels * sizeof(int16_t*)];
/*memcpy(m_audioFrame, buffer, frame->header.blocksize * frame->header.channels);

return FLAC__StreamDecoderWriteStatus::FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;*/

// Code below inspired by SDL_Mixer. (Remember to give proper credit later if I decide to stick with it!!!)

int16_t *data; // TODO:: Switch to smart pointers once everything works.
unsigned int i, j, channels;
int shift_amount = 0;

if (!is_valid()) {
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}

switch (get_bits_per_sample()) {
case 16:
shift_amount = 0;
break;
case 20:
shift_amount = 4;
break;
case 24:
shift_amount = 8;
break;
default:
fprintf(stderr, "FLAC decoder doesn't support %d bits_per_sample", get_bits_per_sample());
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}

if (get_channels() == 3) {
/* We'll just drop the center channel for now */
channels = 2;
}
else {
channels = get_channels();
}

data = new int16_t[frame->header.blocksize * channels];
if (!data) {
fprintf(stderr, "Couldn't allocate %d bytes stack memory", (int)(frame->header.blocksize * channels * sizeof(*data)));
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}

if (get_channels() == 3) {
int16_t *dst = data;
for (i = 0; i < frame->header.blocksize; ++i) {
int16_t FL = (buffer[0][i] >> shift_amount);
int16_t FR = (buffer[1][i] >> shift_amount);
int16_t FCmix = (int16_t)((buffer[2][i] >> shift_amount) * 0.5f);
int sample;

sample = (FL + FCmix);
if (sample > std::numeric_limits<int16_t>::max()) {
*dst = std::numeric_limits<int16_t>::max();
}
else if (sample < std::numeric_limits<int16_t>::min()) {
*dst = std::numeric_limits<int16_t>::min();
}
else {
*dst = sample;
}
++dst;

sample = (FR + FCmix);
if (sample > std::numeric_limits<int16_t>::max()) {
*dst = std::numeric_limits<int16_t>::max();
}
else if (sample < std::numeric_limits<int16_t>::min()) {
*dst = std::numeric_limits<int16_t>::min();
}
else {
*dst = sample;
}
++dst;
}
}
else {
for (i = 0; i < channels; ++i) {
int16_t *dst = data + i;
for (j = 0; j < frame->header.blocksize; ++j) {
*dst = (buffer[i][j] >> shift_amount);
dst += channels;
}
}
}

// Supposedly, the audio *should* be interleaved
memcpy(m_audioFrame, data, (frame->header.blocksize * channels * sizeof(*data)));

delete[] data;

return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

void FLACDecoder::metadata_callback(const ::FLAC__StreamMetadata *metadata)
{
/* print some stats */
if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
/* save for later */
uint64_t total_samples = metadata->data.stream_info.total_samples;
unsigned int sample_rate = metadata->data.stream_info.sample_rate;
unsigned int channels = metadata->data.stream_info.channels;
unsigned int bps = metadata->data.stream_info.bits_per_sample;fprintf(stderr, "blocksize    : %u bytes\n", metadata->data.stream_info.max_blocksize);
fprintf(stderr, "sample rate    : %u Hz\n", sample_rate);
fprintf(stderr, "channels       : %u\n", channels);
fprintf(stderr, "bits per sample: %u\n", bps);
fprintf(stderr, "total samples  : %llu\n", total_samples);
}
}

void FLACDecoder::error_callback(::FLAC__StreamDecoderErrorStatus status)
{
fprintf(stderr, "Got error callback: %d\n", status/*FLAC__StreamDecoderErrorStatusString[status]*/);
}

/* Helper function to fill up the audio buffer up to n frames. As the FLAC decoder can only do
a whole file at once, or frame by frame */
void fillBuffer(FLACDecoder &d, uint32_t frames) {
for (size_t i = 0; i < frames; ++i) {
d.process_single();
memcpy(&m_audioBuf[i], &m_audioFrame, d.get_blocksize() * d.get_channels());
}
}

constexpr void SafeRelease(IUnknown** p) {
if (p) {
(*p)->Release();
}
}

// TODO: Move everything out of the constructor once playback works.
// TODO: Have the class create a thread, instead of the burden of doing so being on the user.
WASAPIBackend::WASAPIBackend() : hr(0), pEnumerator(nullptr), pDevice(nullptr), pAudioClient(nullptr), pAudioRenderClient(nullptr), pMixFormat(nullptr)
{
CoInitializeEx(nullptr, COINIT_MULTITHREADED);

hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&pEnumerator));
if (FAILED(hr)) {
printf("Got error: %x, while creating MMDeviceEnumerator\n", hr);
}

hr = pEnumerator->GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eConsole, &pDevice);

if (FAILED(hr)) {
printf("Got error: %x, while attempting to fetch default audio endpoint (render)\n", hr);
}

hr = pDevice->Activate(__uuidof(IAudioClient3), CLSCTX_ALL, NULL, reinterpret_cast<void**>(&pAudioClient));

if (FAILED(hr)) {
printf("Got error: %x, while attempting activate IAudioClient3 (render)\n", hr);
}

// The sample file is a 2 channel, 16bit, 41000hz FLAC. Since this fetches the audio engine format, it should be fine for now.
// Not that it would matter according to the docs. In shared mode, Windows takes care of converting to and from the desired audio format.
hr = pAudioClient->GetMixFormat(&pMixFormat);if (FAILED(hr)) {
printf("Got error: %x, while attempting get mix format (render)\n", hr);
}

hr = pAudioClient->GetSharedModeEnginePeriod(pMixFormat, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);

if (FAILED(hr)) {
printf("Got error: %x, while attempting get shared mode periodicities (render)\n", hr);
}

hr = pAudioClient->InitializeSharedAudioStream(AUDCLNT_STREAMFLAGS_EVENTCALLBACK, minPeriodInFrames, pMixFormat, nullptr);

if (FAILED(hr)) {
printf("Got error: %x, while initializing shared audio stream (render)\n", hr);
}

HANDLE audioSamplesReadyEvent = CreateEventEx(NULL, NULL, 0, SYNCHRONIZE | EVENT_MODIFY_STATE);

if (!audioSamplesReadyEvent) {
printf("Got error: %x, attempting to create event\n", GetLastError());
}

hr = pAudioClient->SetEventHandle(audioSamplesReadyEvent);

if (FAILED(hr)) {
printf("Got error: %x, while attempting to set event handle (render)\n", hr);
}

hr = pAudioClient->GetService(__uuidof(IAudioRenderClient), reinterpret_cast<void**>(&pAudioRenderClient));

if (FAILED(hr)) {
printf("Got error: %x, while attempting to GetService(IAudioRenderClient) (render)\n", hr);
}

uint32_t bufferSize = 0;
uint32_t safeToWrite = 0;
uint32_t padding = 0;
pAudioClient->GetCurrentPadding(&padding);
pAudioClient->GetBufferSize(&bufferSize);

// TODO: This is garbage, figure out how to organize the code at a later time.
FLACDecoder d;
// I got this file from OCRemix, remember to thank them later.
d.init("audio/07 DaMonz - Choose Your Destiny (Super Smash Bros. Melee).flac");

d.process_until_end_of_metadata();

// Grab the entire buffer for the initial fill operation.
hr = pAudioRenderClient->GetBuffer(bufferSize, &m_audioBuf);

if (FAILED(hr)) {
printf("Got error: %x, while retrieving audio buffer (render)\n", hr);
}

fillBuffer(d, bufferSize);

hr = pAudioRenderClient->ReleaseBuffer(bufferSize, 0);

if (FAILED(hr)) {
printf("Got error: %x, while attempting to release audio buffer (render)\n", hr);
}

pAudioClient->Start();

// TODO: Infinite loops without ending conditions are bad, so fix it up once everything works.
while (true) {
// In theory, this will only hang if the audio client hasn't started playback.
WaitForSingleObject(audioSamplesReadyEvent, INFINITE);

pAudioClient->GetCurrentPadding(&padding);

// printf("Current padding: %d\n", padding);

safeToWrite = bufferSize - padding;

hr = pAudioRenderClient->GetBuffer(safeToWrite, &m_audioBuf);

if (FAILED(hr)) {
printf("Got error: %x, while retrieving audio buffer (rendering)\n", hr);
}

if (FAILED(hr)) {
printf("Got error: %x, while retrieving current audio buffer padding (rendering)\n", hr);
}

// Fill all the "safe space" in the buffer with new data.
fillBuffer(d, safeToWrite);

hr = pAudioRenderClient->ReleaseBuffer(safeToWrite, 0);

if (FAILED(hr)) {
printf("Got error: %x, while attempting to release audio buffer (render)\n", hr);
}
}

pAudioClient->Stop();

}

WASAPIBackend::~WASAPIBackend()
{
CoTaskMemFree(pMixFormat);
SafeRelease(reinterpret_cast<IUnknown**>(&pAudioRenderClient));
SafeRelease(reinterpret_cast<IUnknown**>(&pAudioClient));
SafeRelease(reinterpret_cast<IUnknown**>(&pDevice));
SafeRelease(reinterpret_cast<IUnknown**>(&pEnumerator));
CoUninitialize();
}

Извините за массивные блоки кода. Если бы я знал, что случилось, я бы отправил только подозрительный код. Я получаю смешанные сигналы повсюду. Согласно выводу моей консоли, он должен работать, так как я не получаю ошибок, и я получаю это: соответствующий вывод консоли который является выходом из декодера FLAC metadata_callback.

0

Решение

Задача ещё не решена.

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

Других решений пока нет …

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