├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── README_CN.md ├── RS_ASIO.sln ├── RS_ASIO ├── AsioHelpers.cpp ├── AsioHelpers.h ├── AsioSharedHost.cpp ├── AsioSharedHost.h ├── AudioProcessing.cpp ├── AudioProcessing.h ├── ComBaseUnknown.h ├── Configurator.cpp ├── Configurator.h ├── DebugDeviceEnum.cpp ├── DebugDeviceEnum.h ├── DebugWrapperAudioClient.cpp ├── DebugWrapperAudioClient.h ├── DebugWrapperAudioEndpointVolume.cpp ├── DebugWrapperAudioEndpointVolume.h ├── DebugWrapperCaptureClient.cpp ├── DebugWrapperCaptureClient.h ├── DebugWrapperDevice.cpp ├── DebugWrapperDevice.h ├── DebugWrapperDevicePropertyStore.cpp ├── DebugWrapperDevicePropertyStore.h ├── DebugWrapperEndpoint.cpp ├── DebugWrapperEndpoint.h ├── DebugWrapperRenderClient.cpp ├── DebugWrapperRenderClient.h ├── Log.cpp ├── Log.h ├── MyUnknown.h ├── NtProtectVirtualMemory.asm ├── Patcher.cpp ├── Patcher.h ├── Patcher_21a8959a.cpp ├── Patcher_6ea6d1ba.cpp ├── Patcher_d1b38fcb.cpp ├── RSAggregatorDeviceEnum.cpp ├── RSAggregatorDeviceEnum.h ├── RSAsioAudioCaptureClient.cpp ├── RSAsioAudioCaptureClient.h ├── RSAsioAudioClient.cpp ├── RSAsioAudioClient.h ├── RSAsioAudioClientServiceBase.cpp ├── RSAsioAudioClientServiceBase.h ├── RSAsioAudioEndpointVolume.cpp ├── RSAsioAudioEndpointVolume.h ├── RSAsioAudioRenderClient.cpp ├── RSAsioAudioRenderClient.h ├── RSAsioDevice.cpp ├── RSAsioDevice.h ├── RSAsioDeviceEnum.cpp ├── RSAsioDeviceEnum.h ├── RSAsioDevicePropertyStore.cpp ├── RSAsioDevicePropertyStore.h ├── RSBaseDeviceEnum.cpp ├── RSBaseDeviceEnum.h ├── RSDeviceCollection.cpp ├── RSDeviceCollection.h ├── RS_ASIO.args.json ├── RS_ASIO.vcxproj ├── RS_ASIO.vcxproj.filters ├── TrampolineToMethod.h ├── Utils.cpp ├── Utils.h ├── asio.h ├── crc32.cpp ├── crc32.h ├── dllmain.cpp ├── dllmain.h ├── exports.def ├── function_traits.h ├── stdafx.cpp ├── stdafx.h └── targetver.h ├── avrt ├── avrt.cpp ├── avrt.def ├── avrt.vcxproj ├── avrt.vcxproj.filters ├── dllmain.cpp ├── stdafx.cpp ├── stdafx.h └── targetver.h ├── dist └── RS_ASIO.ini └── docs ├── README.md ├── asus_strix_soar └── README.md ├── audient_evo_4 ├── Annotation 2020-08-16 224310.png ├── Annotation 2020-08-16 224424.png ├── Annotation 2020-08-16 232728.png ├── Annotation 2020-08-16 232812.png └── README.md ├── behringer_mic2usb └── README.md ├── boss_gcs_8 └── README.md ├── digidesign └── mbox2pro ├── focusrite_scarlett_18i8_2nd_gen_OR_asio_link_pro └── README.md ├── focusrite_solo └── README.md ├── irig_pro_duo_io └── README.md ├── katana_mk2 └── README.md ├── linux └── ubuntu_1204_lts.md ├── midiplus_studio_s └── README.md ├── roland_ua_55 └── README.md ├── roland_ua_5_usb ├── AsioAllV2.png ├── README.md └── edirol-ua-usb.jpg ├── steinberg_ur12 └── README.md ├── streaming ├── README.md └── README_CN.md ├── universal_audio_volt1 └── README.md ├── universal_audio_volt_276 └── README.md └── xtone_smartstomp ├── README.md └── xtone_smartstomp.png /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: windows-2019 7 | steps: 8 | - name: Checkout current build target 9 | uses: actions/checkout@v2 10 | - name: Setup MSBuild.exe 11 | uses: microsoft/setup-msbuild@v1.0.2 12 | - name: Build with MSBuild 13 | run: msbuild '.\RS_ASIO.sln' 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | 33 | # Visual Studio 2015/2017 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # Visual Studio 2017 auto generated files 39 | Generated\ Files/ 40 | 41 | # MSTest test Results 42 | [Tt]est[Rr]esult*/ 43 | [Bb]uild[Ll]og.* 44 | 45 | # NUnit 46 | *.VisualState.xml 47 | TestResult.xml 48 | nunit-*.xml 49 | 50 | # Build Results of an ATL Project 51 | [Dd]ebugPS/ 52 | [Rr]eleasePS/ 53 | dlldata.c 54 | 55 | # Benchmark Results 56 | BenchmarkDotNet.Artifacts/ 57 | 58 | # .NET Core 59 | project.lock.json 60 | project.fragment.lock.json 61 | artifacts/ 62 | 63 | # StyleCop 64 | StyleCopReport.xml 65 | 66 | # Files built by Visual Studio 67 | *_i.c 68 | *_p.c 69 | *_h.h 70 | *.ilk 71 | *.meta 72 | *.obj 73 | *.iobj 74 | *.pch 75 | *.pdb 76 | *.ipdb 77 | *.pgc 78 | *.pgd 79 | *.rsp 80 | *.sbr 81 | *.tlb 82 | *.tli 83 | *.tlh 84 | *.tmp 85 | *.tmp_proj 86 | *_wpftmp.csproj 87 | *.log 88 | *.vspscc 89 | *.vssscc 90 | .builds 91 | *.pidb 92 | *.svclog 93 | *.scc 94 | 95 | # Chutzpah Test files 96 | _Chutzpah* 97 | 98 | # Visual C++ cache files 99 | ipch/ 100 | *.aps 101 | *.ncb 102 | *.opendb 103 | *.opensdf 104 | *.sdf 105 | *.cachefile 106 | *.VC.db 107 | *.VC.VC.opendb 108 | 109 | # Visual Studio profiler 110 | *.psess 111 | *.vsp 112 | *.vspx 113 | *.sap 114 | 115 | # Visual Studio Trace Files 116 | *.e2e 117 | 118 | # TFS 2012 Local Workspace 119 | $tf/ 120 | 121 | # Guidance Automation Toolkit 122 | *.gpState 123 | 124 | # ReSharper is a .NET coding add-in 125 | _ReSharper*/ 126 | *.[Rr]e[Ss]harper 127 | *.DotSettings.user 128 | 129 | # JustCode is a .NET coding add-in 130 | .JustCode 131 | 132 | # TeamCity is a build add-in 133 | _TeamCity* 134 | 135 | # DotCover is a Code Coverage Tool 136 | *.dotCover 137 | 138 | # AxoCover is a Code Coverage Tool 139 | .axoCover/* 140 | !.axoCover/settings.json 141 | 142 | # Visual Studio code coverage results 143 | *.coverage 144 | *.coveragexml 145 | 146 | # NCrunch 147 | _NCrunch_* 148 | .*crunch*.local.xml 149 | nCrunchTemp_* 150 | 151 | # MightyMoose 152 | *.mm.* 153 | AutoTest.Net/ 154 | 155 | # Web workbench (sass) 156 | .sass-cache/ 157 | 158 | # Installshield output folder 159 | [Ee]xpress/ 160 | 161 | # DocProject is a documentation generator add-in 162 | DocProject/buildhelp/ 163 | DocProject/Help/*.HxT 164 | DocProject/Help/*.HxC 165 | DocProject/Help/*.hhc 166 | DocProject/Help/*.hhk 167 | DocProject/Help/*.hhp 168 | DocProject/Help/Html2 169 | DocProject/Help/html 170 | 171 | # Click-Once directory 172 | publish/ 173 | 174 | # Publish Web Output 175 | *.[Pp]ublish.xml 176 | *.azurePubxml 177 | # Note: Comment the next line if you want to checkin your web deploy settings, 178 | # but database connection strings (with potential passwords) will be unencrypted 179 | *.pubxml 180 | *.publishproj 181 | 182 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 183 | # checkin your Azure Web App publish settings, but sensitive information contained 184 | # in these scripts will be unencrypted 185 | PublishScripts/ 186 | 187 | # NuGet Packages 188 | *.nupkg 189 | # NuGet Symbol Packages 190 | *.snupkg 191 | # The packages folder can be ignored because of Package Restore 192 | **/[Pp]ackages/* 193 | # except build/, which is used as an MSBuild target. 194 | !**/[Pp]ackages/build/ 195 | # Uncomment if necessary however generally it will be regenerated when needed 196 | #!**/[Pp]ackages/repositories.config 197 | # NuGet v3's project.json files produces more ignorable files 198 | *.nuget.props 199 | *.nuget.targets 200 | 201 | # Microsoft Azure Build Output 202 | csx/ 203 | *.build.csdef 204 | 205 | # Microsoft Azure Emulator 206 | ecf/ 207 | rcf/ 208 | 209 | # Windows Store app package directories and files 210 | AppPackages/ 211 | BundleArtifacts/ 212 | Package.StoreAssociation.xml 213 | _pkginfo.txt 214 | *.appx 215 | *.appxbundle 216 | *.appxupload 217 | 218 | # Visual Studio cache files 219 | # files ending in .cache can be ignored 220 | *.[Cc]ache 221 | # but keep track of directories ending in .cache 222 | !?*.[Cc]ache/ 223 | 224 | # Others 225 | ClientBin/ 226 | ~$* 227 | *~ 228 | *.dbmdl 229 | *.dbproj.schemaview 230 | *.jfm 231 | *.pfx 232 | *.publishsettings 233 | orleans.codegen.cs 234 | 235 | # Including strong name files can present a security risk 236 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 237 | #*.snk 238 | 239 | # Since there are multiple workflows, uncomment next line to ignore bower_components 240 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 241 | #bower_components/ 242 | 243 | # RIA/Silverlight projects 244 | Generated_Code/ 245 | 246 | # Backup & report files from converting an old project file 247 | # to a newer Visual Studio version. Backup files are not needed, 248 | # because we have git ;-) 249 | _UpgradeReport_Files/ 250 | Backup*/ 251 | UpgradeLog*.XML 252 | UpgradeLog*.htm 253 | ServiceFabricBackup/ 254 | *.rptproj.bak 255 | 256 | # SQL Server files 257 | *.mdf 258 | *.ldf 259 | *.ndf 260 | 261 | # Business Intelligence projects 262 | *.rdl.data 263 | *.bim.layout 264 | *.bim_*.settings 265 | *.rptproj.rsuser 266 | *- [Bb]ackup.rdl 267 | *- [Bb]ackup ([0-9]).rdl 268 | *- [Bb]ackup ([0-9][0-9]).rdl 269 | 270 | # Microsoft Fakes 271 | FakesAssemblies/ 272 | 273 | # GhostDoc plugin setting file 274 | *.GhostDoc.xml 275 | 276 | # Node.js Tools for Visual Studio 277 | .ntvs_analysis.dat 278 | node_modules/ 279 | 280 | # Visual Studio 6 build log 281 | *.plg 282 | 283 | # Visual Studio 6 workspace options file 284 | *.opt 285 | 286 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 287 | *.vbw 288 | 289 | # Visual Studio LightSwitch build output 290 | **/*.HTMLClient/GeneratedArtifacts 291 | **/*.DesktopClient/GeneratedArtifacts 292 | **/*.DesktopClient/ModelManifest.xml 293 | **/*.Server/GeneratedArtifacts 294 | **/*.Server/ModelManifest.xml 295 | _Pvt_Extensions 296 | 297 | # Paket dependency manager 298 | .paket/paket.exe 299 | paket-files/ 300 | 301 | # FAKE - F# Make 302 | .fake/ 303 | 304 | # CodeRush personal settings 305 | .cr/personal 306 | 307 | # Python Tools for Visual Studio (PTVS) 308 | __pycache__/ 309 | *.pyc 310 | 311 | # Cake - Uncomment if you are using it 312 | # tools/** 313 | # !tools/packages.config 314 | 315 | # Tabs Studio 316 | *.tss 317 | 318 | # Telerik's JustMock configuration file 319 | *.jmconfig 320 | 321 | # BizTalk build output 322 | *.btp.cs 323 | *.btm.cs 324 | *.odx.cs 325 | *.xsd.cs 326 | 327 | # OpenCover UI analysis results 328 | OpenCover/ 329 | 330 | # Azure Stream Analytics local run output 331 | ASALocalRun/ 332 | 333 | # MSBuild Binary and Structured Log 334 | *.binlog 335 | 336 | # NVidia Nsight GPU debugger configuration file 337 | *.nvuser 338 | 339 | # MFractors (Xamarin productivity tool) working folder 340 | .mfractor/ 341 | 342 | # Local History for Visual Studio 343 | .localhistory/ 344 | 345 | # BeatPulse healthcheck temp database 346 | healthchecksdb 347 | 348 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 349 | MigrationBackup/ 350 | 351 | # Ionide (cross platform F# VS Code tools) working folder 352 | .ionide/ 353 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Micael Dias 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RS_ASIO.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.329 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "avrt", "avrt\avrt.vcxproj", "{397924BB-35EE-4CE8-BCDD-A79E12C1E180}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "RS_ASIO", "RS_ASIO\RS_ASIO.vcxproj", "{4339185F-E3EA-4FE0-BBD7-67D4FE3FBF3E}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|x86 = Debug|x86 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {397924BB-35EE-4CE8-BCDD-A79E12C1E180}.Debug|x86.ActiveCfg = Debug|Win32 17 | {397924BB-35EE-4CE8-BCDD-A79E12C1E180}.Debug|x86.Build.0 = Debug|Win32 18 | {397924BB-35EE-4CE8-BCDD-A79E12C1E180}.Release|x86.ActiveCfg = Release|Win32 19 | {397924BB-35EE-4CE8-BCDD-A79E12C1E180}.Release|x86.Build.0 = Release|Win32 20 | {4339185F-E3EA-4FE0-BBD7-67D4FE3FBF3E}.Debug|x86.ActiveCfg = Debug|Win32 21 | {4339185F-E3EA-4FE0-BBD7-67D4FE3FBF3E}.Debug|x86.Build.0 = Debug|Win32 22 | {4339185F-E3EA-4FE0-BBD7-67D4FE3FBF3E}.Release|x86.ActiveCfg = Release|Win32 23 | {4339185F-E3EA-4FE0-BBD7-67D4FE3FBF3E}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {51FD37F8-4C73-4548-99B2-78BE01CBBF32} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /RS_ASIO/AsioHelpers.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "AsioHelpers.h" 3 | #include "AsioSharedHost.h" 4 | 5 | static const TCHAR* ASIO_PATH = TEXT("software\\asio"); 6 | static const TCHAR* ASIO_DESC = TEXT("description"); 7 | static const TCHAR* COM_CLSID = TEXT("clsid"); 8 | static const TCHAR* INPROC_SERVER = TEXT("InprocServer32"); 9 | 10 | template 11 | static bool ReadRegistryStringA(std::string& out, HKEY hKey, const TCHAR* valueName) 12 | { 13 | char buffer[bufferSize]; 14 | DWORD dataType = 0; 15 | DWORD readSize = bufferSize; 16 | LSTATUS regStatus = RegQueryValueEx(hKey, valueName, nullptr, &dataType, (BYTE*)buffer, &readSize); 17 | if (regStatus != ERROR_SUCCESS || dataType != REG_SZ) 18 | return false; 19 | 20 | if (readSize == 0) 21 | { 22 | out.clear(); 23 | return false; 24 | } 25 | 26 | #ifdef UNICODE 27 | char converted[bufferSize]; 28 | 29 | if (WideCharToMultiByte(CP_ACP, 0, (wchar_t*)buffer, readSize / sizeof(wchar_t), converted, bufferSize, nullptr, nullptr) == 0) 30 | return false; 31 | 32 | out = converted; 33 | #else 34 | out = buffer; 35 | #endif 36 | 37 | return true; 38 | } 39 | 40 | template 41 | static bool ReadRegistryStringW(std::wstring& out, HKEY hKey, const TCHAR* valueName) 42 | { 43 | char buffer[bufferSize]; 44 | DWORD dataType = 0; 45 | DWORD readSize = bufferSize; 46 | LSTATUS regStatus = RegQueryValueEx(hKey, valueName, nullptr, &dataType, (BYTE*)buffer, &readSize); 47 | if (regStatus != ERROR_SUCCESS || dataType != REG_SZ) 48 | return false; 49 | 50 | if (readSize == 0) 51 | { 52 | out.clear(); 53 | return false; 54 | } 55 | 56 | #ifdef UNICODE 57 | out = (wchar_t*)buffer; 58 | #else 59 | wchar_t converted[bufferSize]; 60 | 61 | if (MultiByteToWideChar(CP_ACP, 0, buffer, readSize, converted, bufferSize) == 0) 62 | return false; 63 | 64 | out = converted; 65 | #endif 66 | 67 | return true; 68 | } 69 | 70 | static bool ReadRegistryClsid(CLSID& out, HKEY hKey, const TCHAR* valueName) 71 | { 72 | //RegQueryValueEx 73 | 74 | #ifdef UNICODE 75 | std::wstring str; 76 | bool res = ReadRegistryStringW<128>(str, hKey, valueName); 77 | #else 78 | std::string str; 79 | bool res = ReadRegistryStringA<128>(str, hKey, valueName); 80 | #endif 81 | if (!res) 82 | return false; 83 | 84 | HRESULT hr = CLSIDFromString(str.c_str(), &out); 85 | if (FAILED(hr)) 86 | return false; 87 | 88 | return true; 89 | } 90 | 91 | static bool GetRegistryAsioDriverPath(std::string& out, const CLSID& clsid) 92 | { 93 | LPOLESTR clsidStr = nullptr; 94 | if (FAILED(StringFromCLSID(clsid, &clsidStr))) 95 | { 96 | return false; 97 | } 98 | 99 | bool result = false; 100 | HKEY hKeyClsidRoot = nullptr; 101 | 102 | LSTATUS regStatus = RegOpenKey(HKEY_CLASSES_ROOT, COM_CLSID, &hKeyClsidRoot); 103 | if (regStatus == ERROR_SUCCESS) 104 | { 105 | HKEY hKeyClsidFound = nullptr; 106 | regStatus = RegOpenKeyEx(hKeyClsidRoot, clsidStr, 0, KEY_READ, &hKeyClsidFound); 107 | if (regStatus == ERROR_SUCCESS) 108 | { 109 | HKEY hKeyInprocServer = nullptr; 110 | regStatus = RegOpenKeyEx(hKeyClsidFound, INPROC_SERVER, 0, KEY_READ, &hKeyInprocServer); 111 | if (regStatus == ERROR_SUCCESS) 112 | { 113 | result = ReadRegistryStringA(out, hKeyInprocServer, nullptr); 114 | RegCloseKey(hKeyInprocServer); 115 | } 116 | 117 | RegCloseKey(hKeyClsidFound); 118 | } 119 | 120 | RegCloseKey(hKeyClsidRoot); 121 | } 122 | 123 | CoTaskMemFree(clsidStr); 124 | 125 | return result; 126 | } 127 | 128 | static bool GetRegistryDriverInfo(AsioHelpers::DriverInfo& outInfo, HKEY hKey, const TCHAR* keyName) 129 | { 130 | bool result = false; 131 | 132 | HKEY hksub; 133 | LSTATUS regStatus = RegOpenKeyEx(hKey, keyName, 0, KEY_READ, &hksub); 134 | if (regStatus == ERROR_SUCCESS) 135 | { 136 | AsioHelpers::DriverInfo tmpInfo; 137 | 138 | // set name 139 | #ifdef UNICODE 140 | tmpInfo.Name = ConvertWStrToStr(keyName); 141 | #else 142 | tmpInfo.Name = keyName; 143 | #endif 144 | 145 | // read CLSID 146 | result = ReadRegistryClsid(tmpInfo.Clsid, hksub, COM_CLSID); 147 | if (result) 148 | { 149 | // read description 150 | ReadRegistryStringA<128>(tmpInfo.Description, hksub, ASIO_DESC); 151 | 152 | // figure out DLL path 153 | result = GetRegistryAsioDriverPath(tmpInfo.DllPath, tmpInfo.Clsid); 154 | } 155 | 156 | RegCloseKey(hksub); 157 | 158 | if (result) 159 | { 160 | outInfo = std::move(tmpInfo); 161 | } 162 | } 163 | 164 | return result; 165 | } 166 | 167 | static std::optional GetWineAsioInfo() 168 | { 169 | static std::optional result; 170 | 171 | static bool isFirstCall = true; 172 | if (isFirstCall) 173 | { 174 | isFirstCall = false; 175 | 176 | std::array possibleDllNames = { 177 | "wineasio32.dll", 178 | "wineasio.dll" 179 | }; 180 | 181 | bool keepLooking = true; 182 | 183 | for (const char* dllToFind : possibleDllNames) 184 | { 185 | if (!keepLooking) 186 | { 187 | break; 188 | } 189 | 190 | rslog::info_ts() << __FUNCTION__ << " - Looking for \"" << dllToFind << "\"..."; 191 | 192 | HMODULE hWineAsio = LoadLibraryExA(dllToFind, nullptr, DONT_RESOLVE_DLL_REFERENCES); 193 | if (hWineAsio) 194 | { 195 | char path[512] = {}; 196 | if (GetModuleFileNameA(hWineAsio, path, sizeof(path) - 1)) 197 | { 198 | rslog::info << " Loaded and found at \"" << path << "\"." << std::endl; 199 | 200 | AsioHelpers::DriverInfo info; 201 | info.Clsid = { 0x48d0c522, 0xbfcc, 0x45cc, { 0x8b, 0x84, 0x17, 0xf2, 0x5f, 0x33, 0xe6, 0xe8 } }; 202 | info.Description = "Auto-detected wineasio dll"; 203 | info.DllPath = path; 204 | info.Name = "wineasio-rsasio"; 205 | 206 | rslog::info_ts() << " name: " << info.Name.c_str() << std::endl; 207 | result = info; 208 | keepLooking = false; 209 | } 210 | else 211 | { 212 | rslog::info_ts() << " Loaded but could not get path." << std::endl; 213 | } 214 | FreeLibrary(hWineAsio); 215 | } 216 | else 217 | { 218 | rslog::info << " Not found." << std::endl; 219 | } 220 | } 221 | } 222 | 223 | return result; 224 | } 225 | 226 | std::vector AsioHelpers::FindDrivers() 227 | { 228 | static std::optional WineAsioInfo = GetWineAsioInfo(); 229 | 230 | rslog::info_ts() << __FUNCTION__ << std::endl; 231 | 232 | std::vector result; 233 | 234 | HKEY hkEnum = 0; 235 | LSTATUS regStatus = RegOpenKey(HKEY_LOCAL_MACHINE, ASIO_PATH, &hkEnum); 236 | if (regStatus == ERROR_SUCCESS) 237 | { 238 | TCHAR keyName[MAX_PATH]; 239 | DWORD keyIndex = 0; 240 | 241 | while (RegEnumKey(hkEnum, keyIndex++, keyName, MAX_PATH) == ERROR_SUCCESS) 242 | { 243 | AsioHelpers::DriverInfo info; 244 | if (GetRegistryDriverInfo(info, hkEnum, keyName)) 245 | { 246 | rslog::info_ts() << " " << info.Name.c_str() << std::endl; 247 | result.push_back(std::move(info)); 248 | } 249 | } 250 | 251 | RegCloseKey(hkEnum); 252 | } 253 | 254 | if (WineAsioInfo.has_value()) 255 | { 256 | rslog::info_ts() << " " << (*WineAsioInfo).Name.c_str() << std::endl; 257 | result.push_back(*WineAsioInfo); 258 | } 259 | 260 | return result; 261 | } 262 | 263 | AsioSharedHost* AsioHelpers::CreateAsioHost(const DriverInfo& driverInfo) 264 | { 265 | AsioSharedHost* host = new AsioSharedHost(driverInfo.Clsid, driverInfo.DllPath); 266 | if (!host->IsValid()) 267 | { 268 | host->Release(); 269 | host = nullptr; 270 | } 271 | 272 | return host; 273 | } -------------------------------------------------------------------------------- /RS_ASIO/AsioHelpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | class AsioSharedHost; 4 | 5 | namespace AsioHelpers 6 | { 7 | struct DriverInfo 8 | { 9 | CLSID Clsid = {}; 10 | std::string Name; 11 | std::string Description; 12 | std::string DllPath; 13 | }; 14 | 15 | std::vector FindDrivers(); 16 | AsioSharedHost* CreateAsioHost(const DriverInfo& driverInfo); 17 | } 18 | -------------------------------------------------------------------------------- /RS_ASIO/AsioSharedHost.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class IAsioBufferSwitchListener 6 | { 7 | public: 8 | virtual void OnAsioBufferSwitch(unsigned buffIdx) = 0; 9 | }; 10 | 11 | class AsioSharedHost : public ComBaseUnknown 12 | { 13 | public: 14 | AsioSharedHost(const CLSID& clsid, const std::string& asioDllPath); 15 | AsioSharedHost(const AsioSharedHost&) = delete; 16 | AsioSharedHost(AsioSharedHost&&) = delete; 17 | virtual ~AsioSharedHost(); 18 | 19 | bool IsValid() const; 20 | IAsioDriver* GetDriver() { return m_Driver; } 21 | const bool GetIsAsio4All() const { return m_AsioDllIsAsio4all; } 22 | 23 | const std::string GetAsioDllPath() const { return m_AsioDllPath; } 24 | 25 | ASIOError SetSamplerate(const DWORD rate); 26 | ASIOError Setup(const WAVEFORMATEX& format, const DWORD bufferDurationFrames); 27 | void Reset(); 28 | 29 | ASIOError Start(); 30 | 31 | void Stop(); 32 | bool GetPreferredBufferSize(DWORD& outBufferSizeFrames) const; 33 | bool ClampBufferSizeToLimits(DWORD& inOutBufferSizeFrames) const; 34 | 35 | void AddBufferSwitchListener(IAsioBufferSwitchListener* listener); 36 | void RemoveBufferSwitchListener(IAsioBufferSwitchListener* listener); 37 | 38 | bool IsWaveFormatSupported(const WAVEFORMATEX& format, bool output, unsigned firstAsioChannel, unsigned numAsioChannels) const; 39 | bool CheckSampleTypeAcrossChannels(ASIOSampleType& outType, bool output, unsigned firstAsioChannel, unsigned numAsioChannels) const; 40 | 41 | UINT32 GetBufferNumFrames() const; 42 | bool GetLatencyTime(REFERENCE_TIME& in, REFERENCE_TIME& out); 43 | 44 | ASIOBufferInfo* GetOutputBuffer(unsigned channel); 45 | ASIOBufferInfo* GetInputBuffer(unsigned channel); 46 | const ASIOChannelInfo* GetOutputChannelInfo(unsigned channel) const; 47 | const ASIOChannelInfo* GetInputChannelInfo(unsigned channel) const; 48 | unsigned GetNumInputChannels() const { return m_AsioInChannelInfo.size(); } 49 | unsigned GetNumOutputChannels() const { return m_AsioOutChannelInfo.size(); } 50 | 51 | void ResetDebugLogAsioBufferSwitches(); 52 | 53 | private: 54 | void DisplayCurrentError() const; 55 | 56 | void __cdecl AsioCalback_bufferSwitch(long doubleBufferIndex, ASIOBool directProcess); 57 | void __cdecl AsioCalback_sampleRateDidChange(ASIOSampleRate sRate); 58 | long __cdecl AsioCalback_asioMessage(long selector, long value, void* message, double* opt); 59 | ASIOTime* __cdecl AsioCalback_bufferSwitchTimeInfo(ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess); 60 | 61 | HMODULE m_Module = nullptr; 62 | IAsioDriver* m_Driver = nullptr; 63 | ULONG m_StartCount = 0; 64 | bool m_PostOutputReady = false; 65 | bool m_IsSetup = false; 66 | WAVEFORMATEXTENSIBLE m_CurrentWaveFormat; 67 | 68 | TrampolineToMethod m_Trampoline_bufferSwitch; 69 | TrampolineToMethod m_Trampoline_sampleRateDidChange; 70 | TrampolineToMethod m_Trampoline_asioMessage; 71 | TrampolineToMethod m_Trampoline_bufferSwitchTimeInfo; 72 | ASIOCallbacks m_AsioCallbacks; 73 | 74 | UINT32 m_NumBufferFrames = 0; 75 | std::vector m_AsioBuffers; 76 | std::vector m_AsioInChannelInfo; 77 | std::vector m_AsioOutChannelInfo; 78 | std::set m_AsioBufferListeners; 79 | std::mutex m_AsioMutex; 80 | std::optional m_SampleRateRestore; 81 | 82 | std::unique_ptr m_AsioEventsThread; 83 | 84 | unsigned m_dbgNumBufferSwitches; 85 | std::string m_DriverName; 86 | std::string m_AsioDllPath; 87 | bool m_AsioDllIsAsio4all = false; 88 | }; 89 | -------------------------------------------------------------------------------- /RS_ASIO/AudioProcessing.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace AudioProcessing 4 | { 5 | bool CopyConvertFormat(const BYTE* inData, ASIOSampleType inSampleType, const WORD inStride, DWORD numFrames, BYTE* outData, ASIOSampleType outSampleType, const WORD outStride); 6 | bool DoSoftwareVolumeDsp(BYTE* data, ASIOSampleType inSampleType, DWORD numSamples, float fVolumeScalar); 7 | } -------------------------------------------------------------------------------- /RS_ASIO/ComBaseUnknown.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template 4 | class ComBaseUnknown : public TBase 5 | { 6 | static_assert(std::is_base_of::value, "TBase is not a subclass of IUnknown"); 7 | public: 8 | virtual ~ComBaseUnknown() = default; 9 | 10 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) 11 | { 12 | if (!ppvObject) 13 | return E_POINTER; 14 | 15 | if (riid == __uuidof(IUnknown)) 16 | { 17 | *ppvObject = this; 18 | this->AddRef(); 19 | return S_OK; 20 | } 21 | 22 | rslog::error_ts() << __FUNCTION__ << " - interface not found; riid: " << riid << std::endl; 23 | 24 | return E_NOINTERFACE; 25 | } 26 | 27 | virtual ULONG STDMETHODCALLTYPE AddRef() 28 | { 29 | return InterlockedIncrement(&m_RefCount); 30 | } 31 | 32 | virtual ULONG STDMETHODCALLTYPE Release() 33 | { 34 | ULONG ret = InterlockedDecrement(&m_RefCount); 35 | if (ret == 0) 36 | { 37 | delete this; 38 | } 39 | return ret; 40 | } 41 | 42 | private: 43 | ULONG m_RefCount = 1; 44 | }; 45 | -------------------------------------------------------------------------------- /RS_ASIO/Configurator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void SetupDeviceEnumerator(RSAggregatorDeviceEnum& deviceCollection); -------------------------------------------------------------------------------- /RS_ASIO/DebugDeviceEnum.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "DebugDeviceEnum.h" 3 | 4 | #include "RSAggregatorDeviceEnum.h" 5 | #include "DebugWrapperDevice.h" 6 | 7 | DebugDeviceEnum::DebugDeviceEnum(IMMDeviceEnumerator* enumerator) 8 | : m_RealEnumerator(enumerator) 9 | { 10 | if (m_RealEnumerator) 11 | { 12 | m_RealEnumerator->AddRef(); 13 | m_DeviceListNeedsUpdate = true; 14 | } 15 | } 16 | 17 | DebugDeviceEnum::~DebugDeviceEnum() 18 | { 19 | if (m_RealEnumerator) 20 | { 21 | m_RealEnumerator->Release(); 22 | m_RealEnumerator = nullptr; 23 | } 24 | } 25 | 26 | HRESULT STDMETHODCALLTYPE DebugDeviceEnum::QueryInterface(REFIID riid, void **ppvObject) 27 | { 28 | rslog::info_ts() << __FUNCTION__ << std::endl; 29 | 30 | return m_RealEnumerator->QueryInterface(riid, ppvObject); 31 | } 32 | 33 | HRESULT STDMETHODCALLTYPE DebugDeviceEnum::EnumAudioEndpoints(EDataFlow dataFlow, DWORD dwStateMask, IMMDeviceCollection **ppDevices) 34 | { 35 | rslog::info_ts() << __FUNCTION__ " - dataFlow: " << Dataflow2String(dataFlow) << " - dwStateMask: " << dwStateMask << std::endl; 36 | 37 | HRESULT hr = RSBaseDeviceEnum::EnumAudioEndpoints(dataFlow, dwStateMask, ppDevices); 38 | rslog::info_ts() << " hr: " << HResultToStr(hr) << std::endl; 39 | if (ppDevices) 40 | { 41 | rslog::info_ts() << " *ppDevices: " << *ppDevices << std::endl; 42 | } 43 | 44 | return hr; 45 | } 46 | 47 | HRESULT STDMETHODCALLTYPE DebugDeviceEnum::GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, IMMDevice **ppEndpoint) 48 | { 49 | rslog::info_ts() << __FUNCTION__ " - dataFlow: " << Dataflow2String(dataFlow) << " - role: " << Role2String(role) << std::endl; 50 | 51 | HRESULT hr = RSBaseDeviceEnum::GetDefaultAudioEndpoint(dataFlow, role, ppEndpoint); 52 | rslog::info_ts() << " hr: " << HResultToStr(hr) << std::endl; 53 | if (ppEndpoint) 54 | { 55 | rslog::info_ts() << " *ppEndpoint: " << *ppEndpoint << std::endl; 56 | } 57 | 58 | return hr; 59 | } 60 | 61 | HRESULT STDMETHODCALLTYPE DebugDeviceEnum::GetDevice(LPCWSTR pwstrId, IMMDevice **ppDevice) 62 | { 63 | HRESULT hr = RSBaseDeviceEnum::GetDevice(pwstrId, ppDevice); 64 | rslog::info_ts() << " hr: " << HResultToStr(hr) << std::endl; 65 | if (ppDevice) 66 | { 67 | rslog::info_ts() << " *ppEndpoint: " << *ppDevice << std::endl; 68 | } 69 | 70 | return hr; 71 | } 72 | 73 | HRESULT STDMETHODCALLTYPE DebugDeviceEnum::RegisterEndpointNotificationCallback(IMMNotificationClient *pClient) 74 | { 75 | rslog::info_ts() << __FUNCTION__ << std::endl; 76 | 77 | return m_RealEnumerator->RegisterEndpointNotificationCallback(pClient); 78 | } 79 | 80 | HRESULT STDMETHODCALLTYPE DebugDeviceEnum::UnregisterEndpointNotificationCallback(IMMNotificationClient *pClient) 81 | { 82 | rslog::info_ts() << __FUNCTION__ << std::endl; 83 | 84 | return m_RealEnumerator->UnregisterEndpointNotificationCallback(pClient); 85 | } 86 | 87 | void DebugDeviceEnum::UpdateAvailableDevices() 88 | { 89 | RSDeviceCollection aggregatedRenderCollection; 90 | RSDeviceCollection aggregatedCaptureCollection; 91 | 92 | // release defaults 93 | for (IMMDevice*& dev : m_DefaultRenderDevices) 94 | { 95 | if (dev) 96 | { 97 | dev->Release(); 98 | dev = nullptr; 99 | } 100 | } 101 | for (IMMDevice*& dev : m_DefaultCaptureDevices) 102 | { 103 | if (dev) 104 | { 105 | dev->Release(); 106 | dev = nullptr; 107 | } 108 | } 109 | 110 | auto fnUpdateCollection = [](IMMDeviceEnumerator* enumerator, EDataFlow dataFlow, RSDeviceCollection& out, std::array& outDefaults) 111 | { 112 | IMMDeviceCollection* collection = nullptr; 113 | if (SUCCEEDED(enumerator->EnumAudioEndpoints(dataFlow, DEVICE_STATEMASK_ALL, &collection))) 114 | { 115 | UINT num = 0; 116 | collection->GetCount(&num); 117 | for (UINT i = 0; i < num; ++i) 118 | { 119 | IMMDevice* dev = nullptr; 120 | if (SUCCEEDED(collection->Item(i, &dev)) && dev) 121 | { 122 | IMMDevice* debugDev = new DebugWrapperDevice(*dev); 123 | dev->Release(); 124 | out.AddDevice(debugDev); 125 | debugDev->Release(); 126 | } 127 | } 128 | 129 | collection->Release(); 130 | } 131 | 132 | for (int i = eConsole; i < ERole_enum_count; ++i) 133 | { 134 | const ERole role = (ERole)i; 135 | if (!outDefaults[role]) 136 | { 137 | IMMDevice* defaultDevice = nullptr; 138 | 139 | if (SUCCEEDED(enumerator->GetDefaultAudioEndpoint(dataFlow, role, &defaultDevice))) 140 | { 141 | outDefaults[role] = new DebugWrapperDevice(*defaultDevice); 142 | defaultDevice->Release(); 143 | } 144 | } 145 | } 146 | }; 147 | 148 | if (m_RealEnumerator) 149 | { 150 | fnUpdateCollection(m_RealEnumerator, eRender, aggregatedRenderCollection, m_DefaultRenderDevices); 151 | fnUpdateCollection(m_RealEnumerator, eCapture, aggregatedCaptureCollection, m_DefaultCaptureDevices); 152 | } 153 | 154 | m_RenderDevices.UpdateDevicesFromCollection(aggregatedRenderCollection, true); 155 | m_CaptureDevices.UpdateDevicesFromCollection(aggregatedCaptureCollection, true); 156 | 157 | rslog::info_ts() << __FUNCTION__ << " - " << m_RenderDevices.size() << " render devices, " << m_CaptureDevices.size() << " capture devices" << std::endl; 158 | 159 | m_DeviceListNeedsUpdate = false; 160 | } 161 | -------------------------------------------------------------------------------- /RS_ASIO/DebugDeviceEnum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RSBaseDeviceEnum.h" 4 | 5 | class DebugDeviceEnum : public RSBaseDeviceEnum 6 | { 7 | public: 8 | DebugDeviceEnum(IMMDeviceEnumerator* enumerator); 9 | DebugDeviceEnum(const DebugDeviceEnum&) = delete; 10 | DebugDeviceEnum(DebugDeviceEnum&&) = delete; 11 | virtual ~DebugDeviceEnum(); 12 | 13 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 14 | 15 | virtual HRESULT STDMETHODCALLTYPE EnumAudioEndpoints(EDataFlow dataFlow, DWORD dwStateMask, IMMDeviceCollection **ppDevices) override; 16 | virtual HRESULT STDMETHODCALLTYPE GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, IMMDevice **ppEndpoint) override; 17 | virtual HRESULT STDMETHODCALLTYPE GetDevice(LPCWSTR pwstrId, IMMDevice **ppDevice) override; 18 | virtual HRESULT STDMETHODCALLTYPE RegisterEndpointNotificationCallback(IMMNotificationClient *pClient) override; 19 | virtual HRESULT STDMETHODCALLTYPE UnregisterEndpointNotificationCallback(IMMNotificationClient *pClient) override; 20 | 21 | private: 22 | virtual void UpdateAvailableDevices() override; 23 | 24 | IMMDeviceEnumerator* m_RealEnumerator = nullptr; 25 | }; 26 | -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperAudioClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | template 6 | class DebugWrapperAudioClient : public ComBaseUnknown 7 | { 8 | public: 9 | DebugWrapperAudioClient(TBase& realAudioClient, const std::wstring deviceId); 10 | DebugWrapperAudioClient(const DebugWrapperAudioClient&) = delete; 11 | DebugWrapperAudioClient(DebugWrapperAudioClient&&) = delete; 12 | virtual ~DebugWrapperAudioClient(); 13 | 14 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); 15 | 16 | virtual HRESULT STDMETHODCALLTYPE Initialize(AUDCLNT_SHAREMODE ShareMode, DWORD StreamFlags, REFERENCE_TIME hnsBufferDuration, REFERENCE_TIME hnsPeriodicity, const WAVEFORMATEX *pFormat, LPCGUID AudioSessionGuid) override; 17 | virtual HRESULT STDMETHODCALLTYPE GetBufferSize(UINT32 *pNumBufferFrames) override; 18 | virtual HRESULT STDMETHODCALLTYPE GetStreamLatency(REFERENCE_TIME *phnsLatency) override; 19 | virtual HRESULT STDMETHODCALLTYPE GetCurrentPadding(UINT32 *pNumPaddingFrames) override; 20 | virtual HRESULT STDMETHODCALLTYPE IsFormatSupported(AUDCLNT_SHAREMODE ShareMode, const WAVEFORMATEX *pFormat, WAVEFORMATEX **ppClosestMatch) override; 21 | virtual HRESULT STDMETHODCALLTYPE GetMixFormat(WAVEFORMATEX **ppDeviceFormat) override; 22 | virtual HRESULT STDMETHODCALLTYPE GetDevicePeriod(REFERENCE_TIME *phnsDefaultDevicePeriod, REFERENCE_TIME *phnsMinimumDevicePeriod) override; 23 | virtual HRESULT STDMETHODCALLTYPE Start() override; 24 | virtual HRESULT STDMETHODCALLTYPE Stop() override; 25 | virtual HRESULT STDMETHODCALLTYPE Reset() override; 26 | virtual HRESULT STDMETHODCALLTYPE SetEventHandle(HANDLE eventHandle) override; 27 | virtual HRESULT STDMETHODCALLTYPE GetService(REFIID riid, void **ppv) override; 28 | 29 | const std::wstring GetDeviceId() const; 30 | 31 | protected: 32 | TBase& m_RealAudioClient; 33 | std::wstring m_DeviceId; 34 | 35 | IAudioCaptureClient* m_CaptureClient = nullptr; 36 | IAudioRenderClient* m_RenderClient = nullptr; 37 | }; 38 | 39 | template 40 | class DebugWrapperAudioClient2 : public DebugWrapperAudioClient 41 | { 42 | public: 43 | DebugWrapperAudioClient2(TBase& realAudioClient, const std::wstring deviceId); 44 | DebugWrapperAudioClient2(const DebugWrapperAudioClient2&) = delete; 45 | DebugWrapperAudioClient2(DebugWrapperAudioClient2&&) = delete; 46 | virtual ~DebugWrapperAudioClient2(); 47 | 48 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); 49 | 50 | virtual HRESULT STDMETHODCALLTYPE IsOffloadCapable(AUDIO_STREAM_CATEGORY Category, BOOL *pbOffloadCapable) override; 51 | virtual HRESULT STDMETHODCALLTYPE SetClientProperties(const AudioClientProperties *pProperties) override; 52 | virtual HRESULT STDMETHODCALLTYPE GetBufferSizeLimits(const WAVEFORMATEX *pFormat, BOOL bEventDriven, REFERENCE_TIME *phnsMinBufferDuration, REFERENCE_TIME *phnsMaxBufferDuration) override; 53 | }; 54 | 55 | class DebugWrapperAudioClient3 : public DebugWrapperAudioClient2 56 | { 57 | public: 58 | DebugWrapperAudioClient3(IAudioClient3& realAudioClient, const std::wstring deviceId); 59 | DebugWrapperAudioClient3(const DebugWrapperAudioClient3&) = delete; 60 | DebugWrapperAudioClient3(DebugWrapperAudioClient3&&) = delete; 61 | virtual ~DebugWrapperAudioClient3(); 62 | 63 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); 64 | 65 | virtual HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod(const WAVEFORMATEX *pFormat, UINT32 *pDefaultPeriodInFrames, UINT32 *pFundamentalPeriodInFrames, UINT32 *pMinPeriodInFrames, UINT32 *pMaxPeriodInFrames) override; 66 | virtual HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod(WAVEFORMATEX **ppFormat, UINT32 *pCurrentPeriodInFrames) override; 67 | virtual HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream(DWORD StreamFlags, UINT32 PeriodInFrames, const WAVEFORMATEX *pFormat, LPCGUID AudioSessionGuid) override; 68 | }; -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperAudioEndpointVolume.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "DebugWrapperAudioEndpointVolume.h" 3 | 4 | #define DEBUG_PRINT_HR(hr) if(FAILED(hr)) rslog::info_ts() << " hr: " << HResultToStr(hr) << std::endl 5 | 6 | DebugWrapperAudioEndpointVolume::DebugWrapperAudioEndpointVolume(IAudioEndpointVolume& realAudioEndpointVolume, const std::wstring& deviceId) 7 | : m_RealAudioEndpointVolume(realAudioEndpointVolume) 8 | , m_DeviceId(deviceId) 9 | { 10 | m_RealAudioEndpointVolume.AddRef(); 11 | } 12 | 13 | DebugWrapperAudioEndpointVolume::~DebugWrapperAudioEndpointVolume() 14 | { 15 | m_RealAudioEndpointVolume.Release(); 16 | } 17 | 18 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::RegisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) 19 | { 20 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 21 | 22 | HRESULT hr = m_RealAudioEndpointVolume.RegisterControlChangeNotify(pNotify); 23 | DEBUG_PRINT_HR(hr); 24 | 25 | return hr; 26 | } 27 | 28 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::UnregisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) 29 | { 30 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 31 | 32 | HRESULT hr = m_RealAudioEndpointVolume.UnregisterControlChangeNotify(pNotify); 33 | DEBUG_PRINT_HR(hr); 34 | 35 | return hr; 36 | } 37 | 38 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::GetChannelCount(UINT *pnChannelCount) 39 | { 40 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 41 | 42 | HRESULT hr = m_RealAudioEndpointVolume.GetChannelCount(pnChannelCount); 43 | DEBUG_PRINT_HR(hr); 44 | 45 | return hr; 46 | } 47 | 48 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::SetMasterVolumeLevel(float fLevelDB, LPCGUID pguidEventContext) 49 | { 50 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ "fLevelDB: " << fLevelDB << std::endl; 51 | 52 | HRESULT hr = m_RealAudioEndpointVolume.SetMasterVolumeLevel(fLevelDB, pguidEventContext); 53 | DEBUG_PRINT_HR(hr); 54 | 55 | return hr; 56 | } 57 | 58 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::SetMasterVolumeLevelScalar(float fLevel, LPCGUID pguidEventContext) 59 | { 60 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " fLevel: " << fLevel << std::endl; 61 | 62 | HRESULT hr = m_RealAudioEndpointVolume.SetMasterVolumeLevelScalar(fLevel, pguidEventContext); 63 | DEBUG_PRINT_HR(hr); 64 | 65 | return hr; 66 | } 67 | 68 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::GetMasterVolumeLevel(float *pfLevelDB) 69 | { 70 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 71 | 72 | HRESULT hr = m_RealAudioEndpointVolume.GetMasterVolumeLevel(pfLevelDB); 73 | DEBUG_PRINT_HR(hr); 74 | 75 | return hr; 76 | } 77 | 78 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::GetMasterVolumeLevelScalar(float *pfLevel) 79 | { 80 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 81 | 82 | HRESULT hr = m_RealAudioEndpointVolume.GetMasterVolumeLevelScalar(pfLevel); 83 | DEBUG_PRINT_HR(hr); 84 | 85 | return hr; 86 | } 87 | 88 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::SetChannelVolumeLevel(UINT nChannel, float fLevelDB, LPCGUID pguidEventContext) 89 | { 90 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " nChannel: " << nChannel << " fLevelDB:" << fLevelDB << std::endl; 91 | 92 | HRESULT hr = m_RealAudioEndpointVolume.SetChannelVolumeLevel(nChannel, fLevelDB, pguidEventContext); 93 | DEBUG_PRINT_HR(hr); 94 | 95 | return hr; 96 | } 97 | 98 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::SetChannelVolumeLevelScalar(UINT nChannel, float fLevel, LPCGUID pguidEventContext) 99 | { 100 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " nChannel: " << nChannel << " fLevel:" << fLevel << std::endl; 101 | 102 | HRESULT hr = m_RealAudioEndpointVolume.SetChannelVolumeLevelScalar(nChannel, fLevel, pguidEventContext); 103 | DEBUG_PRINT_HR(hr); 104 | 105 | return hr; 106 | } 107 | 108 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::GetChannelVolumeLevel(UINT nChannel, float *pfLevelDB) 109 | { 110 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 111 | 112 | HRESULT hr = m_RealAudioEndpointVolume.GetChannelVolumeLevel(nChannel, pfLevelDB); 113 | DEBUG_PRINT_HR(hr); 114 | 115 | return hr; 116 | } 117 | 118 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::GetChannelVolumeLevelScalar(UINT nChannel, float *pfLevel) 119 | { 120 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 121 | 122 | HRESULT hr = m_RealAudioEndpointVolume.GetChannelVolumeLevelScalar(nChannel, pfLevel); 123 | DEBUG_PRINT_HR(hr); 124 | 125 | return hr; 126 | } 127 | 128 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::SetMute(BOOL bMute, LPCGUID pguidEventContext) 129 | { 130 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " bMute: " << bMute << std::endl; 131 | 132 | HRESULT hr = m_RealAudioEndpointVolume.SetMute(bMute, pguidEventContext); 133 | DEBUG_PRINT_HR(hr); 134 | 135 | return hr; 136 | } 137 | 138 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::GetMute(BOOL *pbMute) 139 | { 140 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 141 | 142 | HRESULT hr = m_RealAudioEndpointVolume.GetMute(pbMute); 143 | DEBUG_PRINT_HR(hr); 144 | 145 | return hr; 146 | } 147 | 148 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::GetVolumeStepInfo(UINT *pnStep, UINT *pnStepCount) 149 | { 150 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 151 | 152 | HRESULT hr = m_RealAudioEndpointVolume.GetVolumeStepInfo(pnStep, pnStepCount); 153 | DEBUG_PRINT_HR(hr); 154 | 155 | return hr; 156 | } 157 | 158 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::VolumeStepUp(LPCGUID pguidEventContext) 159 | { 160 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 161 | 162 | HRESULT hr = m_RealAudioEndpointVolume.VolumeStepUp(pguidEventContext); 163 | DEBUG_PRINT_HR(hr); 164 | 165 | return hr; 166 | } 167 | 168 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::VolumeStepDown(LPCGUID pguidEventContext) 169 | { 170 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 171 | 172 | HRESULT hr = m_RealAudioEndpointVolume.VolumeStepDown(pguidEventContext); 173 | DEBUG_PRINT_HR(hr); 174 | 175 | return hr; 176 | } 177 | 178 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::QueryHardwareSupport(DWORD *pdwHardwareSupportMask) 179 | { 180 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 181 | 182 | HRESULT hr = m_RealAudioEndpointVolume.QueryHardwareSupport(pdwHardwareSupportMask); 183 | DEBUG_PRINT_HR(hr); 184 | 185 | return hr; 186 | } 187 | 188 | HRESULT STDMETHODCALLTYPE DebugWrapperAudioEndpointVolume::GetVolumeRange(float *pflVolumeMindB, float *pflVolumeMaxdB, float *pflVolumeIncrementdB) 189 | { 190 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 191 | 192 | HRESULT hr = m_RealAudioEndpointVolume.GetVolumeRange(pflVolumeMindB, pflVolumeMaxdB, pflVolumeIncrementdB); 193 | DEBUG_PRINT_HR(hr); 194 | 195 | return hr; 196 | } 197 | -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperAudioEndpointVolume.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class DebugWrapperAudioEndpointVolume : public ComBaseUnknown 6 | { 7 | public: 8 | DebugWrapperAudioEndpointVolume(IAudioEndpointVolume& realAudioEndpointVolume, const std::wstring& deviceId); 9 | DebugWrapperAudioEndpointVolume(const DebugWrapperAudioEndpointVolume&) = delete; 10 | DebugWrapperAudioEndpointVolume(DebugWrapperAudioEndpointVolume&&) = delete; 11 | virtual ~DebugWrapperAudioEndpointVolume(); 12 | 13 | virtual HRESULT STDMETHODCALLTYPE RegisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override; 14 | virtual HRESULT STDMETHODCALLTYPE UnregisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override; 15 | virtual HRESULT STDMETHODCALLTYPE GetChannelCount(UINT *pnChannelCount) override; 16 | virtual HRESULT STDMETHODCALLTYPE SetMasterVolumeLevel(float fLevelDB, LPCGUID pguidEventContext) override; 17 | virtual HRESULT STDMETHODCALLTYPE SetMasterVolumeLevelScalar(float fLevel, LPCGUID pguidEventContext) override; 18 | virtual HRESULT STDMETHODCALLTYPE GetMasterVolumeLevel(float *pfLevelDB) override; 19 | virtual HRESULT STDMETHODCALLTYPE GetMasterVolumeLevelScalar(float *pfLevel) override; 20 | virtual HRESULT STDMETHODCALLTYPE SetChannelVolumeLevel(UINT nChannel, float fLevelDB, LPCGUID pguidEventContext) override; 21 | virtual HRESULT STDMETHODCALLTYPE SetChannelVolumeLevelScalar(UINT nChannel, float fLevel, LPCGUID pguidEventContext) override; 22 | virtual HRESULT STDMETHODCALLTYPE GetChannelVolumeLevel(UINT nChannel, float *pfLevelDB) override; 23 | virtual HRESULT STDMETHODCALLTYPE GetChannelVolumeLevelScalar(UINT nChannel, float *pfLevel) override; 24 | virtual HRESULT STDMETHODCALLTYPE SetMute(BOOL bMute, LPCGUID pguidEventContext) override; 25 | virtual HRESULT STDMETHODCALLTYPE GetMute(BOOL *pbMute) override; 26 | virtual HRESULT STDMETHODCALLTYPE GetVolumeStepInfo(UINT *pnStep, UINT *pnStepCount) override; 27 | virtual HRESULT STDMETHODCALLTYPE VolumeStepUp(LPCGUID pguidEventContext) override; 28 | virtual HRESULT STDMETHODCALLTYPE VolumeStepDown(LPCGUID pguidEventContext) override; 29 | virtual HRESULT STDMETHODCALLTYPE QueryHardwareSupport(DWORD *pdwHardwareSupportMask) override; 30 | virtual HRESULT STDMETHODCALLTYPE GetVolumeRange(float *pflVolumeMindB, float *pflVolumeMaxdB, float *pflVolumeIncrementdB) override; 31 | 32 | private: 33 | IAudioEndpointVolume& m_RealAudioEndpointVolume; 34 | std::wstring m_DeviceId; 35 | }; -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperCaptureClient.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "DebugWrapperCaptureClient.h" 3 | 4 | #define DEBUG_PRINT_HR(hr) if(FAILED(hr)) rslog::info_ts() << " hr: " << HResultToStr(hr) << std::endl 5 | 6 | DebugWrapperCaptureClient::DebugWrapperCaptureClient(IAudioCaptureClient& realClient, const std::wstring& deviceId) 7 | : m_RealClient(realClient) 8 | , m_DeviceId(deviceId) 9 | { 10 | m_RealClient.AddRef(); 11 | } 12 | 13 | DebugWrapperCaptureClient::~DebugWrapperCaptureClient() 14 | { 15 | m_RealClient.Release(); 16 | } 17 | 18 | HRESULT STDMETHODCALLTYPE DebugWrapperCaptureClient::GetBuffer(BYTE **ppData, UINT32 *pNumFramesToRead, DWORD *pdwFlags, UINT64 *pu64DevicePosition, UINT64 *pu64QPCPosition) 19 | { 20 | HRESULT hr = E_FAIL; 21 | 22 | if (m_GetCount < 3) 23 | { 24 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 25 | 26 | hr = m_RealClient.GetBuffer(ppData, pNumFramesToRead, pdwFlags, pu64DevicePosition, pu64QPCPosition); 27 | } 28 | else 29 | { 30 | hr = m_RealClient.GetBuffer(ppData, pNumFramesToRead, pdwFlags, pu64DevicePosition, pu64QPCPosition); 31 | if (FAILED(hr)) 32 | { 33 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 34 | } 35 | } 36 | 37 | DEBUG_PRINT_HR(hr); 38 | return hr; 39 | } 40 | 41 | HRESULT STDMETHODCALLTYPE DebugWrapperCaptureClient::ReleaseBuffer(UINT32 NumFramesRead) 42 | { 43 | HRESULT hr = E_FAIL; 44 | 45 | if (m_GetCount < 3) 46 | { 47 | ++m_GetCount; 48 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " NumFramesRead: " << NumFramesRead << std::endl; 49 | hr = m_RealClient.ReleaseBuffer(NumFramesRead); 50 | } 51 | else 52 | { 53 | hr = m_RealClient.ReleaseBuffer(NumFramesRead); 54 | if (FAILED(hr)) 55 | { 56 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " NumFramesRead: " << NumFramesRead << std::endl; 57 | } 58 | } 59 | 60 | DEBUG_PRINT_HR(hr); 61 | 62 | return hr; 63 | } 64 | 65 | HRESULT STDMETHODCALLTYPE DebugWrapperCaptureClient::GetNextPacketSize(UINT32 *pNumFramesInNextPacket) 66 | { 67 | HRESULT hr = E_FAIL; 68 | 69 | if (m_GetCount < 3) 70 | { 71 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 72 | hr = m_RealClient.GetNextPacketSize(pNumFramesInNextPacket); 73 | if (SUCCEEDED(hr)) 74 | { 75 | rslog::info_ts() << " *pNumFramesInNextPacket: " << (*pNumFramesInNextPacket) << std::endl; 76 | } 77 | } 78 | else 79 | { 80 | hr = m_RealClient.GetNextPacketSize(pNumFramesInNextPacket); 81 | if (FAILED(hr)) 82 | { 83 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 84 | } 85 | } 86 | 87 | DEBUG_PRINT_HR(hr); 88 | 89 | return hr; 90 | } -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperCaptureClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class DebugWrapperCaptureClient : public ComBaseUnknown 6 | { 7 | public: 8 | DebugWrapperCaptureClient(IAudioCaptureClient& realClient, const std::wstring& deviceId); 9 | DebugWrapperCaptureClient(const DebugWrapperCaptureClient&) = delete; 10 | DebugWrapperCaptureClient(DebugWrapperCaptureClient&&) = delete; 11 | virtual ~DebugWrapperCaptureClient(); 12 | 13 | virtual HRESULT STDMETHODCALLTYPE GetBuffer(BYTE **ppData, UINT32 *pNumFramesToRead, DWORD *pdwFlags, UINT64 *pu64DevicePosition, UINT64 *pu64QPCPosition) override; 14 | virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer(UINT32 NumFramesRead) override; 15 | virtual HRESULT STDMETHODCALLTYPE GetNextPacketSize(UINT32 *pNumFramesInNextPacket) override; 16 | 17 | private: 18 | IAudioCaptureClient& m_RealClient; 19 | std::wstring m_DeviceId; 20 | 21 | unsigned m_GetCount = 0; 22 | }; -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperDevice.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "DebugWrapperDevice.h" 3 | #include "DebugWrapperAudioClient.h" 4 | #include "DebugWrapperDevicePropertyStore.h" 5 | #include "DebugWrapperEndpoint.h" 6 | #include "DebugWrapperAudioEndpointVolume.h" 7 | 8 | #define DEBUG_PRINT_HR(hr) if(FAILED(hr)) rslog::info_ts() << " hr: " << HResultToStr(hr) << std::endl; 9 | 10 | DebugWrapperDevice::DebugWrapperDevice(IMMDevice& realDevice) 11 | : m_RealDevice(realDevice) 12 | { 13 | realDevice.AddRef(); 14 | 15 | LPWSTR pStrId = NULL; 16 | realDevice.GetId(&pStrId); 17 | m_Id = pStrId; 18 | CoTaskMemFree(pStrId); 19 | } 20 | 21 | DebugWrapperDevice::~DebugWrapperDevice() 22 | { 23 | m_RealDevice.Release(); 24 | } 25 | 26 | HRESULT STDMETHODCALLTYPE DebugWrapperDevice::QueryInterface(REFIID riid, void **ppvObject) 27 | { 28 | rslog::info_ts() << m_Id << " " __FUNCTION__" - riid: " << riid << std::endl; 29 | 30 | HRESULT hr = m_RealDevice.QueryInterface(riid, ppvObject); 31 | DEBUG_PRINT_HR(hr); 32 | 33 | if (SUCCEEDED(hr) && riid == __uuidof(IMMEndpoint) && ppvObject) 34 | { 35 | IMMEndpoint* realEndpoint = *(IMMEndpoint**)ppvObject; 36 | *ppvObject = new DebugWrapperEndpoint(*realEndpoint, m_Id); 37 | realEndpoint->Release(); 38 | realEndpoint = nullptr; 39 | } 40 | 41 | return hr; 42 | } 43 | 44 | HRESULT STDMETHODCALLTYPE DebugWrapperDevice::Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface) 45 | { 46 | rslog::info_ts() << m_Id << " " __FUNCTION__ " - Activate iid: " << iid << " dwClsCtx: " << std::hex << dwClsCtx << std::endl; 47 | 48 | HRESULT hr = E_NOINTERFACE; 49 | 50 | if (!ppInterface) 51 | { 52 | hr = E_POINTER; 53 | } 54 | else 55 | { 56 | if (iid == __uuidof(IAudioClient) || iid == __uuidof(IAudioClient2) || iid == __uuidof(IAudioClient3)) 57 | { 58 | // first - attempt creating IAudioClient3 59 | { 60 | IAudioClient3* realAudioClient = nullptr; 61 | hr = m_RealDevice.Activate(__uuidof(IAudioClient3), dwClsCtx, pActivationParams, (void**)&realAudioClient); 62 | if (SUCCEEDED(hr)) 63 | { 64 | *ppInterface = new DebugWrapperAudioClient3(*realAudioClient, m_Id); 65 | realAudioClient->Release(); 66 | } 67 | } 68 | 69 | // if failed, keep trying - with IAudioClient2 if possible 70 | if (FAILED(hr) && (iid == __uuidof(IAudioClient) || iid == __uuidof(IAudioClient2))) 71 | { 72 | IAudioClient2* realAudioClient = nullptr; 73 | hr = m_RealDevice.Activate(__uuidof(IAudioClient2), dwClsCtx, pActivationParams, (void**)&realAudioClient); 74 | if (SUCCEEDED(hr)) 75 | { 76 | *ppInterface = new DebugWrapperAudioClient2<>(*realAudioClient, m_Id); 77 | realAudioClient->Release(); 78 | } 79 | } 80 | 81 | // if failed, keep trying - with IAudioClient if possible 82 | if (FAILED(hr) && iid == __uuidof(IAudioClient)) 83 | { 84 | IAudioClient* realAudioClient = nullptr; 85 | hr = m_RealDevice.Activate(__uuidof(IAudioClient), dwClsCtx, pActivationParams, (void**)&realAudioClient); 86 | if (SUCCEEDED(hr)) 87 | { 88 | *ppInterface = new DebugWrapperAudioClient<>(*realAudioClient, m_Id); 89 | realAudioClient->Release(); 90 | } 91 | } 92 | } 93 | else if (iid == __uuidof(IAudioEndpointVolume)) 94 | { 95 | IAudioEndpointVolume* realInterface = nullptr; 96 | hr = m_RealDevice.Activate(iid, dwClsCtx, pActivationParams, (void**)&realInterface); 97 | if (SUCCEEDED(hr) && realInterface) 98 | { 99 | *ppInterface = new DebugWrapperAudioEndpointVolume(*realInterface, m_Id); 100 | realInterface->Release(); 101 | } 102 | } 103 | else 104 | { 105 | hr = m_RealDevice.Activate(iid, dwClsCtx, pActivationParams, ppInterface); 106 | } 107 | } 108 | 109 | DEBUG_PRINT_HR(hr); 110 | 111 | return hr; 112 | } 113 | 114 | HRESULT STDMETHODCALLTYPE DebugWrapperDevice::OpenPropertyStore(DWORD stgmAccess, IPropertyStore **ppProperties) 115 | { 116 | rslog::info_ts() << m_Id << " " __FUNCTION__ " - stgmAccess: " << std::hex << stgmAccess << std::endl; 117 | 118 | IPropertyStore* realPropStore = nullptr; 119 | HRESULT hr = m_RealDevice.OpenPropertyStore(stgmAccess, &realPropStore); 120 | DEBUG_PRINT_HR(hr); 121 | if (SUCCEEDED(hr)) 122 | { 123 | *ppProperties = new DebugWrapperDevicePropertyStore(*realPropStore, m_Id); 124 | realPropStore->Release(); 125 | realPropStore = nullptr; 126 | } 127 | 128 | return hr; 129 | } 130 | 131 | HRESULT STDMETHODCALLTYPE DebugWrapperDevice::GetId(LPWSTR *ppstrId) 132 | { 133 | HRESULT hr = m_RealDevice.GetId(ppstrId); 134 | DEBUG_PRINT_HR(hr); 135 | return hr; 136 | } 137 | 138 | HRESULT STDMETHODCALLTYPE DebugWrapperDevice::GetState(DWORD *pdwState) 139 | { 140 | HRESULT hr = m_RealDevice.GetState(pdwState); 141 | DEBUG_PRINT_HR(hr); 142 | return hr; 143 | } -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperDevice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class DebugWrapperDevice : public ComBaseUnknown 6 | { 7 | public: 8 | DebugWrapperDevice(IMMDevice& realDevice); 9 | DebugWrapperDevice(const DebugWrapperDevice&) = delete; 10 | DebugWrapperDevice(DebugWrapperDevice&&) = delete; 11 | virtual ~DebugWrapperDevice(); 12 | 13 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 14 | 15 | virtual HRESULT STDMETHODCALLTYPE Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface) override; 16 | virtual HRESULT STDMETHODCALLTYPE OpenPropertyStore(DWORD stgmAccess, IPropertyStore **ppProperties) override; 17 | virtual HRESULT STDMETHODCALLTYPE GetId(LPWSTR *ppstrId) override; 18 | virtual HRESULT STDMETHODCALLTYPE GetState(DWORD *pdwState) override; 19 | 20 | private: 21 | IMMDevice& m_RealDevice; 22 | std::wstring m_Id; 23 | }; -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperDevicePropertyStore.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "DebugWrapperDevicePropertyStore.h" 3 | 4 | #include 5 | #include 6 | 7 | // example: L"{1}.TUSBAUDIO_ENUM\\VID_1397&PID_0508&KS\\9&34A5FE73&4&4" 8 | DEFINE_PROPERTYKEY(PKEY_Device_DeviceIdHiddenKey1, 0xb3f8fa53, 0x0004, 0x438e, 0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc, 2); 9 | 10 | // example: L"oem24.inf:7a9b59c8209f0782:_Install_8.NTamd64:4.59.0.56775:TUSBAUDIO_ENUM\\VID_1397&PID_0508&KS" 11 | DEFINE_PROPERTYKEY(PKEY_Device_DeviceIdHiddenKey2, 0x83DA6326, 0x97A6, 0x4088, 0x94, 0x53, 0xA1, 0x92, 0x3F, 0x57, 0x3B, 0x29, 3); 12 | 13 | 14 | #define DEBUG_PRINT_HR(hr) if(FAILED(hr)) rslog::info_ts() << " hr: " << HResultToStr(hr) << std::endl 15 | 16 | DebugWrapperDevicePropertyStore::DebugWrapperDevicePropertyStore(IPropertyStore& realPropertyStore, const std::wstring& deviceId) 17 | : m_RealPropertyStore(realPropertyStore) 18 | , m_DeviceId(deviceId) 19 | { 20 | m_RealPropertyStore.AddRef(); 21 | } 22 | 23 | DebugWrapperDevicePropertyStore::~DebugWrapperDevicePropertyStore() 24 | { 25 | m_RealPropertyStore.Release(); 26 | } 27 | 28 | HRESULT STDMETHODCALLTYPE DebugWrapperDevicePropertyStore::GetCount(DWORD *cProps) 29 | { 30 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 31 | 32 | HRESULT hr = m_RealPropertyStore.GetCount(cProps); 33 | 34 | DEBUG_PRINT_HR(hr); 35 | 36 | if (SUCCEEDED(hr) && cProps) 37 | { 38 | rslog::info_ts() << " *cProps: " << std::dec << *cProps << std::endl; 39 | } 40 | 41 | return hr; 42 | } 43 | 44 | HRESULT STDMETHODCALLTYPE DebugWrapperDevicePropertyStore::GetAt(DWORD iProp, PROPERTYKEY *pkey) 45 | { 46 | // NOTE: this log entry gets too noisy on WASAPI devices... 47 | //rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " - iProp: " << std::dec << iProp << std::endl; 48 | 49 | HRESULT hr = m_RealPropertyStore.GetAt(iProp, pkey); 50 | DEBUG_PRINT_HR(hr); 51 | 52 | return hr; 53 | } 54 | 55 | HRESULT STDMETHODCALLTYPE DebugWrapperDevicePropertyStore::GetValue(REFPROPERTYKEY key, PROPVARIANT *pv) 56 | { 57 | //rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " - key: " << key << std::endl; 58 | 59 | HRESULT hr = m_RealPropertyStore.GetValue(key, pv); 60 | DEBUG_PRINT_HR(hr); 61 | /* 62 | if (hr == S_OK) 63 | { 64 | if (pv->vt == VT_EMPTY) 65 | { 66 | rslog::info_ts() << " unhandled key" << std::endl; 67 | } 68 | else if (key == PKEY_AudioEngine_DeviceFormat && pv->vt == VT_BLOB) 69 | { 70 | rslog::info_ts() << *(WAVEFORMATEX*)pv->blob.pBlobData; 71 | } 72 | else if (key == PKEY_AudioEndpoint_FormFactor && pv->vt == VT_UI4) 73 | { 74 | rslog::info_ts() << " form factor: " << std::dec << pv->uintVal << std::endl; 75 | } 76 | else if (key == PKEY_Device_FriendlyName) 77 | { 78 | if (pv->vt == VT_LPWSTR) 79 | rslog::info_ts() << " friendly name: " << std::dec << pv->pwszVal << std::endl; 80 | else if (pv->vt == VT_LPSTR) 81 | rslog::info_ts() << " friendly name: " << std::dec << pv->pszVal << std::endl; 82 | } 83 | } 84 | */ 85 | 86 | return hr; 87 | } 88 | 89 | HRESULT STDMETHODCALLTYPE DebugWrapperDevicePropertyStore::SetValue(REFPROPERTYKEY key, REFPROPVARIANT propvar) 90 | { 91 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " - key: " << key << std::endl; 92 | 93 | HRESULT hr = m_RealPropertyStore.SetValue(key, propvar); 94 | DEBUG_PRINT_HR(hr); 95 | 96 | return hr; 97 | } 98 | 99 | HRESULT STDMETHODCALLTYPE DebugWrapperDevicePropertyStore::Commit() 100 | { 101 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 102 | 103 | HRESULT hr = m_RealPropertyStore.Commit(); 104 | DEBUG_PRINT_HR(hr); 105 | 106 | return hr; 107 | } 108 | -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperDevicePropertyStore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class DebugWrapperDevicePropertyStore : public ComBaseUnknown 6 | { 7 | public: 8 | DebugWrapperDevicePropertyStore(IPropertyStore& realPropertyStore, const std::wstring& deviceId); 9 | DebugWrapperDevicePropertyStore(const DebugWrapperDevicePropertyStore&) = delete; 10 | DebugWrapperDevicePropertyStore(DebugWrapperDevicePropertyStore&&) = delete; 11 | virtual ~DebugWrapperDevicePropertyStore(); 12 | 13 | virtual HRESULT STDMETHODCALLTYPE GetCount(DWORD *cProps) override; 14 | virtual HRESULT STDMETHODCALLTYPE GetAt(DWORD iProp, PROPERTYKEY *pkey) override; 15 | virtual HRESULT STDMETHODCALLTYPE GetValue(REFPROPERTYKEY key, PROPVARIANT *pv) override; 16 | virtual HRESULT STDMETHODCALLTYPE SetValue(REFPROPERTYKEY key, REFPROPVARIANT propvar) override; 17 | virtual HRESULT STDMETHODCALLTYPE Commit(void) override; 18 | 19 | private: 20 | IPropertyStore& m_RealPropertyStore; 21 | std::wstring m_DeviceId; 22 | }; -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperEndpoint.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "DebugWrapperEndpoint.h" 3 | 4 | #define DEBUG_PRINT_HR(hr) if(FAILED(hr)) rslog::info_ts() << " hr: " << HResultToStr(hr) << std::endl 5 | 6 | DebugWrapperEndpoint::DebugWrapperEndpoint(IMMEndpoint& realEndpoint, const std::wstring& deviceId) 7 | : m_RealEndpoint(realEndpoint) 8 | , m_DeviceId(deviceId) 9 | { 10 | m_RealEndpoint.AddRef(); 11 | } 12 | 13 | DebugWrapperEndpoint::~DebugWrapperEndpoint() 14 | { 15 | m_RealEndpoint.Release(); 16 | } 17 | 18 | HRESULT STDMETHODCALLTYPE DebugWrapperEndpoint::QueryInterface(REFIID riid, void **ppvObject) 19 | { 20 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " riid: " << riid << std::endl; 21 | 22 | HRESULT hr = m_RealEndpoint.QueryInterface(riid, ppvObject); 23 | DEBUG_PRINT_HR(hr); 24 | 25 | return hr; 26 | } 27 | 28 | HRESULT STDMETHODCALLTYPE DebugWrapperEndpoint::GetDataFlow(EDataFlow *pDataFlow) 29 | { 30 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ << std::endl; 31 | 32 | HRESULT hr = m_RealEndpoint.GetDataFlow(pDataFlow); 33 | DEBUG_PRINT_HR(hr); 34 | 35 | if (SUCCEEDED(hr)) 36 | { 37 | rslog::info_ts() << " *pDataFlow: " << Dataflow2String(*pDataFlow) << std::endl; 38 | } 39 | 40 | return hr; 41 | } -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperEndpoint.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class DebugWrapperEndpoint : public ComBaseUnknown 6 | { 7 | public: 8 | DebugWrapperEndpoint(IMMEndpoint& realEndpoint, const std::wstring& deviceId); 9 | DebugWrapperEndpoint(const DebugWrapperEndpoint&) = delete; 10 | DebugWrapperEndpoint(DebugWrapperEndpoint&&) = delete; 11 | virtual ~DebugWrapperEndpoint(); 12 | 13 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 14 | 15 | virtual HRESULT STDMETHODCALLTYPE GetDataFlow(EDataFlow *pDataFlow) override; 16 | 17 | private: 18 | IMMEndpoint& m_RealEndpoint; 19 | std::wstring m_DeviceId; 20 | }; -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperRenderClient.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "DebugWrapperRenderClient.h" 3 | 4 | #define DEBUG_PRINT_HR(hr) if(FAILED(hr)) rslog::info_ts() << " hr: " << HResultToStr(hr) << std::endl 5 | 6 | DebugWrapperRenderClient::DebugWrapperRenderClient(IAudioRenderClient& realClient, const std::wstring& deviceId) 7 | : m_RealClient(realClient) 8 | , m_DeviceId(deviceId) 9 | { 10 | m_RealClient.AddRef(); 11 | } 12 | 13 | DebugWrapperRenderClient::~DebugWrapperRenderClient() 14 | { 15 | m_RealClient.Release(); 16 | } 17 | 18 | HRESULT STDMETHODCALLTYPE DebugWrapperRenderClient::GetBuffer(UINT32 NumFramesRequested, BYTE **ppData) 19 | { 20 | HRESULT hr = E_FAIL; 21 | 22 | if (m_GetCount < 3) 23 | { 24 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " NumFramesRequested: " << NumFramesRequested << std::endl; 25 | 26 | hr = m_RealClient.GetBuffer(NumFramesRequested, ppData); 27 | } 28 | else 29 | { 30 | hr = m_RealClient.GetBuffer(NumFramesRequested, ppData); 31 | if (FAILED(hr)) 32 | { 33 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " NumFramesRequested: " << NumFramesRequested << std::endl; 34 | } 35 | } 36 | 37 | DEBUG_PRINT_HR(hr); 38 | return hr; 39 | } 40 | 41 | HRESULT STDMETHODCALLTYPE DebugWrapperRenderClient::ReleaseBuffer(UINT32 NumFramesWritten, DWORD dwFlags) 42 | { 43 | HRESULT hr = E_FAIL; 44 | 45 | if (m_GetCount < 3) 46 | { 47 | ++m_GetCount; 48 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " NumFramesWritten: " << NumFramesWritten << std::endl; 49 | hr = m_RealClient.ReleaseBuffer(NumFramesWritten, dwFlags); 50 | } 51 | else 52 | { 53 | hr = m_RealClient.ReleaseBuffer(NumFramesWritten, dwFlags); 54 | if (FAILED(hr)) 55 | { 56 | rslog::info_ts() << m_DeviceId << " " __FUNCTION__ " NumFramesWritten: " << NumFramesWritten << std::endl; 57 | } 58 | } 59 | 60 | DEBUG_PRINT_HR(hr); 61 | 62 | return hr; 63 | } -------------------------------------------------------------------------------- /RS_ASIO/DebugWrapperRenderClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class DebugWrapperRenderClient : public ComBaseUnknown 6 | { 7 | public: 8 | DebugWrapperRenderClient(IAudioRenderClient& realClient, const std::wstring& deviceId); 9 | DebugWrapperRenderClient(const DebugWrapperRenderClient&) = delete; 10 | DebugWrapperRenderClient(DebugWrapperRenderClient&&) = delete; 11 | virtual ~DebugWrapperRenderClient(); 12 | 13 | virtual HRESULT STDMETHODCALLTYPE GetBuffer(UINT32 NumFramesRequested, BYTE **ppData) override; 14 | virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer(UINT32 NumFramesWritten, DWORD dwFlags) override; 15 | 16 | private: 17 | IAudioRenderClient& m_RealClient; 18 | std::wstring m_DeviceId; 19 | 20 | unsigned m_GetCount = 0; 21 | }; -------------------------------------------------------------------------------- /RS_ASIO/Log.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #define LOG_TO_CONSOLE 0 4 | 5 | #if !LOG_TO_CONSOLE 6 | static std::filebuf logFileBuffer; 7 | #endif 8 | static TimeStamp initTimeStamp; 9 | 10 | namespace rslog 11 | { 12 | #if !LOG_TO_CONSOLE 13 | std::ostream info(&logFileBuffer); 14 | std::ostream error(&logFileBuffer); 15 | #else 16 | std::ostream info(std::cout.rdbuf()); 17 | std::ostream error(std::cerr.rdbuf()); 18 | #endif 19 | 20 | std::ostream& info_ts() 21 | { 22 | char tmp[16]; 23 | snprintf(tmp, sizeof(tmp), "%0.3lf", (TimeStamp() - initTimeStamp).GetSeconds()); 24 | 25 | return info << tmp << " [INFO] "; 26 | } 27 | 28 | std::ostream& error_ts() 29 | { 30 | char tmp[16]; 31 | snprintf(tmp, sizeof(tmp), "%0.3lf", (TimeStamp() - initTimeStamp).GetSeconds()); 32 | 33 | return error << tmp << " [ERROR] "; 34 | } 35 | 36 | void InitLog() 37 | { 38 | #if !LOG_TO_CONSOLE 39 | logFileBuffer.open("RS_ASIO-log.txt", std::ios_base::out | std::ios_base::trunc); 40 | initTimeStamp = TimeStamp(); 41 | #else 42 | AllocConsole(); 43 | AttachConsole(GetCurrentProcessId()); 44 | 45 | FILE* fDummy = nullptr; 46 | freopen_s(&fDummy, "CONOUT$", "w", stdout); 47 | freopen_s(&fDummy, "CONOUT$", "w", stderr); 48 | freopen_s(&fDummy, "CONIN$", "r", stdin); 49 | 50 | info.set_rdbuf(std::cout.rdbuf()); 51 | error.set_rdbuf(std::cerr.rdbuf()); 52 | #endif 53 | } 54 | 55 | void CleanupLog() 56 | { 57 | #if !LOG_TO_CONSOLE 58 | logFileBuffer.close(); 59 | #else 60 | #endif 61 | } 62 | } -------------------------------------------------------------------------------- /RS_ASIO/Log.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace rslog 4 | { 5 | extern std::ostream info; 6 | extern std::ostream error; 7 | 8 | std::ostream& info_ts(); 9 | std::ostream& error_ts(); 10 | 11 | void InitLog(); 12 | void CleanupLog(); 13 | } -------------------------------------------------------------------------------- /RS_ASIO/MyUnknown.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | // {F2D67F48-1977-4991-A3FC-A093835A7DC2} 6 | DEFINE_GUID(IID_IMyUnknown , 0xf2d67f48, 0x1977, 0x4991, 0xa3, 0xfc, 0xa0, 0x93, 0x83, 0x5a, 0x7d, 0xc2); 7 | 8 | class __declspec(uuid("F2D67F48-1977-4991-A3FC-A093835A7DC2")) MyUnknown : public ComBaseUnknown 9 | { 10 | public: 11 | bool RefCountHackEnabled = false; 12 | }; 13 | -------------------------------------------------------------------------------- /RS_ASIO/NtProtectVirtualMemory.asm: -------------------------------------------------------------------------------- 1 | IFNDEF RAX 2 | .MODEL FLAT, C 3 | ENDIF 4 | 5 | .CODE 6 | 7 | 8 | NtProtectVirtualMemory PROC 9 | mov eax, 50h 10 | call KiSystemCall 11 | ret 12 | NtProtectVirtualMemory ENDP 13 | 14 | KiSystemCall PROC 15 | IFDEF RAX 16 | mov r10,rcx 17 | syscall 18 | ret 19 | ELSE 20 | db 234 21 | dd exit 22 | dw 51 23 | exit: 24 | inc ecx 25 | jmp dword ptr [edi+248] 26 | ENDIF 27 | KiSystemCall ENDP 28 | 29 | END -------------------------------------------------------------------------------- /RS_ASIO/Patcher.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void InitPatcher(); 4 | void DeinitPatcher(); 5 | void PatchOriginalCode(); 6 | void* GetVirtualProtectFnPtr(); 7 | void SetVirtualProtectFnPtr(void* fn); 8 | std::vector GetUntouchedVirtualProtectBytes(unsigned numBytes); 9 | 10 | std::vector FindBytesOffsets(const BYTE* bytes, size_t numBytes); 11 | void Patch_CallAbsoluteIndirectAddress(const std::vector& offsets, void* TargetFn, size_t numNopsFollowing=0); 12 | void Patch_CallRelativeAddress(const std::vector& offsets, void* TargetFn); 13 | void Patch_ReplaceWithNops(void* offset, size_t numBytes); 14 | void Patch_ReplaceWithBytes(void* offset, size_t numBytes, const BYTE* replaceBytes); 15 | -------------------------------------------------------------------------------- /RS_ASIO/Patcher_21a8959a.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "dllmain.h" 3 | #include "Patcher.h" 4 | 5 | // this file contains the patch code for the game version released BEFORE 2022-09-11 (pre-patch) 6 | 7 | static const BYTE originalBytes_call_CoCreateInstance[]{ 8 | 0xff, 0x15, 0x34, 0x87, 0x18, 0x02, 9 | 0x85, 0xc0 10 | }; 11 | 12 | static const BYTE originalBytes_call_PortAudio_MarshalStreamComPointers[]{ 13 | 0xe8, 0x9e, 0xe1, 0xff, 0xff, // call 14 | 0x83, 0xc4, 0x04, // add esp, 4 15 | 0x85, 0xc0 // test eax, eax 16 | }; 17 | 18 | static const BYTE originalBytes_call_UnmarshalStreamComPointers[]{ 19 | 0xe8, 0x59, 0xdf, 0xff, 0xff, // call 20 | 0x57, // push edi 21 | 0xe8, 0x33, 0xe0, 0xff, 0xff // call (to another uninteresting function) 22 | }; 23 | 24 | static const uintptr_t location_TwoRealToneCablesMessageBox = 0x017b9518; 25 | 26 | static const BYTE originalBytes_TwoRealToneCablesMessageBox[]{ 27 | 0x8b, 0xb5, 0x8c, 0xff, 0xff, 0xff // mov esi, dword ptr [ebp-0x74] 28 | }; 29 | 30 | static const BYTE patchedBytes_TwoRealToneCablesMessageBox[]{ 31 | 0xe9, 0x2f, 0x01, 0x00, 0x00, // jmp to 0x17b964c (skip over the message box) 32 | 0x90 // nop 33 | }; 34 | 35 | static const BYTE originalBytes_UnknownCrash[]{ 36 | 0x8b, 0x8d, 0xc4, 0xfd, 0xff, 0xff, 37 | 0x8b, 0x1c, 0x81 38 | }; 39 | 40 | /// 41 | /// Write x86 ASM (HEX) to static address. 42 | /// 43 | /// - Pointer you want to edit 44 | /// - Edit you want to make 45 | /// - How long is the edit 46 | /// Patch Completed 47 | static bool Patch_ReplaceAssembly(LPVOID location, LPVOID newAssembly, UINT lengthOfAssembly) { 48 | DWORD dwOldProt, dwDummy; 49 | 50 | if (!VirtualProtect(location, lengthOfAssembly, PAGE_EXECUTE_READWRITE, &dwOldProt)) { 51 | return false; 52 | } 53 | 54 | memcpy(location, newAssembly, lengthOfAssembly); 55 | 56 | FlushInstructionCache(GetCurrentProcess(), location, lengthOfAssembly); 57 | VirtualProtect(location, lengthOfAssembly, dwOldProt, &dwDummy); 58 | 59 | return true; 60 | } 61 | 62 | void PatchOriginalCode_21a8959a() 63 | { 64 | std::vector offsets_CoCreateInstance = FindBytesOffsets(originalBytes_call_CoCreateInstance, sizeof(originalBytes_call_CoCreateInstance)); 65 | std::vector offsets_PaMarshalPointers = FindBytesOffsets(originalBytes_call_PortAudio_MarshalStreamComPointers, sizeof(originalBytes_call_PortAudio_MarshalStreamComPointers)); 66 | std::vector offsets_PaUnmarshalPointers = FindBytesOffsets(originalBytes_call_UnmarshalStreamComPointers, sizeof(originalBytes_call_UnmarshalStreamComPointers)); 67 | 68 | std::vector offsets_UnknownCrash = FindBytesOffsets(originalBytes_UnknownCrash, sizeof(originalBytes_UnknownCrash)); 69 | 70 | if (offsets_CoCreateInstance.size() == 0 && offsets_PaMarshalPointers.size() == 0 && offsets_PaUnmarshalPointers.size() == 0) 71 | { 72 | rslog::error_ts() << "No valid locations for patching were found. Make sure you're trying this on the right game version." << std::endl; 73 | } 74 | else 75 | { 76 | // patch CoCreateInstance calls 77 | rslog::info_ts() << "Patching CoCreateInstance" << std::endl; 78 | Patch_CallAbsoluteIndirectAddress(offsets_CoCreateInstance, &Patched_CoCreateInstance, 1); 79 | 80 | // patch PortAudio MarshalStreamComPointers 81 | rslog::info_ts() << "Patching PortAudio MarshalStreamComPointers" << std::endl; 82 | Patch_CallRelativeAddress(offsets_PaMarshalPointers, &Patched_PortAudio_MarshalStreamComPointers); 83 | 84 | // patch PortAudio UnmarshalStreamComPointers 85 | rslog::info_ts() << "Patching PortAudio UnmarshalStreamComPointers" << std::endl; 86 | Patch_CallRelativeAddress(offsets_PaUnmarshalPointers, &Patched_PortAudio_UnmarshalStreamComPointers); 87 | 88 | Patch_ReplaceAssembly((LPVOID)location_TwoRealToneCablesMessageBox, (LPVOID)patchedBytes_TwoRealToneCablesMessageBox, sizeof(patchedBytes_TwoRealToneCablesMessageBox)); 89 | 90 | // patch weird crash (this crash happens even without rs-asio) when certain audio devices are present (like voicemeeter modern versions) 91 | rslog::info_ts() << "Patching unknown crash when certain audio devices are found (num locations: " << offsets_UnknownCrash.size() << ")" << std::endl; 92 | for (void* offset : offsets_UnknownCrash) 93 | { 94 | const BYTE replaceBytes[] 95 | { 96 | 0x90, 0x90, 0x90, 0x90, 0x90, // nops... 97 | 0x31, 0xc9, // xor ecx, ecx 98 | 0x31, 0xdb, // xor ebx, ebx 99 | }; 100 | rslog::info_ts() << "Patching bytes at " << offset << std::endl; 101 | Patch_ReplaceWithBytes(offset, sizeof(replaceBytes), replaceBytes); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /RS_ASIO/Patcher_d1b38fcb.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "dllmain.h" 3 | #include "Patcher.h" 4 | #include "crc32.h" 5 | 6 | // this file contains the patch code for the game version released ON 2022-09-11 (post-patch) 7 | 8 | static const BYTE originalBytes_call_CoCreateInstance[]{ 9 | 0xe8, 0x3e, 0x9d, 0x7d, 0x00, // call relative 10 | 0xcd, // ? 11 | 0x85, 0xc0 // test eax, eax 12 | }; 13 | 14 | static const BYTE originalBytes_call_CoCreateInstance2[]{ 15 | 0xe8, 0xc2, 0x23, 0x57, 0x00, // call relative 16 | 0x96, // xchg eax, esi 17 | 0x85, 0xc0 // test eax, eax 18 | }; 19 | 20 | // these are unused for now 21 | /* 22 | static const BYTE originalBytes_call_CoCreateInstance3[]{ 23 | 0xe8, 0x4d, 0xfa, 0x5a, 0x00, // call relative 24 | 0x85, 0xc0, // test eax, eax 25 | 0x75, 0x74 // jnz short .. 26 | }; 27 | 28 | static const BYTE originalBytes_call_CoCreateInstance4[]{ 29 | 0xe8, 0x31, 0xdd, 0x4a, 0x00, // call relative 30 | 0x85, 0xc0, // test eax, eax 31 | 0x79, 0x0c // jns short .. 32 | }; 33 | 34 | static const BYTE originalBytes_call_CoCreateInstance5[]{ 35 | 0xe8, 0x79, 0x7a, 0x4a, 0x00, // call relative 36 | 0x85, 0xc0, // test eax, eax 37 | 0x78, 0x33 // js short .. 38 | }; 39 | */ 40 | 41 | 42 | static const BYTE originalBytes_call_PortAudio_MarshalStreamComPointers[]{ 43 | 0xe8, 0xdc, 0xe4, 0xff, 0xff, // call 44 | 0x83, 0xc4, 0x04, // add esp, 4 45 | 0x85, 0xc0 // test eax, eax 46 | }; 47 | 48 | static const BYTE originalBytes_call_UnmarshalStreamComPointers[]{ 49 | 0xe8, 0x97, 0xe2, 0xff, 0xff, // call 50 | 0x57, // push edi 51 | 0xe8, 0x71, 0xe3, 0xff, 0xff // call (to another uninteresting function) 52 | }; 53 | 54 | static const BYTE originalBytes_TwoRealToneCablesMessageBoxStarting[]{ 55 | // 0x8b, 0x75, 0x8c, // mov esi, .... 56 | // 0xe8, 0x7c, 0x22, 0xe2, 0xff, // call 57 | // 0x84, 0xc0, // test al, al 58 | 0x74, 0x6b, // jz ... 59 | 0xba, 0x34, 0xfe, 0x1c, 0x01, // mov edx, offset "TooManyGuitarInputs" 60 | 0x8d, 0xb3, 0x4c, 0x01, 0x00, 0x00, // lea esi, [ebx+0x14C] 61 | 0xe8, 0xd8, 0xd8, 0xc4, 0xff // call 62 | }; 63 | 64 | static const BYTE originalBytes_TwoRealToneCablesMessageBoxMainMenu[]{ 65 | // 0x80, 0xbb, 0x4a, 0x01, 0x00, 0x00, 0x00 // cmp byte ptr [ebx+0x14A], 0 66 | 0x0f, 0x84, 0x95, 0x00, 0x00, 0x00, // jz ... 67 | 0xba, 0x48, 0xfe, 0x1c, 0x01, // mov edx, offset "$[34872]Warning! Two Rocksmith Real Ton..." 68 | 0x8d, 0x75, 0xb0, // lea esi, [ebp+...] 69 | }; 70 | 71 | static const BYTE originalBytes_UnknownCrash[]{ 72 | 0x8b, 0x8d, 0xc4, 0xfd, 0xff, 0xff, 73 | 0x8b, 0x1c, 0x81 74 | }; 75 | 76 | template 77 | void vector_append(std::vector& inOut, const std::vector& source) 78 | { 79 | for (auto& it : source) 80 | { 81 | inOut.push_back(it); 82 | } 83 | } 84 | 85 | void PatchOriginalCode_d1b38fcb() 86 | { 87 | std::vector offsets_CoCreateInstanceAbs = FindBytesOffsets(originalBytes_call_CoCreateInstance, sizeof(originalBytes_call_CoCreateInstance)); 88 | vector_append(offsets_CoCreateInstanceAbs, FindBytesOffsets(originalBytes_call_CoCreateInstance2, sizeof(originalBytes_call_CoCreateInstance2))); 89 | 90 | // this is not patching properly, and causing issues with midi stuff in RSMods. 91 | // we don't seem to need it, so let's keep it disabled. 92 | //std::vector offsets_CoCreateInstanceRel = FindBytesOffsets(originalBytes_call_CoCreateInstance3, sizeof(originalBytes_call_CoCreateInstance3)); 93 | //vector_append(offsets_CoCreateInstanceRel, FindBytesOffsets(originalBytes_call_CoCreateInstance4, sizeof(originalBytes_call_CoCreateInstance4))); 94 | //vector_append(offsets_CoCreateInstanceRel, FindBytesOffsets(originalBytes_call_CoCreateInstance5, sizeof(originalBytes_call_CoCreateInstance5))); 95 | 96 | std::vector offsets_PaMarshalPointers = FindBytesOffsets(originalBytes_call_PortAudio_MarshalStreamComPointers, sizeof(originalBytes_call_PortAudio_MarshalStreamComPointers)); 97 | std::vector offsets_PaUnmarshalPointers = FindBytesOffsets(originalBytes_call_UnmarshalStreamComPointers, sizeof(originalBytes_call_UnmarshalStreamComPointers)); 98 | 99 | std::vector offsets_TwoRealToneCablesMessageBoxStarting = FindBytesOffsets(originalBytes_TwoRealToneCablesMessageBoxStarting, sizeof(originalBytes_TwoRealToneCablesMessageBoxStarting)); 100 | std::vector offsets_TwoRealToneCablesMessageBoxMainMenu = FindBytesOffsets(originalBytes_TwoRealToneCablesMessageBoxMainMenu, sizeof(originalBytes_TwoRealToneCablesMessageBoxMainMenu)); 101 | 102 | std::vector offsets_UnknownCrash = FindBytesOffsets(originalBytes_UnknownCrash, sizeof(originalBytes_UnknownCrash)); 103 | 104 | if (offsets_CoCreateInstanceAbs.size() == 0 && offsets_PaMarshalPointers.size() == 0 && offsets_PaUnmarshalPointers.size() == 0) 105 | { 106 | rslog::error_ts() << "No valid locations for patching were found. Make sure you're trying this on the right game version." << std::endl; 107 | } 108 | else 109 | { 110 | // patch CoCreateInstance calls 111 | rslog::info_ts() << "Patching CoCreateInstance" << std::endl; 112 | Patch_CallAbsoluteIndirectAddress(offsets_CoCreateInstanceAbs, &Patched_CoCreateInstance, 1); 113 | //Patch_CallRelativeAddress<(void*)&Patched_CoCreateInstance>(offsets_CoCreateInstanceRel); 114 | 115 | // patch PortAudio MarshalStreamComPointers 116 | rslog::info_ts() << "Patching PortAudio MarshalStreamComPointers" << std::endl; 117 | Patch_CallRelativeAddress(offsets_PaMarshalPointers, &Patched_PortAudio_MarshalStreamComPointers); 118 | 119 | // patch PortAudio UnmarshalStreamComPointers 120 | rslog::info_ts() << "Patching PortAudio UnmarshalStreamComPointers" << std::endl; 121 | Patch_CallRelativeAddress(offsets_PaUnmarshalPointers, &Patched_PortAudio_UnmarshalStreamComPointers); 122 | 123 | // patch two guitar cables connected message in single-player 124 | rslog::info_ts() << "Patching Two Guitar Tones Connected Message Box (starting menu) (num locations: " << offsets_TwoRealToneCablesMessageBoxStarting.size() << ")" << std::endl; 125 | for (void* offset : offsets_TwoRealToneCablesMessageBoxStarting) 126 | { 127 | const BYTE replaceBytes[] 128 | { 129 | 0xeb, // jmp rel8 130 | }; 131 | rslog::info_ts() << "Patching bytes at " << offset << std::endl; 132 | Patch_ReplaceWithBytes(offset, sizeof(replaceBytes), replaceBytes); 133 | } 134 | 135 | rslog::info_ts() << "Patching Two Guitar Tones Connected Message Box (main menu) (num locations: " << offsets_TwoRealToneCablesMessageBoxMainMenu.size() << ")" << std::endl; 136 | for (void* offset : offsets_TwoRealToneCablesMessageBoxMainMenu) 137 | { 138 | const BYTE replaceBytes[] 139 | { 140 | 0x90, // original instruction at this point is 6 byte wide, we only need 5 bytes, so put a nop here 141 | 0xe9, // jmp rel32 142 | }; 143 | rslog::info_ts() << "Patching bytes at " << offset << std::endl; 144 | Patch_ReplaceWithBytes(offset, sizeof(replaceBytes), replaceBytes); 145 | } 146 | 147 | // patch weird crash (this crash happens even without rs-asio) when certain audio devices are present (like voicemeeter modern versions) 148 | rslog::info_ts() << "Patching unknown crash when certain audio devices are found (num locations: " << offsets_UnknownCrash.size() << ")" << std::endl; 149 | for (void* offset : offsets_UnknownCrash) 150 | { 151 | const BYTE replaceBytes[] 152 | { 153 | 0x90, 0x90, 0x90, 0x90, 0x90, // nops... 154 | 0x31, 0xc9, // xor ecx, ecx 155 | 0x31, 0xdb, // xor ebx, ebx 156 | }; 157 | rslog::info_ts() << "Patching bytes at " << offset << std::endl; 158 | Patch_ReplaceWithBytes(offset, sizeof(replaceBytes), replaceBytes); 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /RS_ASIO/RSAggregatorDeviceEnum.cpp: -------------------------------------------------------------------------------- 1 | // RSDeviceEnumWrapper.cpp : Defines the exported functions for the DLL application. 2 | // 3 | 4 | #include "stdafx.h" 5 | #include "RSAggregatorDeviceEnum.h" 6 | 7 | RSAggregatorDeviceEnum::RSAggregatorDeviceEnum() 8 | { 9 | } 10 | 11 | RSAggregatorDeviceEnum::~RSAggregatorDeviceEnum() 12 | { 13 | ClearAll(); 14 | } 15 | 16 | void RSAggregatorDeviceEnum::AddDeviceEnumerator(IMMDeviceEnumerator* enumerator, bool enableOutputs, bool enableInputs) 17 | { 18 | if (!enumerator) 19 | return; 20 | 21 | TrackedEnumerator* existingTrackedEnum = FindTrackedEnumerator(enumerator); 22 | if (!existingTrackedEnum) 23 | { 24 | m_Enumerators.emplace_back(enumerator, enableOutputs, enableInputs); 25 | } 26 | else 27 | { 28 | existingTrackedEnum->enableOutputs |= enableOutputs; 29 | existingTrackedEnum->enableInputs |= enableInputs; 30 | } 31 | 32 | m_DeviceListNeedsUpdate = true; 33 | } 34 | 35 | RSAggregatorDeviceEnum::TrackedEnumerator* RSAggregatorDeviceEnum::FindTrackedEnumerator(IMMDeviceEnumerator* enumerator) 36 | { 37 | for (RSAggregatorDeviceEnum::TrackedEnumerator& te : m_Enumerators) 38 | { 39 | if (te.enumerator == enumerator) 40 | return &te; 41 | } 42 | return nullptr; 43 | } 44 | 45 | void RSAggregatorDeviceEnum::UpdateAvailableDevices() 46 | { 47 | rslog::info_ts() << __FUNCTION__ << std::endl; 48 | 49 | RSDeviceCollection aggregatedRenderCollection; 50 | RSDeviceCollection aggregatedCaptureCollection; 51 | 52 | // release defaults 53 | for (IMMDevice*& dev : m_DefaultRenderDevices) 54 | { 55 | if (dev) 56 | { 57 | dev->Release(); 58 | dev = nullptr; 59 | } 60 | } 61 | for (IMMDevice*& dev : m_DefaultCaptureDevices) 62 | { 63 | if (dev) 64 | { 65 | dev->Release(); 66 | dev = nullptr; 67 | } 68 | } 69 | 70 | auto fnUpdateCollection = [](IMMDeviceEnumerator* enumerator, EDataFlow dataFlow, RSDeviceCollection& out, std::array& outDefaults) 71 | { 72 | IMMDeviceCollection* collection = nullptr; 73 | if (SUCCEEDED(enumerator->EnumAudioEndpoints(dataFlow, DEVICE_STATEMASK_ALL, &collection))) 74 | { 75 | out.UpdateDevicesFromCollection(*collection, false); 76 | collection->Release(); 77 | } 78 | 79 | for (int i = eConsole; i < ERole_enum_count; ++i) 80 | { 81 | const ERole role = (ERole)i; 82 | if (!outDefaults[role]) 83 | { 84 | IMMDevice* defaultDevice = nullptr; 85 | 86 | if (SUCCEEDED(enumerator->GetDefaultAudioEndpoint(dataFlow, role, &defaultDevice))) 87 | { 88 | outDefaults[role] = defaultDevice; 89 | } 90 | } 91 | } 92 | }; 93 | 94 | for (const RSAggregatorDeviceEnum::TrackedEnumerator& te : m_Enumerators) 95 | { 96 | if (te.enableOutputs) 97 | fnUpdateCollection(te.enumerator, eRender, aggregatedRenderCollection, m_DefaultRenderDevices); 98 | if (te.enableInputs) 99 | fnUpdateCollection(te.enumerator, eCapture, aggregatedCaptureCollection, m_DefaultCaptureDevices); 100 | } 101 | 102 | m_RenderDevices.UpdateDevicesFromCollection(aggregatedRenderCollection, true); 103 | m_CaptureDevices.UpdateDevicesFromCollection(aggregatedCaptureCollection, true); 104 | 105 | m_DeviceListNeedsUpdate = false; 106 | } 107 | 108 | void RSAggregatorDeviceEnum::ClearAll() 109 | { 110 | RSBaseDeviceEnum::ClearAll(); 111 | m_Enumerators.clear(); 112 | } -------------------------------------------------------------------------------- /RS_ASIO/RSAggregatorDeviceEnum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RSBaseDeviceEnum.h" 4 | 5 | class RSAggregatorDeviceEnum : public RSBaseDeviceEnum 6 | { 7 | public: 8 | RSAggregatorDeviceEnum(); 9 | RSAggregatorDeviceEnum(const RSAggregatorDeviceEnum&) = delete; 10 | RSAggregatorDeviceEnum(RSAggregatorDeviceEnum&&) = delete; 11 | virtual ~RSAggregatorDeviceEnum(); 12 | 13 | void AddDeviceEnumerator(IMMDeviceEnumerator* enumerator, bool enableOutputs, bool enableInputs); 14 | 15 | protected: 16 | struct TrackedEnumerator 17 | { 18 | TrackedEnumerator(IMMDeviceEnumerator* enumerator, bool enableOutputs, bool enableInputs) 19 | { 20 | this->enumerator = enumerator; 21 | this->enableOutputs = enableOutputs; 22 | this->enableInputs = enableInputs; 23 | if (enumerator) 24 | { 25 | enumerator->AddRef(); 26 | } 27 | } 28 | 29 | TrackedEnumerator(const TrackedEnumerator& other) 30 | { 31 | *this = other; 32 | } 33 | 34 | TrackedEnumerator(TrackedEnumerator&& other) 35 | { 36 | *this = other; 37 | other.enumerator = nullptr; 38 | } 39 | 40 | ~TrackedEnumerator() 41 | { 42 | if (enumerator) 43 | { 44 | enumerator->Release(); 45 | } 46 | } 47 | 48 | TrackedEnumerator& operator=(const TrackedEnumerator& other) 49 | { 50 | enumerator = other.enumerator; 51 | enableOutputs = other.enableOutputs; 52 | enableInputs = other.enableInputs; 53 | if (enumerator) 54 | { 55 | enumerator->AddRef(); 56 | } 57 | return *this; 58 | } 59 | 60 | IMMDeviceEnumerator* enumerator = nullptr; 61 | bool enableOutputs = true; 62 | bool enableInputs = true; 63 | }; 64 | 65 | protected: 66 | TrackedEnumerator* FindTrackedEnumerator(IMMDeviceEnumerator* enumerator); 67 | virtual void UpdateAvailableDevices() override; 68 | virtual void ClearAll() override; 69 | 70 | std::vector m_Enumerators; 71 | }; 72 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioAudioCaptureClient.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "RSAsioAudioCaptureClient.h" 3 | #include "RSAsioAudioClient.h" 4 | #include "RSAsioDevice.h" 5 | 6 | RSAsioAudioCaptureClient::RSAsioAudioCaptureClient(RSAsioAudioClient& asioAudioClient) 7 | : RSAsioAudioClientServiceBase(asioAudioClient, /*isOutput =*/false) 8 | { 9 | } 10 | 11 | HRESULT STDMETHODCALLTYPE RSAsioAudioCaptureClient::GetBuffer(BYTE **ppData, UINT32 *pNumFramesToRead, DWORD *pdwFlags, UINT64 *pu64DevicePosition, UINT64 *pu64QPCPosition) 12 | { 13 | std::lock_guard g(m_mutex); 14 | 15 | if (!ppData || !pNumFramesToRead || !pdwFlags) 16 | return E_POINTER; 17 | 18 | if (!m_AsioAudioClient.GetAsioDevice().GetAsioHost().IsValid()) 19 | return AUDCLNT_E_DEVICE_INVALIDATED; 20 | 21 | if (m_WaitingForBufferRelease) 22 | return AUDCLNT_E_OUT_OF_ORDER; 23 | 24 | // put the newest input data on the backbuffer 25 | m_AsioAudioClient.SwapBuffers(); 26 | 27 | bool isDiscontinuity = true; 28 | UINT64 backbufferTimestamp = 0; 29 | BYTE* backbuffer = GetBackbufferIfPending(true, &backbufferTimestamp, &isDiscontinuity); 30 | 31 | *ppData = backbuffer; 32 | *pNumFramesToRead = backbuffer ? m_AsioAudioClient.GetBufferNumFrames() : 0; 33 | 34 | DWORD dwOutFlags = 0; 35 | if (backbufferTimestamp == 0) 36 | { 37 | dwOutFlags |= AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR; 38 | } 39 | if (!backbuffer) 40 | { 41 | dwOutFlags |= AUDCLNT_BUFFERFLAGS_SILENT; 42 | } 43 | if (isDiscontinuity) 44 | { 45 | dwOutFlags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY; 46 | } 47 | *pdwFlags = dwOutFlags; 48 | 49 | // not implemented 50 | if (pu64DevicePosition) 51 | { 52 | *pu64DevicePosition = 0; 53 | } 54 | 55 | if (pu64QPCPosition) 56 | { 57 | *pu64QPCPosition = backbufferTimestamp; 58 | } 59 | 60 | m_WaitingForBufferRelease = true; 61 | 62 | return S_OK; 63 | } 64 | 65 | HRESULT STDMETHODCALLTYPE RSAsioAudioCaptureClient::ReleaseBuffer(UINT32 NumFramesRead) 66 | { 67 | std::lock_guard g(m_mutex); 68 | 69 | if (!m_WaitingForBufferRelease) 70 | return AUDCLNT_E_OUT_OF_ORDER; 71 | 72 | m_WaitingForBufferRelease = false; 73 | 74 | return S_OK; 75 | } 76 | 77 | HRESULT STDMETHODCALLTYPE RSAsioAudioCaptureClient::GetNextPacketSize(UINT32 *pNumFramesInNextPacket) 78 | { 79 | rslog::info_ts() << __FUNCTION__ << std::endl; 80 | 81 | if (!pNumFramesInNextPacket) 82 | return E_POINTER; 83 | 84 | *pNumFramesInNextPacket = m_AsioAudioClient.GetBufferNumFrames(); 85 | 86 | return S_OK; 87 | } 88 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioAudioCaptureClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RSAsioAudioClientServiceBase.h" 4 | 5 | class RSAsioAudioCaptureClient : public RSAsioAudioClientServiceBase 6 | { 7 | public: 8 | RSAsioAudioCaptureClient(RSAsioAudioClient& asioAudioClient); 9 | RSAsioAudioCaptureClient(const RSAsioAudioCaptureClient&) = delete; 10 | RSAsioAudioCaptureClient(RSAsioAudioCaptureClient&&) = delete; 11 | 12 | virtual HRESULT STDMETHODCALLTYPE GetBuffer(BYTE **ppData, UINT32 *pNumFramesToRead, DWORD *pdwFlags, UINT64 *pu64DevicePosition, UINT64 *pu64QPCPosition) override; 13 | virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer(UINT32 NumFramesRead) override; 14 | virtual HRESULT STDMETHODCALLTYPE GetNextPacketSize(UINT32 *pNumFramesInNextPacket) override; 15 | 16 | private: 17 | bool m_WaitingForBufferRelease = false; 18 | 19 | std::mutex m_mutex; 20 | }; 21 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioAudioClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | #include "AsioSharedHost.h" 5 | #include "MyUnknown.h" 6 | 7 | class AsioSharedHost; 8 | class RSAsioDevice; 9 | class RSAsioAudioRenderClient; 10 | class RSAsioAudioCaptureClient; 11 | 12 | class RSAsioAudioClient : public ComBaseUnknown, protected IAsioBufferSwitchListener 13 | { 14 | public: 15 | RSAsioAudioClient(RSAsioDevice& asioDevice); 16 | RSAsioAudioClient(const RSAsioAudioClient&) = delete; 17 | RSAsioAudioClient(RSAsioAudioClient&&) = delete; 18 | virtual ~RSAsioAudioClient(); 19 | 20 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override; 21 | 22 | // IAudioClient 23 | virtual HRESULT STDMETHODCALLTYPE Initialize(AUDCLNT_SHAREMODE ShareMode, DWORD StreamFlags, REFERENCE_TIME hnsBufferDuration, REFERENCE_TIME hnsPeriodicity, const WAVEFORMATEX *pFormat, LPCGUID AudioSessionGuid) override; 24 | virtual HRESULT STDMETHODCALLTYPE GetBufferSize(UINT32 *pNumBufferFrames) override; 25 | virtual HRESULT STDMETHODCALLTYPE GetStreamLatency(REFERENCE_TIME *phnsLatency) override; 26 | virtual HRESULT STDMETHODCALLTYPE GetCurrentPadding(UINT32 *pNumPaddingFrames) override; 27 | virtual HRESULT STDMETHODCALLTYPE IsFormatSupported(AUDCLNT_SHAREMODE ShareMode, const WAVEFORMATEX *pFormat, WAVEFORMATEX **ppClosestMatch) override; 28 | virtual HRESULT STDMETHODCALLTYPE GetMixFormat(WAVEFORMATEX **ppDeviceFormat) override; 29 | virtual HRESULT STDMETHODCALLTYPE GetDevicePeriod(REFERENCE_TIME *phnsDefaultDevicePeriod, REFERENCE_TIME *phnsMinimumDevicePeriod) override; 30 | virtual HRESULT STDMETHODCALLTYPE Start() override; 31 | virtual HRESULT STDMETHODCALLTYPE Stop() override; 32 | virtual HRESULT STDMETHODCALLTYPE Reset() override; 33 | virtual HRESULT STDMETHODCALLTYPE SetEventHandle(HANDLE eventHandle) override; 34 | virtual HRESULT STDMETHODCALLTYPE GetService(REFIID riid, void **ppv) override; 35 | 36 | // IAudioClient2 37 | virtual HRESULT STDMETHODCALLTYPE IsOffloadCapable(AUDIO_STREAM_CATEGORY Category, BOOL *pbOffloadCapable) override; 38 | virtual HRESULT STDMETHODCALLTYPE SetClientProperties(const AudioClientProperties *pProperties) override; 39 | virtual HRESULT STDMETHODCALLTYPE GetBufferSizeLimits(const WAVEFORMATEX *pFormat, BOOL bEventDriven, REFERENCE_TIME *phnsMinBufferDuration, REFERENCE_TIME *phnsMaxBufferDuration) override; 40 | 41 | // IAudioClient3 42 | virtual HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod(const WAVEFORMATEX *pFormat, UINT32 *pDefaultPeriodInFrames, UINT32 *pFundamentalPeriodInFrames, UINT32 *pMinPeriodInFrames, UINT32 *pMaxPeriodInFrames) override; 43 | virtual HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod(WAVEFORMATEX **ppFormat, UINT32 *pCurrentPeriodInFrames) override; 44 | virtual HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream(DWORD StreamFlags, UINT32 PeriodInFrames, const WAVEFORMATEX *pFormat, LPCGUID AudioSessionGuid) override; 45 | 46 | RSAsioDevice& GetAsioDevice() { return m_AsioDevice; } 47 | 48 | std::vector& GetBackBuffer() { return m_backBuffer; } 49 | DWORD GetBufferNumFrames() const { return m_bufferNumFrames; } 50 | void SwapBuffers(); 51 | 52 | protected: 53 | virtual void OnAsioBufferSwitch(unsigned buffIdx) override; 54 | 55 | private: 56 | void UpdateChannelMap(); 57 | 58 | RSAsioDevice& m_AsioDevice; 59 | AsioSharedHost& m_AsioSharedHost; 60 | 61 | WAVEFORMATEXTENSIBLE m_WaveFormat; 62 | bool m_WaveFormatIsFloat = false; 63 | 64 | bool m_IsInitialized = false; 65 | bool m_IsStarted = false; 66 | bool m_UsingEventHandle = false; 67 | HANDLE m_EventHandle = NULL; 68 | 69 | RSAsioAudioRenderClient* m_RenderClient = nullptr; 70 | RSAsioAudioCaptureClient* m_CaptureClient = nullptr; 71 | 72 | std::vector m_frontBuffer; 73 | std::vector m_backBuffer; 74 | bool m_BuffersWereSwapped = false; 75 | bool m_IsFirstBuffer = false; 76 | 77 | unsigned m_dbgNumBufferSwitches; 78 | 79 | std::mutex m_bufferMutex; 80 | std::mutex m_controlMutex; 81 | DWORD m_bufferNumFrames; 82 | 83 | // for output: for each asio output, which wasapi channel data to copy from 84 | // for input: for each wasapi channel, which asio channel to read from 85 | std::vector m_ChannelMap; 86 | 87 | // this is used for ... hacks 88 | MyUnknown* m_MyUnknown = nullptr; 89 | }; -------------------------------------------------------------------------------- /RS_ASIO/RSAsioAudioClientServiceBase.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "RSAsioAudioClientServiceBase.h" 3 | #include "RSAsioAudioClient.h" 4 | #include "RSAsioDevice.h" 5 | 6 | template 7 | RSAsioAudioClientServiceBase::RSAsioAudioClientServiceBase(RSAsioAudioClient& asioAudioClient, bool isOutput) 8 | : m_AsioAudioClient(asioAudioClient) 9 | , m_NewBufferWaiting(isOutput) // output can immediately fill the backbuffer, input needs to wait for new buffer 10 | { 11 | 12 | } 13 | 14 | template 15 | RSAsioAudioClientServiceBase::~RSAsioAudioClientServiceBase() 16 | { 17 | 18 | } 19 | 20 | template 21 | void RSAsioAudioClientServiceBase::NotifyNewBuffer() 22 | { 23 | std::lock_guard g(m_bufferMutex); 24 | 25 | LARGE_INTEGER li; 26 | 27 | if (QueryPerformanceCounter(&li)) 28 | m_NewBufferPerfCounter = li.QuadPart; 29 | else 30 | m_NewBufferPerfCounter = 0; 31 | 32 | m_DataDiscontinuityFlag |= m_NewBufferWaiting; 33 | m_NewBufferWaiting = true; 34 | 35 | if (m_DataDiscontinuityFlag) 36 | { 37 | ++m_NumSequentialDiscontinuities; 38 | } 39 | else 40 | { 41 | if (m_NumSequentialDiscontinuities >= 2 && m_IgnoreDiscontinuityLoggingCountdown == 0) 42 | { 43 | rslog::info_ts() << m_AsioAudioClient.GetAsioDevice().GetIdRef() << " recovered from " << std::dec << m_NumSequentialDiscontinuities << " consecutive xruns. Ignoring for some time." << std::endl; 44 | m_IgnoreDiscontinuityLoggingCountdown = 1000; 45 | } 46 | m_NumSequentialDiscontinuities = 0; 47 | } 48 | 49 | if (m_IgnoreDiscontinuityLoggingCountdown == 0) 50 | { 51 | if (m_NumSequentialDiscontinuities == 1) 52 | { 53 | rslog::info_ts() << m_AsioAudioClient.GetAsioDevice().GetIdRef() << " xrun" << std::endl; 54 | } 55 | else if (m_NumSequentialDiscontinuities == 100) 56 | { 57 | rslog::info_ts() << m_AsioAudioClient.GetAsioDevice().GetIdRef() << " " << std::dec << m_NumSequentialDiscontinuities << " consecute xruns. Ignoring the next ones." << std::endl; 58 | } 59 | } 60 | else 61 | { 62 | --m_IgnoreDiscontinuityLoggingCountdown; 63 | } 64 | } 65 | 66 | template 67 | bool RSAsioAudioClientServiceBase::HasNewBufferWaiting() const 68 | { 69 | std::lock_guard g(m_bufferMutex); 70 | 71 | return m_NewBufferWaiting; 72 | } 73 | 74 | template 75 | BYTE* RSAsioAudioClientServiceBase::GetBackbufferIfPending(bool resetPendingFlag, UINT64* outOptionalPerfCounter, bool* outOptionalIsDiscontinuity) 76 | { 77 | std::lock_guard g(m_bufferMutex); 78 | if (!m_NewBufferWaiting) 79 | { 80 | if (outOptionalPerfCounter) 81 | { 82 | *outOptionalPerfCounter = 0; 83 | } 84 | if (outOptionalIsDiscontinuity) 85 | { 86 | *outOptionalIsDiscontinuity = true; 87 | } 88 | 89 | return nullptr; 90 | } 91 | 92 | std::vector& buffer = m_AsioAudioClient.GetBackBuffer(); 93 | if (outOptionalPerfCounter) 94 | { 95 | *outOptionalPerfCounter = m_NewBufferPerfCounter; 96 | } 97 | if (outOptionalIsDiscontinuity) 98 | { 99 | *outOptionalIsDiscontinuity = m_DataDiscontinuityFlag; 100 | } 101 | if (resetPendingFlag) 102 | { 103 | m_NewBufferWaiting = false; 104 | m_DataDiscontinuityFlag = false; 105 | } 106 | return buffer.data(); 107 | } 108 | 109 | 110 | 111 | template class RSAsioAudioClientServiceBase; 112 | template class RSAsioAudioClientServiceBase; 113 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioAudioClientServiceBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class RSAsioAudioClient; 6 | 7 | template 8 | class RSAsioAudioClientServiceBase : public ComBaseUnknown 9 | { 10 | public: 11 | RSAsioAudioClientServiceBase(RSAsioAudioClient& asioAudioClient, bool isOutput); 12 | RSAsioAudioClientServiceBase(const RSAsioAudioClientServiceBase&) = delete; 13 | RSAsioAudioClientServiceBase(RSAsioAudioClientServiceBase&&) = delete; 14 | virtual ~RSAsioAudioClientServiceBase(); 15 | 16 | void NotifyNewBuffer(); 17 | bool HasNewBufferWaiting() const; 18 | 19 | protected: 20 | RSAsioAudioClient& m_AsioAudioClient; 21 | 22 | BYTE* GetBackbufferIfPending(bool resetPendingFlag, UINT64* outOptionalPerfCounter, bool* outOptionalIsDiscontinuity); 23 | 24 | private: 25 | mutable std::mutex m_bufferMutex; 26 | 27 | bool m_NewBufferWaiting; // set in ctor 28 | UINT64 m_NewBufferPerfCounter = 0; 29 | 30 | bool m_DataDiscontinuityFlag = false; 31 | 32 | unsigned m_NumSequentialDiscontinuities = 0; 33 | unsigned m_IgnoreDiscontinuityLoggingCountdown = 0; 34 | }; 35 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioAudioEndpointVolume.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "RSAsioAudioEndpointVolume.h" 3 | #include "RSAsioDevice.h" 4 | 5 | RSAsioAudioEndpointVolume::RSAsioAudioEndpointVolume(RSAsioDevice& asioDevice) 6 | : m_AsioDevice(asioDevice) 7 | { 8 | 9 | } 10 | 11 | RSAsioAudioEndpointVolume::~RSAsioAudioEndpointVolume() 12 | { 13 | } 14 | 15 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::RegisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) 16 | { 17 | return E_NOTIMPL; 18 | } 19 | 20 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::UnregisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) 21 | { 22 | return E_NOTIMPL; 23 | } 24 | 25 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::GetChannelCount(UINT *pnChannelCount) 26 | { 27 | return E_NOTIMPL; 28 | } 29 | 30 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::SetMasterVolumeLevel(float fLevelDB, LPCGUID pguidEventContext) 31 | { 32 | return E_NOTIMPL; 33 | } 34 | 35 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::SetMasterVolumeLevelScalar(float fLevel, LPCGUID pguidEventContext) 36 | { 37 | if (fLevel < 0.0f || fLevel > 1.0f) 38 | return E_INVALIDARG; 39 | 40 | if (!m_AsioDevice.SetEndpointVolumeLevelScalar(fLevel)) 41 | return E_FAIL; 42 | 43 | return S_OK; 44 | } 45 | 46 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::GetMasterVolumeLevel(float *pfLevelDB) 47 | { 48 | return E_NOTIMPL; 49 | } 50 | 51 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::GetMasterVolumeLevelScalar(float *pfLevel) 52 | { 53 | if (!pfLevel) 54 | return E_POINTER; 55 | 56 | *pfLevel = m_AsioDevice.GetEndpointVolumeLevelScalar(); 57 | 58 | return S_OK; 59 | } 60 | 61 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::SetChannelVolumeLevel(UINT nChannel, float fLevelDB, LPCGUID pguidEventContext) 62 | { 63 | return E_NOTIMPL; 64 | } 65 | 66 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::SetChannelVolumeLevelScalar(UINT nChannel, float fLevel, LPCGUID pguidEventContext) 67 | { 68 | return E_NOTIMPL; 69 | } 70 | 71 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::GetChannelVolumeLevel(UINT nChannel, float *pfLevelDB) 72 | { 73 | return E_NOTIMPL; 74 | } 75 | 76 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::GetChannelVolumeLevelScalar(UINT nChannel, float *pfLevel) 77 | { 78 | return E_NOTIMPL; 79 | } 80 | 81 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::SetMute(BOOL bMute, LPCGUID pguidEventContext) 82 | { 83 | return E_NOTIMPL; 84 | } 85 | 86 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::GetMute(BOOL *pbMute) 87 | { 88 | return E_NOTIMPL; 89 | } 90 | 91 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::GetVolumeStepInfo(UINT *pnStep, UINT *pnStepCount) 92 | { 93 | return E_NOTIMPL; 94 | } 95 | 96 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::VolumeStepUp(LPCGUID pguidEventContext) 97 | { 98 | return E_NOTIMPL; 99 | } 100 | 101 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::VolumeStepDown(LPCGUID pguidEventContext) 102 | { 103 | return E_NOTIMPL; 104 | } 105 | 106 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::QueryHardwareSupport(DWORD *pdwHardwareSupportMask) 107 | { 108 | return E_NOTIMPL; 109 | } 110 | 111 | HRESULT STDMETHODCALLTYPE RSAsioAudioEndpointVolume::GetVolumeRange(float *pflVolumeMindB, float *pflVolumeMaxdB, float *pflVolumeIncrementdB) 112 | { 113 | return E_NOTIMPL; 114 | } 115 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioAudioEndpointVolume.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class RSAsioDevice; 6 | 7 | class RSAsioAudioEndpointVolume : public ComBaseUnknown 8 | { 9 | public: 10 | RSAsioAudioEndpointVolume(RSAsioDevice& asioDevice); 11 | RSAsioAudioEndpointVolume(const RSAsioAudioEndpointVolume&) = delete; 12 | RSAsioAudioEndpointVolume(RSAsioAudioEndpointVolume&&) = delete; 13 | virtual ~RSAsioAudioEndpointVolume(); 14 | 15 | virtual HRESULT STDMETHODCALLTYPE RegisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override; 16 | virtual HRESULT STDMETHODCALLTYPE UnregisterControlChangeNotify(IAudioEndpointVolumeCallback *pNotify) override; 17 | virtual HRESULT STDMETHODCALLTYPE GetChannelCount(UINT *pnChannelCount) override; 18 | virtual HRESULT STDMETHODCALLTYPE SetMasterVolumeLevel(float fLevelDB, LPCGUID pguidEventContext) override; 19 | virtual HRESULT STDMETHODCALLTYPE SetMasterVolumeLevelScalar(float fLevel, LPCGUID pguidEventContext) override; 20 | virtual HRESULT STDMETHODCALLTYPE GetMasterVolumeLevel(float *pfLevelDB) override; 21 | virtual HRESULT STDMETHODCALLTYPE GetMasterVolumeLevelScalar(float *pfLevel) override; 22 | virtual HRESULT STDMETHODCALLTYPE SetChannelVolumeLevel(UINT nChannel, float fLevelDB, LPCGUID pguidEventContext) override; 23 | virtual HRESULT STDMETHODCALLTYPE SetChannelVolumeLevelScalar(UINT nChannel, float fLevel, LPCGUID pguidEventContext) override; 24 | virtual HRESULT STDMETHODCALLTYPE GetChannelVolumeLevel(UINT nChannel, float *pfLevelDB) override; 25 | virtual HRESULT STDMETHODCALLTYPE GetChannelVolumeLevelScalar(UINT nChannel, float *pfLevel) override; 26 | virtual HRESULT STDMETHODCALLTYPE SetMute(BOOL bMute, LPCGUID pguidEventContext) override; 27 | virtual HRESULT STDMETHODCALLTYPE GetMute(BOOL *pbMute) override; 28 | virtual HRESULT STDMETHODCALLTYPE GetVolumeStepInfo(UINT *pnStep, UINT *pnStepCount) override; 29 | virtual HRESULT STDMETHODCALLTYPE VolumeStepUp(LPCGUID pguidEventContext) override; 30 | virtual HRESULT STDMETHODCALLTYPE VolumeStepDown(LPCGUID pguidEventContext) override; 31 | virtual HRESULT STDMETHODCALLTYPE QueryHardwareSupport(DWORD *pdwHardwareSupportMask) override; 32 | virtual HRESULT STDMETHODCALLTYPE GetVolumeRange(float *pflVolumeMindB, float *pflVolumeMaxdB, float *pflVolumeIncrementdB) override; 33 | 34 | private: 35 | RSAsioDevice& m_AsioDevice; 36 | }; -------------------------------------------------------------------------------- /RS_ASIO/RSAsioAudioRenderClient.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "RSAsioAudioRenderClient.h" 3 | #include "RSAsioAudioClient.h" 4 | #include "RSAsioDevice.h" 5 | 6 | RSAsioAudioRenderClient::RSAsioAudioRenderClient(RSAsioAudioClient& asioAudioClient) 7 | : RSAsioAudioClientServiceBase(asioAudioClient, /*isOutput =*/true) 8 | { 9 | } 10 | 11 | HRESULT STDMETHODCALLTYPE RSAsioAudioRenderClient::GetBuffer(UINT32 NumFramesRequested, BYTE **ppData) 12 | { 13 | std::lock_guard g(m_mutex); 14 | 15 | if (!ppData) 16 | return E_POINTER; 17 | 18 | if (!m_AsioAudioClient.GetAsioDevice().GetAsioHost().IsValid()) 19 | return AUDCLNT_E_DEVICE_INVALIDATED; 20 | 21 | if (m_WaitingForBufferRelease) 22 | return AUDCLNT_E_OUT_OF_ORDER; 23 | 24 | if (NumFramesRequested != m_AsioAudioClient.GetBufferNumFrames()) 25 | return AUDCLNT_E_BUFFER_SIZE_ERROR; 26 | 27 | bool isDiscontinuity = true; 28 | UINT64 backbufferTimestamp = 0; 29 | BYTE* backbuffer = GetBackbufferIfPending(true, &backbufferTimestamp, &isDiscontinuity); 30 | 31 | if (!backbuffer) 32 | { 33 | *ppData = nullptr; 34 | return AUDCLNT_E_BUFFER_ERROR; 35 | } 36 | 37 | *ppData = backbuffer; 38 | m_WaitingForBufferRelease = true; 39 | 40 | return S_OK; 41 | } 42 | 43 | HRESULT STDMETHODCALLTYPE RSAsioAudioRenderClient::ReleaseBuffer(UINT32 NumFramesWritten, DWORD dwFlags) 44 | { 45 | std::lock_guard g(m_mutex); 46 | 47 | if (!m_AsioAudioClient.GetAsioDevice().GetAsioHost().IsValid()) 48 | return AUDCLNT_E_DEVICE_INVALIDATED; 49 | 50 | if (!m_WaitingForBufferRelease) 51 | return AUDCLNT_E_OUT_OF_ORDER; 52 | 53 | const DWORD expectedNumFrames = m_AsioAudioClient.GetBufferNumFrames(); 54 | 55 | if (NumFramesWritten > expectedNumFrames) 56 | return AUDCLNT_E_INVALID_SIZE; 57 | if (NumFramesWritten != expectedNumFrames) 58 | return AUDCLNT_E_BUFFER_SIZE_ERROR; 59 | 60 | m_WaitingForBufferRelease = false; 61 | 62 | // move the data we've put in the backbuffer to the frontbuffer for output 63 | m_AsioAudioClient.SwapBuffers(); 64 | 65 | return S_OK; 66 | } 67 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioAudioRenderClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RSAsioAudioClientServiceBase.h" 4 | 5 | class RSAsioAudioRenderClient : public RSAsioAudioClientServiceBase 6 | { 7 | public: 8 | RSAsioAudioRenderClient(RSAsioAudioClient& asioAudioClient); 9 | RSAsioAudioRenderClient(const RSAsioAudioRenderClient&) = delete; 10 | RSAsioAudioRenderClient(RSAsioAudioRenderClient&&) = delete; 11 | 12 | virtual HRESULT STDMETHODCALLTYPE GetBuffer(UINT32 NumFramesRequested, BYTE **ppData) override; 13 | virtual HRESULT STDMETHODCALLTYPE ReleaseBuffer(UINT32 NumFramesWritten, DWORD dwFlags) override; 14 | 15 | private: 16 | bool m_WaitingForBufferRelease = false; 17 | 18 | std::mutex m_mutex; 19 | }; 20 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioDevice.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "AsioSharedHost.h" 3 | #include "RSAsioDevice.h" 4 | #include "RSAsioDevicePropertyStore.h" 5 | #include "RSAsioAudioClient.h" 6 | #include "RSAsioAudioEndpointVolume.h" 7 | 8 | RSAsioDevice::RSAsioDevice(AsioSharedHost& asioSharedHost, const std::wstring& id, const Config& config) 9 | : m_AsioSharedHost(asioSharedHost) 10 | , m_Id(id) 11 | , m_Config(config) 12 | { 13 | m_AsioSharedHost.AddRef(); 14 | } 15 | 16 | RSAsioDevice::~RSAsioDevice() 17 | { 18 | m_AsioSharedHost.Release(); 19 | } 20 | 21 | HRESULT STDMETHODCALLTYPE RSAsioDevice::QueryInterface(REFIID riid, void **ppvObject) 22 | { 23 | if (!ppvObject) 24 | return E_POINTER; 25 | 26 | if (riid == __uuidof(IMMEndpoint)) 27 | { 28 | *ppvObject = new RSAsioEndpoint(*this, m_Config.isOutput ? eRender : eCapture); 29 | return S_OK; 30 | } 31 | 32 | return ComBaseUnknown::QueryInterface(riid, ppvObject); 33 | } 34 | 35 | HRESULT STDMETHODCALLTYPE RSAsioDevice::Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface) 36 | { 37 | if (!ppInterface) 38 | return E_POINTER; 39 | 40 | if (iid == __uuidof(IAudioClient) || iid == __uuidof(IAudioClient2) || iid == __uuidof(IAudioClient3)) 41 | { 42 | *ppInterface = new RSAsioAudioClient(*this); 43 | return S_OK; 44 | } 45 | else if (iid == __uuidof(IAudioEndpointVolume)) 46 | { 47 | *ppInterface = new RSAsioAudioEndpointVolume(*this); 48 | return S_OK; 49 | } 50 | 51 | return E_NOINTERFACE; 52 | } 53 | 54 | HRESULT STDMETHODCALLTYPE RSAsioDevice::OpenPropertyStore(DWORD stgmAccess, IPropertyStore **ppProperties) 55 | { 56 | if (stgmAccess != STGM_READ) 57 | return E_FAIL; 58 | 59 | *ppProperties = new RSAsioDevicePropertyStore(*this); 60 | 61 | return S_OK; 62 | } 63 | 64 | HRESULT STDMETHODCALLTYPE RSAsioDevice::GetId(LPWSTR *ppstrId) 65 | { 66 | if (!ppstrId) 67 | return E_POINTER; 68 | 69 | const size_t numCharacters = m_Id.length(); 70 | const size_t numBytes = (numCharacters + 1) * sizeof(*m_Id.c_str()); 71 | LPWSTR str = (LPWSTR)CoTaskMemAlloc(numBytes); 72 | memcpy(str, m_Id.c_str(), numBytes); 73 | 74 | *ppstrId = str; 75 | 76 | return S_OK; 77 | } 78 | 79 | HRESULT STDMETHODCALLTYPE RSAsioDevice::GetState(DWORD *pdwState) 80 | { 81 | if (!pdwState) 82 | return E_POINTER; 83 | 84 | *pdwState = DEVICE_STATE_ACTIVE; 85 | 86 | return S_OK; 87 | } 88 | 89 | AsioSharedHost& RSAsioDevice::GetAsioHost() 90 | { 91 | return m_AsioSharedHost; 92 | } 93 | 94 | unsigned RSAsioDevice::GetNumWasapiChannels() const 95 | { 96 | return std::max(m_Config.numAsioChannels, 2); 97 | } 98 | 99 | bool RSAsioDevice::SetEndpointVolumeLevelScalar(float fLevel) 100 | { 101 | if (!m_Config.enableSoftwareEndpointVolmeControl) 102 | return false; 103 | m_SoftwareEndpointVolumeScalar = fLevel; 104 | return true; 105 | } 106 | 107 | float RSAsioDevice::GetEndpointVolumeLevelScalar() const 108 | { 109 | return m_SoftwareEndpointVolumeScalar; 110 | } 111 | 112 | bool RSAsioDevice::SetMasterVolumeLevelScalar(float fLevel) 113 | { 114 | if (!m_Config.enableSoftwareMasterVolumeControl) 115 | return false; 116 | m_SoftwareMasterVolumeScalar = fLevel; 117 | return true; 118 | } 119 | 120 | float RSAsioDevice::GetMasterVolumeLevelScalar() const 121 | { 122 | return m_SoftwareMasterVolumeScalar; 123 | } 124 | 125 | bool RSAsioDevice::GetSoftwareVolumeScalar(float* pOutScalar) const 126 | { 127 | if (!m_Config.enableSoftwareEndpointVolmeControl && !m_Config.enableSoftwareMasterVolumeControl) 128 | return false; 129 | 130 | if (pOutScalar) 131 | { 132 | float volumeScalar = 1.0f; 133 | 134 | if (m_Config.enableSoftwareEndpointVolmeControl) 135 | volumeScalar *= m_SoftwareEndpointVolumeScalar; 136 | 137 | if (m_Config.enableSoftwareMasterVolumeControl) 138 | volumeScalar *= m_SoftwareMasterVolumeScalar; 139 | 140 | *pOutScalar = volumeScalar; 141 | } 142 | 143 | return true; 144 | } 145 | 146 | 147 | 148 | RSAsioEndpoint::RSAsioEndpoint(RSAsioDevice& asioDevice, EDataFlow dataFlow) 149 | : m_AsioDevice(asioDevice) 150 | , m_DataFlow(dataFlow) 151 | { 152 | m_AsioDevice.AddRef(); 153 | } 154 | 155 | RSAsioEndpoint::~RSAsioEndpoint() 156 | { 157 | m_AsioDevice.Release(); 158 | } 159 | 160 | HRESULT RSAsioEndpoint::GetDataFlow(EDataFlow *pDataFlow) 161 | { 162 | if (!pDataFlow) 163 | return E_POINTER; 164 | 165 | *pDataFlow = m_DataFlow; 166 | 167 | return S_OK; 168 | } -------------------------------------------------------------------------------- /RS_ASIO/RSAsioDevice.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | #include "AsioSharedHost.h" 5 | 6 | class RSAsioDevice : public ComBaseUnknown 7 | { 8 | public: 9 | enum BufferSizeMode 10 | { 11 | BufferSizeMode_Host, 12 | BufferSizeMode_Driver, 13 | BufferSizeMode_Custom, 14 | }; 15 | 16 | struct Config 17 | { 18 | bool isOutput; 19 | unsigned baseAsioChannelNumber = 0; 20 | std::optional altOutputBaseAsioChannelNumber; 21 | unsigned numAsioChannels = 1; 22 | BufferSizeMode bufferSizeMode = BufferSizeMode_Driver; 23 | unsigned customBufferSize = 128; 24 | bool enableSoftwareEndpointVolmeControl = true; 25 | bool enableSoftwareMasterVolumeControl = true; 26 | bool isMicrophone = false; 27 | std::optional enableRefCountHack; 28 | }; 29 | 30 | public: 31 | RSAsioDevice(AsioSharedHost& asioSharedHost, const std::wstring& id, const Config& config); 32 | RSAsioDevice(const RSAsioDevice&) = delete; 33 | RSAsioDevice(RSAsioDevice&&) = delete; 34 | virtual ~RSAsioDevice(); 35 | 36 | virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); 37 | 38 | virtual HRESULT STDMETHODCALLTYPE Activate(REFIID iid, DWORD dwClsCtx, PROPVARIANT *pActivationParams, void **ppInterface) override; 39 | virtual HRESULT STDMETHODCALLTYPE OpenPropertyStore(DWORD stgmAccess, IPropertyStore **ppProperties) override; 40 | virtual HRESULT STDMETHODCALLTYPE GetId(LPWSTR *ppstrId) override; 41 | virtual HRESULT STDMETHODCALLTYPE GetState(DWORD *pdwState) override; 42 | 43 | AsioSharedHost& GetAsioHost(); 44 | const Config& GetConfig() const { return m_Config; } 45 | 46 | unsigned GetNumWasapiChannels() const; 47 | 48 | bool SetEndpointVolumeLevelScalar(float fLevel); 49 | float GetEndpointVolumeLevelScalar() const; 50 | 51 | bool SetMasterVolumeLevelScalar(float fLevel); 52 | float GetMasterVolumeLevelScalar() const; 53 | 54 | bool GetSoftwareVolumeScalar(float* pOutScalar) const; 55 | 56 | const std::wstring& GetIdRef() const { return m_Id; } 57 | 58 | private: 59 | std::wstring m_Id; 60 | Config m_Config; 61 | 62 | AsioSharedHost& m_AsioSharedHost; 63 | unsigned m_BaseChannelNumber; 64 | unsigned m_NumChannels; 65 | 66 | float m_SoftwareEndpointVolumeScalar = 1.0f; 67 | float m_SoftwareMasterVolumeScalar = 1.0f; 68 | }; 69 | 70 | class RSAsioEndpoint : public ComBaseUnknown 71 | { 72 | public: 73 | RSAsioEndpoint(RSAsioDevice& asioDevice, EDataFlow dataFlow); 74 | RSAsioEndpoint(const RSAsioEndpoint&) = delete; 75 | RSAsioEndpoint(RSAsioEndpoint&&) = delete; 76 | virtual ~RSAsioEndpoint(); 77 | 78 | virtual HRESULT STDMETHODCALLTYPE GetDataFlow(EDataFlow *pDataFlow); 79 | 80 | private: 81 | RSAsioDevice& m_AsioDevice; 82 | EDataFlow m_DataFlow; 83 | }; -------------------------------------------------------------------------------- /RS_ASIO/RSAsioDeviceEnum.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "RSAsioDeviceEnum.h" 3 | #include "RSAsioDevice.h" 4 | #include "AsioHelpers.h" 5 | #include "AsioSharedHost.h" 6 | 7 | void RSAsioDeviceEnum::SetConfig(const RSAsioConfig& config) 8 | { 9 | m_DeviceListNeedsUpdate = true; 10 | m_Config = config; 11 | } 12 | 13 | void RSAsioDeviceEnum::UpdateAvailableDevices() 14 | { 15 | m_DeviceListNeedsUpdate = false; 16 | 17 | ClearAll(); 18 | 19 | std::vector asioDriversInfo = AsioHelpers::FindDrivers(); 20 | 21 | std::vector createdAsioHosts; 22 | auto fnFindOrCreateAsioHost = [&](const std::string& name) -> AsioSharedHost* 23 | { 24 | for (const auto& info : asioDriversInfo) 25 | { 26 | if (info.Name == name) 27 | { 28 | // check if we already have this host created 29 | for (const auto& host : createdAsioHosts) 30 | { 31 | if (host->GetAsioDllPath() == info.DllPath) 32 | { 33 | host->AddRef(); 34 | return host; 35 | } 36 | } 37 | 38 | // if we got here, we don't have it created yet and we'll create it now 39 | AsioSharedHost* newHost = AsioHelpers::CreateAsioHost(info); 40 | if (newHost) 41 | { 42 | createdAsioHosts.push_back(newHost); 43 | newHost->AddRef(); 44 | return newHost; 45 | } 46 | } 47 | } 48 | 49 | return nullptr; 50 | }; 51 | 52 | if (!m_Config.output.asioDriverName.empty()) 53 | { 54 | rslog::info_ts() << __FUNCTION__ " - " << "output requesting ASIO driver: " << m_Config.output.asioDriverName << std::endl; 55 | 56 | AsioSharedHost* host = fnFindOrCreateAsioHost(m_Config.output.asioDriverName); 57 | if (host) 58 | { 59 | RSAsioDevice::Config config; 60 | config.isOutput = true; 61 | config.baseAsioChannelNumber = m_Config.output.baseChannel; 62 | config.altOutputBaseAsioChannelNumber = m_Config.output.altBaseChannel; 63 | config.numAsioChannels = m_Config.output.numChannels; 64 | config.bufferSizeMode = m_Config.bufferMode; 65 | config.customBufferSize = m_Config.customBufferSize; 66 | config.enableSoftwareEndpointVolmeControl = m_Config.output.enableSoftwareEndpointVolumeControl; 67 | config.enableSoftwareMasterVolumeControl = m_Config.output.enableSoftwareMasterVolumeControl; 68 | config.enableRefCountHack = m_Config.output.enableRefCountHack; 69 | 70 | auto device = new RSAsioDevice(*host, L"{ASIO Out}", config); 71 | device->SetMasterVolumeLevelScalar((float)m_Config.output.softwareMasterVolumePercent / 100.0f); 72 | m_RenderDevices.AddDevice(device); 73 | device->Release(); 74 | device = nullptr; 75 | 76 | rslog::info_ts() << __FUNCTION__ " - OK" << std::endl; 77 | host->Release(); 78 | } 79 | else 80 | { 81 | rslog::error_ts() << __FUNCTION__ " - " << "failed." << std::endl; 82 | } 83 | } 84 | 85 | int inputIdx = 0; 86 | for (const RSAsioInputConfig& inputCfg : m_Config.inputs) 87 | { 88 | if (!inputCfg.asioDriverName.empty()) 89 | { 90 | rslog::info_ts() << __FUNCTION__ " - " << "input[" << inputIdx << "] requesting ASIO driver: " << inputCfg.asioDriverName << std::endl; 91 | 92 | AsioSharedHost* host = fnFindOrCreateAsioHost(inputCfg.asioDriverName); 93 | if (host) 94 | { 95 | wchar_t id[64]{}; 96 | swprintf(id, 63, L"{ASIO IN %i}", inputIdx); 97 | 98 | RSAsioDevice::Config config; 99 | config.isOutput = false; 100 | config.baseAsioChannelNumber = inputCfg.useChannel; 101 | config.numAsioChannels = 1; 102 | config.bufferSizeMode = m_Config.bufferMode; 103 | config.customBufferSize = m_Config.customBufferSize; 104 | config.enableSoftwareEndpointVolmeControl = inputCfg.enableSoftwareEndpointVolumeControl; 105 | config.enableSoftwareMasterVolumeControl = inputCfg.enableSoftwareMasterVolumeControl; 106 | config.isMicrophone = inputCfg.microphone; 107 | config.enableRefCountHack = inputCfg.enableRefCountHack; 108 | 109 | auto device = new RSAsioDevice(*host, id, config); 110 | device->SetMasterVolumeLevelScalar((float)inputCfg.softwareMasterVolumePercent / 100.0f); 111 | m_CaptureDevices.AddDevice(device); 112 | device->Release(); 113 | device = nullptr; 114 | 115 | rslog::info_ts() << __FUNCTION__ " - OK" << std::endl; 116 | host->Release(); 117 | } 118 | else 119 | { 120 | rslog::error_ts() << __FUNCTION__ " - " << "failed." << std::endl; 121 | } 122 | } 123 | 124 | ++inputIdx; 125 | } 126 | 127 | // clear up 128 | for (auto host : createdAsioHosts) 129 | host->Release(); 130 | createdAsioHosts.clear(); 131 | 132 | // fill defaults 133 | for (IMMDevice* dev : m_RenderDevices) 134 | { 135 | for (IMMDevice*& defaultDev : m_DefaultRenderDevices) 136 | { 137 | defaultDev = dev; 138 | defaultDev->AddRef(); 139 | } 140 | break; 141 | } 142 | for (IMMDevice* dev : m_CaptureDevices) 143 | { 144 | for (IMMDevice*& defaultDev : m_DefaultCaptureDevices) 145 | { 146 | defaultDev = dev; 147 | defaultDev->AddRef(); 148 | } 149 | break; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioDeviceEnum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RSBaseDeviceEnum.h" 4 | #include "RSAsioDevice.h" 5 | 6 | #include 7 | 8 | struct RSAsioOutputConfig 9 | { 10 | std::string asioDriverName; 11 | unsigned baseChannel = 0; 12 | std::optional altBaseChannel; 13 | unsigned numChannels = 2; 14 | bool enableSoftwareEndpointVolumeControl = true; 15 | bool enableSoftwareMasterVolumeControl = true; 16 | int softwareMasterVolumePercent = 100; 17 | std::optional enableRefCountHack; 18 | }; 19 | 20 | struct RSAsioInputConfig 21 | { 22 | std::string asioDriverName; 23 | unsigned useChannel = 0; 24 | bool enableSoftwareEndpointVolumeControl = true; 25 | bool enableSoftwareMasterVolumeControl = true; 26 | int softwareMasterVolumePercent = 100; 27 | bool microphone = false; 28 | std::optional enableRefCountHack; 29 | }; 30 | 31 | struct RSAsioConfig 32 | { 33 | RSAsioOutputConfig output; 34 | std::array inputs; 35 | RSAsioDevice::BufferSizeMode bufferMode = RSAsioDevice::BufferSizeMode_Driver; 36 | unsigned customBufferSize = 128; 37 | }; 38 | 39 | struct RSConfig 40 | { 41 | std::optional enableWasapiOutputs = false; 42 | bool enableWasapiInputs = false; 43 | bool enableAsioOutput = false; 44 | bool enableAsioInputs = false; 45 | RSAsioConfig asioConfig; 46 | }; 47 | 48 | class RSAsioDeviceEnum : public RSBaseDeviceEnum 49 | { 50 | public: 51 | void SetConfig(const RSAsioConfig& config); 52 | 53 | protected: 54 | virtual void UpdateAvailableDevices() override; 55 | 56 | RSAsioConfig m_Config; 57 | }; 58 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioDevicePropertyStore.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "RSAsioDevicePropertyStore.h" 3 | #include "RSAsioDevice.h" 4 | #include "AsioSharedHost.h" 5 | 6 | #include 7 | 8 | RSAsioDevicePropertyStore::RSAsioDevicePropertyStore(RSAsioDevice& device) 9 | : m_AsioDevice(device) 10 | { 11 | m_AsioDevice.AddRef(); 12 | } 13 | 14 | RSAsioDevicePropertyStore::~RSAsioDevicePropertyStore() 15 | { 16 | m_AsioDevice.Release(); 17 | } 18 | 19 | HRESULT STDMETHODCALLTYPE RSAsioDevicePropertyStore::GetCount(DWORD *cProps) 20 | { 21 | if (!cProps) 22 | return E_POINTER; 23 | 24 | if (m_AsioDevice.GetConfig().isOutput) 25 | *cProps = 2; 26 | else 27 | { 28 | if (m_AsioDevice.GetConfig().isMicrophone) 29 | *cProps = 3; 30 | else 31 | *cProps = 5; 32 | } 33 | 34 | return S_OK; 35 | } 36 | 37 | HRESULT STDMETHODCALLTYPE RSAsioDevicePropertyStore::GetAt(DWORD iProp, PROPERTYKEY *pkey) 38 | { 39 | switch(iProp) 40 | { 41 | case 0: 42 | *pkey = PKEY_AudioEngine_DeviceFormat; 43 | return S_OK; 44 | case 1: 45 | *pkey = PKEY_Device_FriendlyName; 46 | return S_OK; 47 | case 2: 48 | *pkey = PKEY_AudioEndpoint_FormFactor; 49 | return S_OK; 50 | case 3: 51 | *pkey = PKEY_Device_DeviceIdHiddenKey1; 52 | return S_OK; 53 | case 4: 54 | *pkey = PKEY_Device_DeviceIdHiddenKey2; 55 | return S_OK; 56 | } 57 | 58 | return E_FAIL; 59 | } 60 | 61 | HRESULT STDMETHODCALLTYPE RSAsioDevicePropertyStore::GetValue(REFPROPERTYKEY key, PROPVARIANT *pv) 62 | { 63 | //rslog::info_ts() << __FUNCTION__ " - key: " << key << std::endl; 64 | 65 | if (!pv) 66 | return E_POINTER; 67 | 68 | pv->vt = VT_EMPTY; 69 | 70 | AsioSharedHost& asioHost = m_AsioDevice.GetAsioHost(); 71 | if (asioHost.IsValid()) 72 | { 73 | if (key == PKEY_AudioEngine_DeviceFormat) 74 | { 75 | IAsioDriver* asioDriver = asioHost.GetDriver(); 76 | 77 | long numInputChannels = 0; 78 | long numOutputChannels = 0; 79 | ASIOSampleRate sampleRate = 0.0; 80 | 81 | if (asioDriver->getChannels(&numInputChannels, &numOutputChannels) == ASE_OK 82 | && asioDriver->getSampleRate(&sampleRate) == ASE_OK) 83 | { 84 | WAVEFORMATEXTENSIBLE* wfe = (WAVEFORMATEXTENSIBLE*)CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE)); 85 | wfe->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; 86 | wfe->Format.nChannels = m_AsioDevice.GetNumWasapiChannels(); 87 | wfe->Format.wBitsPerSample = 32; 88 | wfe->Format.nSamplesPerSec = std::lround(sampleRate); 89 | wfe->Format.nBlockAlign = wfe->Format.nChannels * (wfe->Format.wBitsPerSample / 8); 90 | wfe->Format.nAvgBytesPerSec = wfe->Format.nBlockAlign * wfe->Format.nSamplesPerSec; 91 | wfe->Format.cbSize = 22; 92 | wfe->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; 93 | wfe->Samples.wSamplesPerBlock = 24; 94 | wfe->dwChannelMask = 0; 95 | 96 | for (WORD i = 0; i < wfe->Format.nChannels; ++i) 97 | { 98 | wfe->dwChannelMask = (wfe->dwChannelMask << 1) | 1; 99 | } 100 | 101 | pv->vt = VT_BLOB; 102 | pv->blob.pBlobData = (BYTE*)wfe; 103 | pv->blob.cbSize = sizeof(*wfe); 104 | } 105 | } 106 | else if (key == PKEY_Device_FriendlyName) 107 | { 108 | if (SUCCEEDED(m_AsioDevice.GetId(&pv->pwszVal))) 109 | { 110 | pv->vt = VT_LPWSTR; 111 | } 112 | } 113 | else if (key == PKEY_Device_DeviceIdHiddenKey1) 114 | { 115 | const wchar_t strDeviceId[] = L"{1}.FAKEDEVICE\\VID_12BA&PID_00FF&KS\\YOLO"; 116 | 117 | if (pv->pwszVal = (LPWSTR)CoTaskMemAlloc(sizeof(strDeviceId))) 118 | { 119 | memcpy(pv->pwszVal, strDeviceId, sizeof(strDeviceId)); 120 | pv->vt = VT_LPWSTR; 121 | } 122 | } 123 | else if (key == PKEY_Device_DeviceIdHiddenKey2) 124 | { 125 | const wchar_t strDeviceId[] = L"oem24.inf:0000000000000000:_Install_8.NTamd64:4.59.0.56775:FAKEDEVICE\\VID_12BA&PID_00FF&KS"; 126 | 127 | if (pv->pwszVal = (LPWSTR)CoTaskMemAlloc(sizeof(strDeviceId))) 128 | { 129 | memcpy(pv->pwszVal, strDeviceId, sizeof(strDeviceId)); 130 | pv->vt = VT_LPWSTR; 131 | } 132 | } 133 | else if (key == PKEY_AudioEndpoint_FormFactor) 134 | { 135 | pv->vt = VT_UI4; 136 | pv->uintVal = m_AsioDevice.GetConfig().isMicrophone ? Microphone : LineLevel; 137 | } 138 | } 139 | 140 | return S_OK; 141 | } 142 | 143 | HRESULT STDMETHODCALLTYPE RSAsioDevicePropertyStore::SetValue(REFPROPERTYKEY key, REFPROPVARIANT propvar) 144 | { 145 | rslog::info_ts() << __FUNCTION__ << std::endl; 146 | 147 | return E_NOTIMPL; 148 | } 149 | 150 | HRESULT STDMETHODCALLTYPE RSAsioDevicePropertyStore::Commit(void) 151 | { 152 | rslog::info_ts() << __FUNCTION__ << std::endl; 153 | 154 | return STG_E_ACCESSDENIED; 155 | } 156 | -------------------------------------------------------------------------------- /RS_ASIO/RSAsioDevicePropertyStore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class RSAsioDevice; 6 | 7 | class RSAsioDevicePropertyStore : public ComBaseUnknown 8 | { 9 | public: 10 | RSAsioDevicePropertyStore(RSAsioDevice& device); 11 | virtual ~RSAsioDevicePropertyStore(); 12 | 13 | virtual HRESULT STDMETHODCALLTYPE GetCount(DWORD *cProps) override; 14 | virtual HRESULT STDMETHODCALLTYPE GetAt(DWORD iProp, PROPERTYKEY *pkey) override; 15 | virtual HRESULT STDMETHODCALLTYPE GetValue(REFPROPERTYKEY key, PROPVARIANT *pv) override; 16 | virtual HRESULT STDMETHODCALLTYPE SetValue(REFPROPERTYKEY key, REFPROPVARIANT propvar) override; 17 | virtual HRESULT STDMETHODCALLTYPE Commit(void) override; 18 | 19 | private: 20 | RSAsioDevice& m_AsioDevice; 21 | }; 22 | -------------------------------------------------------------------------------- /RS_ASIO/RSBaseDeviceEnum.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "RSBaseDeviceEnum.h" 3 | 4 | RSBaseDeviceEnum::RSBaseDeviceEnum() 5 | { 6 | } 7 | 8 | RSBaseDeviceEnum::~RSBaseDeviceEnum() 9 | { 10 | ClearAll(); 11 | } 12 | 13 | HRESULT STDMETHODCALLTYPE RSBaseDeviceEnum::EnumAudioEndpoints(EDataFlow dataFlow, DWORD dwStateMask, IMMDeviceCollection **ppDevices) 14 | { 15 | if (m_DeviceListNeedsUpdate) 16 | UpdateAvailableDevices(); 17 | 18 | if (!ppDevices) 19 | return E_POINTER; 20 | 21 | RSDeviceCollection* newCollection = new RSDeviceCollection(); 22 | if (dataFlow == eRender || dataFlow == eAll) 23 | newCollection->UpdateDevicesFromCollection(m_RenderDevices, false); 24 | if (dataFlow == eCapture || dataFlow == eAll) 25 | newCollection->UpdateDevicesFromCollection(m_CaptureDevices, false); 26 | 27 | newCollection->RemoveIf([dwStateMask](IMMDevice* device) -> bool 28 | { 29 | DWORD devState = 0; 30 | if (FAILED(device->GetState(&devState))) 31 | return true; 32 | return (devState & dwStateMask) == 0; 33 | }); 34 | *ppDevices = newCollection; 35 | 36 | return S_OK; 37 | } 38 | 39 | HRESULT STDMETHODCALLTYPE RSBaseDeviceEnum::GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, IMMDevice **ppEndpoint) 40 | { 41 | if (m_DeviceListNeedsUpdate) 42 | UpdateAvailableDevices(); 43 | 44 | if (!ppEndpoint) 45 | return E_POINTER; 46 | 47 | if (role < 0 || role >= ERole_enum_count) 48 | return E_INVALIDARG; 49 | 50 | if (dataFlow == eRender) 51 | { 52 | if (m_DefaultRenderDevices[role]) 53 | { 54 | m_DefaultRenderDevices[role]->AddRef(); 55 | *ppEndpoint = m_DefaultRenderDevices[role]; 56 | return S_OK; 57 | } 58 | 59 | *ppEndpoint = nullptr; 60 | return E_NOTFOUND; 61 | } 62 | else if (dataFlow == eCapture) 63 | { 64 | if (m_DefaultCaptureDevices[role]) 65 | { 66 | m_DefaultCaptureDevices[role]->AddRef(); 67 | *ppEndpoint = m_DefaultCaptureDevices[role]; 68 | return S_OK; 69 | } 70 | 71 | *ppEndpoint = nullptr; 72 | return E_NOTFOUND; 73 | } 74 | 75 | return E_INVALIDARG; 76 | } 77 | 78 | HRESULT STDMETHODCALLTYPE RSBaseDeviceEnum::GetDevice(LPCWSTR pwstrId, IMMDevice **ppDevice) 79 | { 80 | if (m_DeviceListNeedsUpdate) 81 | UpdateAvailableDevices(); 82 | 83 | if (!ppDevice) 84 | return E_POINTER; 85 | 86 | IMMDevice* dev = m_RenderDevices.FindById(pwstrId); 87 | if (!dev) 88 | { 89 | dev = m_CaptureDevices.FindById(pwstrId); 90 | } 91 | if (dev) 92 | { 93 | dev->AddRef(); 94 | *ppDevice = dev; 95 | return S_OK; 96 | } 97 | 98 | return E_NOTFOUND; 99 | } 100 | 101 | HRESULT STDMETHODCALLTYPE RSBaseDeviceEnum::RegisterEndpointNotificationCallback(IMMNotificationClient *pClient) 102 | { 103 | return E_NOTIMPL; 104 | } 105 | 106 | HRESULT STDMETHODCALLTYPE RSBaseDeviceEnum::UnregisterEndpointNotificationCallback(IMMNotificationClient *pClient) 107 | { 108 | return E_NOTIMPL; 109 | } 110 | 111 | void RSBaseDeviceEnum::ClearAll() 112 | { 113 | m_RenderDevices.Clear(); 114 | m_CaptureDevices.Clear(); 115 | 116 | for (IMMDevice*& dev : m_DefaultRenderDevices) 117 | { 118 | if (dev) 119 | { 120 | dev->Release(); 121 | dev = nullptr; 122 | } 123 | } 124 | for (IMMDevice*& dev : m_DefaultCaptureDevices) 125 | { 126 | if (dev) 127 | { 128 | dev->Release(); 129 | dev = nullptr; 130 | } 131 | } 132 | } -------------------------------------------------------------------------------- /RS_ASIO/RSBaseDeviceEnum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "RSDeviceCollection.h" 4 | #include "ComBaseUnknown.h" 5 | 6 | class RSBaseDeviceEnum : public ComBaseUnknown 7 | { 8 | public: 9 | RSBaseDeviceEnum(); 10 | RSBaseDeviceEnum(const RSBaseDeviceEnum&) = delete; 11 | RSBaseDeviceEnum(RSBaseDeviceEnum&&) = delete; 12 | virtual ~RSBaseDeviceEnum(); 13 | 14 | virtual HRESULT STDMETHODCALLTYPE EnumAudioEndpoints(EDataFlow dataFlow, DWORD dwStateMask, IMMDeviceCollection **ppDevices) override; 15 | virtual HRESULT STDMETHODCALLTYPE GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, IMMDevice **ppEndpoint) override; 16 | virtual HRESULT STDMETHODCALLTYPE GetDevice(LPCWSTR pwstrId, IMMDevice **ppDevice) override; 17 | virtual HRESULT STDMETHODCALLTYPE RegisterEndpointNotificationCallback(IMMNotificationClient *pClient) override; 18 | virtual HRESULT STDMETHODCALLTYPE UnregisterEndpointNotificationCallback(IMMNotificationClient *pClient) override; 19 | 20 | void AddDeviceEnumerator(IMMDeviceEnumerator* enumerator); 21 | 22 | protected: 23 | virtual void UpdateAvailableDevices() = 0; 24 | virtual void ClearAll(); 25 | 26 | bool m_DeviceListNeedsUpdate = false; 27 | 28 | RSDeviceCollection m_RenderDevices; 29 | RSDeviceCollection m_CaptureDevices; 30 | std::array m_DefaultRenderDevices; 31 | std::array m_DefaultCaptureDevices; 32 | }; 33 | -------------------------------------------------------------------------------- /RS_ASIO/RSDeviceCollection.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | #include "RSDeviceCollection.h" 3 | 4 | RSDeviceCollection::RSDeviceCollection() 5 | { 6 | } 7 | 8 | RSDeviceCollection::RSDeviceCollection(const RSDeviceCollection& other) 9 | { 10 | Clear(); 11 | m_Devices.reserve(other.m_Devices.size()); 12 | for (IMMDevice* dev : other.m_Devices) 13 | { 14 | m_Devices.push_back(dev); 15 | dev->AddRef(); 16 | } 17 | } 18 | 19 | RSDeviceCollection::RSDeviceCollection(RSDeviceCollection&& other) 20 | { 21 | m_Devices.swap(other.m_Devices); 22 | } 23 | 24 | RSDeviceCollection::~RSDeviceCollection() 25 | { 26 | Clear(); 27 | } 28 | 29 | HRESULT STDMETHODCALLTYPE RSDeviceCollection::GetCount(UINT *pcDevices) 30 | { 31 | if (!pcDevices) 32 | return E_POINTER; 33 | *pcDevices = (UINT)m_Devices.size(); 34 | return S_OK; 35 | } 36 | 37 | HRESULT STDMETHODCALLTYPE RSDeviceCollection::Item(UINT nDevice, IMMDevice **ppDevice) 38 | { 39 | if (nDevice >= m_Devices.size()) 40 | return E_INVALIDARG; 41 | if (!ppDevice) 42 | return E_POINTER; 43 | 44 | IMMDevice* dev = m_Devices[nDevice]; 45 | dev->AddRef(); 46 | *ppDevice = dev; 47 | 48 | return S_OK; 49 | } 50 | 51 | bool RSDeviceCollection::UpdateDevicesFromCollection(IMMDeviceCollection& collection, bool checkForRemoval) 52 | { 53 | UINT n = 0; 54 | if (FAILED(collection.GetCount(&n))) 55 | { 56 | return false; 57 | } 58 | 59 | // extract devices into a vector to ease data iterating 60 | std::vector devices; 61 | devices.reserve(n); 62 | 63 | for (UINT i = 0; i < n; ++i) 64 | { 65 | IMMDevice* dev = nullptr; 66 | if (SUCCEEDED(collection.Item(i, &dev))) 67 | { 68 | devices.push_back(dev); 69 | } 70 | } 71 | 72 | if (checkForRemoval) 73 | { 74 | RemoveIf([&devices](IMMDevice* dev) -> bool 75 | { 76 | return std::find(devices.begin(), devices.end(), dev) == devices.end(); 77 | }); 78 | } 79 | 80 | // add the new ones 81 | UINT numAdded = 0; 82 | for (IMMDevice* dev : devices) 83 | { 84 | if (!HasDevice(dev)) 85 | { 86 | dev->AddRef(); 87 | m_Devices.push_back(dev); 88 | ++numAdded; 89 | } 90 | } 91 | 92 | // release temporary devices array 93 | for (IMMDevice* dev : devices) 94 | { 95 | dev->Release(); 96 | } 97 | devices.clear(); 98 | 99 | //rslog::info_ts() << "Added " << numAdded << " to collection; total now is " << m_Devices.size() << std::endl; 100 | 101 | return true; 102 | } 103 | 104 | bool RSDeviceCollection::HasDevice(IMMDevice* device) const 105 | { 106 | return std::find(m_Devices.begin(), m_Devices.end(), device) != m_Devices.end(); 107 | } 108 | 109 | void RSDeviceCollection::AddDevice(IMMDevice* device) 110 | { 111 | if (device) 112 | { 113 | device->AddRef(); 114 | m_Devices.push_back(device); 115 | } 116 | } 117 | 118 | void RSDeviceCollection::Clear() 119 | { 120 | for (auto device : m_Devices) 121 | { 122 | device->Release(); 123 | } 124 | m_Devices.clear(); 125 | } 126 | 127 | void RSDeviceCollection::RemoveIf(std::function callback) 128 | { 129 | auto itRemove = std::remove_if(m_Devices.begin(), m_Devices.end(), callback); 130 | for (auto it = itRemove; it != m_Devices.end(); ++it) 131 | { 132 | (*it)->Release(); 133 | } 134 | m_Devices.erase(itRemove, m_Devices.end()); 135 | } 136 | 137 | IMMDevice* RSDeviceCollection::FindById(LPCWSTR id) 138 | { 139 | for (IMMDevice* dev : m_Devices) 140 | { 141 | LPWSTR strId = nullptr; 142 | if (SUCCEEDED(dev->GetId(&strId))) 143 | { 144 | bool match = (wcscmp(id, strId) == 0); 145 | CoTaskMemFree(strId); 146 | if (match) 147 | { 148 | return dev; 149 | } 150 | } 151 | } 152 | 153 | return nullptr; 154 | } -------------------------------------------------------------------------------- /RS_ASIO/RSDeviceCollection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ComBaseUnknown.h" 4 | 5 | class RSDeviceCollection : public ComBaseUnknown 6 | { 7 | public: 8 | RSDeviceCollection(); 9 | RSDeviceCollection(const RSDeviceCollection&); 10 | RSDeviceCollection(RSDeviceCollection&&); 11 | virtual ~RSDeviceCollection(); 12 | 13 | virtual HRESULT STDMETHODCALLTYPE GetCount(UINT *pcDevices) override; 14 | virtual HRESULT STDMETHODCALLTYPE Item(UINT nDevice, IMMDevice **ppDevice) override; 15 | 16 | bool UpdateDevicesFromCollection(IMMDeviceCollection& collection, bool checkForRemoval); 17 | bool HasDevice(IMMDevice* device) const; 18 | void AddDevice(IMMDevice* device); 19 | void Clear(); 20 | void RemoveIf(std::function callback); 21 | IMMDevice* FindById(LPCWSTR id); 22 | 23 | std::vector::iterator begin() { return m_Devices.begin(); } 24 | std::vector::iterator end() { return m_Devices.end(); } 25 | std::vector::const_iterator cbegin() const { return m_Devices.cbegin(); } 26 | std::vector::const_iterator cend() const { return m_Devices.cend(); } 27 | size_t size() const { return m_Devices.size(); } 28 | 29 | private: 30 | ULONG m_RefCount = 1; 31 | std::vector m_Devices; 32 | }; 33 | -------------------------------------------------------------------------------- /RS_ASIO/RS_ASIO.args.json: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 2, 3 | "Id": "4339185f-e3ea-4fe0-bbd7-67d4fe3fbf3e", 4 | "Items": [ 5 | { 6 | "Id": "a2d778b6-ffaf-451e-84f5-66f351588f71", 7 | "Command": "" 8 | } 9 | ] 10 | } -------------------------------------------------------------------------------- /RS_ASIO/TrampolineToMethod.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "function_traits.h" 4 | 5 | #if _WIN32 6 | template 7 | class TrampolineToMethod 8 | { 9 | private: 10 | using TFuncRetType = typename function_traits::result_type; 11 | 12 | public: 13 | template 14 | TrampolineToMethod(TClass& obj, TMemberFunc fn) 15 | { 16 | static void* memberFnPtr = *(void**)&fn; 17 | static void* redirectorFnPtr = GenerateFuncPtr::arity>(&obj);; 18 | 19 | const BYTE opCodes[]{ 20 | 0x89, 0x44, 0x24, 0xf4, // mov DWORD PTR [esp-12], eax (backup eax value) 21 | 0x58, // pop eax (pop return address into eax) 22 | 0x68, 0xff, 0xff, 0xff, 0xff, // push fn pointer 23 | 0x68, 0xff, 0xff, 0xff, 0xff, // push this pointer 24 | 0x50, // push eax (restore return address into stack) 25 | 0x8b, 0x44, 0x24, 0xfc, // mov eax, DWORD PTR [esp-4] (restore eax from backup) 26 | 0xff, 0x15, 0xff, 0xff, 0xff, 0xff, // call 0xffffffff 27 | 28 | // backup eax on the stack and get the original return address into eax 29 | 0x50, // push eax 30 | 0x83, 0xc4, 0x04, // add esp, 0x04 31 | 0x58, // pop eax 32 | 33 | // move return address to it's original position and restore eax 34 | 0x83, 0xc4, 0x08, // add esp, 8 35 | 0x50, // push eax 36 | 0x8b, 0x44, 0x24, 0xf4, // mov eax, DWORD PTR[esp-12] 37 | 38 | 0xc3, // ret 39 | }; 40 | 41 | m_Size = sizeof(opCodes); 42 | 43 | m_Blob = (BYTE*)VirtualAlloc(NULL, m_Size, MEM_COMMIT, PAGE_READWRITE); 44 | if (m_Blob) 45 | { 46 | memcpy(m_Blob, opCodes, m_Size); 47 | *((void**)&m_Blob[6]) = memberFnPtr; 48 | *((void**)&m_Blob[11]) = &obj; 49 | *((void**)&m_Blob[22]) = &redirectorFnPtr; 50 | 51 | DWORD oldProtect = 0; 52 | if (!VirtualProtect(m_Blob, m_Size, PAGE_EXECUTE, &oldProtect)) 53 | { 54 | VirtualFree(m_Blob, m_Size, MEM_RELEASE); 55 | m_Blob = nullptr; 56 | } 57 | } 58 | } 59 | 60 | ~TrampolineToMethod() 61 | { 62 | if (m_Blob) 63 | { 64 | VirtualFree(m_Blob, m_Size, MEM_RELEASE); 65 | } 66 | } 67 | 68 | TFunc GetFuncPtr() const 69 | { 70 | return reinterpret_cast(m_Blob); 71 | } 72 | 73 | private: 74 | template> 75 | void* GenerateFuncPtr(TClass* obj) 76 | { 77 | return GenerateFuncPtr_Impl(obj, Indices{}); 78 | } 79 | 80 | template 81 | void* GenerateFuncPtr_Impl(TClass* obj, std::index_sequence) 82 | { 83 | return (void*)&Redirector::type_list>::type...>; 84 | } 85 | 86 | template 87 | static typename function_traits::result_type __cdecl Redirector(void* savedReturnPointer, TClass* obj, TMemberFunc fn, Args ... args) 88 | { 89 | return (obj->*fn)(args...); 90 | } 91 | 92 | BYTE* m_Blob; 93 | size_t m_Size; 94 | }; 95 | #else 96 | #error TrampolineToMethod not supported in 64 bit 97 | #endif -------------------------------------------------------------------------------- /RS_ASIO/Utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | EXTERN_C const PROPERTYKEY PKEY_Device_DeviceIdHiddenKey1; 4 | EXTERN_C const PROPERTYKEY PKEY_Device_DeviceIdHiddenKey2; 5 | 6 | struct HResultToStr 7 | { 8 | HResultToStr(HRESULT hr) { this->hr = hr; } 9 | HRESULT hr; 10 | }; 11 | 12 | struct TimeStamp 13 | { 14 | TimeStamp() 15 | { 16 | QueryPerformanceCounter((LARGE_INTEGER*)&perfCount); 17 | } 18 | TimeStamp(LONGLONG pc) : perfCount(pc) {} 19 | 20 | LONGLONG GetMilisecs() const; 21 | LONGLONG GetMicrosecs() const; 22 | double GetSeconds() const; 23 | 24 | TimeStamp operator - (const TimeStamp& other); 25 | 26 | LONGLONG perfCount = 0; 27 | 28 | private: 29 | LONGLONG GetPerformanceFreq() const; 30 | }; 31 | 32 | std::string ConvertWStrToStr(const std::wstring& wstr); 33 | std::string IID2String(REFIID iid); 34 | const char* Dataflow2String(EDataFlow dataFlow); 35 | const char* Role2String(ERole role); 36 | 37 | std::ostream & operator<<(std::ostream & os, REFIID iid); 38 | std::ostream & operator<<(std::ostream & os, REFPROPERTYKEY key); 39 | std::ostream & operator<<(std::ostream & os, const wchar_t* wStr); 40 | std::ostream & operator<<(std::ostream & os, const std::wstring wStr); 41 | std::ostream & operator<<(std::ostream & os, const WAVEFORMATEX& fmt); 42 | std::ostream & operator<<(std::ostream & os, const AUDCLNT_SHAREMODE& mode); 43 | std::ostream & operator<<(std::ostream & os, const HResultToStr& hresult); 44 | std::ostream & operator<<(std::ostream & os, ASIOSampleType sampleType); 45 | std::ostream & operator<<(std::ostream & os, const TimeStamp& time); 46 | 47 | REFERENCE_TIME MilisecsToRefTime(LONGLONG ms); 48 | LONGLONG RefTimeToMilisecs(const REFERENCE_TIME& time); 49 | LONGLONG DurationToAudioFrames(const REFERENCE_TIME& time, DWORD sampleRate); 50 | REFERENCE_TIME AudioFramesToDuration(const LONGLONG& frames, DWORD sampleRate); 51 | 52 | bool AsioSampleTypeFromFormat(ASIOSampleType* out, WORD bitsPerSample, bool isFloat); 53 | WORD GetAsioSampleTypeNumBytes(ASIOSampleType sampleType); 54 | 55 | bool IsWaveFormatSame(const WAVEFORMATEX& fmt_a, const WAVEFORMATEX& fmt_b); 56 | 57 | HWND GetGameWindow(); 58 | 59 | const std::wstring& GetGamePath(); -------------------------------------------------------------------------------- /RS_ASIO/crc32.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #include "crc32.h" 4 | 5 | static DWORD crc_32_tab[] = { 6 | 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 7 | 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 8 | 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 9 | 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 10 | 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 11 | 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 12 | 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 13 | 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 14 | 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 15 | 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 16 | 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 17 | 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 18 | 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 19 | 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 20 | 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 21 | 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 22 | 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 23 | 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 24 | 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 25 | 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 26 | 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 27 | 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 28 | 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 29 | 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 30 | 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 31 | 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 32 | 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 33 | 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 34 | 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 35 | 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 36 | 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 37 | 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 38 | 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 39 | 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 40 | 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 41 | 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 42 | 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 43 | 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 44 | 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 45 | 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 46 | 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 47 | 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 48 | 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d 49 | }; 50 | 51 | static inline DWORD updateCRC32(unsigned char ch, DWORD crc) 52 | { 53 | return crc_32_tab[(crc ^ ch) & 0xff] ^ (crc >> 8); 54 | } 55 | 56 | bool crc32file(char* name, DWORD& outCrc) 57 | { 58 | FILE* f = nullptr; 59 | fopen_s(&f, name, "rb"); 60 | if (!f) 61 | return false; 62 | 63 | bool ret = true; 64 | 65 | char buf[256]; 66 | outCrc = 0xffffffff; 67 | for(;;) 68 | { 69 | const size_t numBytesRead = fread(buf, 1, sizeof(buf), f); 70 | for (size_t i = 0; i < numBytesRead; ++i) 71 | { 72 | outCrc = updateCRC32(buf[i], outCrc); 73 | } 74 | 75 | if (feof(f)) 76 | break; 77 | 78 | if (ferror(f)) 79 | { 80 | ret = false; 81 | break; 82 | } 83 | } 84 | 85 | fclose(f); 86 | outCrc = ~outCrc; 87 | 88 | return ret; 89 | } 90 | 91 | DWORD crc32buf(const void* buf, size_t len) 92 | { 93 | const BYTE* bufBytes = (const BYTE*)buf; 94 | DWORD oldcrc32 = 0xFFFFFFFF; 95 | 96 | for (size_t i=0; i 4 | struct sumSizeOfArgs 5 | { 6 | static constexpr size_t totalSize = 0; 7 | }; 8 | 9 | template 10 | struct sumSizeOfArgs 11 | { 12 | static constexpr size_t totalSize = sizeof(typename std::decay::type) + sumSizeOfArgs::totalSize; 13 | }; 14 | 15 | template 16 | struct sumSizeOfArgs 17 | { 18 | static constexpr size_t totalSize = sizeof(typename std::decay::type); 19 | }; 20 | 21 | template 22 | struct function_traits_impl; 23 | 24 | template 25 | struct function_traits_impl 26 | { 27 | static constexpr size_t arity = sizeof...(Args); 28 | 29 | using result_type = ReturnType; 30 | 31 | static constexpr size_t totalSize = sumSizeOfArgs::totalSize; 32 | 33 | using type_list = std::tuple; 34 | 35 | template 36 | struct arg 37 | { 38 | using type = typename std::tuple_element>::type; 39 | }; 40 | }; 41 | 42 | template 43 | struct function_traits_impl 44 | : function_traits_impl {}; 45 | 46 | template 47 | struct function_traits_impl 48 | { 49 | static constexpr size_t arity = sizeof...(Args); 50 | 51 | using result_type = ReturnType; 52 | 53 | static constexpr size_t totalSize = sumSizeOfArgs::totalSize; 54 | 55 | using type_list = std::tuple; 56 | 57 | template 58 | struct arg 59 | { 60 | using type = typename std::tuple_element>::type; 61 | }; 62 | }; 63 | 64 | template 65 | struct function_traits_impl : function_traits_impl {}; 66 | 67 | template 68 | struct function_traits : function_traits_impl {}; 69 | 70 | template 71 | struct function_traits : function_traits_impl {}; -------------------------------------------------------------------------------- /RS_ASIO/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | 3 | #pragma comment(lib, "propsys.lib") 4 | -------------------------------------------------------------------------------- /RS_ASIO/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | 22 | 23 | // reference additional headers your program requires here 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "asio.h" 37 | #include "Utils.h" 38 | #include "Log.h" 39 | #include "TrampolineToMethod.h" -------------------------------------------------------------------------------- /RS_ASIO/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /avrt/avrt.cpp: -------------------------------------------------------------------------------- 1 | // avrt.cpp : Defines the exported functions for the DLL application. 2 | // 3 | 4 | #include "stdafx.h" 5 | 6 | 7 | -------------------------------------------------------------------------------- /avrt/avrt.def: -------------------------------------------------------------------------------- 1 | LIBRARY avrt 2 | EXPORTS 3 | AvCreateTaskIndex = Wrapped_AvCreateTaskIndex @1 4 | AvQuerySystemResponsiveness = Wrapped_AvQuerySystemResponsiveness @2 5 | AvQueryTaskIndexValue = Wrapped_AvQueryTaskIndexValue @3 6 | AvRevertMmThreadCharacteristics = Wrapped_AvRevertMmThreadCharacteristics @4 7 | AvRtCreateThreadOrderingGroup = Wrapped_AvRtCreateThreadOrderingGroup @5 8 | AvRtCreateThreadOrderingGroupExA = Wrapped_AvRtCreateThreadOrderingGroupExA @6 9 | AvRtCreateThreadOrderingGroupExW = Wrapped_AvRtCreateThreadOrderingGroupExW @7 10 | AvRtDeleteThreadOrderingGroup = Wrapped_AvRtDeleteThreadOrderingGroup @8 11 | AvRtJoinThreadOrderingGroup = Wrapped_AvRtJoinThreadOrderingGroup @9 12 | AvRtLeaveThreadOrderingGroup = Wrapped_AvRtLeaveThreadOrderingGroup @10 13 | AvRtWaitOnThreadOrderingGroup = Wrapped_AvRtWaitOnThreadOrderingGroup @11 14 | AvSetMmMaxThreadCharacteristicsA = Wrapped_AvSetMmMaxThreadCharacteristicsA @12 15 | AvSetMmMaxThreadCharacteristicsW = Wrapped_AvSetMmMaxThreadCharacteristicsW @13 16 | AvSetMmThreadCharacteristicsA = Wrapped_AvSetMmThreadCharacteristicsA @14 17 | AvSetMmThreadCharacteristicsW = Wrapped_AvSetMmThreadCharacteristicsW @15 18 | AvSetMmThreadPriority = Wrapped_AvSetMmThreadPriority @16 19 | AvSetMultimediaMode = Wrapped_AvSetMultimediaMode @17 20 | AvTaskIndexYield = Wrapped_AvTaskIndexYield @18 21 | AvTaskIndexYieldCancel = Wrapped_AvTaskIndexYieldCancel @19 22 | AvThreadOpenTaskIndex = Wrapped_AvThreadOpenTaskIndex @20 -------------------------------------------------------------------------------- /avrt/avrt.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | 37 | 38 | Source Files 39 | 40 | 41 | -------------------------------------------------------------------------------- /avrt/dllmain.cpp: -------------------------------------------------------------------------------- 1 | // dllmain.cpp : Defines the entry point for the DLL application. 2 | #include "stdafx.h" 3 | 4 | typedef int(*BLINDWRAPPER)(void); 5 | 6 | struct REAL_FUNCTIONS 7 | { 8 | BLINDWRAPPER AvCreateTaskIndex; 9 | BLINDWRAPPER AvQuerySystemResponsiveness; 10 | BLINDWRAPPER AvQueryTaskIndexValue; 11 | BLINDWRAPPER AvRevertMmThreadCharacteristics; 12 | BLINDWRAPPER AvRtCreateThreadOrderingGroup; 13 | BLINDWRAPPER AvRtCreateThreadOrderingGroupExA; 14 | BLINDWRAPPER AvRtCreateThreadOrderingGroupExW; 15 | BLINDWRAPPER AvRtDeleteThreadOrderingGroup; 16 | BLINDWRAPPER AvRtJoinThreadOrderingGroup; 17 | BLINDWRAPPER AvRtLeaveThreadOrderingGroup; 18 | BLINDWRAPPER AvRtWaitOnThreadOrderingGroup; 19 | BLINDWRAPPER AvSetMmMaxThreadCharacteristicsA; 20 | BLINDWRAPPER AvSetMmMaxThreadCharacteristicsW; 21 | BLINDWRAPPER AvSetMmThreadCharacteristicsA; 22 | BLINDWRAPPER AvSetMmThreadCharacteristicsW; 23 | BLINDWRAPPER AvSetMmThreadPriority; 24 | BLINDWRAPPER AvSetMultimediaMode; 25 | BLINDWRAPPER AvTaskIndexYield; 26 | BLINDWRAPPER AvTaskIndexYieldCancel; 27 | BLINDWRAPPER AvThreadOpenTaskIndex; 28 | }; 29 | 30 | static HMODULE hModuleReal = NULL; 31 | static REAL_FUNCTIONS RealFunctions = {}; 32 | 33 | #define IMPLEMENT_FORWARDER(x) void __declspec(naked) Wrapped_##x(){ _asm { jmp RealFunctions.##x }; } 34 | #define FILLREALPROCADDRESS(x) RealFunctions.##x = (decltype(RealFunctions.##x))GetProcAddress(hModuleReal, #x); 35 | 36 | IMPLEMENT_FORWARDER(AvCreateTaskIndex); 37 | IMPLEMENT_FORWARDER(AvQuerySystemResponsiveness); 38 | IMPLEMENT_FORWARDER(AvQueryTaskIndexValue); 39 | IMPLEMENT_FORWARDER(AvRevertMmThreadCharacteristics); 40 | IMPLEMENT_FORWARDER(AvRtCreateThreadOrderingGroup); 41 | IMPLEMENT_FORWARDER(AvRtCreateThreadOrderingGroupExA); 42 | IMPLEMENT_FORWARDER(AvRtCreateThreadOrderingGroupExW); 43 | IMPLEMENT_FORWARDER(AvRtDeleteThreadOrderingGroup); 44 | IMPLEMENT_FORWARDER(AvRtJoinThreadOrderingGroup); 45 | IMPLEMENT_FORWARDER(AvRtLeaveThreadOrderingGroup); 46 | IMPLEMENT_FORWARDER(AvRtWaitOnThreadOrderingGroup); 47 | IMPLEMENT_FORWARDER(AvSetMmMaxThreadCharacteristicsA); 48 | IMPLEMENT_FORWARDER(AvSetMmMaxThreadCharacteristicsW); 49 | IMPLEMENT_FORWARDER(AvSetMmThreadCharacteristicsA); 50 | IMPLEMENT_FORWARDER(AvSetMmThreadCharacteristicsW); 51 | IMPLEMENT_FORWARDER(AvSetMmThreadPriority); 52 | IMPLEMENT_FORWARDER(AvSetMultimediaMode); 53 | IMPLEMENT_FORWARDER(AvTaskIndexYield); 54 | IMPLEMENT_FORWARDER(AvTaskIndexYieldCancel); 55 | IMPLEMENT_FORWARDER(AvThreadOpenTaskIndex); 56 | 57 | static void LoadOriginalDll() 58 | { 59 | char sysDir[MAX_PATH] = {}; 60 | GetSystemDirectoryA(sysDir, MAX_PATH); 61 | 62 | char originalPath[MAX_PATH] = {}; 63 | snprintf(originalPath, sizeof(originalPath), "%s\\avrt.dll", sysDir); 64 | 65 | std::cout << "Attempting to load original DLL from: " << originalPath << std::endl; 66 | hModuleReal = LoadLibraryA(originalPath); 67 | if (!hModuleReal) 68 | { 69 | std::cerr << "Load failed" << std::endl; 70 | } 71 | else 72 | { 73 | std::cout << "Load OK; fetching procedure addresses..." << std::endl; 74 | 75 | FILLREALPROCADDRESS(AvCreateTaskIndex); 76 | FILLREALPROCADDRESS(AvQuerySystemResponsiveness); 77 | FILLREALPROCADDRESS(AvQueryTaskIndexValue); 78 | FILLREALPROCADDRESS(AvRevertMmThreadCharacteristics); 79 | FILLREALPROCADDRESS(AvRtCreateThreadOrderingGroup); 80 | FILLREALPROCADDRESS(AvRtCreateThreadOrderingGroupExA); 81 | FILLREALPROCADDRESS(AvRtCreateThreadOrderingGroupExW); 82 | FILLREALPROCADDRESS(AvRtDeleteThreadOrderingGroup); 83 | FILLREALPROCADDRESS(AvRtJoinThreadOrderingGroup); 84 | FILLREALPROCADDRESS(AvRtLeaveThreadOrderingGroup); 85 | FILLREALPROCADDRESS(AvRtWaitOnThreadOrderingGroup); 86 | FILLREALPROCADDRESS(AvSetMmMaxThreadCharacteristicsA); 87 | FILLREALPROCADDRESS(AvSetMmMaxThreadCharacteristicsW); 88 | FILLREALPROCADDRESS(AvSetMmThreadCharacteristicsA); 89 | FILLREALPROCADDRESS(AvSetMmThreadCharacteristicsW); 90 | FILLREALPROCADDRESS(AvSetMmThreadPriority); 91 | FILLREALPROCADDRESS(AvSetMultimediaMode); 92 | FILLREALPROCADDRESS(AvTaskIndexYield); 93 | FILLREALPROCADDRESS(AvTaskIndexYieldCancel); 94 | FILLREALPROCADDRESS(AvThreadOpenTaskIndex); 95 | } 96 | } 97 | 98 | static void UnloadOriginalDll() 99 | { 100 | if (hModuleReal) 101 | { 102 | std::cout << "Unloading original DLL" << std::endl; 103 | 104 | FreeLibrary(hModuleReal); 105 | hModuleReal = NULL; 106 | } 107 | } 108 | 109 | static void InitConsole() 110 | { 111 | AllocConsole(); 112 | AttachConsole(GetCurrentProcessId()); 113 | 114 | FILE* fDummy = nullptr; 115 | freopen_s(&fDummy, "CONOUT$", "w", stdout); 116 | freopen_s(&fDummy, "CONOUT$", "w", stderr); 117 | freopen_s(&fDummy, "CONIN$", "r", stdin); 118 | } 119 | 120 | static HWND GetGameWindow() 121 | { 122 | return FindWindowA("Rocksmith 2014", "Rocksmith 2014"); 123 | } 124 | 125 | BOOL APIENTRY DllMain( HMODULE hModule, 126 | DWORD ul_reason_for_call, 127 | LPVOID lpReserved 128 | ) 129 | { 130 | switch (ul_reason_for_call) 131 | { 132 | case DLL_PROCESS_ATTACH: 133 | #ifdef _DEBUG 134 | // InitConsole(); 135 | #endif 136 | LoadOriginalDll(); 137 | if (!LoadLibrary(TEXT("RS_ASIO.dll"))) 138 | { 139 | MessageBox(GetGameWindow(), TEXT("Failed to load RS_ASIO.dll"), TEXT("Error"), MB_OK | MB_ICONERROR); 140 | exit(1); 141 | } 142 | 143 | break; 144 | case DLL_PROCESS_DETACH: 145 | UnloadOriginalDll(); 146 | break; 147 | 148 | case DLL_THREAD_ATTACH: 149 | case DLL_THREAD_DETACH: 150 | break; 151 | } 152 | return TRUE; 153 | } 154 | 155 | -------------------------------------------------------------------------------- /avrt/stdafx.cpp: -------------------------------------------------------------------------------- 1 | #include "stdafx.h" 2 | -------------------------------------------------------------------------------- /avrt/stdafx.h: -------------------------------------------------------------------------------- 1 | // stdafx.h : include file for standard system include files, 2 | // or project specific include files that are used frequently, but 3 | // are changed infrequently 4 | // 5 | 6 | #pragma once 7 | 8 | #include "targetver.h" 9 | 10 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 11 | // Windows Header Files 12 | #include 13 | 14 | 15 | 16 | 17 | // reference additional headers your program requires here 18 | #include 19 | #include 20 | #include -------------------------------------------------------------------------------- /avrt/targetver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Including SDKDDKVer.h defines the highest available Windows platform. 4 | 5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /dist/RS_ASIO.ini: -------------------------------------------------------------------------------- 1 | # for "EnableWasapiOutputs" you can use -1 to have a message prompting 2 | # to use either WASAPI or ASIO for output every time you boot the game 3 | [Config] 4 | EnableWasapiOutputs=0 5 | EnableWasapiInputs=0 6 | EnableAsio=1 7 | 8 | [Asio] 9 | ; available buffer size modes: 10 | ; driver - respect buffer size setting set in the driver 11 | ; host - use a buffer size as close as possible as that requested by the host application 12 | ; custom - use the buffer size specified in CustomBufferSize field 13 | BufferSizeMode=driver 14 | CustomBufferSize= 15 | 16 | # if your game hangs or crashes on exit, try setting "EnableRefCountHack" to true. 17 | # when blank or invalid, the value of "EnableRefCountHack" will be interpreted as 18 | # true if RS ASIO detects the usage of Asio4All. 19 | # the same applies for all inputs. 20 | [Asio.Output] 21 | Driver= 22 | BaseChannel=0 23 | AltBaseChannel= 24 | EnableSoftwareEndpointVolumeControl=1 25 | EnableSoftwareMasterVolumeControl=1 26 | SoftwareMasterVolumePercent=100 27 | EnableRefCountHack= 28 | 29 | [Asio.Input.0] 30 | Driver= 31 | Channel=0 32 | EnableSoftwareEndpointVolumeControl=1 33 | EnableSoftwareMasterVolumeControl=1 34 | SoftwareMasterVolumePercent=100 35 | EnableRefCountHack= 36 | 37 | [Asio.Input.1] 38 | Driver= 39 | Channel=1 40 | EnableSoftwareEndpointVolumeControl=1 41 | EnableSoftwareMasterVolumeControl=1 42 | SoftwareMasterVolumePercent=100 43 | EnableRefCountHack= 44 | 45 | [Asio.Input.Mic] 46 | Driver= 47 | Channel=1 48 | EnableSoftwareEndpointVolumeControl=1 49 | EnableSoftwareMasterVolumeControl=1 50 | SoftwareMasterVolumePercent=100 51 | EnableRefCountHack= -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Specific interface guides 2 | 3 | Please put your working configuration examples and notes into subfolder in this folder. 4 | -------------------------------------------------------------------------------- /docs/asus_strix_soar/README.md: -------------------------------------------------------------------------------- 1 | # Asus Strix Soar 2 | 3 | Works without problems using the driver supplied buffer size. 4 | Rocksmith's `LatencyBuffer` can be set to 3 on faster systems. 5 | 6 | Instrument can only be heard when using `EnableWasapiInputs=1` so that the RealTone Cable is found and used by the Software. 7 | 8 | ## Config file 9 | 10 | **RS_ASIO.ini** 11 | 12 | ```ini 13 | [Config] 14 | EnableWasapiOutputs=0 15 | EnableWasapiInputs=1 16 | EnableAsio=1 17 | 18 | [Asio] 19 | ; available buffer size modes: 20 | ; driver - respect buffer size setting set in the driver 21 | ; host - use a buffer size as close as possible as that requested by the host application 22 | ; custom - use the buffer size specified in CustomBufferSize field 23 | BufferSizeMode=driver 24 | CustomBufferSize= 25 | 26 | [Asio.Output] 27 | Driver=STRIX SOUND CARD ASIO 28 | BaseChannel=0 29 | EnableSoftwareEndpointVolumeControl=1 30 | EnableSoftwareMasterVolumeControl=1 31 | SoftwareMasterVolumePercent=100 32 | 33 | [Asio.Input.0] 34 | Driver= 35 | Channel=0 36 | EnableSoftwareEndpointVolumeControl=1 37 | EnableSoftwareMasterVolumeControl=1 38 | SoftwareMasterVolumePercent=100 39 | 40 | [Asio.Input.1] 41 | Driver= 42 | Channel=1 43 | EnableSoftwareEndpointVolumeControl=1 44 | EnableSoftwareMasterVolumeControl=1 45 | SoftwareMasterVolumePercent=100 46 | ``` 47 | 48 | **Rocksmith.ini** 49 | ```ini 50 | [Audio] 51 | EnableMicrophone=0 52 | ExclusiveMode=1 53 | LatencyBuffer=3 54 | ForceDefaultPlaybackDevice= 55 | ForceWDM=0 56 | ForceDirectXSink=0 57 | DumpAudioLog=0 58 | MaxOutputBufferSize=0 59 | RealToneCableOnly=0 60 | Win32UltraLowLatencyMode=1 61 | ``` 62 | -------------------------------------------------------------------------------- /docs/audient_evo_4/Annotation 2020-08-16 224310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdias/rs_asio/b04675bb000edb5ec479120513042aaf3dba30e1/docs/audient_evo_4/Annotation 2020-08-16 224310.png -------------------------------------------------------------------------------- /docs/audient_evo_4/Annotation 2020-08-16 224424.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdias/rs_asio/b04675bb000edb5ec479120513042aaf3dba30e1/docs/audient_evo_4/Annotation 2020-08-16 224424.png -------------------------------------------------------------------------------- /docs/audient_evo_4/Annotation 2020-08-16 232728.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdias/rs_asio/b04675bb000edb5ec479120513042aaf3dba30e1/docs/audient_evo_4/Annotation 2020-08-16 232728.png -------------------------------------------------------------------------------- /docs/audient_evo_4/Annotation 2020-08-16 232812.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdias/rs_asio/b04675bb000edb5ec479120513042aaf3dba30e1/docs/audient_evo_4/Annotation 2020-08-16 232812.png -------------------------------------------------------------------------------- /docs/audient_evo_4/README.md: -------------------------------------------------------------------------------- 1 | # Audient Evo 4 / Evo 16 2 | 3 | ## About 4 | 5 | This device is confirmed to be working as of _v0.5.3_ 6 | 7 | _Note: This is tested for single user/player only!_ 8 | 9 | ### Known issues 10 | 11 | - None as of now with the latest driver and patch updates. 12 | 13 | ## Setup steps 14 | 15 | ### Install drivers 16 | 17 | Install the latest [official driver](https://audient.com/products/audio-interfaces/sono/downloads/) (v4.1.3 as of time of writing). 18 | 19 | Note: Also make sure that your interface is working fine. Easiest way is to check by installing Audacity and check if your guitar tone (clean) is being recorded. If it is recording fine and you don't have issues with the driver move ahead. 20 | 21 | ### ASIO and Control panel settings 22 | 23 | Although at the time of writing this you do no need to change any default settings [sample rate (96Kz), depth (24 bit) and buffer size (1024)] for inputs and outputs in the control panel or from the EVO app in the Window's notification tray. You also won't have to disable any input (a microphone in my case) in the control panel. But make sure that your default input is set your `Mic | Line | Instrument 1`. With the current RS_ASIO patch and Audient drivers everything works fine without tinkering any settings. 24 | 25 | Here are the settings on my computer. 26 | 27 | ![image](https://raw.githubusercontent.com/AmolAmrit/rs_asio/master/docs/audient_evo_4/Annotation%202020-08-16%20224310.png) 28 | 29 | ![image](https://raw.githubusercontent.com/AmolAmrit/rs_asio/master/docs/audient_evo_4/Annotation%202020-08-16%20224424.png) 30 | 31 | ![image](https://raw.githubusercontent.com/AmolAmrit/rs_asio/master/docs/audient_evo_4/Annotation%202020-08-16%20232812.png) 32 | 33 | ![image](https://raw.githubusercontent.com/AmolAmrit/rs_asio/master/docs/audient_evo_4/Annotation%202020-08-16%20232728.png) 34 | 35 | _Note:_ **Rocksmith however supports input only at a sample rate till 48Kz. The RS_ASIO patch automatically sets the sample rate to 48Kz so by default you won't need to hinder any settings. But in case if you face issues with it is always recommende to set the sample rate manually to 48Kz from the EVO app.** 36 | 37 | ![image](https://user-images.githubusercontent.com/10779424/86245120-ef2bad80-bba0-11ea-96cb-c04e88f91391.png) 38 | 39 | There isn't actually any panel but you should see an `e` logo in your taskbar. Right click it and set the sample rate to 48KHz. 40 | 41 | ### Setup RS_ASIO 42 | 43 | Follow the instructions from the [configuration guide](https://github.com/mdias/rs_asio#basic-configuration-guide) to set the desired outputs and inputs to `Audient USB Audio ASIO Driver`. 44 | 45 | In the EVO 4 the arrangement of input is a little different. So don't get confused from the configuration file. The Channel=0 is actually Channel=1 in the EVO 4. I'll be sharing my files below too. You can see that I have commented out Asio Input 1 by using a semi colon. Because if you don't then Rocksmith will call for both inputs and your interface won't be detected as the Rocksmith cable. 46 | 47 | _Note: Our Renderer.Win32 might be different depending on our video configuration._ 48 | 49 | 50 | ## Config files 51 | 52 | 53 |
54 | My RS_ASIO.ini 55 | 56 | 57 | 58 | ``` 59 | [Config] 60 | EnableWasapiOutputs=0 61 | EnableWasapiInputs=0 62 | EnableAsio=1 63 | 64 | [Asio] 65 | ; available buffer size modes: 66 | ; driver - respect buffer size setting set in the driver 67 | ; host - use a buffer size as close as possible as that requested by the host application 68 | ; custom - use the buffer size specified in CustomBufferSize field 69 | BufferSizeMode=driver 70 | CustomBufferSize= 71 | 72 | [Asio.Output] 73 | Driver=Audient USB Audio ASIO Driver 74 | BaseChannel=0 75 | EnableSoftwareEndpointVolumeControl=1 76 | EnableSoftwareMasterVolumeControl=1 77 | SoftwareMasterVolumePercent=100 78 | 79 | [Asio.Input.0] 80 | Driver=Audient USB Audio ASIO Driver 81 | Channel=0 82 | EnableSoftwareEndpointVolumeControl=1 83 | EnableSoftwareMasterVolumeControl=1 84 | SoftwareMasterVolumePercent=100 85 | 86 | [Asio.Input.1] 87 | ;Driver= 88 | ;Channel=1 89 | ;EnableSoftwareEndpointVolumeControl=1 90 | ;EnableSoftwareMasterVolumeControl=1 91 | ;SoftwareMasterVolumePercent=100 92 | ``` 93 | 94 |
95 | 96 |
97 | My Rocksmith.ini 98 | 99 | 100 | 101 | ``` 102 | [Audio] 103 | EnableMicrophone=1 104 | ExclusiveMode=1 105 | LatencyBuffer=4 106 | ForceDefaultPlaybackDevice=1 107 | ForceWDM=0 108 | ForceDirectXSink=0 109 | DumpAudioLog=0 110 | MaxOutputBufferSize=0 111 | RealToneCableOnly=0 112 | Win32UltraLowLatencyMode=1 113 | [Renderer.Win32] 114 | ShowGamepadUI=0 115 | ScreenWidth=1920 116 | ScreenHeight=1080 117 | Fullscreen=2 118 | VisualQuality=2 119 | RenderingWidth=0 120 | RenderingHeight=0 121 | EnablePostEffects=1 122 | EnableShadows=1 123 | EnableHighResScope=1 124 | EnableDepthOfField=1 125 | EnablePerPixelLighting=1 126 | MsaaSamples=4 127 | DisableBrowser=0 128 | [Net] 129 | UseProxy=1 130 | 131 | ``` 132 | 133 |
134 | 135 | ## In the game 136 | 137 | _**After you have done all of this setting up. It is very important to calibrate your guitar. If you don't then some strings may not be detected as it happened with me**_ 138 | 139 | ## Credits: 140 | [For Rocksmith.ini](https://www.reddit.com/r/rocksmith/comments/4qt9fa/solution_for_crackling_noisetoo_much_distortion/) 141 | 142 | [For solving issues](https://www.reddit.com/r/rocksmith/comments/i9nbix/help_with_rs_asio/) -------------------------------------------------------------------------------- /docs/behringer_mic2usb/README.md: -------------------------------------------------------------------------------- 1 | # Behringer MIC2 USB 2 | 3 | Very nice sound qulity with combination of Digitech BP355 bass processor. 4 | 5 | Please double-check your ini-files settings before reporting an issue. 6 | 7 | ## First steps 8 | 9 | Run a game, skip calibration, run in-game tuner. 10 | 11 | ## Config files 12 | 13 | **RS_ASIO.ini** 14 | 15 | ```ini 16 | [Config] 17 | EnableWasapiOutputs=0 18 | EnableWasapiInputs=0 19 | EnableAsio=1 20 | 21 | [Asio] 22 | ; available buffer size modes: 23 | ; driver - respect buffer size setting set in the driver 24 | ; host - use a buffer size as close as possible as that requested by the host application 25 | ; custom - use the buffer size specified in CustomBufferSize field 26 | BufferSizeMode=custom 27 | CustomBufferSize=192 28 | [Asio.Output] 29 | Driver=ASIO4ALL v2 30 | BaseChannel=0 31 | EnableSoftwareEndpointVolumeControl=1 32 | EnableSoftwareMasterVolumeControl=1 33 | SoftwareMasterVolumePercent=100 34 | 35 | [Asio.Input.0] 36 | Driver=ASIO4ALL v2 37 | Channel=0 38 | EnableSoftwareEndpointVolumeControl=1 39 | EnableSoftwareMasterVolumeControl=1 40 | SoftwareMasterVolumePercent=100 41 | 42 | [Asio.Input.1] 43 | Driver= 44 | Channel=1 45 | EnableSoftwareEndpointVolumeControl=1 46 | EnableSoftwareMasterVolumeControl=1 47 | SoftwareMasterVolumePercent=100 48 | ``` 49 | 50 | **Rocksmith.ini** 51 | 52 | ```ini 53 | [Audio] 54 | ExclusiveMode=1 55 | LatencyBuffer=1 56 | ForceWDM=0 57 | Win32UltraLowLatencyMode=1 58 | ``` 59 | 60 | ## Troubleshooting 61 | 62 | If the above config results in distorted audio, try changing the `CustomBufferSize` to 192 also in ASIO4ALL control panel. 63 | -------------------------------------------------------------------------------- /docs/boss_gcs_8/README.md: -------------------------------------------------------------------------------- 1 | # BOSS Gigcaster 8 (GCS-8) 2 | 3 | This is the configuration for using a Boss Gigcaster 8 (GCS-8) 4 | for audio output and guitar input on Windows. 5 | 6 | ## Install the Boss GCS-8 Drivers and Switch to MKT-RECORD Mode 7 | To use the ASIO drivers you must switch the Gigcaster to 8 | the audio mode `MKT-RECORD` so that it uses its own driver 9 | to send individual channels over USB (by default it sends 10 | the mix and uses the standard Windows driver not the Boss one). 11 | 12 | First, install the Windows drivers for the Gigcaster, available 13 | from the Boss product support page. 14 | 15 | Now on the Gigcaster, go to the Main Menu, push the three-line 16 | "hamburger" icon to enter the main Menu, push Setup (gear icon) 17 | and then the USB icon. Select audio mode `MKT-RECORD`. Unplug and plug 18 | in the USB cable to make the computer mount the device with the new 19 | Boss driver. 20 | 21 | ## Connect the Guitar with Effects Disabled 22 | Connect the guitar to the front jack and turn off 23 | the effects for the guitar channel to send a clean signal 24 | (Press the Channel 1/guitar button, then Effects to turn them off - 25 | the circle icon at the top should be grey not green). 26 | 27 | Make sure send the Channel 1 (no light in the dashed loudspeaker icon), 28 | and turn of the monitoring to hear only the processed sound from the 29 | computer (no light in the headphone icon). 30 | 31 | The computer mix will go to the USB channel, so put that in your 32 | headphone or the main output. 33 | 34 | ## RS_ASIO.ini Config File 35 | 36 | **RS_ASIO.ini** 37 | 38 | ```ini 39 | ;; This is the configuration for using a Boss Gigcaster 8 (GCS-8) 40 | ;; for audio output and guitar input on Windows. 41 | ;; 42 | ;; To use the ASIO drivers you must switch the Gigcaster to 43 | ;; the audio mode MKT-RECORD so that it uses its own driver 44 | ;; to send individual channels over USB (by default it sends 45 | ;; the mix and uses the standard Windows driver not the Boss one). 46 | ;; First, install the Windows drivers for the Gigcaster, available 47 | ;; from the Boss product support page. 48 | ;; Go to Main Menu, push the three-line "hamburger" icon to enter 49 | ;; the main Menu, push Setup (gear icon) and then the USB icon. 50 | ;; Select audio mode MKT-RECORD. Unplug and plug in the USB cable 51 | ;; to make the computer mount the device with the new Boss driver. 52 | ;; 53 | ;; Connect the guitar to the front jack and turn off 54 | ;; the effects for the guitar channel to send a clean signal 55 | ;; (Press the Channel 1/guitar button, then Effects to turn them off 56 | ;; - the circle icon at the top should be grey not green) 57 | ;; 58 | ;; Make sure send the Channel 1 (no light in the dashed loudspeaker icon), 59 | ;; and turn of the monitoring to hear only the processed sound from the 60 | ;; computer (no light in the headphone icon). 61 | ;; 62 | ;; The computer mix will go to the USB channel, so put that in your 63 | ;; headphone or the main output. 64 | 65 | 66 | # for "EnableWasapiOutputs" you can use -1 to have a message prompting 67 | # to use either WASAPI or ASIO for output every time you boot the game 68 | [Config] 69 | EnableWasapiOutputs=0 70 | EnableWasapiInputs=0 71 | EnableAsio=1 72 | 73 | [Asio] 74 | ; available buffer size modes: 75 | ; driver - respect buffer size setting set in the driver 76 | ; host - use a buffer size as close as possible as that requested by the host application 77 | ; custom - use the buffer size specified in CustomBufferSize field 78 | BufferSizeMode=driver 79 | CustomBufferSize= 80 | 81 | # if your game hangs or crashes on exit, try setting "EnableRefCountHack" to true. 82 | # when blank or invalid, the value of "EnableRefCountHack" will be interpreted as 83 | # true if RS ASIO detects the usage of Asio4All. 84 | # the same applies for all inputs. 85 | [Asio.Output] 86 | Driver=GCS-8 87 | BaseChannel=0 88 | AltBaseChannel= 89 | EnableSoftwareEndpointVolumeControl=1 90 | EnableSoftwareMasterVolumeControl=1 91 | SoftwareMasterVolumePercent=100 92 | EnableRefCountHack= 93 | 94 | [Asio.Input.0] 95 | Driver=GCS-8 96 | Channel=4 97 | EnableSoftwareEndpointVolumeControl=1 98 | EnableSoftwareMasterVolumeControl=1 99 | SoftwareMasterVolumePercent=100 100 | EnableRefCountHack= 101 | 102 | [Asio.Input.1] 103 | Driver= 104 | Channel=1 105 | EnableSoftwareEndpointVolumeControl=1 106 | EnableSoftwareMasterVolumeControl=1 107 | SoftwareMasterVolumePercent=100 108 | EnableRefCountHack= 109 | 110 | [Asio.Input.Mic] 111 | Driver= 112 | Channel=1 113 | EnableSoftwareEndpointVolumeControl=1 114 | EnableSoftwareMasterVolumeControl=1 115 | SoftwareMasterVolumePercent=100 116 | EnableRefCountHack= 117 | 118 | ``` -------------------------------------------------------------------------------- /docs/digidesign/mbox2pro: -------------------------------------------------------------------------------- 1 | [Config] 2 | EnableWasapiOutputs=0 3 | EnableWasapiInputs=0 4 | EnableAsio=1 5 | 6 | [Asio] 7 | ; available buffer size modes: 8 | ; driver - respect buffer size setting set in the driver 9 | ; host - use a buffer size as close as possible as that requested by the host application 10 | ; custom - use the buffer size specified in CustomBufferSize field 11 | BufferSizeMode=custom 12 | CustomBufferSize=128 13 | 14 | [Asio.Output] 15 | Driver=Mbox 2 Pro 16 | BaseChannel=0 17 | EnableSoftwareEndpointVolumeControl=1 18 | EnableSoftwareMasterVolumeControl=1 19 | SoftwareMasterVolumePercent=100 20 | 21 | [Asio.Input.0] 22 | Driver=Mbox 2 Pro 23 | Channel=0 24 | EnableSoftwareEndpointVolumeControl=1 25 | EnableSoftwareMasterVolumeControl=1 26 | SoftwareMasterVolumePercent=100 27 | 28 | [Asio.Input.1] 29 | Driver= 30 | Channel=1 31 | EnableSoftwareEndpointVolumeControl=1 32 | EnableSoftwareMasterVolumeControl=1 33 | SoftwareMasterVolumePercent=100 34 | 35 | [Asio.Input.Mic] 36 | Driver= 37 | Channel=1 38 | EnableSoftwareEndpointVolumeControl=1 39 | EnableSoftwareMasterVolumeControl=1 40 | SoftwareMasterVolumePercent=100 41 | 42 | -------------------------------------------------------------------------------- /docs/focusrite_scarlett_18i8_2nd_gen_OR_asio_link_pro/README.md: -------------------------------------------------------------------------------- 1 | # Focusrite Scarlett 18i8 2nd Gen + (optional) ASIO Link Pro 2 | 3 | ASIO Link Pro is free these days and is useful to capture ASIO OUT within the OS - for streaming, for example) 4 | When using ASIO Link Pro, **do not** pre-start it using ASIO Link Pro Tool - let the game fail to load it (it will complain several times right after start) 5 | and wait about 10 seconds (till Rocksmith 2014 screen with "press Enter") - driver will start around then and you will get sound. 6 | It also helps if you pre-configure a profile in ASIO Link Pro UI and save it - then you won't have to deal with driver selection popup every time. 7 | 8 | ## Config file 9 | 10 | **RS_ASIO.ini** 11 | 12 | ```ini 13 | [Config] 14 | EnableWasapiOutputs=0 15 | EnableWasapiInputs=0 16 | EnableAsio=1 17 | 18 | [Asio] 19 | ; use ["host"] or ["custom" + BufferSizeMode=144] - somehow that's what Rocksmith demands 20 | ; from my driver on Windows 11 and setting any other buffer results in no input (only output). 21 | ; "driver" setting is useless, Focusrite ASIO panel does not allow to set a custom buffer size like "144" 22 | ; and no other buffer size works. 23 | BufferSizeMode=host 24 | CustomBufferSize=144 25 | 26 | [Asio.Output] 27 | ; switch drivers (uncomment & comment) here and further down if using ASIO Link Pro 28 | ;Driver=ASIO Link Pro 29 | Driver=Focusrite USB ASIO 30 | BaseChannel=0 31 | EnableSoftwareEndpointVolumeControl=1 32 | EnableSoftwareMasterVolumeControl=1 33 | SoftwareMasterVolumePercent=100 34 | 35 | [Asio.Input.0] 36 | ;Driver=ASIO Link Pro 37 | Driver=Focusrite USB ASIO 38 | Channel=0 39 | EnableSoftwareEndpointVolumeControl=1 40 | EnableSoftwareMasterVolumeControl=1 41 | SoftwareMasterVolumePercent=100 42 | 43 | [Asio.Input.1] 44 | ;Driver=ASIO Link Pro 45 | Driver=Focusrite USB ASIO 46 | Channel=1 47 | EnableSoftwareEndpointVolumeControl=1 48 | EnableSoftwareMasterVolumeControl=1 49 | SoftwareMasterVolumePercent=100 50 | -------------------------------------------------------------------------------- /docs/focusrite_solo/README.md: -------------------------------------------------------------------------------- 1 | # Focusrite Scarlett Solo Gen 3 2 | 3 | Note: may also work on other Solo generations. 4 | 5 | ## Config file 6 | 7 | **RS_ASIO.ini** 8 | 9 | ```ini 10 | [Config] 11 | EnableWasapiOutputs=0 12 | EnableWasapiInputs=0 13 | EnableAsio=1 14 | 15 | [Asio] 16 | ; available buffer size modes: 17 | ; driver - respect buffer size setting set in the driver 18 | ; host - use a buffer size as close as possible as that requested by the host application 19 | ; custom - use the buffer size specified in CustomBufferSize field 20 | BufferSizeMode=custom 21 | CustomBufferSize=48 22 | 23 | [Asio.Output] 24 | Driver=Focusrite USB ASIO 25 | BaseChannel=0 26 | EnableSoftwareEndpointVolumeControl=1 27 | EnableSoftwareMasterVolumeControl=1 28 | SoftwareMasterVolumePercent=100 29 | 30 | [Asio.Input.0] 31 | Driver=Focusrite USB ASIO 32 | Channel=1 33 | EnableSoftwareEndpointVolumeControl=1 34 | EnableSoftwareMasterVolumeControl=1 35 | SoftwareMasterVolumePercent=100 36 | 37 | [Asio.Input.1] 38 | ;Driver=Focusrite USB ASIO 39 | Channel=1 40 | EnableSoftwareEndpointVolumeControl=1 41 | EnableSoftwareMasterVolumeControl=1 42 | SoftwareMasterVolumePercent=100 43 | ``` 44 | 45 | ## Troubleshooting 46 | 47 | If the above config results in distorted audio, try changing the `CustomBufferSize` to 96 or 192. 48 | -------------------------------------------------------------------------------- /docs/irig_pro_duo_io/README.md: -------------------------------------------------------------------------------- 1 | # iRig Pro Duo I/O 2 | 3 | known working config file for IK Multimedia iRig Pro Duo I/O - https://www.ikmultimedia.com/products/irigproduoio/ 4 | Using "iRig USB ASIO Driver for Windows (v.5.22.0)" ASIO drivers from https://www.ikmultimedia.com/userarea/drivers/ 5 | Tested as of October 28, 2021 6 | 7 | Input 1 on the interface is ASIO input 0, input 2 on device is ASIO input 1, remove the ";" from the driver line of the "[Asio.Input.1]" block to enable multiplayer 8 | 9 | The below "Actual" measured latency figures were recorded by connecting the headphone output to the instrument 1 input and using this utility - https://oblique-audio.com/rtl-utility.php 10 | 11 | Custom buffer values being used here as you can leave the driver set to something more suitable for other software and just force ROcksmith friendly settings on launch. 12 | 16 is too aggressive for Rocksmith in my testing. - Driver reports this as 0.3ms, RTL measures it at 10.1 ms total latency. 13 | 14 | 32 works but has some minor crackling on my test system, might be ok on yours. - Driver reports this as 0.7ms, RTL measures it at 10.4 ms total latency. 15 | 16 | 64 works perfectly on my system and should be a safe starting point. - Driver reports this as 1.3ms, RTL measures it at 11.1 ms total latency. 17 | 128 works well and should be fine even on older systems. - Driver reports this as 2.7ms, RTL measures it at 14.4 ms total latency. 18 | 19 | ## Config file 20 | 21 | **RS_ASIO.ini** 22 | 23 | ```ini 24 | [Config] 25 | EnableWasapiOutputs=0 26 | EnableWasapiInputs=0 27 | EnableAsio=1 28 | 29 | [Asio] 30 | ; available buffer size modes: 31 | ; driver - respect buffer size setting set in the driver 32 | ; host - use a buffer size as close as possible as that requested by the host application 33 | ; custom - use the buffer size specified in CustomBufferSize field 34 | BufferSizeMode=custom 35 | CustomBufferSize=64 36 | 37 | [Asio.Output] 38 | Driver=iRig Device 39 | BaseChannel=0 40 | AltBaseChannel= 41 | EnableSoftwareEndpointVolumeControl=1 42 | EnableSoftwareMasterVolumeControl=1 43 | SoftwareMasterVolumePercent=100 44 | 45 | [Asio.Input.0] 46 | Driver=iRig Device 47 | Channel=0 48 | EnableSoftwareEndpointVolumeControl=1 49 | EnableSoftwareMasterVolumeControl=1 50 | SoftwareMasterVolumePercent=100 51 | 52 | [Asio.Input.1] 53 | ;Driver=iRig Device 54 | Channel=1 55 | EnableSoftwareEndpointVolumeControl=1 56 | EnableSoftwareMasterVolumeControl=1 57 | SoftwareMasterVolumePercent=100 58 | 59 | [Asio.Input.Mic] 60 | Driver= 61 | Channel=1 62 | EnableSoftwareEndpointVolumeControl=1 63 | EnableSoftwareMasterVolumeControl=1 64 | SoftwareMasterVolumePercent=100 65 | ``` 66 | -------------------------------------------------------------------------------- /docs/katana_mk2/README.md: -------------------------------------------------------------------------------- 1 | # BOSS Katana MkII 2 | 3 | **RS_ASIO.ini** 4 | 5 | ```ini 6 | [Config] 7 | ; use WasapiOutput if you want to use the Katana's speaker by feeding in the audio into the aux-in 8 | ; not needed when using headphones (or external speakers) via the rec/headphone out 9 | ; To fix error where Rocksmith gets no signal try to turn the Katana On and Off, and make sure that the guitar is pluged in before you turn it on 10 | EnableWasapiOutputs=0 11 | EnableWasapiInputs=0 12 | EnableAsio=1 13 | 14 | [Asio] 15 | ; available buffer size modes: 16 | ; driver - respect buffer size setting set in the driver 17 | ; host - use a buffer size as close as possible as that requested by the host application 18 | ; custom - use the buffer size specified in CustomBufferSize field 19 | ; use the Katana's driver control panel to configure the buffer size/samplerate (Startmenu → Boss → KATANA) 20 | BufferSizeMode=driver 21 | CustomBufferSize= 22 | 23 | ; output audio via the Katana. NOTE: Audio fed into the Katana using the primary USB-channel is not output 24 | ; via the speaker, so you need to use headphones/plug your speaker into the Katana's rec/headphone out 25 | ; if you want to use the Katana's speaker, you'd need to use a multi-device setup and feed the audio into 26 | ; the aux-in of the Katana 27 | ; if you want to hear Rocksmith through your PC speakers change Driver to Driver= (yes, empty) and set EnableWasapiOutputs=1 under Config at the top. 28 | ; If you want to hear yourself through your speakers (using Rocksmith AMP) put Katana on stand-by mode 29 | ; and if you want to hear yourself through your AMP using your AMP settings go to rocksmith mixer in-game and turn guitar 1 volume to 0 (so you won't hear it twice). 30 | [Asio.Output] 31 | Driver=KATANA 32 | BaseChannel=0 33 | EnableSoftwareEndpointVolumeControl=1 34 | EnableSoftwareMasterVolumeControl=1 35 | SoftwareMasterVolumePercent=100 36 | 37 | ; use the Katana's DI signal output via the SECONDARY input 38 | ; default singal level might be too low for Rocksmith calibration to pass, 39 | ; but in Boss Tone Studio to bump the "dry out level" to 200% if necessary 40 | ; (Boss Tone Studio → System → USB-Settings) 41 | ; or use the SoftwareMasterVolumePercent control below 42 | ; * to use tones by Rocksmith: turn channel volume to zero 43 | ; * to use Katana's tones: use the mixer in Rocksmith to turn the guitar down 44 | [Asio.Input.0] 45 | Driver=KATANA 46 | Channel=2 47 | EnableSoftwareEndpointVolumeControl=1 48 | EnableSoftwareMasterVolumeControl=1 49 | SoftwareMasterVolumePercent=100 50 | 51 | 52 | [Asio.Input.1] 53 | Driver= 54 | Channel=1 55 | EnableSoftwareEndpointVolumeControl=1 56 | EnableSoftwareMasterVolumeControl=1 57 | SoftwareMasterVolumePercent=100 58 | 59 | [Asio.Input.Mic] 60 | Driver= 61 | Channel=1 62 | EnableSoftwareEndpointVolumeControl=1 63 | EnableSoftwareMasterVolumeControl=1 64 | SoftwareMasterVolumePercent=100 65 | 66 | ``` 67 | -------------------------------------------------------------------------------- /docs/linux/ubuntu_1204_lts.md: -------------------------------------------------------------------------------- 1 | # Ubuntu 12.04 - Steam Proton setup 2 | 3 | 4 | ### Configuring Steam 5 | 6 | 1. Install the `steam-installer` package. DO NOT install the Steam snap version. 7 | 2. Open steam and login with your steam account 8 | - It's recommended you launch steam through a command terminal to see extra log information. 9 | 3. Go to menu `Steam -> Settings`, and on the `Compatibility` tab switch on the option `Enable Steam Play for all other titles` 10 | 4. Install Rocksmith 2014 11 | - Since we didn't configure anything else, we should be using the `Proton Experimental` version at this point. 12 | 5. Run the game once to check that the game boots up fine 13 | 14 | ### Installing WineASIO 15 | 16 | In order to install WineASIO we must first add the repositories that contain the `wineasio` package: 17 | 18 | 1. Go to [https://kx.studio/Repositories](https://kx.studio/Repositories) and download the `kxstudio-repos.deb` file 19 | 2. Install the deb file you just downloaded 20 | 3. Open a command terminal and execute `sudo apt-get update` 21 | 22 | Now you should be able to install WineASIO. 23 | 24 | NOTE: we're assuming your steam is installed in `~/.steam/debian-installation/`. If that's not the case for you, change the relevant commands accordingly. 25 | 26 | 1. Open a command terminal and execute `sudo apt-get install wineasio` 27 | 2. Now we need to make the wineasio files available for the installation of the game 28 | - Copy `/usr/lib/i386-linux-gnu/wine/wineasio.dll.so` to `~/.steam/debian-installation/steamapps/common/Proton - Experimental/files/lib/wine/i386-unix` 29 | - Copy `/usr/lib/i386-linux-gnu/wine/wineasio.dll` to `~/.steam/debian-installation/steamapps/common/Proton - Experimental/files/lib/wine/i386-windows` 30 | 3. Make sure the owner of the files is your user, for example: 31 | - `chown your_user:your_user ~/.steam/debian-installation/steamapps/common/Proton\ -\ Experimental/files/lib/wine/i386-unix/wineasio.dll.so` 32 | - `chown your_user:your_user ~/.steam/debian-installation/steamapps/common/Proton\ -\ Experimental/files/lib/wine/i386-windows/wineasio.dll` 33 | 4. Make sure permissions are `r-xr-xr-x` for the files, for example: 34 | - `chmod 555 ~/.steam/debian-installation/steamapps/common/Proton\ -\ Experimental/files/lib/wine/i386-unix/wineasio.dll.so` 35 | - `chmod 555 ~/.steam/debian-installation/steamapps/common/Proton\ -\ Experimental/files/lib/wine/i386-windows/wineasio.dll` 36 | 37 | ### Installing RS-ASIO 38 | 39 | - Copy the contents (`avrt.dll`, `RS_ASIO.dll`, `RS_ASIO.ini`) of [latest release](https://github.com/mdias/rs_asio/releases/latest) (zip archive release-xxx.zip) to the game folder. 40 | - The rest of the guide assumes game folder is `~/.steam/debian-installation/steamapps/common/Rocksmith2014` 41 | - Run the game 42 | - The game should now complain that it has no available audio devices 43 | - Close the game 44 | - The `RS_ASIO-log.txt` file has been generated in the game folder, if you open it you should see the following in the log: 45 | ``` 46 | ... 47 | 0.397 [INFO] GetWineAsioInfo - Looking for wineasio.dll... 48 | 0.422 [INFO] loaded 49 | 0.422 [INFO] path: C:\windows\system32\wineasio.dll 50 | 0.422 [INFO] name: wineasio-rsasio 51 | ... 52 | ``` 53 | 54 | If you see these entries in the log, that means RS-ASIO is able to locate and load your WineASIO installation. However this doesn't mean that WineASIO itself will be able to initialize properly. 55 | 56 | NOTE: RS-ASIO has functionality to try to find wineasio without having to use `regsvr32` to register the wineasio dll. This is the message you see in the log entries above. When it finds it, it will expose it with driver name `wineasio-rsasio`, but if you also registered the wineasio.dll manually, you might have both `wineasio` and `wineasio-rsasio` entries showing on your log; this is not a problem and you can use either, but the `wineasio-rsasio` one has the guarantee that the file exists as RS-ASIO just found it. 57 | 58 | ### Configuring RS-ASIO.ini 59 | 60 | A typical `RS-ASIO.ini` for `wineasio` will look like this: 61 | ``` 62 | [Config] 63 | EnableWasapiOutputs=0 64 | EnableWasapiInputs=0 65 | EnableAsio=1 66 | 67 | [Asio] 68 | ; available buffer size modes: 69 | ; driver - respect buffer size setting set in the driver 70 | ; host - use a buffer size as close as possible as that requested by the host application 71 | ; custom - use the buffer size specified in CustomBufferSize field 72 | ;BufferSizeMode=custom 73 | ;CustomBufferSize= 74 | BufferSizeMode=driver 75 | 76 | [Asio.Output] 77 | Driver=wineasio-rsasio 78 | BaseChannel=0 79 | AltBaseChannel= 80 | EnableSoftwareEndpointVolumeControl=1 81 | EnableSoftwareMasterVolumeControl=1 82 | SoftwareMasterVolumePercent=100 83 | EnableRefCountHack=false 84 | 85 | [Asio.Input.0] 86 | Driver=wineasio-rsasio 87 | Channel=0 88 | EnableSoftwareEndpointVolumeControl=1 89 | EnableSoftwareMasterVolumeControl=1 90 | SoftwareMasterVolumePercent=100 91 | EnableRefCountHack=false 92 | 93 | [Asio.Input.1] 94 | Driver=wineasio-rsasio 95 | Channel=1 96 | EnableSoftwareEndpointVolumeControl=1 97 | EnableSoftwareMasterVolumeControl=1 98 | SoftwareMasterVolumePercent=100 99 | EnableRefCountHack=false 100 | 101 | [Asio.Input.Mic] 102 | Channel= 103 | EnableSoftwareEndpointVolumeControl=1 104 | EnableSoftwareMasterVolumeControl=1 105 | SoftwareMasterVolumePercent=100 106 | EnableRefCountHack=false 107 | ``` 108 | 109 | If you run the game now RS-ASIO will attempt to use WineASIO, which may or may not succeed depending on whether you have it (and jackd) configured correctly. If you launched steam from a command line terminal you should also see some entries there about wineasio attempting to connect to jackd etc. -------------------------------------------------------------------------------- /docs/midiplus_studio_s/README.md: -------------------------------------------------------------------------------- 1 | # MIDIPLUS Studio S 2 | 3 | ## Config files 4 | 5 | **RS_ASIO.ini** 6 | 7 | ```ini 8 | [Config] 9 | EnableWasapiOutputs=0 10 | EnableWasapiInputs=0 11 | EnableAsio=1 12 | 13 | [Asio] 14 | ; available buffer size modes: 15 | ; driver - respect buffer size setting set in the driver 16 | ; host - use a buffer size as close as possible as that requested by the host application 17 | ; custom - use the buffer size specified in CustomBufferSize field 18 | BufferSizeMode=custom 19 | CustomBufferSize=64 20 | 21 | [Asio.Output] 22 | Driver=Midiplus Studio USB 23 | BaseChannel=0 24 | AltBaseChannel= 25 | EnableSoftwareEndpointVolumeControl=1 26 | EnableSoftwareMasterVolumeControl=1 27 | SoftwareMasterVolumePercent=100 28 | 29 | [Asio.Input.0] 30 | Driver=Midiplus Studio USB 31 | Channel=1 32 | EnableSoftwareEndpointVolumeControl=1 33 | EnableSoftwareMasterVolumeControl=1 34 | SoftwareMasterVolumePercent=100 35 | 36 | [Asio.Input.1] 37 | Driver= 38 | Channel=1 39 | EnableSoftwareEndpointVolumeControl=1 40 | EnableSoftwareMasterVolumeControl=1 41 | SoftwareMasterVolumePercent=100 42 | 43 | [Asio.Input.Mic] 44 | Driver= 45 | Channel=1 46 | EnableSoftwareEndpointVolumeControl=1 47 | EnableSoftwareMasterVolumeControl=1 48 | SoftwareMasterVolumePercent=100 49 | 50 | 51 | ## Troubleshooting 52 | 53 | If your audio start cracking, you can increase the `CustomBufferSize` value. 54 | -------------------------------------------------------------------------------- /docs/roland_ua_55/README.md: -------------------------------------------------------------------------------- 1 | # Roland ua55 2 | 3 | ## RockSmith.ini 4 | 5 | the default LatencyBuffer value 4 is the minial working value, you won't need to modifiy it. 6 | 7 | Rolan ua55 driver have a default loopback mixer, you should turn the volume to minial in the roland control panel to avoid loopback. 8 | 9 | ## Config files - Guitar plugged into input 1 10 | 11 | **RS_ASIO.ini** 12 | 13 | ```ini 14 | [Config] 15 | EnableWasapiOutputs=0 16 | EnableWasapiInputs=0 17 | EnableAsio=1 18 | 19 | [Asio] 20 | ; available buffer size modes: 21 | ; driver - respect buffer size setting set in the driver 22 | ; host - use a buffer size as close as possible as that requested by the host application 23 | ; custom - use the buffer size specified in CustomBufferSize field 24 | BufferSizeMode=driver 25 | CustomBufferSize= 26 | 27 | [Asio.Output] 28 | Driver=QUAD CAPTURE 29 | BaseChannel=0 30 | EnableSoftwareEndpointVolumeControl=1 31 | EnableSoftwareMasterVolumeControl=1 32 | SoftwareMasterVolumePercent=100 33 | 34 | [Asio.Input.0] 35 | Driver=QUAD CAPTURE 36 | Channel=0 37 | EnableSoftwareEndpointVolumeControl=1 38 | EnableSoftwareMasterVolumeControl=1 39 | SoftwareMasterVolumePercent=100 40 | 41 | [Asio.Input.1] 42 | Driver= 43 | Channel=1 44 | EnableSoftwareEndpointVolumeControl=1 45 | EnableSoftwareMasterVolumeControl=1 46 | SoftwareMasterVolumePercent=100 47 | ``` 48 | 49 | ## Config file - If plugging in input 2 50 | 51 | **RS_ASIO.ini** 52 | 53 | ```ini 54 | [Config] 55 | EnableWasapiOutputs=0 56 | EnableWasapiInputs=0 57 | EnableAsio=1 58 | 59 | [Asio] 60 | ; available buffer size modes: 61 | ; driver - respect buffer size setting set in the driver 62 | ; host - use a buffer size as close as possible as that requested by the host application 63 | ; custom - use the buffer size specified in CustomBufferSize field 64 | BufferSizeMode=driver 65 | CustomBufferSize= 66 | 67 | [Asio.Output] 68 | Driver=QUAD CAPTURE 69 | BaseChannel=0 70 | EnableSoftwareEndpointVolumeControl=1 71 | EnableSoftwareMasterVolumeControl=1 72 | SoftwareMasterVolumePercent=100 73 | 74 | [Asio.Input.0] 75 | Driver= 76 | Channel=0 77 | EnableSoftwareEndpointVolumeControl=1 78 | EnableSoftwareMasterVolumeControl=1 79 | SoftwareMasterVolumePercent=100 80 | 81 | [Asio.Input.1] 82 | Driver=QUAD CAPTURE 83 | Channel=1 84 | EnableSoftwareEndpointVolumeControl=1 85 | EnableSoftwareMasterVolumeControl=1 86 | SoftwareMasterVolumePercent=100 87 | ``` 88 | -------------------------------------------------------------------------------- /docs/roland_ua_5_usb/AsioAllV2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdias/rs_asio/b04675bb000edb5ec479120513042aaf3dba30e1/docs/roland_ua_5_usb/AsioAllV2.png -------------------------------------------------------------------------------- /docs/roland_ua_5_usb/README.md: -------------------------------------------------------------------------------- 1 | # Roland Edirol ua5 USB 2 | 3 | ![Roland Edirol ua5 USB](edirol-ua-usb.jpg) 4 | 5 | ## Windows Drivers 6 | 7 | Under windows 10, I installed [ASIO all](https://www.asio4all.org/) drivers for this device. 8 | 9 | My Asio4All Setup with the VIA onboard souncard and UA5-USB soundcard: 10 | 11 | ![Asio All setup](AsioAllV2.png) 12 | 13 | ## The logs generated by rs_asio 14 | 15 | **RS_ASIO-log.txt** 16 | 17 | ``` 18 | ... 19 | 0.494 [INFO] AsioHelpers::FindDrivers 20 | 0.495 [INFO] ASIO4ALL v2 21 | 0.495 [INFO] RSAsioDeviceEnum::UpdateAvailableDevices - output requesting ASIO driver: ASIO4ALL v2 22 | 0.495 [INFO] Creating AsioSharedHost - dll: C:\Program Files (x86)\ASIO4ALL v2\asio4all.dll 23 | 1.169 [INFO] info: asio4all detected 24 | 1.169 [INFO] ASIO input channels info: 25 | 1.169 [INFO] 0 - active: 0, channel: 0, group: 0, isInput: 1, type: ASIOSTInt32LSB, name: VIA HD Audio Input 1 26 | 1.169 [INFO] 1 - active: 0, channel: 1, group: 0, isInput: 1, type: ASIOSTInt32LSB, name: VIA HD Audio Input 2 27 | 1.169 [INFO] 2 - active: 0, channel: 2, group: 0, isInput: 1, type: ASIOSTInt32LSB, name: EDIROL UA-5 1 28 | 1.169 [INFO] 3 - active: 0, channel: 3, group: 0, isInput: 1, type: ASIOSTInt32LSB, name: EDIROL UA-5 2 29 | 1.169 [INFO] ASIO output channels info: 30 | 1.169 [INFO] 0 - active: 0, channel: 0, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: VIA HD Audio Headphone 1 31 | 1.169 [INFO] 1 - active: 0, channel: 1, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: VIA HD Audio Headphone 2 32 | 1.169 [INFO] 2 - active: 0, channel: 2, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: VIA HD Audio Output 2 1 33 | 1.169 [INFO] 3 - active: 0, channel: 3, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: VIA HD Audio Output 2 2 34 | 1.169 [INFO] 4 - active: 0, channel: 4, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: VIA HD Audio Output 2 3 35 | 1.169 [INFO] 5 - active: 0, channel: 5, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: VIA HD Audio Output 2 4 36 | 1.169 [INFO] 6 - active: 0, channel: 6, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: VIA HD Audio Output 2 5 37 | 1.169 [INFO] 7 - active: 0, channel: 7, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: VIA HD Audio Output 2 6 38 | 1.169 [INFO] 8 - active: 0, channel: 8, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: EDIROL UA-5 1 39 | 1.169 [INFO] 9 - active: 0, channel: 9, group: 0, isInput: 0, type: ASIOSTInt32LSB, name: EDIROL UA-5 2 40 | 1.169 [INFO] RSAsioDeviceEnum::UpdateAvailableDevices - OK 41 | 1.169 [INFO] RSAsioDeviceEnum::UpdateAvailableDevices - input[0] requesting ASIO driver: ASIO4ALL v2 42 | 1.169 [INFO] RSAsioDeviceEnum::UpdateAvailableDevices - OK 43 | 1.170 [INFO] DebugDeviceEnum::UpdateAvailableDevices - 1 render devices, 1 capture devices 44 | 1.170 [INFO] hr: S_OK 45 | 1.170 [INFO] *ppEndpoint: 21E07530 46 | 1.170 [INFO] DebugDeviceEnum::GetDefaultAudioEndpoint - dataFlow: eCapture - role: eMultimedia 47 | 1.170 [INFO] hr: S_OK 48 | 1.170 [INFO] *ppEndpoint: 21E07410 49 | 1.170 [INFO] DebugDeviceEnum::EnumAudioEndpoints - dataFlow: eAll - dwStateMask: 1 50 | 1.170 [INFO] hr: S_OK 51 | 1.170 [INFO] *ppDevices: 0CE5A1F0 52 | ... 53 | ``` 54 | 55 | ## Config files - Guitar plugged into input 2/R 56 | 57 | This is my configuration, adapt yours according to your RS_ASIO log file. 58 | 59 | **RS_ASIO.ini** 60 | 61 | ```ini 62 | [Config] 63 | EnableWasapiOutputs=0 64 | EnableWasapiInputs=0 65 | EnableAsio=1 66 | 67 | [Asio] 68 | ; available buffer size modes: 69 | ; driver - respect buffer size setting set in the driver 70 | ; host - use a buffer size as close as possible as that requested by the host application 71 | ; custom - use the buffer size specified in CustomBufferSize field 72 | BufferSizeMode=driver 73 | CustomBufferSize= 74 | 75 | [Asio.Output] 76 | Driver=ASIO4ALL v2 77 | ; VIA HD Audio Output 2 78 | BaseChannel=2 79 | ; VIA HD Audio Headphone 80 | AltBaseChannel=1 81 | ; Output Headphone Edirol 82 | ;BaseChannel=8 83 | EnableSoftwareEndpointVolumeControl=1 84 | EnableSoftwareMasterVolumeControl=1 85 | SoftwareMasterVolumePercent=100 86 | 87 | [Asio.Input.0] 88 | Driver=ASIO4ALL v2 89 | Channel=3 90 | EnableSoftwareEndpointVolumeControl=1 91 | EnableSoftwareMasterVolumeControl=1 92 | SoftwareMasterVolumePercent=100 93 | 94 | [Asio.Input.1] 95 | Driver= 96 | Channel=1 97 | EnableSoftwareEndpointVolumeControl=1 98 | EnableSoftwareMasterVolumeControl=1 99 | SoftwareMasterVolumePercent=100 100 | 101 | 102 | [Asio.Input.Mic] 103 | Driver= 104 | Channel=1 105 | EnableSoftwareEndpointVolumeControl=1 106 | EnableSoftwareMasterVolumeControl=1 107 | SoftwareMasterVolumePercent=100 108 | ``` 109 | -------------------------------------------------------------------------------- /docs/roland_ua_5_usb/edirol-ua-usb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdias/rs_asio/b04675bb000edb5ec479120513042aaf3dba30e1/docs/roland_ua_5_usb/edirol-ua-usb.jpg -------------------------------------------------------------------------------- /docs/steinberg_ur12/README.md: -------------------------------------------------------------------------------- 1 | # Steinberg UR12, UR22, UR22mkII 2 | 3 | ## Config files - Guitar plugged into 1/4in (input 2) jack on the interface 4 | 5 | **RS_ASIO.ini** 6 | 7 | ```ini 8 | [Config] 9 | EnableWasapiOutputs=0 10 | EnableWasapiInputs=0 11 | EnableAsio=1 12 | 13 | [Asio] 14 | ; available buffer size modes: 15 | ; driver - respect buffer size setting set in the driver 16 | ; host - use a buffer size as close as possible as that requested by the host application 17 | ; custom - use the buffer size specified in CustomBufferSize field 18 | BufferSizeMode=driver 19 | CustomBufferSize= 20 | 21 | [Asio.Output] 22 | Driver=Yamaha Steinberg USB ASIO 23 | BaseChannel=0 24 | EnableSoftwareEndpointVolumeControl=1 25 | EnableSoftwareMasterVolumeControl=1 26 | SoftwareMasterVolumePercent=100 27 | 28 | [Asio.Input.0] 29 | ;Driver=Yamaha Steinberg USB ASIO 30 | Channel=0 31 | EnableSoftwareEndpointVolumeControl=1 32 | EnableSoftwareMasterVolumeControl=1 33 | SoftwareMasterVolumePercent=100 34 | 35 | [Asio.Input.1] 36 | Driver=Yamaha Steinberg USB ASIO 37 | Channel=1 38 | EnableSoftwareEndpointVolumeControl=1 39 | EnableSoftwareMasterVolumeControl=1 40 | SoftwareMasterVolumePercent=100 41 | ``` 42 | 43 | ## Config file - If plugging in with XLR (input 1) 44 | 45 | **RS_ASIO.ini** 46 | 47 | ```ini 48 | [Config] 49 | EnableWasapi=0 50 | EnableAsio=1 51 | 52 | [Asio] 53 | ; available buffer size modes: 54 | ; driver - respect buffer size setting set in the driver 55 | ; host - use a buffer size as close as possible as that requested by the host application 56 | ; custom - use the buffer size specified in CustomBufferSize field 57 | BufferSizeMode=driver 58 | CustomBufferSize= 59 | 60 | [Asio.Output] 61 | Driver=Yamaha Steinberg USB ASIO 62 | EnableSoftwareEndpointVolumeControl=1 63 | EnableSoftwareMasterVolumeControl=1 64 | SoftwareMasterVolumePercent=100 65 | 66 | [Asio.Input.0] 67 | Driver=Yamaha Steinberg USB ASIO 68 | Channel=0 69 | EnableSoftwareEndpointVolumeControl=1 70 | EnableSoftwareMasterVolumeControl=1 71 | SoftwareMasterVolumePercent=100 72 | 73 | [Asio.Input.1] 74 | ;Driver=Yamaha Steinberg USB ASIO 75 | Channel=1 76 | EnableSoftwareEndpointVolumeControl=1 77 | EnableSoftwareMasterVolumeControl=1 78 | SoftwareMasterVolumePercent=100 79 | ``` -------------------------------------------------------------------------------- /docs/streaming/README.md: -------------------------------------------------------------------------------- 1 | # Streaming while using RS ASIO 2 | 3 | If you're using RS ASIO for audio output you will find that you cannot stream the audio coming out of the game. This happens because ASIO audio bypasses the windows audio layer and as such other applications cannot detect it. 4 | There are 2 ways to solve this. 5 | 6 | ### Easy way 7 | 8 | The easiest way to solve this is to avoid using the ASIO path for output, **at the cost of potentially a bit higher latency**. You can do this by enabling WASAPI outputs, and disabling the ASIO output in the config file like so (note the empty `Driver=` part in the `Asio.Output` section): 9 | 10 | ```ini 11 | [Config] 12 | EnableWasapiOutputs=1 13 | EnableWasapiInputs=0 14 | EnableAsio=1 15 | 16 | [Asio] 17 | ... 18 | 19 | [Asio.Output] 20 | Driver= 21 | ... 22 | 23 | [Asio.Input.0] 24 | ... 25 | 26 | [Asio.Input.1] 27 | ... 28 | ``` 29 | 30 | ### The complicated way 31 | 32 | There are ways to do this that will still allow you to maintain the low latency, but this is a lot more complex as you'll have to route ASIO audio signals around until it reaches a virtual input device that is more likely to be useable by your streaming software. 33 | 34 | Some guides are available elsewhere to do this kind of thing: 35 | 36 | - [DeathlySin's tutorial for OBS streaming/recording with ASIO + VoiceMeeter](https://www.reddit.com/r/rocksmith/comments/kv6z9f/rs_asio_guide_including_routing_with_voicemeeter/) 37 | - [lastpixel.tv/low-latency-rocksmith-obs-streaming-with-software-effects/](https://lastpixel.tv/low-latency-rocksmith-obs-streaming-with-software-effects/) 38 | - [raidntrade.com/tutorials/RS-ASIO-VoiceMeeter.html](https://raidntrade.com/tutorials/RS-ASIO-VoiceMeeter.html) 39 | 40 | Keep in mind these guides are not maintained by me, and if you need further help setting this up your best bet is to ask for help on [www.reddit/r/rocksmith](https://www.reddit.com/r/rocksmith/) -------------------------------------------------------------------------------- /docs/streaming/README_CN.md: -------------------------------------------------------------------------------- 1 | # 在使用RS ASIO的情况下使用流式传输 2 | 3 | 在使用RS ASIO的时候你会发现流式传输无法正常传输游戏的音频输出。这是因为ASIO音频会绕过Windows的音频层来让其他应用程序无法检测到它。 4 | 5 | 这里有两种解决办法。 6 | 7 | ### 简易方法 8 | 9 | 最简单的办法就是不用ASIO来输出音频,但是代价是**会有更大的声音延迟**。你可以通过下述方式来启用WASAPI的音频输出,并在配置文件里禁用ASIO的音频输出(注意在`Asio.Output`部分`Driver=`这里设置为空): 10 | 11 | ```ini 12 | [Config] 13 | EnableWasapiOutputs=1 14 | EnableWasapiInputs=0 15 | EnableAsio=1 16 | 17 | [Asio] 18 | ... 19 | 20 | [Asio.Output] 21 | Driver= 22 | ... 23 | 24 | [Asio.Input.0] 25 | ... 26 | 27 | [Asio.Input.1] 28 | ... 29 | ``` 30 | 31 | ### 复杂方法 32 | 33 | 有几种方法可以让你仍然拥有ASIO的低延迟优势,但是步骤要更复杂些。你需要把ASIO的音频信号转发到一个虚拟的输入设备上,然后就可以被你的串流软件检测到。 34 | 35 | 这里有一些实现的方法: 36 | 37 | - [DeathlySin's tutorial for OBS streaming/recording with ASIO + VoiceMeeter](https://www.reddit.com/r/rocksmith/comments/kv6z9f/rs_asio_guide_including_routing_with_voicemeeter/) 38 | - [lastpixel.tv/low-latency-rocksmith-obs-streaming-with-software-effects/](https://lastpixel.tv/low-latency-rocksmith-obs-streaming-with-software-effects/) 39 | - [raidntrade.com/tutorials/RS-ASIO-VoiceMeeter.html](https://raidntrade.com/tutorials/RS-ASIO-VoiceMeeter.html) 40 | 41 | 要注意这些指南并不是由我编写的。如果你遇到了问题最好在[www.reddit/r/rocksmith](https://www.reddit.com/r/rocksmith/)上寻求帮助。 -------------------------------------------------------------------------------- /docs/universal_audio_volt1/README.md: -------------------------------------------------------------------------------- 1 | Steps to get RS_ASIO working on a Universal Audio Volt 1 interface. 2 | 3 | This guide should work for all devices in the UA Volt series, but adjust the Input0 channel if you are not using input 0. We will also go over how to download the driver for this device. If you have already installed UA Connect and registered your Volt, skip to step 8. 4 | 5 | 6 | 1. Plug in your UA Volt interface 7 | 2. Go to the Universal Audio website, and their [downloads page](https://www.uaudio.com/downloads) 8 | 3. Click on the Volt option 9 | 4. Download the UA Connect software for Windows 10 | 5. Install the software 11 | 6. Once you get into the software, it should notice your UA Volt device. Register your device by logging into / creating a UA account. 12 | 7. After you register, it should ask to install the drivers. 13 | 8. Go to your Rocksmith folder 14 | 9. Paste the following into your RS_ASIO.ini file. 15 | 16 | ```ini 17 | [Config] 18 | EnableWasapiOutputs=0 19 | EnableWasapiInputs=0 20 | EnableAsio=1 21 | 22 | [Asio] 23 | ; available buffer size modes: 24 | ; driver - respect buffer size setting set in the driver 25 | ; host - use a buffer size as close as possible as that requested by the host application 26 | ; custom - use the buffer size specified in CustomBufferSize field 27 | BufferSizeMode=custom 28 | CustomBufferSize=48 29 | 30 | [Asio.Output] 31 | Driver=Universal Audio Volt 32 | BaseChannel=0 33 | AltBaseChannel= 34 | EnableSoftwareEndpointVolumeControl=1 35 | EnableSoftwareMasterVolumeControl=1 36 | SoftwareMasterVolumePercent=100 37 | 38 | [Asio.Input.0] 39 | Driver=Universal Audio Volt 40 | Channel=0 41 | EnableSoftwareEndpointVolumeControl=1 42 | EnableSoftwareMasterVolumeControl=1 43 | SoftwareMasterVolumePercent=100 44 | 45 | [Asio.Input.1] 46 | Driver= 47 | Channel=0 48 | EnableSoftwareEndpointVolumeControl=1 49 | EnableSoftwareMasterVolumeControl=1 50 | SoftwareMasterVolumePercent=100 51 | 52 | [Asio.Input.Mic] 53 | Driver= 54 | Channel= 55 | EnableSoftwareEndpointVolumeControl= 56 | EnableSoftwareMasterVolumeControl= 57 | SoftwareMasterVolumePercent= 58 | ``` 59 | 60 | Note: This will default your input to channel 0 (left-most jack), and sends the audio output to the Volt device. If you want to use headphones / speakers that are not connected to the Volt, remove the text in the Driver line of Asio.Output and set EnableWasapiOutputs under Config to 1. 61 | 62 | 10. Now in your Rocksmith.ini file, make sure you have both ExclusiveMode and Win32UltraLowLatencyMode set to 1 as those are required for RS_ASIO to work properly. 63 | 11. Launch Rocksmith 64 | 65 | I hope this helps! 66 | -------------------------------------------------------------------------------- /docs/universal_audio_volt_276/README.md: -------------------------------------------------------------------------------- 1 | Steps to get RS_ASIO working on a Universal Audio Volt 276 interface. 2 | 3 | This guide should work for all devices in the UA Volt series, but adjust the Input0 channel if you are not using input 0. We will also go over how to download the driver for this device. If you have already installed UA Connect and registered your Volt, skip to step 8. 4 | 5 | 6 | 1. Plug in your UA Volt interface 7 | 2. Go to the Universal Audio website, and their [downloads page](https://www.uaudio.com/downloads/ua-connect/) 8 | 3. Click on the download for your OS (macOS/Windows) 9 | 4. Download the UA Connect software for your OS 10 | 5. Install the software 11 | 6. Once you get into the software, it should notice your UA Volt device. Register your device by logging into / creating a UA account. 12 | 7. After you register, it should ask to install the drivers. 13 | 8. Go to your Rocksmith folder 14 | 9. Paste the following into your RS_ASIO.ini file. 15 | 16 | ```ini 17 | # for "EnableWasapiOutputs" you can use -1 to have a message prompting 18 | # to use either WASAPI or ASIO for output every time you boot the game 19 | [Config] 20 | EnableWasapiOutputs=0 21 | EnableWasapiInputs=0 22 | EnableAsio=1 23 | 24 | [Asio] 25 | ; available buffer size modes: 26 | ; driver - respect buffer size setting set in the driver 27 | ; host - use a buffer size as close as possible as that requested by the host application 28 | ; custom - use the buffer size specified in CustomBufferSize field 29 | BufferSizeMode=driver 30 | CustomBufferSize= 31 | 32 | # if your game hangs or crashes on exit, try setting "EnableRefCountHack" to true. 33 | # when blank or invalid, the value of "EnableRefCountHack" will be interpreted as 34 | # true if RS ASIO detects the usage of Asio4All. 35 | # the same applies for all inputs. 36 | [Asio.Output] 37 | Driver=Universal Audio Volt 38 | BaseChannel=0 39 | AltBaseChannel= 40 | EnableSoftwareEndpointVolumeControl=1 41 | EnableSoftwareMasterVolumeControl=1 42 | SoftwareMasterVolumePercent=100 43 | EnableRefCountHack= 44 | 45 | [Asio.Input.0] 46 | Driver=Universal Audio Volt 47 | Channel=0 48 | EnableSoftwareEndpointVolumeControl=1 49 | EnableSoftwareMasterVolumeControl=1 50 | SoftwareMasterVolumePercent=100 51 | EnableRefCountHack= 52 | 53 | [Asio.Input.1] 54 | Driver=Universal Audio Volt 55 | Channel=1 56 | EnableSoftwareEndpointVolumeControl=1 57 | EnableSoftwareMasterVolumeControl=1 58 | SoftwareMasterVolumePercent=100 59 | EnableRefCountHack= 60 | 61 | ;[Asio.Input.Mic] 62 | ;Driver= 63 | ;Channel=1 64 | ;EnableSoftwareEndpointVolumeControl=1 65 | ;EnableSoftwareMasterVolumeControl=1 66 | ;SoftwareMasterVolumePercent=100 67 | ;EnableRefCountHack= 68 | ``` 69 | 70 | Note: This will default your input to channel 0 (left-most jack), and sends the audio output to the Volt device. If you want to use headphones / speakers that are not connected to the Volt, remove the text in the Driver line of Asio.Output and set EnableWasapiOutputs under Config to 1. 71 | 72 | 10. Now in your Rocksmith.ini file, make sure you have both ExclusiveMode and Win32UltraLowLatencyMode set to 1 as those are required for RS_ASIO to work properly. 73 | 11. Launch Rocksmith 74 | 75 | I hope this helps! 76 | -------------------------------------------------------------------------------- /docs/xtone_smartstomp/README.md: -------------------------------------------------------------------------------- 1 | # XTONE Smart Stomp 2 | 3 | Those settings worked on Lenovo Thinkpad X1 Extreme for [XTONE Smart Stomp](https://www.xsonic.cc/XTONE). 4 | 5 | I found setting custom buffer size from file more convenient than messing with ASIO4All slider. 6 | 7 | In theory, this approach should work for every ASIO4All device. Note that I've disabled all other ASIO4All devices for simplicity 8 | 9 | ## Rocksmith.ini 10 | 11 | ```txt 12 | LatencyBuffer=2 13 | ``` 14 | 15 | ## RS_ASIO.ini 16 | 17 | ```txt 18 | [Asio.Output] 19 | Driver=ASIO4ALL v2 20 | ... 21 | 22 | [Asio.Input.0] 23 | Driver=ASIO4ALL v2 24 | ... 25 | ``` 26 | 27 | ![settings](xtone_smartstomp.png "xtone smartstomp") 28 | -------------------------------------------------------------------------------- /docs/xtone_smartstomp/xtone_smartstomp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mdias/rs_asio/b04675bb000edb5ec479120513042aaf3dba30e1/docs/xtone_smartstomp/xtone_smartstomp.png --------------------------------------------------------------------------------