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

View File

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

View File

@ -514,19 +514,33 @@ found_or_done:
return result;
}
bool WasapiOut::Configure(IBuffer *buffer) {
HRESULT result;
int WasapiOut::GetDefaultSampleRate() {
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->enumerator) {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
result = CoCreateInstance(
__uuidof(MMDeviceEnumerator),
NULL,
CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator),
(void**) &this->enumerator);
(void**)&this->enumerator);
if (result != S_OK) {
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())
{
return true;
}
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
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;
}
this->Reset();
this->InitializeAudioClient();
DWORD speakerConfig = 0;
switch (buffer->Channels()) {
@ -605,6 +625,8 @@ bool WasapiOut::Configure(IBuffer *buffer) {
wf.dwChannelMask = speakerConfig;
wf.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
HRESULT result = S_FALSE;
DWORD streamFlags = 0;
if (this->audioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX *) &wf, 0) != S_OK) {
streamFlags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;

View File

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