Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Discord server at [https://discord.gg/9rwFdMW](https://discord.gg/9rwFdMW).

## System Requirements

* Windows 7 or later. If you are running a current version of Windows 10, Secure Boot is not supported.
* Windows 10 or later. Secure Boot is not supported.
* An audio interface which supports ASIO. If your hardware doesn't come with a
native ASIO driver, you may be able to use ASIO4ALL instead.
* Digital audio workstation software. SAR is mainly tested using [REAPER](http://www.reaper.fm/).
Expand Down Expand Up @@ -84,12 +84,6 @@ ASIO driver is closed. If you're using REAPER, make sure "Close audio device
when stopped and application is inactive" in the Audio preferences is
disabled.

## Windows 7 Installation Note

Make sure you have Windows update [KB3033929](https://technet.microsoft.com/en-us/library/security/3033929.aspx)
installed prior to installing SAR, otherwise you will receive an error about
an unsigned driver.

## Unsigned prereleased drivers note

Prereleases of SAR are unsigned. That means that it is required to enable
Expand Down
141 changes: 0 additions & 141 deletions SarAsio/sarclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,6 @@ void SarClient::tick(long bufferIndex)
if (!_registers)
return;

if (_updateSampleRateOnTick.exchange(false)) {
DWORD dummy;

DeviceIoControl(_device, SAR_SEND_FORMAT_CHANGE_EVENT,
nullptr, 0, nullptr, 0, &dummy, nullptr);
}

// for each endpoint
// read isActive, generation and buffer offset/size/position
// if offset/size invalid, skip endpoint (fill asio buffers with 0)
Expand Down Expand Up @@ -187,12 +180,6 @@ bool SarClient::start()
return false;
}

if (!openMmNotificationClient()) {
LOG(ERROR) << "Couldn't open MMDevice notification client";
stop();
return false;
}

if (!setBufferLayout()) {
LOG(ERROR) << "Couldn't set layout";
stop();
Expand All @@ -214,21 +201,6 @@ bool SarClient::start()

void SarClient::stop()
{
if (_mmNotificationClientRegistered) {
_mmEnumerator->UnregisterEndpointNotificationCallback(
_mmNotificationClient);
_mmNotificationClientRegistered = false;
}

if (_mmNotificationClient) {
_mmNotificationClient->Release();
_mmNotificationClient = nullptr;
}

if (_mmEnumerator) {
_mmEnumerator = nullptr;
}

if (_device != INVALID_HANDLE_VALUE) {
_registersLock.lock();
CancelIoEx(_device, nullptr);
Expand Down Expand Up @@ -327,34 +299,6 @@ bool SarClient::openControlDevice()
return true;
}

bool SarClient::openMmNotificationClient()
{
if (FAILED(CoCreateInstance(
__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator), (LPVOID *)&_mmEnumerator))) {

return false;
}

if (FAILED(CComObject<NotificationClient>::CreateInstance(
&_mmNotificationClient))) {

return false;
}

_mmNotificationClient->AddRef();
_mmNotificationClient->setClient(shared_from_this());

if (FAILED(_mmEnumerator->RegisterEndpointNotificationCallback(
_mmNotificationClient))) {

return false;
}

_mmNotificationClientRegistered = true;
return true;
}

bool SarClient::setBufferLayout()
{
SarSetBufferLayoutRequest request = {};
Expand Down Expand Up @@ -580,89 +524,4 @@ void SarClient::mux(
}
}

HRESULT STDMETHODCALLTYPE SarClient::NotificationClient::OnDeviceStateChanged(
_In_ LPCWSTR pwstrDeviceId,
_In_ DWORD dwNewState)
{
// When a SAR endpoint is re-activated after its initial creation, its
// supported sample rate may be different. To force the audio engine to
// notice the possible format change, we listen for device state change
// events and tell the kernel mode driver to broadcast a
// KSEVENT_PINCAPS_FORMATCHANGE event, which causes the audio engine to
// re-query the pin capabilities. This isn't needed for newly added
// endpoints or non-SAR endpoints, so we filter out those events.
if (dwNewState != DEVICE_STATE_ACTIVE) {
return S_OK;
}

if (auto client = _client.lock()) {
do {
CComPtr<IMMDeviceEnumerator> mmEnumerator;
CComPtr<IMMDevice> device;
CComPtr<IPropertyStore> ps;
PROPVARIANT pvalue = {};

// This seems a bit shady, but MSDN's device events example
// initializes COM the same way. It's not clear what the ownership
// of the thread that delivers the IMMNotificationClient events is.
CoInitialize(NULL);

if (FAILED(CoCreateInstance(
__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
__uuidof(IMMDeviceEnumerator), (LPVOID *)&mmEnumerator))) {

break;
}

if (FAILED(mmEnumerator->GetDevice(pwstrDeviceId, &device))) {
break;
}

if (FAILED(device->OpenPropertyStore(STGM_READ, &ps))) {
break;
}

if (FAILED(ps->GetValue(
PKEY_SynchronousAudioRouter_EndpointId, &pvalue))) {

break;
}

client->updateSampleRateOnTick();
PropVariantClear(&pvalue);
} while(false);

CoUninitialize();
}

return S_OK;
}

HRESULT STDMETHODCALLTYPE SarClient::NotificationClient::OnDeviceAdded(
_In_ LPCWSTR pwstrDeviceId)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE SarClient::NotificationClient::OnDeviceRemoved(
_In_ LPCWSTR pwstrDeviceId)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE SarClient::NotificationClient::OnDefaultDeviceChanged(
_In_ EDataFlow flow,
_In_ ERole role,
_In_ LPCWSTR pwstrDefaultDeviceId)
{
return S_OK;
}

HRESULT STDMETHODCALLTYPE SarClient::NotificationClient::OnPropertyValueChanged(
_In_ LPCWSTR pwstrDeviceId,
_In_ const PROPERTYKEY key)
{
return S_OK;
}

} // namespace Sar
43 changes: 0 additions & 43 deletions SarAsio/sarclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ struct SarClient: public std::enable_shared_from_this<SarClient>
void tick(long bufferIndex);
bool start();
void stop();
void updateSampleRateOnTick()
{
_updateSampleRateOnTick = true;
}

private:
struct NotificationHandle
Expand All @@ -64,42 +60,7 @@ struct SarClient: public std::enable_shared_from_this<SarClient>
SarHandleQueueResponse responses[32];
};

struct ATL_NO_VTABLE NotificationClient:
public CComObjectRootEx<CComMultiThreadModel>,
public IMMNotificationClient
{
BEGIN_COM_MAP(NotificationClient)
COM_INTERFACE_ENTRY(IMMNotificationClient)
END_COM_MAP()

DECLARE_NO_REGISTRY()

void setClient(std::weak_ptr<SarClient> client)
{
_client = client;
}

virtual HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
_In_ LPCWSTR pwstrDeviceId,
_In_ DWORD dwNewState) override;
virtual HRESULT STDMETHODCALLTYPE OnDeviceAdded(
_In_ LPCWSTR pwstrDeviceId) override;
virtual HRESULT STDMETHODCALLTYPE OnDeviceRemoved(
_In_ LPCWSTR pwstrDeviceId) override;
virtual HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
_In_ EDataFlow flow,
_In_ ERole role,
_In_ LPCWSTR pwstrDefaultDeviceId) override;
virtual HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
_In_ LPCWSTR pwstrDeviceId,
_In_ const PROPERTYKEY key) override;

