mirror of
https://github.com/badaix/snapcast.git
synced 2025-04-29 02:07:55 +02:00
wasapi: added client option for selecting shared/exclusive mode
This commit is contained in:
parent
95a369be79
commit
9737c1ac44
5 changed files with 70 additions and 28 deletions
|
@ -27,6 +27,14 @@
|
|||
|
||||
struct ClientSettings
|
||||
{
|
||||
#ifdef HAS_WASAPI
|
||||
enum class WasapiMode
|
||||
{
|
||||
SHARED,
|
||||
EXCLUSIVE
|
||||
};
|
||||
#endif
|
||||
|
||||
struct ServerSettings
|
||||
{
|
||||
std::string host{""};
|
||||
|
@ -39,6 +47,9 @@ struct ClientSettings
|
|||
int latency{0};
|
||||
PcmDevice pcm_device;
|
||||
SampleFormat sample_format;
|
||||
#ifdef HAS_WASAPI
|
||||
WasapiMode wasapi_mode{ WasapiMode::SHARED };
|
||||
#endif
|
||||
};
|
||||
|
||||
struct LoggingSettings
|
||||
|
|
|
@ -145,7 +145,7 @@ void Controller::onMessageReceived(ClientConnection* /*connection*/, const msg::
|
|||
#endif
|
||||
#ifdef HAS_WASAPI
|
||||
if (!player_ && (player_name.empty() || (player_name == "wasapi")))
|
||||
player_ = make_unique<WASAPIPlayer>(pcm_device, stream_);
|
||||
player_ = make_unique<WASAPIPlayer>(pcm_device, stream_, settings_.player.wasapi_mode);
|
||||
#endif
|
||||
if (!player_)
|
||||
throw SnapException("No audio player support");
|
||||
|
|
|
@ -61,7 +61,7 @@ EXTERN_C const PROPERTYKEY DECLSPEC_SELECTANY PKEY_Device_FriendlyName = { { 0xa
|
|||
|
||||
#define CHECK_HR(hres) if(FAILED(hres)){stringstream ss;ss<<"HRESULT fault status: "<<hex<<(hres)<<" line "<<dec<<__LINE__<<endl; LOG(FATAL, LOG_TAG) << ss.str();throw SnapException(ss.str());}
|
||||
|
||||
WASAPIPlayer::WASAPIPlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> stream) : Player(pcmDevice, stream)
|
||||
WASAPIPlayer::WASAPIPlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> stream, ClientSettings::WasapiMode mode) : Player(pcmDevice, stream), mode_(mode)
|
||||
{
|
||||
HRESULT hr = CoInitializeEx(
|
||||
NULL,
|
||||
|
@ -92,7 +92,7 @@ inline PcmDevice convertToDevice(int idx, IMMDevicePtr& device)
|
|||
PropVariantInit(&deviceName);
|
||||
|
||||
hr = properties->GetValue(PKEY_Device_FriendlyName, &deviceName);
|
||||
CHECK_HR(hr);
|
||||
CHECK_HR(hr);
|
||||
|
||||
desc.idx = idx;
|
||||
desc.name = wstring_convert<codecvt_utf8<wchar_t>, wchar_t>().to_bytes(id);
|
||||
|
@ -214,18 +214,20 @@ void WASAPIPlayer::worker()
|
|||
CHECK_HR(hr);
|
||||
|
||||
PWAVEFORMATEX formatEx = (PWAVEFORMATEX)format.blob.pBlobData;
|
||||
LOG(INFO, LOG_TAG) << "Device accepts format: " << formatEx->nSamplesPerSec << ":" << formatEx->wBitsPerSample << ":" << formatEx->nChannels << "\n";
|
||||
|
||||
LOG(INFO, LOG_TAG) << "Device accepts format: " << formatEx->nSamplesPerSec << ":" << formatEx->wBitsPerSample << ":" << formatEx->nChannels << "\n";
|
||||
// Activate the device
|
||||
IAudioClientPtr audioClient = nullptr;
|
||||
hr = device->Activate(IID_IAudioClient, CLSCTX_SERVER, NULL, (void**)&audioClient);
|
||||
CHECK_HR(hr);
|
||||
|
||||
hr = audioClient->IsFormatSupported(
|
||||
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||
&(waveformatExtended->Format),
|
||||
NULL);
|
||||
CHECK_HR(hr);
|
||||
if(mode_ == ClientSettings::WasapiMode::EXCLUSIVE)
|
||||
{
|
||||
hr = audioClient->IsFormatSupported(
|
||||
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||
&(waveformatExtended->Format),
|
||||
NULL);
|
||||
CHECK_HR(hr);
|
||||
}
|
||||
|
||||
IAudioSessionManagerPtr sessionManager = nullptr;
|
||||
// Get the session manager for the endpoint device.
|
||||
|
@ -249,14 +251,22 @@ void WASAPIPlayer::worker()
|
|||
hr = audioClient->GetDevicePeriod(NULL, &hnsRequestedDuration);
|
||||
CHECK_HR(hr);
|
||||
|
||||
LOG(INFO, LOG_TAG) << "Initializing WASAPI in " << (mode_ == ClientSettings::WasapiMode::SHARED ? "shared" : "exclusive") << " mode\n";
|
||||
|
||||
_AUDCLNT_SHAREMODE share_mode = mode_ == ClientSettings::WasapiMode::SHARED ? AUDCLNT_SHAREMODE_SHARED : AUDCLNT_SHAREMODE_EXCLUSIVE;
|
||||
DWORD stream_flags = mode_ == ClientSettings::WasapiMode::SHARED
|
||||
? AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
||||
: AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
||||
|
||||
// Initialize the client at minimum latency
|
||||
hr = audioClient->Initialize(
|
||||
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
share_mode,
|
||||
stream_flags,
|
||||
hnsRequestedDuration,
|
||||
hnsRequestedDuration,
|
||||
&(waveformatExtended->Format),
|
||||
NULL);
|
||||
|
||||
if (hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED)
|
||||
{
|
||||
UINT32 alignedBufferSize;
|
||||
|
@ -267,8 +277,8 @@ void WASAPIPlayer::worker()
|
|||
hr = device->Activate(IID_IAudioClient, CLSCTX_SERVER, NULL, (void**)&audioClient);
|
||||
CHECK_HR(hr);
|
||||
hr = audioClient->Initialize(
|
||||
AUDCLNT_SHAREMODE_EXCLUSIVE,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
share_mode,
|
||||
stream_flags,
|
||||
hnsRequestedDuration,
|
||||
hnsRequestedDuration,
|
||||
&(waveformatExtended->Format),
|
||||
|
@ -350,20 +360,32 @@ void WASAPIPlayer::worker()
|
|||
volCorrection_ = audioEventListener_->getVolume();
|
||||
|
||||
clock->GetPosition(&position, NULL);
|
||||
|
||||
UINT32 padding = 0;
|
||||
if(mode_ == ClientSettings::WasapiMode::SHARED)
|
||||
{
|
||||
hr = audioClient->GetCurrentPadding(&padding);
|
||||
CHECK_HR(hr);
|
||||
}
|
||||
|
||||
int available = bufferFrameCount - padding;
|
||||
|
||||
if (stream_->getPlayerChunk(queueBuffer.get(), microseconds(
|
||||
((bufferPosition * 1000000) / waveformat->nSamplesPerSec) -
|
||||
((position * 1000000) / frequency)),
|
||||
bufferFrameCount))
|
||||
available))
|
||||
{
|
||||
adjustVolume(queueBuffer.get(), bufferFrameCount);
|
||||
hr = renderClient->GetBuffer(bufferFrameCount, &buffer);
|
||||
CHECK_HR(hr);
|
||||
memcpy(buffer, queueBuffer.get(), bufferSize);
|
||||
hr = renderClient->ReleaseBuffer(bufferFrameCount, 0);
|
||||
CHECK_HR(hr);
|
||||
if (available > 0)
|
||||
{
|
||||
adjustVolume(queueBuffer.get(), available);
|
||||
hr = renderClient->GetBuffer(available, &buffer);
|
||||
CHECK_HR(hr);
|
||||
memcpy(buffer, queueBuffer.get(), bufferSize);
|
||||
hr = renderClient->ReleaseBuffer(available, 0);
|
||||
CHECK_HR(hr);
|
||||
|
||||
bufferPosition += bufferFrameCount;
|
||||
bufferPosition += available;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
|
||||
#ifndef WASAPI_PLAYER_H
|
||||
#define WASAPI_PLAYER_H
|
||||
|
||||
#include "player.hpp"
|
||||
#include <audiopolicy.h>
|
||||
#include "client_settings.hpp"
|
||||
|
||||
class AudioSessionEventListener : public IAudioSessionEvents
|
||||
{
|
||||
|
@ -93,7 +93,7 @@ public:
|
|||
class WASAPIPlayer : public Player
|
||||
{
|
||||
public:
|
||||
WASAPIPlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> stream);
|
||||
WASAPIPlayer(const PcmDevice& pcmDevice, std::shared_ptr<Stream> stream, ClientSettings::WasapiMode mode);
|
||||
virtual ~WASAPIPlayer();
|
||||
|
||||
static std::vector<PcmDevice> pcm_list(void);
|
||||
|
@ -102,6 +102,7 @@ protected:
|
|||
|
||||
private:
|
||||
AudioSessionEventListener* audioEventListener_;
|
||||
ClientSettings::WasapiMode mode_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -115,6 +115,9 @@ int main(int argc, char** argv)
|
|||
#ifdef HAS_SOXR
|
||||
auto sample_format = op.add<Value<string>>("", "sampleformat", "resample audio stream to <rate>:<bits>:<channels>", "");
|
||||
#endif
|
||||
#ifdef HAS_WASAPI
|
||||
auto wasapi_mode = op.add<Value<string>>("", "wasapimode", "WASAPI mode to use [shared/exclusive]", "shared");
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -225,10 +228,6 @@ int main(int argc, char** argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAS_WASAPI)
|
||||
settings.player.player_name = "wasapi";
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SOXR
|
||||
if (sample_format->is_set())
|
||||
{
|
||||
|
@ -241,6 +240,15 @@ int main(int argc, char** argv)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_WASAPI
|
||||
settings.player.player_name = "wasapi";
|
||||
if (wasapi_mode->is_set())
|
||||
{
|
||||
settings.player.wasapi_mode = (strcmp(wasapi_mode->value().c_str(), "exclusive") == 0) ?
|
||||
ClientSettings::WasapiMode::EXCLUSIVE : ClientSettings::WasapiMode::SHARED;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool active = true;
|
||||
std::shared_ptr<Controller> controller;
|
||||
auto signal_handler = install_signal_handler({
|
||||
|
|
Loading…
Add table
Reference in a new issue