* Updated WasapiOut to provide the preferred sample rate for the

selected device
* Updated FfmpegDecoder to accept the preferred sample rate from the
  output, and use it when resampling if it is non-negative. Otherwise,
  use the sample rate defined by the file.

Note: this is not wired up in the main app yet.
This commit is contained in:
casey langen 2021-09-19 21:50:54 -07:00
parent f2e8fd45f8
commit 06bdf32959
4 changed files with 63 additions and 35 deletions

View File

@ -52,6 +52,8 @@ using namespace musik::core::sdk;
static const char* TAG = "ffmpegdecoder"; static const char* TAG = "ffmpegdecoder";
static IDebug* debug = nullptr; static IDebug* debug = nullptr;
#define RESOLVE_SAMPLE_RATE() this->preferredSampleRate > 0 ? this->preferredSampleRate : this->rate
extern "C" DLLEXPORT void SetDebug(IDebug* debug) { extern "C" DLLEXPORT void SetDebug(IDebug* debug) {
::debug = debug; ::debug = debug;
} }
@ -149,6 +151,7 @@ FfmpegDecoder::FfmpegDecoder() {
this->bufferSize = AV_INPUT_BUFFER_PADDING_SIZE + BUFFER_SIZE; this->bufferSize = AV_INPUT_BUFFER_PADDING_SIZE + BUFFER_SIZE;
this->buffer = new unsigned char[this->bufferSize]; this->buffer = new unsigned char[this->bufferSize];
this->outputFifo = nullptr; this->outputFifo = nullptr;
this->preferredSampleRate = -1;
} }
FfmpegDecoder::~FfmpegDecoder() { FfmpegDecoder::~FfmpegDecoder() {
@ -192,7 +195,7 @@ double FfmpegDecoder::SetPosition(double seconds) {
bool FfmpegDecoder::GetBuffer(IBuffer *buffer) { bool FfmpegDecoder::GetBuffer(IBuffer *buffer) {
if (this->ioContext) { if (this->ioContext) {
buffer->SetSampleRate((long) this->rate); buffer->SetSampleRate((long) RESOLVE_SAMPLE_RATE());
buffer->SetChannels((long) this->channels); buffer->SetChannels((long) this->channels);
buffer->SetSamples(0); buffer->SetSamples(0);
@ -256,7 +259,7 @@ bool FfmpegDecoder::InitializeResampler(IBuffer* buffer) {
this->resampler, this->resampler,
this->codecContext->channel_layout, this->codecContext->channel_layout,
AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLT,
(int) this->rate, (int) RESOLVE_SAMPLE_RATE(),
this->codecContext->channel_layout, this->codecContext->channel_layout,
this->codecContext->sample_fmt, this->codecContext->sample_fmt,
this->codecContext->sample_rate, this->codecContext->sample_rate,
@ -404,7 +407,7 @@ bool FfmpegDecoder::ReadSendAndReceivePacket(AVPacket* packet) {
this->resampledFrame = this->AllocFrame( this->resampledFrame = this->AllocFrame(
this->resampledFrame, this->resampledFrame,
AV_SAMPLE_FMT_FLT, AV_SAMPLE_FMT_FLT,
this->rate, RESOLVE_SAMPLE_RATE(),
this->decodedFrame->nb_samples); this->decodedFrame->nb_samples);
error = swr_convert_frame( error = swr_convert_frame(

View File

@ -64,7 +64,7 @@ class FfmpegDecoder: public musik::core::sdk::IDecoder {
double GetDuration() override; double GetDuration() override;
bool Open(musik::core::sdk::IDataStream *stream) override; bool Open(musik::core::sdk::IDataStream *stream) override;
bool Exhausted() override; bool Exhausted() override;
void SetPreferredSampleRate(int rate) override { } void SetPreferredSampleRate(int rate) override { this->preferredSampleRate = rate; }
IDataStream* Stream() { return this->stream; } IDataStream* Stream() { return this->stream; }
@ -87,6 +87,7 @@ class FfmpegDecoder: public musik::core::sdk::IDecoder {
AVFrame* resampledFrame; AVFrame* resampledFrame;
SwrContext* resampler; SwrContext* resampler;
unsigned char* buffer; unsigned char* buffer;
int preferredSampleRate { -1 };
int bufferSize; int bufferSize;
int rate, channels; int rate, channels;
int streamId; int streamId;

View File

@ -514,19 +514,33 @@ found_or_done:
return result; return result;
} }
bool WasapiOut::Configure(IBuffer *buffer) { int WasapiOut::GetDefaultSampleRate() {
HRESULT result; int result = -1;
this->InitializeAudioClient();
if (this->audioClient) {
WAVEFORMATEX* deviceFormat = nullptr;
audioClient->GetMixFormat(&deviceFormat);
if (deviceFormat) {
result = deviceFormat->nSamplesPerSec;
CoTaskMemFree(deviceFormat);
}
}
return result;
}
bool WasapiOut::InitializeAudioClient() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
HRESULT result = S_FALSE;
if (!this->audioClient) { if (!this->audioClient) {
if (!this->enumerator) { if (!this->enumerator) {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
result = CoCreateInstance( result = CoCreateInstance(
__uuidof(MMDeviceEnumerator), __uuidof(MMDeviceEnumerator),
NULL, NULL,
CLSCTX_ALL, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator), __uuidof(IMMDeviceEnumerator),
(void**) &this->enumerator); (void**)&this->enumerator);
if (result != S_OK) { if (result != S_OK) {
return false; return false;
@ -542,37 +556,43 @@ bool WasapiOut::Configure(IBuffer *buffer) {
} }
} }
if (waveFormat.Format.nChannels == buffer->Channels() && if (!this->device) {
bool preferredDeviceOk = false;
IMMDevice* preferredDevice = this->GetPreferredDevice();
if (preferredDevice) {
if ((result = preferredDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&this->audioClient)) == S_OK) {
preferredDeviceOk = true;
this->device = preferredDevice;
}
}
if (!preferredDeviceOk) {
if (preferredDevice) {
preferredDevice->Release();
}
if ((result = this->enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &this->device)) != S_OK) {
return false;
}
}
if ((result = this->device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&this->audioClient)) != S_OK) {
return false;
}
}
}
bool WasapiOut::Configure(IBuffer *buffer) {
if (this->audioClient &&
waveFormat.Format.nChannels == buffer->Channels() &&
waveFormat.Format.nSamplesPerSec == buffer->SampleRate()) waveFormat.Format.nSamplesPerSec == buffer->SampleRate())
{ {
return true; return true;
} }
CoInitializeEx(nullptr, COINIT_MULTITHREADED); this->Reset();
this->InitializeAudioClient();
bool preferredDeviceOk = false;
IMMDevice* preferredDevice = this->GetPreferredDevice();
if (preferredDevice) {
if ((result = preferredDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**) &this->audioClient)) == S_OK) {
preferredDeviceOk = true;
this->device = preferredDevice;
}
}
if (!preferredDeviceOk) {
if (preferredDevice) {
preferredDevice->Release();
}
if ((result = this->enumerator->GetDefaultAudioEndpoint(eRender, eMultimedia, &this->device)) != S_OK) {
return false;
}
}
if ((result = this->device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**) &this->audioClient)) != S_OK) {
return false;
}
DWORD speakerConfig = 0; DWORD speakerConfig = 0;
switch (buffer->Channels()) { switch (buffer->Channels()) {
@ -605,6 +625,8 @@ bool WasapiOut::Configure(IBuffer *buffer) {
wf.dwChannelMask = speakerConfig; wf.dwChannelMask = speakerConfig;
wf.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; wf.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
HRESULT result = S_FALSE;
DWORD streamFlags = 0; DWORD streamFlags = 0;
if (this->audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX *) &wf, 0) != S_OK) { if (this->audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX *) &wf, 0) != S_OK) {
streamFlags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; streamFlags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;

View File

@ -70,6 +70,7 @@ class WasapiOut : public IOutput {
IDeviceList* GetDeviceList() override; IDeviceList* GetDeviceList() override;
bool SetDefaultDevice(const char* deviceId) override; bool SetDefaultDevice(const char* deviceId) override;
IDevice* GetDefaultDevice() override; IDevice* GetDefaultDevice() override;
int GetDefaultSampleRate() override;
void OnDeviceChanged() { this->deviceChanged = true; } void OnDeviceChanged() { this->deviceChanged = true; }
@ -81,6 +82,7 @@ class WasapiOut : public IOutput {
}; };
bool Configure(IBuffer *buffer); bool Configure(IBuffer *buffer);
bool InitializeAudioClient();
void Reset(); void Reset();
IMMDevice* GetPreferredDevice(); IMMDevice* GetPreferredDevice();