Windows: Fix test failures (#1300)
* Windows: increase audio block time to match other implementations. * Set Communications category for FreeDV streams. * Fix compiler errors. * Another attempt at fixing compiler errors. * Fix typo. * Disable HW offload. * Add PR #1300 to changelog. * AudioClientProperties needs to be set before getting mix format. * Try including intttypes.h. * Try FALSE in GetBufferSizeLimits. * Try enabling offload first, then fall back to no offload. * Try installing audio drivers as administrator. * Fix syntax errors. * Fix typo in script. * Fix additional typo. * Take wakeup time into account for timeout calculation. * Fix incorrect cast. * Use same method of retrieving cert as for Virtual Audio Cable. * Update driver install script to create missing Registry key. * Round up to next millisecond. * Add basic implementation of IAudioClient3, no tuning of periods yet. * Fix typo. * Fix another compiler error.pull/1306/head
parent
8022b8bbaa
commit
6940f812e1
|
|
@ -111,11 +111,14 @@ jobs:
|
|||
run: |
|
||||
Invoke-WebRequest https://download.vb-audio.com/Download_CABLE/VBCABLE_Driver_Pack45.zip -OutFile VBCABLE_Driver_Pack45.zip
|
||||
Expand-Archive -Path VBCABLE_Driver_Pack45.zip -DestinationPath VBCABLE_Driver_Pack45
|
||||
Import-Certificate -FilePath ${{github.workspace}}\ci\vbcable.cer -CertStoreLocation Cert:\LocalMachine\root
|
||||
Import-Certificate -FilePath ${{github.workspace}}\ci\vbcable.cer -CertStoreLocation Cert:\LocalMachine\TrustedPublisher
|
||||
Get-ChildItem -Path $Env:WindowsSdkDir -Recurse -Filter "devgen.exe" | Select-Object FullName
|
||||
& "${{ env.WindowsSdkDir }}\Tools\${{ env.WindowsSDKVersion }}\${{ env.Platform }}\devgen.exe" /add /bus ROOT /hardwareid VBAudioVACWDM
|
||||
pnputil /add-driver VBCable_Driver_Pack45\vbMmeCable64_win10.inf /install
|
||||
$driverFile = 'VBCable_Driver_Pack45\vbaudio_cable64_win10.sys';
|
||||
$outputFile = '${{github.workspace}}\vbcable.cer';
|
||||
$exportType = [System.Security.Cryptography.X509Certificates.X509ContentType]::Cert;
|
||||
|
||||
$cert = (Get-AuthenticodeSignature $driverFile).SignerCertificate;
|
||||
[System.IO.File]::WriteAllBytes($outputFile, $cert.Export($exportType));
|
||||
|
||||
.\ci\Install-Driver.ps1 $outputFile VBCable_Driver_Pack45\vbMmeCable64_win10.inf VBAudioVACWDM
|
||||
|
||||
- name: Install VAC ("analog" sound device)
|
||||
run: |
|
||||
|
|
@ -128,10 +131,7 @@ jobs:
|
|||
$cert = (Get-AuthenticodeSignature $driverFile).SignerCertificate;
|
||||
[System.IO.File]::WriteAllBytes($outputFile, $cert.Export($exportType));
|
||||
|
||||
Import-Certificate -FilePath ${{github.workspace}}\vac.cer -CertStoreLocation Cert:\LocalMachine\root
|
||||
Import-Certificate -FilePath ${{github.workspace}}\vac.cer -CertStoreLocation Cert:\LocalMachine\TrustedPublisher
|
||||
& "${{ env.WindowsSdkDir }}\Tools\${{ env.WindowsSDKVersion }}\${{ env.Platform }}\devgen.exe" /add /bus ROOT /hardwareid VirtualAudioCable_83ed7f0e-2028-4956-b0b4-39c76fdaef1d
|
||||
pnputil /add-driver "vac470lite\vrtaucbl.inf" /install
|
||||
.\ci\Install-Driver.ps1 $outputFile "vac470lite/vrtaucbl.inf" VirtualAudioCable_83ed7f0e-2028-4956-b0b4-39c76fdaef1d
|
||||
|
||||
- name: Screenshot if failed
|
||||
if: failure()
|
||||
|
|
|
|||
|
|
@ -952,7 +952,7 @@ LDPC | Low Density Parity Check Codes - a family of powerful FEC codes
|
|||
* Update Hamlib to 4.7.0. (PR #1226)
|
||||
* Update wxWidgets to 3.3.2. (PR #1244)
|
||||
* Flex: Update Docker container version to match AppImage version. (PR #1256)
|
||||
* Reenable Windows on ARM builds previously disabled in 2.0.0. (PR #1297)
|
||||
* Reenable Windows on ARM builds previously disabled in 2.0.0. (PR #1297, #1300)
|
||||
4. Documentation:
|
||||
* Update README instructions to reflect current Windows build steps. (PR #1232)
|
||||
* Add OmniRig troubleshooting to the user manual. (PR #1264)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
# Make sure we are running as Admin
|
||||
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")){
|
||||
$arguments = "& '" + $myinvocation.mycommand.definition + "'"
|
||||
Start-Process powershell -Verb runAs -ArgumentList $arguments
|
||||
Break
|
||||
}
|
||||
|
||||
$certName = $args[0]
|
||||
$infName = $args[1]
|
||||
$hwId = $args[2]
|
||||
|
||||
Import-Certificate -FilePath $certName -CertStoreLocation Cert:\LocalMachine\root
|
||||
|
||||
# Some runners in the GH environment will emit "Access Denied" for Import-Certificate
|
||||
# if the proper TrustedPublisher Registry key doesn't exist first. Create prior to
|
||||
# attempting import.
|
||||
# See https://learn.microsoft.com/en-us/answers/questions/1679945/(import-certificate)-unauthorizedaccessexception-e
|
||||
$Key = "HKLM:\Software\Microsoft\SystemCertificates\TrustedPublisher"
|
||||
If (-Not (Test-Path $Key)) {
|
||||
New-Item -Path $Key -ItemType RegistryKey -Force
|
||||
}
|
||||
`
|
||||
Import-Certificate -FilePath $certName -CertStoreLocation Cert:\LocalMachine\TrustedPublisher
|
||||
|
||||
& "$Env:WindowsSdkDir\Tools\$Env:WindowsSDKVersion\$Env:Platform\devgen.exe" /add /bus ROOT /hardwareid $hwId
|
||||
pnputil /add-driver $infName /install
|
||||
|
|
@ -27,16 +27,17 @@
|
|||
#include <thread>
|
||||
#include <future>
|
||||
#include <avrt.h>
|
||||
#include <inttypes.h>
|
||||
#include "../util/logging/ulog.h"
|
||||
|
||||
#define BLOCK_TIME_NS (10000000)
|
||||
#define BLOCK_TIME_NS (20000000)
|
||||
|
||||
// Nanoseconds per REFERENCE_TIME unit
|
||||
#define NS_PER_REFTIME (100)
|
||||
|
||||
thread_local HANDLE WASAPIAudioDevice::HelperTask_ = nullptr;
|
||||
|
||||
WASAPIAudioDevice::WASAPIAudioDevice(ComPtr<IAudioClient> client, ComPtr<IMMDevice> device, IAudioEngine::AudioDirection direction, int sampleRate, int numChannels)
|
||||
WASAPIAudioDevice::WASAPIAudioDevice(ComPtr<IAudioClient3> client, ComPtr<IMMDevice> device, IAudioEngine::AudioDirection direction, int sampleRate, int numChannels)
|
||||
: Win32COMObject("WASAPIDev")
|
||||
, client_(client)
|
||||
, device_(device)
|
||||
|
|
@ -52,6 +53,7 @@ WASAPIAudioDevice::WASAPIAudioDevice(ComPtr<IAudioClient> client, ComPtr<IMMDevi
|
|||
, isRenderCaptureRunning_(false)
|
||||
, semaphore_(nullptr)
|
||||
, tmpBuf_(nullptr)
|
||||
, extraTimeMs_(0)
|
||||
{
|
||||
// empty
|
||||
}
|
||||
|
|
@ -94,12 +96,33 @@ void WASAPIAudioDevice::start()
|
|||
WAVEFORMATEX* streamFormatPtr = nullptr;
|
||||
WAVEFORMATEX streamFormat;
|
||||
bool freeStreamFormat = false;
|
||||
|
||||
|
||||
// Set AudioClientProperties for stream. Must be done prior to Initialize().
|
||||
AudioClientProperties prop;
|
||||
prop.cbSize = sizeof(AudioClientProperties);
|
||||
prop.bIsOffload = TRUE;
|
||||
prop.eCategory = AudioCategory_Communications;
|
||||
prop.Options = AUDCLNT_STREAMOPTIONS_NONE;
|
||||
HRESULT hr = client_->SetClientProperties(&prop);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Try disabling offload as not all devices support it.
|
||||
prop.bIsOffload = FALSE;
|
||||
hr = client_->SetClientProperties(&prop);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Non-critical error, can continue without setting properties.
|
||||
std::stringstream ss;
|
||||
ss << "Could not set AudioClient properties (hr = " << hr << ")";
|
||||
log_warn(ss.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// Populate stream format based on requested sample
|
||||
// rate/number of channels.
|
||||
// NOTE: this should already have been determined valid
|
||||
// by the audio engine!
|
||||
HRESULT hr = client_->GetMixFormat(&streamFormatPtr);
|
||||
hr = client_->GetMixFormat(&streamFormatPtr);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
freeStreamFormat = true;
|
||||
|
|
@ -187,16 +210,65 @@ void WASAPIAudioDevice::start()
|
|||
|
||||
if (!initialized_)
|
||||
{
|
||||
//REFERENCE_TIME desiredRefTime = BLOCK_TIME_NS / NS_PER_REFTIME; // REFERENCE_TIME is in 100ns units
|
||||
UINT32 defaultPeriodInFrames = 0;
|
||||
UINT32 fundamentalPeriodInFrames = 0;
|
||||
UINT32 minPeriodInFrames = 0;
|
||||
UINT32 maxPeriodInFrames = 0;
|
||||
hr = client_->GetSharedModeEnginePeriod(
|
||||
streamFormatPtr,
|
||||
&defaultPeriodInFrames,
|
||||
&fundamentalPeriodInFrames,
|
||||
&minPeriodInFrames,
|
||||
&maxPeriodInFrames);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Could not get audio engine period (hr = " << hr << ")";
|
||||
log_error(ss.str().c_str());
|
||||
if (onAudioErrorFunction)
|
||||
{
|
||||
onAudioErrorFunction(*this, ss.str(), onAudioErrorState);
|
||||
}
|
||||
prom->set_value();
|
||||
return;
|
||||
}
|
||||
|
||||
log_info(
|
||||
"Default period: %d, fundamental period: %d, minPeriod: %d, maxPeriod: %d",
|
||||
defaultPeriodInFrames,
|
||||
fundamentalPeriodInFrames,
|
||||
minPeriodInFrames,
|
||||
maxPeriodInFrames);
|
||||
|
||||
// Initialize the audio client with the above format
|
||||
hr = client_->Initialize(
|
||||
AUDCLNT_SHAREMODE_SHARED,
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
|
||||
AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM |
|
||||
AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY,
|
||||
BLOCK_TIME_NS / NS_PER_REFTIME, // REFERENCE_TIME is in 100ns units
|
||||
0,
|
||||
hr = client_->InitializeSharedAudioStream(
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
defaultPeriodInFrames,
|
||||
streamFormatPtr,
|
||||
nullptr);
|
||||
if (AUDCLNT_E_ENGINE_PERIODICITY_LOCKED == hr)
|
||||
{
|
||||
// Try again with actual period
|
||||
WAVEFORMATEX* tempFormat = nullptr;
|
||||
hr = client_->GetCurrentSharedModeEnginePeriod(
|
||||
&tempFormat,
|
||||
&defaultPeriodInFrames);
|
||||
(void)tempFormat; // ignore warnings
|
||||
if (FAILED(hr))
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Could not get current engine period (hr = " << hr << ")";
|
||||
log_warn(ss.str().c_str());
|
||||
}
|
||||
|
||||
hr = client_->InitializeSharedAudioStream(
|
||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
||||
defaultPeriodInFrames,
|
||||
streamFormatPtr,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
if (freeStreamFormat)
|
||||
{
|
||||
CoTaskMemFree(streamFormatPtr);
|
||||
|
|
@ -402,7 +474,7 @@ void WASAPIAudioDevice::start()
|
|||
// Capture references for use by this thread.
|
||||
ComPtr<IAudioRenderClient> renderClientRef = renderClient_;
|
||||
ComPtr<IAudioCaptureClient> captureClientRef = captureClient_;
|
||||
ComPtr<IAudioClient> clientRef = client_;
|
||||
ComPtr<IAudioClient3> clientRef = client_;
|
||||
|
||||
HRESULT res = CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
|
||||
if (FAILED(res))
|
||||
|
|
@ -525,7 +597,7 @@ void WASAPIAudioDevice::setHelperRealTime()
|
|||
|
||||
void WASAPIAudioDevice::startRealTimeWork()
|
||||
{
|
||||
// empty
|
||||
startTime_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
void WASAPIAudioDevice::stopRealTimeWork(bool fastMode)
|
||||
|
|
@ -537,9 +609,21 @@ void WASAPIAudioDevice::stopRealTimeWork(bool fastMode)
|
|||
return;
|
||||
}
|
||||
|
||||
int64_t msec = ((1000 * bufferFrameCount_) / sampleRate_) >> (fastMode ? 1 : 0);
|
||||
msec -= extraTimeMs_;
|
||||
if (msec <= 0)
|
||||
{
|
||||
extraTimeMs_ = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait a maximum of (bufferSize / sampleRate) seconds for the semaphore to return
|
||||
DWORD result = WaitForSingleObject(semaphore_, ((1000 * bufferFrameCount_) / sampleRate_) >> (fastMode ? 1 : 0));
|
||||
|
||||
auto endTime = std::chrono::steady_clock::now();
|
||||
auto duration = std::chrono::ceil<std::chrono::milliseconds>(endTime - startTime_).count() - msec;
|
||||
extraTimeMs_ = std::max((int64_t)0, (int64_t)duration); // cap extra time to >= 0.
|
||||
|
||||
if (result != WAIT_TIMEOUT && result != WAIT_OBJECT_0)
|
||||
{
|
||||
// Fallback to a simple sleep.
|
||||
|
|
|
|||
|
|
@ -69,10 +69,10 @@ public:
|
|||
protected:
|
||||
friend class WASAPIAudioEngine;
|
||||
|
||||
WASAPIAudioDevice(ComPtr<IAudioClient> client, ComPtr<IMMDevice> device, IAudioEngine::AudioDirection direction, int sampleRate, int numChannels);
|
||||
WASAPIAudioDevice(ComPtr<IAudioClient3> client, ComPtr<IMMDevice> device, IAudioEngine::AudioDirection direction, int sampleRate, int numChannels);
|
||||
|
||||
private:
|
||||
ComPtr<IAudioClient> client_;
|
||||
ComPtr<IAudioClient3> client_;
|
||||
ComPtr<IMMDevice> device_;
|
||||
ComPtr<IAudioRenderClient> renderClient_;
|
||||
ComPtr<IAudioCaptureClient> captureClient_;
|
||||
|
|
@ -93,6 +93,10 @@ private:
|
|||
bool isFloatingPoint_;
|
||||
short* tmpBuf_;
|
||||
|
||||
// For handling additional wakeup time after semaphore timeout
|
||||
int extraTimeMs_;
|
||||
std::chrono::time_point<std::chrono::steady_clock> startTime_;
|
||||
|
||||
void renderAudio_(ComPtr<IAudioRenderClient> renderClient);
|
||||
void captureAudio_(ComPtr<IAudioCaptureClient> captureClient);
|
||||
void copyFromWindowsBuffer_(void* buf, int numFrames);
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ std::shared_ptr<IAudioDevice> WASAPIAudioEngine::getAudioDevice(wxString deviceN
|
|||
log_info("Creating WASAPIAudioDevice for device %s (ID %d, direction = %d, sample rate = %d, number of channels = %d)", (const char*)deviceName.ToUTF8(), (int)dev.deviceId, (int)direction, sampleRate, numChannels);
|
||||
|
||||
ComPtr<IMMDevice> device = nullptr;
|
||||
ComPtr<IAudioClient> client = nullptr;
|
||||
ComPtr<IAudioClient3> client = nullptr;
|
||||
|
||||
HRESULT hr = coll->Item(dev.deviceId, device.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
|
|
@ -299,7 +299,7 @@ std::shared_ptr<IAudioDevice> WASAPIAudioEngine::getAudioDevice(wxString deviceN
|
|||
}
|
||||
|
||||
hr = device->Activate(
|
||||
IID_IAudioClient, CLSCTX_ALL,
|
||||
IID_IAudioClient3, CLSCTX_ALL,
|
||||
nullptr, (void**)client.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
|
|
@ -461,8 +461,8 @@ AudioDeviceSpecification WASAPIAudioEngine::getDeviceSpecification_(ComPtr<IMMDe
|
|||
}
|
||||
|
||||
// Activate IAudioClient so we can obtain format info
|
||||
ComPtr<IAudioClient> audioClient = nullptr;
|
||||
hr = device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, (void**)audioClient.GetAddressOf());
|
||||
ComPtr<IAudioClient3> audioClient = nullptr;
|
||||
hr = device->Activate(IID_IAudioClient3, CLSCTX_ALL, nullptr, (void**)audioClient.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
|
|
|||
Loading…
Reference in New Issue