private:
std::weak_ptr<SarClient> _client;
};

bool openControlDevice();
bool openMmNotificationClient();
bool setBufferLayout();
bool createEndpoints();
bool enableRegistryFilter();
Expand Down Expand Up @@ -127,10 +88,6 @@ struct SarClient: public std::enable_shared_from_this<SarClient>
volatile SarEndpointRegisters *_registers;
HandleQueueCompletion _handleQueueCompletion;
bool _handleQueueStarted;
CComPtr<IMMDeviceEnumerator> _mmEnumerator;
CComObject<NotificationClient> *_mmNotificationClient = nullptr;
bool _mmNotificationClientRegistered = false;
std::atomic<bool> _updateSampleRateOnTick = false;
std::mutex _registersLock;
};

Expand Down
2 changes: 1 addition & 1 deletion SarAsio/targetver.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
#include <winsdkver.h>

#define _WIN32_WINNT 0x0601
#define _WIN32_WINNT 0x0A00 // Windows 10

#include <SDKDDKVer.h>
90 changes: 11 additions & 79 deletions SarConfigure/SarConfigure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,89 +59,21 @@ static int openConfigurationDialog(Sar::AsioDriver& driver) {
}
}

static bool broadcastPinFormatChange() {
HDEVINFO devinfo;
SP_DEVICE_INTERFACE_DATA interfaceData;
PSP_DEVICE_INTERFACE_DETAIL_DATA interfaceDetail;
DWORD requiredSize;
HANDLE _device;
DWORD dummy;

interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
devinfo = SetupDiGetClassDevs(
&GUID_DEVINTERFACE_SYNCHRONOUSAUDIOROUTER, nullptr, nullptr,
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);

if (devinfo == INVALID_HANDLE_VALUE) {
return false;
}

if (!SetupDiEnumDeviceInterfaces(devinfo, NULL,
&GUID_DEVINTERFACE_SYNCHRONOUSAUDIOROUTER, 0, &interfaceData)) {

return false;
}

SetLastError(0);
SetupDiGetDeviceInterfaceDetail(
devinfo, &interfaceData, nullptr, 0, &requiredSize, nullptr);

if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
return false;
}

interfaceDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(requiredSize);
interfaceDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

if (!SetupDiGetDeviceInterfaceDetail(
devinfo, &interfaceData, interfaceDetail,
requiredSize, nullptr, nullptr)) {

free(interfaceDetail);
return false;
}

_device = CreateFile(interfaceDetail->DevicePath,
GENERIC_ALL, 0, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, nullptr);

if (_device == INVALID_HANDLE_VALUE) {
free(interfaceDetail);
return false;
}

free(interfaceDetail);

DeviceIoControl(_device, SAR_SEND_FORMAT_CHANGE_EVENT,
nullptr, 0, nullptr, 0, &dummy, nullptr);
printf("Sent SAR_SEND_FORMAT_CHANGE_EVENT\n");

CloseHandle(_device);

return true;
}

int main(int argc, char *argv[])
{
CoInitialize(NULL);

if (argc >= 2 && !strcmp(argv[1], "-u")) {
broadcastPinFormatChange();
}
else {
CoInitialize(NULL);

std::vector<Sar::AsioDriver> asioDrivers = Sar::InstalledAsioDrivers();
std::vector<Sar::AsioDriver> asioDrivers = Sar::InstalledAsioDrivers();

printf("\n");
for (size_t i = 0; i < asioDrivers.size(); i++) {
printf("Checking driver: %s == %s\n", asioDrivers[i].clsid.c_str(), CLSID_STR_SynchronousAudioRouter);
if (asioDrivers[i].clsid == CLSID_STR_SynchronousAudioRouter) {
Sar::AsioDriver& driver = asioDrivers[i];
printf("Found SAR ASIO driver: %s\n", driver.name.c_str());
return openConfigurationDialog(driver);
}
printf("\n");
for (size_t i = 0; i < asioDrivers.size(); i++) {
printf("Checking driver: %s == %s\n", asioDrivers[i].clsid.c_str(), CLSID_STR_SynchronousAudioRouter);
if (asioDrivers[i].clsid == CLSID_STR_SynchronousAudioRouter) {
Sar::AsioDriver& driver = asioDrivers[i];
printf("Found SAR ASIO driver: %s\n", driver.name.c_str());
return openConfigurationDialog(driver);
}

CoUninitialize();
}

CoUninitialize();
}
2 changes: 1 addition & 1 deletion SarInstallerActions/targetver.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

#include <winsdkver.h>

#define _WIN32_WINNT 0x0601
#define _WIN32_WINNT 0x0A00 // Windows 10

#include <sdkddkver.h>
8 changes: 8 additions & 0 deletions SarTest/SarTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ int usage()
"Other options: --results <file.json> --phase-timeout S --no-sarasio-log\n"
" --keep-config (host/run: do not rewrite default.json)\n"
" --app-routing (turn on application routing in default.json)\n"
" --expect-format (fail streams whose engine format isn't --rate\n"
" and --channels)\n"
" --registered (use the COM-registered SarAsio instead of a path)\n"
" --wait S --wait-gone S --restart-delay MS --rate HZ\n"
" --min-valid RATIO --max-discontinuities N\n"
Expand Down Expand Up @@ -132,6 +134,12 @@ WasapiOptions wasapiOptions(const Args& args, const EndpointLayout& layout)
options.settleSeconds = args.getDouble(L"settle", 2.0);
options.maxReopens = args.getInt(L"max-reopens", 20);
options.maxTransitionFrames = args.getInt(L"max-transition", 4800);

if (args.has(L"expect-format")) {
options.expectedRate = (unsigned long)args.getDouble(L"rate", 48000.0);
options.expectedChannels = layout.channels;
}

return options;
}

Expand Down
Loading
Loading