diff --git a/.clang-format-ignore b/.clang-format-ignore index 4b7b414bb9..4bc1bc359f 100644 --- a/.clang-format-ignore +++ b/.clang-format-ignore @@ -1,3 +1,4 @@ # exclude third party code from clang-format checks ./libs ./windows/nsProcess +./windows/ASIOSDK2 diff --git a/.gitignore b/.gitignore index 4d7e05e0fb..e446bd6938 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,10 @@ Makefile *.pro.user* **.cppe **.he +.vs .cproject .project -.settings +.settings .idea .vscode .cache @@ -42,7 +43,18 @@ distributions/fluidsynth* distributions/jamulus.desktop distributions/jamulus-server.desktop .xcode +*.xcodeproj +._* +*.bak +*.log Debug-iphoneos/ Jamulus.xcodeproj jamulus_plugin_import.cpp autoLatestChangelog.md +*.pro.user +*.pro.user.* +*.sln +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +Jamulus*.ps1 diff --git a/Jamulus.pro b/Jamulus.pro index 7783099ae6..e699fb6e60 100644 --- a/Jamulus.pro +++ b/Jamulus.pro @@ -1,4 +1,4 @@ -VERSION = 3.8.2rc1dev +VERSION = 3.8.2dev # use target name which does not use a capital letter at the beginning contains(CONFIG, "noupcasename") { @@ -66,17 +66,19 @@ DEFINES += APP_VERSION=\\\"$$VERSION\\\" \ DEFINES += QT_NO_DEPRECATED_WARNINGS win32 { - DEFINES -= UNICODE # fixes issue with ASIO SDK (asiolist.cpp is not unicode compatible) + DEFINES -= UNICODE # fixes issue with ASIO SDK (asiolist.cpp is not unicode compatible) pgScorpio: is this still valid ??? asiolist.cpp no longer exists! DEFINES += NOMINMAX # solves a compiler error in qdatetime.h (Qt5) - HEADERS += windows/sound.h - SOURCES += windows/sound.cpp \ - windows/ASIOSDK2/common/asio.cpp \ - windows/ASIOSDK2/host/asiodrivers.cpp \ - windows/ASIOSDK2/host/pc/asiolist.cpp + INCLUDEPATH += windows/ASIOSDK2/common + HEADERS += \ + windows/ASIOSDK2/common/asio.h \ + windows/ASIOSDK2/common/iasiodrv.h \ + windows/asiosys.h \ + windows/asiodriver.h \ + windows/sound.h + SOURCES += \ + windows/asiodriver.cpp \ + windows/sound.cpp RC_FILE = windows/mainicon.rc - INCLUDEPATH += windows/ASIOSDK2/common \ - windows/ASIOSDK2/host \ - windows/ASIOSDK2/host/pc mingw* { LIBS += -lole32 \ -luser32 \ @@ -114,8 +116,10 @@ win32 { HEADERS -= windows/sound.h SOURCES -= windows/sound.cpp - HEADERS += linux/sound.h - SOURCES += linux/sound.cpp + HEADERS += linux/sound.h \ + linux/jackclient.h + SOURCES += linux/sound.cpp \ + linux/jackclient.cpp DEFINES += WITH_JACK DEFINES += JACK_REPLACES_ASIO DEFINES += _STDINT_H # supposed to solve compilation error in systemdeps.h @@ -137,10 +141,10 @@ win32 { } QT += macextras - HEADERS += mac/sound.h - SOURCES += mac/sound.cpp - HEADERS += mac/activity.h - OBJECTIVE_SOURCES += mac/activity.mm + HEADERS += mac/activity.h \ + mac/sound.h + SOURCES += mac/sound.cpp + OBJECTIVE_SOURCES += mac/activity.mm CONFIG += x86 QMAKE_TARGET_BUNDLE_PREFIX = io.jamulus QMAKE_APPLICATION_BUNDLE_NAME. = $$TARGET @@ -184,8 +188,10 @@ win32 { HEADERS -= mac/sound.h SOURCES -= mac/sound.cpp - HEADERS += linux/sound.h - SOURCES += linux/sound.cpp + HEADERS += linux/sound.h \ + linux/jackclient.h + SOURCES += linux/sound.cpp \ + linux/jackclient.cpp DEFINES += WITH_JACK DEFINES += JACK_REPLACES_COREAUDIO INCLUDEPATH += /usr/local/include @@ -349,8 +355,10 @@ win32 { # unnecessarily without this workaround (#741): QMAKE_LFLAGS += -Wl,--as-needed - HEADERS += linux/sound.h - SOURCES += linux/sound.cpp + HEADERS += linux/sound.h \ + linux/jackclient.h + SOURCES += linux/sound.cpp \ + linux/jackclient.cpp # we assume to have lrintf() one moderately modern linux distributions # would be better to have that tested, though diff --git a/JamulusWin-README.md b/JamulusWin-README.md new file mode 100644 index 0000000000..78dba4a107 --- /dev/null +++ b/JamulusWin-README.md @@ -0,0 +1,25 @@ +JamulusWin.* files for builing the windows version using visual studio 2019 (by pgScorpio) + +#================================================================================================== +# WARNING: +# The vcx project files in this folder are heavily modified to make them movable. +# DO NOT REGENERATE VCX FILES FROM Qt! +# When the Jamulus pro file is changed, the vcx files need (often manual) editing too! +# +# In Visual Studio NEVER use ABSOLUTE PATHS but always use the appropriate variables. +# In Visual Studio NEVER use PERSONAL PATHS but always use appropriate environment variables. +# +# Do NOT directly open the JamulusWin.vcxproj or JamulusWin.sln files by double-clicking ! +# ALWAYS start Visual studio using this script from Powershell using./JamulusWin.ps1 --startvs +#================================================================================================== + + +JamulusWin.ps1 is a PowerShell script to setup the environment needed for builing with Visual Studio. +JamulusWin.ps1 also handles all Custom Build Steps during building of the project. (Modify when needed.) + +Run ./JamulusWin.ps1 without parameters for command options info. + +You should check the variables defined in JamulusWin.ps1 to reflect your Qt installation + +You can modify the 'User Defined functions' in JamulusWin.ps1 if needed. + diff --git a/JamulusWin.ps1 b/JamulusWin.ps1 new file mode 100644 index 0000000000..805602fd18 --- /dev/null +++ b/JamulusWin.ps1 @@ -0,0 +1,389 @@ +#================================================================================================== +# Visual Studio Tool Script by pgScorpio +#================================================================================================== +Add-Type -AssemblyName 'PresentationFramework' +#================================================================================================== +# WARNING: +# The vcx project files in this folder are heavily modified to make them movable. +# DO NOT REGENERATE VCX FILES FROM Qt! +# When the Jamulus pro file is changed, the vcx files need (often manual) editing too! +# +# In Visual Studio NEVER use ABSOLUTE PATHS but always use the appropriate variables. +# In Visual Studio NEVER use PERSONAL PATHS but always use appropriate environment variables. +# +# Do NOT directly open the JamulusWin.vcxproj or JamulusWin.sln files by double-clicking +# or by opening them from Visual Studio IDE ! +# +# ALWAYS start Visual studio using this script from Powershell using./JamulusWin.ps1 --startvs +#================================================================================================== + +# General Env names in Visual Studio (Though there ARE several exceptions!!): +# $(*Dir) Ends with \ or / Undefined variable defaults to the root of the current drive +# $(*Path) Does NOT end with \ or / Undefined variable defaults to the current folder (But $(*Path) is also used for filenames!) + + +#================================================================================================== +# Modify this section to reflect your Qt and Visual Studio installation. +#================================================================================================== + +$QtBase = "C:\Qt\5.15.2" +$QtMSVC = "msvc2019" + +$Jamulus_Version="3.8.2" +#For development versions uncomment next line: +$Jamulus_Version =$Jamulus_Version + 'dev-'+$(git describe --match=xxxxxxxxxxxxxxxxxxxx --always --abbrev --dirty) + +#================================================================================================== +# Fixed settings: +#================================================================================================== + +$QtSrcPath = "$("$QtBase\$QtMSVC")_64" +$QtBinPath = "$QtSrcPath\bin" + +#QtSrcDir and QtBinDir will be set accordingly with added \ + +#================================================================================================== +# configuration for this script +#================================================================================================== +$BuildType = "" + +$InfoForeground = "Green" +$InfoBackground = "DarkBlue" + +$WarningForeground = "Yellow" +$WarningBackground = "Black" + +$ErrorForeground = "Red" +$ErrorBackground = "Black" + +$PromptForeground = "Yellow" +$PromptBackground = "DarkBlue" + + +#================================================================================================== +# Get project name and location +#================================================================================================== + +# Scriptname should match the vcx Project Name! +$vcxproject_name = [io.path]::GetFileNameWithoutExtension( $MyInvocation.MyCommand.Name ) +$vcxproject_file = "$vcxproject_name.vcxproj" + +# The full folder path in which the vcxproj should be located (Same as this script location) +$ProjectFolderPath = [io.path]::GetDirectoryName( $MyInvocation.MyCommand.Path ) + + +#================================================================================================== +# Script tools: +#================================================================================================== + +function info( $text ) +{ + Write-Host $text -ForegroundColor $InfoForeground -BackgroundColor $InfoBackground +} + +function warning( $text ) +{ + + Write-Host $text -ForegroundColor $WarningForeground -BackgroundColor $WarningBackground +} + +function error( $text ) +{ + Write-Host $text -ForegroundColor $ErrorForeground -BackgroundColor $ErrorBackground +} + + +## safe getEnv: return empty string if var does not exist... +function getEnv( [string] $var ) +{ + if ( Test-Path "Env:$($var)" ) + { + return (Get-Item -Path Env:$($var)).Value + } + else + { + return "" + } +} + +## verbose setEnv: reports changes... +function setEnv( [string] $var, [string] $value ) +{ + if ( $(getEnv( $var )) -ne $value ) + { + Set-Item -Path Env:$var -Value $value + Write-Host "$var set to '$(getEnv($var))' !" -ForegroundColor $InfoForeground -BackgroundColor $InfoBackground + } +} + +## safe getEnv: return empty string if var does not exist... +function AddToPath( [string]$addPath ) +{ + if ( Test-Path $addPath ) + { + $arrPath = $Env:Path -split ';' + + $found = $false + foreach( $dir in $arrPath) + { + if ($dir -eq $addPath) + { + $found = $true; + } + } + + if ( -not ($found) ) + { + $last = ($arrPath.length - 1) + if ( $arrPath[$last] -eq '' ) + { + $arrPath[$last] = $addPath + } + else + { + $arrPath += $addPath + } + + info "Adding $addPath to PATH" + } + else + { + # Already in path! + return + } + + $Env:Path = $arrPath -join ';' + + return + } + else + { + error ( @( + "Dir $addPath does not exist! " + "So it was NOT addded to Path !" + ) -join "`n" ) + + + return + } +} + + +#============================================================================= +# 'User Defined' functions: +#============================================================================= + +function CheckEnvironment() +{ + if ( -not ( Test-Path -Path ".\$vcxproject_file" ) ) + { + error "Error: This script must be run from the folder containing the $vcxproject_file file!" + exit 1; + } + + Write-Host "Checking environment for $vcxproject_name...." + + + + #BuildDir should NOT be located under the Jamulus repo folder! + #so we set BuildDir to a build folder on the same level as the repo folder... + $BuildRoot = [io.path]::GetFullPath( "$ProjectFolderPath\..\build" ) + # Use the same repo/project folder structure as the current repo + # The folder name in which the vcxproj is located + $ProjectFolderName = [io.path]::GetFileName( $ProjectFolderPath ) + $BuildPath = "$BuildRoot\$ProjectFolderName\$vcxproject_name" + #BuildDir will be set accordingly with added \ + + + AddToPath $QtBinPath + + + #========================================================================= + # Add user defined paths here: + #========================================================================= + # Use: AddToPath "NEWPATH" + + #========================================================================= + + + setEnv 'VCProjectPath' $PWD + setEnv 'VCProjectDir' "$PWD\" + + setEnv 'BuildPath' $BuildPath + setEnv 'BuildDir' "$BuildPath\" + + setEnv 'QtBase' $QtBase + setEnv 'QtSrcPath' $QtSrcPath + setEnv 'QtSrcDir' "$QtSrcPath\" + setEnv 'QtBinPath' $QtBinPath + setEnv 'QtBinDir' "$QtBinPath\" + setEnv 'QMAKESPEC' $QMAKESPEC + + setEnv 'Jamulus_Version' $Jamulus_Version + + #========================================================================= + # Add user defined environment variables here: + #========================================================================= + # Use: setEnv "NAME" "VALUE" + + #========================================================================= + + + Write-Host "Environment set" +} + + +function PreBuild +{ + # Custom Pre-Build step called from VS. $BuildType should be debug | release + + # Save the current build environment to a textfile in the current build folder. + cmd /c "set > $Env:BuildDir$BuildType\build-env.txt" + + # Dirty Fix for: warning MSB8064: Custom build for item "..\build\JamulusWin\release\moc_predefs.h.cbt" succeeded, + # but specified dependency "f:\repos\build\jamuluswin\release\moc_predefs.h.cbt" does not exist. + # This may cause incremental build to work incorrectly + + $moc_predefs = "$Env:BuildDir$BuildType\moc\moc_predefs.h" + + if ( -not ( Test-Path -Path "$moc_predefs" ) ) + { + #remove cbt file after a clean + if ( Test-Path -Path "$moc_predefs.cbt" ) + { + echo "Prebuils: Deleting $moc_predefs.cbt..." + Remove-Item "$moc_predefs.cbt" + } + } +} + +function PreLink +{ + # Custom Pre-Link step called from VS. $BuildType should be debug | release + + + # Dirty Fix for: warning MSB8064: Custom build for item "..\build\JamulusWin\release\moc_predefs.h.cbt" succeeded, + # but specified dependency "f:\repos\build\jamuluswin\release\moc_predefs.h.cbt" does not exist. + # This may cause incremental build to work incorrectly + + $moc_predefs = "$Env:BuildDir$BuildType\moc\moc_predefs.h" + + if ( ( Test-Path -Path "$moc_predefs" ) -and (-not ( Test-Path -Path "$moc_predefs.cbt" )) ) + { + echo "Prelink: Generating $moc_predefs.cbt..." + + "Generated by $($vcxproject_name).ps1 --prebuild $BuildType" > "$moc_predefs.cbt" + } +} + +function PostBuild +{ + # Custom Post-Build step called from VS. $args[1] should be debug | release + # WARNING: This step is ALSO called by Visual Studio BEFORE a re-build ! +} + + + +#============================================================================= +#============================================================================= + +function help +{ + echo "" + echo "==================================================================================================" + echo "$($vcxproject_name).ps1: (toolset script by psScorpio for Visual Studio project $vcxproject_name.)" + echo "==================================================================================================" + echo "" + echo "parameters: *)" + echo "--setenv Just set the environment for the project as defined in this script." + echo "--startvs Set the environment and open $vcxproject_name.vcxproj." + echo "" + echo "Also handles Custom build events for VS build with parameters:" + echo "--prebuild debug | release" + echo "--prelink debug | release" + echo "--postbuild debug | release" + echo "" + echo "*) $($vcxproject_name).ps1 must be run from the folder containing the $vcxproject_name project files." + echo " Do NOT open the project in Visual studio without setting the environment first!" + echo "" + echo "==================================================================================================" +} + +if ( $args.count -ge 1 ) +{ + $option = $args[0] + + if ( $args.count -ge 2 ) + { + $BuildType = $args[1] + } + + if ( $option -eq "--setenv" ) + { + CheckEnvironment + exit 0 + } + + if ( $option -eq "--startvs" ) + { + CheckEnvironment + Invoke-Expression -Command ".\$vcxproject_file" + exit + } + + if ( $option -eq "--prebuild" ) + { + echo "Custom Pre-Build step for $vcxproject_name $BuildType" + + if ( $args.count -ge 2 ) + { + PreBuild + exit 0 + } + + echo "Custom Pre-Build step missing BuildType parameter !" + exit 1; + } + + if ( $option -eq "--prelink" ) + { + echo "Custom Pre-Link step for $vcxproject_name $BuildType" + + if ( $args.count -ge 2 ) + { + PreLink + exit 0; + } + + echo "Custom Pre-Link step missing BuildType parameter !" + exit 1 + } + + if ( $option -eq "--postbuild" ) + { + echo "Custom Post-Build step for $vcxproject_name $BuildType" + + if ( $args.count -ge 2 ) + { + PostBuild + exit 0; + } + + echo "Custom Post-Build step missing BuildType parameter !" + exit 1 + } + + if ( $option -eq "--help" ) + { + Help + exit 0; + } + + error "Invalid parameter $option" + help + exit 1; + } + + help + exit 1; + \ No newline at end of file diff --git a/JamulusWin.sln b/JamulusWin.sln new file mode 100644 index 0000000000..2a51e8a625 --- /dev/null +++ b/JamulusWin.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32126.315 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JamulusWin", "JamulusWin.vcxproj", "{15DF5CF0-7981-3697-9DD5-FC2FD7374F49}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {15DF5CF0-7981-3697-9DD5-FC2FD7374F49}.Debug|x64.ActiveCfg = Debug|x64 + {15DF5CF0-7981-3697-9DD5-FC2FD7374F49}.Debug|x64.Build.0 = Debug|x64 + {15DF5CF0-7981-3697-9DD5-FC2FD7374F49}.Release|x64.ActiveCfg = Release|x64 + {15DF5CF0-7981-3697-9DD5-FC2FD7374F49}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CA894F71-E94A-4011-B95F-8188A921E80C} + EndGlobalSection +EndGlobal diff --git a/JamulusWin.vcxproj b/JamulusWin.vcxproj new file mode 100644 index 0000000000..7d81e7f87e --- /dev/null +++ b/JamulusWin.vcxproj @@ -0,0 +1,1721 @@ + + + + + Release + x64 + + + Debug + x64 + + + + {15DF5CF0-7981-3697-9DD5-FC2FD7374F49} + Jamulus + Qt4VSv1.0 + 10.0 + + + + v142 + $(BuildDir)release\ + false + NotSet + Application + $(BuidDir)release\temp\ + Jamulus + + + v142 + $(BuildDir)debug\ + false + NotSet + Application + $(BuidDir)debug\temp\ + Jamulus + + + + + + + + + + + + $(BuildDir)release\ + $(BuildDir)release\temp\ + Jamulus + true + false + $(BuildDir)debug\ + $(BuildDir)debug\temp\ + Jamulus + true + + + + src;libs\opus\include;libs\opus\celt;libs\opus\silk;libs\opus\silk\float;libs\opus\silk\fixed;libs\opus;$(QtSrcPath)\include;$(QtSrcPath)\include\QtWidgets;$(QtSrcPath)\include\QtGui;$(QtSrcPath)\include\QtANGLE;$(QtSrcPath)\include\QtNetwork;$(QtSrcPath)\include\QtXml;$(QtSrcPath)\include\QtConcurrent;$(QtSrcPath)\include\QtCore;$(OutputPath)moc;$(OutputPath)ui;windows\ASIOSDK2\common + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + None + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + MaxSpeed + _WINDOWS;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;APP_VERSION="$(Jamulus_Version)";CUSTOM_MODES;_REENTRANT;QT_NO_DEPRECATED_WARNINGS;NOMINMAX;OPUS_X86_MAY_HAVE_SSE;OPUS_X86_MAY_HAVE_SSE2;OPUS_X86_MAY_HAVE_SSE4_1;OPUS_X86_PRESUME_SSE=1;OPUS_X86_PRESUME_SSE2=1;CPU_INFO_BY_C;OPUS_BUILD=1;USE_ALLOCA=1;OPUS_HAVE_RTCD=1;HAVE_LRINTF=1;HAVE_LRINT=1;NDEBUG;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_XML_LIB;QT_CONCURRENT_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + false + + + MultiThreadedDLL + true + true + Level3 + + + ole32.lib;user32.lib;advapi32.lib;winmm.lib;ws2_32.lib;$(QtSrcPath)\lib\Qt5Widgets.lib;$(QtSrcPath)\lib\Qt5Gui.lib;$(QtSrcPath)\lib\Qt5Network.lib;$(QtSrcPath)\lib\Qt5Xml.lib;$(QtSrcPath)\lib\Qt5Concurrent.lib;$(QtSrcPath)\lib\Qt5Core.lib;$(QtSrcPath)\lib\qtmain.lib;shell32.lib;%(AdditionalDependencies) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + false + true + false + true + $(OutDir)\Jamulus.exe + false + Windows + true + 3.8 + + + Unsigned + None + 0 + + + _WINDOWS;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;APP_VERSION=\"$(Jamulus_Version)\";CUSTOM_MODES;_REENTRANT;QT_NO_DEPRECATED_WARNINGS;NOMINMAX;OPUS_X86_MAY_HAVE_SSE;OPUS_X86_MAY_HAVE_SSE2;OPUS_X86_MAY_HAVE_SSE4_1;OPUS_X86_PRESUME_SSE=1;OPUS_X86_PRESUME_SSE2=1;CPU_INFO_BY_C;OPUS_BUILD=1;USE_ALLOCA=1;OPUS_HAVE_RTCD=1;HAVE_LRINTF=1;HAVE_LRINT=1;NDEBUG;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_XML_LIB;QT_CONCURRENT_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + + + Powershell $(ProjectDir)$(ProjectName).ps1 --prebuild release + + + Powershell $(ProjectDir)$(ProjectName).ps1 --prelink release + + + Powershell $(ProjectDir)$(ProjectName).ps1 --postbuild release + + + + + src;libs\opus\include;libs\opus\celt;libs\opus\silk;libs\opus\silk\float;libs\opus\silk\fixed;libs\opus;$(QtSrcPath)\include;$(QtSrcPath)\include\QtWidgets;$(QtSrcPath)\include\QtGui;$(QtSrcPath)\include\QtANGLE;$(QtSrcPath)\include\QtNetwork;$(QtSrcPath)\include\QtXml;$(QtSrcPath)\include\QtConcurrent;$(QtSrcPath)\include\QtCore;$(OutputPath)moc;$(OutputPath)ui;windows\ASIOSDK2\common + -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions) + $(IntDir) + false + ProgramDatabase + 4577;4467;%(DisableSpecificWarnings) + Sync + $(IntDir) + Disabled + _WINDOWS;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;APP_VERSION="$(Jamulus_Version)";CUSTOM_MODES;_REENTRANT;QT_NO_DEPRECATED_WARNINGS;NOMINMAX;OPUS_X86_MAY_HAVE_SSE;OPUS_X86_MAY_HAVE_SSE2;OPUS_X86_MAY_HAVE_SSE4_1;OPUS_X86_PRESUME_SSE=1;OPUS_X86_PRESUME_SSE2=1;CPU_INFO_BY_C;OPUS_BUILD=1;USE_ALLOCA=1;OPUS_HAVE_RTCD=1;HAVE_LRINTF=1;HAVE_LRINT=1;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_XML_LIB;QT_CONCURRENT_LIB;QT_CORE_LIB;%(PreprocessorDefinitions) + false + MultiThreadedDebugDLL + true + true + Level3 + + + ole32.lib;user32.lib;advapi32.lib;winmm.lib;ws2_32.lib;$(QtSrcPath)\lib\Qt5Widgetsd.lib;$(QtSrcPath)\lib\Qt5Guid.lib;$(QtSrcPath)\lib\Qt5Networkd.lib;$(QtSrcPath)\lib\Qt5Xmld.lib;$(QtSrcPath)\lib\Qt5Concurrentd.lib;$(QtSrcPath)\lib\Qt5Cored.lib;$(QtSrcPath)\lib\qtmaind.lib;shell32.lib;%(AdditionalDependencies) + "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) + true + true + true + $(OutDir)\Jamulus.exe + false + Windows + true + 3.8 + + + Unsigned + None + 0 + + + _WINDOWS;_UNICODE;WIN32;_ENABLE_EXTENDED_ALIGNED_STORAGE;WIN64;APP_VERSION=\"$(Jamulus_Version)\";CUSTOM_MODES;_REENTRANT;QT_NO_DEPRECATED_WARNINGS;NOMINMAX;OPUS_X86_MAY_HAVE_SSE;OPUS_X86_MAY_HAVE_SSE2;OPUS_X86_MAY_HAVE_SSE4_1;OPUS_X86_PRESUME_SSE=1;OPUS_X86_PRESUME_SSE2=1;CPU_INFO_BY_C;OPUS_BUILD=1;USE_ALLOCA=1;OPUS_HAVE_RTCD=1;HAVE_LRINTF=1;HAVE_LRINT=1;QT_WIDGETS_LIB;QT_GUI_LIB;QT_NETWORK_LIB;QT_XML_LIB;QT_CONCURRENT_LIB;QT_CORE_LIB;_DEBUG;%(PreprocessorDefinitions) + + + Powershell $(ProjectDir)$(ProjectName).ps1 --prebuild debug + + + Powershell $(ProjectDir)$(ProjectName).ps1 --prelink debug + + + Powershell $(ProjectDir)$(ProjectName).ps1 --postbuild debug + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + src\analyzerconsole.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\analyzerconsole.h -o $(BuildDir)release\moc\moc_analyzerconsole.cpp + MOC src/analyzerconsole.h + $(BuildDir)release\moc\moc_analyzerconsole.cpp;%(Outputs) + src\analyzerconsole.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\analyzerconsole.h -o $(BuildDir)debug\moc\moc_analyzerconsole.cpp + MOC src/analyzerconsole.h + $(BuildDir)debug\moc\moc_analyzerconsole.cpp;%(Outputs) + + + + + + + src\audiomixerboard.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\audiomixerboard.h -o $(BuildDir)release\moc\moc_audiomixerboard.cpp + MOC src/audiomixerboard.h + $(BuildDir)release\moc\moc_audiomixerboard.cpp;%(Outputs) + src\audiomixerboard.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\audiomixerboard.h -o $(BuildDir)debug\moc\moc_audiomixerboard.cpp + MOC src/audiomixerboard.h + $(BuildDir)debug\moc\moc_audiomixerboard.cpp;%(Outputs) + + + + + + + + src\channel.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\channel.h -o $(BuildDir)release\moc\moc_channel.cpp + MOC src/channel.h + $(BuildDir)release\moc\moc_channel.cpp;%(Outputs) + src\channel.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\channel.h -o $(BuildDir)debug\moc\moc_channel.cpp + MOC src/channel.h + $(BuildDir)debug\moc\moc_channel.cpp;%(Outputs) + + + src\chatdlg.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\chatdlg.h -o $(BuildDir)release\moc\moc_chatdlg.cpp + MOC src/chatdlg.h + $(BuildDir)release\moc\moc_chatdlg.cpp;%(Outputs) + src\chatdlg.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\chatdlg.h -o $(BuildDir)debug\moc\moc_chatdlg.cpp + MOC src/chatdlg.h + $(BuildDir)debug\moc\moc_chatdlg.cpp;%(Outputs) + + + src\client.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\client.h -o $(BuildDir)release\moc\moc_client.cpp + MOC src/client.h + $(BuildDir)release\moc\moc_client.cpp;%(Outputs) + src\client.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\client.h -o $(BuildDir)debug\moc\moc_client.cpp + MOC src/client.h + $(BuildDir)debug\moc\moc_client.cpp;%(Outputs) + + + src\clientdlg.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\clientdlg.h -o $(BuildDir)release\moc\moc_clientdlg.cpp + MOC src/clientdlg.h + $(BuildDir)release\moc\moc_clientdlg.cpp;%(Outputs) + src\clientdlg.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\clientdlg.h -o $(BuildDir)debug\moc\moc_clientdlg.cpp + MOC src/clientdlg.h + $(BuildDir)debug\moc\moc_clientdlg.cpp;%(Outputs) + + + src\clientsettingsdlg.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\clientsettingsdlg.h -o $(BuildDir)release\moc\moc_clientsettingsdlg.cpp + MOC src/clientsettingsdlg.h + $(BuildDir)release\moc\moc_clientsettingsdlg.cpp;%(Outputs) + src\clientsettingsdlg.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\clientsettingsdlg.h -o $(BuildDir)debug\moc\moc_clientsettingsdlg.cpp + MOC src/clientsettingsdlg.h + $(BuildDir)debug\moc\moc_clientsettingsdlg.cpp;%(Outputs) + + + src\connectdlg.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\connectdlg.h -o $(BuildDir)release\moc\moc_connectdlg.cpp + MOC src/connectdlg.h + $(BuildDir)release\moc\moc_connectdlg.cpp;%(Outputs) + src\connectdlg.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\connectdlg.h -o $(BuildDir)debug\moc\moc_connectdlg.cpp + MOC src/connectdlg.h + $(BuildDir)debug\moc\moc_connectdlg.cpp;%(Outputs) + + + + + src\recorder\creaperproject.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\recorder\creaperproject.h -o $(BuildDir)release\moc\moc_creaperproject.cpp + MOC src/recorder/creaperproject.h + $(BuildDir)release\moc\moc_creaperproject.cpp;%(Outputs) + src\recorder\creaperproject.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\recorder\creaperproject.h -o $(BuildDir)debug\moc\moc_creaperproject.cpp + MOC src/recorder/creaperproject.h + $(BuildDir)debug\moc\moc_creaperproject.cpp;%(Outputs) + + + + + + + + + + + + + + + src\recorder\jamcontroller.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\recorder\jamcontroller.h -o $(BuildDir)release\moc\moc_jamcontroller.cpp + MOC src/recorder/jamcontroller.h + $(BuildDir)release\moc\moc_jamcontroller.cpp;%(Outputs) + src\recorder\jamcontroller.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\recorder\jamcontroller.h -o $(BuildDir)debug\moc\moc_jamcontroller.cpp + MOC src/recorder/jamcontroller.h + $(BuildDir)debug\moc\moc_jamcontroller.cpp;%(Outputs) + + + src\recorder\jamrecorder.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\recorder\jamrecorder.h -o $(BuildDir)release\moc\moc_jamrecorder.cpp + MOC src/recorder/jamrecorder.h + $(BuildDir)release\moc\moc_jamrecorder.cpp;%(Outputs) + src\recorder\jamrecorder.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\recorder\jamrecorder.h -o $(BuildDir)debug\moc\moc_jamrecorder.cpp + MOC src/recorder/jamrecorder.h + $(BuildDir)debug\moc\moc_jamrecorder.cpp;%(Outputs) + + + + + src\levelmeter.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\levelmeter.h -o $(BuildDir)release\moc\moc_levelmeter.cpp + MOC src/levelmeter.h + $(BuildDir)release\moc\moc_levelmeter.cpp;%(Outputs) + src\levelmeter.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\levelmeter.h -o $(BuildDir)debug\moc\moc_levelmeter.cpp + MOC src/levelmeter.h + $(BuildDir)debug\moc\moc_levelmeter.cpp;%(Outputs) + + + + + + + + + + + + src\multicolorled.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\multicolorled.h -o $(BuildDir)release\moc\moc_multicolorled.cpp + MOC src/multicolorled.h + $(BuildDir)release\moc\moc_multicolorled.cpp;%(Outputs) + src\multicolorled.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\multicolorled.h -o $(BuildDir)debug\moc\moc_multicolorled.cpp + MOC src/multicolorled.h + $(BuildDir)debug\moc\moc_multicolorled.cpp;%(Outputs) + + + + + + + + + + + + src\protocol.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\protocol.h -o $(BuildDir)release\moc\moc_protocol.cpp + MOC src/protocol.h + $(BuildDir)release\moc\moc_protocol.cpp;%(Outputs) + src\protocol.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\protocol.h -o $(BuildDir)debug\moc\moc_protocol.cpp + MOC src/protocol.h + $(BuildDir)debug\moc\moc_protocol.cpp;%(Outputs) + + + + + + + + src\server.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\server.h -o $(BuildDir)release\moc\moc_server.cpp + MOC src/server.h + $(BuildDir)release\moc\moc_server.cpp;%(Outputs) + src\server.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\server.h -o $(BuildDir)debug\moc\moc_server.cpp + MOC src/server.h + $(BuildDir)debug\moc\moc_server.cpp;%(Outputs) + + + src\serverdlg.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\serverdlg.h -o $(BuildDir)release\moc\moc_serverdlg.cpp + MOC src/serverdlg.h + $(BuildDir)release\moc\moc_serverdlg.cpp;%(Outputs) + src\serverdlg.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\serverdlg.h -o $(BuildDir)debug\moc\moc_serverdlg.cpp + MOC src/serverdlg.h + $(BuildDir)debug\moc\moc_serverdlg.cpp;%(Outputs) + + + src\serverlist.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\serverlist.h -o $(BuildDir)release\moc\moc_serverlist.cpp + MOC src/serverlist.h + $(BuildDir)release\moc\moc_serverlist.cpp;%(Outputs) + src\serverlist.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\serverlist.h -o $(BuildDir)debug\moc\moc_serverlist.cpp + MOC src/serverlist.h + $(BuildDir)debug\moc\moc_serverlist.cpp;%(Outputs) + + + + src\settings.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\settings.h -o $(BuildDir)release\moc\moc_settings.cpp + MOC src/settings.h + $(BuildDir)release\moc\moc_settings.cpp;%(Outputs) + src\settings.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\settings.h -o $(BuildDir)debug\moc\moc_settings.cpp + MOC src/settings.h + $(BuildDir)debug\moc\moc_settings.cpp;%(Outputs) + + + src\signalhandler.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\signalhandler.h -o $(BuildDir)release\moc\moc_signalhandler.cpp + MOC src/signalhandler.h + $(BuildDir)release\moc\moc_signalhandler.cpp;%(Outputs) + src\signalhandler.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\signalhandler.h -o $(BuildDir)debug\moc\moc_signalhandler.cpp + MOC src/signalhandler.h + $(BuildDir)debug\moc\moc_signalhandler.cpp;%(Outputs) + + + src\socket.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\socket.h -o $(BuildDir)release\moc\moc_socket.cpp + MOC src/socket.h + $(BuildDir)release\moc\moc_socket.cpp;%(Outputs) + src\socket.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\socket.h -o $(BuildDir)debug\moc\moc_socket.cpp + MOC src/socket.h + $(BuildDir)debug\moc\moc_socket.cpp;%(Outputs) + + + windows\sound.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore windows\sound.h -o $(BuildDir)release\moc\moc_sound.cpp + MOC windows/sound.h + $(BuildDir)release\moc\moc_sound.cpp;%(Outputs) + windows\sound.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore windows\sound.h -o $(BuildDir)debug\moc\moc_sound.cpp + MOC windows/sound.h + $(BuildDir)debug\moc\moc_sound.cpp;%(Outputs) + + + src\soundbase.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\soundbase.h -o $(BuildDir)release\moc\moc_soundbase.cpp + MOC src/soundbase.h + $(BuildDir)release\moc\moc_soundbase.cpp;%(Outputs) + src\soundbase.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\soundbase.h -o $(BuildDir)debug\moc\moc_soundbase.cpp + MOC src/soundbase.h + $(BuildDir)debug\moc\moc_soundbase.cpp;%(Outputs) + + + + + + + + + src\testbench.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\testbench.h -o $(BuildDir)release\moc\moc_testbench.cpp + MOC src/testbench.h + $(BuildDir)release\moc\moc_testbench.cpp;%(Outputs) + src\testbench.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\testbench.h -o $(BuildDir)debug\moc\moc_testbench.cpp + MOC src/testbench.h + $(BuildDir)debug\moc\moc_testbench.cpp;%(Outputs) + + + + + + src\util.h;$(BuildDir)release\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DNDEBUG -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\util.h -o $(BuildDir)release\moc\moc_util.cpp + MOC src/util.h + $(BuildDir)release\moc\moc_util.cpp;%(Outputs) + src\util.h;$(BuildDir)debug\moc\moc_predefs.h;$(QtBinDir)moc.exe;%(AdditionalInputs) + $(QtBinDir)moc.exe -D_UNICODE -DWIN32 -D_ENABLE_EXTENDED_ALIGNED_STORAGE -DWIN64 -DAPP_VERSION=\"$(Jamulus_Version)\" -DCUSTOM_MODES -D_REENTRANT -DQT_NO_DEPRECATED_WARNINGS -DNOMINMAX -DOPUS_X86_MAY_HAVE_SSE -DOPUS_X86_MAY_HAVE_SSE2 -DOPUS_X86_MAY_HAVE_SSE4_1 -DOPUS_X86_PRESUME_SSE=1 -DOPUS_X86_PRESUME_SSE2=1 -DCPU_INFO_BY_C -DOPUS_BUILD=1 -DUSE_ALLOCA=1 -DOPUS_HAVE_RTCD=1 -DHAVE_LRINTF=1 -DHAVE_LRINT=1 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_NETWORK_LIB -DQT_XML_LIB -DQT_CONCURRENT_LIB -DQT_CORE_LIB --compiler-flavor=msvc --include $(OutputPath)moc\moc_predefs.h -I$(QtSrcPath)/mkspecs/win32-msvc -I$(VCProjectDir)src -I$(VCProjectDir)libs/opus/include -I$(VCProjectDir)libs/opus/celt -I$(VCProjectDir)libs/opus/silk -I$(VCProjectDir)libs/opus/silk/float -I$(VCProjectDir)libs/opus/silk/fixed -I$(VCProjectDir)libs/opus -I$(QtSrcPath)/include -I$(QtSrcPath)/include/QtWidgets -I$(QtSrcPath)/include/QtGui -I$(QtSrcPath)/include/QtANGLE -I$(QtSrcPath)/include/QtNetwork -I$(QtSrcPath)/include/QtXml -I$(QtSrcPath)/include/QtConcurrent -I$(QtSrcPath)/include/QtCore src\util.h -o $(BuildDir)debug\moc\moc_util.cpp + MOC src/util.h + $(BuildDir)debug\moc\moc_util.cpp;%(Outputs) + + + + + + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + Document + true + $(QtSrcPath)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx$(QtBinDir)qmake.exe -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -Zi -MDd -E $(QtSrcPath)\mkspecs\features\data\dummy.cpp 2>NUL >$(BuildDir)debug\moc\moc_predefs.h + Generate moc_predefs.h + $(BuildDir)debug\moc\moc_predefs.h;%(Outputs) + + + Document + $(QtSrcPath)\mkspecs\features\data\dummy.cpp;%(AdditionalInputs) + cl -Bx$(QtBinDir)qmake.exe -nologo -Zc:wchar_t -FS -Zc:rvalueCast -Zc:inline -Zc:strictStrings -Zc:throwingNew -Zc:referenceBinding -Zc:__cplusplus -O2 -MD -E $(QtSrcPath)\mkspecs\features\data\dummy.cpp 2>NUL >$(BuildDir)release\moc\moc_predefs.h + Generate moc_predefs.h + $(BuildDir)release\moc\moc_predefs.h;%(Outputs) + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + true + + + + + + src\res\translation\translation_de_DE.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_de_DE.ts -qm src\res\translation\release\translation_de_DE.qm + lrelease + src\res\translation\release\translation_de_DE.qm;%(Outputs) + src\res\translation\translation_de_DE.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_de_DE.ts -qm src\res\translation\debug\translation_de_DE.qm + lrelease + src\res\translation\debug\translation_de_DE.qm;%(Outputs) + + + src\res\translation\translation_es_ES.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_es_ES.ts -qm src\res\translation\release\translation_es_ES.qm + lrelease + src\res\translation\release\translation_es_ES.qm;%(Outputs) + src\res\translation\translation_es_ES.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_es_ES.ts -qm src\res\translation\debug\translation_es_ES.qm + lrelease + src\res\translation\debug\translation_es_ES.qm;%(Outputs) + + + src\res\translation\translation_fr_FR.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_fr_FR.ts -qm src\res\translation\release\translation_fr_FR.qm + lrelease + src\res\translation\release\translation_fr_FR.qm;%(Outputs) + src\res\translation\translation_fr_FR.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_fr_FR.ts -qm src\res\translation\debug\translation_fr_FR.qm + lrelease + src\res\translation\debug\translation_fr_FR.qm;%(Outputs) + + + src\res\translation\translation_it_IT.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_it_IT.ts -qm src\res\translation\release\translation_it_IT.qm + lrelease + src\res\translation\release\translation_it_IT.qm;%(Outputs) + src\res\translation\translation_it_IT.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_it_IT.ts -qm src\res\translation\debug\translation_it_IT.qm + lrelease + src\res\translation\debug\translation_it_IT.qm;%(Outputs) + + + src\res\translation\translation_nl_NL.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_nl_NL.ts -qm src\res\translation\release\translation_nl_NL.qm + lrelease + src\res\translation\release\translation_nl_NL.qm;%(Outputs) + src\res\translation\translation_nl_NL.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_nl_NL.ts -qm src\res\translation\debug\translation_nl_NL.qm + lrelease + src\res\translation\debug\translation_nl_NL.qm;%(Outputs) + + + src\res\translation\translation_pl_PL.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_pl_PL.ts -qm src\res\translation\release\translation_pl_PL.qm + lrelease + src\res\translation\release\translation_pl_PL.qm;%(Outputs) + src\res\translation\translation_pl_PL.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_pl_PL.ts -qm src\res\translation\debug\translation_pl_PL.qm + lrelease + src\res\translation\debug\translation_pl_PL.qm;%(Outputs) + + + src\res\translation\translation_pt_BR.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_pt_BR.ts -qm src\res\translation\release\translation_pt_BR.qm + lrelease + src\res\translation\release\translation_pt_BR.qm;%(Outputs) + src\res\translation\translation_pt_BR.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_pt_BR.ts -qm src\res\translation\debug\translation_pt_BR.qm + lrelease + src\res\translation\debug\translation_pt_BR.qm;%(Outputs) + + + src\res\translation\translation_pt_PT.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_pt_PT.ts -qm src\res\translation\release\translation_pt_PT.qm + lrelease + src\res\translation\release\translation_pt_PT.qm;%(Outputs) + src\res\translation\translation_pt_PT.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_pt_PT.ts -qm src\res\translation\debug\translation_pt_PT.qm + lrelease + src\res\translation\debug\translation_pt_PT.qm;%(Outputs) + + + src\res\translation\translation_sk_SK.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_sk_SK.ts -qm src\res\translation\release\translation_sk_SK.qm + lrelease + src\res\translation\release\translation_sk_SK.qm;%(Outputs) + src\res\translation\translation_sk_SK.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_sk_SK.ts -qm src\res\translation\debug\translation_sk_SK.qm + lrelease + src\res\translation\debug\translation_sk_SK.qm;%(Outputs) + + + src\res\translation\translation_sv_SE.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_sv_SE.ts -qm src\res\translation\release\translation_sv_SE.qm + lrelease + src\res\translation\release\translation_sv_SE.qm;%(Outputs) + src\res\translation\translation_sv_SE.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_sv_SE.ts -qm src\res\translation\debug\translation_sv_SE.qm + lrelease + src\res\translation\debug\translation_sv_SE.qm;%(Outputs) + + + src\res\translation\translation_zh_CN.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_zh_CN.ts -qm src\res\translation\release\translation_zh_CN.qm + lrelease + src\res\translation\release\translation_zh_CN.qm;%(Outputs) + src\res\translation\translation_zh_CN.ts;%(AdditionalInputs) + $(QtBinDir)lrelease.exe src\res\translation\translation_zh_CN.ts -qm src\res\translation\debug\translation_zh_CN.qm + lrelease + src\res\translation\debug\translation_zh_CN.qm;%(Outputs) + + + + + Document + src\aboutdlgbase.ui;$(QtBinDir)uic.exe;%(AdditionalInputs) + $(QtBinDir)uic.exe src\aboutdlgbase.ui -o $(BuildDir)release\ui\ui_aboutdlgbase.h + UIC src/aboutdlgbase.ui + $(BuildDir)release\ui\ui_aboutdlgbase.h;%(Outputs) + src\aboutdlgbase.ui;$(QtBinDir)uic.exe;%(AdditionalInputs) + $(QtBinDir)uic.exe src\aboutdlgbase.ui -o $(BuildDir)debug\ui\ui_aboutdlgbase.h + UIC src/aboutdlgbase.ui + $(BuildDir)debug\ui\ui_aboutdlgbase.h;%(Outputs) + + + Document + src\chatdlgbase.ui;$(QtBinDir)uic.exe;%(AdditionalInputs) + $(QtBinDir)uic.exe src\chatdlgbase.ui -o $(BuildDir)release\ui\ui_chatdlgbase.h + UIC src/chatdlgbase.ui + $(BuildDir)release\ui\ui_chatdlgbase.h;%(Outputs) + src\chatdlgbase.ui;$(QtBinDir)uic.exe;%(AdditionalInputs) + $(QtBinDir)uic.exe src\chatdlgbase.ui -o $(BuildDir)debug\ui\ui_chatdlgbase.h + UIC src/chatdlgbase.ui + $(BuildDir)debug\ui\ui_chatdlgbase.h;%(Outputs) + + + Document + src\clientdlgbase.ui;$(QtBinDir)uic.exe;src\multicolorled.h;src\audiomixerboard.h;src\levelmeter.h;%(AdditionalInputs) + $(QtBinDir)uic.exe src\clientdlgbase.ui -o $(BuildDir)release\ui\ui_clientdlgbase.h + UIC src/clientdlgbase.ui + $(BuildDir)release\ui\ui_clientdlgbase.h;%(Outputs) + src\clientdlgbase.ui;$(QtBinDir)uic.exe;src\multicolorled.h;src\audiomixerboard.h;src\levelmeter.h;%(AdditionalInputs) + $(QtBinDir)uic.exe src\clientdlgbase.ui -o $(BuildDir)debug\ui\ui_clientdlgbase.h + UIC src/clientdlgbase.ui + $(BuildDir)debug\ui\ui_clientdlgbase.h;%(Outputs) + + + Document + src\clientsettingsdlgbase.ui;$(QtBinDir)uic.exe;src\util.h;%(AdditionalInputs) + $(QtBinDir)uic.exe src\clientsettingsdlgbase.ui -o $(BuildDir)release\ui\ui_clientsettingsdlgbase.h + UIC src/clientsettingsdlgbase.ui + $(BuildDir)release\ui\ui_clientsettingsdlgbase.h;%(Outputs) + src\clientsettingsdlgbase.ui;$(QtBinDir)uic.exe;src\util.h;%(AdditionalInputs) + $(QtBinDir)uic.exe src\clientsettingsdlgbase.ui -o $(BuildDir)debug\ui\ui_clientsettingsdlgbase.h + UIC src/clientsettingsdlgbase.ui + $(BuildDir)debug\ui\ui_clientsettingsdlgbase.h;%(Outputs) + + + Document + src\connectdlgbase.ui;$(QtBinDir)uic.exe;%(AdditionalInputs) + $(QtBinDir)uic.exe src\connectdlgbase.ui -o $(BuildDir)release\ui\ui_connectdlgbase.h + UIC src/connectdlgbase.ui + $(BuildDir)release\ui\ui_connectdlgbase.h;%(Outputs) + src\connectdlgbase.ui;$(QtBinDir)uic.exe;%(AdditionalInputs) + $(QtBinDir)uic.exe src\connectdlgbase.ui -o $(BuildDir)debug\ui\ui_connectdlgbase.h + UIC src/connectdlgbase.ui + $(BuildDir)debug\ui\ui_connectdlgbase.h;%(Outputs) + + + Document + src\serverdlgbase.ui;$(QtBinDir)uic.exe;src\util.h;%(AdditionalInputs) + $(QtBinDir)uic.exe src\serverdlgbase.ui -o $(BuildDir)release\ui\ui_serverdlgbase.h + UIC src/serverdlgbase.ui + $(BuildDir)release\ui\ui_serverdlgbase.h;%(Outputs) + src\serverdlgbase.ui;$(QtBinDir)uic.exe;src\util.h;%(AdditionalInputs) + $(QtBinDir)uic.exe src\serverdlgbase.ui -o $(BuildDir)debug\ui\ui_serverdlgbase.h + UIC src/serverdlgbase.ui + $(BuildDir)debug\ui\ui_serverdlgbase.h;%(Outputs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + src\resources.qrc;$(QtBinDir)rcc.exe;src\res\faderhandlesmall.png;src\res\transparent1x1.png;src\res\mixerboardbackground.png;src\res\ledbuttonpressed.png;src\res\faderhandle.png;src\res\faderbackground.png;src\res\ledbuttonnotpressed.png;src\res\mutediconorange.png;src\res\instruments\vocalalto.png;src\res\instruments\rapping.png;src\res\instruments\vocal.png;src\res\instruments\doublebass.png;src\res\instruments\trombone.png;src\res\instruments\microphone.png;src\res\instruments\keyboardvocal.png;src\res\instruments\synthesizer.png;src\res\instruments\bassoon.png;src\res\instruments\aguitar.png;src\res\instruments\bongo.png;src\res\instruments\none.png;src\res\instruments\vocallead.png;src\res\instruments\ukulele.png;src\res\instruments\mandolin.png;src\res\instruments\violin.png;src\res\instruments\congas.png;src\res\instruments\accordeon.png;src\res\instruments\recorder.png;src\res\instruments\cello.png;src\res\instruments\frenchhorn.png;src\res\instruments\viola.png;src\res\instruments\harmonica.png;src\res\instruments\flute.png;src\res\instruments\conductor.png;src\res\instruments\eguitar.png;src\res\instruments\djembe.png;src\res\instruments\banjo.png;src\res\instruments\guitarvocal.png;src\res\instruments\bassguitar.png;src\res\instruments\keyboard.png;src\res\instruments\grandpiano.png;src\res\instruments\oboe.png;src\res\instruments\drumset.png;src\res\instruments\bassukulele.png;src\res\instruments\vocaltenor.png;src\res\instruments\vibraphone.png;src\res\instruments\trumpet.png;src\res\instruments\listener.png;src\res\instruments\harp.png;src\res\instruments\vocalbass.png;src\res\instruments\tuba.png;src\res\instruments\mountaindulcimer.png;src\res\instruments\saxophone.png;src\res\instruments\scratching.png;src\res\instruments\vocalbaritone.png;src\res\instruments\vocalsoprano.png;src\res\instruments\streamer.png;src\res\instruments\clarinet.png;src\res\instruments\bodhran.png;src\res\IndicatorGreen.png;src\res\CLEDDisabledSmall.png;src\res\LEDGreenSmall.png;src\res\CLEDGreenSmall.png;src\res\LEDRedSmall.png;src\res\HLEDGreenSmall.png;src\res\IndicatorRed.png;src\res\HLEDYellowSmall.png;src\res\IndicatorYellow.png;src\res\CLEDGreySmall.png;src\res\LEDYellowSmall.png;src\res\CLEDYellowSmall.png;src\res\IndicatorYellowFancy.png;src\res\LEDBlackSmall.png;src\res\HLEDRedSmall.png;src\res\CLEDBlackSmall.png;src\res\CLEDRedSmall.png;src\res\HLEDBlackSmall.png;src\res\IndicatorRedFancy.png;src\res\flags\ca.png;src\res\flags\ky.png;src\res\flags\tc.png;src\res\flags\li.png;src\res\flags\tr.png;src\res\flags\do.png;src\res\flags\mh.png;src\res\flags\mw.png;src\res\flags\va.png;src\res\flags\ng.png;src\res\flags\fm.png;src\res\flags\gl.png;src\res\flags\pe.png;src\res\flags\hk.png;src\res\flags\pt.png;src\res\flags\bo.png;src\res\flags\sb.png;src\res\flags\kh.png;src\res\flags\cn.png;src\res\flags\kw.png;src\res\flags\dm.png;src\res\flags\lv.png;src\res\flags\mu.png;src\res\flags\ne.png;src\res\flags\vn.png;src\res\flags\fk.png;src\res\flags\gy.png;src\res\flags\pr.png;src\res\flags\an.png;src\res\flags\bm.png;src\res\flags\so.png;src\res\flags\cl.png;src\res\flags\tn.png;src\res\flags\dk.png;src\res\flags\lt.png;src\res\flags\dz.png;src\res\flags\md.png;src\res\flags\um.png;src\res\flags\ms.png;src\res\flags\nc.png;src\res\flags\fi.png;src\res\flags\nr.png;src\res\flags\gh.png;src\res\flags\gw.png;src\res\flags\pa.png;src\res\flags\al.png;src\res\flags\zw.png;src\res\flags\bz.png;src\res\flags\sm.png;src\res\flags\cy.png;src\res\flags\lc.png;src\res\flags\tl.png;src\res\flags\lr.png;src\res\flags\eh.png;src\res\flags\mq.png;src\res\flags\uz.png;src\res\flags\na.png;src\res\flags\np.png;src\res\flags\gf.png;src\res\flags\gu.png;src\res\flags\pn.png;src\res\flags\ht.png;src\res\flags\id.png;src\res\flags\flagnone.png;src\res\flags\is.png;src\res\flags\bi.png;src\res\flags\sk.png;src\res\flags\ch.png;src\res\flags\sz.png;src\res\flags\la.png;src\res\flags\tj.png;src\res\flags\mo.png;src\res\flags\gd.png;src\res\flags\om.png;src\res\flags\gs.png;src\res\flags\pl.png;src\res\flags\hr.png;src\res\flags\ye.png;src\res\flags\yt.png;src\res\flags\iq.png;src\res\flags\aw.png;src\res\flags\bg.png;src\res\flags\jp.png;src\res\flags\bv.png;src\res\flags\si.png;src\res\flags\cf.png;src\res\flags\cu.png;src\res\flags\th.png;src\res\flags\de.png;src\res\flags\tw.png;src\res\flags\ug.png;src\res\flags\mm.png;src\res\flags\es.png;src\res\flags\nl.png;src\res\flags\vu.png;src\res\flags\fr.png;src\res\flags\gb.png;src\res\flags\gq.png;src\res\flags\py.png;src\res\flags\af.png;src\res\flags\io.png;src\res\flags\au.png;src\res\flags\be.png;src\res\flags\rw.png;src\res\flags\bt.png;src\res\flags\sg.png;src\res\flags\cd.png;src\res\flags\km.png;src\res\flags\sv.png;src\res\flags\cs.png;src\res\flags\tf.png;src\res\flags\mk.png;src\res\flags\mz.png;src\res\flags\ph.png;src\res\flags\hn.png;src\res\flags\pw.png;src\res\flags\ad.png;src\res\flags\as.png;src\res\flags\ru.png;src\res\flags\br.png;src\res\flags\se.png;src\res\flags\st.png;src\res\flags\kz.png;src\res\flags\td.png;src\res\flags\ly.png;src\res\flags\mx.png;src\res\flags\gm.png;src\res\flags\pf.png;src\res\flags\zm.png;src\res\flags\ba.png;src\res\flags\rs.png;src\res\flags\sc.png;src\res\flags\ki.png;src\res\flags\sr.png;src\res\flags\co.png;src\res\flags\ua.png;src\res\flags\mg.png;src\res\flags\mv.png;src\res\flags\nf.png;src\res\flags\nu.png;src\res\flags\ps.png;src\res\flags\ao.png;src\res\flags\bn.png;src\res\flags\sa.png;src\res\flags\kg.png;src\res\flags\fam.png;src\res\flags\cm.png;src\res\flags\to.png;src\res\flags\lu.png;src\res\flags\me.png;src\res\flags\mt.png;src\res\flags\fj.png;src\res\flags\gi.png;src\res\flags\qa.png;src\res\flags\am.png;src\res\flags\ro.png;src\res\flags\ke.png;src\res\flags\sn.png;src\res\flags\ck.png;src\res\flags\cz.png;src\res\flags\tm.png;src\res\flags\dj.png;src\res\flags\ls.png;src\res\flags\mc.png;src\res\flags\mr.png;src\res\flags\hu.png;src\res\flags\ie.png;src\res\flags\it.png;src\res\flags\az.png;src\res\flags\bj.png;src\res\flags\by.png;src\res\flags\sl.png;src\res\flags\ci.png;src\res\flags\kr.png;src\res\flags\cx.png;src\res\flags\lb.png;src\res\flags\tk.png;src\res\flags\tz.png;src\res\flags\ma.png;src\res\flags\eg.png;src\res\flags\mp.png;src\res\flags\uy.png;src\res\flags\vi.png;src\res\flags\no.png;src\res\flags\ge.png;src\res\flags\gt.png;src\res\flags\pm.png;src\res\flags\ai.png;src\res\flags\ir.png;src\res\flags\ax.png;src\res\flags\bh.png;src\res\flags\bw.png;src\res\flags\sj.png;src\res\flags\cg.png;src\res\flags\kp.png;src\res\flags\sy.png;src\res\flags\cv.png;src\res\flags\ee.png;src\res\flags\mn.png;src\res\flags\et.png;src\res\flags\vg.png;src\res\flags\wf.png;src\res\flags\gr.png;src\res\flags\pk.png;src\res\flags\ag.png;src\res\flags\bf.png;src\res\flags\jo.png;src\res\flags\sh.png;src\res\flags\kn.png;src\res\flags\tg.png;src\res\flags\tv.png;src\res\flags\ec.png;src\res\flags\ml.png;src\res\flags\er.png;src\res\flags\ve.png;src\res\flags\nz.png;src\res\flags\ga.png;src\res\flags\ws.png;src\res\flags\gp.png;src\res\flags\ae.png;src\res\flags\in.png;src\res\flags\za.png;src\res\flags\at.png;src\res\flags\bd.png;src\res\flags\jm.png;src\res\flags\bs.png;src\res\flags\cc.png;src\res\flags\cr.png;src\res\flags\lk.png;src\res\flags\tt.png;src\res\flags\us.png;src\res\flags\my.png;src\res\flags\vc.png;src\res\flags\ni.png;src\res\flags\fo.png;src\res\flags\gn.png;src\res\flags\pg.png;src\res\flags\hm.png;src\res\flags\il.png;src\res\flags\ar.png;src\res\flags\re.png;src\res\flags\bb.png;src\res\flags\sd.png;src\res\servertrayiconinactive.png;src\res\fronticonserver.png;src\res\fronticon.png;src\res\servertrayiconactive.png;src\res\translation\translation_pl_PL.qm;src\res\translation\translation_sv_SE.qm;src\res\translation\translation_zh_CN.qm;src\res\translation\translation_fr_FR.qm;src\res\translation\translation_es_ES.qm;src\res\translation\translation_nl_NL.qm;src\res\translation\translation_pt_PT.qm;src\res\translation\translation_sk_SK.qm;src\res\translation\translation_pt_BR.qm;src\res\translation\translation_de_DE.qm;src\res\translation\translation_it_IT.qm;%(AdditionalInputs) + $(QtBinDir)rcc.exe -name resources src\resources.qrc -o src\res\qrc_resources.cpp + RCC src/resources.qrc + src\res\qrc_resources.cpp;%(Outputs) + src\resources.qrc;$(QtBinDir)rcc.exe;src\res\faderhandlesmall.png;src\res\transparent1x1.png;src\res\mixerboardbackground.png;src\res\ledbuttonpressed.png;src\res\faderhandle.png;src\res\faderbackground.png;src\res\ledbuttonnotpressed.png;src\res\mutediconorange.png;src\res\instruments\vocalalto.png;src\res\instruments\rapping.png;src\res\instruments\vocal.png;src\res\instruments\doublebass.png;src\res\instruments\trombone.png;src\res\instruments\microphone.png;src\res\instruments\keyboardvocal.png;src\res\instruments\synthesizer.png;src\res\instruments\bassoon.png;src\res\instruments\aguitar.png;src\res\instruments\bongo.png;src\res\instruments\none.png;src\res\instruments\vocallead.png;src\res\instruments\ukulele.png;src\res\instruments\mandolin.png;src\res\instruments\violin.png;src\res\instruments\congas.png;src\res\instruments\accordeon.png;src\res\instruments\recorder.png;src\res\instruments\cello.png;src\res\instruments\frenchhorn.png;src\res\instruments\viola.png;src\res\instruments\harmonica.png;src\res\instruments\flute.png;src\res\instruments\conductor.png;src\res\instruments\eguitar.png;src\res\instruments\djembe.png;src\res\instruments\banjo.png;src\res\instruments\guitarvocal.png;src\res\instruments\bassguitar.png;src\res\instruments\keyboard.png;src\res\instruments\grandpiano.png;src\res\instruments\oboe.png;src\res\instruments\drumset.png;src\res\instruments\bassukulele.png;src\res\instruments\vocaltenor.png;src\res\instruments\vibraphone.png;src\res\instruments\trumpet.png;src\res\instruments\listener.png;src\res\instruments\harp.png;src\res\instruments\vocalbass.png;src\res\instruments\tuba.png;src\res\instruments\mountaindulcimer.png;src\res\instruments\saxophone.png;src\res\instruments\scratching.png;src\res\instruments\vocalbaritone.png;src\res\instruments\vocalsoprano.png;src\res\instruments\streamer.png;src\res\instruments\clarinet.png;src\res\instruments\bodhran.png;src\res\IndicatorGreen.png;src\res\CLEDDisabledSmall.png;src\res\LEDGreenSmall.png;src\res\CLEDGreenSmall.png;src\res\LEDRedSmall.png;src\res\HLEDGreenSmall.png;src\res\IndicatorRed.png;src\res\HLEDYellowSmall.png;src\res\IndicatorYellow.png;src\res\CLEDGreySmall.png;src\res\LEDYellowSmall.png;src\res\CLEDYellowSmall.png;src\res\IndicatorYellowFancy.png;src\res\LEDBlackSmall.png;src\res\HLEDRedSmall.png;src\res\CLEDBlackSmall.png;src\res\CLEDRedSmall.png;src\res\HLEDBlackSmall.png;src\res\IndicatorRedFancy.png;src\res\flags\ca.png;src\res\flags\ky.png;src\res\flags\tc.png;src\res\flags\li.png;src\res\flags\tr.png;src\res\flags\do.png;src\res\flags\mh.png;src\res\flags\mw.png;src\res\flags\va.png;src\res\flags\ng.png;src\res\flags\fm.png;src\res\flags\gl.png;src\res\flags\pe.png;src\res\flags\hk.png;src\res\flags\pt.png;src\res\flags\bo.png;src\res\flags\sb.png;src\res\flags\kh.png;src\res\flags\cn.png;src\res\flags\kw.png;src\res\flags\dm.png;src\res\flags\lv.png;src\res\flags\mu.png;src\res\flags\ne.png;src\res\flags\vn.png;src\res\flags\fk.png;src\res\flags\gy.png;src\res\flags\pr.png;src\res\flags\an.png;src\res\flags\bm.png;src\res\flags\so.png;src\res\flags\cl.png;src\res\flags\tn.png;src\res\flags\dk.png;src\res\flags\lt.png;src\res\flags\dz.png;src\res\flags\md.png;src\res\flags\um.png;src\res\flags\ms.png;src\res\flags\nc.png;src\res\flags\fi.png;src\res\flags\nr.png;src\res\flags\gh.png;src\res\flags\gw.png;src\res\flags\pa.png;src\res\flags\al.png;src\res\flags\zw.png;src\res\flags\bz.png;src\res\flags\sm.png;src\res\flags\cy.png;src\res\flags\lc.png;src\res\flags\tl.png;src\res\flags\lr.png;src\res\flags\eh.png;src\res\flags\mq.png;src\res\flags\uz.png;src\res\flags\na.png;src\res\flags\np.png;src\res\flags\gf.png;src\res\flags\gu.png;src\res\flags\pn.png;src\res\flags\ht.png;src\res\flags\id.png;src\res\flags\flagnone.png;src\res\flags\is.png;src\res\flags\bi.png;src\res\flags\sk.png;src\res\flags\ch.png;src\res\flags\sz.png;src\res\flags\la.png;src\res\flags\tj.png;src\res\flags\mo.png;src\res\flags\gd.png;src\res\flags\om.png;src\res\flags\gs.png;src\res\flags\pl.png;src\res\flags\hr.png;src\res\flags\ye.png;src\res\flags\yt.png;src\res\flags\iq.png;src\res\flags\aw.png;src\res\flags\bg.png;src\res\flags\jp.png;src\res\flags\bv.png;src\res\flags\si.png;src\res\flags\cf.png;src\res\flags\cu.png;src\res\flags\th.png;src\res\flags\de.png;src\res\flags\tw.png;src\res\flags\ug.png;src\res\flags\mm.png;src\res\flags\es.png;src\res\flags\nl.png;src\res\flags\vu.png;src\res\flags\fr.png;src\res\flags\gb.png;src\res\flags\gq.png;src\res\flags\py.png;src\res\flags\af.png;src\res\flags\io.png;src\res\flags\au.png;src\res\flags\be.png;src\res\flags\rw.png;src\res\flags\bt.png;src\res\flags\sg.png;src\res\flags\cd.png;src\res\flags\km.png;src\res\flags\sv.png;src\res\flags\cs.png;src\res\flags\tf.png;src\res\flags\mk.png;src\res\flags\mz.png;src\res\flags\ph.png;src\res\flags\hn.png;src\res\flags\pw.png;src\res\flags\ad.png;src\res\flags\as.png;src\res\flags\ru.png;src\res\flags\br.png;src\res\flags\se.png;src\res\flags\st.png;src\res\flags\kz.png;src\res\flags\td.png;src\res\flags\ly.png;src\res\flags\mx.png;src\res\flags\gm.png;src\res\flags\pf.png;src\res\flags\zm.png;src\res\flags\ba.png;src\res\flags\rs.png;src\res\flags\sc.png;src\res\flags\ki.png;src\res\flags\sr.png;src\res\flags\co.png;src\res\flags\ua.png;src\res\flags\mg.png;src\res\flags\mv.png;src\res\flags\nf.png;src\res\flags\nu.png;src\res\flags\ps.png;src\res\flags\ao.png;src\res\flags\bn.png;src\res\flags\sa.png;src\res\flags\kg.png;src\res\flags\fam.png;src\res\flags\cm.png;src\res\flags\to.png;src\res\flags\lu.png;src\res\flags\me.png;src\res\flags\mt.png;src\res\flags\fj.png;src\res\flags\gi.png;src\res\flags\qa.png;src\res\flags\am.png;src\res\flags\ro.png;src\res\flags\ke.png;src\res\flags\sn.png;src\res\flags\ck.png;src\res\flags\cz.png;src\res\flags\tm.png;src\res\flags\dj.png;src\res\flags\ls.png;src\res\flags\mc.png;src\res\flags\mr.png;src\res\flags\hu.png;src\res\flags\ie.png;src\res\flags\it.png;src\res\flags\az.png;src\res\flags\bj.png;src\res\flags\by.png;src\res\flags\sl.png;src\res\flags\ci.png;src\res\flags\kr.png;src\res\flags\cx.png;src\res\flags\lb.png;src\res\flags\tk.png;src\res\flags\tz.png;src\res\flags\ma.png;src\res\flags\eg.png;src\res\flags\mp.png;src\res\flags\uy.png;src\res\flags\vi.png;src\res\flags\no.png;src\res\flags\ge.png;src\res\flags\gt.png;src\res\flags\pm.png;src\res\flags\ai.png;src\res\flags\ir.png;src\res\flags\ax.png;src\res\flags\bh.png;src\res\flags\bw.png;src\res\flags\sj.png;src\res\flags\cg.png;src\res\flags\kp.png;src\res\flags\sy.png;src\res\flags\cv.png;src\res\flags\ee.png;src\res\flags\mn.png;src\res\flags\et.png;src\res\flags\vg.png;src\res\flags\wf.png;src\res\flags\gr.png;src\res\flags\pk.png;src\res\flags\ag.png;src\res\flags\bf.png;src\res\flags\jo.png;src\res\flags\sh.png;src\res\flags\kn.png;src\res\flags\tg.png;src\res\flags\tv.png;src\res\flags\ec.png;src\res\flags\ml.png;src\res\flags\er.png;src\res\flags\ve.png;src\res\flags\nz.png;src\res\flags\ga.png;src\res\flags\ws.png;src\res\flags\gp.png;src\res\flags\ae.png;src\res\flags\in.png;src\res\flags\za.png;src\res\flags\at.png;src\res\flags\bd.png;src\res\flags\jm.png;src\res\flags\bs.png;src\res\flags\cc.png;src\res\flags\cr.png;src\res\flags\lk.png;src\res\flags\tt.png;src\res\flags\us.png;src\res\flags\my.png;src\res\flags\vc.png;src\res\flags\ni.png;src\res\flags\fo.png;src\res\flags\gn.png;src\res\flags\pg.png;src\res\flags\hm.png;src\res\flags\il.png;src\res\flags\ar.png;src\res\flags\re.png;src\res\flags\bb.png;src\res\flags\sd.png;src\res\servertrayiconinactive.png;src\res\fronticonserver.png;src\res\fronticon.png;src\res\servertrayiconactive.png;src\res\translation\translation_pl_PL.qm;src\res\translation\translation_sv_SE.qm;src\res\translation\translation_zh_CN.qm;src\res\translation\translation_fr_FR.qm;src\res\translation\translation_es_ES.qm;src\res\translation\translation_nl_NL.qm;src\res\translation\translation_pt_PT.qm;src\res\translation\translation_sk_SK.qm;src\res\translation\translation_pt_BR.qm;src\res\translation\translation_de_DE.qm;src\res\translation\translation_it_IT.qm;%(AdditionalInputs) + $(QtBinDir)rcc.exe -name resources src\resources.qrc -o src\res\qrc_resources.cpp + RCC src/resources.qrc + src\res\qrc_resources.cpp;%(Outputs) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/JamulusWin.vcxproj.filters b/JamulusWin.vcxproj.filters new file mode 100644 index 0000000000..011cf438b5 --- /dev/null +++ b/JamulusWin.vcxproj.filters @@ -0,0 +1,9583 @@ + + + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + false + + + {71ED8ED8-ACB9-4CE9-BBE1-E00B30144E11} + cpp;c;cxx;moc;h;def;odl;idl;res; + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C} + ts;xlf + false + + + {B83CAF91-C7BF-462F-B76C-EA11631F866C} + * + false + + + {e6b52983-8768-4c59-840a-423e792e6352} + + + png + false + + + qm + false + + + * + false + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + Generated Files + + + + + Generated Files + + + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Generated Files + + + Generated Files + + + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + Form Files + + + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + Resource Files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Translation Files + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Icons + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Translation Qm Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + Distribution Files + + + + + Icons + + + \ No newline at end of file diff --git a/android/sound.cpp b/android/sound.cpp index cff00204ca..59bca44d3c 100644 --- a/android/sound.cpp +++ b/android/sound.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2004-2022 * * Author(s): - * Simon Tomlinson, Volker Fischer + * Simon Tomlinson, Volker Fischer, maintained by pgScorpio * ****************************************************************************** * @@ -25,20 +25,27 @@ #include "sound.h" #include "androiddebug.cpp" +#define RING_FACTOR 20 + /* Implementation *************************************************************/ -const uint8_t CSound::RING_FACTOR = 20; +CSound* CSound::pSound = NULL; -CSound::CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* arg ), - void* arg, - const QString& strMIDISetup, - const bool, - const QString& ) : - CSoundBase ( "Oboe", fpNewProcessCallback, arg, strMIDISetup ) +CSound::CSound ( void ( *theProcessCallback ) ( CVector& psData, void* arg ), void* theProcessCallbackArg ) : + CSoundBase ( "Oboe", theProcessCallback, theProcessCallbackArg ) { + setObjectName ( "CSoundThread" ); + #ifdef ANDROIDDEBUG qInstallMessageHandler ( myMessageHandler ); #endif + + soundProperties.bHasAudioDeviceSelection = false; + soundProperties.bHasInputChannelSelection = false; + soundProperties.bHasOutputChannelSelection = false; + soundProperties.bHasInputGainSelection = false; + + pSound = this; } void CSound::setupCommonStreamParams ( oboe::AudioStreamBuilder* builder ) @@ -51,11 +58,9 @@ void CSound::setupCommonStreamParams ( oboe::AudioStreamBuilder* builder ) ->setSharingMode ( oboe::SharingMode::Exclusive ) ->setChannelCount ( oboe::ChannelCount::Stereo ) ->setSampleRate ( SYSTEM_SAMPLE_RATE_HZ ) - ->setFramesPerCallback ( iOboeBufferSizeMono ) + ->setFramesPerCallback ( iDeviceBufferSize ) ->setSampleRateConversionQuality ( oboe::SampleRateConversionQuality::Medium ) ->setPerformanceMode ( oboe::PerformanceMode::LowLatency ); - - return; } void CSound::closeStream ( oboe::ManagedStream& stream ) @@ -79,7 +84,7 @@ void CSound::closeStream ( oboe::ManagedStream& stream ) } } -void CSound::openStreams() +bool CSound::openStreams() { // Setup output stream oboe::AudioStreamBuilder inBuilder, outBuilder; @@ -92,9 +97,9 @@ void CSound::openStreams() if ( result != oboe::Result::OK ) { - return; + return false; } - mPlayStream->setBufferSizeInFrames ( iOboeBufferSizeStereo ); + mPlayStream->setBufferSizeInFrames ( ( iDeviceBufferSize * 2 ) ); warnIfNotLowLatency ( mPlayStream, "PlayStream" ); printStreamDetails ( mPlayStream ); @@ -112,14 +117,23 @@ void CSound::openStreams() if ( result != oboe::Result::OK ) { closeStream ( mPlayStream ); - return; + return false; } - mRecordingStream->setBufferSizeInFrames ( iOboeBufferSizeStereo ); + mRecordingStream->setBufferSizeInFrames ( ( iDeviceBufferSize * 2 ) ); warnIfNotLowLatency ( mRecordingStream, "RecordStream" ); printStreamDetails ( mRecordingStream ); printStreamDetails ( mPlayStream ); + + return true; +} + +void CSound::closeStreams() +{ + // clean up + closeStream ( mRecordingStream ); + closeStream ( mPlayStream ); } void CSound::printStreamDetails ( oboe::ManagedStream& stream ) @@ -148,54 +162,47 @@ void CSound::warnIfNotLowLatency ( oboe::ManagedStream& stream, QString streamNa if ( stream->getPerformanceMode() != oboe::PerformanceMode::LowLatency ) { QString latencyMode = ( stream->getPerformanceMode() == oboe::PerformanceMode::None ? "None" : "Power Saving" ); + Q_UNUSED ( latencyMode ); } Q_UNUSED ( streamName ); } -void CSound::closeStreams() -{ - // clean up - closeStream ( mRecordingStream ); - closeStream ( mPlayStream ); -} - -void CSound::Start() +bool CSound::start() { - openStreams(); - - // call base class - CSoundBase::Start(); - - // finally start the streams so the callback begins, start with inputstream first. - mRecordingStream->requestStart(); - mPlayStream->requestStart(); -} + if ( !IsStarted() ) + { + if ( openStreams() ) + { + audioBuffer.Init ( iDeviceBufferSize * 2 ); + mOutBuffer.Init ( iDeviceBufferSize * 2 * RING_FACTOR ); + + if ( mRecordingStream->requestStart() == oboe::Result::OK ) + { + if ( mPlayStream->requestStart() == oboe::Result::OK ) + { + return true; + } + + mRecordingStream->requestStop(); + } + } -void CSound::Stop() -{ - closeStreams(); + closeStreams(); + return false; + } - // call base class - CSoundBase::Stop(); + return true; } -int CSound::Init ( const int iNewPrefMonoBufferSize ) +bool CSound::stop() { - // store buffer size - iOboeBufferSizeMono = iNewPrefMonoBufferSize; // 512 - - // init base class - CSoundBase::Init ( iOboeBufferSizeMono ); - - // set internal buffer size value and calculate stereo buffer size - iOboeBufferSizeStereo = 2 * iOboeBufferSizeMono; - - // create memory for intermediate audio buffer - vecsTmpInputAudioSndCrdStereo.Init ( iOboeBufferSizeStereo ); - mOutBuffer.Init ( iOboeBufferSizeStereo * RING_FACTOR ); + if ( IsStarted() ) + { + closeStreams(); + } - return iOboeBufferSizeMono; + return true; } // This is the main callback method for when an audio stream is ready to publish data to an output stream @@ -204,7 +211,7 @@ int CSound::Init ( const int iNewPrefMonoBufferSize ) oboe::DataCallbackResult CSound::onAudioReady ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ) { // only process if we are running - if ( !bRun ) + if ( !IsStarted() ) { return oboe::DataCallbackResult::Continue; } @@ -252,17 +259,17 @@ oboe::DataCallbackResult CSound::onAudioInput ( oboe::AudioStream* oboeStream, v int16_t* intData = static_cast ( audioData ); // Copy recording data to internal vector - memcpy ( vecsTmpInputAudioSndCrdStereo.data(), intData, sizeof ( int16_t ) * numFrames * oboeStream->getChannelCount() ); + memcpy ( audioBuffer.data(), intData, sizeof ( int16_t ) * numFrames * oboeStream->getChannelCount() ); - if ( numFrames != iOboeBufferSizeMono ) + if ( numFrames != (int32_t) iDeviceBufferSize ) { - qDebug() << "Received " << numFrames << " expecting " << iOboeBufferSizeMono; + qDebug() << "Received " << numFrames << " expecting " << iDeviceBufferSize; } mStats.frames_in += numFrames; // Tell parent class that we've put some data ready to send to the server - ProcessCallback ( vecsTmpInputAudioSndCrdStereo ); + processCallback ( audioBuffer ); // The callback has placed in the vector the samples to play addOutputData ( oboeStream->getChannelCount() ); @@ -272,16 +279,16 @@ oboe::DataCallbackResult CSound::onAudioInput ( oboe::AudioStream* oboeStream, v void CSound::addOutputData ( int channel_count ) { - QMutexLocker locker ( &MutexAudioProcessCallback ); + QMutexLocker locker ( &mutexAudioProcessCallback ); // Only copy data if we have data to copy, otherwise fill with silence - if ( vecsTmpInputAudioSndCrdStereo.empty() ) + if ( audioBuffer.empty() ) { // prime output stream buffer with silence - vecsTmpInputAudioSndCrdStereo.resize ( iOboeBufferSizeMono * channel_count, 0 ); + audioBuffer.resize ( iDeviceBufferSize * channel_count, 0 ); } - mOutBuffer.Put ( vecsTmpInputAudioSndCrdStereo, iOboeBufferSizeMono * channel_count ); + mOutBuffer.Put ( audioBuffer, iDeviceBufferSize * channel_count ); if ( mOutBuffer.isFull() ) { @@ -294,7 +301,7 @@ oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, mStats.frames_out += numFrames; mStats.out_callback_calls++; - QMutexLocker locker ( &MutexAudioProcessCallback ); + QMutexLocker locker ( &mutexAudioProcessCallback ); std::size_t to_write = (std::size_t) numFrames * oboeStream->getChannelCount(); std::size_t count = std::min ( (std::size_t) mOutBuffer.GetAvailData(), to_write ); @@ -318,7 +325,7 @@ oboe::DataCallbackResult CSound::onAudioOutput ( oboe::AudioStream* oboeStream, return oboe::DataCallbackResult::Continue; } -// TODO better handling of stream closing errors +// TODO better handling of stream closing errors (use strErrorList ??) void CSound::onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result result ) { qDebug() << "CSound::onErrorAfterClose"; @@ -327,7 +334,7 @@ void CSound::onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result res Q_UNUSED ( result ); } -// TODO better handling of stream closing errors +// TODO better handling of stream closing errors (use strErrorList ??) void CSound::onErrorBeforeClose ( oboe::AudioStream* oboeStream, oboe::Result result ) { qDebug() << "CSound::onErrorBeforeClose"; @@ -352,3 +359,112 @@ void CSound::Stats::log() const << "frames_in: " << frames_in << ",frames_out: " << frames_out << ",frames_filled_out: " << frames_filled_out << ",in_callback_calls: " << in_callback_calls << ",out_callback_calls: " << out_callback_calls << ",ring_overrun: " << ring_overrun; } + +unsigned int CSound::getDeviceBufferSize ( unsigned int iDesiredBufferSize ) +{ + unsigned int deviceBufferSize = iDesiredBufferSize; + // Round up to a multiple of 2 ! + deviceBufferSize++; + deviceBufferSize >>= 1; + deviceBufferSize <<= 1; + + if ( deviceBufferSize < 64 ) + { + deviceBufferSize = 64; + } + else if ( deviceBufferSize > 512 ) + { + deviceBufferSize = 512; + } + + return deviceBufferSize; +} + +void CSound::closeCurrentDevice() // Closes the current driver and Clears Device Info +{ + // nothing to close on android +} + +long CSound::createDeviceList ( bool bRescan ) +{ + Q_UNUSED ( bRescan ); + + strDeviceNames.clear(); + strDeviceNames.append ( SystemDriverTechniqueName() ); + + // Just one device, so we force selection ! + iCurrentDevice = 0; + + return lNumDevices = 1; +} + +bool CSound::setBaseValues() +{ + createDeviceList(); + + clearDeviceInfo(); + + // Set the input channel names: + strInputChannelNames.append ( "input left" ); + strInputChannelNames.append ( "input right" ); + lNumInChan = 2; + + // Set added input channels: ( Always 0 for oboe ) + addAddedInputChannelNames(); + + // Set the output channel names: + strOutputChannelNames.append ( "output left" ); + strOutputChannelNames.append ( "output right" ); + lNumOutChan = 2; + + // Select input and output channels: + resetChannelMapping(); + + // Set the In/Out Latency: + fInOutLatencyMs = 0.0; + // TODO !!! + + return true; +} + +bool CSound::checkCapabilities() +{ + // TODO ??? + // For now anything is OK + return true; +} + +// TODO: ChannelMapping from inifile! +bool CSound::checkDeviceChange ( CSoundBase::tDeviceChangeCheck mode, int iDriverIndex ) // Open device sequence handling.... +{ + // We have just one device ! + if ( iDriverIndex != 0 ) + { + return false; + } + + switch ( mode ) + { + case CSoundBase::tDeviceChangeCheck::CheckOpen: + Stop(); + strErrorList.clear(); + if ( !Start() ) + { + return false; + } + return true; + + case CSoundBase::tDeviceChangeCheck::CheckCapabilities: + return checkCapabilities(); + + case CSoundBase::tDeviceChangeCheck::Activate: + return setBaseValues(); + + case CSoundBase::tDeviceChangeCheck::Abort: + setBaseValues(); // Still set Base values, since there is no other device ! + return true; + + default: + return false; + } +} diff --git a/android/sound.h b/android/sound.h index d5fe6e8b82..6562e1f229 100644 --- a/android/sound.h +++ b/android/sound.h @@ -37,24 +37,7 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback { Q_OBJECT -public: - static const uint8_t RING_FACTOR; - CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* arg ), - void* arg, - const QString& strMIDISetup, - const bool, - const QString& ); - virtual ~CSound() {} - - virtual int Init ( const int iNewPrefMonoBufferSize ); - virtual void Start(); - virtual void Stop(); - - // Call backs for Oboe - virtual oboe::DataCallbackResult onAudioReady ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ); - virtual void onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result result ); - virtual void onErrorBeforeClose ( oboe::AudioStream* oboeStream, oboe::Result result ); - +private: struct Stats { Stats() { reset(); } @@ -68,16 +51,27 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback std::size_t ring_overrun; }; + // used to reach a state where the input buffer is + // empty and the garbage in the first 500ms or so is discarded + static constexpr int32_t kNumCallbacksToDrain = 10; + int32_t mCountCallbacksToDrain = kNumCallbacksToDrain; + Stats mStats; + + oboe::ManagedStream mRecordingStream; + oboe::ManagedStream mPlayStream; + protected: - CVector vecsTmpInputAudioSndCrdStereo; CBuffer mOutBuffer; - int iOboeBufferSizeMono; - int iOboeBufferSizeStereo; + +public: + CSound ( void ( *theProcessCallback ) ( CVector& psData, void* arg ), void* theProcessCallbackArg ); + + virtual ~CSound() {} private: void setupCommonStreamParams ( oboe::AudioStreamBuilder* builder ); void printStreamDetails ( oboe::ManagedStream& stream ); - void openStreams(); + bool openStreams(); void closeStreams(); void warnIfNotLowLatency ( oboe::ManagedStream& stream, QString streamName ); void closeStream ( oboe::ManagedStream& stream ); @@ -87,12 +81,35 @@ class CSound : public CSoundBase, public oboe::AudioStreamCallback void addOutputData ( int channel_count ); - oboe::ManagedStream mRecordingStream; - oboe::ManagedStream mPlayStream; +protected: + bool setBaseValues(); + bool checkCapabilities(); - // used to reach a state where the input buffer is - // empty and the garbage in the first 500ms or so is discarded - static constexpr int32_t kNumCallbacksToDrain = 10; - int32_t mCountCallbacksToDrain = kNumCallbacksToDrain; - Stats mStats; +protected: + // Call backs for Oboe + virtual oboe::DataCallbackResult onAudioReady ( oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames ); + virtual void onErrorAfterClose ( oboe::AudioStream* oboeStream, oboe::Result result ); + virtual void onErrorBeforeClose ( oboe::AudioStream* oboeStream, oboe::Result result ); + + //============================================================================ + // Virtual interface to CSoundBase: + //============================================================================ +protected: // CSoundBase Mandatory pointer to instance (must be set to 'this' in the CSound constructor) + static CSound* pSound; + +public: // CSoundBase Mandatory functions. (but static functions can't be virtual) + static inline CSoundBase* pInstance() { return pSound; } + static inline const CSoundProperties& GetProperties() { return pSound->getSoundProperties(); } + +protected: + // CSoundBase internal + virtual long createDeviceList ( bool bRescan = false ); // Fills strDeviceList. Returns number of devices found + virtual bool checkDeviceChange ( CSoundBase::tDeviceChangeCheck mode, int iDriverIndex ); // Open device sequence handling.... + virtual unsigned int getDeviceBufferSize ( unsigned int iDesiredBufferSize ); + + virtual void closeCurrentDevice(); // Closes the current driver and Clears Device Info + virtual bool openDeviceSetup() { return false; } + + virtual bool start(); + virtual bool stop(); }; diff --git a/linux/jackclient.cpp b/linux/jackclient.cpp new file mode 100644 index 0000000000..4cda1ec7af --- /dev/null +++ b/linux/jackclient.cpp @@ -0,0 +1,422 @@ +/******************************************************************************\ + * Copyright (c) 2022 + * + * Author(s): + * Peter Goderie (pgScorpio) + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include "jackclient.h" + +//================================================ +// CJackClientPort class: a basic Jack client port +//================================================ + +bool CJackClientPort::Connect ( const char* connPort ) +{ + Disconnect(); + int res; + // First portname must be the output, second portname must be the input + if ( bIsInput ) + { + // I am input, so connPort must be output + res = jack_connect ( jackClient, connPort, jack_port_name ( jackPort ) /*jack_port_name ( jackPort )*/ ); + } + else + { + // I am output, so connPort must be input + res = jack_connect ( jackClient, jack_port_name ( jackPort ), connPort ); + } + + bool ok = ( res == 0 ) || ( res == EEXIST ); + + if ( ok ) + { + connName = connPort; + } + + return ( connName.size() > 0 ); +} + +bool CJackClientPort::Disconnect() +{ + if ( connName.size() ) + { + int res; + + // First portname must be the output, second portname must be the input + if ( bIsInput ) + { + // I am input, so connName must be output + res = jack_disconnect ( jackClient, connName.toLocal8Bit().data(), jack_port_name ( jackPort ) ); + } + else + { + // I am output, so connName must be input + res = jack_disconnect ( jackClient, jack_port_name ( jackPort ), connName.toLocal8Bit().data() ); + } + + bool ok = ( res == 0 ); + + // on failure assume we where not connected to connName at all + connName.clear(); + + return ok; + } + + return ( connName.size() == 0 ); +} + +const QString& CJackClientPort::GetConnections() +{ + connName.clear(); + + const char** connections = jack_port_get_connections ( jackPort ); + if ( connections ) + { + const char** connlist = connections; + while ( connlist && *connlist ) + { + + if ( connName.size() ) + { + connName += "+"; + connName += *connlist; + } + else + { + connName = *connlist; + } + + connlist++; + } + + jack_free ( connections ); + } + + return connName; +} + +//================================================ +// CJackAudioPort class: +//================================================ + +bool CJackAudioPort::getLatencyRange ( jack_latency_range_t& latrange ) +{ + latrange.min = 0; + latrange.max = 0; + + if ( jackPort == NULL ) + { + return false; + } + + jack_port_get_latency_range ( jackPort, bIsInput ? JackCaptureLatency : JackPlaybackLatency, &latrange ); + + return true; +} + +bool CJackAudioPort::setLatencyRange ( jack_latency_range_t& latrange ) +{ + if ( jackPort == NULL ) + { + return false; + } + + jack_port_set_latency_range ( jackPort, bIsInput ? JackCaptureLatency : JackPlaybackLatency, &latrange ); + + return true; +} + +//================================================ +// CJackMidiPort class: +//================================================ + +//================================================ +// CJackClient class: The Jack client and it's ports +//================================================ + +CJackClient::CJackClient ( QString aClientName ) : + strServerName ( getenv ( "JACK_DEFAULT_SERVER" ) ), + strClientName ( aClientName ), + strJackClientName ( APP_NAME ), + jackClient ( NULL ), + openOptions ( JackNullOption ), + openStatus ( JackNoSuchClient ), + bIsActive ( false ), + AudioInput(), + AudioOutput(), + MidiInput(), + MidiOutput() +{ + if ( strServerName.isEmpty() ) + { + strServerName = "default"; + } + + qInfo() << qUtf8Printable ( + QString ( "Connecting to JACK \"%1\" instance (use the JACK_DEFAULT_SERVER environment variable to change this)." ).arg ( strServerName ) ); + + if ( !strClientName.isEmpty() ) + { + strJackClientName += " "; + strJackClientName += strClientName; + } +} + +bool CJackClient::Open() +{ + if ( jackClient == NULL ) + { + // we shouldn't have any ports ! + MidiInput.clear(); + MidiOutput.clear(); + AudioInput.clear(); + AudioOutput.clear(); + // and we can't be active ! + bIsActive = false; + + jackClient = jack_client_open ( strJackClientName.toLocal8Bit().data(), openOptions, &openStatus ); + } + + return ( ( jackClient != NULL ) && ( openStatus == 0 ) ); +} + +bool CJackClient::Close() +{ + if ( bIsActive ) + { + Deactivate(); + } + + // close client + if ( jackClient != NULL ) + { + // unregister and delete any ports: + while ( MidiInput.size() ) + { + MidiInput.last().Unregister(); + MidiInput.removeLast(); + } + + while ( MidiOutput.size() ) + { + MidiOutput.last().Unregister(); + MidiOutput.removeLast(); + } + + while ( AudioInput.size() ) + { + AudioInput.last().Unregister(); + AudioInput.removeLast(); + } + + while ( AudioOutput.size() ) + { + AudioOutput.last().Unregister(); + AudioOutput.removeLast(); + } + + bool res = ( jack_client_close ( jackClient ) == 0 ); + + jackClient = NULL; + + return res; + } + else + { + // we shouldn't have any ports ! + MidiInput.clear(); + MidiOutput.clear(); + AudioInput.clear(); + AudioOutput.clear(); + // and we can't be active ! + bIsActive = false; + } + + return true; +} + +bool CJackClient::AddAudioPort ( const QString& portName, bool bInput ) +{ + const char* thePortname = portName.toLocal8Bit().data(); + jack_port_t* newPort = NULL; + + newPort = jack_port_register ( jackClient, thePortname, JACK_DEFAULT_AUDIO_TYPE, bInput ? JackPortIsInput : JackPortIsOutput, 0 ); + + if ( newPort ) + { + const char* jackName = jack_port_name ( newPort ); + CJackAudioPort newAudioPort ( CJackAudioPort ( jackClient, newPort, portName, bInput ) ); + + if ( bInput ) + { + if ( jackName ) + AudioInput.append ( newAudioPort ); + } + else + { + if ( jackName ) + AudioOutput.append ( newAudioPort ); + } + + return true; + } + + return false; +} + +bool CJackClient::AddMidiPort ( const QString& portName, bool bInput ) +{ + jack_port_t* newPort = + jack_port_register ( jackClient, portName.toLocal8Bit().data(), JACK_DEFAULT_MIDI_TYPE, bInput ? JackPortIsInput : JackPortIsOutput, 0 ); + + if ( newPort ) + { + CJackMidiPort newMidiPort ( jackClient, newPort, portName, bInput ); + + if ( bInput ) + { + MidiInput.append ( newMidiPort ); + } + else + { + MidiOutput.append ( newMidiPort ); + } + + return true; + } + + return false; +} + +bool CJackClient::AutoConnect() +{ + // connect the audio ports, note: you cannot do this before + // the client is activated, because we cannot allow + // connections to be made to clients that are not + // running + + if ( ( jackClient == NULL ) || !bIsActive ) + { + return false; + } + + unsigned int numInConnected = 0; + unsigned int numOutConnected = 0; + bool ok = true; + const char** ports; + + // try to connect physical audio input ports + ports = GetJackPorts ( nullptr, nullptr, JackPortIsPhysical | JackPortIsOutput ); + if ( ports != nullptr ) + { + int i = 0; + while ( ports[i] && ( i < AudioInput.size() ) ) + { + if ( AudioInput[i].Connect ( ports[i] ) ) + { + numInConnected++; + } + else + { + ok = false; + } + i++; + } + + jack_free ( ports ); + } + + // try to connect physical audio output ports + ports = GetJackPorts ( nullptr, nullptr, JackPortIsPhysical | JackPortIsInput ); + if ( ports != nullptr ) + { + int i = 0; + while ( ports[i] && ( i < AudioOutput.size() ) ) + { + if ( AudioOutput[i].Connect ( ports[i] ) ) + { + numOutConnected++; + } + else + { + ok = false; + } + i++; + } + + jack_free ( ports ); + } + + return ok && ( numInConnected >= DRV_MIN_IN_CHANNELS ) && ( numOutConnected >= DRV_MIN_OUT_CHANNELS ); +} + +unsigned int CJackClient::GetAudioInputConnections() +{ + unsigned int count = 0; + for ( int i = 0; i < AudioInput.size(); i++ ) + { + if ( !AudioInput[i].GetConnections().isEmpty() ) + { + count++; + } + } + + return count; +} + +unsigned int CJackClient::GetAudioOutputConnections() +{ + unsigned int count = 0; + for ( int i = 0; i < AudioOutput.size(); i++ ) + { + if ( !AudioOutput[i].GetConnections().isEmpty() ) + { + count++; + } + } + + return count; +} + +bool CJackClient::Activate() +{ + if ( !bIsActive ) + { + bIsActive = jackClient ? ( jack_activate ( jackClient ) == 0 ) : false; + } + + return bIsActive; +} + +bool CJackClient::Deactivate() // Also disconnects ports ! +{ + if ( !jackClient ) + { + bIsActive = false; + } + + if ( bIsActive && ( jack_deactivate ( jackClient ) == 0 ) ) + { + bIsActive = false; + } + + return !bIsActive; +} diff --git a/linux/jackclient.h b/linux/jackclient.h new file mode 100644 index 0000000000..1dbe5e6e29 --- /dev/null +++ b/linux/jackclient.h @@ -0,0 +1,219 @@ +#pragma once +/******************************************************************************\ + * Copyright (c) 2022 + * + * Author(s): + * Peter Goderie (pgScorpio) + * + ****************************************************************************** + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * +\******************************************************************************/ + +#include +#include +#include +#include + +#include "util.h" + +//============================================================================ +// JackClient class: Jack client API implementation for Jamulus +//============================================================================ + +class CJackClient; + +//================================================ +// CJackClientPort class: a basic Jack client port +//================================================ +class CJackClientPort +{ +protected: + friend class CJackAudioPort; + friend class CJackMidiPort; + + CJackClientPort ( jack_client_t* theJackClient, jack_port_t* theJackPort, const QString& thePortName, bool bInput ) : + jackClient ( theJackClient ), + jackPort ( theJackPort ), + portName ( thePortName ), + connName ( "" ), + bIsInput ( bInput ) + {} + + void Unregister() + { + if ( jackClient && jackPort ) + { + jack_port_unregister ( jackClient, jackPort ); + } + } + +public: +public: + jack_client_t* jackClient; + jack_port_t* jackPort; + QString portName; + QString connName; + bool bIsInput; + +public: + inline bool isOpen() { return ( jackClient && jackPort ); } + inline bool isConnected() { return ( connName.size() > 0 ); } + + inline QString GetJackPortName() { return jackPort ? jack_port_name ( jackPort ) : ""; } + +public: + inline size_t GetBufferSize ( const char* portType ) { return jack_port_type_get_buffer_size ( jackClient, portType ); } + inline void* GetBuffer ( jack_nframes_t nframes ) const { return jack_port_get_buffer ( jackPort, nframes ); } + +public: + bool Connect ( const char* connPort ); + bool Disconnect(); + const QString& GetConnections(); +}; + +//================================================ +// CJackAudioPort class: +//================================================ +class CJackAudioPort : public CJackClientPort +{ +protected: + friend class CJackClient; + friend class QVector; + + CJackAudioPort() : CJackClientPort ( NULL, NULL, "", false ) {} + + CJackAudioPort ( jack_client_t* theJackClient, jack_port_t* theJackPort, const QString& thePortName, bool bInput ) : + CJackClientPort ( theJackClient, theJackPort, thePortName, bInput ) + {} + +public: + bool getLatencyRange ( jack_latency_range_t& latrange ); + bool setLatencyRange ( jack_latency_range_t& latrange ); +}; + +//================================================ +// CJackMidiPort class: +//================================================ + +class CJackMidiPort : public CJackClientPort +{ +protected: + friend class CJackClient; + friend class QVector; + + CJackMidiPort() : CJackClientPort ( NULL, NULL, "", false ) {} + + CJackMidiPort ( jack_client_t* theJackClient, jack_port_t* theJackPort, const QString& thePortName, bool bInput ) : + CJackClientPort ( theJackClient, theJackPort, thePortName, bInput ) + {} + +public: + inline jack_nframes_t GetEventCount() const { return jack_midi_get_event_count ( jackPort ); } + inline bool GetEvent ( jack_midi_event_t* event, void* buffer, uint32_t eventIndex ) const + { + return ( jack_midi_event_get ( event, buffer, eventIndex ) == 0 ); + } +}; + +//================================================ +// CJackClient class: The Jack client and it's ports +//================================================ + +class CJackClient +{ +protected: + friend class CJackClientPort; + friend class CJackAudioPort; + friend class CJackMidiPort; + + QString strServerName; + QString strClientName; + QString strJackClientName; + + jack_client_t* jackClient; + jack_options_t openOptions; + jack_status_t openStatus; + bool bIsActive; + +public: + // Jack ports + QVector AudioInput; + QVector AudioOutput; + QVector MidiInput; + QVector MidiOutput; + +public: + CJackClient ( QString aClientName ); + virtual ~CJackClient() { Close(); } + +public: + bool Open(); + bool Close(); + + inline bool IsOpen() const { return ( jackClient != NULL ); } + + // temporarily for debugging + jack_client_t* GetJackClient() { return jackClient; } + +public: + bool AddAudioPort ( const QString& portName, bool bInput ); + bool AddMidiPort ( const QString& portName, bool bInput ); + +public: + inline bool SetProcessCallBack ( JackProcessCallback cbProcess, void* arg ) + { + return jackClient ? ( jack_set_process_callback ( jackClient, cbProcess, arg ) == 0 ) : false; + } + + inline bool SetShutDownCallBack ( JackShutdownCallback cbShutdown, void* arg ) + { + if ( jackClient ) + { + jack_on_shutdown ( jackClient, cbShutdown, arg ); + return true; + } + + return false; + } + + inline bool SetBuffersizeCallBack ( JackProcessCallback cbBuffersize, void* arg ) + { + return jackClient ? ( jack_set_buffer_size_callback ( jackClient, cbBuffersize, arg ) == 0 ) : false; + } + +public: + inline bool SetBufferSize ( jack_nframes_t nFrames ) const { return jackClient ? ( jack_set_buffer_size ( jackClient, nFrames ) == 0 ) : false; } + inline jack_nframes_t GetBufferSize() const { return jackClient ? jack_get_buffer_size ( jackClient ) : 0; } + inline jack_nframes_t GetSamplerate() const { return jackClient ? jack_get_sample_rate ( jackClient ) : 0; } + +public: + inline const char** GetJackPorts ( const char* port_name_pattern, const char* type_name_pattern, unsigned long flags ) const + { + return jackClient ? jack_get_ports ( jackClient, port_name_pattern, type_name_pattern, flags ) : NULL; + } + +public: + bool AutoConnect(); + unsigned int GetAudioInputConnections(); + unsigned int GetAudioOutputConnections(); + +public: + bool Activate(); + bool Deactivate(); // Also disconnects all ports ! + + inline bool IsActive() const { return bIsActive; } +}; diff --git a/linux/sound.cpp b/linux/sound.cpp index 0d9d354333..2461f7ac37 100644 --- a/linux/sound.cpp +++ b/linux/sound.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2004-2022 * * Author(s): - * Volker Fischer + * Volker Fischer, revised and maintained by Peter Goderie (pgScorpio) * * This code is based on the simple_client example of the Jack audio interface. * @@ -25,317 +25,437 @@ \******************************************************************************/ #include "sound.h" +#include "jackclient.h" +#include "global.h" -#ifdef WITH_JACK -void CSound::OpenJack ( const bool bNoAutoJackConnect, const char* jackClientName ) +//============================================================================ +// CSound: +//============================================================================ + +CSound* CSound::pSound = NULL; + +CSound::CSound ( void ( *theProcessCallback ) ( CVector& psData, void* arg ), void* theProcessCallbackArg ) : + CSoundBase ( "Jack", theProcessCallback, theProcessCallbackArg ), + jackClient ( strClientName ), // ClientName from CSoundBase ! + bJackWasShutDown ( false ), + bAutoConnect ( true ), + iJackNumInputs ( 2 ) { - jack_status_t JackStatus; - const char* serverName; + CCommandlineOptions cCommandlineOptions; + double dValue; + + setObjectName ( "CSoundThread" ); - if ( ( serverName = getenv ( "JACK_DEFAULT_SERVER" ) ) == NULL ) + if ( cCommandlineOptions.GetFlagArgument ( CMDLN_NOJACKCONNECT ) ) { - serverName = "default"; + bAutoConnect = false; } - qInfo() << qUtf8Printable ( - QString ( "Connecting to JACK \"%1\" instance (use the JACK_DEFAULT_SERVER environment variable to change this)." ).arg ( serverName ) ); - // try to become a client of the JACK server - pJackClient = jack_client_open ( jackClientName, JackNullOption, &JackStatus ); - - if ( pJackClient == nullptr ) + if ( cCommandlineOptions.GetNumericArgument ( CMDLN_JACKINPUTS, 2, 16, dValue ) ) { - throw CGenErr ( tr ( "JACK couldn't be started automatically. " - "Please start JACK manually and check for error messages." ) ); + iJackNumInputs = static_cast ( dValue ); } - // tell the JACK server to call "process()" whenever - // there is work to be done - jack_set_process_callback ( pJackClient, process, this ); + soundProperties.bHasSetupDialog = false; - // register a "buffer size changed" callback function - jack_set_buffer_size_callback ( pJackClient, bufferSizeCallback, this ); + pSound = this; +} - // register shutdown callback function - jack_on_shutdown ( pJackClient, shutdownCallback, this ); +//=============================== +// JACK callbacks: +//=============================== - // check sample rate, if not correct, just fire error - if ( jack_get_sample_rate ( pJackClient ) != SYSTEM_SAMPLE_RATE_HZ ) - { - throw CGenErr ( QString ( tr ( "JACK isn't running at a sample rate of %1 Hz. Please use " - "a tool like QjackCtl to set the " - "the JACK sample rate to %1 Hz." ) ) - .arg ( SYSTEM_SAMPLE_RATE_HZ ) ); - } +int CSound::onBufferSwitch ( jack_nframes_t nframes, void* /* arg */ ) +{ + // make sure we are locked during execution + QMutexLocker locker ( &mutexAudioProcessCallback ); - // create four ports (two for input, two for output -> stereo) - input_port_left = jack_port_register ( pJackClient, "input left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); + // get output selections + int iSelOutLeft = selectedOutputChannels[0]; + int iSelOutRight = selectedOutputChannels[1]; - input_port_right = jack_port_register ( pJackClient, "input right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0 ); + // get input selections + int iSelInLeft, iSelAddInLeft; + int iSelInRight, iSelAddInRight; + getInputSelAndAddChannels ( selectedInputChannels[0], lNumInChan, lNumAddedInChan, iSelInLeft, iSelAddInLeft ); + getInputSelAndAddChannels ( selectedInputChannels[1], lNumInChan, lNumAddedInChan, iSelInRight, iSelAddInRight ); - output_port_left = jack_port_register ( pJackClient, "output left", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + if ( IsStarted() && ( nframes == static_cast ( iDeviceBufferSize ) ) ) + { + // get input data pointers + jack_default_audio_sample_t* in_left = (jack_default_audio_sample_t*) jackClient.AudioInput[iSelInLeft].GetBuffer ( nframes ); + jack_default_audio_sample_t* in_right = (jack_default_audio_sample_t*) jackClient.AudioInput[iSelInRight].GetBuffer ( nframes ); + jack_default_audio_sample_t* add_left = + ( iSelAddInLeft >= 0 ) ? (jack_default_audio_sample_t*) jackClient.AudioInput[iSelAddInLeft].GetBuffer ( nframes ) : nullptr; + jack_default_audio_sample_t* add_right = + ( iSelAddInRight >= 0 ) ? (jack_default_audio_sample_t*) jackClient.AudioInput[iSelAddInRight].GetBuffer ( nframes ) : nullptr; - output_port_right = jack_port_register ( pJackClient, "output right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 ); + // copy input audio data + if ( ( in_left != nullptr ) && ( in_right != nullptr ) ) + { + for ( unsigned int i = 0; i < iDeviceBufferSize; i++ ) + { + unsigned int i_dest = ( i + i ); + audioBuffer[i_dest] = Float2Short ( in_left[i] * _MAXSHORT ); + if ( add_left != nullptr ) + audioBuffer[i_dest] += Float2Short ( add_left[i] * _MAXSHORT ); + + i_dest++; + audioBuffer[i_dest] = Float2Short ( in_right[i] * _MAXSHORT ); + if ( add_right != nullptr ) + audioBuffer[i_dest] += Float2Short ( add_right[i] * _MAXSHORT ); + } + } - if ( ( input_port_left == nullptr ) || ( input_port_right == nullptr ) || ( output_port_left == nullptr ) || ( output_port_right == nullptr ) ) - { - throw CGenErr ( QString ( tr ( "The JACK port registration failed. This is probably an error with JACK. Please stop %1 and JACK. " - "Afterwards check if another program at a sample rate of %2 Hz can connect to JACK." ) ) - .arg ( APP_NAME ) - .arg ( SYSTEM_SAMPLE_RATE_HZ ) ); - } + // call processing callback function + processCallback ( audioBuffer ); - // optional MIDI initialization - if ( iCtrlMIDIChannel != INVALID_MIDI_CH ) - { - input_port_midi = jack_port_register ( pJackClient, "input midi", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 ); + // get output data pointer + jack_default_audio_sample_t* out_left = (jack_default_audio_sample_t*) jackClient.AudioOutput[iSelOutLeft].GetBuffer ( nframes ); + jack_default_audio_sample_t* out_right = (jack_default_audio_sample_t*) jackClient.AudioOutput[iSelOutRight].GetBuffer ( nframes ); - if ( input_port_midi == nullptr ) + // copy output data + if ( ( out_left != nullptr ) && ( out_right != nullptr ) ) { - throw CGenErr ( QString ( tr ( "The JACK port registration failed. This is probably an error with JACK. Please stop %1 and JACK. " - "Afterwards, check if another MIDI program can connect to JACK." ) ) - .arg ( APP_NAME ) ); + for ( unsigned int i = 0; i < iDeviceBufferSize; i++ ) + { + unsigned int i_src = ( i + i ); + out_left[i] = (jack_default_audio_sample_t) audioBuffer[i_src] / _MAXSHORT; + + out_right[i] = (jack_default_audio_sample_t) audioBuffer[++i_src] / _MAXSHORT; + } } } else { - input_port_midi = nullptr; - } + // get output data pointer + jack_default_audio_sample_t* out_left = (jack_default_audio_sample_t*) jackClient.AudioOutput[iSelOutLeft].GetBuffer ( nframes ); + jack_default_audio_sample_t* out_right = (jack_default_audio_sample_t*) jackClient.AudioOutput[iSelOutRight].GetBuffer ( nframes ); - // tell the JACK server that we are ready to roll - if ( jack_activate ( pJackClient ) ) - { - throw CGenErr ( QString ( tr ( "Can't activate the JACK client. This is probably an error with JACK. Please check the JACK output." ) ) - .arg ( APP_NAME ) ); + // clear output data + if ( ( out_left != nullptr ) && ( out_right != nullptr ) ) + { + memset ( out_left, 0, sizeof ( jack_default_audio_sample_t ) * nframes ); + memset ( out_right, 0, sizeof ( jack_default_audio_sample_t ) * nframes ); + } } - if ( !bNoAutoJackConnect ) + // act on MIDI data if MIDI is enabled + if ( jackClient.MidiInput.size() ) { - // connect the ports, note: you cannot do this before - // the client is activated, because we cannot allow - // connections to be made to clients that are not - // running - const char** ports; - - // try to connect physical input ports - if ( ( ports = jack_get_ports ( pJackClient, nullptr, nullptr, JackPortIsPhysical | JackPortIsOutput ) ) != nullptr ) + void* in_midi = jackClient.MidiInput[0].GetBuffer ( nframes ); + + if ( in_midi != 0 ) { - jack_connect ( pJackClient, ports[0], jack_port_name ( input_port_left ) ); + jack_nframes_t event_count = jackClient.MidiInput[0].GetEventCount(); - // before connecting the second stereo channel, check if the input is not mono - if ( ports[1] ) + for ( jack_nframes_t j = 0; j < event_count; j++ ) { - jack_connect ( pJackClient, ports[1], jack_port_name ( input_port_right ) ); - } + jack_midi_event_t in_event; - jack_free ( ports ); - } + jack_midi_event_get ( &in_event, in_midi, j ); - // try to connect physical output ports - if ( ( ports = jack_get_ports ( pJackClient, nullptr, nullptr, JackPortIsPhysical | JackPortIsInput ) ) != nullptr ) - { - jack_connect ( pJackClient, jack_port_name ( output_port_left ), ports[0] ); + // copy packet and send it to the MIDI parser + // clang-format off +// TODO do not call malloc in real-time callback + // clang-format on + CVector vMIDIPaketBytes ( in_event.size ); - // before connecting the second stereo channel, check if the output is not mono - if ( ports[1] ) - { - jack_connect ( pJackClient, jack_port_name ( output_port_right ), ports[1] ); - } + for ( unsigned int i = 0; i < in_event.size; i++ ) + { + vMIDIPaketBytes[i] = static_cast ( in_event.buffer[i] ); + } - jack_free ( ports ); + parseMIDIMessage ( vMIDIPaketBytes ); + } } + } - // input latency - jack_latency_range_t latrange; - latrange.min = 0; - latrange.max = 0; - - jack_port_get_latency_range ( input_port_left, JackCaptureLatency, &latrange ); - int inLatency = latrange.min; // be optimistic - - // output latency - latrange.min = 0; - latrange.max = 0; + return 0; // zero on success, non-zero on error +} - jack_port_get_latency_range ( output_port_left, JackPlaybackLatency, &latrange ); - int outLatency = latrange.min; // be optimistic +int CSound::onBufferSizeCallback() +{ + QMutexLocker locker ( &mutexAudioProcessCallback ); - // compute latency by using the first input and first output - // ports and using the most optimistic values - fInOutLatencyMs = static_cast ( inLatency + outLatency ) * 1000 / SYSTEM_SAMPLE_RATE_HZ; + if ( isRunning() ) + { + emit reinitRequest ( RS_ONLY_RESTART_AND_INIT ); } + + return 0; // zero on success, non-zero on error } -void CSound::CloseJack() +void CSound::onShutdownCallback() { - // deactivate client - jack_deactivate ( pJackClient ); + QMutexLocker locker ( &mutexAudioProcessCallback ); - // unregister ports - jack_port_unregister ( pJackClient, input_port_left ); - jack_port_unregister ( pJackClient, input_port_right ); - jack_port_unregister ( pJackClient, output_port_left ); - jack_port_unregister ( pJackClient, output_port_right ); + bJackWasShutDown = true; - // close client connection to jack server - jack_client_close ( pJackClient ); + // Trying to restart should generate the error.... + emit reinitRequest ( RS_ONLY_RESTART ); } -void CSound::Start() -{ - // call base class - CSoundBase::Start(); -} +//============================================================================ +// CSoundBase: +//============================================================================ -void CSound::Stop() -{ - // call base class - CSoundBase::Stop(); -} +void CSound::closeCurrentDevice() { jackClient.Close(); } -int CSound::Init ( const int /* iNewPrefMonoBufferSize */ ) +unsigned int CSound::getDeviceBufferSize ( unsigned int iDesiredBufferSize ) { + // We can't get the actual buffersize without actualy changing the buffersize, + // so we must first stop and adjust the buffersize + CSoundStopper sound ( *this ); - // clang-format off -// try setting buffer size -// TODO seems not to work! -> no audio after this operation! -// Doesn't this give an infinite loop? The set buffer size function will call our -// registered callback which calls "EmitReinitRequestSignal()". In that function -// this CSound::Init() function is called... -//jack_set_buffer_size ( pJackClient, iNewPrefMonoBufferSize ); - // clang-format on - - // without a Jack server, Jamulus makes no sense to run, throw an error message - if ( bJackWasShutDown ) - { - throw CGenErr ( QString ( tr ( "JACK was shut down. %1 " - "requires JACK to run. Please restart %1 to " - "start JACK again. " ) ) - .arg ( APP_NAME ) ); - } + iProtocolBufferSize = iDesiredBufferSize; + jackClient.SetBufferSize ( iProtocolBufferSize ); - // get actual buffer size - iJACKBufferSizeMono = jack_get_buffer_size ( pJackClient ); + iDeviceBufferSize = jackClient.GetBufferSize(); - // init base class - CSoundBase::Init ( iJACKBufferSizeMono ); + audioBuffer.Init ( iDeviceBufferSize * 2 ); - // set internal buffer size value and calculate stereo buffer size - iJACKBufferSizeStero = 2 * iJACKBufferSizeMono; + return iDeviceBufferSize; +} + +long CSound::createDeviceList ( bool bRescan ) +{ + Q_UNUSED ( bRescan ); - // create memory for intermediate audio buffer - vecsTmpAudioSndCrdStereo.Init ( iJACKBufferSizeStero ); + strDeviceNames.clear(); + strDeviceNames.append ( SystemDriverTechniqueName() ); - return iJACKBufferSizeMono; + // Just one device, so we force selection ! + iCurrentDevice = 0; + + return lNumDevices = 1; } -// JACK callbacks -------------------------------------------------------------- -int CSound::process ( jack_nframes_t nframes, void* arg ) +bool CSound::startJack() { - CSound* pSound = static_cast ( arg ); - int i; + CSoundStopper sound ( *this ); - // make sure we are locked during execution - QMutexLocker locker ( &pSound->MutexAudioProcessCallback ); + // make sure jackClient is closed before starting + jackClient.Close(); - if ( pSound->IsRunning() && ( nframes == static_cast ( pSound->iJACKBufferSizeMono ) ) ) + if ( jackClient.Open() ) { - // get input data pointer - jack_default_audio_sample_t* in_left = (jack_default_audio_sample_t*) jack_port_get_buffer ( pSound->input_port_left, nframes ); - - jack_default_audio_sample_t* in_right = (jack_default_audio_sample_t*) jack_port_get_buffer ( pSound->input_port_right, nframes ); + // since we can open jackClient Jack is not, or no longer, shut down... + bJackWasShutDown = false; + } + else + { + if ( bJackWasShutDown ) + { + strErrorList.append ( tr ( "JACK was shut down. %1 requires JACK to run. Please start JACK again." ).arg ( APP_NAME ) ); + } + else + { + strErrorList.append ( + tr ( "JACK is not running. %1 requires JACK to run. Please start JACK first and check for error messages." ).arg ( APP_NAME ) ); + } + } - // copy input audio data - if ( ( in_left != nullptr ) && ( in_right != nullptr ) ) + if ( jackClient.IsOpen() ) + { + // create ports + if ( iJackNumInputs == 2 ) + { + jackClient.AddAudioPort ( "input left", biINPUT ); + jackClient.AddAudioPort ( "input right", biINPUT ); + } + else { - for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ ) + for ( int i = 1; i <= iJackNumInputs; i++ ) { - pSound->vecsTmpAudioSndCrdStereo[2 * i] = Float2Short ( in_left[i] * _MAXSHORT ); - pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = Float2Short ( in_right[i] * _MAXSHORT ); + + jackClient.AddAudioPort ( QString ( "input " ) + ::QString::number ( i ), biINPUT ); } } - // call processing callback function - pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo ); + jackClient.AddAudioPort ( "output left", biOUTPUT ); + jackClient.AddAudioPort ( "output right", biOUTPUT ); - // get output data pointer - jack_default_audio_sample_t* out_left = (jack_default_audio_sample_t*) jack_port_get_buffer ( pSound->output_port_left, nframes ); + if ( iCtrlMIDIChannel != INVALID_MIDI_CH ) + { + jackClient.AddMidiPort ( "input midi", biINPUT ); + } - jack_default_audio_sample_t* out_right = (jack_default_audio_sample_t*) jack_port_get_buffer ( pSound->output_port_right, nframes ); + // set callbacks + jackClient.SetShutDownCallBack ( _ShutdownCallback, this ); + jackClient.SetProcessCallBack ( _BufferSwitch, this ); + jackClient.SetBuffersizeCallBack ( _BufferSizeCallback, this ); - // copy output data - if ( ( out_left != nullptr ) && ( out_right != nullptr ) ) + // prepare buffers + jackClient.SetBufferSize ( iProtocolBufferSize ); + iDeviceBufferSize = jackClient.GetBufferSize(); + audioBuffer.Init ( iDeviceBufferSize * 2 ); + + // activate jack + if ( jackClient.Activate() ) { - for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ ) + // connect inputs and outputs + if ( bAutoConnect ) { - out_left[i] = (jack_default_audio_sample_t) pSound->vecsTmpAudioSndCrdStereo[2 * i] / _MAXSHORT; - - out_right[i] = (jack_default_audio_sample_t) pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] / _MAXSHORT; + // Auto connect the audio ports + jackClient.AutoConnect(); } + + // Don't we need to connect the midi port ???? + if ( iCtrlMIDIChannel != INVALID_MIDI_CH ) {} } } - else - { - // get output data pointer - jack_default_audio_sample_t* out_left = (jack_default_audio_sample_t*) jack_port_get_buffer ( pSound->output_port_left, nframes ); - jack_default_audio_sample_t* out_right = (jack_default_audio_sample_t*) jack_port_get_buffer ( pSound->output_port_right, nframes ); + return jackClient.IsOpen() && jackClient.IsActive(); +} - // clear output data - if ( ( out_left != nullptr ) && ( out_right != nullptr ) ) +bool CSound::stopJack() +{ + Stop(); + + jackClient.Deactivate(); + jackClient.Close(); + + return !jackClient.IsOpen(); +} + +bool CSound::checkCapabilities() +{ + if ( jackClient.IsOpen() ) + { + bool ok = true; + if ( jackClient.GetSamplerate() != SYSTEM_SAMPLE_RATE_HZ ) { - memset ( out_left, 0, sizeof ( jack_default_audio_sample_t ) * nframes ); + strErrorList.append ( tr ( "JACK isn't running at a sample rate of %1 Hz. Please use " + "a tool like QjackCtl to set the " + "the JACK sample rate to %1 Hz." ) + .arg ( SYSTEM_SAMPLE_RATE_HZ ) ); + ok = false; + } - memset ( out_right, 0, sizeof ( jack_default_audio_sample_t ) * nframes ); + int numInputConnections = jackClient.GetAudioInputConnections(); + int numOutputConnections = jackClient.GetAudioOutputConnections(); + if ( bAutoConnect ) // We don't seem to get any connections when not connected via autoconnect ! + { + bool connOk = true; + if ( numInputConnections < DRV_MIN_IN_CHANNELS ) + { + strErrorList.append ( tr ( "You have less than %1 inputs connected." ).arg ( DRV_MIN_IN_CHANNELS ) ); + ok = connOk = false; + } + + if ( numOutputConnections < DRV_MIN_OUT_CHANNELS ) + { + strErrorList.append ( tr ( "You have less than %1 ouputs connected." ).arg ( DRV_MIN_OUT_CHANNELS ) ); + ok = connOk = false; + } + + if ( !connOk ) + { + strErrorList.append ( tr ( "Please check your JACK connections." ) ); + } } + + return ok; } - // akt on MIDI data if MIDI is enabled - if ( pSound->input_port_midi != nullptr ) + strErrorList.append ( tr ( "Not connected to a Jack server." ) ); + + return false; +} + +bool CSound::setBaseValues() +{ + createDeviceList(); + + clearDeviceInfo(); + // Set the input channel names: + for ( lNumInChan = 0; lNumInChan < jackClient.AudioInput.size(); lNumInChan++ ) { - void* in_midi = jack_port_get_buffer ( pSound->input_port_midi, nframes ); + strInputChannelNames.append ( jackClient.AudioInput[lNumInChan].portName ); // pgScoprpio TODO use fixed names ?? + } - if ( in_midi != 0 ) - { - jack_nframes_t event_count = jack_midi_get_event_count ( in_midi ); + // Set any added input channel names: + addAddedInputChannelNames(); - for ( jack_nframes_t j = 0; j < event_count; j++ ) - { - jack_midi_event_t in_event; + // Set the output channel names: + for ( lNumOutChan = 0; lNumOutChan < jackClient.AudioOutput.size(); lNumOutChan++ ) + { + strOutputChannelNames.append ( jackClient.AudioOutput[lNumOutChan].portName ); // pgScoprpio TODO use fixed names ?? + } - jack_midi_event_get ( &in_event, in_midi, j ); + // Select input and output channels: + resetChannelMapping(); + // pgScorpio TODO: reload channel mapping from inifile - // copy packet and send it to the MIDI parser - // clang-format off -// TODO do not call malloc in real-time callback - // clang-format on - CVector vMIDIPaketBytes ( in_event.size ); + // Set the In/Out Latency: + fInOutLatencyMs = 0.0; + if ( ( jackClient.AudioInput.size() ) && ( jackClient.AudioOutput.size() ) ) + { + // compute latency by using the first input and first output + // ports and using the most optimistic values - for ( i = 0; i < static_cast ( in_event.size ); i++ ) - { - vMIDIPaketBytes[i] = static_cast ( in_event.buffer[i] ); - } - pSound->ParseMIDIMessage ( vMIDIPaketBytes ); - } - } + jack_latency_range_t inputLatencyRange; + jack_latency_range_t outputLatencyRange; + + jackClient.AudioInput[0].getLatencyRange ( inputLatencyRange ); + jackClient.AudioOutput[0].getLatencyRange ( outputLatencyRange ); + + // be optimistic + fInOutLatencyMs = static_cast ( inputLatencyRange.min + outputLatencyRange.min ) * 1000 / SYSTEM_SAMPLE_RATE_HZ; } - return 0; // zero on success, non-zero on error + return ( jackClient.IsOpen() && ( lNumInChan >= DRV_MIN_IN_CHANNELS ) && ( lNumOutChan >= DRV_MIN_OUT_CHANNELS ) ); } -int CSound::bufferSizeCallback ( jack_nframes_t, void* arg ) +// TODO: ChannelMapping from inifile! +bool CSound::checkDeviceChange ( CSoundBase::tDeviceChangeCheck mode, int iDriverIndex ) // Open device sequence handling.... { - CSound* pSound = static_cast ( arg ); + // We have just one device ! + if ( iDriverIndex != 0 ) + { + return false; + } - pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART_AND_INIT ); + switch ( mode ) + { + case CSoundBase::tDeviceChangeCheck::CheckOpen: + { + // Now reset jack by closing and re-opening jack + if ( bJackWasShutDown ) + { + stopJack(); + } + if ( !startJack() ) + { + return false; + } + } + return true; - return 0; // zero on success, non-zero on error + case CSoundBase::tDeviceChangeCheck::CheckCapabilities: + return checkCapabilities(); + + case CSoundBase::tDeviceChangeCheck::Activate: + return setBaseValues(); + + case CSoundBase::tDeviceChangeCheck::Abort: + setBaseValues(); // Still try to set Base values, since there is no other device ! + return true; + + default: + return false; + } } -void CSound::shutdownCallback ( void* arg ) +bool CSound::start() { - CSound* pSound = static_cast ( arg ); + if ( !IsStarted() && jackClient.IsActive() ) + { + return true; + } - pSound->bJackWasShutDown = true; - pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART_AND_INIT ); + return IsStarted(); } -#endif // WITH_JACK + +bool CSound::stop() { return true; } diff --git a/linux/sound.h b/linux/sound.h index bffddf8d36..1128050bc7 100644 --- a/linux/sound.h +++ b/linux/sound.h @@ -2,7 +2,7 @@ * Copyright (c) 2004-2022 * * Author(s): - * Volker Fischer + * Volker Fischer, revised and maintained by Peter Goderie (pgScorpio) * ****************************************************************************** * @@ -24,10 +24,8 @@ #pragma once -#ifndef JACK_REPLACES_ASIO // these headers are not available in Windows OS -# include -# include -#endif +#include +#include #include #include #include @@ -38,119 +36,74 @@ #include "soundbase.h" #include "global.h" -#if WITH_JACK -# include -# include -#endif +#include "jackclient.h" -/* Definitions ****************************************************************/ -#define NUM_IN_OUT_CHANNELS 2 // always stereo +// Values for bool bInput/bIsInput +#define biINPUT true +#define biOUTPUT false -// the number of periods is critical for latency -#define NUM_PERIOD_BLOCKS_IN 2 -#define NUM_PERIOD_BLOCKS_OUT 1 +//============================================================================ +// CSound: +//============================================================================ -#define MAX_SND_BUF_IN 200 -#define MAX_SND_BUF_OUT 200 - -/* Classes ********************************************************************/ -#if WITH_JACK class CSound : public CSoundBase { Q_OBJECT -public: - CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* arg ), - void* arg, - const QString& strMIDISetup, - const bool bNoAutoJackConnect, - const QString& strJackClientName ) : - CSoundBase ( "Jack", fpNewProcessCallback, arg, strMIDISetup ), - iJACKBufferSizeMono ( 0 ), - bJackWasShutDown ( false ), - fInOutLatencyMs ( 0.0f ) - { - QString strJackName = QString ( APP_NAME ); - - if ( !strJackClientName.isEmpty() ) - { - strJackName += " " + strJackClientName; - } +protected: // Jack: + CJackClient jackClient; + bool bJackWasShutDown; + bool bAutoConnect; + int iJackNumInputs; - OpenJack ( bNoAutoJackConnect, strJackName.toLocal8Bit().data() ); - } + bool startJack(); + bool stopJack(); + bool checkCapabilities(); + bool setBaseValues(); - virtual ~CSound() { CloseJack(); } +public: + CSound ( void ( *theProcessCallback ) ( CVector& psData, void* arg ), void* theProcessCallbackArg ); - virtual int Init ( const int iNewPrefMonoBufferSize ); - virtual void Start(); - virtual void Stop(); + virtual ~CSound() + { + Stop(); + stopJack(); + } - virtual float GetInOutLatencyMs() { return fInOutLatencyMs; } +protected: + // CSound callbacks: + int onBufferSwitch ( jack_nframes_t nframes, void* arg ); - // these variables should be protected but cannot since we want - // to access them from the callback function - CVector vecsTmpAudioSndCrdStereo; - int iJACKBufferSizeMono; - int iJACKBufferSizeStero; - bool bJackWasShutDown; + int onBufferSizeCallback(); - jack_port_t* input_port_left; - jack_port_t* input_port_right; - jack_port_t* output_port_left; - jack_port_t* output_port_right; - jack_port_t* input_port_midi; + void onShutdownCallback(); protected: - void OpenJack ( const bool bNoAutoJackConnect, const char* jackClientName ); + // static callbacks: + static int _BufferSwitch ( jack_nframes_t nframes, void* arg ) { return pSound->onBufferSwitch ( nframes, arg ); } - void CloseJack(); + static int _BufferSizeCallback ( jack_nframes_t, void* ) { return pSound->onBufferSizeCallback(); } - // callbacks - static int process ( jack_nframes_t nframes, void* arg ); - static int bufferSizeCallback ( jack_nframes_t, void* arg ); - static void shutdownCallback ( void* ); - jack_client_t* pJackClient; + static void _ShutdownCallback ( void* ) { pSound->onShutdownCallback(); } - float fInOutLatencyMs; -}; -#else -// no sound -> dummy class definition -# include "server.h" -class CSound : public CSoundBase -{ - Q_OBJECT + //============================================================================ + // Virtual interface to CSoundBase: + //============================================================================ +protected: // CSoundBase Mandatory pointer to instance (must be set to 'this' in the CSound constructor) + static CSound* pSound; -public: - CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* pParg ), - void* pParg, - const QString& strMIDISetup, - const bool, - const QString& ) : - CSoundBase ( "nosound", fpNewProcessCallback, pParg, strMIDISetup ), - HighPrecisionTimer ( true ) - { - HighPrecisionTimer.Start(); - QObject::connect ( &HighPrecisionTimer, &CHighPrecisionTimer::timeout, this, &CSound::OnTimer ); - } - virtual ~CSound() {} - virtual int Init ( const int iNewPrefMonoBufferSize ) - { - CSoundBase::Init ( iNewPrefMonoBufferSize ); - vecsTemp.Init ( 2 * iNewPrefMonoBufferSize ); - return iNewPrefMonoBufferSize; - } - CHighPrecisionTimer HighPrecisionTimer; - CVector vecsTemp; +public: // CSoundBase Mandatory functions. (but static functions can't be virtual) + static inline CSoundBase* pInstance() { return pSound; } + static inline const CSoundProperties& GetProperties() { return pSound->getSoundProperties(); } -public slots: - void OnTimer() - { - vecsTemp.Reset ( 0 ); - if ( IsRunning() ) - { - ProcessCallback ( vecsTemp ); - } - } +protected: + virtual void onChannelSelectionChanged(){}; // Needed on macOS + + virtual long createDeviceList ( bool bRescan = false ); // Fills strDeviceNames returns lNumDevices + virtual bool checkDeviceChange ( tDeviceChangeCheck mode, int iDriverIndex ); // Performs the different actions for a device change + virtual unsigned int getDeviceBufferSize ( unsigned int iDesiredBufferSize ); // returns the nearest possible buffersize of selected device + virtual void closeCurrentDevice(); // Closes the current driver and Clears Device Info + virtual bool openDeviceSetup() { return false; } + virtual bool start(); // Should call _onStarted() just before return + virtual bool stop(); // Should call _onStopped() just before return }; -#endif // WITH_JACK diff --git a/mac/sound.cpp b/mac/sound.cpp index 1d175e2937..4778633163 100644 --- a/mac/sound.cpp +++ b/mac/sound.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2004-2022 * * Author(s): - * Volker Fischer + * Volker Fischer, revised and maintained by Peter Goderie (pgScorpio) * ****************************************************************************** * @@ -24,145 +24,440 @@ #include "sound.h" -/* Implementation *************************************************************/ -CSound::CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* arg ), - void* arg, - const QString& strMIDISetup, - const bool, - const QString& ) : - CSoundBase ( "CoreAudio", fpNewProcessCallback, arg, strMIDISetup ), - midiInPortRef ( static_cast ( NULL ) ) -{ - // Apple Mailing Lists: Subject: GUI Apps should set kAudioHardwarePropertyRunLoop - // in the HAL, From: Jeff Moore, Date: Fri, 6 Dec 2002 - // Most GUI applciations have several threads on which they receive - // notifications already, so the having the HAL's thread around is wasteful. - // Here is what you should do: On the thread you want the HAL to use for - // notifications (for most apps, this will be the main thread), add the - // following lines of code: - // tell the HAL to use the current thread as it's run loop - CFRunLoopRef theRunLoop = CFRunLoopGetCurrent(); - AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; +//============================================================================ +// Helpers: +//============================================================================ - AudioObjectSetPropertyData ( kAudioObjectSystemObject, &property, 0, NULL, sizeof ( CFRunLoopRef ), &theRunLoop ); +bool ConvertCFStringToQString ( const CFStringRef stringRef, QString& sOut ) +{ + bool ok = false; - // initial query for available input/output sound devices in the system - GetAvailableInOutDevices(); - - // init device index as not initialized (invalid) - lCurDev = INVALID_INDEX; - CurrentAudioInputDeviceID = 0; - CurrentAudioOutputDeviceID = 0; - iNumInChan = 0; - iNumInChanPlusAddChan = 0; - iNumOutChan = 0; - iSelInputLeftChannel = 0; - iSelInputRightChannel = 0; - iSelOutputLeftChannel = 0; - iSelOutputRightChannel = 0; + sOut.clear(); - // Optional MIDI initialization -------------------------------------------- - if ( iCtrlMIDIChannel != INVALID_MIDI_CH ) + // check if the string reference is a valid pointer + if ( stringRef != NULL ) { - // create client and ports - MIDIClientRef midiClient = static_cast ( NULL ); - MIDIClientCreate ( CFSTR ( APP_NAME ), NULL, NULL, &midiClient ); - MIDIInputPortCreate ( midiClient, CFSTR ( "Input port" ), callbackMIDI, this, &midiInPortRef ); + // first check if the string is not empty + if ( CFStringGetLength ( stringRef ) > 0 ) + { + // convert CFString in c-string (quick hack!) and then in QString + char* sC_strPropValue = (char*) malloc ( CFStringGetLength ( stringRef ) * 3 + 1 ); - // open connections from all sources - const int iNMIDISources = MIDIGetNumberOfSources(); + if ( CFStringGetCString ( stringRef, sC_strPropValue, CFStringGetLength ( stringRef ) * 3 + 1, kCFStringEncodingUTF8 ) ) + { + sOut = sC_strPropValue; + ok = true; + } - for ( int i = 0; i < iNMIDISources; i++ ) + free ( sC_strPropValue ); + } + } + + return ok; +} + +//============================================================================ +// Audio Hardware Helpers: +//============================================================================ + +bool ahGetDefaultDevice ( bool bInput, AudioDeviceID& deviceId ) +{ + UInt32 iPropertySize = sizeof ( AudioDeviceID ); + AudioObjectPropertyAddress stPropertyAddress; + + stPropertyAddress.mSelector = bInput ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + + return ( AudioObjectGetPropertyData ( kAudioObjectSystemObject, &stPropertyAddress, 0, NULL, &iPropertySize, &deviceId ) == noErr ); +} + +bool ahGetAudioDeviceList ( CVector& vAudioDevices ) +{ + UInt32 iPropertySize = 0; + AudioObjectPropertyAddress stPropertyAddress; + + stPropertyAddress.mSelector = kAudioHardwarePropertyDevices; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + + if ( AudioObjectGetPropertyDataSize ( kAudioObjectSystemObject, &stPropertyAddress, 0, NULL, &iPropertySize ) == noErr ) + { + vAudioDevices.Init ( iPropertySize / sizeof ( AudioDeviceID ) ); + + if ( AudioObjectGetPropertyData ( kAudioObjectSystemObject, &stPropertyAddress, 0, NULL, &iPropertySize, &vAudioDevices[0] ) == noErr ) { - MIDIEndpointRef src = MIDIGetSource ( i ); - MIDIPortConnectSource ( midiInPortRef, src, NULL ); + return true; } } + + vAudioDevices.clear(); + return false; } -void CSound::GetAvailableInOutDevices() +bool ahGetDeviceStreamIdList ( AudioDeviceID deviceID, bool bInput, CVector& streamIdList ) { UInt32 iPropertySize = 0; AudioObjectPropertyAddress stPropertyAddress; - stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; - stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + stPropertyAddress.mSelector = kAudioDevicePropertyStreams; + stPropertyAddress.mScope = bInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; - // first get property size of devices array and allocate memory - stPropertyAddress.mSelector = kAudioHardwarePropertyDevices; + if ( ( AudioObjectGetPropertyDataSize ( deviceID, &stPropertyAddress, 0, NULL, &iPropertySize ) == noErr ) && ( iPropertySize ) ) + { + streamIdList.Init ( iPropertySize / sizeof ( AudioStreamID ) ); + + if ( AudioObjectGetPropertyData ( deviceID, &stPropertyAddress, 0, NULL, &iPropertySize, &streamIdList[0] ) == noErr ) + { + return ( streamIdList.Size() > 0 ); + } + } + + streamIdList.clear(); + return false; +} + +bool ahGetStreamFormat ( AudioStreamID streamId, AudioStreamBasicDescription& streamFormat ) +{ + UInt32 iPropertySize; + AudioObjectPropertyAddress stPropertyAddress; + + stPropertyAddress.mSelector = kAudioStreamPropertyVirtualFormat; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + + if ( AudioObjectGetPropertyDataSize ( streamId, &stPropertyAddress, 0, NULL, &iPropertySize ) == noErr ) + { + if ( iPropertySize == sizeof ( AudioStreamBasicDescription ) ) + { + if ( AudioObjectGetPropertyData ( streamId, &stPropertyAddress, 0, NULL, &iPropertySize, &streamFormat ) == noErr ) + { + return true; + } + } + } + + memset ( &streamFormat, 0, sizeof ( streamFormat ) ); + return false; +} + +bool ahGetStreamLatency ( AudioStreamID streamId, UInt32& iLatencyFrames ) +{ + UInt32 iPropertySize = sizeof ( UInt32 ); - AudioObjectGetPropertyDataSize ( kAudioObjectSystemObject, &stPropertyAddress, 0, NULL, &iPropertySize ); + AudioObjectPropertyAddress stPropertyAddress; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + stPropertyAddress.mSelector = kAudioStreamPropertyLatency; - CVector vAudioDevices ( iPropertySize ); + // number of frames of latency in the AudioStream + OSStatus ret = AudioObjectGetPropertyData ( streamId, &stPropertyAddress, 0, NULL, &iPropertySize, &iLatencyFrames ); + if ( ret != noErr ) + { + iLatencyFrames = 0; + return false; + } - // now actually query all devices present in the system - AudioObjectGetPropertyData ( kAudioObjectSystemObject, &stPropertyAddress, 0, NULL, &iPropertySize, &vAudioDevices[0] ); + return true; +} - // calculate device count based on size of returned data array - const UInt32 iDeviceCount = iPropertySize / sizeof ( AudioDeviceID ); +bool ahGetDeviceLatency ( AudioDeviceID deviceId, bool bInput, UInt32& iLatency ) +{ + UInt32 deviceLatency = 0; + UInt32 safetyOffset = 0; + UInt32 streamLatency = 0; - // always add system default devices for input and output as first entry - lNumDevs = 0; - strDriverNames[lNumDevs] = "System Default In/Out Devices"; + UInt32 size = sizeof ( deviceLatency ); + AudioObjectPropertyAddress stPropertyAddress; - iPropertySize = sizeof ( AudioDeviceID ); - stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + iLatency = 0; + + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + stPropertyAddress.mSelector = kAudioDevicePropertyLatency; + stPropertyAddress.mScope = bInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + if ( AudioObjectGetPropertyData ( deviceId, &stPropertyAddress, 0, nullptr, &size, &deviceLatency ) == noErr ) + { + iLatency += deviceLatency; + } - if ( AudioObjectGetPropertyData ( kAudioObjectSystemObject, &stPropertyAddress, 0, NULL, &iPropertySize, &audioInputDevice[lNumDevs] ) ) + size = sizeof ( safetyOffset ); + stPropertyAddress.mSelector = kAudioDevicePropertySafetyOffset; + if ( AudioObjectGetPropertyData ( deviceId, &stPropertyAddress, 0, nullptr, &size, &safetyOffset ) != noErr ) { - throw CGenErr ( tr ( "No sound card is available in your system. " - "CoreAudio input AudioHardwareGetProperty call failed." ) ); + return false; } - iPropertySize = sizeof ( AudioDeviceID ); - stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + iLatency += safetyOffset; - if ( AudioObjectGetPropertyData ( kAudioObjectSystemObject, &stPropertyAddress, 0, NULL, &iPropertySize, &audioOutputDevice[lNumDevs] ) ) + // Query stream latency + stPropertyAddress.mSelector = kAudioDevicePropertyStreams; + if ( AudioObjectGetPropertyDataSize ( deviceId, &stPropertyAddress, 0, nullptr, &size ) != noErr ) { - throw CGenErr ( tr ( "No sound card is available in the system. " - "CoreAudio output AudioHardwareGetProperty call failed." ) ); + return false; } - lNumDevs++; // next device + CVector streamList; + streamList.Init ( size / sizeof ( AudioStreamID ) ); - // add detected devices - // - // we add combined entries for input and output for each device so that we - // do not need two combo boxes in the GUI for input and output (therefore - // all possible combinations are required which can be a large number) - for ( UInt32 i = 0; i < iDeviceCount; i++ ) + if ( streamList.Size() == 0 ) { - for ( UInt32 j = 0; j < iDeviceCount; j++ ) - { - // get device infos for both current devices - QString strDeviceName_i; - QString strDeviceName_j; - bool bIsInput_i; - bool bIsInput_j; - bool bIsOutput_i; - bool bIsOutput_j; + return false; + } - GetAudioDeviceInfos ( vAudioDevices[i], strDeviceName_i, bIsInput_i, bIsOutput_i ); + if ( AudioObjectGetPropertyData ( deviceId, &stPropertyAddress, 0, nullptr, &size, &streamList[0] ) != noErr ) + { + return false; + } - GetAudioDeviceInfos ( vAudioDevices[j], strDeviceName_j, bIsInput_j, bIsOutput_j ); + stPropertyAddress.mSelector = kAudioStreamPropertyLatency; + size = sizeof ( streamLatency ); + // We could check all streams for the device, but it only ever seems to return the stream latency on the first stream + if ( AudioObjectGetPropertyData ( streamList[0], &stPropertyAddress, 0, nullptr, &size, &streamLatency ) != noErr ) + { + return false; + } - // check if i device is input and j device is output and that we are - // in range - if ( bIsInput_i && bIsOutput_j && ( lNumDevs < MAX_NUMBER_SOUND_CARDS ) ) - { - strDriverNames[lNumDevs] = "in: " + strDeviceName_i + "/out: " + strDeviceName_j; + iLatency += streamLatency; + + return true; +} + +bool ahGetDeviceBufferList ( AudioDeviceID devID, bool bInput, CVector& vBufferList ) +{ + UInt32 iPropertySize = sizeof ( AudioBufferList ); + AudioObjectPropertyAddress stPropertyAddress; + + stPropertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration; + stPropertyAddress.mScope = bInput ? kAudioObjectPropertyScopeInput : kAudioObjectPropertyScopeOutput; + stPropertyAddress.mElement = 0; - // store audio device IDs - audioInputDevice[lNumDevs] = vAudioDevices[i]; - audioOutputDevice[lNumDevs] = vAudioDevices[j]; + vBufferList.clear(); + AudioBufferList* pBufferList = NULL; - lNumDevs++; // next device + if ( AudioObjectGetPropertyDataSize ( devID, &stPropertyAddress, 0, NULL, &iPropertySize ) == noErr ) + { + pBufferList = (AudioBufferList*) malloc ( iPropertySize ); + + if ( pBufferList && ( AudioObjectGetPropertyData ( devID, &stPropertyAddress, 0, NULL, &iPropertySize, pBufferList ) == noErr ) ) + { + const unsigned int numBuffers = pBufferList->mNumberBuffers; + for ( unsigned int i = 0; i < numBuffers; i++ ) + { + vBufferList.Add ( pBufferList->mBuffers[i] ); } + + free ( pBufferList ); + + return ( vBufferList.Size() > 0 ); } } + + if ( pBufferList ) + { + free ( pBufferList ); + } + + return false; } -void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID, QString& strDeviceName, bool& bIsInput, bool& bIsOutput ) +bool ahGetDeviceName ( AudioDeviceID devId, CFStringRef& stringValue ) +{ + UInt32 iPropertySize = sizeof ( CFStringRef ); + AudioObjectPropertyAddress stPropertyAddress; + + stPropertyAddress.mSelector = kAudioObjectPropertyElementName; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + + return ( AudioObjectGetPropertyData ( devId, &stPropertyAddress, 0, NULL, &iPropertySize, &stringValue ) == noErr ); +} + +bool ahGetDeviceChannelName ( AudioDeviceID devID, bool bInput, int chIndex, QString& strName ) +{ + bool ok = false; + UInt32 iPropertySize = sizeof ( CFStringRef ); + AudioObjectPropertyAddress stPropertyAddress; + + stPropertyAddress.mSelector = kAudioObjectPropertyElementName; + stPropertyAddress.mScope = bInput ? kAudioObjectPropertyScopeInput : kAudioObjectPropertyScopeOutput; + stPropertyAddress.mElement = chIndex + 1; + + strName.clear(); + CFStringRef cfName = NULL; + + if ( AudioObjectGetPropertyData ( devID, &stPropertyAddress, 0, NULL, &iPropertySize, &cfName ) == noErr ) + { + ok = ConvertCFStringToQString ( cfName, strName ); + CFRelease ( cfName ); + } + + if ( strName.length() == 0 ) + { + strName = ( bInput ) ? "In" : "Out"; + strName += " " + QString::number ( stPropertyAddress.mElement ); + + return false; + } + + return ok; +} + +bool ahGetDeviceSampleRate ( AudioDeviceID devId, Float64& sampleRate ) +{ + UInt32 iPropertySize = sizeof ( Float64 ); + AudioObjectPropertyAddress stPropertyAddress; + + stPropertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + + return ( AudioObjectGetPropertyData ( devId, &stPropertyAddress, 0, NULL, &iPropertySize, &sampleRate ) == noErr ); +} + +bool ahSetDeviceSampleRate ( AudioDeviceID devId, Float64 sampleRate ) +{ + UInt32 iPropertySize = sizeof ( Float64 ); + AudioObjectPropertyAddress stPropertyAddress; + + stPropertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + + return ( AudioObjectSetPropertyData ( devId, &stPropertyAddress, 0, NULL, iPropertySize, &sampleRate ) == noErr ); +} + +//============================================================================ +// CSound data classes: +//============================================================================ + +bool CSound::cChannelInfo::IsValid ( bool asAdded ) +{ + if ( asAdded ) + { + // Valid Added Input info ? + return ( iListIndex < 0 ) || ( ( DeviceId != (AudioDeviceID) 0 ) && ( DeviceId != (AudioDeviceID) -1 ) && ( iBuffer >= 0 ) && + ( iChanPerFrame > 0 ) && ( iInterlCh >= 0 ) && ( iInterlCh < iChanPerFrame ) ); + } + else + { + // Valid Normal Input or Output info ? + return ( iListIndex >= 0 ) && ( ( DeviceId != (AudioDeviceID) 0 ) && ( DeviceId != (AudioDeviceID) -1 ) && ( iBuffer >= 0 ) && + ( iChanPerFrame > 0 ) && ( iInterlCh >= 0 ) && ( iInterlCh < iChanPerFrame ) ); + } +} + +bool CSound::cChannelInfo::IsValidFor ( AudioDeviceID forDeviceId, bool asInput, bool asAdded ) +{ + if ( asAdded ) + { + // Valid Added Input info ? + return ( iListIndex < 0 ) || ( ( DeviceId == forDeviceId ) && ( isInput == asInput ) && ( iBuffer >= 0 ) && ( iChanPerFrame > 0 ) && + ( iInterlCh >= 0 ) && ( iInterlCh < iChanPerFrame ) ); + } + else + { + // Valid Normal Input or Output info ? + return ( iListIndex >= 0 ) && ( ( DeviceId == forDeviceId ) && ( isInput == asInput ) && ( iBuffer >= 0 ) && ( iChanPerFrame > 0 ) && + ( iInterlCh >= 0 ) && ( iInterlCh < iChanPerFrame ) ); + } +} + +bool CSound::cDeviceInfo::IsValid() +{ + if ( ( InputDeviceId == 0 ) || ( InputDeviceId == (AudioDeviceID) -1 ) || ( OutputDeviceId == 0 ) || ( OutputDeviceId == (AudioDeviceID) -1 ) || + ( lNumInChan <= 0 ) || ( lNumAddedInChan < 0 ) || ( lNumOutChan <= 0 ) ) + { + return false; + } + + if ( ( input.Size() != ( lNumInChan + lNumAddedInChan ) ) || ( output.Size() != lNumOutChan ) ) + { + return false; + } + + for ( int i = 0; i < lNumInChan; i++ ) + { + if ( !input[i].IsValidFor ( InputDeviceId, biINPUT, false ) ) + { + return false; + } + } + + for ( int i = 0; i < lNumAddedInChan; i++ ) + { + if ( !input[lNumInChan + i].IsValidFor ( InputDeviceId, biINPUT, true ) ) + { + return false; + } + } + + for ( int i = 0; i < output.Size(); i++ ) + { + if ( !output[i].IsValidFor ( OutputDeviceId, biOUTPUT, false ) ) + { + return false; + } + } + + return true; +} + +//============================================================================ +// CSound class: +//============================================================================ + +CSound* CSound::pSound = NULL; + +CSound::cChannelInfo CSound::noChannelInfo; + +CSound::CSound ( void ( *theProcessCallback ) ( CVector& psData, void* arg ), void* theProcessCallbackArg ) : + CSoundBase ( "CoreAudio", theProcessCallback, theProcessCallbackArg ), + midiInPortRef ( 0 ) +{ + setObjectName ( "CSoundThread" ); + + // Apple Mailing Lists: Subject: GUI Apps should set kAudioHardwarePropertyRunLoop + // in the HAL, From: Jeff Moore, Date: Fri, 6 Dec 2002 + // Most GUI applciations have several threads on which they receive + // notifications already, so the having the HAL's thread around is wasteful. + // Here is what you should do: On the thread you want the HAL to use for + // notifications (for most apps, this will be the main thread), add the + // following lines of code: + // tell the HAL to use the current thread as it's run loop + CFRunLoopRef theRunLoop = CFRunLoopGetCurrent(); + AudioObjectPropertyAddress property = { kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; + + AudioObjectSetPropertyData ( kAudioObjectSystemObject, &property, 0, NULL, sizeof ( CFRunLoopRef ), &theRunLoop ); + + // initial query for available input/output sound devices in the system + // GetAvailableInOutDevices(); + + // Optional MIDI initialization -------------------------------------------- + if ( iCtrlMIDIChannel != INVALID_MIDI_CH ) + { + // create client and ports + MIDIClientRef midiClient = 0; + MIDIClientCreate ( CFSTR ( APP_NAME ), NULL, NULL, &midiClient ); + MIDIInputPortCreate ( midiClient, CFSTR ( "Input port" ), _onMIDI, this, &midiInPortRef ); + + // open connections from all sources + const int iNMIDISources = MIDIGetNumberOfSources(); + + for ( int i = 0; i < iNMIDISources; i++ ) + { + MIDIEndpointRef src = MIDIGetSource ( i ); + MIDIPortConnectSource ( midiInPortRef, src, NULL ); + } + } + + // set my properties in CSoundbase: + soundProperties.bHasSetupDialog = false; + + pSound = this; +} + +void getAudioDeviceInfos ( int index, const AudioDeviceID DeviceID, QString& strDeviceName, bool& bIsInput, bool& bIsOutput ) { UInt32 iPropertySize; AudioObjectPropertyAddress stPropertyAddress; @@ -191,6 +486,7 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID, QString& strDev bIsOutput = ( iPropertySize > 0 ); // check if any output streams are available + strDeviceName.clear(); // get property name CFStringRef sPropertyStringValue = NULL; @@ -198,132 +494,257 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID, QString& strDev stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; iPropertySize = sizeof ( CFStringRef ); - AudioObjectGetPropertyData ( DeviceID, &stPropertyAddress, 0, NULL, &iPropertySize, &sPropertyStringValue ); + if ( AudioObjectGetPropertyData ( DeviceID, &stPropertyAddress, 0, NULL, &iPropertySize, &sPropertyStringValue ) == noErr ) + { + // convert string + ConvertCFStringToQString ( sPropertyStringValue, strDeviceName ); - // convert string - if ( !ConvertCFStringToQString ( sPropertyStringValue, strDeviceName ) ) + CFRelease ( sPropertyStringValue ); + } + + if ( strDeviceName.length() == 0 ) { // use a default name in case the conversion did not succeed - strDeviceName = "UNKNOWN"; + strDeviceName = "Device" + QString::number ( index ); } } -int CSound::CountChannels ( AudioDeviceID devID, bool isInput ) +bool CSound::setBufferSize ( AudioDeviceID& audioInDeviceID, + AudioDeviceID& audioOutDeviceID, + unsigned int iPrefBufferSize, + unsigned int& iActualBufferSize ) { - OSStatus err; - UInt32 propSize; - int result = 0; + bool ok = true; - if ( isInput ) + UInt32 iSizeBufValue; + UInt32 iRequestedBuffersize = iPrefBufferSize; + UInt32 iActualInBufferSize = 0; + UInt32 iActualOutBufferSize = 0; + AudioObjectPropertyAddress stPropertyAddress; + + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + stPropertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize; + + if ( audioInDeviceID ) { - vecNumInBufChan.Init ( 0 ); + iSizeBufValue = sizeof ( UInt32 ); + stPropertyAddress.mScope = kAudioObjectPropertyScopeInput; + + ok &= ( AudioObjectSetPropertyData ( audioInDeviceID, &stPropertyAddress, 0, NULL, iSizeBufValue, &iRequestedBuffersize ) == noErr ); + ok &= ( AudioObjectGetPropertyData ( audioInDeviceID, &stPropertyAddress, 0, NULL, &iSizeBufValue, &iActualInBufferSize ) == noErr ); + ok &= ( iSizeBufValue == sizeof ( UInt32 ) ); } - else + + if ( audioOutDeviceID ) { - vecNumOutBufChan.Init ( 0 ); + iSizeBufValue = sizeof ( UInt32 ); + stPropertyAddress.mScope = kAudioObjectPropertyScopeOutput; + + ok &= ( AudioObjectSetPropertyData ( audioOutDeviceID, &stPropertyAddress, 0, NULL, iSizeBufValue, &iRequestedBuffersize ) == noErr ); + ok &= ( AudioObjectGetPropertyData ( audioOutDeviceID, &stPropertyAddress, 0, NULL, &iSizeBufValue, &iActualOutBufferSize ) == noErr ); + ok &= ( iSizeBufValue == sizeof ( UInt32 ) ); } - // it seems we have multiple buffers where each buffer has only one channel, - // in that case we assume that each input channel has its own buffer - AudioObjectPropertyScope theScope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput; + if ( audioInDeviceID && audioOutDeviceID ) + { + // Return lowest size: (they still might be zero!) + iActualBufferSize = ( iActualInBufferSize < iActualOutBufferSize ) ? iActualInBufferSize : iActualOutBufferSize; - AudioObjectPropertyAddress theAddress = { kAudioDevicePropertyStreamConfiguration, theScope, 0 }; + return ok && ( iActualBufferSize != 0 ) && ( iActualInBufferSize == iActualOutBufferSize ); + } + else if ( audioInDeviceID ) + { + iActualBufferSize = iActualInBufferSize; + return ok && ( iActualBufferSize != 0 ); + } + else if ( audioOutDeviceID ) + { + iActualBufferSize = iActualOutBufferSize; + return ok && ( iActualBufferSize != 0 ); + } + + iActualBufferSize = 0; + return false; +} - AudioObjectGetPropertyDataSize ( devID, &theAddress, 0, NULL, &propSize ); +//============================================================================ +// Callbacks: +//============================================================================ - AudioBufferList* buflist = (AudioBufferList*) malloc ( propSize ); +OSStatus CSound::onDeviceNotification ( AudioDeviceID, UInt32, const AudioObjectPropertyAddress* inAddresses ) +{ + if ( ( inAddresses->mSelector == kAudioDevicePropertyDeviceHasChanged ) || ( inAddresses->mSelector == kAudioDevicePropertyDeviceIsAlive ) || + ( inAddresses->mSelector == kAudioHardwarePropertyDefaultOutputDevice ) || + ( inAddresses->mSelector == kAudioHardwarePropertyDefaultInputDevice ) ) + { + if ( ( inAddresses->mSelector == kAudioDevicePropertyDeviceHasChanged ) || ( inAddresses->mSelector == kAudioDevicePropertyDeviceIsAlive ) ) + { + // if any property of the device has changed, do a full reload + emit reinitRequest ( tSndCrdResetType::RS_RELOAD_RESTART_AND_INIT ); + } + else + { + // for any other change in audio devices, just initiate a restart which + // triggers an update of the sound device selection combo box + emit reinitRequest ( RS_ONLY_RESTART ); + } + } - err = AudioObjectGetPropertyData ( devID, &theAddress, 0, NULL, &propSize, buflist ); + return noErr; +} - if ( !err ) +OSStatus CSound::onBufferSwitch ( AudioDeviceID deviceID, + const AudioTimeStamp*, + const AudioBufferList* inInputData, + const AudioTimeStamp*, + AudioBufferList* outOutputData, + const AudioTimeStamp* ) +{ + // both, the input and output device use the same callback function + QMutexLocker locker ( &mutexAudioProcessCallback ); + static bool awaitIn = true; + + if ( IsStarted() ) { - for ( UInt32 i = 0; i < buflist->mNumberBuffers; ++i ) + if ( awaitIn && inInputData && ( deviceID == selectedDevice.InputDeviceId ) ) { - // The correct value mNumberChannels for an AudioBuffer can be derived from the mChannelsPerFrame - // and the interleaved flag. For non interleaved formats, mNumberChannels is always 1. - // For interleaved formats, mNumberChannels is equal to mChannelsPerFrame. - result += buflist->mBuffers[i].mNumberChannels; + int iSelBufferLeft = selectedDevice.InLeft.iBuffer; + int iSelChanPerFrameLeft = selectedDevice.InLeft.iChanPerFrame; + int iSelInterlChLeft = selectedDevice.InLeft.iInterlCh; + + int iSelBufferRight = selectedDevice.InRight.iBuffer; + int iSelChanPerFrameRight = selectedDevice.InRight.iChanPerFrame; + int iSelInterlChRight = selectedDevice.InRight.iInterlCh; + + int iAddBufferLeft = selectedDevice.AddInLeft.iBuffer; + int iAddChanPerFrameLeft = selectedDevice.AddInLeft.iChanPerFrame; + int iAddInterlChLeft = selectedDevice.AddInLeft.iInterlCh; + + int iAddBufferRight = selectedDevice.AddInRight.iBuffer; + int iAddChanPerFrameRight = selectedDevice.AddInRight.iChanPerFrame; + int iAddInterlChRight = selectedDevice.AddInRight.iInterlCh; + + Float32* pLeftData = static_cast ( inInputData->mBuffers[iSelBufferLeft].mData ); + Float32* pRightData = static_cast ( inInputData->mBuffers[iSelBufferRight].mData ); - if ( isInput ) + // copy input data + for ( unsigned int i = 0; i < iDeviceBufferSize; i++ ) { - vecNumInBufChan.Add ( buflist->mBuffers[i].mNumberChannels ); + // copy left and right channels separately + audioBuffer[2 * i] = static_cast ( pLeftData[iSelChanPerFrameLeft * i + iSelInterlChLeft] * _MAXSHORT ); + audioBuffer[2 * i + 1] = static_cast ( pRightData[iSelChanPerFrameRight * i + iSelInterlChRight] * _MAXSHORT ); } - else + + // add an additional optional channel + if ( iAddBufferLeft >= 0 ) { - vecNumOutBufChan.Add ( buflist->mBuffers[i].mNumberChannels ); - } - } - } - free ( buflist ); + pLeftData = static_cast ( inInputData->mBuffers[iAddBufferLeft].mData ); - return result; -} + for ( unsigned int i = 0; i < iDeviceBufferSize; i++ ) + { + audioBuffer[2 * i] += static_cast ( pLeftData[iAddChanPerFrameLeft * i + iAddInterlChLeft] * _MAXSHORT ); + } + } -QString CSound::LoadAndInitializeDriver ( QString strDriverName, bool ) -{ - // secure lNumDevs/strDriverNames access - QMutexLocker locker ( &Mutex ); + if ( iAddBufferRight >= 0 ) + { + pRightData = static_cast ( inInputData->mBuffers[iAddBufferRight].mData ); - // reload the driver list of available sound devices - GetAvailableInOutDevices(); + for ( unsigned int i = 0; i < iDeviceBufferSize; i++ ) + { + audioBuffer[2 * i + 1] += static_cast ( pRightData[iAddChanPerFrameRight * i + iAddInterlChRight] * _MAXSHORT ); + } + } - // find driver index from given driver name - int iDriverIdx = INVALID_INDEX; // initialize with an invalid index + // call processing callback function + processCallback ( audioBuffer ); + awaitIn = false; + } - for ( int i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ ) - { - if ( strDriverName.compare ( strDriverNames[i] ) == 0 ) + if ( !awaitIn && outOutputData && ( deviceID == selectedDevice.OutputDeviceId ) ) { - iDriverIdx = i; + int iSelBufferLeft = selectedDevice.OutLeft.iBuffer; + int iSelChanPerFrameLeft = selectedDevice.OutLeft.iChanPerFrame; + int iSelInterlChLeft = selectedDevice.OutLeft.iInterlCh; + + int iSelBufferRight = selectedDevice.OutRight.iBuffer; + int iSelChanPerFrameRight = selectedDevice.OutRight.iChanPerFrame; + int iSelInterlChRight = selectedDevice.OutRight.iInterlCh; + + Float32* pLeftData = static_cast ( outOutputData->mBuffers[iSelBufferLeft].mData ); + Float32* pRightData = static_cast ( outOutputData->mBuffers[iSelBufferRight].mData ); + + // copy output data + for ( unsigned int i = 0; i < iDeviceBufferSize; i++ ) + { + // copy left and right channels separately + pLeftData[iSelChanPerFrameLeft * i + iSelInterlChLeft] = (Float32) audioBuffer[2 * i] / _MAXSHORT; + pRightData[iSelChanPerFrameRight * i + iSelInterlChRight] = (Float32) audioBuffer[2 * i + 1] / _MAXSHORT; + } + awaitIn = true; } } - - // if the selected driver was not found, return an error message - if ( iDriverIdx == INVALID_INDEX ) + else { - return tr ( "The currently selected audio device is no longer present. Please check your audio device." ); + awaitIn = true; } - // check device capabilities if it fulfills our requirements - const QString strStat = CheckDeviceCapabilities ( iDriverIdx ); + return kAudioHardwareNoError; +} - // check if device is capable and if not the same device is used - if ( strStat.isEmpty() && ( strCurDevName.compare ( strDriverNames[iDriverIdx] ) != 0 ) ) +void CSound::onMIDI ( const MIDIPacketList* pktlist ) +{ + if ( midiInPortRef ) { - AudioObjectPropertyAddress stPropertyAddress; + MIDIPacket* midiPacket = const_cast ( pktlist->packet ); - // unregister callbacks if previous device was valid - if ( lCurDev != INVALID_INDEX ) + for ( unsigned int j = 0; j < pktlist->numPackets; j++ ) { - stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; - stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; - - // unregister callback functions for device property changes - stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + // copy packet and send it to the MIDI parser + CVector vMIDIPaketBytes ( midiPacket->length ); - AudioObjectRemovePropertyListener ( kAudioObjectSystemObject, &stPropertyAddress, deviceNotification, this ); + for ( int i = 0; i < midiPacket->length; i++ ) + { + vMIDIPaketBytes[i] = static_cast ( midiPacket->data[i] ); + } - stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + parseMIDIMessage ( vMIDIPaketBytes ); - AudioObjectRemovePropertyListener ( kAudioObjectSystemObject, &stPropertyAddress, deviceNotification, this ); + midiPacket = MIDIPacketNext ( midiPacket ); + } + } +} - stPropertyAddress.mSelector = kAudioDevicePropertyDeviceHasChanged; +void CSound::unregisterDeviceCallBacks() +{ + if ( selectedDevice.IsActive() ) + { + AudioObjectPropertyAddress stPropertyAddress; - AudioObjectRemovePropertyListener ( audioOutputDevice[lCurDev], &stPropertyAddress, deviceNotification, this ); + stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; - AudioObjectRemovePropertyListener ( audioInputDevice[lCurDev], &stPropertyAddress, deviceNotification, this ); + stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + AudioObjectRemovePropertyListener ( kAudioObjectSystemObject, &stPropertyAddress, _onDeviceNotification, this ); - stPropertyAddress.mSelector = kAudioDevicePropertyDeviceIsAlive; + stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + AudioObjectRemovePropertyListener ( kAudioObjectSystemObject, &stPropertyAddress, _onDeviceNotification, this ); - AudioObjectRemovePropertyListener ( audioOutputDevice[lCurDev], &stPropertyAddress, deviceNotification, this ); + stPropertyAddress.mSelector = kAudioDevicePropertyDeviceHasChanged; + AudioObjectRemovePropertyListener ( selectedDevice.InputDeviceId, &stPropertyAddress, _onDeviceNotification, this ); + AudioObjectRemovePropertyListener ( selectedDevice.InputDeviceId, &stPropertyAddress, _onDeviceNotification, this ); - AudioObjectRemovePropertyListener ( audioInputDevice[lCurDev], &stPropertyAddress, deviceNotification, this ); - } + stPropertyAddress.mSelector = kAudioDevicePropertyDeviceIsAlive; + AudioObjectRemovePropertyListener ( selectedDevice.OutputDeviceId, &stPropertyAddress, _onDeviceNotification, this ); + AudioObjectRemovePropertyListener ( selectedDevice.OutputDeviceId, &stPropertyAddress, _onDeviceNotification, this ); + } +} - // store ID of selected driver if initialization was successful - lCurDev = iDriverIdx; - CurrentAudioInputDeviceID = audioInputDevice[iDriverIdx]; - CurrentAudioOutputDeviceID = audioOutputDevice[iDriverIdx]; +void CSound::registerDeviceCallBacks() +{ + if ( selectedDevice.IsActive() ) + { + AudioObjectPropertyAddress stPropertyAddress; // register callbacks stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; @@ -331,655 +752,608 @@ QString CSound::LoadAndInitializeDriver ( QString strDriverName, bool ) // setup callbacks for device property changes stPropertyAddress.mSelector = kAudioDevicePropertyDeviceHasChanged; - - AudioObjectAddPropertyListener ( audioInputDevice[lCurDev], &stPropertyAddress, deviceNotification, this ); - - AudioObjectAddPropertyListener ( audioOutputDevice[lCurDev], &stPropertyAddress, deviceNotification, this ); + AudioObjectAddPropertyListener ( selectedDevice.InputDeviceId, &stPropertyAddress, _onDeviceNotification, this ); + AudioObjectAddPropertyListener ( selectedDevice.OutputDeviceId, &stPropertyAddress, _onDeviceNotification, this ); stPropertyAddress.mSelector = kAudioDevicePropertyDeviceIsAlive; - - AudioObjectAddPropertyListener ( audioInputDevice[lCurDev], &stPropertyAddress, deviceNotification, this ); - - AudioObjectAddPropertyListener ( audioOutputDevice[lCurDev], &stPropertyAddress, deviceNotification, this ); + AudioObjectAddPropertyListener ( selectedDevice.InputDeviceId, &stPropertyAddress, _onDeviceNotification, this ); + AudioObjectAddPropertyListener ( selectedDevice.OutputDeviceId, &stPropertyAddress, _onDeviceNotification, this ); stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - - AudioObjectAddPropertyListener ( kAudioObjectSystemObject, &stPropertyAddress, deviceNotification, this ); + AudioObjectAddPropertyListener ( kAudioObjectSystemObject, &stPropertyAddress, _onDeviceNotification, this ); stPropertyAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice; + AudioObjectAddPropertyListener ( kAudioObjectSystemObject, &stPropertyAddress, _onDeviceNotification, this ); + } +} - AudioObjectAddPropertyListener ( kAudioObjectSystemObject, &stPropertyAddress, deviceNotification, this ); +//============================================================================ +// Channel Info & Selection: +//============================================================================ - // the device has changed, per definition we reset the channel - // mapping to the defaults (first two available channels) - SetLeftInputChannel ( 0 ); - SetRightInputChannel ( 1 ); - SetLeftOutputChannel ( 0 ); - SetRightOutputChannel ( 1 ); +bool CSound::setSelectedDevice() +{ + bool ok = true; - // store the current name of the driver - strCurDevName = strDriverNames[iDriverIdx]; - } + unregisterDeviceCallBacks(); - return strStat; -} + selectedDevice.clear(); -QString CSound::CheckDeviceCapabilities ( const int iDriverIdx ) -{ - UInt32 iPropertySize; - AudioStreamBasicDescription CurDevStreamFormat; - Float64 inputSampleRate = 0; - Float64 outputSampleRate = 0; - const Float64 fSystemSampleRate = static_cast ( SYSTEM_SAMPLE_RATE_HZ ); - AudioObjectPropertyAddress stPropertyAddress; + // Get Channel numbers: + int iInLeft; + int iAddLeft; + getInputSelAndAddChannels ( selectedInputChannels[0], lNumInChan, lNumAddedInChan, iInLeft, iAddLeft ); - stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; - stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + int iInRight; + int iAddRight; + getInputSelAndAddChannels ( selectedInputChannels[1], lNumInChan, lNumAddedInChan, iInRight, iAddRight ); - // check input device sample rate - stPropertyAddress.mSelector = kAudioDevicePropertyNominalSampleRate; - iPropertySize = sizeof ( Float64 ); + int iOutLeft = selectedOutputChannels[0]; + int iOutRight = selectedOutputChannels[1]; + + selectedDevice.InputDeviceId = device[iCurrentDevice].InputDeviceId; + selectedDevice.OutputDeviceId = device[iCurrentDevice].OutputDeviceId; + ok &= ( selectedDevice.InputDeviceId != 0 ) && ( selectedDevice.OutputDeviceId != 0 ); - if ( AudioObjectGetPropertyData ( audioInputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, &iPropertySize, &inputSampleRate ) ) + // Get Channel Ids + if ( device[iCurrentDevice].input[iInLeft].IsValid ( false ) ) + { + selectedDevice.InLeft = device[iCurrentDevice].input[iInLeft]; + } + else { - return QString ( tr ( "The audio input device is no longer available. Please check if your input device is connected correctly." ) ); + selectedDevice.InLeft = noChannelInfo; + ok = false; } - if ( inputSampleRate != fSystemSampleRate ) + if ( device[iCurrentDevice].input[iInRight].IsValid ( false ) ) { - // try to change the sample rate - if ( AudioObjectSetPropertyData ( audioInputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, sizeof ( Float64 ), &fSystemSampleRate ) != - noErr ) + selectedDevice.InRight = device[iCurrentDevice].input[iInRight]; + } + else + { + selectedDevice.InRight = noChannelInfo; + ok = false; + } + + if ( iAddLeft >= 0 ) + { + if ( device[iCurrentDevice].input[iAddLeft].IsValid ( true ) ) { - return QString ( tr ( "The sample rate on the current input device isn't %1 Hz and is therefore incompatible. " - "Please select another device or try setting the sample rate to %1 Hz " - "manually via Audio-MIDI-Setup (in Applications->Utilities)." ) ) - .arg ( SYSTEM_SAMPLE_RATE_HZ ); + selectedDevice.AddInLeft = device[iCurrentDevice].input[iAddLeft]; + } + else + { + selectedDevice.AddInLeft = noChannelInfo; + ok = false; } } - - // check output device sample rate - iPropertySize = sizeof ( Float64 ); - - if ( AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, &iPropertySize, &outputSampleRate ) ) + else { - return QString ( tr ( "The audio output device is no longer available. Please check if your output device is connected correctly." ) ); + selectedDevice.AddInLeft = noChannelInfo; } - if ( outputSampleRate != fSystemSampleRate ) + if ( iAddRight >= 0 ) { - // try to change the sample rate - if ( AudioObjectSetPropertyData ( audioOutputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, sizeof ( Float64 ), &fSystemSampleRate ) != - noErr ) + if ( device[iCurrentDevice].input[iAddRight].IsValid ( true ) ) + { + selectedDevice.AddInRight = device[iCurrentDevice].input[iAddRight]; + } + else { - return QString ( tr ( "The sample rate on the current output device isn't %1 Hz and is therefore incompatible. " - "Please select another device or try setting the sample rate to %1 Hz " - "manually via Audio-MIDI-Setup (in Applications->Utilities)." ) ) - .arg ( SYSTEM_SAMPLE_RATE_HZ ); + selectedDevice.AddInRight = noChannelInfo; + ok = false; } } - - // get the stream ID of the input device (at least one stream must always exist) - iPropertySize = 0; - stPropertyAddress.mSelector = kAudioDevicePropertyStreams; - stPropertyAddress.mScope = kAudioObjectPropertyScopeInput; - - AudioObjectGetPropertyDataSize ( audioInputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, &iPropertySize ); - - CVector vInputStreamIDList ( iPropertySize ); - - AudioObjectGetPropertyData ( audioInputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, &iPropertySize, &vInputStreamIDList[0] ); - - const AudioStreamID inputStreamID = vInputStreamIDList[0]; - - // get the stream ID of the output device (at least one stream must always exist) - iPropertySize = 0; - stPropertyAddress.mSelector = kAudioDevicePropertyStreams; - stPropertyAddress.mScope = kAudioObjectPropertyScopeOutput; - - AudioObjectGetPropertyDataSize ( audioOutputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, &iPropertySize ); - - CVector vOutputStreamIDList ( iPropertySize ); - - AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, &iPropertySize, &vOutputStreamIDList[0] ); - - const AudioStreamID outputStreamID = vOutputStreamIDList[0]; - - // According to the AudioHardware documentation: "If the format is a linear PCM - // format, the data will always be presented as 32 bit, native endian floating - // point. All conversions to and from the true physical format of the hardware - // is handled by the devices driver.". - // check the input - iPropertySize = sizeof ( AudioStreamBasicDescription ); - stPropertyAddress.mSelector = kAudioStreamPropertyVirtualFormat; - stPropertyAddress.mScope = kAudioObjectPropertyScopeGlobal; - - AudioObjectGetPropertyData ( inputStreamID, &stPropertyAddress, 0, NULL, &iPropertySize, &CurDevStreamFormat ); - - if ( ( CurDevStreamFormat.mFormatID != kAudioFormatLinearPCM ) || ( CurDevStreamFormat.mFramesPerPacket != 1 ) || - ( CurDevStreamFormat.mBitsPerChannel != 32 ) || ( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsFloat ) ) || - ( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsPacked ) ) ) + else { - return QString ( tr ( "The stream format on the current input device isn't " - "compatible with this software. Please select another device." ) ); + selectedDevice.AddInRight = noChannelInfo; } - // check the output - AudioObjectGetPropertyData ( outputStreamID, &stPropertyAddress, 0, NULL, &iPropertySize, &CurDevStreamFormat ); - - if ( ( CurDevStreamFormat.mFormatID != kAudioFormatLinearPCM ) || ( CurDevStreamFormat.mFramesPerPacket != 1 ) || - ( CurDevStreamFormat.mBitsPerChannel != 32 ) || ( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsFloat ) ) || - ( !( CurDevStreamFormat.mFormatFlags & kAudioFormatFlagIsPacked ) ) ) + if ( device[iCurrentDevice].output[iOutLeft].IsValid ( false ) ) { - return QString ( tr ( "The stream format on the current output device isn't " - "compatible with %1. Please select another device." ) ) - .arg ( APP_NAME ); + selectedDevice.OutLeft = device[iCurrentDevice].output[iOutLeft]; + } + else + { + selectedDevice.OutLeft = noChannelInfo; + ok = false; } - // store the input and out number of channels for this device - iNumInChan = CountChannels ( audioInputDevice[iDriverIdx], true ); - iNumOutChan = CountChannels ( audioOutputDevice[iDriverIdx], false ); - - // clip the number of input/output channels to our allowed maximum - if ( iNumInChan > MAX_NUM_IN_OUT_CHANNELS ) + if ( device[iCurrentDevice].output[iOutRight].IsValid ( false ) ) { - iNumInChan = MAX_NUM_IN_OUT_CHANNELS; + selectedDevice.OutRight = device[iCurrentDevice].output[iOutRight]; } - if ( iNumOutChan > MAX_NUM_IN_OUT_CHANNELS ) + else { - iNumOutChan = MAX_NUM_IN_OUT_CHANNELS; + selectedDevice.OutRight = noChannelInfo; + ok = false; } - // get the channel names of the input device - for ( int iCurInCH = 0; iCurInCH < iNumInChan; iCurInCH++ ) + if ( ok ) { - CFStringRef sPropertyStringValue = NULL; - - stPropertyAddress.mSelector = kAudioObjectPropertyElementName; - stPropertyAddress.mElement = iCurInCH + 1; - stPropertyAddress.mScope = kAudioObjectPropertyScopeInput; - iPropertySize = sizeof ( CFStringRef ); + selectedDevice.Activated = true; + registerDeviceCallBacks(); + } - AudioObjectGetPropertyData ( audioInputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, &iPropertySize, &sPropertyStringValue ); + return ok; +} - // convert string - const bool bConvOK = ConvertCFStringToQString ( sPropertyStringValue, sChannelNamesInput[iCurInCH] ); +void CSound::setBaseChannelInfo ( cDeviceInfo& deviceSelection ) +{ + const int NumInputChannels = deviceSelection.input.Size(); + const int NumOutputChannels = deviceSelection.output.Size(); - // add the "[n]:" at the beginning as is in the Audio-Midi-Setup - if ( !bConvOK || ( iPropertySize == 0 ) ) - { - // use a default name in case there was an error or the name is empty - sChannelNamesInput[iCurInCH] = QString ( "%1: Channel %1" ).arg ( iCurInCH + 1 ); - } - else - { - sChannelNamesInput[iCurInCH].prepend ( QString ( "%1: " ).arg ( iCurInCH + 1 ) ); - } + strInputChannelNames.clear(); + for ( int i = 0; i < NumInputChannels; i++ ) + { + strInputChannelNames.append ( deviceSelection.input[i].strName ); } - // get the channel names of the output device - for ( int iCurOutCH = 0; iCurOutCH < iNumOutChan; iCurOutCH++ ) + strOutputChannelNames.clear(); + for ( int i = 0; i < NumOutputChannels; i++ ) { - CFStringRef sPropertyStringValue = NULL; + strOutputChannelNames.append ( deviceSelection.output[i].strName ); + } - stPropertyAddress.mSelector = kAudioObjectPropertyElementName; - stPropertyAddress.mElement = iCurOutCH + 1; - stPropertyAddress.mScope = kAudioObjectPropertyScopeOutput; - iPropertySize = sizeof ( CFStringRef ); + lNumInChan = deviceSelection.lNumInChan; + lNumAddedInChan = deviceSelection.lNumAddedInChan; + lNumOutChan = deviceSelection.lNumOutChan; +} - AudioObjectGetPropertyData ( audioOutputDevice[iDriverIdx], &stPropertyAddress, 0, NULL, &iPropertySize, &sPropertyStringValue ); +bool CSound::getDeviceChannelInfo ( cDeviceInfo& deviceInfo, bool bInput ) +{ + CSound::cChannelInfo channelInfo; - // convert string - const bool bConvOK = ConvertCFStringToQString ( sPropertyStringValue, sChannelNamesOutput[iCurOutCH] ); + AudioDeviceID DeviceId = bInput ? deviceInfo.InputDeviceId : deviceInfo.OutputDeviceId; + CVector& vChannelInfo = bInput ? deviceInfo.input : deviceInfo.output; - // add the "[n]:" at the beginning as is in the Audio-Midi-Setup - if ( !bConvOK || ( iPropertySize == 0 ) ) - { - // use a default name in case there was an error or the name is empty - sChannelNamesOutput[iCurOutCH] = QString ( "%1: Channel %1" ).arg ( iCurOutCH + 1 ); - } - else - { - sChannelNamesOutput[iCurOutCH].prepend ( QString ( "%1: " ).arg ( iCurOutCH + 1 ) ); - } - } + vChannelInfo.clear(); - // special case with 4 input channels: support adding channels - if ( iNumInChan == 4 ) + channelInfo.isInput = bInput; + channelInfo.DeviceId = DeviceId; + // Get Primary channels: { - // add four mixed channels (i.e. 4 normal, 4 mixed channels) - iNumInChanPlusAddChan = 8; + CVector vBufferList; - for ( int iCh = 0; iCh < iNumInChanPlusAddChan; iCh++ ) + if ( ahGetDeviceBufferList ( DeviceId, bInput, vBufferList ) ) { - int iSelCH, iSelAddCH; - - GetSelCHAndAddCH ( iCh, iNumInChan, iSelCH, iSelAddCH ); + int numBuffers = vBufferList.Size(); + int numChannels; - if ( iSelAddCH >= 0 ) + for ( int i = 0; i < numBuffers; i++ ) { - // for mixed channels, show both audio channel names to be mixed - sChannelNamesInput[iCh] = sChannelNamesInput[iSelCH] + " + " + sChannelNamesInput[iSelAddCH]; + channelInfo.iBuffer = i; + channelInfo.iChanPerFrame = numChannels = vBufferList[i].mNumberChannels; + + for ( int c = 0; c < numChannels; c++ ) + { + channelInfo.iInterlCh = c; + + channelInfo.iListIndex = vChannelInfo.Size(); + ahGetDeviceChannelName ( DeviceId, bInput, channelInfo.iListIndex, channelInfo.strName ); + vChannelInfo.Add ( channelInfo ); + } } } } + + if ( bInput ) + { + long numInChan = deviceInfo.lNumInChan = vChannelInfo.Size(); + long ChannelsToAdd = getNumInputChannelsToAdd ( numInChan ); + + int iSelChan, iAddChan; + + for ( int i = 0; i < ChannelsToAdd; i++ ) + { + getInputSelAndAddChannels ( vChannelInfo.Size(), numInChan, ChannelsToAdd, iSelChan, iAddChan ); + channelInfo = vChannelInfo[iAddChan]; + channelInfo.iListIndex = vChannelInfo.Size(); + channelInfo.strName = vChannelInfo[iSelChan].strName + " + " + vChannelInfo[iAddChan].strName; + vChannelInfo.Add ( channelInfo ); + } + + deviceInfo.lNumAddedInChan = ChannelsToAdd; + } else { - // regular case: no mixing input channels used - iNumInChanPlusAddChan = iNumInChan; + deviceInfo.lNumOutChan = vChannelInfo.Size(); } - // everything is ok, return empty string for "no error" case - return ""; + return ( vChannelInfo.Size() > 0 ); } -void CSound::UpdateChSelection() +bool StreamformatOk ( AudioStreamBasicDescription& streamDescription ) { - // calculate the selected input/output buffer and the selected interleaved - // channel index in the buffer, note that each buffer can have a different - // number of interleaved channels - int iChCnt; - int iSelCHLeft, iSelAddCHLeft; - int iSelCHRight, iSelAddCHRight; - - // initialize all buffer indexes with an invalid value - iSelInBufferLeft = INVALID_INDEX; - iSelInBufferRight = INVALID_INDEX; - iSelAddInBufferLeft = INVALID_INDEX; // if no additional channel used, this will stay on the invalid value - iSelAddInBufferRight = INVALID_INDEX; // if no additional channel used, this will stay on the invalid value - iSelOutBufferLeft = INVALID_INDEX; - iSelOutBufferRight = INVALID_INDEX; - - // input - GetSelCHAndAddCH ( iSelInputLeftChannel, iNumInChan, iSelCHLeft, iSelAddCHLeft ); - GetSelCHAndAddCH ( iSelInputRightChannel, iNumInChan, iSelCHRight, iSelAddCHRight ); + return ( ( streamDescription.mFormatID == kAudioFormatLinearPCM ) && ( streamDescription.mFramesPerPacket == 1 ) && + ( streamDescription.mBitsPerChannel == 32 ) && ( streamDescription.mFormatFlags & kAudioFormatFlagIsFloat ) && + ( streamDescription.mFormatFlags & kAudioFormatFlagIsPacked ) ); +} - iChCnt = 0; +bool CSound::CheckDeviceCapabilities ( cDeviceInfo& deviceSelection ) +{ + bool ok = true; + bool inputDeviceOk = true; + bool outputDeviceOk = true; + const Float64 fSystemSampleRate = static_cast ( SYSTEM_SAMPLE_RATE_HZ ); + Float64 inputSampleRate = 0; + Float64 outputSampleRate = 0; + AudioStreamBasicDescription CurDevStreamFormat; + CVector vInputStreamIDList; + CVector vOutputStreamIDList; - for ( int iBuf = 0; iBuf < vecNumInBufChan.Size(); iBuf++ ) + if ( !ahGetDeviceSampleRate ( deviceSelection.InputDeviceId, inputSampleRate ) ) { - iChCnt += vecNumInBufChan[iBuf]; + strErrorList.append ( + tr ( "The audio %1 device is no longer available. Please check if your %1 device is connected correctly." ).arg ( "input" ) ); + ok = inputDeviceOk = false; + } - if ( ( iSelInBufferLeft < 0 ) && ( iChCnt > iSelCHLeft ) ) + if ( ( inputDeviceOk ) && ( inputSampleRate != fSystemSampleRate ) ) + { + // try to change the sample rate + if ( !ahSetDeviceSampleRate ( deviceSelection.InputDeviceId, fSystemSampleRate ) ) { - iSelInBufferLeft = iBuf; - iSelInInterlChLeft = iSelCHLeft - iChCnt + vecNumInBufChan[iBuf]; + strErrorList.append ( tr ( "The sample rate on the current %1 device isn't %2 Hz and is therefore incompatible. " ) + .arg ( "input" ) + .arg ( SYSTEM_SAMPLE_RATE_HZ ) + + tr ( "Try setting the sample rate to % 1 Hz manually via Audio-MIDI-Setup (in Applications->Utilities)." ) + .arg ( SYSTEM_SAMPLE_RATE_HZ ) ); + ok = false; } + } - if ( ( iSelInBufferRight < 0 ) && ( iChCnt > iSelCHRight ) ) - { - iSelInBufferRight = iBuf; - iSelInInterlChRight = iSelCHRight - iChCnt + vecNumInBufChan[iBuf]; - } + if ( !ahGetDeviceSampleRate ( deviceSelection.OutputDeviceId, outputSampleRate ) ) + { + strErrorList.append ( + tr ( "The audio %1 device is no longer available. Please check if your %1 device is connected correctly." ).arg ( "output" ) ); + ok = outputDeviceOk = false; + } - if ( ( iSelAddCHLeft >= 0 ) && ( iSelAddInBufferLeft < 0 ) && ( iChCnt > iSelAddCHLeft ) ) + if ( outputDeviceOk && ( outputSampleRate != fSystemSampleRate ) ) + { + // try to change the sample rate + if ( !ahSetDeviceSampleRate ( deviceSelection.OutputDeviceId, fSystemSampleRate ) ) { - iSelAddInBufferLeft = iBuf; - iSelAddInInterlChLeft = iSelAddCHLeft - iChCnt + vecNumInBufChan[iBuf]; + strErrorList.append ( tr ( "The sample rate on the current %1 device isn't %2 Hz and is therefore incompatible. " ) + .arg ( "output" ) + .arg ( SYSTEM_SAMPLE_RATE_HZ ) + + tr ( "Try setting the sample rate to %1 Hz manually via Audio-MIDI-Setup (in Applications->Utilities)." ) + .arg ( SYSTEM_SAMPLE_RATE_HZ ) ); + ok = false; } + } - if ( ( iSelAddCHRight >= 0 ) && ( iSelAddInBufferRight < 0 ) && ( iChCnt > iSelAddCHRight ) ) + if ( inputDeviceOk ) + { + if ( !ahGetDeviceStreamIdList ( deviceSelection.InputDeviceId, biINPUT, vInputStreamIDList ) ) { - iSelAddInBufferRight = iBuf; - iSelAddInInterlChRight = iSelAddCHRight - iChCnt + vecNumInBufChan[iBuf]; + strErrorList.append ( tr ( "The input device doesn't have any valid inputs." ) ); + ok = inputDeviceOk = false; } } - // output - GetSelCHAndAddCH ( iSelOutputLeftChannel, iNumOutChan, iSelCHLeft, iSelAddCHLeft ); - GetSelCHAndAddCH ( iSelOutputRightChannel, iNumOutChan, iSelCHRight, iSelAddCHRight ); - - iChCnt = 0; - - for ( int iBuf = 0; iBuf < vecNumOutBufChan.Size(); iBuf++ ) + if ( outputDeviceOk ) { - iChCnt += vecNumOutBufChan[iBuf]; - - if ( ( iSelOutBufferLeft < 0 ) && ( iChCnt > iSelCHLeft ) ) + // get the stream ID of the output device (at least one stream must always exist) + if ( !ahGetDeviceStreamIdList ( deviceSelection.OutputDeviceId, biOUTPUT, vOutputStreamIDList ) ) { - iSelOutBufferLeft = iBuf; - iSelOutInterlChLeft = iSelCHLeft - iChCnt + vecNumOutBufChan[iBuf]; + strErrorList.append ( tr ( "The output device doesn't have any valid outputs." ) ); + ok = outputDeviceOk = false; } + } - if ( ( iSelOutBufferRight < 0 ) && ( iChCnt > iSelCHRight ) ) + if ( ok ) + { + ok = setBufferSize ( deviceSelection.InputDeviceId, deviceSelection.OutputDeviceId, iProtocolBufferSize, iDeviceBufferSize ); + if ( ok ) { - iSelOutBufferRight = iBuf; - iSelOutInterlChRight = iSelCHRight - iChCnt + vecNumOutBufChan[iBuf]; + // According to the AudioHardware documentation: "If the format is a linear PCM + // format, the data will always be presented as 32 bit, native endian floating + // point. All conversions to and from the true physical format of the hardware + // is handled by the devices driver.". + // check the input + if ( !ahGetStreamFormat ( vInputStreamIDList[0], CurDevStreamFormat ) || // pgScorpio: this one fails ! + !StreamformatOk ( CurDevStreamFormat ) ) + { + strErrorList.append ( tr ( "The stream format on the current input device isn't compatible with %1." ).arg ( APP_NAME ) ); + ok = inputDeviceOk = false; + } + + if ( !ahGetStreamFormat ( vOutputStreamIDList[0], CurDevStreamFormat ) || !StreamformatOk ( CurDevStreamFormat ) ) + { + strErrorList.append ( tr ( "The stream format on the current output device isn't compatible with %1." ).arg ( APP_NAME ) ); + ok = outputDeviceOk = false; + } } } -} -void CSound::SetLeftInputChannel ( const int iNewChan ) -{ - // apply parameter after input parameter check - if ( ( iNewChan >= 0 ) && ( iNewChan < iNumInChanPlusAddChan ) ) - { - iSelInputLeftChannel = iNewChan; - UpdateChSelection(); - } + return ok; } -void CSound::SetRightInputChannel ( const int iNewChan ) -{ - // apply parameter after input parameter check - if ( ( iNewChan >= 0 ) && ( iNewChan < iNumInChanPlusAddChan ) ) - { - iSelInputRightChannel = iNewChan; - UpdateChSelection(); - } -} +//============================================================================ +// CSoundBase virtuals: +//============================================================================ -void CSound::SetLeftOutputChannel ( const int iNewChan ) +long CSound::createDeviceList ( bool bRescan ) // Fills strDeviceList. Returns number of devices found { - // apply parameter after input parameter check - if ( ( iNewChan >= 0 ) && ( iNewChan < iNumOutChan ) ) + if ( bRescan || ( lNumDevices == 0 ) || ( strDeviceNames.size() != lNumDevices ) || ( device.Size() != lNumDevices ) ) { - iSelOutputLeftChannel = iNewChan; - UpdateChSelection(); - } -} + CVector vAudioDevices; + cDeviceInfo newDevice; -void CSound::SetRightOutputChannel ( const int iNewChan ) -{ - // apply parameter after input parameter check - if ( ( iNewChan >= 0 ) && ( iNewChan < iNumOutChan ) ) - { - iSelOutputRightChannel = iNewChan; - UpdateChSelection(); - } -} + clearDeviceInfo(); // We will have no more device to select... -void CSound::Start() -{ - // register the callback function for input and output - AudioDeviceCreateIOProcID ( audioInputDevice[lCurDev], callbackIO, this, &audioInputProcID ); + lNumDevices = 0; + strDeviceNames.clear(); + device.clear(); + + if ( !ahGetAudioDeviceList ( vAudioDevices ) ) + { + strErrorList.append ( tr ( "No audiodevices found." ) ); + return 0; + } - AudioDeviceCreateIOProcID ( audioOutputDevice[lCurDev], callbackIO, this, &audioOutputProcID ); + const UInt32 iDeviceCount = vAudioDevices.Size(); - // start the audio stream - AudioDeviceStart ( audioInputDevice[lCurDev], audioInputProcID ); - AudioDeviceStart ( audioOutputDevice[lCurDev], audioOutputProcID ); + // always add system default devices for input and output as first entry + if ( !( ahGetDefaultDevice ( biINPUT, newDevice.InputDeviceId ) && ahGetDefaultDevice ( biOUTPUT, newDevice.OutputDeviceId ) ) ) + { + strErrorList.append ( tr ( "No sound card is available in your system." ) ); + return 0; + } + getDeviceChannelInfo ( newDevice, biINPUT ); + getDeviceChannelInfo ( newDevice, biOUTPUT ); + device.Add ( newDevice ); + strDeviceNames.append ( tr ( "System Default In/Out Devices" ) ); + + QString strDeviceName_i; + QString strDeviceName_j; + bool bIsInput_i; + bool bIsOutput_i; + bool bIsInput_j; + bool bIsOutput_j; + + // First get in + out devices... + for ( UInt32 i = 0; i < iDeviceCount; i++ ) + { + getAudioDeviceInfos ( i, vAudioDevices[i], strDeviceName_i, bIsInput_i, bIsOutput_i ); + if ( bIsInput_i && bIsOutput_i ) + { + newDevice.InputDeviceId = vAudioDevices[i]; + newDevice.OutputDeviceId = vAudioDevices[i]; + getDeviceChannelInfo ( newDevice, biINPUT ); + getDeviceChannelInfo ( newDevice, biOUTPUT ); + device.Add ( newDevice ); + strDeviceNames.append ( strDeviceName_i ); + } + } - // call base class - CSoundBase::Start(); -} + // Then find all other in / out combinations... + for ( UInt32 i = 0; i < iDeviceCount; i++ ) + { + getAudioDeviceInfos ( i, vAudioDevices[i], strDeviceName_i, bIsInput_i, bIsOutput_i ); -void CSound::Stop() -{ - // stop the audio stream - AudioDeviceStop ( audioInputDevice[lCurDev], audioInputProcID ); - AudioDeviceStop ( audioOutputDevice[lCurDev], audioOutputProcID ); + if ( bIsInput_i ) + { + newDevice.InputDeviceId = vAudioDevices[i]; + getDeviceChannelInfo ( newDevice, biINPUT ); + + for ( UInt32 j = 0; j < iDeviceCount; j++ ) + { + // Skip primary in + out devices, we already got those... + if ( i != j ) + { + getAudioDeviceInfos ( j, vAudioDevices[j], strDeviceName_j, bIsInput_j, bIsOutput_j ); + + if ( bIsOutput_j ) + { + newDevice.OutputDeviceId = vAudioDevices[j]; + getDeviceChannelInfo ( newDevice, biOUTPUT ); + device.Add ( newDevice ); + strDeviceNames.append ( strDeviceName_i + " / " + strDeviceName_j ); + } + } + } + } + } - // unregister the callback function for input and output - AudioDeviceDestroyIOProcID ( audioInputDevice[lCurDev], audioInputProcID ); - AudioDeviceDestroyIOProcID ( audioOutputDevice[lCurDev], audioOutputProcID ); + lNumDevices = strDeviceNames.length(); + } - // call base class - CSoundBase::Stop(); + return lNumDevices; } -int CSound::Init ( const int iNewPrefMonoBufferSize ) +bool CSound::getCurrentLatency() { - UInt32 iActualMonoBufferSize; - - // Error message string: in case buffer sizes on input and output cannot be - // set to the same value - const QString strErrBufSize = tr ( "The buffer sizes of the current " - "input and output audio device can't be set to a common value. Please " - "select different input/output devices in your system settings." ); + if ( !selectedDevice.IsActive() ) + { + return false; + } - // try to set input buffer size - iActualMonoBufferSize = SetBufferSize ( audioInputDevice[lCurDev], true, iNewPrefMonoBufferSize ); + bool ok = true; + CVector streamIdList; + UInt32 iInputLatencyFrames = 0; + UInt32 iOutputLatencyFrames = 0; - if ( iActualMonoBufferSize != static_cast ( iNewPrefMonoBufferSize ) ) + if ( !ahGetDeviceLatency ( selectedDevice.InputDeviceId, biINPUT, iInputLatencyFrames ) ) { - // try to set the input buffer size to the output so that we - // have a matching pair - if ( SetBufferSize ( audioOutputDevice[lCurDev], false, iActualMonoBufferSize ) != iActualMonoBufferSize ) - { - throw CGenErr ( strErrBufSize ); - } + ok = false; + iInputLatencyFrames = iDeviceBufferSize; } - else + + if ( !ahGetDeviceLatency ( selectedDevice.OutputDeviceId, biOUTPUT, iOutputLatencyFrames ) ) { - // try to set output buffer size - if ( SetBufferSize ( audioOutputDevice[lCurDev], false, iNewPrefMonoBufferSize ) != static_cast ( iNewPrefMonoBufferSize ) ) - { - throw CGenErr ( strErrBufSize ); - } + ok = false; + iOutputLatencyFrames = iDeviceBufferSize; } - // store buffer size - iCoreAudioBufferSizeMono = iActualMonoBufferSize; - - // init base class - CSoundBase::Init ( iCoreAudioBufferSizeMono ); - - // set internal buffer size value and calculate stereo buffer size - iCoreAudioBufferSizeStereo = 2 * iCoreAudioBufferSizeMono; + iInputLatencyFrames += iDeviceBufferSize; + iOutputLatencyFrames += iDeviceBufferSize; - // create memory for intermediate audio buffer - vecsTmpAudioSndCrdStereo.Init ( iCoreAudioBufferSizeStereo ); + fInOutLatencyMs = ( ( static_cast ( iInputLatencyFrames ) + iOutputLatencyFrames ) * 1000 ) / SYSTEM_SAMPLE_RATE_HZ; - return iCoreAudioBufferSizeMono; + return ok; } -UInt32 CSound::SetBufferSize ( AudioDeviceID& audioDeviceID, const bool bIsInput, UInt32 iPrefBufferSize ) +bool CSound::checkDeviceChange ( CSoundBase::tDeviceChangeCheck mode, int iDriverIndex ) // Open device sequence handling.... { - AudioObjectPropertyAddress stPropertyAddress; - stPropertyAddress.mSelector = kAudioDevicePropertyBufferFrameSize; - - if ( bIsInput ) + if ( ( iDriverIndex < 0 ) || ( iDriverIndex >= lNumDevices ) ) { - stPropertyAddress.mScope = kAudioDevicePropertyScopeInput; + return false; } - else + + switch ( mode ) { - stPropertyAddress.mScope = kAudioDevicePropertyScopeOutput; - } + case CSoundBase::tDeviceChangeCheck::CheckOpen: + return device[iDriverIndex].IsValid(); + + case CSoundBase::tDeviceChangeCheck::CheckCapabilities: + return CheckDeviceCapabilities ( device[iDriverIndex] ); - stPropertyAddress.mElement = kAudioObjectPropertyElementMaster; + case CSoundBase::tDeviceChangeCheck::Activate: + clearDeviceInfo(); + setBaseChannelInfo ( device[iDriverIndex] ); + resetChannelMapping(); - // first set the value - UInt32 iSizeBufValue = sizeof ( UInt32 ); + fInOutLatencyMs = 0.0; + iCurrentDevice = iDriverIndex; - AudioObjectSetPropertyData ( audioDeviceID, &stPropertyAddress, 0, NULL, iSizeBufValue, &iPrefBufferSize ); + // TODO: ChannelMapping from inifile! - // read back which value is actually used - UInt32 iActualMonoBufferSize = 0; + return setSelectedDevice(); - AudioObjectGetPropertyData ( audioDeviceID, &stPropertyAddress, 0, NULL, &iSizeBufValue, &iActualMonoBufferSize ); + case CSoundBase::tDeviceChangeCheck::Abort: + return true; - return iActualMonoBufferSize; + default: + return false; + } } -OSStatus CSound::deviceNotification ( AudioDeviceID, UInt32, const AudioObjectPropertyAddress* inAddresses, void* inRefCon ) +unsigned int CSound::getDeviceBufferSize ( unsigned int iDesiredBufferSize ) { - CSound* pSound = static_cast ( inRefCon ); + CSoundStopper sound ( *this ); - if ( ( inAddresses->mSelector == kAudioDevicePropertyDeviceHasChanged ) || ( inAddresses->mSelector == kAudioDevicePropertyDeviceIsAlive ) || - ( inAddresses->mSelector == kAudioHardwarePropertyDefaultOutputDevice ) || - ( inAddresses->mSelector == kAudioHardwarePropertyDefaultInputDevice ) ) + if ( selectedDevice.IsActive() ) { - if ( ( inAddresses->mSelector == kAudioDevicePropertyDeviceHasChanged ) || ( inAddresses->mSelector == kAudioDevicePropertyDeviceIsAlive ) ) - { - // if any property of the device has changed, do a full reload - pSound->EmitReinitRequestSignal ( RS_RELOAD_RESTART_AND_INIT ); - } - else + unsigned int iActualBufferSize; + + if ( setBufferSize ( selectedDevice.InputDeviceId, selectedDevice.OutputDeviceId, iDesiredBufferSize, iActualBufferSize ) ) { - // for any other change in audio devices, just initiate a restart which - // triggers an update of the sound device selection combo box - pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART ); + return iActualBufferSize; } } - return noErr; + return 0; } -OSStatus CSound::callbackIO ( AudioDeviceID inDevice, - const AudioTimeStamp*, - const AudioBufferList* inInputData, - const AudioTimeStamp*, - AudioBufferList* outOutputData, - const AudioTimeStamp*, - void* inRefCon ) +void CSound::closeCurrentDevice() // Closes the current driver and Clears Device Info { - CSound* pSound = static_cast ( inRefCon ); + // Can't close current device while running + Stop(); + // Clear CSoundBase device info + clearDeviceInfo(); + // Clear my selected device + selectedDevice.clear(); +} - // both, the input and output device use the same callback function - QMutexLocker locker ( &pSound->MutexAudioProcessCallback ); - - const int iCoreAudioBufferSizeMono = pSound->iCoreAudioBufferSizeMono; - const int iSelInBufferLeft = pSound->iSelInBufferLeft; - const int iSelInBufferRight = pSound->iSelInBufferRight; - const int iSelInInterlChLeft = pSound->iSelInInterlChLeft; - const int iSelInInterlChRight = pSound->iSelInInterlChRight; - const int iSelAddInBufferLeft = pSound->iSelAddInBufferLeft; - const int iSelAddInBufferRight = pSound->iSelAddInBufferRight; - const int iSelAddInInterlChLeft = pSound->iSelAddInInterlChLeft; - const int iSelAddInInterlChRight = pSound->iSelAddInInterlChRight; - const int iSelOutBufferLeft = pSound->iSelOutBufferLeft; - const int iSelOutBufferRight = pSound->iSelOutBufferRight; - const int iSelOutInterlChLeft = pSound->iSelOutInterlChLeft; - const int iSelOutInterlChRight = pSound->iSelOutInterlChRight; - const CVector& vecNumInBufChan = pSound->vecNumInBufChan; - const CVector& vecNumOutBufChan = pSound->vecNumOutBufChan; - - if ( ( inDevice == pSound->CurrentAudioInputDeviceID ) && inInputData && pSound->bRun ) - { - // check sizes (note that float32 has four bytes) - if ( ( iSelInBufferLeft >= 0 ) && ( iSelInBufferLeft < static_cast ( inInputData->mNumberBuffers ) ) && ( iSelInBufferRight >= 0 ) && - ( iSelInBufferRight < static_cast ( inInputData->mNumberBuffers ) ) && - ( iSelAddInBufferLeft < static_cast ( inInputData->mNumberBuffers ) ) && - ( iSelAddInBufferRight < static_cast ( inInputData->mNumberBuffers ) ) && - ( inInputData->mBuffers[iSelInBufferLeft].mDataByteSize == - static_cast ( vecNumInBufChan[iSelInBufferLeft] * iCoreAudioBufferSizeMono * 4 ) ) && - ( inInputData->mBuffers[iSelInBufferRight].mDataByteSize == - static_cast ( vecNumInBufChan[iSelInBufferRight] * iCoreAudioBufferSizeMono * 4 ) ) ) - { - Float32* pLeftData = static_cast ( inInputData->mBuffers[iSelInBufferLeft].mData ); - Float32* pRightData = static_cast ( inInputData->mBuffers[iSelInBufferRight].mData ); - int iNumChanPerFrameLeft = vecNumInBufChan[iSelInBufferLeft]; - int iNumChanPerFrameRight = vecNumInBufChan[iSelInBufferRight]; +//============================================================================ +// Start/Stop: +//============================================================================ - // copy input data - for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ ) - { - // copy left and right channels separately - pSound->vecsTmpAudioSndCrdStereo[2 * i] = (short) ( pLeftData[iNumChanPerFrameLeft * i + iSelInInterlChLeft] * _MAXSHORT ); - pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = (short) ( pRightData[iNumChanPerFrameRight * i + iSelInInterlChRight] * _MAXSHORT ); - } +bool CSound::prepareAudio ( bool start ) +{ + if ( !selectedDevice.IsActive() || ( iDeviceBufferSize == 0 ) ) + { + return false; + } - // add an additional optional channel - if ( iSelAddInBufferLeft >= 0 ) - { - pLeftData = static_cast ( inInputData->mBuffers[iSelAddInBufferLeft].mData ); - iNumChanPerFrameLeft = vecNumInBufChan[iSelAddInBufferLeft]; + // create memory for intermediate audio buffer + audioBuffer.Init ( PROT_NUM_IN_CHANNELS * iDeviceBufferSize ); - for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ ) - { - pSound->vecsTmpAudioSndCrdStereo[2 * i] = Float2Short ( pSound->vecsTmpAudioSndCrdStereo[2 * i] + - pLeftData[iNumChanPerFrameLeft * i + iSelAddInInterlChLeft] * _MAXSHORT ); - } - } + // selectedDevice.InputIdLeft = inputInfo[selectedInputChannels[0]].deviceID; //????? + // selectedDevice.OutputIdLeft = inputInfo[selectedOutputChannels[0]].deviceID; //????? - if ( iSelAddInBufferRight >= 0 ) - { - pRightData = static_cast ( inInputData->mBuffers[iSelAddInBufferRight].mData ); - iNumChanPerFrameRight = vecNumInBufChan[iSelAddInBufferRight]; + // register the callback function for input and output + bool ok = ( AudioDeviceCreateIOProcID ( selectedDevice.InputDeviceId, _onBufferSwitch, this, &audioInputProcID ) == noErr ); + if ( ok ) + { + ok = ( AudioDeviceCreateIOProcID ( selectedDevice.OutputDeviceId, _onBufferSwitch, this, &audioOutputProcID ) == noErr ); - for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ ) + if ( ok && audioInputProcID && audioOutputProcID && start ) + { + // start the audio stream + ok = ( AudioDeviceStart ( selectedDevice.InputDeviceId, audioInputProcID ) == noErr ); + if ( ok ) + { + ok = ( AudioDeviceStart ( selectedDevice.OutputDeviceId, audioOutputProcID ) == noErr ); + if ( ok ) { - pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = Float2Short ( - pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] + pRightData[iNumChanPerFrameRight * i + iSelAddInInterlChRight] * _MAXSHORT ); + getCurrentLatency(); + + return true; } + + AudioDeviceStop ( selectedDevice.InputDeviceId, audioInputProcID ); } } - else - { - // incompatible sizes, clear work buffer - pSound->vecsTmpAudioSndCrdStereo.Reset ( 0 ); - } - - // call processing callback function - pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo ); } - if ( ( inDevice == pSound->CurrentAudioOutputDeviceID ) && outOutputData && pSound->bRun ) + if ( !ok ) { - // check sizes (note that float32 has four bytes) - if ( ( iSelOutBufferLeft >= 0 ) && ( iSelOutBufferLeft < static_cast ( outOutputData->mNumberBuffers ) ) && - ( iSelOutBufferRight >= 0 ) && ( iSelOutBufferRight < static_cast ( outOutputData->mNumberBuffers ) ) && - ( outOutputData->mBuffers[iSelOutBufferLeft].mDataByteSize == - static_cast ( vecNumOutBufChan[iSelOutBufferLeft] * iCoreAudioBufferSizeMono * 4 ) ) && - ( outOutputData->mBuffers[iSelOutBufferRight].mDataByteSize == - static_cast ( vecNumOutBufChan[iSelOutBufferRight] * iCoreAudioBufferSizeMono * 4 ) ) ) + if ( audioInputProcID ) { - Float32* pLeftData = static_cast ( outOutputData->mBuffers[iSelOutBufferLeft].mData ); - Float32* pRightData = static_cast ( outOutputData->mBuffers[iSelOutBufferRight].mData ); - int iNumChanPerFrameLeft = vecNumOutBufChan[iSelOutBufferLeft]; - int iNumChanPerFrameRight = vecNumOutBufChan[iSelOutBufferRight]; - - // copy output data - for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ ) - { - // copy left and right channels separately - pLeftData[iNumChanPerFrameLeft * i + iSelOutInterlChLeft] = (Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i] / _MAXSHORT; - pRightData[iNumChanPerFrameRight * i + iSelOutInterlChRight] = (Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] / _MAXSHORT; - } + AudioDeviceDestroyIOProcID ( selectedDevice.InputDeviceId, audioInputProcID ); + audioInputProcID = NULL; + } + if ( audioOutputProcID ) + { + AudioDeviceDestroyIOProcID ( selectedDevice.OutputDeviceId, audioOutputProcID ); + audioOutputProcID = NULL; } } - return kAudioHardwareNoError; + return ok; } -void CSound::callbackMIDI ( const MIDIPacketList* pktlist, void* refCon, void* ) +bool CSound::start() { - CSound* pSound = static_cast ( refCon ); + strErrorList.clear(); - if ( pSound->midiInPortRef != static_cast ( NULL ) ) + if ( !IsStarted() ) { - MIDIPacket* midiPacket = const_cast ( pktlist->packet ); - - for ( unsigned int j = 0; j < pktlist->numPackets; j++ ) + if ( prepareAudio ( true ) ) { - // copy packet and send it to the MIDI parser - CVector vMIDIPaketBytes ( midiPacket->length ); - for ( int i = 0; i < midiPacket->length; i++ ) - { - vMIDIPaketBytes[i] = static_cast ( midiPacket->data[i] ); - } - pSound->ParseMIDIMessage ( vMIDIPaketBytes ); - - midiPacket = MIDIPacketNext ( midiPacket ); + return true; + } + else + { + // We failed!, audio is stopped ! + strErrorList.append ( htmlBold ( tr ( "Failed to start your audio device!." ) ) ); + strErrorList.append ( tr ( "Please check your device settings..." ) ); } } + + return false; } -bool CSound::ConvertCFStringToQString ( const CFStringRef stringRef, QString& sOut ) +bool CSound::stop() { - // check if the string reference is a valid pointer - if ( stringRef != NULL ) - { - // first check if the string is not empty - if ( CFStringGetLength ( stringRef ) > 0 ) - { - // convert CFString in c-string (quick hack!) and then in QString - char* sC_strPropValue = (char*) malloc ( CFStringGetLength ( stringRef ) * 3 + 1 ); + strErrorList.clear(); - if ( CFStringGetCString ( stringRef, sC_strPropValue, CFStringGetLength ( stringRef ) * 3 + 1, kCFStringEncodingUTF8 ) ) - { - sOut = sC_strPropValue; - free ( sC_strPropValue ); + if ( IsStarted() ) + { - return true; // OK - } - } + // stop the audio stream + AudioDeviceStop ( selectedDevice.InputDeviceId, audioInputProcID ); + AudioDeviceStop ( selectedDevice.OutputDeviceId, audioOutputProcID ); - // release the string reference because it is not needed anymore - CFRelease ( stringRef ); + // unregister the callback function for input and output + AudioDeviceDestroyIOProcID ( selectedDevice.InputDeviceId, audioInputProcID ); + AudioDeviceDestroyIOProcID ( selectedDevice.OutputDeviceId, audioOutputProcID ); } - return false; // not OK + return true; } diff --git a/mac/sound.h b/mac/sound.h index e549760e79..72014bd13f 100644 --- a/mac/sound.h +++ b/mac/sound.h @@ -2,7 +2,7 @@ * Copyright (c) 2004-2022 * * Author(s): - * Volker Fischer + * Volker Fischer, revised and maintained by Peter Goderie (pgScorpio) * ****************************************************************************** * @@ -24,112 +24,246 @@ #pragma once +#ifdef _WIN32 +# define NO_COMPILING +#endif + +#ifdef NO_COMPILING +// just for writing code on Windows Visual Studio (No building or debugging, just syntax checking !) +// macOS header files folder must be in additional include path ! +# define __x86_64__ +# define __BIG_ENDIAN__ +# define __WATCHOS_PROHIBITED +# define __TVOS_PROHIBITED +# define Float32 float +# define __attribute__( a ) +# include +# include +# include +# include +# include +#endif + #include #include #include #include #include + #include "soundbase.h" #include "global.h" +// Values for bool bInput/bIsInput +#define biINPUT true +#define biOUTPUT false + +// Values for bool bOutput/bIsOutput +#define boOUTPUT true +#define boINPUT false + /* Classes ********************************************************************/ class CSound : public CSoundBase { Q_OBJECT -public: - CSound ( void ( *fpNewProcessCallback ) ( CVector& psData, void* arg ), - void* arg, - const QString& strMIDISetup, - const bool, - const QString& ); - - virtual int Init ( const int iNewPrefMonoBufferSize ); - virtual void Start(); - virtual void Stop(); - - // channel selection - virtual int GetNumInputChannels() { return iNumInChanPlusAddChan; } - virtual QString GetInputChannelName ( const int iDiD ) { return sChannelNamesInput[iDiD]; } - virtual void SetLeftInputChannel ( const int iNewChan ); - virtual void SetRightInputChannel ( const int iNewChan ); - virtual int GetLeftInputChannel() { return iSelInputLeftChannel; } - virtual int GetRightInputChannel() { return iSelInputRightChannel; } - - virtual int GetNumOutputChannels() { return iNumOutChan; } - virtual QString GetOutputChannelName ( const int iDiD ) { return sChannelNamesOutput[iDiD]; } - virtual void SetLeftOutputChannel ( const int iNewChan ); - virtual void SetRightOutputChannel ( const int iNewChan ); - virtual int GetLeftOutputChannel() { return iSelOutputLeftChannel; } - virtual int GetRightOutputChannel() { return iSelOutputRightChannel; } - - // these variables/functions should be protected but cannot since we want - // to access them from the callback function - CVector vecsTmpAudioSndCrdStereo; - int iCoreAudioBufferSizeMono; - int iCoreAudioBufferSizeStereo; - AudioDeviceID CurrentAudioInputDeviceID; - AudioDeviceID CurrentAudioOutputDeviceID; - long lCurDev; - int iNumInChan; - int iNumInChanPlusAddChan; // includes additional "added" channels - int iNumOutChan; - int iSelInputLeftChannel; - int iSelInputRightChannel; - int iSelOutputLeftChannel; - int iSelOutputRightChannel; - int iSelInBufferLeft; - int iSelInBufferRight; - int iSelInInterlChLeft; - int iSelInInterlChRight; - int iSelAddInBufferLeft; - int iSelAddInBufferRight; - int iSelAddInInterlChLeft; - int iSelAddInInterlChRight; - int iSelOutBufferLeft; - int iSelOutBufferRight; - int iSelOutInterlChLeft; - int iSelOutInterlChRight; - CVector vecNumInBufChan; - CVector vecNumOutBufChan; +protected: + // Audio Channels + + class cChannelInfo + { + public: + cChannelInfo() : iListIndex ( -1 ), isInput ( false ), DeviceId ( 0 ), strName(), iBuffer ( -1 ), iChanPerFrame ( -1 ), iInterlCh ( -1 ) {} + + public: + // All values are obtained by getAvailableChannels(DeviceId, isInput, CVector& list): + // Just for validation and debugging: + int iListIndex; // index in input/output list + bool isInput; // input (true) or output (false) + AudioDeviceID DeviceId; // input/output DeviceId + + // Actually needed values: + QString strName; // Channel name to be added to strInputChannelNames/strOutputChannelNames on device selection + int iBuffer; // Buffer number on device + int iChanPerFrame; // Number of channels interlaced. + int iInterlCh; // Interlace channel number in Buffer + + bool IsValid ( bool asAdded ); // Checks if this ChannelInfo is valid as normal Input or Output or as Added Input + bool IsValidFor ( AudioDeviceID forDeviceId, + bool asInput, + bool asAdded ); // Checks if this ChannelInfo is valid as a specific Input or Output or Added Input + }; + + static cChannelInfo noChannelInfo; + + class cSelectedDevice + { + public: + cSelectedDevice() : + Activated ( false ), + InputDeviceId ( 0 ), + InLeft ( noChannelInfo ), + InRight ( noChannelInfo ), + AddInLeft ( noChannelInfo ), + AddInRight ( noChannelInfo ), + OutputDeviceId ( 0 ), + OutLeft ( noChannelInfo ), + OutRight ( noChannelInfo ) + {} + + void clear() + { + Activated = false; + InputDeviceId = 0; + InLeft = noChannelInfo; + InRight = noChannelInfo; + AddInLeft = noChannelInfo; + AddInRight = noChannelInfo; + OutputDeviceId = 0; + OutLeft = noChannelInfo; + OutRight = noChannelInfo; + } + + bool IsActive() { return Activated; } + + public: + bool Activated; // Is this cSelectedDevice set as active device ? (Set by setSelectedDevice, cleared by clear() ) + + AudioDeviceID InputDeviceId; + + cChannelInfo InLeft; + cChannelInfo InRight; + + cChannelInfo AddInLeft; + cChannelInfo AddInRight; + + AudioDeviceID OutputDeviceId; + + cChannelInfo OutLeft; + cChannelInfo OutRight; + }; + + cSelectedDevice selectedDevice; + + bool setSelectedDevice(); // sets selectedDevice according current selections in CSoundBase protected: - virtual QString LoadAndInitializeDriver ( QString strDriverName, bool ); + // Audio Devices - QString CheckDeviceCapabilities ( const int iDriverIdx ); - void UpdateChSelection(); - void GetAvailableInOutDevices(); + // Input/Output device combinations for device selection + // Filled by createDeviceList, + // Device names should be stored in strDeviceNames and lNumDevices should be set according these lists sizes. - int CountChannels ( AudioDeviceID devID, bool isInput ); + // input/output device info filled by createDeviceList + class cDeviceInfo + { + public: + cDeviceInfo() : InputDeviceId ( 0 ), lNumInChan ( 0 ), lNumAddedInChan ( 0 ), input(), OutputDeviceId ( 0 ), lNumOutChan ( 0 ), output(){}; - UInt32 SetBufferSize ( AudioDeviceID& audioDeviceID, const bool bIsInput, UInt32 iPrefBufferSize ); + bool IsValid(); - void GetAudioDeviceInfos ( const AudioDeviceID DeviceID, QString& strDeviceName, bool& bIsInput, bool& bIsOutput ); + public: + AudioDeviceID InputDeviceId; // DeviceId of the inputs device + long lNumInChan; // Should be set to input.Size() + long lNumAddedInChan; // Number of additional input channels added for mixed input channels. Set by capability check. + CVector input; // Input channel list + // + AudioDeviceID OutputDeviceId; // DeviceId of the outputs device + long lNumOutChan; // Should be set to output.Size() + CVector output; // Output channel list + }; - bool ConvertCFStringToQString ( const CFStringRef stringRef, QString& sOut ); + CVector device; + bool getDeviceChannelInfo ( cDeviceInfo& deviceInfo, bool bInput ); // Retrieves DeviceInfo.input or DeviceInfo.output + +public: + CSound ( void ( *theProcessCallback ) ( CVector& psData, void* arg ), void* theProcessCallbackArg ); + +protected: + MIDIPortRef midiInPortRef; + +protected: + OSStatus onDeviceNotification ( AudioDeviceID idDevice, UInt32 unknown, const AudioObjectPropertyAddress* inAddresses ); + + OSStatus onBufferSwitch ( AudioDeviceID deviceID, + const AudioTimeStamp* pAudioTimeStamp1, + const AudioBufferList* inInputData, + const AudioTimeStamp* pAudioTimeStamp2, + AudioBufferList* outOutputData, + const AudioTimeStamp* pAudioTimeStamp3 ); + + void onMIDI ( const MIDIPacketList* pktlist ); + +protected: // callbacks - static OSStatus deviceNotification ( AudioDeviceID, UInt32, const AudioObjectPropertyAddress* inAddresses, void* inRefCon ); - static OSStatus callbackIO ( AudioDeviceID inDevice, - const AudioTimeStamp*, - const AudioBufferList* inInputData, - const AudioTimeStamp*, - AudioBufferList* outOutputData, - const AudioTimeStamp*, - void* inRefCon ); + static OSStatus _onDeviceNotification ( AudioDeviceID deviceID, + UInt32 unknown, + const AudioObjectPropertyAddress* inAddresses, + void* /* inRefCon */ ) + { + return pSound->onDeviceNotification ( deviceID, unknown, inAddresses ); + } - static void callbackMIDI ( const MIDIPacketList* pktlist, void* refCon, void* ); + static OSStatus _onBufferSwitch ( AudioDeviceID inDevice, + const AudioTimeStamp* pAudioTimeStamp1, + const AudioBufferList* inInputData, + const AudioTimeStamp* pAudioTimeStamp2, + AudioBufferList* outOutputData, + const AudioTimeStamp* pAudioTimeStamp3, + void* /* inRefCon */ ) + { + return pSound->onBufferSwitch ( inDevice, pAudioTimeStamp1, inInputData, pAudioTimeStamp2, outOutputData, pAudioTimeStamp3 ); + } - AudioDeviceID audioInputDevice[MAX_NUMBER_SOUND_CARDS]; - AudioDeviceID audioOutputDevice[MAX_NUMBER_SOUND_CARDS]; - AudioDeviceIOProcID audioInputProcID; - AudioDeviceIOProcID audioOutputProcID; + // typedef void (*MIDIReadProc)(const MIDIPacketList* pktlist, void* __nullable readProcRefCon, void* __nullable srcConnRefCon); !!?? + static void _onMIDI ( const MIDIPacketList* pktlist, void* /* readProcRefCon */, void* /* srcConnRefCon */ ) { pSound->onMIDI ( pktlist ); } - MIDIPortRef midiInPortRef; + void registerDeviceCallBacks(); + void unregisterDeviceCallBacks(); + +protected: + void setBaseChannelInfo ( cDeviceInfo& deviceSelection ); + bool setBufferSize ( AudioDeviceID& audioInDeviceID, + AudioDeviceID& audioOutDeviceID, + unsigned int iPrefBufferSize, + unsigned int& iActualBufferSize ); + bool CheckDeviceCapabilities ( cDeviceInfo& deviceSelection ); + bool getCurrentLatency(); + +protected: + // For Start: + + bool prepareAudio ( bool start ); + + AudioDeviceIOProcID audioInputProcID; // AudioDeviceIOProcID for current input device + AudioDeviceIOProcID audioOutputProcID; // AudioDeviceIOProcID for current output device + + //============================================================================ + // Virtual interface to CSoundBase: + //============================================================================ +protected: // CSoundBase Mandatory pointer to instance (must be set to 'this' in CSound constructor) + static CSound* pSound; + +public: // CSoundBase Mandatory functions. (but static functions can't be virtual) + static inline CSoundBase* pInstance() { return pSound; } + static inline const CSoundProperties& GetProperties() { return pSound->getSoundProperties(); } + +protected: + // CSoundBase internal + virtual void onChannelSelectionChanged() + { + CSoundStopper sound ( *this ); + setSelectedDevice(); + } + + virtual long createDeviceList ( bool bRescan = false ); // Fills strDeviceList. and my device list. Returns number of devices found + virtual bool checkDeviceChange ( CSoundBase::tDeviceChangeCheck mode, int iDriverIndex ); // Open device sequence handling.... + virtual unsigned int getDeviceBufferSize ( unsigned int iDesiredBufferSize ); - QString sChannelNamesInput[MAX_NUM_IN_OUT_CHANNELS]; - QString sChannelNamesOutput[MAX_NUM_IN_OUT_CHANNELS]; + virtual void closeCurrentDevice(); // Closes the current driver and Clears Device Info + virtual bool openDeviceSetup() { return false; } - QMutex Mutex; + virtual bool start(); + virtual bool stop(); }; diff --git a/src/client.cpp b/src/client.cpp index 203b1a0f36..b74995353d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -25,14 +25,18 @@ #include "client.h" /* Implementation *************************************************************/ -CClient::CClient ( const quint16 iPortNumber, - const quint16 iQosNumber, - const QString& strConnOnStartupAddress, - const QString& strMIDISetup, - const bool bNoAutoJackConnect, - const QString& strNClientName, - const bool bNEnableIPv6, - const bool bNMuteMeInPersonalMix ) : +// clang-format off +//TODO: Don't pass bNoAutoJackConnect (and strNClientName ?) to CCLient and CSound but pass commandline options ! +// Use the original char** argv so we can use the global command line parsing functions! +// Each class should parse the commandline for it's own options, so commandline options will be transparant. +// clang-format on +CClient::CClient ( const quint16 iPortNumber, + const quint16 iQosNumber, + const QString& strConnOnStartupAddress, + const QString& strNClientName, + const bool bNEnableIPv6, + const bool bNMuteMeInPersonalMix + ) : ChannelInfo(), strClientName ( strNClientName ), Channel ( false ), /* we need a client channel -> "false" */ @@ -48,7 +52,7 @@ CClient::CClient ( const quint16 iPortNumber, bMuteOutStream ( false ), fMuteOutStreamGain ( 1.0f ), Socket ( &Channel, iPortNumber, iQosNumber, "", bNEnableIPv6 ), - Sound ( AudioCallback, this, strMIDISetup, bNoAutoJackConnect, strNClientName ), + Sound ( AudioCallback, this), iAudioInFader ( AUD_FADER_IN_MIDDLE ), bReverbOnLeftChan ( false ), iReverbLevel ( 0 ), @@ -154,17 +158,17 @@ CClient::CClient ( const quint16 iPortNumber, QObject::connect ( &ConnLessProtocol, &CProtocol::CLChannelLevelListReceived, this, &CClient::CLChannelLevelListReceived ); // other - QObject::connect ( &Sound, &CSound::ReinitRequest, this, &CClient::OnSndCrdReinitRequest ); + QObject::connect ( &Sound, &CSound::reinitRequest, this, &CClient::OnSndCrdReinitRequest ); - QObject::connect ( &Sound, &CSound::ControllerInFaderLevel, this, &CClient::OnControllerInFaderLevel ); + QObject::connect ( &Sound, &CSound::controllerInFaderLevel, this, &CClient::OnControllerInFaderLevel ); - QObject::connect ( &Sound, &CSound::ControllerInPanValue, this, &CClient::OnControllerInPanValue ); + QObject::connect ( &Sound, &CSound::controllerInPanValue, this, &CClient::OnControllerInPanValue ); - QObject::connect ( &Sound, &CSound::ControllerInFaderIsSolo, this, &CClient::OnControllerInFaderIsSolo ); + QObject::connect ( &Sound, &CSound::controllerInFaderIsSolo, this, &CClient::OnControllerInFaderIsSolo ); - QObject::connect ( &Sound, &CSound::ControllerInFaderIsMute, this, &CClient::OnControllerInFaderIsMute ); + QObject::connect ( &Sound, &CSound::controllerInFaderIsMute, this, &CClient::OnControllerInFaderIsMute ); - QObject::connect ( &Sound, &CSound::ControllerInMuteMyself, this, &CClient::OnControllerInMuteMyself ); + QObject::connect ( &Sound, &CSound::controllerInMuteMyself, this, &CClient::OnControllerInMuteMyself ); QObject::connect ( &Socket, &CHighPrioSocket::InvalidPacketReceived, this, &CClient::OnInvalidPacketReceived ); @@ -188,9 +192,9 @@ CClient::CClient ( const quint16 iPortNumber, CClient::~CClient() { // if we were running, stop sound device - if ( Sound.IsRunning() ) + if ( Sound.IsStarted() ) { - Sound.Stop(); + StopSound(); } // free audio encoders and decoders @@ -208,6 +212,30 @@ CClient::~CClient() opus_custom_mode_destroy ( Opus64Mode ); } +void CClient::ShowError ( QString strError ) +{ + if ( !strError.isEmpty() ) + { + QMessageBox::critical ( NULL, QString ( APP_NAME ) + " " + tr ( "Error" ), strError, tr ( "Ok" ), nullptr ); + } +} + +void CClient::ShowWarning ( QString strWarning ) +{ + if ( !strWarning.isEmpty() ) + { + QMessageBox::warning ( NULL, QString ( APP_NAME ) + " " + tr ( "Warning" ), strWarning, tr ( "Ok" ), nullptr ); + } +} + +void CClient::ShowInfo ( QString strInfo ) +{ + if ( !strInfo.isEmpty() ) + { + QMessageBox::information ( NULL, QString ( APP_NAME ) + " " + tr ( "Information" ), strInfo, tr ( "Ok" ), nullptr ); + } +} + void CClient::OnSendProtMessage ( CVector vecMessage ) { // the protocol queries me to call the function to send the message @@ -293,7 +321,7 @@ void CClient::CreateServerJitterBufferMessage() void CClient::OnCLPingReceived ( CHostAddress InetAddr, int iMs ) { // make sure we are running and the server address is correct - if ( IsRunning() && ( InetAddr == Channel.GetAddress() ) ) + if ( SoundIsStarted() && ( InetAddr == Channel.GetAddress() ) ) { // take care of wrap arounds (if wrapping, do not use result) const int iCurDiff = EvaluatePingMessage ( iMs ); @@ -392,195 +420,107 @@ void CClient::SetSndCrdPrefFrameSizeFactor ( const int iNewFactor ) { // init with new parameter, if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } + + CSoundBase::CSoundStopper sound ( Sound ); // set new parameter iSndCrdPrefFrameSizeFactor = iNewFactor; // init with new block size index parameter Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } } } void CClient::SetEnableOPUS64 ( const bool eNEnableOPUS64 ) { + CSoundBase::CSoundStopper sound ( Sound ); + // init with new parameter, if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - // set new parameter bEnableOPUS64 = eNEnableOPUS64; Init(); - - if ( bWasRunning ) - { - Sound.Start(); - } } void CClient::SetAudioQuality ( const EAudioQuality eNAudioQuality ) { + CSoundBase::CSoundStopper sound ( Sound ); + // init with new parameter, if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } // set new parameter eAudioQuality = eNAudioQuality; Init(); - - if ( bWasRunning ) - { - Sound.Start(); - } } void CClient::SetAudioChannels ( const EAudChanConf eNAudChanConf ) { // init with new parameter, if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } + + CSoundBase::CSoundStopper sound ( Sound ); // set new parameter eAudioChannelConf = eNAudChanConf; Init(); - - if ( bWasRunning ) - { - Sound.Start(); - } } -QString CClient::SetSndCrdDev ( const QString strNewDev ) +void CClient::SetSndCrdDev ( const QString strNewDev ) { // if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) + CSoundBase::CSoundStopper sound ( Sound ); + + if ( !Sound.SetDevice ( strNewDev ) ) { - Sound.Stop(); + sound.Abort(); // Do not restart! + emit SoundDeviceChanged ( true ); // Disconnect } - const QString strError = Sound.SetDev ( strNewDev ); - // init again because the sound card actual buffer size might // be changed on new device Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } - - // in case of an error inform the GUI about it - if ( !strError.isEmpty() ) - { - emit SoundDeviceChanged ( strError ); - } - - return strError; } void CClient::SetSndCrdLeftInputChannel ( const int iNewChan ) { // if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } + CSoundBase::CSoundStopper sound ( Sound ); Sound.SetLeftInputChannel ( iNewChan ); Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } } void CClient::SetSndCrdRightInputChannel ( const int iNewChan ) { // if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } + CSoundBase::CSoundStopper sound ( Sound ); Sound.SetRightInputChannel ( iNewChan ); Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } } void CClient::SetSndCrdLeftOutputChannel ( const int iNewChan ) { // if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } + CSoundBase::CSoundStopper sound ( Sound ); Sound.SetLeftOutputChannel ( iNewChan ); Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } } void CClient::SetSndCrdRightOutputChannel ( const int iNewChan ) { + CSoundBase::CSoundStopper sound ( Sound ); + // if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } - Sound.SetRightOutputChannel ( iNewChan ); Init(); - - if ( bWasRunning ) - { - // restart client - Sound.Start(); - } } void CClient::OnSndCrdReinitRequest ( int iSndCrdResetType ) @@ -591,43 +531,43 @@ void CClient::OnSndCrdReinitRequest ( int iSndCrdResetType ) // different thread, therefore we need a mutex here MutexDriverReinit.lock(); { + CSoundBase::CSoundStopper sound ( Sound ); + // in older QT versions, enums cannot easily be used in signals without - // registering them -> workaroud: we use the int type and cast to the enum - const ESndCrdResetType eSndCrdResetType = static_cast ( iSndCrdResetType ); + // registering them -> workaround: we use the int type and cast to the enum + const tSndCrdResetType eSndCrdResetType = static_cast ( iSndCrdResetType ); // if client was running then first // stop it and restart again after new initialization - const bool bWasRunning = Sound.IsRunning(); - if ( bWasRunning ) - { - Sound.Stop(); - } // perform reinit request as indicated by the request type parameter - if ( eSndCrdResetType != RS_ONLY_RESTART ) + if ( eSndCrdResetType != tSndCrdResetType::RS_ONLY_RESTART ) { - if ( eSndCrdResetType != RS_ONLY_RESTART_AND_INIT ) + if ( eSndCrdResetType != tSndCrdResetType::RS_ONLY_RESTART_AND_INIT ) { // reinit the driver if requested // (we use the currently selected driver) - strError = Sound.SetDev ( Sound.GetDev() ); + Sound.ResetDevice(); // pgScorpio: was Sound.SetDev ( Sound.GetDev() ); But no! we should just reset the current device ! } // init client object (must always be performed if the driver - // was changed) - Init(); - } + // was changed) pgScorpio: driver changed ??? Just settings changed Should be called by Sound.ResetDev() or Sound.Start if neccesary!!! - if ( bWasRunning ) - { - // restart client - Sound.Start(); + Init(); } } MutexDriverReinit.unlock(); - // inform GUI about the sound card device change - emit SoundDeviceChanged ( strError ); + // inform GUI about the sound card device change, pgScorpio: device change??? + if ( strError.isEmpty() ) + { + emit SoundDeviceChanged ( false ); + } + else + { + emit SoundDeviceChanged ( true ); + ShowError ( strError ); + } } void CClient::OnHandledSignal ( int sigNum ) @@ -642,7 +582,7 @@ void CClient::OnHandledSignal ( int sigNum ) case SIGINT: case SIGTERM: // if connected, terminate connection (needed for headless mode) - if ( IsRunning() ) + if ( SoundIsStarted() ) { Stop(); } @@ -730,7 +670,7 @@ void CClient::OnClientIDReceived ( int iChanID ) emit ClientIDReceived ( iChanID ); } -void CClient::Start() +bool CClient::Start() { // init object Init(); @@ -739,13 +679,13 @@ void CClient::Start() Channel.SetEnable ( true ); // start audio interface - Sound.Start(); + return StartSound(); } void CClient::Stop() { // stop audio interface - Sound.Stop(); + StopSound(); // disable channel Channel.SetEnable ( false ); @@ -783,15 +723,9 @@ void CClient::Init() const int iFraSizeDefault = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_DEFAULT; const int iFraSizeSafe = SYSTEM_FRAME_SIZE_SAMPLES * FRAME_SIZE_FACTOR_SAFE; -#if defined( Q_OS_IOS ) - bFraSiFactPrefSupported = true; // to reduce sound init time, because we know it's supported in iOS - bFraSiFactDefSupported = true; - bFraSiFactSafeSupported = true; -#else - bFraSiFactPrefSupported = ( Sound.Init ( iFraSizePreffered ) == iFraSizePreffered ); - bFraSiFactDefSupported = ( Sound.Init ( iFraSizeDefault ) == iFraSizeDefault ); - bFraSiFactSafeSupported = ( Sound.Init ( iFraSizeSafe ) == iFraSizeSafe ); -#endif + bFraSiFactPrefSupported = Sound.BufferSizeSupported ( iFraSizePreffered ); + bFraSiFactDefSupported = Sound.BufferSizeSupported ( iFraSizeDefault ); + bFraSiFactSafeSupported = Sound.BufferSizeSupported ( iFraSizeSafe ); // translate block size index in actual block size const int iPrefMonoFrameSize = iSndCrdPrefFrameSizeFactor * SYSTEM_FRAME_SIZE_SAMPLES; @@ -805,7 +739,12 @@ void CClient::Init() // iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); // Problem is legitimate setting changes (buffer size for example). // so the condition should be something like "if ( Sound.isInitialized and APP_IS_INIALIZING)" - iMonoBlockSizeSam = Sound.Init ( iPrefMonoFrameSize ); + // + // pgScorpio: Solution was NOT using Init(iPrefMonoFrameSize) but to use the new + // Sound.BufferSizeSupported(iFrameSize) and + // Sound.SetBufferSize ( iPrefMonoFrameSize ) + // The actual init will be done by Sound.Start() from StartSound() + iMonoBlockSizeSam = Sound.SetBufferSize ( iPrefMonoFrameSize ); // Calculate the current sound card frame size factor. In case // the current mono block size is not a multiple of the system @@ -825,6 +764,8 @@ void CClient::Init() // An unsupported sound card buffer size is currently used -> we have // to use a conversion buffer. Per definition we use the smallest buffer // size as the current frame size. + // + // pgScorpio: TODO The buffer size conversion should be done by CSound ! // store actual sound card buffer size (stereo) bSndCrdConversionBufferRequired = true; @@ -1046,7 +987,8 @@ void CClient::ProcessAudioDataIntern ( CVector& vecsStereoSndCrd ) // Transmit signal --------------------------------------------------------- - if ( iInputBoost != 1 ) + if ( iInputBoost != + 1 ) // pgScorpio: This can now be done by CSound during sample conversion (Much faster than itterating the whole buffer again!) { // apply a general gain boost to all audio input: for ( i = 0, j = 0; i < iMonoBlockSizeSam; i++, j += 2 ) diff --git a/src/client.h b/src/client.h index e7e44da2a9..8a346d5d8a 100644 --- a/src/client.h +++ b/src/client.h @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef USE_OPUS_SHARED_LIB # include "opus/opus_custom.h" #else @@ -110,18 +111,22 @@ class CClient : public QObject CClient ( const quint16 iPortNumber, const quint16 iQosNumber, const QString& strConnOnStartupAddress, - const QString& strMIDISetup, - const bool bNoAutoJackConnect, const QString& strNClientName, const bool bNEnableIPv6, const bool bNMuteMeInPersonalMix ); virtual ~CClient(); - void Start(); + // pgScorpio: use clear function names !! + bool StartSound() { return Sound.Start(); } + bool StopSound() { return Sound.Stop(); } + bool SoundIsStarted() { return Sound.IsStarted(); } // Was IsRunning, See Client dialog code: IsRunning is misused on some places + bool SoundIsActive() const { return Sound.IsActive(); } + + // pgScorpio: Start/Stop what ?? + bool Start(); void Stop(); - bool IsRunning() { return Sound.IsRunning(); } - bool IsCallbackEntered() const { return Sound.IsCallbackEntered(); } + bool SetServerAddr ( QString strNAddr ); double GetLevelForMeterdBLeft() { return SignalLevelMeter.GetLevelForMeterdBLeftOrMono(); } @@ -177,11 +182,11 @@ class CClient : public QObject int GetUploadRateKbps() { return Channel.GetUploadRateKbps(); } // sound card device selection - QStringList GetSndCrdDevNames() { return Sound.GetDevNames(); } + QStringList GetSndCrdDevNames() { return Sound.GetDeviceNames(); } - QString SetSndCrdDev ( const QString strNewDev ); - QString GetSndCrdDev() { return Sound.GetDev(); } - void OpenSndCrdDriverSetup() { Sound.OpenDriverSetup(); } + void SetSndCrdDev ( const QString strNewDev ); + QString GetSndCrdDev() { return Sound.GetCurrentDeviceName(); } + void OpenSndCrdDriverSetup() { Sound.OpenDeviceSetup(); } // sound card channel selection int GetSndCrdNumInputChannels() { return Sound.GetNumInputChannels(); } @@ -242,7 +247,11 @@ class CClient : public QObject void SetRemoteChanPan ( const int iId, const float fPan ) { Channel.SetRemoteChanPan ( iId, fPan ); } - void SetInputBoost ( const int iNewBoost ) { iInputBoost = iNewBoost; } + void SetInputBoost ( const int iNewBoost ) //{ iInputBoost = iNewBoost; } + { // pgScorpio: Moved input gain to CSound + iInputBoost = 1; + Sound.SetInputGain ( iNewBoost ); + } void SetRemoteInfo() { Channel.SetRemoteInfo ( ChannelInfo ); } @@ -273,7 +282,8 @@ class CClient : public QObject QString strClientName; #ifdef LLCON_VST_PLUGIN - // VST version must have direct access to sound object + // VST version must have direct access to sound object. + // pgScorpio: We can now use CSound::pInstance() to get a pointer to it's CSoundBase interface CSound* GetSound() { return &Sound; } #endif @@ -364,6 +374,11 @@ class CClient : public QObject CSignalHandler* pSignalHandler; +protected: + void ShowError ( QString strError ); + void ShowWarning ( QString strWarning ); + void ShowInfo ( QString strInfo ); + protected slots: void OnHandledSignal ( int sigNum ); void OnSendProtMessage ( CVector vecMessage ); @@ -419,7 +434,8 @@ protected slots: void CLChannelLevelListReceived ( CHostAddress InetAddr, CVector vecLevelList ); void Disconnected(); - void SoundDeviceChanged ( QString strError ); + + void SoundDeviceChanged ( bool bDisconnect ); void ControllerInFaderLevel ( int iChannelIdx, int iValue ); void ControllerInPanValue ( int iChannelIdx, int iValue ); void ControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); diff --git a/src/clientdlg.cpp b/src/clientdlg.cpp index c94c03b707..2ac178c9b0 100644 --- a/src/clientdlg.cpp +++ b/src/clientdlg.cpp @@ -28,7 +28,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP, CClientSettings* pNSetP, const QString& strConnOnStartupAddress, - const QString& strMIDISetup, + const bool bUseMIDICtrl, const bool bNewShowComplRegConnList, const bool bShowAnalyzerConsole, const bool bMuteStream, @@ -38,7 +38,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP, pClient ( pNCliP ), pSettings ( pNSetP ), bConnectDlgWasShown ( false ), - bMIDICtrlUsed ( !strMIDISetup.isEmpty() ), + bMIDICtrlUsed ( bUseMIDICtrl ), bDetectFeedback ( false ), bEnableIPv6 ( bNEnableIPv6 ), eLastRecorderState ( RS_UNDEFINED ), // for SetMixerBoardDeco @@ -265,11 +265,10 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // prepare update check info label (invisible by default) lblUpdateCheck->setOpenExternalLinks ( true ); // enables opening a web browser if one clicks on a html link - lblUpdateCheck->setText ( "" + APP_UPGRADE_AVAILABLE_MSG_TEXT.arg ( APP_NAME ).arg ( VERSION ) + "" ); + lblUpdateCheck->setText ( "" + APP_UPGRADE_AVAILABLE_MSG_TEXT.arg ( APP_NAME, VERSION ) + "" ); lblUpdateCheck->hide(); // setup timers - TimerCheckAudioDeviceOk.setSingleShot ( true ); // only check once after connection TimerDetectFeedback.setSingleShot ( true ); // Connect on startup ------------------------------------------------------ @@ -386,11 +385,11 @@ CClientDlg::CClientDlg ( CClient* pNCliP, // Settings menu -------------------------------------------------------------- QMenu* pSettingsMenu = new QMenu ( tr ( "Sett&ings" ), this ); - pSettingsMenu->addAction ( tr ( "My &Profile..." ), this, SLOT ( OnOpenUserProfileSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_P ) ); + pSettingsMenu->addAction ( tr ( "&User Settings..." ), this, SLOT ( OnOpenUserProfileSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_P ) ); - pSettingsMenu->addAction ( tr ( "Audio/Network &Settings..." ), this, SLOT ( OnOpenAudioNetSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_S ) ); + pSettingsMenu->addAction ( tr ( "&Audio Settings..." ), this, SLOT ( OnOpenAudioSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_S ) ); - pSettingsMenu->addAction ( tr ( "A&dvanced Settings..." ), this, SLOT ( OnOpenAdvancedSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_D ) ); + pSettingsMenu->addAction ( tr ( "&Network Settings..." ), this, SLOT ( OnOpenNetworkSettings() ), QKeySequence ( Qt::CTRL + Qt::Key_D ) ); // Main menu bar ----------------------------------------------------------- QMenuBar* pMenu = new QMenuBar ( this ); @@ -440,6 +439,9 @@ CClientDlg::CClientDlg ( CClient* pNCliP, } // Connections ------------------------------------------------------------- + + QObject::connect ( CSound::pInstance(), &CSound::soundActiveTimeout, this, &CClientDlg::OnSoundActiveTimeout ); + // push buttons QObject::connect ( butConnect, &QPushButton::clicked, this, &CClientDlg::OnConnectDisconBut ); @@ -459,8 +461,6 @@ CClientDlg::CClientDlg ( CClient* pNCliP, QObject::connect ( &TimerPing, &QTimer::timeout, this, &CClientDlg::OnTimerPing ); - QObject::connect ( &TimerCheckAudioDeviceOk, &QTimer::timeout, this, &CClientDlg::OnTimerCheckAudioDeviceOk ); - QObject::connect ( &TimerDetectFeedback, &QTimer::timeout, this, &CClientDlg::OnTimerDetectFeedback ); QObject::connect ( sldAudioReverb, &QSlider::valueChanged, this, &CClientDlg::OnAudioReverbValueChanged ); @@ -609,7 +609,7 @@ void CClientDlg::closeEvent ( QCloseEvent* Event ) AnalyzerConsole.close(); // if connected, terminate connection - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) { pClient->Stop(); } @@ -690,6 +690,13 @@ void CClientDlg::OnConnectDlgAccepted() // we process the accepted signal only once after the dialog was initially shown. if ( bConnectDlgWasShown ) { + // first check if we are already connected, if this is the case we have to + // disconnect the old server first + if ( pClient->SoundIsStarted() ) + { + Disconnect(); + } + // get the address from the connect dialog QString strSelectedAddress = ConnectDlg.GetSelectedAddress(); @@ -730,13 +737,6 @@ void CClientDlg::OnConnectDlgAccepted() } } - // first check if we are already connected, if this is the case we have to - // disconnect the old server first - if ( pClient->IsRunning() ) - { - Disconnect(); - } - // initiate connection Connect ( strSelectedAddress, strMixerBoardLabel ); @@ -745,13 +745,14 @@ void CClientDlg::OnConnectDlgAccepted() } } +void CClientDlg::OnSoundActiveTimeout() { Disconnect(); } + void CClientDlg::OnConnectDisconBut() { // the connect/disconnect button implements a toggle functionality - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) { Disconnect(); - SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); } else { @@ -846,7 +847,7 @@ void CClientDlg::OnLicenceRequired ( ELicenceType eLicenceType ) CLicenceDlg LicenceDlg; // mute the client output stream - pClient->SetMuteOutStream ( true ); + pClient->SetMuteOutStream ( true ); // pgScorpio: This should be a signal ! // Open the licence dialog and check if the licence was accepted. In // case the dialog is just closed or the decline button was pressed, @@ -859,7 +860,7 @@ void CClientDlg::OnLicenceRequired ( ELicenceType eLicenceType ) // unmute the client output stream if local mute button is not pressed if ( chbLocalMute->checkState() == Qt::Unchecked ) { - pClient->SetMuteOutStream ( false ); + pClient->SetMuteOutStream ( false ); // pgScorpio: This should be a signal ! } } } @@ -885,9 +886,9 @@ void CClientDlg::OnNumClientsChanged ( int iNewNumClients ) SetMyWindowTitle ( iNewNumClients ); } -void CClientDlg::OnOpenAudioNetSettings() { ShowGeneralSettings ( SETTING_TAB_AUDIONET ); } +void CClientDlg::OnOpenAudioSettings() { ShowGeneralSettings ( SETTING_TAB_AUDIO ); } -void CClientDlg::OnOpenAdvancedSettings() { ShowGeneralSettings ( SETTING_TAB_ADVANCED ); } +void CClientDlg::OnOpenNetworkSettings() { ShowGeneralSettings ( SETTING_TAB_NETWORK ); } void CClientDlg::OnOpenUserProfileSettings() { ShowGeneralSettings ( SETTING_TAB_USER ); } @@ -1008,7 +1009,7 @@ void CClientDlg::OnSettingsStateChanged ( int value ) { if ( value == Qt::Checked ) { - ShowGeneralSettings ( SETTING_TAB_AUDIONET ); + ShowGeneralSettings ( SETTING_TAB_AUDIO ); } else { @@ -1030,7 +1031,7 @@ void CClientDlg::OnChatStateChanged ( int value ) void CClientDlg::OnLocalMuteStateChanged ( int value ) { - pClient->SetMuteOutStream ( value == Qt::Checked ); + pClient->SetMuteOutStream ( value == Qt::Checked ); // pgScorpio: This should be a signal ! // show/hide info label if ( value == Qt::Checked ) @@ -1135,49 +1136,25 @@ void CClientDlg::OnPingTimeResult ( int iPingTime ) ledDelay->SetLight ( eOverallDelayLEDColor ); } -void CClientDlg::OnTimerCheckAudioDeviceOk() -{ - // check if the audio device entered the audio callback after a pre-defined - // timeout to check if a valid device is selected and if we do not have - // fundamental settings errors (in which case the GUI would only show that - // it is trying to connect the server which does not help to solve the problem (#129)) - if ( !pClient->IsCallbackEntered() ) - { - QMessageBox::warning ( this, - APP_NAME, - tr ( "Your sound card is not working correctly. " - "Please open the settings dialog and check the device selection and the driver settings." ) ); - } -} - void CClientDlg::OnTimerDetectFeedback() { bDetectFeedback = false; } -void CClientDlg::OnSoundDeviceChanged ( QString strError ) +void CClientDlg::OnSoundDeviceChanged ( bool bDisconnect ) { - if ( !strError.isEmpty() ) + if ( bDisconnect ) { - // the sound device setup has a problem, disconnect any active connection - if ( pClient->IsRunning() ) - { - Disconnect(); - } - - // show the error message of the device setup - QMessageBox::critical ( this, APP_NAME, strError, tr ( "Ok" ), nullptr ); - } + Disconnect(); - // if the check audio device timer is running, it must be restarted on a device change - if ( TimerCheckAudioDeviceOk.isActive() ) - { - TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); + TimerDetectFeedback.stop(); + bDetectFeedback = false; } - - if ( pSettings->bEnableFeedbackDetection && TimerDetectFeedback.isActive() ) + else { - TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); - bDetectFeedback = true; + if ( pSettings->bEnableFeedbackDetection && TimerDetectFeedback.isActive() ) + { + TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); + bDetectFeedback = true; + } } - // update the settings dialog ClientSettingsDlg.UpdateSoundDeviceChannelSelectionFrame(); } @@ -1188,6 +1165,11 @@ void CClientDlg::OnCLPingTimeWithNumClientsReceived ( CHostAddress InetAddr, int ConnectDlg.SetPingTimeAndNumClientsResult ( InetAddr, iPingTime, iNumClients ); } +// clang-format off +//pgScorpio TODO: +// Connect() does NOT belong here! Dialog code should never take any non ui actions ! +// Signal CClient, and CLient should take the needed actions and control CClientDlg by onConnected(), onDisconnected(). +// clang-format on void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& strMixerBoardLabel ) { // set address and check if address is valid @@ -1197,7 +1179,7 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str // running state but show error message try { - if ( !pClient->IsRunning() ) + if ( !pClient->SoundIsStarted() ) { pClient->Start(); } @@ -1210,39 +1192,46 @@ void CClientDlg::Connect ( const QString& strSelectedAddress, const QString& str return; } - // hide label connect to server - lblConnectToServer->hide(); - lbrInputLevelL->setEnabled ( true ); - lbrInputLevelR->setEnabled ( true ); + if ( pClient->SoundIsStarted() ) + { + // hide label connect to server + lblConnectToServer->hide(); + lbrInputLevelL->setEnabled ( true ); + lbrInputLevelR->setEnabled ( true ); - // change connect button text to "disconnect" - butConnect->setText ( tr ( "&Disconnect" ) ); + // change connect button text to "disconnect" + butConnect->setText ( tr ( "&Disconnect" ) ); - // set server name in audio mixer group box title - MainMixerBoard->SetServerName ( strMixerBoardLabel ); + // set server name in audio mixer group box title + MainMixerBoard->SetServerName ( strMixerBoardLabel ); - // start timer for level meter bar and ping time measurement - TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); - TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); - TimerPing.start ( PING_UPDATE_TIME_MS ); - TimerCheckAudioDeviceOk.start ( CHECK_AUDIO_DEV_OK_TIME_MS ); // is single shot timer + // start timer for level meter bar and ping time measurement + TimerSigMet.start ( LEVELMETER_UPDATE_TIME_MS ); + TimerBuffersLED.start ( BUFFER_LED_UPDATE_TIME_MS ); + TimerPing.start ( PING_UPDATE_TIME_MS ); - // audio feedback detection - if ( pSettings->bEnableFeedbackDetection ) - { - TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer - bDetectFeedback = true; + // audio feedback detection + if ( pSettings->bEnableFeedbackDetection ) + { + TimerDetectFeedback.start ( DETECT_FEEDBACK_TIME_MS ); // single shot timer + bDetectFeedback = true; + } } } } +// clang-format off +//pgScorpio TODO: +// Disconnect() does NOT belong here! Dialog code should never take any non ui actions ! +// Signal CClient, and CLient should take the needed actions and control CClientDlg by onConnected(), onDisconnected(). +// clang-format on void CClientDlg::Disconnect() { // only stop client if currently running, in case we received // the stopped message, the client is already stopped but the // connect/disconnect button and other GUI controls must be // updated - if ( pClient->IsRunning() ) + if ( pClient->SoundIsStarted() ) { pClient->Stop(); } @@ -1266,7 +1255,6 @@ void CClientDlg::Disconnect() // stop other timers TimerBuffersLED.stop(); TimerPing.stop(); - TimerCheckAudioDeviceOk.stop(); TimerDetectFeedback.stop(); bDetectFeedback = false; @@ -1288,6 +1276,8 @@ OnTimerStatus(); // clear mixer board (remove all faders) MainMixerBoard->HideAll(); + + // SetMixerBoardDeco ( RS_UNDEFINED, pClient->GetGUIDesign() ); } void CClientDlg::UpdateDisplay() diff --git a/src/clientdlg.h b/src/clientdlg.h index bbcc604766..14f18acc4b 100644 --- a/src/clientdlg.h +++ b/src/clientdlg.h @@ -77,7 +77,7 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase CClientDlg ( CClient* pNCliP, CClientSettings* pNSetP, const QString& strConnOnStartupAddress, - const QString& strMIDISetup, + const bool bUseMIDICtrl, const bool bNewShowComplRegConnList, const bool bShowAnalyzerConsole, const bool bMuteStream, @@ -113,7 +113,6 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase QTimer TimerBuffersLED; QTimer TimerStatus; QTimer TimerPing; - QTimer TimerCheckAudioDeviceOk; QTimer TimerDetectFeedback; virtual void closeEvent ( QCloseEvent* Event ); @@ -127,10 +126,10 @@ class CClientDlg : public CBaseDlg, private Ui_CClientDlgBase CAnalyzerConsole AnalyzerConsole; public slots: + void OnSoundActiveTimeout(); void OnConnectDisconBut(); void OnTimerSigMet(); void OnTimerBuffersLED(); - void OnTimerCheckAudioDeviceOk(); void OnTimerDetectFeedback(); void OnTimerStatus() { UpdateDisplay(); } @@ -157,8 +156,8 @@ public slots: void OnSaveChannelSetup(); void OnOpenConnectionSetupDialog() { ShowConnectionSetupDialog(); } void OnOpenUserProfileSettings(); - void OnOpenAudioNetSettings(); - void OnOpenAdvancedSettings(); + void OnOpenAudioSettings(); + void OnOpenNetworkSettings(); void OnOpenChatDialog() { ShowChatWindow(); } void OnOpenAnalyzerConsole() { ShowAnalyzerConsole(); } void OnOwnFaderFirst() @@ -191,7 +190,7 @@ public slots: void OnConClientListMesReceived ( CVector vecChanInfo ); void OnChatTextReceived ( QString strChatText ); void OnLicenceRequired ( ELicenceType eLicenceType ); - void OnSoundDeviceChanged ( QString strError ); + void OnSoundDeviceChanged ( bool bDisconnect ); void OnChangeChanGain ( int iId, float fGain, bool bIsMyOwnFader ) { pClient->SetRemoteChanGain ( iId, fGain, bIsMyOwnFader ); } diff --git a/src/clientsettingsdlg.cpp b/src/clientsettingsdlg.cpp index 61fc525c9e..23d320269b 100644 --- a/src/clientsettingsdlg.cpp +++ b/src/clientsettingsdlg.cpp @@ -30,6 +30,9 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet pClient ( pNCliP ), pSettings ( pNSetP ) { + // pgScorpio TODO: All CSound related dependancies must come from CSoundProperties ! No ifdef's here ! + const CSoundProperties& soundProperties = CSound::GetProperties(); + setupUi ( this ); #if defined( Q_OS_IOS ) @@ -65,11 +68,11 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet "%1, where %2 is the current attenuation indicator." ) .arg ( "" + tr ( "L" ) + " -x", "x" ); - lblAudioPan->setWhatsThis ( strAudFader ); + groupBoxBalance->setWhatsThis ( strAudFader ); lblAudioPanValue->setWhatsThis ( strAudFader ); - sldAudioPan->setWhatsThis ( strAudFader ); + sldAudioPan_5->setWhatsThis ( strAudFader ); - sldAudioPan->setAccessibleName ( tr ( "Local audio input fader (left/right)" ) ); + sldAudioPan_5->setAccessibleName ( tr ( "Local audio input fader (left/right)" ) ); // jitter buffer QString strJitterBufferSize = "" + tr ( "Jitter Buffer Size" ) + ": " + @@ -117,33 +120,19 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet chbAutoJitBuf->setAccessibleName ( tr ( "Auto jitter buffer switch" ) ); chbAutoJitBuf->setToolTip ( strJitterBufferSizeTT ); -#if !defined( WITH_JACK ) // sound card device - lblSoundcardDevice->setWhatsThis ( "" + tr ( "Audio Device" ) + ": " + - tr ( "Under the Windows operating system the ASIO driver (sound card) can be " - "selected using %1. If the selected ASIO driver is not valid an error " - "message is shown and the previous valid driver is selected. " - "Under macOS the input and output hardware can be selected." ) - .arg ( APP_NAME ) + - "
" + - tr ( "If the driver is selected during an active connection, the connection " - "is stopped, the driver is changed and the connection is started again " - "automatically." ) ); - - cbxSoundcard->setAccessibleName ( tr ( "Sound card device selector combo box" ) ); - -# if defined( _WIN32 ) - // set Windows specific tool tip - cbxSoundcard->setToolTip ( tr ( "If the ASIO4ALL driver is used, " - "please note that this driver usually introduces approx. 10-30 ms of " - "additional audio delay. Using a sound card with a native ASIO driver " - "is therefore recommended." ) + - "
" + - tr ( "If you are using the kX ASIO " - "driver, make sure to connect the ASIO inputs in the kX DSP settings " - "panel." ) + - TOOLTIP_COM_END_TEXT ); -# endif + groupBoxAudioDevice->setWhatsThis ( soundProperties.AudioDeviceWhatsThis() ); + cbxSoundcard->setAccessibleName ( soundProperties.AudioDeviceAccessibleName() ); + cbxSoundcard->setToolTip ( soundProperties.AudioDeviceToolTip() + TOOLTIP_COM_END_TEXT ); + cbxSoundcard->setHidden ( !soundProperties.HasAudioDeviceSelection() ); + + // driver setup button + butDriverSetup->setWhatsThis ( soundProperties.SetupButtonWhatsThis() ); + butDriverSetup->setAccessibleName ( soundProperties.SetupButtonAccessibleName() ); + butDriverSetup->setToolTip ( soundProperties.SetupButtonToolTip() + TOOLTIP_COM_END_TEXT ); + + butDriverSetup->setText ( soundProperties.SetupButtonText() ); + butDriverSetup->setHidden ( !soundProperties.HasSetupDialog() ); // sound card input/output channel mapping QString strSndCrdChanMapp = "" + tr ( "Sound Card Channel Mapping" ) + ": " + @@ -157,16 +146,21 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet .arg ( APP_NAME ); lblInChannelMapping->setWhatsThis ( strSndCrdChanMapp ); + lblInChannelMapping->setVisible ( soundProperties.HasInputChannelSelection() ); lblOutChannelMapping->setWhatsThis ( strSndCrdChanMapp ); + lblOutChannelMapping->setVisible ( soundProperties.HasOutputChannelSelection() ); cbxLInChan->setWhatsThis ( strSndCrdChanMapp ); cbxLInChan->setAccessibleName ( tr ( "Left input channel selection combo box" ) ); + cbxLInChan->setVisible ( soundProperties.HasInputChannelSelection() ); cbxRInChan->setWhatsThis ( strSndCrdChanMapp ); cbxRInChan->setAccessibleName ( tr ( "Right input channel selection combo box" ) ); + cbxRInChan->setVisible ( soundProperties.HasInputChannelSelection() ); cbxLOutChan->setWhatsThis ( strSndCrdChanMapp ); cbxLOutChan->setAccessibleName ( tr ( "Left output channel selection combo box" ) ); + cbxLOutChan->setVisible ( soundProperties.HasOutputChannelSelection() ); cbxROutChan->setWhatsThis ( strSndCrdChanMapp ); cbxROutChan->setAccessibleName ( tr ( "Right output channel selection combo box" ) ); -#endif + cbxROutChan->setVisible ( soundProperties.HasOutputChannelSelection() ); // enable OPUS64 chbEnableOPUS64->setWhatsThis ( "" + tr ( "Enable Small Network Buffers" ) + ": " + @@ -228,23 +222,6 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet .arg ( APP_NAME ) + TOOLTIP_COM_END_TEXT; -#if defined( _WIN32 ) && !defined( WITH_JACK ) - // Driver setup button - QString strSndCardDriverSetup = "" + tr ( "Sound card driver settings" ) + ": " + - tr ( "This opens the driver settings of your sound card. Some drivers " - "allow you to change buffer settings, others like ASIO4ALL " - "lets you choose input or outputs of your device(s). " - "More information can be found on jamulus.io." ); - - QString strSndCardDriverSetupTT = tr ( "Opens the driver settings. Note: %1 currently only supports devices " - "with a sample rate of %2 Hz. " - "You will not be able to select a driver/device which doesn't. " - "For more help see jamulus.io." ) - .arg ( APP_NAME ) - .arg ( SYSTEM_SAMPLE_RATE_HZ ) + - TOOLTIP_COM_END_TEXT; -#endif - rbtBufferDelayPreferred->setWhatsThis ( strSndCrdBufDelay ); rbtBufferDelayPreferred->setAccessibleName ( tr ( "64 samples setting radio button" ) ); rbtBufferDelayPreferred->setToolTip ( strSndCrdBufDelayTT ); @@ -255,12 +232,6 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet rbtBufferDelaySafe->setAccessibleName ( tr ( "256 samples setting radio button" ) ); rbtBufferDelaySafe->setToolTip ( strSndCrdBufDelayTT ); -#if defined( _WIN32 ) && !defined( WITH_JACK ) - butDriverSetup->setWhatsThis ( strSndCardDriverSetup ); - butDriverSetup->setAccessibleName ( tr ( "ASIO Device Settings push button" ) ); - butDriverSetup->setToolTip ( strSndCardDriverSetupTT ); -#endif - // fancy skin lblSkin->setWhatsThis ( "" + tr ( "Skin" ) + ": " + tr ( "Select the skin to be used for the main window." ) ); @@ -335,7 +306,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet "fader level if no other fader level from a previous connection " "of that client was already stored." ); - lblNewClientLevel->setWhatsThis ( strNewClientLevel ); + groupBoxNewClientLevel->setWhatsThis ( strNewClientLevel ); edtNewClientLevel->setWhatsThis ( strNewClientLevel ); edtNewClientLevel->setAccessibleName ( tr ( "New client level edit box" ) ); @@ -352,9 +323,10 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet "there. Instead, decrease your input level by getting farther away " "from your microphone, adjusting your sound equipment " "or by decreasing your operating system's input settings." ); - lblInputBoost->setWhatsThis ( strInputBoost ); + groupBoxInputBoost->setWhatsThis ( strInputBoost ); cbxInputBoost->setWhatsThis ( strInputBoost ); cbxInputBoost->setAccessibleName ( tr ( "Input Boost combo box" ) ); + groupBoxInputBoost->setVisible ( soundProperties.HasInputGainSelection() ); // custom directories QString strCustomDirectories = "" + tr ( "Custom Directories" ) + ": " + @@ -363,7 +335,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet "To remove a value, select it, delete the text in the input box, " "then move focus out of the control." ); - lblCustomDirectories->setWhatsThis ( strCustomDirectories ); + groupBoxCustomDirectories->setWhatsThis ( strCustomDirectories ); cbxCustomDirectories->setWhatsThis ( strCustomDirectories ); cbxCustomDirectories->setAccessibleName ( tr ( "Custom Directories combo box" ) ); @@ -386,21 +358,13 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet QString strDetectFeedback = "" + tr ( "Feedback Protection" ) + ": " + tr ( "Enable feedback protection to detect acoustic feedback between " "microphone and speakers." ); - lblDetectFeedback->setWhatsThis ( strDetectFeedback ); + groupBoxFeedBackProtection->setWhatsThis ( strDetectFeedback ); chbDetectFeedback->setWhatsThis ( strDetectFeedback ); chbDetectFeedback->setAccessibleName ( tr ( "Feedback Protection check box" ) ); - // init driver button -#if defined( _WIN32 ) && !defined( WITH_JACK ) - butDriverSetup->setText ( tr ( "ASIO Device Settings" ) ); -#else - // no use for this button for MacOS/Linux right now or when using JACK -> hide it - butDriverSetup->hide(); -#endif - // init audio in fader - sldAudioPan->setRange ( AUD_FADER_IN_MIN, AUD_FADER_IN_MAX ); - sldAudioPan->setTickInterval ( AUD_FADER_IN_MAX / 5 ); + sldAudioPan_5->setRange ( AUD_FADER_IN_MIN, AUD_FADER_IN_MAX ); + sldAudioPan_5->setTickInterval ( AUD_FADER_IN_MAX / 5 ); UpdateAudioFaderSlider(); // init delay and other information controls @@ -492,7 +456,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet // Add help text to controls ----------------------------------------------- // Musician Profile - QString strFaderTag = "" + tr ( "Musician Profile" ) + ": " + + QString strFaderTag = "" + tr ( "User Profile" ) + ": " + tr ( "Write your name or an alias here so the other musicians you want to " "play with know who you are. You may also add a picture of the instrument " "you play and a flag of the country or region you are located in. " @@ -504,6 +468,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet "you." ) .arg ( APP_NAME ); + grbMusician->setWhatsThis ( strFaderTag ); plblAlias->setWhatsThis ( strFaderTag ); pedtAlias->setAccessibleName ( tr ( "Alias or name edit box" ) ); plblInstrument->setWhatsThis ( strFaderTag ); @@ -687,14 +652,11 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, CClientSettings* pNSet &CClientSettingsDlg::OnInputBoostChanged ); // buttons -#if defined( _WIN32 ) && !defined( WITH_JACK ) - // Driver Setup button is only available for Windows when JACK is not used QObject::connect ( butDriverSetup, &QPushButton::clicked, this, &CClientSettingsDlg::OnDriverSetupClicked ); -#endif // misc // sliders - QObject::connect ( sldAudioPan, &QSlider::valueChanged, this, &CClientSettingsDlg::OnAudioPanValueChanged ); + QObject::connect ( sldAudioPan_5, &QSlider::valueChanged, this, &CClientSettingsDlg::OnAudioPanValueChanged ); QObject::connect ( &SndCrdBufferDelayButtonGroup, static_cast ( &QButtonGroup::buttonClicked ), @@ -841,23 +803,25 @@ void CClientSettingsDlg::UpdateSoundDeviceChannelSelectionFrame() cbxSoundcard->setCurrentText ( pClient->GetSndCrdDev() ); // update input/output channel selection -#if defined( _WIN32 ) || defined( __APPLE__ ) || defined( __MACOSX ) + // #if defined( _WIN32 ) || defined( __APPLE__ ) || defined( __MACOSX ) int iSndChanIdx; // Definition: The channel selection frame shall only be visible, // if more than two input or output channels are available - const int iNumInChannels = pClient->GetSndCrdNumInputChannels(); - const int iNumOutChannels = pClient->GetSndCrdNumOutputChannels(); + /* + const int iNumInChannels = pClient->GetSndCrdNumInputChannels(); + const int iNumOutChannels = pClient->GetSndCrdNumOutputChannels(); - if ( ( iNumInChannels <= 2 ) && ( iNumOutChannels <= 2 ) ) - { - // as defined, make settings invisible - FrameSoundcardChannelSelection->setVisible ( false ); - } - else + if ( ( iNumInChannels < 2 ) && ( iNumOutChannels < 2 ) ) + { + // as defined, make settings invisible + FrameSoundcardChannelSelection->setVisible ( false ); + } + else + */ { // update combo boxes - FrameSoundcardChannelSelection->setVisible ( true ); + // FrameSoundcardChannelSelection->setVisible ( true ); // input cbxLInChan->clear(); @@ -887,10 +851,12 @@ void CClientSettingsDlg::UpdateSoundDeviceChannelSelectionFrame() cbxROutChan->setCurrentIndex ( pClient->GetSndCrdRightOutputChannel() ); } } -#else - // for other OS, no sound card channel selection is supported - FrameSoundcardChannelSelection->setVisible ( false ); -#endif + /* + #else + // for other OS, no sound card channel selection is supported + FrameSoundcardChannelSelection->setVisible ( false ); + #endif + */ } void CClientSettingsDlg::SetEnableFeedbackDetection ( bool enable ) @@ -899,9 +865,7 @@ void CClientSettingsDlg::SetEnableFeedbackDetection ( bool enable ) chbDetectFeedback->setCheckState ( pSettings->bEnableFeedbackDetection ? Qt::Checked : Qt::Unchecked ); } -#if defined( _WIN32 ) && !defined( WITH_JACK ) void CClientSettingsDlg::OnDriverSetupClicked() { pClient->OpenSndCrdDriverSetup(); } -#endif void CClientSettingsDlg::OnNetBufValueChanged ( int value ) { @@ -1049,7 +1013,7 @@ void CClientSettingsDlg::UpdateDisplay() UpdateJitterBufferFrame(); UpdateSoundCardFrame(); - if ( !pClient->IsRunning() ) + if ( !pClient->SoundIsStarted() ) { // clear text labels with client parameters lblUpstreamValue->setText ( "---" ); @@ -1155,7 +1119,7 @@ void CClientSettingsDlg::UpdateAudioFaderSlider() { // update slider and label of audio fader const int iCurAudInFader = pClient->GetAudioInFader(); - sldAudioPan->setValue ( iCurAudInFader ); + sldAudioPan_5->setValue ( iCurAudInFader ); // show in label the center position and what channel is // attenuated diff --git a/src/clientsettingsdlg.h b/src/clientsettingsdlg.h index acdcf256ec..a1c6e18365 100644 --- a/src/clientsettingsdlg.h +++ b/src/clientsettingsdlg.h @@ -105,11 +105,7 @@ public slots: void OnTabChanged(); void OnMakeTabChange ( int iTabIdx ); void OnAudioPanValueChanged ( int value ); - -#if defined( _WIN32 ) && !defined( WITH_JACK ) - // Only include this slot for Windows when JACK is NOT used void OnDriverSetupClicked(); -#endif signals: void GUIDesignChanged(); diff --git a/src/clientsettingsdlgbase.ui b/src/clientsettingsdlgbase.ui index 847d32c550..d35a5ec09f 100644 --- a/src/clientsettingsdlgbase.ui +++ b/src/clientsettingsdlgbase.ui @@ -6,10 +6,16 @@ 0 0 - 436 - 524 + 761 + 466 + + + 0 + 0 + + Settings @@ -20,41 +26,37 @@ true - + - + 0 0 + + + 25 + 0 + + + + Qt::LeftToRight + - 1 + 0 true - My Profile + User Setup - + - - - - - Qt::Horizontal - - - - 0 - 20 - - - - + @@ -68,15 +70,15 @@ 20 - 13 + 0 - + - Musician's Profile + My Profile @@ -85,6 +87,12 @@ + + + 0 + 0 + + Alias/Name @@ -92,6 +100,12 @@ + + + 0 + 0 + + Instrument @@ -99,6 +113,12 @@ + + + 0 + 0 + + Country/Region @@ -106,6 +126,12 @@ + + + 0 + 0 + + City @@ -113,6 +139,12 @@ + + + 0 + 0 + + Skill @@ -125,7 +157,7 @@ - + 0 0 @@ -140,6 +172,12 @@ + + + 0 + 0 + + 150 @@ -150,6 +188,12 @@ + + + 0 + 0 + + 150 @@ -161,7 +205,7 @@ - + 0 0 @@ -176,6 +220,12 @@ + + + 0 + 0 + + 150 @@ -196,10 +246,13 @@ Qt::Vertical + + QSizePolicy::Preferred + - 20 - 40 + 0 + 0 @@ -247,13 +300,33 @@ - + + + + 0 + 0 + + + - + + + + 0 + 0 + + + + + + 0 + 0 + + 150 @@ -265,7 +338,7 @@ - + 0 0 @@ -295,157 +368,238 @@ - 20 - 14 + 0 + 0 + + + - - - Qt::Horizontal - - - - 0 - 20 - - - - - - - - - - - Audio/Network Setup - - - - - - - - - - Audio Device - - - + - - - - 0 - 0 - - - - - 0 - 0 - + + + New Client Level + + + + + + + + + + + + % + + + + + + + + - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 10 - - - - - - - - - 0 - 0 - - - - Driver Setup - - - false + + + Custom Directories + + + + + + + true + + + + + + - + Qt::Vertical 20 - 10 + 40 - - - - - 0 - 0 - - - - QFrame::NoFrame - - - QFrame::Plain - - - - 0 - - - 0 - - - 0 - - - 0 + + + + + + + + + Audio Setup + + + + + + + 0 + 0 + + + + + 0 + 275 + + + + Audio Device + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + QLayout::SetDefaultConstraint + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + QLayout::SetMinimumSize + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Driver Setup + + + false + + + + + + + + 16777215 + 25 + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + 0 + 0 + + Input Channel Mapping + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + - - - 3 - + - + + + QLayout::SetFixedSize + - + 0 0 + + + 0 + 0 + + + + + 16777215 + 16777215 + + L @@ -454,11 +608,23 @@ - + 0 0 + + + 0 + 0 + + + + + 16777215 + 16777215 + + R @@ -467,34 +633,78 @@ - - - 3 - + - - - - - - + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 25 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 25 + + + + + + + + 0 + 0 + + Output Channel Mapping + + Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft + - - - 3 - + - + + + + @@ -503,6 +713,18 @@ 0 + + + 0 + 0 + + + + + 16777215 + 16777215 + + L @@ -516,6 +738,18 @@ 0 + + + 0 + 0 + + + + + 16777215 + 16777215 + + R @@ -524,742 +758,843 @@ - - - 3 - + - + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 25 + + + - + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 25 + + + - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 10 - - - - - - - - + + + + + + + + + Input Boost + + - - - Audio Channels - - + + + + + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + 0 + 0 + + + + FeedBack Protection + + - - - Audio Quality - - + + + + + + 0 + 0 + + + + Enable + + + + - - - + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + 0 + 0 + + + + Audio Mode + + - + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Channels + + + Qt::PlainText + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Quality + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + 200 + 0 + + + + Audio Stream Rate + + + + 2 + - + + + + + Qt::Horizontal + + + + 1 + 5 + + + + + + + + kbps + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + val + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 2 + + + + + + + Qt::Horizontal + + + + 1 + 5 + + + + + - - - - - - - Qt::Vertical - - - - 20 - 10 - - - - - - - - Buffer Delay - - + + + + + + + + + + + Qt::Vertical + + + + 20 + 1 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 100 + + + + Input Balance + + + + + + 3 + + + - - - - 0 - 0 - + + + Qt::Horizontal - - (preferred) + + QSizePolicy::Minimum - + + + 0 + 20 + + + - + - + 0 0 - - (default) + + + 0 + 0 + + + + + + + 1 + + + Qt::Horizontal + + + QSlider::TicksBothSides - - - - 0 - 0 - + + + Qt::Horizontal - - (safe) + + QSizePolicy::Minimum - + + + 0 + 20 + + + - - - - + + + + + + 0 + 0 + + + + + 50 + 0 + + + + Center + + + Qt::AlignCenter + + + false + + + + + + + + + + + + + Network Setup + + + + - + - Qt::Horizontal + Qt::Vertical QSizePolicy::MinimumExpanding - 0 - 20 + 20 + 40 - - - - - Jitter Buffer - - + + + + 0 + 0 + + + + Small Network Buffers + + + + - + - + 0 0 + + + 0 + 0 + + + + + 200 + 16777215 + + - Auto + Enable + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Buffer Delay + + + + + + + 0 + 0 + + + + (preferred) + + + + + + + + 0 + 0 + + + + (default) + + + + + + + + 0 + 0 + + + + (safe) + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Jitter Buffer + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + + 16777215 + 16777215 + + + + Auto + + + + + - - - - - Local - - - Qt::AlignCenter - - - false - - - - - - - Server - - - Qt::AlignCenter - - - false - - - - - - - - - - - Size - - - Qt::AlignCenter - - - false - - - - - - - Size - - - Qt::AlignCenter - - - false - - - - + + + Local + + + Qt::AlignCenter + + + false + + - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 0 - 0 - - - - - - - - 1 - - - 20 - - - 1 - - - Qt::Vertical - - - QSlider::TicksBothSides - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 0 - 0 - - - - - - - - 1 - - - 20 - - - 1 - - - Qt::Vertical - - - QSlider::TicksBothSides - - - - + + + Server + + + Qt::AlignCenter + + + false + + - - - - - - - 0 - 0 - - - - Enable Small Network Buffers - - - - - - - Audio Stream Rate - - - - 2 - + + + - - - - - Qt::Horizontal - - - - 1 - 5 - - - - - - - - kbps - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - val - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 2 - - - - - - - Qt::Horizontal - - - - 1 - 5 - - - - - + + + Size + + + Qt::AlignCenter + + + false + + + + + + + Size + + + Qt::AlignCenter + + + false + + - - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 0 - 20 - - - - - - - - - - - Advanced Setup - - - - - - - - - - Qt::Horizontal - - - - 10 - 20 - - - - - - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 17 - 20 - - - - - - - - Custom Directories: - - - - - - - true - - - - - - - Qt::Vertical - - - - 20 - 13 - - - - - - - - New Client Level - - - - - - - - - - - - % - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Input Boost - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Feedback Protection - - - - - - - - 0 - 0 - - - - Enable - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - - 10 - 20 - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 20 - 20 - - - - - - - - Input Balance - - + + + - - - 3 + + + Qt::Horizontal - - - - Pan - - - Qt::AlignCenter - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 0 - 20 - - - - - - - - - 0 - 0 - - - - - 0 - 0 - - - - - - - 1 - - - Qt::Horizontal - - - QSlider::TicksBothSides - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 0 - 20 - - - - - - - - - - - 50 - 0 - - - - Center - - - Qt::AlignCenter - - - false - - - - + + QSizePolicy::Minimum + + + + 0 + 0 + + + + + + + + 1 + + + 20 + + + 1 + + + Qt::Vertical + + + QSlider::TicksBothSides + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 0 + 0 + + + + + + + + 1 + + + 20 + + + 1 + + + Qt::Vertical + + + QSlider::TicksBothSides + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 0 + 0 + + + - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 20 - 20 - - - - - + + + - - - - Qt::Vertical - - - - 367 - 13 - - - - @@ -1283,26 +1618,6 @@ cbxMeterStyle cbxLanguage spnMixerRows - cbxSoundcard - butDriverSetup - cbxLInChan - cbxRInChan - cbxLOutChan - cbxROutChan - cbxAudioChannels - cbxAudioQuality - rbtBufferDelayPreferred - rbtBufferDelayDefault - rbtBufferDelaySafe - chbAutoJitBuf - sldNetBuf - sldNetBufServer - chbEnableOPUS64 - cbxCustomDirectories - edtNewClientLevel - cbxInputBoost - chbDetectFeedback - sldAudioPan diff --git a/src/global.h b/src/global.h index 68e58e9b8f..f34380348a 100644 --- a/src/global.h +++ b/src/global.h @@ -71,11 +71,38 @@ LED bar: lbr //#define _DEBUG_ #undef _DEBUG_ +// html text macro's: +#define htmlBold( T ) "" + T + "" +#define htmlNewLine() "
" + // version and application name (use version from qt prject file) #undef VERSION #define VERSION APP_VERSION #define APP_NAME "Jamulus" +// Sound Definitions +// stereo for input and output on protocol +#define PROT_NUM_IN_CHANNELS 2 +#define PROT_NUM_OUT_CHANNELS 2 + +// minimum driver requirements +#define DRV_MIN_IN_CHANNELS 1 +#define DRV_MIN_OUT_CHANNELS 2 + +// define the maximum number of audio channel for input/output we can store +// channel infos for (and therefore this is the maximum number of entries in +// the channel selection combo box regardless of the actual available number +// of channels by the audio device) +// pgScorpio: No longer a limit in CSoundBase, should also be no limit in CSound, but it still is a limit used in settings.... +#define DRV_MAX_NUM_IN_CHANNELS 64 +#define DRV_MAX_NUM_OUT_CHANNELS 64 + +// maximum number of recognized sound cards installed in the system +#define DRV_MAX_NUM_DEVICES 129 // e.g. 16 inputs, 8 outputs + default entry (MacOS) +// pgScorpio: was DRV_MAX_NUM_DEVICES + +#define DRV_MAX_INPUT_GAIN 10 + // Windows registry key name of auto run entry for the server #define AUTORUN_SERVER_REG_NAME "Jamulus server" @@ -178,15 +205,6 @@ LED bar: lbr // maximum number of fader groups (must be consistent to audiomixerboard implementation) #define MAX_NUM_FADER_GROUPS 8 -// maximum number of recognized sound cards installed in the system -#define MAX_NUMBER_SOUND_CARDS 129 // e.g. 16 inputs, 8 outputs + default entry (MacOS) - -// define the maximum number of audio channel for input/output we can store -// channel infos for (and therefore this is the maximum number of entries in -// the channel selection combo box regardless of the actual available number -// of channels by the audio device) -#define MAX_NUM_IN_OUT_CHANNELS 64 - // maximum number of elemts in the server address combo box #define MAX_NUM_SERVER_ADDR_ITEMS 12 @@ -260,9 +278,9 @@ LED bar: lbr #define MAX_LEN_VERSION_TEXT 30 // define Settings tab indexes -#define SETTING_TAB_USER 0 -#define SETTING_TAB_AUDIONET 1 -#define SETTING_TAB_ADVANCED 2 +#define SETTING_TAB_USER 0 +#define SETTING_TAB_AUDIO 1 +#define SETTING_TAB_NETWORK 2 // common tool tip bottom line text #define TOOLTIP_COM_END_TEXT \ @@ -351,17 +369,174 @@ class CCustomEvent : public QEvent /* Prototypes for global functions ********************************************/ // command line parsing, TODO do not declare functions globally but in a class -QString UsageArguments ( char** argv ); +// pgScorpio: A Class is there now ! +// (Still containing these functions) -bool GetFlagArgument ( char** argv, int& i, QString strShortOpt, QString strLongOpt ); +extern QString UsageArguments ( QString appPath ); -bool GetStringArgument ( int argc, char** argv, int& i, QString strShortOpt, QString strLongOpt, QString& strArg ); +//============================================================================ +// CCommandlineOptions class: +// Note that passing commandline arguments to classes is no longer required, +// since an instance of this class can get commandline options anywhere. +//============================================================================ + +class CCommandlineOptions +{ +public: + CCommandlineOptions() { reset(); } + +private: + friend int main ( int argc, char** argv ); + + // Statics assigned from main () + static int appArgc; + static char** appArgv; + +protected: + // GetFirstArgument/GetNextArgument: + int currentIndex; + char** currentArgv; + + void reset() + { + currentArgv = appArgv; + currentIndex = 0; + } + +public: + // GetFirstArgument/GetNextArgument: + + QString GetFirstArgument() + { + reset(); + // Skip program path + return GetNextArgument(); + } + + QString GetNextArgument() + { + if ( currentIndex < appArgc ) + { + currentArgv++; + currentIndex++; + + if ( currentIndex < appArgc ) + { + return QString ( *currentArgv ); + } + } + + return QString(); + } + +public: + QString GetProgramPath() { return QString ( *appArgv ); } + +public: + // sequencial parse functions: + + bool GetFlagArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt ) const; + + bool GetStringArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt, QString& strArg ) const; + + bool GetNumericArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt, double rRangeStart, double rRangeStop, double& rValue ) + const; + +public: + // Get specific argument: + + bool GetFlagArgument ( const QString& strShortOpt, const QString& strLongOpt ) const + { + int i = 1; + while ( i < appArgc ) + { + if ( GetFlagArgument ( i, strShortOpt, strLongOpt ) ) + { + return true; + } + + i++; + } + + return false; + } + + bool GetStringArgument ( const QString& strShortOpt, const QString& strLongOpt, QString& strArg ) const + { + int i = 1; + while ( i < appArgc ) + { + if ( GetStringArgument ( i, strShortOpt, strLongOpt, strArg ) ) + { + return true; + } + + i++; + } + + return false; + } + + bool GetNumericArgument ( const QString& strShortOpt, const QString& strLongOpt, double rRangeStart, double rRangeStop, double& rValue ) const + { + int i = 1; + while ( i < appArgc ) + { + if ( GetNumericArgument ( i, strShortOpt, strLongOpt, rRangeStart, rRangeStop, rValue ) ) + { + return true; + } + + i++; + } + + return false; + } +}; -bool GetNumericArgument ( int argc, - char** argv, - int& i, - QString strShortOpt, - QString strLongOpt, - double rRangeStart, - double rRangeStop, - double& rValue ); +// defines for commandline options in the style "shortopt", "longopt" +// Name is standard CMDLN_LONGOPTNAME +// These defines can be used for strShortOpt, strLongOpt parameters +// of the CCommandlineOptions functions. +// +// clang-format off +#define CMDLN_SERVER "-s", "--server" +#define CMDLN_INIFILE "-i", "--inifile" +#define CMDLN_NOGUI "-n", "--nogui" +#define CMDLN_PORT "-p", "--port" +#define CMDLN_QOS "-Q", "--qos" +#define CMDLN_NOTRANSLATION "-t", "--notranslation" +#define CMDLN_ENABLEIPV6 "-6", "--enableipv6" +#define CMDLN_DISCONONQUIT "-d", "--discononquit" +#define CMDLN_DIRECTORYSERVER "-e", "--directoryserver" +#define CMDLN_DIRECTORYFILE "--directoryfile", "--directoryfile" +#define CMDLN_LISTFILTER "-f", "--listfilter" +#define CMDLN_FASTUPDATE "-F", "--fastupdate" +#define CMDLN_LOG "-l", "--log" +#define CMDLN_LICENCE "-L", "--licence" +#define CMDLN_HTMLSTATUS "-m", "--htmlstatus" +#define CMDLN_SERVERINFO "-o", "--serverinfo" +#define CMDLN_SERVERPUBLICIP "--serverpublicip", "--serverpublicip" +#define CMDLN_DELAYPAN "-P", "--delaypan" +#define CMDLN_RECORDING "-R", "--recording" +#define CMDLN_NORECORD "--norecord", "--norecord" +#define CMDLN_SERVERBINDIP "--serverbindip", "--serverbindip" +#define CMDLN_MULTITHREADING "-T", "--multithreading" +#define CMDLN_NUMCHANNELS "-u", "--numchannels" +#define CMDLN_WELCOMEMESSAGE "-w", "--welcomemessage" +#define CMDLN_STARTMINIMIZED "-z", "--startminimized" +#define CMDLN_CONNECT "-c", "--connect" +#define CMDLN_NOJACKCONNECT "-j", "--nojackconnect" +#define CMDLN_MUTESTREAM "-M", "--mutestream" +#define CMDLN_MUTEMYOWN "--mutemyown", "--mutemyown" +#define CMDLN_CLIENTNAME "--clientname", "--clientname" +#define CMDLN_CTRLMIDICH "--ctrlmidich", "--ctrlmidich" +// Backwards compatibilyty: +#define CMDLN_CENTRALSERVER "--centralserver", "--centralserver" +// pgScorpio: TODO These are NOT in help !: +#define CMDLN_SHOWALLSERVERS "--showallservers", "--showallservers" +#define CMDLN_SHOWANALYZERCONSOLE "--showanalyzerconsole", "--showanalyzerconsole" +// CMDLN_SPECIAL: Mostly used for debugging, any option after --special is accepted, should NOT be in help ! +#define CMDLN_SPECIAL "--special", "--special" +#define CMDLN_JACKINPUTS "--jackinputs", "--jackinputs" +// clang-format on diff --git a/src/main.cpp b/src/main.cpp index e63300c678..19a3811de3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -45,8 +45,15 @@ extern void qt_set_sequence_auto_mnemonic ( bool bEnable ); // Implementation ************************************************************** +int CCommandlineOptions::appArgc = 0; +char** CCommandlineOptions::appArgv = NULL; + int main ( int argc, char** argv ) { + CCommandlineOptions::appArgc = argc; + CCommandlineOptions::appArgv = argv; + + CCommandlineOptions cCommandlineOptions; #if defined( Q_OS_MACX ) // Mnemonic keys are default disabled in Qt for MacOS. The following function enables them. @@ -68,6 +75,7 @@ int main ( int argc, char** argv ) #else bool bIsClient = true; #endif + bool bSpecialOptions = false; // Any options after this option will be accepted ! (mostly used for debugging purpouses) bool bUseGUI = true; bool bStartMinimized = false; bool bShowComplRegConnList = false; @@ -115,14 +123,20 @@ int main ( int argc, char** argv ) // QT docu: argv()[0] is the program name, argv()[1] is the first // argument and argv()[argc()-1] is the last argument. // Start with first argument, therefore "i = 1" + + // clang-format off +// pgScorio TODO: +// Extra Checks on parameters: +// If given, CMDLN_SERVER MUST be FIRST parameter. +// And then only check parameters valid for common, server or client ! + // clang-format on + for ( int i = 1; i < argc; i++ ) { - // Help (usage) flag --------------------------------------------------- if ( ( !strcmp ( argv[i], "--help" ) ) || ( !strcmp ( argv[i], "-h" ) ) || ( !strcmp ( argv[i], "-?" ) ) ) { - const QString strHelp = UsageArguments ( argv ); - std::cout << qUtf8Printable ( strHelp ); + std::cout << qUtf8Printable ( UsageArguments ( argv[0] ) ); exit ( 0 ); } @@ -136,7 +150,7 @@ int main ( int argc, char** argv ) // Common options: // Initialization file ------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-i", "--inifile", strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_INIFILE, strArgument ) ) { strIniFileName = strArgument; qInfo() << qUtf8Printable ( QString ( "- initialization file name: %1" ).arg ( strIniFileName ) ); @@ -145,7 +159,7 @@ int main ( int argc, char** argv ) } // Disable GUI flag ---------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-n", "--nogui" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_NOGUI ) ) { bUseGUI = false; qInfo() << "- no GUI mode chosen"; @@ -154,7 +168,7 @@ int main ( int argc, char** argv ) } // Port number --------------------------------------------------------- - if ( GetNumericArgument ( argc, argv, i, "-p", "--port", 0, 65535, rDbleArgument ) ) + if ( cCommandlineOptions.GetNumericArgument ( i, CMDLN_PORT, 0, 65535, rDbleArgument ) ) { iPortNumber = static_cast ( rDbleArgument ); bCustomPortNumberGiven = true; @@ -164,7 +178,7 @@ int main ( int argc, char** argv ) } // Quality of Service -------------------------------------------------- - if ( GetNumericArgument ( argc, argv, i, "-Q", "--qos", 0, 255, rDbleArgument ) ) + if ( cCommandlineOptions.GetNumericArgument ( i, CMDLN_QOS, 0, 255, rDbleArgument ) ) { iQosNumber = static_cast ( rDbleArgument ); qInfo() << qUtf8Printable ( QString ( "- selected QoS value: %1" ).arg ( iQosNumber ) ); @@ -173,7 +187,7 @@ int main ( int argc, char** argv ) } // Disable translations ------------------------------------------------ - if ( GetFlagArgument ( argv, i, "-t", "--notranslation" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_NOTRANSLATION ) ) { bUseTranslation = false; qInfo() << "- translations disabled"; @@ -182,7 +196,7 @@ int main ( int argc, char** argv ) } // Enable IPv6 --------------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-6", "--enableipv6" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_ENABLEIPV6 ) ) { bEnableIPv6 = true; qInfo() << "- IPv6 enabled"; @@ -193,7 +207,7 @@ int main ( int argc, char** argv ) // Server only: // Disconnect all clients on quit -------------------------------------- - if ( GetFlagArgument ( argv, i, "-d", "--discononquit" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_DISCONONQUIT ) ) { bDisconnectAllClientsOnQuit = true; qInfo() << "- disconnect all clients on quit"; @@ -203,22 +217,9 @@ int main ( int argc, char** argv ) } // Directory server ---------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-e", "--directoryserver", strArgument ) ) - { - strDirectoryServer = strArgument; - qInfo() << qUtf8Printable ( QString ( "- directory server: %1" ).arg ( strDirectoryServer ) ); - CommandLineOptions << "--directoryserver"; - ServerOnlyOptions << "--directoryserver"; - continue; - } - - // Central server ** D E P R E C A T E D ** ---------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--centralserver", // no short form - "--centralserver", - strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_DIRECTORYSERVER, strArgument ) || + cCommandlineOptions.GetStringArgument ( i, CMDLN_CENTRALSERVER, strArgument ) // ** D E P R E C A T E D ** + ) { strDirectoryServer = strArgument; qInfo() << qUtf8Printable ( QString ( "- directory server: %1" ).arg ( strDirectoryServer ) ); @@ -228,12 +229,7 @@ int main ( int argc, char** argv ) } // Directory file ------------------------------------------------------ - if ( GetStringArgument ( argc, - argv, - i, - "--directoryfile", // no short form - "--directoryfile", - strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_DIRECTORYFILE, strArgument ) ) { strServerListFileName = strArgument; qInfo() << qUtf8Printable ( QString ( "- directory server persistence file: %1" ).arg ( strServerListFileName ) ); @@ -243,7 +239,7 @@ int main ( int argc, char** argv ) } // Server list filter -------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-f", "--listfilter", strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_LISTFILTER, strArgument ) ) { strServerListFilter = strArgument; qInfo() << qUtf8Printable ( QString ( "- server list filter: %1" ).arg ( strServerListFilter ) ); @@ -253,7 +249,7 @@ int main ( int argc, char** argv ) } // Use 64 samples frame size mode -------------------------------------- - if ( GetFlagArgument ( argv, i, "-F", "--fastupdate" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_FASTUPDATE ) ) { bUseDoubleSystemFrameSize = false; // 64 samples frame size qInfo() << qUtf8Printable ( QString ( "- using %1 samples frame size mode" ).arg ( SYSTEM_FRAME_SIZE_SAMPLES ) ); @@ -263,7 +259,7 @@ int main ( int argc, char** argv ) } // Use logging --------------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-l", "--log", strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_LOG, strArgument ) ) { strLoggingFileName = strArgument; qInfo() << qUtf8Printable ( QString ( "- logging file name: %1" ).arg ( strLoggingFileName ) ); @@ -273,7 +269,7 @@ int main ( int argc, char** argv ) } // Use licence flag ---------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-L", "--licence" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_LICENCE ) ) { // LT_CREATIVECOMMONS is now used just to enable the pop up eLicenceType = LT_CREATIVECOMMONS; @@ -284,7 +280,7 @@ int main ( int argc, char** argv ) } // HTML status file ---------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-m", "--htmlstatus", strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_HTMLSTATUS, strArgument ) ) { strHTMLStatusFileName = strArgument; qInfo() << qUtf8Printable ( QString ( "- HTML status file name: %1" ).arg ( strHTMLStatusFileName ) ); @@ -294,7 +290,7 @@ int main ( int argc, char** argv ) } // Server info --------------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-o", "--serverinfo", strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_SERVERINFO, strArgument ) ) { strServerInfo = strArgument; qInfo() << qUtf8Printable ( QString ( "- server info: %1" ).arg ( strServerInfo ) ); @@ -304,12 +300,7 @@ int main ( int argc, char** argv ) } // Server Public IP ---------------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--serverpublicip", // no short form - "--serverpublicip", - strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_SERVERPUBLICIP, strArgument ) ) { strServerPublicIP = strArgument; qInfo() << qUtf8Printable ( QString ( "- server public IP: %1" ).arg ( strServerPublicIP ) ); @@ -319,7 +310,7 @@ int main ( int argc, char** argv ) } // Enable delay panning on startup ------------------------------------- - if ( GetFlagArgument ( argv, i, "-P", "--delaypan" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_DELAYPAN ) ) { bDelayPan = true; qInfo() << "- starting with delay panning"; @@ -329,7 +320,7 @@ int main ( int argc, char** argv ) } // Recording directory ------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-R", "--recording", strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_RECORDING, strArgument ) ) { strRecordingDirName = strArgument; qInfo() << qUtf8Printable ( QString ( "- recording directory name: %1" ).arg ( strRecordingDirName ) ); @@ -339,10 +330,7 @@ int main ( int argc, char** argv ) } // Disable recording on startup ---------------------------------------- - if ( GetFlagArgument ( argv, - i, - "--norecord", // no short form - "--norecord" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_NORECORD ) ) { bDisableRecording = true; qInfo() << "- recording will not take place until enabled"; @@ -352,7 +340,7 @@ int main ( int argc, char** argv ) } // Server mode flag ---------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-s", "--server" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_SERVER ) ) { bIsClient = false; qInfo() << "- server mode chosen"; @@ -362,12 +350,7 @@ int main ( int argc, char** argv ) } // Server Bind IP -------------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--serverbindip", // no short form - "--serverbindip", - strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_SERVERBINDIP, strArgument ) ) { strServerBindIP = strArgument; qInfo() << qUtf8Printable ( QString ( "- server bind IP: %1" ).arg ( strServerBindIP ) ); @@ -377,7 +360,7 @@ int main ( int argc, char** argv ) } // Use multithreading -------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-T", "--multithreading" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_MULTITHREADING ) ) { bUseMultithreading = true; qInfo() << "- using multithreading"; @@ -387,7 +370,7 @@ int main ( int argc, char** argv ) } // Maximum number of channels ------------------------------------------ - if ( GetNumericArgument ( argc, argv, i, "-u", "--numchannels", 1, MAX_NUM_CHANNELS, rDbleArgument ) ) + if ( cCommandlineOptions.GetNumericArgument ( i, CMDLN_NUMCHANNELS, 1, MAX_NUM_CHANNELS, rDbleArgument ) ) { iNumServerChannels = static_cast ( rDbleArgument ); @@ -399,7 +382,7 @@ int main ( int argc, char** argv ) } // Server welcome message ---------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-w", "--welcomemessage", strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_WELCOMEMESSAGE, strArgument ) ) { strWelcomeMessage = strArgument; qInfo() << qUtf8Printable ( QString ( "- welcome message: %1" ).arg ( strWelcomeMessage ) ); @@ -409,7 +392,7 @@ int main ( int argc, char** argv ) } // Start minimized ----------------------------------------------------- - if ( GetFlagArgument ( argv, i, "-z", "--startminimized" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_STARTMINIMIZED ) ) { bStartMinimized = true; qInfo() << "- start minimized enabled"; @@ -421,7 +404,7 @@ int main ( int argc, char** argv ) // Client only: // Connect on startup -------------------------------------------------- - if ( GetStringArgument ( argc, argv, i, "-c", "--connect", strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_CONNECT, strArgument ) ) { strConnOnStartupAddress = NetworkUtil::FixAddress ( strArgument ); qInfo() << qUtf8Printable ( QString ( "- connect on startup to address: %1" ).arg ( strConnOnStartupAddress ) ); @@ -431,7 +414,7 @@ int main ( int argc, char** argv ) } // Disabling auto Jack connections ------------------------------------- - if ( GetFlagArgument ( argv, i, "-j", "--nojackconnect" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_NOJACKCONNECT ) ) { bNoAutoJackConnect = true; qInfo() << "- disable auto Jack connections"; @@ -441,7 +424,7 @@ int main ( int argc, char** argv ) } // Mute stream on startup ---------------------------------------------- - if ( GetFlagArgument ( argv, i, "-M", "--mutestream" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_MUTESTREAM ) ) { bMuteStream = true; qInfo() << "- mute stream activated"; @@ -451,10 +434,7 @@ int main ( int argc, char** argv ) } // For headless client mute my own signal in personal mix -------------- - if ( GetFlagArgument ( argv, - i, - "--mutemyown", // no short form - "--mutemyown" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_MUTEMYOWN ) ) { bMuteMeInPersonalMix = true; qInfo() << "- mute me in my personal mix"; @@ -464,12 +444,7 @@ int main ( int argc, char** argv ) } // Client Name --------------------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--clientname", // no short form - "--clientname", - strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_CLIENTNAME, strArgument ) ) { strClientName = strArgument; qInfo() << qUtf8Printable ( QString ( "- client name: %1" ).arg ( strClientName ) ); @@ -479,12 +454,7 @@ int main ( int argc, char** argv ) } // Controller MIDI channel --------------------------------------------- - if ( GetStringArgument ( argc, - argv, - i, - "--ctrlmidich", // no short form - "--ctrlmidich", - strArgument ) ) + if ( cCommandlineOptions.GetStringArgument ( i, CMDLN_CTRLMIDICH, strArgument ) ) { strMIDISetup = strArgument; qInfo() << qUtf8Printable ( QString ( "- MIDI controller settings: %1" ).arg ( strMIDISetup ) ); @@ -499,10 +469,7 @@ int main ( int argc, char** argv ) // Undocumented debugging command line argument: Show all registered // servers in the server list regardless if a ping to the server is // possible or not. - if ( GetFlagArgument ( argv, - i, - "--showallservers", // no short form - "--showallservers" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_SHOWALLSERVERS ) ) { bShowComplRegConnList = true; qInfo() << "- show all registered servers in server list"; @@ -514,10 +481,7 @@ int main ( int argc, char** argv ) // Show analyzer console ----------------------------------------------- // Undocumented debugging command line argument: Show the analyzer // console to debug network buffer properties. - if ( GetFlagArgument ( argv, - i, - "--showanalyzerconsole", // no short form - "--showanalyzerconsole" ) ) + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_SHOWANALYZERCONSOLE ) ) { bShowAnalyzerConsole = true; qInfo() << "- show analyzer console"; @@ -526,14 +490,27 @@ int main ( int argc, char** argv ) continue; } + // Enable Special Options ---------------------------------------------- + if ( cCommandlineOptions.GetFlagArgument ( i, CMDLN_SPECIAL ) ) + { + bSpecialOptions = true; + qInfo() << "- Special options enabled !"; + continue; + } + // Unknown option ------------------------------------------------------ qCritical() << qUtf8Printable ( QString ( "%1: Unknown option '%2' -- use '--help' for help" ).arg ( argv[0] ).arg ( argv[i] ) ); + // pgScorpio: No exit for options after the "--special" option. + // Used for debugging and testing new options... + if ( !bSpecialOptions ) + { // clicking on the Mac application bundle, the actual application // is called with weird command line args -> do not exit on these #if !( defined( Q_OS_MACX ) ) - exit ( 1 ); + exit ( 1 ); #endif + } } // Dependencies ------------------------------------------------------------ @@ -796,14 +773,7 @@ int main ( int argc, char** argv ) { // Client: // actual client object - CClient Client ( iPortNumber, - iQosNumber, - strConnOnStartupAddress, - strMIDISetup, - bNoAutoJackConnect, - strClientName, - bEnableIPv6, - bMuteMeInPersonalMix ); + CClient Client ( iPortNumber, iQosNumber, strConnOnStartupAddress, strClientName, bEnableIPv6, bMuteMeInPersonalMix ); // load settings from init-file (command line options override) CClientSettings Settings ( &Client, strIniFileName ); @@ -823,7 +793,7 @@ int main ( int argc, char** argv ) CClientDlg ClientDlg ( &Client, &Settings, strConnOnStartupAddress, - strMIDISetup, + !strMIDISetup.isEmpty(), bShowComplRegConnList, bShowAnalyzerConsole, bMuteStream, @@ -939,7 +909,7 @@ int main ( int argc, char** argv ) /******************************************************************************\ * Command Line Argument Parsing * \******************************************************************************/ -QString UsageArguments ( char** argv ) +QString UsageArguments ( QString appPath ) { // clang-format off return QString ( @@ -949,17 +919,8 @@ QString UsageArguments ( char** argv ) " -h, -?, --help display this help text and exit\n" " -v, --version display version information and exit\n" "\n" - "Common options:\n" - " -i, --inifile initialization file name\n" - " (not supported for headless server mode)\n" - " -n, --nogui disable GUI (\"headless\")\n" - " -p, --port set the local port number\n" - " -Q, --qos set the QoS value. Default is 128. Disable with 0\n" - " (see the Jamulus website to enable QoS on Windows)\n" - " -t, --notranslation disable translation (use English language)\n" - " -6, --enableipv6 enable IPv6 addressing (IPv4 is always enabled)\n" - "\n" "Server only:\n" + " -s, --server start as server\n" " -d, --discononquit disconnect all clients on quit\n" " -e, --directoryserver address of the directory server with which to register\n" " (or 'localhost' to host a server list on this server)\n" @@ -978,7 +939,6 @@ QString UsageArguments ( char** argv ) " -P, --delaypan start with delay panning enabled\n" " -R, --recording sets directory to contain recorded jams\n" " --norecord disables recording (when enabled by default by -R)\n" - " -s, --server start server\n" " --serverbindip IP address the server will bind to (rather than all)\n" " -T, --multithreading use multithreading to make better use of\n" " multi-core CPUs and support more clients\n" @@ -995,17 +955,27 @@ QString UsageArguments ( char** argv ) " --clientname client name (window title and jack client name)\n" " --ctrlmidich MIDI controller channel to listen\n" "\n" + "Common options:\n" + " -i, --inifile initialization file name\n" + " (not supported for headless server mode)\n" + " -n, --nogui disable GUI (\"headless\")\n" + " -p, --port set the local port number\n" + " -Q, --qos set the QoS value. Default is 128. Disable with 0\n" + " (see the Jamulus website to enable QoS on Windows)\n" + " -t, --notranslation disable translation (use English language)\n" + " -6, --enableipv6 enable IPv6 addressing (IPv4 is always enabled)\n" + "\n" "Example: %1 -s --inifile myinifile.ini\n" "\n" "For more information and localized help see:\n" "https://jamulus.io/wiki/Command-Line-Options\n" - ).arg( argv[0] ); + ).arg( appPath ); // clang-format on } -bool GetFlagArgument ( char** argv, int& i, QString strShortOpt, QString strLongOpt ) +bool CCommandlineOptions::GetFlagArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt ) const { - if ( ( !strShortOpt.compare ( argv[i] ) ) || ( !strLongOpt.compare ( argv[i] ) ) ) + if ( ( !strShortOpt.compare ( appArgv[i] ) ) || ( !strLongOpt.compare ( appArgv[i] ) ) ) { return true; } @@ -1015,17 +985,17 @@ bool GetFlagArgument ( char** argv, int& i, QString strShortOpt, QString strLong } } -bool GetStringArgument ( int argc, char** argv, int& i, QString strShortOpt, QString strLongOpt, QString& strArg ) +bool CCommandlineOptions::GetStringArgument ( int& i, const QString& strShortOpt, const QString& strLongOpt, QString& strArg ) const { - if ( ( !strShortOpt.compare ( argv[i] ) ) || ( !strLongOpt.compare ( argv[i] ) ) ) + if ( ( !strShortOpt.compare ( appArgv[i] ) ) || ( !strLongOpt.compare ( appArgv[i] ) ) ) { - if ( ++i >= argc ) + if ( ++i >= appArgc ) { - qCritical() << qUtf8Printable ( QString ( "%1: '%2' needs a string argument." ).arg ( argv[0] ).arg ( argv[i - 1] ) ); + qCritical() << qUtf8Printable ( QString ( "%1: '%2' needs a string argument." ).arg ( appArgv[0] ).arg ( appArgv[i - 1] ) ); exit ( 1 ); } - strArg = argv[i]; + strArg = appArgv[i]; return true; } @@ -1035,29 +1005,27 @@ bool GetStringArgument ( int argc, char** argv, int& i, QString strShortOpt, QSt } } -bool GetNumericArgument ( int argc, - char** argv, - int& i, - QString strShortOpt, - QString strLongOpt, - double rRangeStart, - double rRangeStop, - double& rValue ) +bool CCommandlineOptions::GetNumericArgument ( int& i, + const QString& strShortOpt, + const QString& strLongOpt, + double rRangeStart, + double rRangeStop, + double& rValue ) const { - if ( ( !strShortOpt.compare ( argv[i] ) ) || ( !strLongOpt.compare ( argv[i] ) ) ) + if ( ( !strShortOpt.compare ( appArgv[i] ) ) || ( !strLongOpt.compare ( appArgv[i] ) ) ) { QString errmsg = "%1: '%2' needs a numeric argument from '%3' to '%4'."; - if ( ++i >= argc ) + if ( ++i >= appArgc ) { - qCritical() << qUtf8Printable ( errmsg.arg ( argv[0] ).arg ( argv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); + qCritical() << qUtf8Printable ( errmsg.arg ( appArgv[0] ).arg ( appArgv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); exit ( 1 ); } char* p; - rValue = strtod ( argv[i], &p ); + rValue = strtod ( appArgv[i], &p ); if ( *p || ( rValue < rRangeStart ) || ( rValue > rRangeStop ) ) { - qCritical() << qUtf8Printable ( errmsg.arg ( argv[0] ).arg ( argv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); + qCritical() << qUtf8Printable ( errmsg.arg ( appArgv[0] ).arg ( appArgv[i - 1] ).arg ( rRangeStart ).arg ( rRangeStop ) ); exit ( 1 ); } diff --git a/src/settings.cpp b/src/settings.cpp index eb8884a9f5..48dcca4f99 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -331,41 +331,32 @@ void CClientSettings::ReadSettingsFromXML ( const QDomDocument& IniXMLDocument, } // sound card selection - const QString strError = pClient->SetSndCrdDev ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "auddev_base64", "" ) ) ); - - if ( !strError.isEmpty() ) - { -#ifndef HEADLESS - // special case: when settings are loaded no GUI is yet created, therefore - // we have to create a warning message box here directly - QMessageBox::warning ( nullptr, APP_NAME, strError ); -#endif - } + pClient->SetSndCrdDev ( FromBase64ToString ( GetIniSetting ( IniXMLDocument, "client", "auddev_base64", "" ) ) ); // sound card channel mapping settings: make sure these settings are // set AFTER the sound card device is set, otherwise the settings are // overwritten by the defaults // // sound card left input channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinlch", 0, DRV_MAX_NUM_IN_CHANNELS - 1, iValue ) ) { pClient->SetSndCrdLeftInputChannel ( iValue ); } // sound card right input channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdinrch", 0, DRV_MAX_NUM_IN_CHANNELS - 1, iValue ) ) { pClient->SetSndCrdRightInputChannel ( iValue ); } // sound card left output channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutlch", 0, DRV_MAX_NUM_OUT_CHANNELS - 1, iValue ) ) { pClient->SetSndCrdLeftOutputChannel ( iValue ); } // sound card right output channel mapping - if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", 0, MAX_NUM_IN_OUT_CHANNELS - 1, iValue ) ) + if ( GetNumericIniSet ( IniXMLDocument, "client", "sndcrdoutrch", 0, DRV_MAX_NUM_OUT_CHANNELS - 1, iValue ) ) { pClient->SetSndCrdRightOutputChannel ( iValue ); } @@ -501,7 +492,7 @@ else if ( GetNumericIniSet ( IniXMLDocument, "client", "centservaddrtype", 0, st } else { - // if directory is not set to custom, or if no custom directory index is found in the settings .ini file, then initialize to zero + // if directory is not set to custom, or if no custom directory index is found in the settings .ini file, then initialise to zero iCustomDirectoryIndex = 0; } diff --git a/src/settings.h b/src/settings.h index f7f4e8ec81..6f2e66a0a3 100644 --- a/src/settings.h +++ b/src/settings.h @@ -120,7 +120,7 @@ class CClientSettings : public CSettings vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ), iNewClientFaderLevel ( 100 ), iInputBoost ( 1 ), - iSettingsTab ( SETTING_TAB_AUDIONET ), + iSettingsTab ( SETTING_TAB_AUDIO ), bConnectDlgShowAllMusicians ( true ), eChannelSortType ( ST_NO_SORT ), iNumMixerPanelRows ( 1 ), diff --git a/src/soundbase.cpp b/src/soundbase.cpp index f91375183f..a9d47df0f5 100644 --- a/src/soundbase.cpp +++ b/src/soundbase.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2004-2022 * * Author(s): - * Volker Fischer + * Volker Fischer, revised and maintained by Peter Goderie (pgScorpio) * ****************************************************************************** * @@ -35,207 +35,848 @@ char const sMidiCtlChar[] = { /* [EMidiCtlType::MuteMyself] = */ 'o', /* [EMidiCtlType::None] = */ '\0' }; -/* Implementation *************************************************************/ -CSoundBase::CSoundBase ( const QString& strNewSystemDriverTechniqueName, - void ( *fpNewProcessCallback ) ( CVector& psData, void* pParg ), - void* pParg, - const QString& strMIDISetup ) : - fpProcessCallback ( fpNewProcessCallback ), - pProcessCallbackArg ( pParg ), - bRun ( false ), - bCallbackEntered ( false ), - strSystemDriverTechniqueName ( strNewSystemDriverTechniqueName ), - iCtrlMIDIChannel ( INVALID_MIDI_CH ), - aMidiCtls ( 128 ) +//============================================================================ +// Static helpers: +//============================================================================ + +//==================================== +// Flip bits: +//==================================== + +int16_t CSoundBase::Flip16Bits ( const int16_t iIn ) { - // parse the MIDI setup command line argument string - ParseCommandLineArgument ( strMIDISetup ); + uint16_t iMask = ( 1 << 15 ); + int16_t iOut = 0; - // initializations for the sound card names (default) - lNumDevs = 1; - strDriverNames[0] = strSystemDriverTechniqueName; + for ( unsigned int i = 0; i < 16; i++ ) + { + // copy current bit to correct position + iOut |= ( iIn & iMask ) ? 1 : 0; + + // shift out value and mask by one bit + iOut <<= 1; + iMask >>= 1; + } - // set current device - strCurDevName = ""; // default device + return iOut; } -void CSoundBase::Stop() +int32_t CSoundBase::Flip32Bits ( const int32_t iIn ) { - // set flag so that thread can leave the main loop - bRun = false; + uint32_t iMask = ( static_cast ( 1 ) << 31 ); + int32_t iOut = 0; - // wait for draining the audio process callback - QMutexLocker locker ( &MutexAudioProcessCallback ); + for ( unsigned int i = 0; i < 32; i++ ) + { + // copy current bit to correct position + iOut |= ( iIn & iMask ) ? 1 : 0; + + // shift out value and mask by one bit + iOut <<= 1; + iMask >>= 1; + } + + return iOut; } -/******************************************************************************\ -* Device handling * -\******************************************************************************/ -QStringList CSoundBase::GetDevNames() +int64_t CSoundBase::Flip64Bits ( const int64_t iIn ) +{ + uint64_t iMask = ( static_cast ( 1 ) << 63 ); + int64_t iOut = 0; + + for ( unsigned int i = 0; i < 64; i++ ) + { + // copy current bit to correct position + iOut |= ( iIn & iMask ) ? 1 : 0; + + // shift out value and mask by one bit + iOut <<= 1; + iMask >>= 1; + } + + return iOut; +} + +//============================================================================ +// Messageboxes: (pgScorpio: TODO Fing global solution) +//============================================================================ + +void CSoundBase::showError ( QString strError ) +{ + if ( !strError.isEmpty() ) + { +#ifndef HEADLESS + QString strTitle = APP_NAME; + if ( strClientName.size() ) + { + strTitle += " "; + strTitle += strClientName; + } + + QMessageBox::critical ( NULL, strTitle + ": " + tr ( "Error" ), strError, tr ( "Ok" ), nullptr ); +#endif + } +} + +void CSoundBase::showWarning ( QString strWarning ) +{ + if ( !strWarning.isEmpty() ) + { +#ifndef HEADLESS + QString strTitle = APP_NAME; + if ( strClientName.size() ) + { + strTitle += " "; + strTitle += strClientName; + } + + QMessageBox::warning ( NULL, strTitle + ": " + tr ( "Warning" ), strWarning, tr ( "Ok" ), nullptr ); +#endif + } +} + +void CSoundBase::showInfo ( QString strInfo ) +{ + if ( !strInfo.isEmpty() ) + { +#ifndef HEADLESS + QString strTitle = APP_NAME; + if ( strClientName.size() ) + { + strTitle += " "; + strTitle += strClientName; + } + + QMessageBox::information ( NULL, strTitle + ": " + tr ( "Information" ), strInfo, tr ( "Ok" ), nullptr ); +#endif + } +} + +//============================================================================ +// CSoundProperties: +//============================================================================ + +CSoundProperties::CSoundProperties() : + bHasAudioDeviceSelection ( true ), + bHasSetupDialog ( false ), + bHasInputChannelSelection ( true ), + bHasOutputChannelSelection ( true ), + bHasInputGainSelection ( true ) +{ + strSetupButtonText = CSoundBase::tr ( "Device Settings" ), + strSetupButtonWhatsThis = htmlBold ( CSoundBase::tr ( "Sound card driver settings:" ) ) + htmlNewLine() + + CSoundBase::tr ( "This opens the driver settings of your sound card. Some drivers " + "allow you to change buffer settings, others like ASIO4ALL " + "lets you choose input or outputs of your device(s). " + "More information can be found on jamulus.io." ); + strSetupButtonAccessibleName = CSoundBase::tr ( "Device Settings push button" ); + + strAudioDeviceWhatsThis = ( "" + CSoundBase::tr ( "Audio Device" ) + ": " + + CSoundBase::tr ( "Your audio device (sound card) can be " + "selected or using %1. If the selected driver is not valid an error " + "message will be shown. " ) + .arg ( APP_NAME ) + + "
" + + CSoundBase::tr ( "If the driver is selected during an active connection, the connection " + "is stopped, the driver is changed and the connection is started again " + "automatically." ) ); + strAudioDeviceAccessibleName = CSoundBase::tr ( "Sound card device selector combo box" ); +} + +//============================================================================ +// CSoundBase: +//============================================================================ + +CSoundBase::CSoundBase ( const QString& systemDriverTechniqueName, + void ( *theProcessCallback ) ( CVector& psData, void* pArg ), + void* theProcessCallbackArg ) : + // iDeviceBufferSize ( 0 ), + // lNumDevices ( 0 ), + // fInOutLatencyMs ( 0.0 ), + // lNumInChan ( 0 ), + // lNumAddedInChan ( 0 ), + // lNumOutChan ( 0 ), + iProtocolBufferSize ( 128 ), + strDriverTechniqueName ( systemDriverTechniqueName ), + strClientName ( "" ), + bAutoConnect ( true ), + bStarted ( false ), + bActive ( false ), + iCurrentDevice ( -1 ), + iCtrlMIDIChannel ( INVALID_MIDI_CH ), + aMidiCtls ( 128 ), + fpProcessCallback ( theProcessCallback ), + pProcessCallbackArg ( theProcessCallbackArg ) { - QMutexLocker locker ( &MutexDevProperties ); + CCommandlineOptions cCommandlineOptions; + + setObjectName ( "CSoundThread" ); - QStringList slDevNames; + cCommandlineOptions.GetStringArgument ( CMDLN_CLIENTNAME, strClientName ); - // put all device names in the string list - for ( int iDev = 0; iDev < lNumDevs; iDev++ ) + QString strMIDISetup; + if ( cCommandlineOptions.GetStringArgument ( CMDLN_CTRLMIDICH, strMIDISetup ) ) { - slDevNames << strDriverNames[iDev]; + parseCommandLineArgument ( strMIDISetup ); } - return slDevNames; + clearDeviceInfo(); + resetInputChannelsGain(); + resetChannelMapping(); + + // setup timers + timerCheckActive.setSingleShot ( true ); // only check once + QObject::connect ( &timerCheckActive, &QTimer::timeout, this, &CSoundBase::OnTimerCheckActive ); +} + +void CSoundBase::OnTimerCheckActive() +{ + if ( !bActive ) + { + emit soundActiveTimeout(); + showWarning ( htmlBold ( tr ( "Your audio device is not working correctly." ) ) + htmlNewLine() + + tr ( "Please check your device settings or try another device." ) ); + } } -QString CSoundBase::SetDev ( const QString strDevName ) +bool CSoundBase::addError ( const QString& strError ) { - QMutexLocker locker ( &MutexDevProperties ); + if ( !strError.isEmpty() ) + { + strErrorList.append ( strError ); + + return true; + } - // init return parameter with "no error" - QString strReturn = ""; + return ( strErrorList.count() > 0 ); +} - // init flag for "load any driver" - bool bTryLoadAnyDriver = false; +void CSoundBase::clearDeviceInfo() +{ + // No current device selection + iCurrentDevice = -1; + // No input channels + lNumInChan = 0; + lNumAddedInChan = 0; + strInputChannelNames.clear(); + // No added output channels + lNumOutChan = 0; // No outnput channels + strOutputChannelNames.clear(); - // check if an ASIO driver was already initialized - if ( !strCurDevName.isEmpty() ) { - // a device was already been initialized and is used, first clean up - // driver - UnloadCurrentDriver(); + // No input selection + memset ( &selectedInputChannels, 0xFF, sizeof ( selectedInputChannels ) ); + // No output selection + memset ( &selectedOutputChannels, 0xFF, sizeof ( selectedOutputChannels ) ); + } - const QString strErrorMessage = LoadAndInitializeDriver ( strDevName, false ); + fInOutLatencyMs = 0.0; +} + +void CSoundBase::resetInputChannelsGain() +{ + QMutexLocker locker ( &mutexAudioProcessCallback ); - if ( !strErrorMessage.isEmpty() ) + for ( unsigned int i = 0; i < PROT_NUM_IN_CHANNELS; i++ ) + { + inputChannelsGain[i] = 1; + } +} + +void CSoundBase::resetChannelMapping() +{ + QMutexLocker locker ( &mutexAudioProcessCallback ); + + if ( PROT_NUM_IN_CHANNELS > 2 ) + { + memset ( selectedInputChannels, 0xFF, sizeof ( selectedInputChannels ) ); + } + + // init selected channel numbers with defaults: use first available + // channels for input and output + selectedInputChannels[0] = 0; + selectedInputChannels[1] = ( lNumInChan > 1 ) ? 1 : 0; + selectedOutputChannels[0] = 0; + selectedOutputChannels[1] = ( lNumOutChan > 1 ) ? 1 : 0; + ; +} + +long CSoundBase::getNumInputChannelsToAdd ( long lNumInChan ) +{ + if ( lNumInChan <= 2 ) + { + return 0; + } + + return ( lNumInChan - 2 ) * 2; +} + +void CSoundBase::getInputSelAndAddChannels ( int iSelChan, long lNumInChan, long lNumAddedInChan, int& iSelChanOut, int& iAddChanOut ) +{ + // we have a mixed channel setup, definitions: + // - mixed channel setup only for 4 physical inputs: + // SelCH == 4: Ch 0 + Ch 2 + // SelCh == 5: Ch 0 + Ch 3 + // SelCh == 6: Ch 1 + Ch 2 + // SelCh == 7: Ch 1 + Ch 3 + + if ( ( iSelChan >= 0 ) && ( iSelChan < ( lNumInChan + lNumAddedInChan ) ) ) + { + if ( iSelChan < lNumInChan ) { - if ( strDevName != strCurDevName ) - { - // loading and initializing the new driver failed, go back to - // original driver and create error message - LoadAndInitializeDriver ( strCurDevName, false ); - - // store error return message - strReturn = QString ( tr ( "Can't use the selected audio device " - "because of the following error: %1 " - "The previous driver will be selected." ) - .arg ( strErrorMessage ) ); - } - else - { - // loading and initializing the current driver failed, try to find - // at least one usable driver - bTryLoadAnyDriver = true; - } + iSelChanOut = iSelChan; + iAddChanOut = INVALID_INDEX; + + return; + } + else + { + long addedChan = ( iSelChan - lNumInChan ); + long numAdds = ( lNumInChan - 2 ); + + iSelChanOut = ( addedChan / numAdds ); + iAddChanOut = ( addedChan % numAdds ) + 2; } + + return; + } + + iSelChanOut = INVALID_INDEX; + iAddChanOut = INVALID_INDEX; +} + +long CSoundBase::addAddedInputChannelNames() +{ + int curInChannelNames = strInputChannelNames.size(); + + if ( ( lNumInChan == 0 ) || ( curInChannelNames == 0 ) || ( curInChannelNames != lNumInChan ) ) + { + // Error !!! lNumInChan and strInputChannelNames must be set and match !! + return -1; + } + + // Determine number of channels to add + lNumAddedInChan = getNumInputChannelsToAdd ( lNumInChan ); + + // Set any added input channel names + for ( int i = 0; i < lNumAddedInChan; i++ ) + { + int iSelChan, iAddChan; + + getInputSelAndAddChannels ( lNumInChan + i, lNumInChan, lNumAddedInChan, iSelChan, iAddChan ); + + strInputChannelNames.append ( strInputChannelNames[iSelChan] + " + " + strInputChannelNames[iAddChan] ); + } + + return lNumAddedInChan; +} + +//============================================================================ +// Public Interface to CClient: +//============================================================================ + +bool CSoundBase::GetErrorStringList ( QStringList& errorStringList ) +{ + if ( strErrorList.count() ) + { + errorStringList = strErrorList; + strErrorList.clear(); + + return true; } else { - if ( !strDevName.isEmpty() ) + errorStringList.clear(); + + return false; + } +} + +QString CSoundBase::GetErrorString() +{ + QString str ( strErrorList.join ( htmlNewLine() ) ); + strErrorList.clear(); + return str; +} + +//============================================================================ +// +//============================================================================ + +QStringList CSoundBase::GetDeviceNames() +{ + QMutexLocker locker ( &mutexDeviceProperties ); + + return strDeviceNames; +} + +QString CSoundBase::GetCurrentDeviceName() +{ + QMutexLocker locker ( &mutexDeviceProperties ); + + return ( ( iCurrentDevice >= 0 ) && ( iCurrentDevice < strDeviceNames.count() ) ) ? strDeviceNames[iCurrentDevice] : QString(); +} + +int CSoundBase::GetIndexOfDevice ( const QString& strDeviceName ) +{ + QMutexLocker locker ( &mutexDeviceProperties ); + + return strDeviceNames.indexOf ( strDeviceName ); +} + +int CSoundBase::GetNumInputChannels() +{ + QMutexLocker locker ( &mutexDeviceProperties ); + + return ( lNumInChan + lNumAddedInChan ); +} + +QString CSoundBase::GetInputChannelName ( const int index ) +{ + QMutexLocker locker ( &mutexDeviceProperties ); + + return ( ( index >= 0 ) && ( index < strInputChannelNames.count() ) ) ? strInputChannelNames[index] : QString(); +} + +QString CSoundBase::GetOutputChannelName ( const int index ) +{ + QMutexLocker locker ( &mutexDeviceProperties ); + + return ( ( index >= 0 ) && ( index < strOutputChannelNames.count() ) ) ? strOutputChannelNames[index] : QString(); +} + +void CSoundBase::SetLeftInputChannel ( const int iNewChan ) +{ + if ( !soundProperties.bHasInputChannelSelection ) + { + return; + } + + // apply parameter after input parameter check + if ( ( iNewChan >= 0 ) && ( iNewChan < ( lNumInChan + lNumAddedInChan ) ) && ( iNewChan != selectedInputChannels[0] ) ) + { { - // This is the first time a driver is to be initialized, we first - // try to load the selected driver, if this fails, we try to load - // the first available driver in the system. If this fails, too, we - // throw an error that no driver is available -> it does not make - // sense to start the software if no audio hardware is available. - if ( !LoadAndInitializeDriver ( strDevName, false ).isEmpty() ) + QMutexLocker locker ( &mutexAudioProcessCallback ); + + selectedInputChannels[0] = iNewChan; + } + + onChannelSelectionChanged(); + } +} + +void CSoundBase::SetRightInputChannel ( const int iNewChan ) +{ + if ( !soundProperties.bHasInputChannelSelection ) + { + return; + } + + // apply parameter after input parameter check + if ( ( iNewChan >= 0 ) && ( iNewChan < ( lNumInChan + lNumAddedInChan ) ) && ( iNewChan != selectedInputChannels[1] ) ) + { + { + QMutexLocker locker ( &mutexAudioProcessCallback ); + + selectedInputChannels[1] = iNewChan; + } + + onChannelSelectionChanged(); + } +} + +void CSoundBase::SetLeftOutputChannel ( const int iNewChan ) +{ + if ( !soundProperties.bHasOutputChannelSelection ) + { + return; + } + + // apply parameter after input parameter check + if ( ( iNewChan >= 0 ) && ( iNewChan < lNumOutChan ) && ( iNewChan != selectedOutputChannels[0] ) ) + { + { + QMutexLocker locker ( &mutexAudioProcessCallback ); + + int prevChan = selectedOutputChannels[0]; + selectedOutputChannels[0] = iNewChan; + + // Dirty hack, since we can't easily manage 2x same output + if ( selectedOutputChannels[1] == iNewChan ) { - // loading and initializing the new driver failed, try to find - // at least one usable driver - bTryLoadAnyDriver = true; + if ( ( prevChan >= 0 ) && ( prevChan != iNewChan ) ) + { + // Swap channels... + selectedOutputChannels[1] = prevChan; + } + else + { + // Select higher channel number for left + int otherChan = iNewChan + 1; + if ( otherChan >= lNumOutChan ) + { + otherChan = 0; + } + selectedInputChannels[1] = otherChan; + } } } - else + + onChannelSelectionChanged(); + } +} + +void CSoundBase::SetRightOutputChannel ( const int iNewChan ) +{ + if ( !soundProperties.bHasOutputChannelSelection ) + { + return; + } + + // apply parameter after input parameter check + if ( ( iNewChan >= 0 ) && ( iNewChan < lNumOutChan ) && ( iNewChan != selectedOutputChannels[1] ) ) + { { - // try to find one usable driver (select the first valid driver) - bTryLoadAnyDriver = true; + QMutexLocker locker ( &mutexAudioProcessCallback ); + + int prevChan = selectedOutputChannels[1]; + selectedOutputChannels[1] = iNewChan; + + // Dirty hack, since we can't easily manage 2x same output + if ( selectedOutputChannels[0] == iNewChan ) + { + if ( ( prevChan >= 0 ) && ( prevChan != iNewChan ) ) + { + // Swap channels... + selectedOutputChannels[0] = prevChan; + } + else + { + // Select lower channel number for left + int otherChan = iNewChan - 1; + if ( otherChan < 0 ) + { + otherChan = lNumOutChan - 1; + } + selectedOutputChannels[0] = otherChan; + } + } } + + onChannelSelectionChanged(); + } +} + +int CSoundBase::SetInputGain ( int iGain ) +{ + if ( !soundProperties.bHasInputGainSelection ) + { + return 1; + } + + QMutexLocker locker ( &mutexAudioProcessCallback ); // get mutex lock + + if ( iGain < 1 ) + { + iGain = 1; + } + else if ( iGain > DRV_MAX_INPUT_GAIN ) + { + iGain = DRV_MAX_INPUT_GAIN; + } + + for ( unsigned int i = 0; i < PROT_NUM_IN_CHANNELS; i++ ) + { + inputChannelsGain[i] = iGain; + } + + return iGain; +} + +int CSoundBase::SetInputGain ( int channelIndex, int iGain ) +{ + if ( ( channelIndex < 0 ) || ( channelIndex >= PROT_NUM_IN_CHANNELS ) ) + { + return 0; + } + + if ( !soundProperties.bHasInputGainSelection ) + { + return inputChannelsGain[channelIndex]; + } + + QMutexLocker locker ( &mutexAudioProcessCallback ); // get mutex lock + + if ( iGain < 1 ) + { + iGain = 1; + } + else if ( iGain > DRV_MAX_INPUT_GAIN ) + { + iGain = DRV_MAX_INPUT_GAIN; + } + + inputChannelsGain[channelIndex] = iGain; + + return iGain; +} + +bool CSoundBase::OpenDeviceSetup() +{ + if ( !openDeviceSetup() ) + { + // With ASIO there always is a setup button, but not all ASIO drivers support a setup dialog! + + showInfo ( htmlBold ( tr ( "This audio device does not support setup from jamulus." ) ) + htmlNewLine() + + tr ( "Check the device manual to see how you can check and change it's settings." ) ); + return false; + } + + return true; +} + +//============================================================================ +// Device handling +//============================================================================ + +bool CSoundBase::selectDevice ( int iDriverIndex, bool bOpenDriverSetup, bool bTryAnyDriver ) +{ + bool driverOk = false; + bool capabilitiesOk = false; + bool aborted = false; + long lNumDevices = strDeviceNames.count(); + long retries = 0; + int startindex = iDriverIndex; + + strErrorList.clear(); + + if ( !soundProperties.bHasSetupDialog ) + { + bOpenDriverSetup = false; } - if ( bTryLoadAnyDriver ) + // find and load driver + if ( ( iDriverIndex < 0 ) || ( iDriverIndex >= lNumDevices ) ) { - // if a driver was previously selected, show a warning message - if ( !strDevName.isEmpty() ) + strErrorList.append ( htmlBold ( tr ( "The selected audio device is no longer present in the system." ) ) ); + strErrorList.append ( tr ( "Selecting The first usable device..." ) ); + startindex = iDriverIndex = 0; + bTryAnyDriver = true; + } + + do + { + if ( !driverOk ) { - strReturn = tr ( "The previously selected audio device " - "is no longer available or the driver has changed to an incompatible state. " - "We'll attempt to find a valid audio device, but this new audio device may cause feedback. " - "Before connecting to a server, please check your audio device settings." ); - } + driverOk = checkDeviceChange ( tDeviceChangeCheck::CheckOpen, iDriverIndex ); - // try to load and initialize any valid driver - QVector vsErrorList = LoadAndInitializeFirstValidDriver(); + if ( !driverOk ) + { + if ( bTryAnyDriver ) + { + strErrorList.append ( tr ( "Failed to open %1" ).arg ( strDeviceNames[iDriverIndex] ) ); + iDriverIndex++; - if ( !vsErrorList.isEmpty() ) + if ( iDriverIndex == startindex ) + { + aborted = true; + } + else if ( iDriverIndex >= lNumDevices ) + { + iDriverIndex = 0; + } + strErrorList.append ( "" ); + strErrorList.append ( tr ( "Trying %1:" ).arg ( strDeviceNames[iDriverIndex] ) ); + retries++; + } + else + { + checkDeviceChange ( tDeviceChangeCheck::Abort, iDriverIndex ); + + if ( ( strErrorList.size() == 0 ) || ( iDriverIndex != startindex ) ) + { + // Revert... + strErrorList.append ( htmlBold ( tr ( "Couldn't initialise the audio driver." ) ) ); + strErrorList.append ( tr ( "Check if your audio hardware is plugged in and verify your driver settings." ) ); + } + + return false; + } + } + } + + if ( driverOk ) { - // create error message with all details - QString sErrorMessage = "" + QString ( tr ( "No usable %1 audio device found." ) ).arg ( strSystemDriverTechniqueName ) + - "

" + tr ( "These are all the available drivers with error messages:" ) + "
    "; + // check device capabilities if it fulfills our requirements + capabilitiesOk = checkDeviceChange ( tDeviceChangeCheck::CheckCapabilities, iDriverIndex ); // Checks if new device is usable - for ( int i = 0; i < lNumDevs; i++ ) + // check if device is capable + if ( !capabilitiesOk ) { - sErrorMessage += "
  • " + GetDeviceName ( i ) + ": " + vsErrorList[i] + "
  • "; + // if requested, open ASIO driver setup in case of an error + if ( bOpenDriverSetup && !bTryAnyDriver ) + { + if ( openDeviceSetup() ) + { + strErrorList.append ( htmlBold ( tr ( "Please check and correct your device settings..." ) ) ); + + QMessageBox confirm ( nullptr ); + confirm.setIcon ( QMessageBox::Warning ); + confirm.setWindowTitle ( QString ( APP_NAME ) + "-" + tr ( "Warning" ) ); + confirm.setText ( GetErrorString() ); + confirm.addButton ( QMessageBox::Retry ); + confirm.addButton ( QMessageBox::Abort ); + confirm.exec(); + + strErrorList.clear(); + + switch ( confirm.result() ) + { + case QMessageBox::Retry: + break; + + case QMessageBox::Abort: + default: + aborted = true; + } + } + } + else + { + if ( bTryAnyDriver ) + { + checkDeviceChange ( tDeviceChangeCheck::Abort, iDriverIndex ); + // Try next driver... + driverOk = false; + iDriverIndex++; + if ( iDriverIndex >= lNumDevices ) + { + iDriverIndex = 0; + } + + strErrorList.append ( "" ); + if ( iDriverIndex == startindex ) + { + strErrorList.append ( htmlBold ( tr ( "No usable audio devices found." ) ) ); + aborted = true; + } + else + { + strErrorList.append ( tr ( "Trying %1:" ).arg ( strDeviceNames[iDriverIndex] ) ); + } + retries++; + } + else + { + capabilitiesOk = true; // Ignore not ok... + strErrorList.append ( "" ); + strErrorList.append ( htmlBold ( tr ( "Try to solve the above warnings." ) ) ); + strErrorList.append ( tr ( "Then try selecting the device again or select another device.." ) ); + } + } } - sErrorMessage += "
"; + } -#ifdef _WIN32 - // to be able to access the ASIO driver setup for changing, e.g., the sample rate, we - // offer the user under Windows that we open the driver setups of all registered - // ASIO drivers - sErrorMessage += "
" + tr ( "Do you want to open the ASIO driver setup to try changing your configuration to a working state?" ); + } while ( !( driverOk && capabilitiesOk ) && ( iDriverIndex < lNumDevices ) && !aborted ); - if ( QMessageBox::Yes == QMessageBox::information ( nullptr, APP_NAME, sErrorMessage, QMessageBox::Yes | QMessageBox::No ) ) + if ( aborted && strErrorList.count() ) + { + if ( bTryAnyDriver ) + { + strErrorList.insert ( 0, htmlBold ( tr ( "No device could be opened due to the following errors(s):" ) ) + htmlNewLine() ); + } + else + { + strErrorList.insert ( 0, htmlBold ( tr ( "The device could not be opened due to the following errors(s):" ) ) + htmlNewLine() ); + } + } + else if ( bTryAnyDriver && ( iDriverIndex >= lNumDevices ) ) + { + strErrorList.append ( "" ); + strErrorList.append ( htmlBold ( tr ( "No usable audio devive available!" ) ) ); + } + + if ( driverOk && capabilitiesOk ) + { + // We selected a valid driver.... + if ( strErrorList.count() ) + { + if ( retries ) { - LoadAndInitializeFirstValidDriver ( true ); + strErrorList.append ( htmlBold ( tr ( "Selecting %1" ).arg ( strDeviceNames[iDriverIndex] ) ) ); } + // If there is something in the errorlist, so these are warnings... + // Show warnings now! + showWarning ( GetErrorString() ); + strErrorList.clear(); - sErrorMessage = QString ( tr ( "Can't start %1. Please restart %1 and check/reconfigure your audio settings." ) ).arg ( APP_NAME ); -#endif + if ( checkDeviceChange ( tDeviceChangeCheck::Activate, iDriverIndex ) ) + { + if ( soundProperties.bHasSetupDialog ) + { + OpenDeviceSetup(); + } + } - throw CGenErr ( sErrorMessage ); + return true; + } + else + { + return checkDeviceChange ( tDeviceChangeCheck::Activate, iDriverIndex ); } } - return strReturn; + checkDeviceChange ( tDeviceChangeCheck::Abort, iDriverIndex ); + + return false; } -QVector CSoundBase::LoadAndInitializeFirstValidDriver ( const bool bOpenDriverSetup ) +bool CSoundBase::ResetDevice ( bool bOpenSetupOnError ) { - QVector vsErrorList; + int iWasDevice = iCurrentDevice; + bool bWasStarted = IsStarted(); - // load and initialize first valid ASIO driver - bool bValidDriverDetected = false; - int iDriverCnt = 0; + closeCurrentDevice(); - // try all available drivers in the system ("lNumDevs" devices) - while ( !bValidDriverDetected && ( iDriverCnt < lNumDevs ) ) + if ( iWasDevice >= 0 ) // Did we have a selected device ? { - // try to load and initialize current driver, store error message - const QString strCurError = LoadAndInitializeDriver ( GetDeviceName ( iDriverCnt ), bOpenDriverSetup ); + // can we re-select that device ? + if ( selectDevice ( iWasDevice, bOpenSetupOnError ) ) + { + // Re-start if it was started before... + if ( bWasStarted ) + { + Start(); + } - vsErrorList.append ( strCurError ); + return true; + } + } - if ( strCurError.isEmpty() ) - { - // initialization was successful - bValidDriverDetected = true; + return false; +} - // store ID of selected driver - strCurDevName = GetDeviceName ( iDriverCnt ); +unsigned int CSoundBase::SetBufferSize ( unsigned int iDesiredBufferSize ) +{ + CSoundStopper sound ( *this ); - // empty error list shows that init was successful - vsErrorList.clear(); - } + iProtocolBufferSize = iDesiredBufferSize; + iDeviceBufferSize = getDeviceBufferSize ( iProtocolBufferSize ); - // try next driver - iDriverCnt++; - } + return iDeviceBufferSize; +} + +bool CSoundBase::BufferSizeSupported ( unsigned int iDesiredBufferSize ) +{ + unsigned int iActualBufferSize = getDeviceBufferSize ( iDesiredBufferSize ); - return vsErrorList; + return ( iActualBufferSize == iDesiredBufferSize ); } /******************************************************************************\ * MIDI handling * \******************************************************************************/ -void CSoundBase::ParseCommandLineArgument ( const QString& strMIDISetup ) +void CSoundBase::parseCommandLineArgument ( const QString& strMIDISetup ) { int iMIDIOffsetFader = 70; // Behringer X-TOUCH: offset of 0x46 @@ -291,8 +932,10 @@ void CSoundBase::ParseCommandLineArgument ( const QString& strMIDISetup ) for ( int i = 0; i < MAX_NUM_CHANNELS; i++ ) { if ( i + iMIDIOffsetFader > 127 ) + { break; - aMidiCtls[i + iMIDIOffsetFader] = { EMidiCtlType::Fader, i }; + } + aMidiCtls[i + iMIDIOffsetFader] = { CMidiCtlEntry::tMidiCtlType::Fader, i }; } return; } @@ -303,12 +946,14 @@ void CSoundBase::ParseCommandLineArgument ( const QString& strMIDISetup ) { QString sParm = slMIDIParams[i].trimmed(); if ( sParm.isEmpty() ) + { continue; + } int iCtrl = QString ( sMidiCtlChar ).indexOf ( sParm[0] ); if ( iCtrl < 0 ) continue; - EMidiCtlType eTyp = static_cast ( iCtrl ); + CMidiCtlEntry::tMidiCtlType eTyp = static_cast ( iCtrl ); const QStringList slP = sParm.mid ( 1 ).split ( '*' ); int iFirst = slP[0].toUInt(); @@ -325,7 +970,7 @@ void CSoundBase::ParseCommandLineArgument ( const QString& strMIDISetup ) } } -void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) +void CSoundBase::parseMIDIMessage ( const CVector& vMIDIPaketBytes ) { if ( vMIDIPaketBytes.Size() > 0 ) { @@ -362,7 +1007,7 @@ void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) ; switch ( cCtrl.eType ) { - case Fader: + case CMidiCtlEntry::tMidiCtlType::Fader: { // we are assuming that the controller number is the same // as the audio fader index and the range is 0-127 @@ -370,33 +1015,33 @@ void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) // consider offset for the faders - emit ControllerInFaderLevel ( cCtrl.iChannel, iFaderLevel ); + emit controllerInFaderLevel ( cCtrl.iChannel, iFaderLevel ); } break; - case Pan: + case CMidiCtlEntry::tMidiCtlType::Pan: { // Pan levels need to be symmetric between 1 and 127 - const int iPanValue = static_cast ( static_cast ( qMax ( iValue, 1 ) - 1 ) / 126 * AUD_MIX_PAN_MAX ); + const int iPanValue = static_cast ( ( static_cast ( qMax ( iValue, 1 ) ) - 1 ) / 126 * AUD_MIX_PAN_MAX ); - emit ControllerInPanValue ( cCtrl.iChannel, iPanValue ); + emit controllerInPanValue ( cCtrl.iChannel, iPanValue ); } break; - case Solo: + case CMidiCtlEntry::tMidiCtlType::Solo: { // We depend on toggles reflecting the desired state - emit ControllerInFaderIsSolo ( cCtrl.iChannel, iValue >= 0x40 ); + emit controllerInFaderIsSolo ( cCtrl.iChannel, iValue >= 0x40 ); } break; - case Mute: + case CMidiCtlEntry::tMidiCtlType::Mute: { // We depend on toggles reflecting the desired state - emit ControllerInFaderIsMute ( cCtrl.iChannel, iValue >= 0x40 ); + emit controllerInFaderIsMute ( cCtrl.iChannel, iValue >= 0x40 ); } break; - case MuteMyself: + case CMidiCtlEntry::tMidiCtlType::MuteMyself: { // We depend on toggles reflecting the desired state to Mute Myself - emit ControllerInMuteMyself ( iValue >= 0x40 ); + emit controllerInMuteMyself ( iValue >= 0x40 ); } break; default: @@ -408,3 +1053,69 @@ void CSoundBase::ParseMIDIMessage ( const CVector& vMIDIPaketBytes ) } } } + +//======================================================================== +// CSoundStopper Used to temporarily stop CSound i.e. while changing settings. +// Stops CSound while in scope and Starts CSound again when going out of scope +// if it was started before. +// One can also query CSoundStopper to see if CSound was started and/or active. +//======================================================================== + +CSoundBase::CSoundStopper::CSoundStopper ( CSoundBase& sound ) : Sound ( sound ), bAborted ( false ) +{ + bSoundWasActive = Sound.IsActive(); + bSoundWasStarted = Sound.IsStarted(); + + if ( bSoundWasStarted ) + { + Sound.Stop(); + } +} + +CSoundBase::CSoundStopper::~CSoundStopper() +{ + if ( bSoundWasStarted && !bAborted ) + { + Sound.Start(); + } +} + +bool CSoundBase::Start() +{ + if ( start() ) + { + _onStarted(); + return true; + } + + _onStopped(); + + QString strError = GetErrorString(); + if ( strError.size() ) + { + showError ( strError ); + } + + return false; +} + +bool CSoundBase::Stop() +{ + if ( stop() ) + { + _onStopped(); + return true; + } + + // We didn't seem to be stopped + // so restart active check + _onStarted(); + + QString strError = GetErrorString(); + if ( strError.size() ) + { + showError ( strError ); + } + + return false; +} diff --git a/src/soundbase.h b/src/soundbase.h index 2249904aec..e84bcfa384 100644 --- a/src/soundbase.h +++ b/src/soundbase.h @@ -2,7 +2,7 @@ * Copyright (c) 2004-2022 * * Author(s): - * Volker Fischer + * Volker Fischer, revised and maintained by Peter Goderie (pgScorpio) * ****************************************************************************** * @@ -22,155 +22,506 @@ * \******************************************************************************/ +//=============================================================================== +// Additions by pgScorpio: +// +// CSoundBase: Is a base class for CSound specifying the public interface for CClient. +// +// CSound should implement all CSoundBase virtual functions and +// implement the "glue" between CSoundbase and the sound driver(s) +// +// All additional variables and functions in CSound should always be protected +// or private ! If you really need extra functionality for CClient it probably +// belongs in CSoundBase ! +// +// Note that all Public funtions start with a Capital, all private and protected +// variables and functuns start with a lowercase letter. +// (static) callback functions start with an underscore. +// +//======================================================================================= +// Good practice for clarity of the code source: +//======================================================================================= +// All declarations should always be grouped in the order private, protected, +// and public. +// +// All class variables should always be listed first (grouped by category) +// +// Than the constructor followed by any destructor. +// (Any constructor that uses new or malloc should always have a public +// destructor to delete the allocated data! But do NOT call any virtual +// functions from a destructor, since any descendant is already destructed! +// Base classes should ALWAYS have a protected constructor and a public +// VIRTUAL destructor!, even when nothing to delete.) +// +// Than any protected static functions (grouped by category) +// Than any public static functions (grouped by category) +// +// Than any private functions (grouped by category) +// Than any protected functions (grouped by category) +// Than any public functions (grouped by category) +// +// Than any protected virtual functions (grouped by category) +// Than any public virtual functions (grouped by category) +// +// (Grouping means repeat private: protected: public: before each group +// or at least insert an empty line between groups of the same kind.) +// +// Any friends expected to access multiple protected property groups should be +// listed in a separate protected group at the beginning of the class +// declaration (comment why it should be a friend!). +// Any friends expected to access only a specific protected property group +// should be listed as first in that protected group itself (also comment why!). +// +// Functions that do NOT change any class variables should always be +// declared as a const function. ( funcname(...) const { ...; } ) +// +// Functions that do NOT use any class variables or functions should always +// be declared static or, often better, outside the class ! +// +// Finally: Always try to have the same order of definitions in the .cpp file +// as the order of declarations order in the .h file. +// +//======================================================================================= + #pragma once #include #include #include +#include + #ifndef HEADLESS # include #endif + #include "global.h" #include "util.h" +#define CHECK_SOUND_ACTIVE_TIME_MS 2000 // ms + // TODO better solution with enum definition // problem: in signals it seems not to work to use CSoundBase::ESndCrdResetType -enum ESndCrdResetType + +typedef enum TSNDCRDRESETTYPE { RS_ONLY_RESTART = 1, RS_ONLY_RESTART_AND_INIT, RS_RELOAD_RESTART_AND_INIT -}; +} tSndCrdResetType; -enum EMidiCtlType +class CMidiCtlEntry { - Fader = 0, - Pan, - Solo, - Mute, - MuteMyself, - None +public: + typedef enum class EMidiCtlType + { + Fader = 0, + Pan, + Solo, + Mute, + MuteMyself, + None + } tMidiCtlType; + + tMidiCtlType eType; + int iChannel; + + CMidiCtlEntry ( tMidiCtlType eT = tMidiCtlType::None, int iC = 0 ) : eType ( eT ), iChannel ( iC ) {} }; -class CMidiCtlEntry +//======================================================================================= +// CSoundProperties class: (UI related CSound interface) +//======================================================================================= + +class CSoundProperties { +protected: + friend class CSoundBase; + friend class CSound; + + bool bHasAudioDeviceSelection; + QString strAudioDeviceToolTip; + QString strAudioDeviceWhatsThis; + QString strAudioDeviceAccessibleName; + + bool bHasSetupDialog; + QString strSetupButtonText; + + QString strSetupButtonToolTip; + QString strSetupButtonWhatsThis; + QString strSetupButtonAccessibleName; + + bool bHasInputChannelSelection; + bool bHasOutputChannelSelection; + bool bHasInputGainSelection; + + CSoundProperties(); // Sets the default values for CSoundProperties, + // Constructor of CSound should set modified values if needed. + public: - CMidiCtlEntry ( EMidiCtlType eT = EMidiCtlType::None, int iC = 0 ) : eType ( eT ), iChannel ( iC ) {} - EMidiCtlType eType; - int iChannel; + inline bool HasAudioDeviceSelection() const { return bHasAudioDeviceSelection; } + inline const QString& AudioDeviceToolTip() const { return strAudioDeviceToolTip; } + inline const QString& AudioDeviceWhatsThis() const { return strAudioDeviceWhatsThis; } + inline const QString& AudioDeviceAccessibleName() const { return strAudioDeviceAccessibleName; } + + inline bool HasSetupDialog() const { return bHasSetupDialog; } + inline const QString& SetupButtonText() const { return strSetupButtonText; } + + inline const QString& SetupButtonToolTip() const { return strSetupButtonToolTip; } + inline const QString& SetupButtonWhatsThis() const { return strSetupButtonWhatsThis; } + inline const QString& SetupButtonAccessibleName() const { return strSetupButtonAccessibleName; } + + inline bool HasInputChannelSelection() const { return bHasInputChannelSelection; } + inline bool HasOutputChannelSelection() const { return bHasOutputChannelSelection; } + inline bool HasInputGainSelection() const { return bHasInputGainSelection; } + + // Add more ui texts here if needed... + // (Make sure to also set defaults and/or set values in CSound constructor) }; -/* Classes ********************************************************************/ +//======================================================================================= +// CSoundBase class: +//======================================================================================= + class CSoundBase : public QThread { Q_OBJECT +protected: + CSoundProperties soundProperties; // CSound Information for the user interface + + const CSoundProperties& getSoundProperties() const { return soundProperties; } + public: - CSoundBase ( const QString& strNewSystemDriverTechniqueName, - void ( *fpNewProcessCallback ) ( CVector& psData, void* pParg ), - void* pParg, - const QString& strMIDISetup ); + // utility functions + static int16_t Flip16Bits ( const int16_t iIn ); + static int32_t Flip32Bits ( const int32_t iIn ); + static int64_t Flip64Bits ( const int64_t iIn ); - virtual int Init ( const int iNewPrefMonoBufferSize ) { return iNewPrefMonoBufferSize; } - virtual void Start() - { - bRun = true; - bCallbackEntered = false; - } - virtual void Stop(); +protected: + // Only to be called from a descendant: + CSoundBase ( const QString& systemDriverTechniqueName, + void ( *theProcessCallback ) ( CVector& psData, void* pParg ), + void* theCallBackArg ); - // device selection - QStringList GetDevNames(); - QString SetDev ( const QString strDevName ); - QString GetDev() +public: + // destructors MUST be public, but base classes should always define a virtual destructor! + virtual ~CSoundBase() { - QMutexLocker locker ( &MutexDevProperties ); - return strCurDevName; + // Nothing to delete here, but since this is a base class there MUST be a public VIRTUAL destructor! } - virtual int GetNumInputChannels() { return 2; } - virtual QString GetInputChannelName ( const int ) { return "Default"; } - virtual void SetLeftInputChannel ( const int ) {} - virtual void SetRightInputChannel ( const int ) {} - virtual int GetLeftInputChannel() { return 0; } - virtual int GetRightInputChannel() { return 1; } - - virtual int GetNumOutputChannels() { return 2; } - virtual QString GetOutputChannelName ( const int ) { return "Default"; } - virtual void SetLeftOutputChannel ( const int ) {} - virtual void SetRightOutputChannel ( const int ) {} - virtual int GetLeftOutputChannel() { return 0; } - virtual int GetRightOutputChannel() { return 1; } +protected: + // Message boxes: + void showError ( QString strError ); + void showWarning ( QString strWarning ); + void showInfo ( QString strInfo ); - virtual float GetInOutLatencyMs() { return 0.0f; } // "0.0" means no latency is available +protected: + QMutex mutexAudioProcessCallback; // Use in audio processing callback and when changing channel selections ! + QMutex mutexDeviceProperties; // Use when accessing device properties in callbacks or changing device properties ! - virtual void OpenDriverSetup() {} + QTimer timerCheckActive; - bool IsRunning() const { return bRun; } - bool IsCallbackEntered() const { return bCallbackEntered; } + unsigned int iProtocolBufferSize; // The buffersize requested by SetBufferSize (Expected by the protocol) + unsigned int iDeviceBufferSize; // The buffersize returned by SetBufferSize (The audio device's actual buffersize) - // TODO this should be protected but since it is used - // in a callback function it has to be public -> better solution - void EmitReinitRequestSignal ( const ESndCrdResetType eSndCrdResetType ) { emit ReinitRequest ( eSndCrdResetType ); } + CVector audioBuffer; // The audio buffer passed in the sound callback, used for both input and output by onBufferSwitch() protected: - virtual QString LoadAndInitializeDriver ( QString, bool ) { return ""; } - virtual void UnloadCurrentDriver() {} - QVector LoadAndInitializeFirstValidDriver ( const bool bOpenDriverSetup = false ); - void ParseCommandLineArgument ( const QString& strMIDISetup ); - QString GetDeviceName ( const int iDiD ) { return strDriverNames[iDiD]; } + QString strDriverTechniqueName; + QString strClientName; + bool bAutoConnect; - static void GetSelCHAndAddCH ( const int iSelCH, const int iNumInChan, int& iSelCHOut, int& iSelAddCHOut ) +private: + bool bStarted; // Set by _onStart, reset by _onStop + bool bActive; // Set by _onCallback, reset by _onStart and _onStop + + inline void _onStarted() { - // we have a mixed channel setup, definitions: - // - mixed channel setup only for 4 physical inputs: - // SelCH == 4: Ch 0 + Ch 2 - // SelCh == 5: Ch 0 + Ch 3 - // SelCh == 6: Ch 1 + Ch 2 - // SelCh == 7: Ch 1 + Ch 3 - if ( iSelCH >= iNumInChan ) - { - iSelAddCHOut = ( ( iSelCH - iNumInChan ) % 2 ) + 2; - iSelCHOut = ( iSelCH - iNumInChan ) / 2; - } - else + bStarted = true; + bActive = false; + timerCheckActive.start ( CHECK_SOUND_ACTIVE_TIME_MS ); + } + + inline void _onStopped() + { + timerCheckActive.stop(); + + // make sure the working thread is actually done + // (by checking the locked state) + if ( mutexAudioProcessCallback.tryLock ( 5000 ) ) { - iSelAddCHOut = INVALID_INDEX; // set it to an invalid number - iSelCHOut = iSelCH; + mutexAudioProcessCallback.unlock(); } + + bStarted = false; + bActive = false; } - // function pointer to callback function +protected: + // Control of privates: + //============================================================================ + // Descendant callbacks: + // Functions to be called from corresponding + // virtual functions in the derived class + //============================================================================ + + // pgScorpio TODO: These functions should also control the connection timeout timer to check bCallbackEntered ! + // This timer is now running in CClient, but a connection timeout should be a signal too ! + + inline void _onProcessCallback() { bActive = true; } + +protected: + // Device list: (Should be filled with availe device names by the virtual function createDeviceList() ) + long lNumDevices; // The number of devices available for selection. + QStringList strDeviceNames; // Should be a lNumDevices long list of devicenames + +protected: + // Device Info: (Should be set by the virtual function activateNewDevice()) + long lNumInChan; + long lNumAddedInChan; // number of fictive input channels added for i.e. mixed input channels + long lNumOutChan; + int iCurrentDevice; + float fInOutLatencyMs; + + QStringList strInputChannelNames; // Should be (lNumInChan + lNumAddedInChan) long + QStringList strOutputChannelNames; // Should be (lNumOutChan) long + + void clearDeviceInfo(); + +protected: + // Channel selection: + int inputChannelsGain[PROT_NUM_IN_CHANNELS]; // Array with gain factor for input channels + int selectedInputChannels[PROT_NUM_IN_CHANNELS]; // Array with indexes of selected input channels + int selectedOutputChannels[PROT_NUM_OUT_CHANNELS]; // Array with indexes of selected output channels + + void resetInputChannelsGain(); // Sets all protocol InputChannel gains to 1 + void resetChannelMapping(); // Sets default input/output channel selection (first available channels) + +protected: + static long getNumInputChannelsToAdd ( long lNumInChan ); + static void getInputSelAndAddChannels ( int iSelChan, long lNumInChan, long lNumAddedInChan, int& iSelCHOut, int& iAddCHOut ); + + long addAddedInputChannelNames(); // Sets lNumOutChan. And adds added channel names to strInputChannelNames + // lNumInChan and strInputChannelNames must be set first !! + +protected: + // Misc. + QStringList strErrorList; // List with errorstrings to be returned by + + bool addError ( const QString& strError ); + +protected: + // Midi handling + int iCtrlMIDIChannel; + QVector aMidiCtls; + +protected: + // Callbacks + + // pointer to callback function void ( *fpProcessCallback ) ( CVector& psData, void* arg ); + + // pointer to callback arguments void* pProcessCallbackArg; // callback function call for derived classes - void ProcessCallback ( CVector& psData ) + inline void processCallback ( CVector& psData ) { - bCallbackEntered = true; + _onProcessCallback(); ( *fpProcessCallback ) ( psData, pProcessCallbackArg ); } - void ParseMIDIMessage ( const CVector& vMIDIPaketBytes ); +signals: + void reinitRequest ( int iSndCrdResetType ); + void soundActiveTimeout(); - bool bRun; - bool bCallbackEntered; - QMutex MutexAudioProcessCallback; - QMutex MutexDevProperties; +protected slots: + void OnTimerCheckActive(); - QString strSystemDriverTechniqueName; - int iCtrlMIDIChannel; - QVector aMidiCtls; +protected: + //======================================================================== + // MIDI handling: + //======================================================================== + void parseCommandLineArgument ( const QString& strMIDISetup ); + void parseMIDIMessage ( const CVector& vMIDIPaketBytes ); - long lNumDevs; - QString strCurDevName; - QString strDriverNames[MAX_NUMBER_SOUND_CARDS]; +signals: // Signals from midi: + void controllerInFaderLevel ( int iChannelIdx, int iValue ); + void controllerInPanValue ( int iChannelIdx, int iValue ); + void controllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); + void controllerInFaderIsMute ( int iChannelIdx, bool bIsMute ); + void controllerInMuteMyself ( bool bMute ); -signals: - void ReinitRequest ( int iSndCrdResetType ); - void ControllerInFaderLevel ( int iChannelIdx, int iValue ); - void ControllerInPanValue ( int iChannelIdx, int iValue ); - void ControllerInFaderIsSolo ( int iChannelIdx, bool bIsSolo ); - void ControllerInFaderIsMute ( int iChannelIdx, bool bIsMute ); - void ControllerInMuteMyself ( bool bMute ); +protected: + // device selection + // selectDevice calls a checkDeviceChange() sequence (checkOpen, checkCapabilities, Activate|Abort) + // Any Errors should be reported in strErorList + // So if false is returned one should call GetErrorList( QStringList& errorList ); + bool selectDevice ( int iDriverIndex, bool bOpenDriverSetup, bool bTryAnyDriver = false ); + +public: + //============================================================================ + // Interface to CClient: + //============================================================================ + + bool Start(); + bool Stop(); + + inline const QString& SystemDriverTechniqueName() const { return strDriverTechniqueName; } + + inline bool IsStarted() const { return bStarted; } + inline bool IsActive() const { return bStarted && bActive; } + + bool GetErrorStringList ( QStringList& errorStringList ); + QString GetErrorString(); + + int GetNumDevices() const { return strDeviceNames.count(); } + + QStringList GetDeviceNames(); + QString GetDeviceName ( const int index ) { return strDeviceNames[index]; } + QString GetCurrentDeviceName(); + + int GetCurrentDeviceIndex() const { return iCurrentDevice; } + int GetIndexOfDevice ( const QString& strDeviceName ); + + int GetNumInputChannels(); + inline int GetNumOutputChannels() { return lNumOutChan; } + + QString GetInputChannelName ( const int index ); + QString GetOutputChannelName ( const int index ); + + void SetLeftInputChannel ( const int iNewChan ); + void SetRightInputChannel ( const int iNewChan ); + + void SetLeftOutputChannel ( const int iNewChan ); + void SetRightOutputChannel ( const int iNewChan ); + + int GetLeftInputChannel() { return selectedInputChannels[0]; } + int GetRightInputChannel() { return selectedInputChannels[1]; } + + int GetLeftOutputChannel() { return selectedOutputChannels[0]; } + int GetRightOutputChannel() { return selectedOutputChannels[1]; } + + float GetInOutLatencyMs() { return fInOutLatencyMs; } + + int SetInputGain ( int channelIndex, int iGain ); + int SetInputGain ( int iGain ); // Sets gain for all protocol input channels + + // inline int GetInputGain() { return inputChannelsGain[0]; }; + inline int GetInputGain ( int channelIndex ) + { + if ( ( channelIndex >= 0 ) && ( channelIndex < PROT_NUM_IN_CHANNELS ) ) + return inputChannelsGain[channelIndex]; + return 0; + }; + + bool OpenDeviceSetup(); + +public: + // device selection + // If false is returned use GetErrorList( QStringList& errorList ) to get the list of error descriptions. + // + // pgScorpio: Set by name should only be used for selection from name in ini file. + // For now bTryAnydriver is only set if there was no device in the inifile (empty string) + // + bool SetDevice ( const QString& strDevName, bool bOpenSetupOnError = false ) + { + QMutexLocker locker ( &mutexDeviceProperties ); + + strErrorList.clear(); + createDeviceList( /* true */ ); // We should always re-create the devicelist when reading the init file + + int iDriverIndex = strDevName.isEmpty() ? 0 : strDeviceNames.indexOf ( strDevName ); + + return selectDevice ( iDriverIndex, bOpenSetupOnError, + strDevName.isEmpty() ); // Calls openDevice and checkDeviceCapabilities + } + + bool SetDevice ( int iDriverIndex, bool bOpenSetupOnError = false ) + { + QMutexLocker locker ( &mutexDeviceProperties ); + + strErrorList.clear(); + createDeviceList(); // Only create devicelist if neccesary + return selectDevice ( iDriverIndex, bOpenSetupOnError ); // Calls openDevice and checkDeviceCapabilities + } + + // Restart device after settings change... + bool ResetDevice ( bool bOpenSetupOnError = false ); + + unsigned int SetBufferSize ( unsigned int iDesiredBufferSize ); + bool BufferSizeSupported ( unsigned int iDesiredBufferSize ); + // { returns true if the current selected device can use exactly this buffersize.} + +public: + //======================================================================== + // CSoundStopper Used to temporarily stop CSound i.e. while changing settings. + // While in scope it Stops CSound when it is running and Starts CSound again, + // when going out of scope if CSound was initially started. + // One can also query CSoundStopper to see if CSound was started and/or active. + // Use SoundStopper.Abort() to prevent restarting when going out of scope. + //======================================================================== + class CSoundStopper + { + protected: + CSoundBase& Sound; + bool bSoundWasStarted; + bool bSoundWasActive; + bool bAborted; + + public: + CSoundStopper ( CSoundBase& sound ); + ~CSoundStopper(); + bool Start(); + bool Stop(); + bool WasStarted() { return bSoundWasStarted; } + bool WasActive() { return bSoundWasActive; } + bool Aborted() { return bAborted; } + + bool Abort() + { + bAborted = true; + return bSoundWasStarted; + } // Do not Restart! + }; + +protected: + typedef enum class TDEVICECHANGECHECK + { + Abort = -1, + CheckOpen, + CheckCapabilities, + Activate, + } tDeviceChangeCheck; + + //======================================================================== + // pgScorpio: For clarity always list all virtual functions in separate + // sections at the end ! + // In this case no Defaults! All virtuals should be abstract, + // so we don't forget to implemenent the neccesary virtuals + // in CSound + //======================================================================== + /* This Section MUST also be included in any CSound class definition ! + +protected: // CSoundBase Mandatory pointer to instance (must be set to 'this' in the CSound constructor) + static CSound* pSound; + +public: // CSoundBase Mandatory functions. (but static functions can't be virtual) + static inline CSoundBase* pInstance() { return pSound; } + static inline const CSoundProperties& GetProperties() { return pSound->getSoundProperties(); } + */ +protected: + //============================================================================ + // Virtual interface to CSoundBase: + //============================================================================ + virtual void onChannelSelectionChanged(){}; // Needed on macOS + + virtual long createDeviceList ( bool bRescan = false ) = 0; // Fills strDeviceNames returns lNumDevices + virtual bool checkDeviceChange ( tDeviceChangeCheck mode, int iDriverIndex ) = 0; // Performs the different actions for a device change + virtual unsigned int getDeviceBufferSize ( unsigned int iDesiredBufferSize ) = 0; // returns the nearest possible buffersize of selected device + virtual void closeCurrentDevice() = 0; // Closes the current driver and Clears Device Info + virtual bool openDeviceSetup() = 0; // { return false; } + virtual bool start() = 0; // Returns true if started, false if stopped + virtual bool stop() = 0; // Returns true if stopped, false if still (partly) running }; diff --git a/src/util.cpp b/src/util.cpp index fdebffc1d5..9a108864f6 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -492,6 +492,7 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : CBaseDlg ( parent ) "

Gary Wang (BLumia)

" "

RobyDati (RobyDati)

" "

Rob-NY (Rob-NY)

" + "

Peter Goderie (pgScorpio)

" "
" + tr ( "For details on the contributions check out the %1" ) .arg ( "" + tr ( "Github Contributors list" ) + "." ) ); diff --git a/windows/asiodriver.cpp b/windows/asiodriver.cpp new file mode 100644 index 0000000000..1a462c2fb2 --- /dev/null +++ b/windows/asiodriver.cpp @@ -0,0 +1,516 @@ +#include "asiodriver.h" + +//============================================================================ +// CAsioDummy: a Dummy ASIO driver +// Defines NO_ASIO_DRIVER, a pointer to a dummy asio interface +// which can be accessed, but always returns ASE_NotPresent or other +// default values. +// +// Note: +// All invalid pIASIO pointer (IASIO*) values should be NO_ASIO_DRIVER, +// and so pointing to the dummy driver ! +//============================================================================ + +const char AsioDummyDriverName[] = "No ASIO Driver"; +const char AsioDummyDriverError[] = "Error: No ASIO Driver!"; + +const ASIOChannelInfo NoAsioChannelInfo = { + /* long channel; */ 0, + /* ASIOBool isInput; */ ASIOFalse, + /* ASIOBool isActive; */ ASIOFalse, + /* long channelGroup; */ -1, + /* ASIOSampleType type; */ ASIOSTInt16MSB, + /* char name[32]; */ "None" }; + +class CAsioDummy : public IASIO +{ +public: // IUnknown: + virtual HRESULT STDMETHODCALLTYPE QueryInterface ( const IID&, void** ) { return 0xC00D0071 /*NS_E_NO_DEVICE*/; } + virtual ULONG STDMETHODCALLTYPE AddRef ( void ) { return 0; } + virtual ULONG STDMETHODCALLTYPE Release ( void ) { return 0; } + +public: + virtual ASIOBool init ( void* ) { return ASIOFalse; } + virtual void getDriverName ( char* name ) { strcpy ( name, AsioDummyDriverName ); } + virtual long getDriverVersion() { return 0; } + virtual void getErrorMessage ( char* msg ) { strcpy ( msg, AsioDummyDriverError ); } + virtual ASIOError start() { return ASE_NotPresent; } + virtual ASIOError stop() { return ASE_NotPresent; } + virtual ASIOError getChannels ( long* ci, long* co ) + { + *ci = *co = 0; + return ASE_NotPresent; + } + virtual ASIOError getLatencies ( long* iL, long* oL ) + { + *iL = *oL = 0; + return ASE_NotPresent; + } + virtual ASIOError getBufferSize ( long* minS, long* maxS, long* prfS, long* gr ) + { + *minS = *maxS = *prfS = *gr = 0; + return ASE_NotPresent; + } + virtual ASIOError canSampleRate ( ASIOSampleRate ) { return ASE_NotPresent; } + virtual ASIOError getSampleRate ( ASIOSampleRate* sr ) + { + *sr = 0; + return ASE_NotPresent; + } + virtual ASIOError setSampleRate ( ASIOSampleRate ) { return ASE_NotPresent; } + virtual ASIOError getClockSources ( ASIOClockSource* c, long* s ) + { + memset ( c, 0, sizeof ( ASIOClockSource ) ); + *s = 0; + return ASE_NotPresent; + } + virtual ASIOError setClockSource ( long ) { return ASE_NotPresent; } + virtual ASIOError getSamplePosition ( ASIOSamples*, ASIOTimeStamp* ) { return ASE_NotPresent; } + virtual ASIOError getChannelInfo ( ASIOChannelInfo* inf ) + { + memcpy ( inf, &NoAsioChannelInfo, sizeof ( ASIOChannelInfo ) ); + return ASE_NotPresent; + } + virtual ASIOError disposeBuffers() { return ASE_NotPresent; } + virtual ASIOError controlPanel() { return ASE_NotPresent; } + virtual ASIOError future ( long, void* ) { return ASE_NotPresent; } + virtual ASIOError outputReady() { return ASE_NotPresent; } + + virtual ASIOError createBuffers ( ASIOBufferInfo* bufferInfos, long numChannels, long /* bufferSize */, ASIOCallbacks* /*callbacks*/ ) + { + ASIOBufferInfo* info = bufferInfos; + for ( long i = 0; ( i < numChannels ); i++, info++ ) + { + info->buffers[0] = NULL; + info->buffers[1] = NULL; + } + + return ASE_NotPresent; + } +}; + +CAsioDummy NoAsioDriver; // Dummy asio interface if no driver is selected. +const pIASIO NO_ASIO_DRIVER = ( (pIASIO) ( &NoAsioDriver ) ); // All pIASIO pointers NOT referencing an opened driver should use NO_ASIO_DRIVER + // to point to NoAsioDriver + +//============================================================================ +// IASIO helper Functions +//============================================================================ + +bool AsioIsOpen ( pIASIO& asio ) +{ + if ( !asio ) + { + // NULL pointer not allowed ! + // Should point to NoAsioDriver. + asio = NO_ASIO_DRIVER; + } + + return ( asio != NO_ASIO_DRIVER ); +}; + +//============================================================================ +// Local helper Functions for obtaining the AsioDriverData +//============================================================================ + +bool FindAsioDriverPath ( char* clsidstr, char* dllpath, int dllpathsize ) +{ + HKEY hkEnum, hksub, hkpath; + char databuf[512]; + LSTATUS cr; + DWORD datatype, datasize; + DWORD index; + HFILE hfile; + bool keyfound = FALSE; + bool pathfound = FALSE; + + CharLowerBuff ( clsidstr, static_cast ( strlen ( clsidstr ) ) ); //@PGO static_cast ! + if ( ( cr = RegOpenKey ( HKEY_CLASSES_ROOT, ASIO_COM_CLSID, &hkEnum ) ) == ERROR_SUCCESS ) + { + index = 0; + while ( cr == ERROR_SUCCESS && !keyfound ) + { + cr = RegEnumKey ( hkEnum, index++, (LPTSTR) databuf, 512 ); + if ( cr == ERROR_SUCCESS ) + { + CharLowerBuff ( databuf, static_cast ( strlen ( databuf ) ) ); + if ( strcmp ( databuf, clsidstr ) == 0 ) + { + keyfound = true; // break out + + if ( ( cr = RegOpenKeyEx ( hkEnum, (LPCTSTR) databuf, 0, KEY_READ, &hksub ) ) == ERROR_SUCCESS ) + { + if ( ( cr = RegOpenKeyEx ( hksub, (LPCTSTR) ASIO_INPROC_SERVER, 0, KEY_READ, &hkpath ) ) == ERROR_SUCCESS ) + { + datatype = REG_SZ; + datasize = (DWORD) dllpathsize; + cr = RegQueryValueEx ( hkpath, 0, 0, &datatype, (LPBYTE) dllpath, &datasize ); + if ( cr == ERROR_SUCCESS ) + { + OFSTRUCT ofs; + memset ( &ofs, 0, sizeof ( OFSTRUCT ) ); + ofs.cBytes = sizeof ( OFSTRUCT ); + hfile = OpenFile ( dllpath, &ofs, OF_EXIST ); + + pathfound = ( hfile ); + } + RegCloseKey ( hkpath ); + } + RegCloseKey ( hksub ); + } + } + } + } + + RegCloseKey ( hkEnum ); + } + + return pathfound; +} + +bool GetAsioDriverData ( tAsioDrvData* data, HKEY hkey, char* keyname ) +{ + // typedef struct TASIODRVDATA + // { + // char drvname[MAXDRVNAMELEN + 1]; + // CLSID clsid; // Driver CLSID + // char dllpath[MAXPATHLEN + 1]; // Path to the driver dlll + // IASIO* asio; // Pointer to the ASIO interface when opened, should point to NO_ASIO_DRIVER if closed + // } tAsioDrvData; + + if ( !data ) + { + return false; + } + + bool ok = false; + HKEY hksub; + char databuf[256]; + WORD wData[100]; + DWORD datatype, datasize; + LSTATUS cr; + + memset ( data, 0, sizeof ( tAsioDrvData ) ); + data->index = -1; + data->openData.asio = NO_ASIO_DRIVER; + + if ( ( cr = RegOpenKeyEx ( hkey, (LPCTSTR) keyname, 0, KEY_READ, &hksub ) ) == ERROR_SUCCESS ) + { + datatype = REG_SZ; + datasize = 256; + cr = RegQueryValueEx ( hksub, ASIO_COM_CLSID, 0, &datatype, (LPBYTE) databuf, &datasize ); + if ( cr == ERROR_SUCCESS ) + { + // Get dllpath + if ( FindAsioDriverPath ( databuf, data->dllpath, MAX_ASIO_PATHLEN ) ) + { + ok = true; + + // Set CLSID + MultiByteToWideChar ( CP_ACP, 0, (LPCSTR) databuf, -1, (LPWSTR) wData, 100 ); + if ( ( cr = CLSIDFromString ( (LPOLESTR) wData, &data->clsid ) ) != S_OK ) + { + memset ( &data->clsid, 0, sizeof ( data->clsid ) ); + ok = false; + } + + // Get drvname + datatype = REG_SZ; + datasize = sizeof ( databuf ); + cr = RegQueryValueEx ( hksub, ASIODRV_DESC, 0, &datatype, (LPBYTE) databuf, &datasize ); + if ( cr == ERROR_SUCCESS ) + { + strcpy_s ( data->drvname, sizeof ( data->drvname ), databuf ); + } + else + { + strcpy_s ( data->drvname, sizeof ( data->drvname ), keyname ); + } + } + } + + RegCloseKey ( hksub ); + } + + return ok; +} + +//============================================================================ +// GetAsioDriverDataList: Obtains AsioDrvData of all available asio drivers. +//============================================================================ + +long GetAsioDriverDataList ( tVAsioDrvDataList& vAsioDrvDataList ) +{ + DWORD lDriverIndex = 0; + HKEY hkEnum = 0; + char keyname[MAX_ASIO_DRVNAMELEN + 1]; + LONG cr; + + vAsioDrvDataList.clear(); + + cr = RegOpenKey ( HKEY_LOCAL_MACHINE, ASIO_PATH, &hkEnum ); + + if ( cr == ERROR_SUCCESS ) + { + do + { + if ( ( cr = RegEnumKey ( hkEnum, lDriverIndex, (LPTSTR) keyname, sizeof ( keyname ) ) ) == ERROR_SUCCESS ) + { + tAsioDrvData AsioDrvData; + + if ( GetAsioDriverData ( &AsioDrvData, hkEnum, keyname ) ) + { + AsioDrvData.index = vAsioDrvDataList.count(); + vAsioDrvDataList.append ( AsioDrvData ); + } + } + else + { + break; + } + + lDriverIndex++; + + } while ( true ); + } + + if ( hkEnum ) + { + RegCloseKey ( hkEnum ); + } + + return vAsioDrvDataList.count(); +} + +//============================================================================ +// AsioDrvData functions: (low level AsioDrvData handling) +//============================================================================ + +bool AsioDriverIsValid ( tAsioDrvData* asioDrvData ) +{ + if ( asioDrvData ) + { + return ( asioDrvData->drvname[0] && // Must have a drvname + asioDrvData->dllpath[0] && // And a dllPath + ( asioDrvData->clsid.Data1 || asioDrvData->clsid.Data2 || asioDrvData->clsid.Data4 || + asioDrvData->clsid.Data4[0] ) // And a non zero clsid + ); + } + + return false; +} + +bool AsioDriverIsOpen ( tAsioDrvData* asioDrvData ) +{ + if ( asioDrvData ) + { + return AsioIsOpen ( asioDrvData->openData.asio ); + } + + return false; +}; + +bool OpenAsioDriver ( tAsioDrvData* asioDrvData ) +{ + HRESULT hr; + + if ( !asioDrvData ) + { + return false; + } + + if ( AsioDriverIsOpen ( asioDrvData ) ) + { + return true; + } + + // Safety check: should already be deleted ! + if ( asioDrvData->openData.addedInChannelData ) + { + delete[] asioDrvData->openData.addedInChannelData; + } + + if ( asioDrvData->openData.inChannelData ) + { + delete asioDrvData->openData.inChannelData; + } + + if ( asioDrvData->openData.outChannelData ) + { + delete asioDrvData->openData.outChannelData; + } + + memset ( &asioDrvData->openData, 0, sizeof ( asioDrvData->openData ) ); + asioDrvData->openData.asio = NO_ASIO_DRIVER; + + // CoCreateInstance( + // _In_ REFCLSID rclsid, lpdrv->clsid + // _In_opt_ LPUNKNOWN pUnkOuter, 0 + // _In_ DWORD dwClsContext, CLSCTX_INPROC_SERVER + // _In_ REFIID riid, pgScorpio: Using lpdrv->clsid instead of a iid ??? + // _COM_Outptr_ _At_(*ppv, _Post_readable_size_(_Inexpressible_(varies))) LPVOID FAR * ppv + // ); + + hr = CoCreateInstance ( asioDrvData->clsid, 0, CLSCTX_INPROC_SERVER, asioDrvData->clsid, (LPVOID*) &( asioDrvData->openData.asio ) ); + if ( SUCCEEDED ( hr ) ) + { + asioDrvData->openData.driverinfo.asioVersion = ASIO_HOST_VERSION; + if ( asioDrvData->openData.asio->init ( &asioDrvData->openData.driverinfo ) ) + { + if ( asioDrvData->drvname[0] == 0 ) + { + asioDrvData->openData.asio->getDriverName ( asioDrvData->drvname ); + } + + asioDrvData->openData.asio->getChannels ( &asioDrvData->openData.lNumInChan, &asioDrvData->openData.lNumOutChan ); + if ( asioDrvData->openData.lNumInChan ) + { + asioDrvData->openData.inChannelData = new ASIOChannelInfo[asioDrvData->openData.lNumInChan]; + for ( long i = 0; i < asioDrvData->openData.lNumInChan; i++ ) + { + asioDrvData->openData.inChannelData[i].channel = i; + asioDrvData->openData.inChannelData[i].isInput = ASIO_INPUT; + asioDrvData->openData.asio->getChannelInfo ( &asioDrvData->openData.inChannelData[i] ); + } + } + if ( asioDrvData->openData.lNumOutChan ) + { + asioDrvData->openData.outChannelData = new ASIOChannelInfo[asioDrvData->openData.lNumOutChan]; + for ( long i = 0; i < asioDrvData->openData.lNumOutChan; i++ ) + { + asioDrvData->openData.outChannelData[i].channel = i; + asioDrvData->openData.outChannelData[i].isInput = ASIO_OUTPUT; + asioDrvData->openData.asio->getChannelInfo ( &asioDrvData->openData.outChannelData[i] ); + } + } + return true; + } + } + + asioDrvData->openData.asio = NO_ASIO_DRIVER; + + return false; +} + +bool CloseAsioDriver ( tAsioDrvData* asioDrvData ) +{ + if ( !asioDrvData ) + { + return false; + } + + if ( AsioDriverIsOpen ( asioDrvData ) ) + { + asioDrvData->openData.asio->Release(); + } + + if ( asioDrvData->openData.addedInChannelData ) + { + delete[] asioDrvData->openData.addedInChannelData; + } + + if ( asioDrvData->openData.inChannelData ) + { + delete[] asioDrvData->openData.inChannelData; + } + + if ( asioDrvData->openData.outChannelData ) + { + delete[] asioDrvData->openData.outChannelData; + } + + memset ( &asioDrvData->openData, 0, sizeof ( asioDrvData->openData ) ); + asioDrvData->openData.asio = NO_ASIO_DRIVER; + + return true; +} + +//============================================================================ +// CAsioDriver: Wrapper for tAsioDrvData. +// Implements the normal ASIO driver access. +//============================================================================ + +CAsioDriver::CAsioDriver ( tAsioDrvData* pDrvData ) +{ + memset ( ( (tAsioDrvData*) this ), 0, sizeof ( tAsioDrvData ) ); + index = -1; + openData.asio = NO_ASIO_DRIVER; + + if ( pDrvData ) + { + AssignFrom ( pDrvData ); + } +} + +bool CAsioDriver::AssignFrom ( tAsioDrvData* pDrvData ) +{ + Close(); + + if ( pDrvData ) + { + // Move to this driver + *( (tAsioDrvData*) ( this ) ) = *pDrvData; + + // Other driver is no longer open + memset ( &pDrvData->openData, 0, sizeof ( pDrvData->openData ) ); + pDrvData->openData.asio = NO_ASIO_DRIVER; + } + else + { + memset ( ( (tAsioDrvData*) ( this ) ), 0, sizeof ( tAsioDrvData ) ); + index = -1; + openData.asio = NO_ASIO_DRIVER; + } + + return AsioDriverIsValid ( this ); +} + +ASIOAddedChannelInfo* CAsioDriver::setNumAddedInputChannels ( long numChan ) +{ + if ( IsOpen() ) + { + if ( openData.addedInChannelData ) + { + delete[] openData.addedInChannelData; + } + openData.addedInChannelData = new ASIOAddedChannelInfo[numChan]; + openData.lNumAddedInChan = numChan; + memset ( openData.addedInChannelData, 0, ( sizeof ( ASIOAddedChannelInfo ) * numChan ) ); + for ( long i = 0; i < numChan; i++ ) + { + openData.addedInChannelData[i].ChannelData.channel = ( openData.lNumInChan + i ); + openData.addedInChannelData[i].ChannelData.isInput = ASIO_INPUT; + } + + return openData.addedInChannelData; + } + else + { + return NULL; + } +} + +long CAsioDriver::GetInputChannelNames ( QStringList& list ) +{ + for ( long i = 0; i < openData.lNumInChan; i++ ) + { + list.append ( openData.inChannelData[i].name ); + } + + for ( long i = 0; i < openData.lNumAddedInChan; i++ ) + { + list.append ( openData.addedInChannelData[i].ChannelData.name ); + } + + return ( openData.lNumInChan + openData.lNumAddedInChan ); +} + +long CAsioDriver::GetOutputChannelNames ( QStringList& list ) +{ + for ( long i = 0; i < openData.lNumOutChan; i++ ) + { + list.append ( openData.outChannelData[i].name ); + } + + return openData.lNumOutChan; +} diff --git a/windows/asiodriver.h b/windows/asiodriver.h new file mode 100644 index 0000000000..52d1ce7e0d --- /dev/null +++ b/windows/asiodriver.h @@ -0,0 +1,208 @@ +#pragma once +#include "asiosys.h" +#include +#include + +// ****************************************************************** +// ASIO Registry definitions: +// ****************************************************************** +#define ASIODRV_DESC "description" +#define ASIO_INPROC_SERVER "InprocServer32" +#define ASIO_PATH "software\\asio" +#define ASIO_COM_CLSID "clsid" + +//============================================================================ +// tAsioDrvData: +// Basic part is obtained from registry by GetAsioDriverData +// The openData struct is initialised on Open() and only valid while open. +//============================================================================ + +// For AddedChannelData we need extra storage space for the 2nd channelname! +// Luckily the name field is at the end of the ASIOChannelInfo struct, +// so we can expand the needed storage space by defining another field after the sruct. +typedef struct +{ + ASIOChannelInfo ChannelData; + char AddedName[34]; // 3 chars for " + " and another additional 31 character name +} ASIOAddedChannelInfo; + +typedef struct TASIODRVDATA +{ + long index; // index in the asioDriverData list + + // Data from registry: + char drvname[MAX_ASIO_DRVNAMELEN + 1]; // Driver name + char dllpath[MAX_ASIO_PATHLEN + 1]; // Path to the driver dlll + CLSID clsid; // Driver CLSID + + struct ASIO_OPEN_DATA + { + //============================================================== + // Data set by OpenAsioDriver() and cleared by CloseAsioDriver: + //============================================================== + IASIO* asio; // Pointer to the ASIO interface: Set by CoCreateInstance, should point to NO_ASIO_DRIVER if closed + + // Obtained from ASIO driverinfo set by asio->ASIOInit( &driverinfo ) + ASIODriverInfo driverinfo; + + // Obtained from asio->ASIOGetChannels( &lNumInChan, &lNumOutChan ): + long lNumInChan; + long lNumOutChan; + + // Obtained from ASIOGetChannelInfo: + ASIOChannelInfo* inChannelData; + ASIOChannelInfo* outChannelData; + + // Set by setNumAddedInputChannels (Usually from capability check) + long lNumAddedInChan; + ASIOAddedChannelInfo* addedInChannelData; + + // Set by capability check: + bool capabilitiesOk; + } openData; +} tAsioDrvData; + +#define tVAsioDrvDataList QVector + +//============================================================================ +// ASIO driver helper functions: +//============================================================================ + +extern bool FindAsioDriverPath ( char* clsidstr, char* dllpath, int dllpathsize ); +extern bool GetAsioDriverData ( tAsioDrvData* data, HKEY hkey, char* keyname ); + +//============================================================================ +// ASIO GetAsioDriverDataList: +// Retrieves AsioDrvData for all available ASIO devices on the system. +//============================================================================ + +extern long GetAsioDriverDataList ( tVAsioDrvDataList& vAsioDrvDataList ); + +//============================================================================ +// ASIO asioDriverData Open/Close functions: +// Once a driver is opened you can access the driver via asioDrvData.asio +//============================================================================ + +extern bool AsioDriverIsValid ( tAsioDrvData* asioDrvData ); // Returns true if non NULL pointer and drvname, dllpath and clsid are all not empty. +extern bool AsioDriverIsOpen ( + tAsioDrvData* asioDrvData ); // Will also set asioDrvData->asio to NO_ASIO_DRIVER if asioDrvData->asio is a NULL pointer! +extern bool OpenAsioDriver ( tAsioDrvData* asioDrvData ); +extern bool CloseAsioDriver ( tAsioDrvData* asioDrvData ); + +//============================================================================ +// CAsioDriver: Wrapper for tAsioDrvData. +// Implements the normal ASIO driver access. +//============================================================================ + +class CAsioDriver : public tAsioDrvData +{ +public: + CAsioDriver ( tAsioDrvData* pDrvData = NULL ); + + ~CAsioDriver() { Close(); } + + // AssignFrom: Switch to another driver, + // Closes this driver if it was open. + // Copies the new driver data. + // Returns true if assigned to a new driver. + // The source DrvData will always be in closed state after assignment to a CAsioDriver! + + bool AssignFrom ( tAsioDrvData* pDrvData ); + +public: + // WARNING! Also AddedInputChannels are only valid while opened ! + + // setNumAddedInputChannels creates numChan added channels and sets openData.lNumAddedInChan + // Only channel number and isInput are set. Channel names and other values still have to be set. + + ASIOAddedChannelInfo* setNumAddedInputChannels ( long numChan ); + +public: // Channel info: + // WARNING! Channel info is only available when opened. And there are NO checks on indexes! + // So first check isOpen() and numXxxxChannels() before accessing channel data !! + + long GetInputChannelNames ( QStringList& list ); + long GetOutputChannelNames ( QStringList& list ); + +public: + inline pIASIO iasiodrv() + { + return openData.asio; + }; // Use iasiodrv()->asioFunction if you need the actual returned ASIOError ! + // See iasiodrv.h + +public: + inline bool IsValid() { return AsioDriverIsValid ( this ); } + inline bool IsOpen() { return AsioDriverIsOpen ( this ); } + + inline bool Open() { return OpenAsioDriver ( this ); } // Calls init(&driverinfo) and retrieves channel info too! + inline bool Close() { return CloseAsioDriver ( this ); } // Also destroys the channel info! + +public: // iASIO functions (driver must be open first !) + inline bool start() { return ASIOError_OK ( openData.asio->start() ); } + inline bool stop() { return ASIOError_OK ( openData.asio->stop() ); } + + inline long driverVersion() { return openData.asio->getDriverVersion(); } + + inline long numInputChannels() { return openData.lNumInChan; } + inline long numAddedInputChannels() { return openData.lNumAddedInChan; } + inline long numTotalInputChannels() { return ( openData.lNumInChan + openData.lNumAddedInChan ); } + + inline long numOutputChannels() { return openData.lNumOutChan; } + + inline const ASIOChannelInfo& inputChannelInfo ( long index ) + { + return ( index < openData.lNumInChan ) ? openData.inChannelData[index] : openData.addedInChannelData[index - openData.lNumInChan].ChannelData; + } + + inline const ASIOChannelInfo& outputChannelInfo ( long index ) { return openData.outChannelData[index]; } + + inline bool canSampleRate ( ASIOSampleRate sampleRate ) { return ASIOError_OK ( openData.asio->canSampleRate ( sampleRate ) ); } + inline bool setSampleRate ( ASIOSampleRate sampleRate ) { return ASIOError_OK ( openData.asio->setSampleRate ( sampleRate ) ); } + inline bool getSampleRate ( ASIOSampleRate* sampleRate ) { return ASIOError_OK ( openData.asio->getSampleRate ( sampleRate ) ); } + + inline bool getBufferSize ( long* minSize, long* maxSize, long* preferredSize, long* granularity ) + { + return ASIOError_OK ( openData.asio->getBufferSize ( minSize, maxSize, preferredSize, granularity ) ); + } + inline bool createBuffers ( ASIOBufferInfo* bufferInfos, long numChannels, long bufferSize, ASIOCallbacks* callbacks ) + { + return ASIOError_OK ( openData.asio->createBuffers ( bufferInfos, numChannels, bufferSize, callbacks ) ); + } + inline bool disposeBuffers() { return ASIOError_OK ( openData.asio->disposeBuffers() ); } + + inline bool getSamplePosition ( ASIOSamples* sPos, ASIOTimeStamp* tStamp ) + { + return ASIOError_OK ( openData.asio->getSamplePosition ( sPos, tStamp ) ); + } + + inline bool getClockSources ( ASIOClockSource* clocks, long* numSources ) + { + return ASIOError_OK ( openData.asio->getClockSources ( clocks, numSources ) ); + } + inline bool setClockSource ( long reference ) { return ASIOError_OK ( openData.asio->setClockSource ( reference ) ); } + + inline bool getLatencies ( long* inputLatency, long* outputLatency ) + { + return ASIOError_OK ( openData.asio->getLatencies ( inputLatency, outputLatency ) ); + } + + inline bool outputReady() { return ASIOError_OK ( openData.asio->outputReady() ); } + + inline bool controlPanel() { return ASIOError_OK ( openData.asio->controlPanel() ); } + + inline void getErrorMessage ( char* string ) { openData.asio->getErrorMessage ( string ); } + + inline bool future ( long selector, void* opt ) { return ASIOError_OK ( openData.asio->future ( selector, opt ) ); } +}; + +//============================================================================ +// Pointer pointing to a dummy IASIO interface +// Used for the asio pointer of not opened asio drivers so it's always +// safe to access the pointer. (Though all calls will return a fail.) +//============================================================================ + +extern const pIASIO NO_ASIO_DRIVER; // All pIASIO pointers NOT referencing an opened driver should use NO_ASIO_DRIVER + // to point to NoAsioDriver + +extern bool AsioIsOpen ( pIASIO& asio ); // Will also set asio to NO_ASIO_DRIVER if it happens to be a NULL pointer! diff --git a/windows/asiosys.h b/windows/asiosys.h new file mode 100644 index 0000000000..b405f56c73 --- /dev/null +++ b/windows/asiosys.h @@ -0,0 +1,53 @@ +//============================================================================ +// asiosys.h +// Jamulus Windows ASIO definitions for the ASIOSDK +// Always include asiosys.h to include the ASIOSDK ! +// Never include iasiodrv.h or asio.h directly +//============================================================================ + +#ifndef __asiosys__ +#define __asiosys__ + +#include "windows.h" + +// ASIO_HOST_VERSION: 0 or >= 2 !! See asio.h +#define ASIO_HOST_VERSION 2 + +#define ASIO_LITTLE_ENDIAN 1 +#define ASIO_CPU_X86 1 + +#define NATIVE_INT64 0 +#define IEEE754_64FLOAT 1 + +#define DRVERR_OK 0 +#define DRVERR -5000 +#define DRVERR_INVALID_PARAM DRVERR - 1 +#define DRVERR_DEVICE_NOT_FOUND DRVERR - 2 +#define DRVERR_DEVICE_NOT_OPEN DRVERR - 3 +#define DRVERR_DEVICE_ALREADY_OPEN DRVERR - 4 +#define DRVERR_DEVICE_CANNOT_OPEN DRVERR - 5 + +#define MAX_ASIO_DRIVERS 64 + +// Max string lengths, buffers should be LEN + 1 for string terminating zero. +#define MAX_ASIO_PATHLEN 511 +#define MAX_ASIO_DRVNAMELEN 127 + +#define __ASIODRIVER_FWD_DEFINED__ + +// Values for ASIOChannelInfo.isInput +#define ASIO_INPUT ASIOTrue +#define ASIO_OUTPUT ASIOFalse + +#include "iasiodrv.h" // will also include asio.h + +typedef IASIO* pIASIO; + +inline bool ASIOError_OK ( ASIOError error ) +{ + return ( ( error == ASE_OK ) || // 0 normal success return value + ( error == ASE_SUCCESS ) // 1061701536, unique success return value for ASIOFuture calls + ); +} + +#endif diff --git a/windows/sound.cpp b/windows/sound.cpp index ef789893d2..1098afbd8b 100644 --- a/windows/sound.cpp +++ b/windows/sound.cpp @@ -1,8 +1,8 @@ /******************************************************************************\ - * Copyright (c) 2004-2022 + * Copyright (c) 2004-2022 * * Author(s): - * Volker Fischer + * Volker Fischer, revised and maintained by Peter Goderie (pgScorpio) * * Description: * Sound card interface for Windows operating systems @@ -27,1118 +27,772 @@ #include "sound.h" -/* Implementation *************************************************************/ -// external references -extern AsioDrivers* asioDrivers; -bool loadAsioDriver ( char* name ); +//============================================================================ +// Helpers for checkNewDeviceCapabilities +//============================================================================ -// pointer to our sound object -CSound* pSound; - -/******************************************************************************\ -* Common * -\******************************************************************************/ -QString CSound::LoadAndInitializeDriver ( QString strDriverName, bool bOpenDriverSetup ) +inline bool SampleTypeSupported ( const ASIOSampleType SamType ) { - // find and load driver - int iDriverIdx = INVALID_INDEX; // initialize with an invalid index - - for ( int i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ ) - { - if ( strDriverName.compare ( cDriverNames[i] ) == 0 ) - { - iDriverIdx = i; - } - } - - // if the selected driver was not found, return an error message - if ( iDriverIdx == INVALID_INDEX ) - { - return tr ( "The selected audio device is no longer present in the system. Please check your audio device." ); - } - - // Save number of channels from last driver - // Need to save these (but not the driver name) as CheckDeviceCapabilities() overwrites them - long lNumInChanPrev = lNumInChan; - long lNumOutChanPrev = lNumOutChan; - - loadAsioDriver ( cDriverNames[iDriverIdx] ); + // check for supported sample types + return ( ( SamType == ASIOSTInt16LSB ) || ( SamType == ASIOSTInt24LSB ) || ( SamType == ASIOSTInt32LSB ) || ( SamType == ASIOSTFloat32LSB ) || + ( SamType == ASIOSTFloat64LSB ) || ( SamType == ASIOSTInt32LSB16 ) || ( SamType == ASIOSTInt32LSB18 ) || + ( SamType == ASIOSTInt32LSB20 ) || ( SamType == ASIOSTInt32LSB24 ) || ( SamType == ASIOSTInt16MSB ) || ( SamType == ASIOSTInt24MSB ) || + ( SamType == ASIOSTInt32MSB ) || ( SamType == ASIOSTFloat32MSB ) || ( SamType == ASIOSTFloat64MSB ) || ( SamType == ASIOSTInt32MSB16 ) || + ( SamType == ASIOSTInt32MSB18 ) || ( SamType == ASIOSTInt32MSB20 ) || ( SamType == ASIOSTInt32MSB24 ) ); +} - // According to the docs, driverInfo.asioVersion and driverInfo.sysRef - // should be set, but we haven't being doing that and it seems to work - // okay... - memset ( &driverInfo, 0, sizeof driverInfo ); +//============================================================================ +// ASIO callback statics +//============================================================================ - if ( ASIOInit ( &driverInfo ) != ASE_OK ) - { - // clean up and return error string - asioDrivers->removeCurrentDriver(); - return tr ( "Couldn't initialise the audio driver. Check if your audio hardware is plugged in and verify your driver settings." ); - } +CSound* CSound::pSound = NULL; +ASIOCallbacks CSound::asioCallbacks; // Pointers to the asio driver callbacks (ASIO driver -> Application) - // check device capabilities if it fulfills our requirements - const QString strStat = CheckDeviceCapabilities(); // also sets lNumInChan and lNumOutChan +//============================================================================ +// CSound: +//============================================================================ - // check if device is capable - if ( strStat.isEmpty() ) - { - // Reset channel mapping if the sound card name has changed or the number of channels has changed - if ( ( strCurDevName.compare ( strDriverNames[iDriverIdx] ) != 0 ) || ( lNumInChanPrev != lNumInChan ) || ( lNumOutChanPrev != lNumOutChan ) ) - { - // In order to fix https://github.com/jamulussoftware/jamulus/issues/796 - // this code runs after a change in the ASIO driver (not when changing the ASIO input selection.) +CSound::CSound ( void ( *fpProcessCallback ) ( CVector& psData, void* arg ), void* pProcessCallBackParg ) : + CSoundBase ( "ASIO", fpProcessCallback, pProcessCallBackParg ), + asioDriversLoaded ( false ) +{ + setObjectName ( "CSoundThread" ); - // mapping to the defaults (first two available channels) - ResetChannelMapping(); + asioDriverData.clear(); - // store ID of selected driver if initialization was successful - strCurDevName = cDriverNames[iDriverIdx]; - } - } - else - { - // if requested, open ASIO driver setup in case of an error - if ( bOpenDriverSetup ) - { - OpenDriverSetup(); - QMessageBox::question ( nullptr, - APP_NAME, - "Are you done with your ASIO driver settings of " + GetDeviceName ( iDriverIdx ) + "?", - QMessageBox::Yes ); - } + // set up the asioCallback structure + asioCallbacks.bufferSwitch = &_onBufferSwitch; + asioCallbacks.sampleRateDidChange = &_onSampleRateChanged; + asioCallbacks.asioMessage = &_onAsioMessages; + asioCallbacks.bufferSwitchTimeInfo = &_onBufferSwitchTimeInfo; - // driver cannot be used, clean up - asioDrivers->removeCurrentDriver(); - } + // We assume NULL'd pointers in this structure indicate that buffers are not + // allocated yet (see UnloadCurrentDriver). + memset ( bufferInfos, 0, sizeof bufferInfos ); - return strStat; + // Set my properties in CSoundbase: + soundProperties.bHasSetupDialog = true; + soundProperties.strSetupButtonText = tr ( "ASIO Device Settings" ); + soundProperties.strSetupButtonToolTip = tr ( "Opens the driver settings when available..." ) + "
" + + tr ( "Note: %1 currently only supports devices with a sample rate of %2 Hz. " + "You may need to re-select the driver before any changed settings will take effect." ) + .arg ( APP_NAME ) + .arg ( SYSTEM_SAMPLE_RATE_HZ ) + + htmlNewLine() + tr ( "For more help see jamulus.io." ); + soundProperties.strSetupButtonAccessibleName = CSoundBase::tr ( "ASIO Device Settings push button" ); + + soundProperties.strAudioDeviceWhatsThis = ( "" + tr ( "Audio Device" ) + ": " + + tr ( "Under the Windows operating system the ASIO driver (sound card) can be " + "selected using %1. If the selected driver is not valid an error " + "message will be shown. " + "Under macOS the input and output hardware can be selected." ) + .arg ( APP_NAME ) + + "
" + + tr ( "If the driver is selected during an active connection, the connection " + "is stopped, the driver is changed and the connection is started again " + "automatically." ) ); + + soundProperties.strAudioDeviceToolTip = tr ( "If the ASIO4ALL driver is used, " + "please note that this driver usually introduces approx. 10-30 ms of " + "additional audio delay. Using a sound card with a native ASIO driver " + "is therefore recommended." ) + + htmlNewLine() + + tr ( "If you are using the kX ASIO " + "driver, make sure to connect the ASIO inputs in the kX DSP settings " + "panel." ); + + // init pointer to our sound object for the asio callback functions + pSound = this; } -void CSound::UnloadCurrentDriver() -{ - // clean up ASIO stuff - if ( bRun ) - { - Stop(); - } - if ( bufferInfos[0].buffers[0] ) - { - ASIODisposeBuffers(); - bufferInfos[0].buffers[0] = NULL; - } - ASIOExit(); - asioDrivers->removeCurrentDriver(); -} +//============================================================================ +// ASIO callback implementations +//============================================================================ -QString CSound::CheckDeviceCapabilities() +void CSound::onBufferSwitch ( long index, ASIOBool /*processNow*/ ) { - // This function checks if our required input/output channel - // properties are supported by the selected device. If the return - // string is empty, the device can be used, otherwise the error - // message is returned. - - // check the sample rate - const ASIOError CanSaRateReturn = ASIOCanSampleRate ( SYSTEM_SAMPLE_RATE_HZ ); - - if ( ( CanSaRateReturn == ASE_NoClock ) || ( CanSaRateReturn == ASE_NotPresent ) ) - { - // return error string - return QString ( tr ( "The selected audio device is incompatible " - "since it doesn't support a sample rate of %1 Hz. Please select another " - "device." ) ) - .arg ( SYSTEM_SAMPLE_RATE_HZ ); - } - - // check if sample rate can be set - const ASIOError SetSaRateReturn = ASIOSetSampleRate ( SYSTEM_SAMPLE_RATE_HZ ); + // pgSorpio: Impoved readability by adding the intermediate pointers pAsioSelInput, pAsioAddInput and pAsioSelOutput. + // And so its immediately clear mixing can be supported for ANY supported sample type! + // (Just adding another int16_t using pAsioAddInput instead of pAsioSelInput!) + // + // Also added input muting here + // + // perform the processing for input and output + QMutexLocker locker ( &mutexAudioProcessCallback ); // get mutex lock - if ( ( SetSaRateReturn == ASE_NoClock ) || ( SetSaRateReturn == ASE_InvalidMode ) || ( SetSaRateReturn == ASE_NotPresent ) ) + // CAPTURE ------------------------------------------------------------- + for ( unsigned int i = 0; i < PROT_NUM_IN_CHANNELS; i++ ) { - // return error string - return QString ( tr ( "The current audio device configuration is incompatible " - "because the sample rate couldn't be set to %2 Hz. Please check for a hardware switch or " - "driver setting to set the sample rate manually and restart %1." ) ) - .arg ( APP_NAME ) - .arg ( SYSTEM_SAMPLE_RATE_HZ ); - } + int iSelCH, iSelAddCH; + getInputSelAndAddChannels ( selectedInputChannels[i], lNumInChan, lNumAddedInChan, iSelCH, iSelAddCH ); - // check the number of available channels - ASIOGetChannels ( &lNumInChan, &lNumOutChan ); + int iInputGain = inputChannelsGain[i]; - if ( ( lNumInChan < NUM_IN_OUT_CHANNELS ) || ( lNumOutChan < NUM_IN_OUT_CHANNELS ) ) - { - // return error string - return QString ( tr ( "The selected audio device is incompatible since it doesn't support " - "%1 in/out channels. Please select another device or configuration." ) ) - .arg ( NUM_IN_OUT_CHANNELS ); - } - - // clip number of input/output channels to our maximum - if ( lNumInChan > MAX_NUM_IN_OUT_CHANNELS ) - { - lNumInChan = MAX_NUM_IN_OUT_CHANNELS; - } - if ( lNumOutChan > MAX_NUM_IN_OUT_CHANNELS ) - { - lNumOutChan = MAX_NUM_IN_OUT_CHANNELS; - } + void* pAsioSelInput = bufferInfos[iSelCH].buffers[index]; + void* pAsioAddInput = ( iSelAddCH >= 0 ) ? bufferInfos[iSelAddCH].buffers[index] : NULL; - // query channel infos for all available input channels - bool bInputChMixingSupported = true; - - for ( int i = 0; i < lNumInChan; i++ ) - { - // setup for input channels - channelInfosInput[i].isInput = ASIOTrue; - channelInfosInput[i].channel = i; - - ASIOGetChannelInfo ( &channelInfosInput[i] ); - - // Check supported sample formats. - // Actually, it would be enough to have at least two channels which - // support the required sample format. But since we have support for - // all known sample types, the following check should always pass and - // therefore we throw the error message on any channel which does not - // fulfill the sample format requirement (quick hack solution). - if ( !CheckSampleTypeSupported ( channelInfosInput[i].type ) ) + // copy new captured block in thread transfer buffer (copy + // mono data interleaved in stereo buffer) + switch ( asioDriver.inputChannelInfo ( iSelCH ).type ) { - // return error string - return tr ( "The selected audio device is incompatible since " - "the required audio sample format isn't available. Please use another device." ); - } - - // store the name of the channel and check if channel mixing is supported - channelInputName[i] = channelInfosInput[i].name; - - if ( !CheckSampleTypeSupportedForCHMixing ( channelInfosInput[i].type ) ) + case ASIOSTInt16LSB: { - bInputChMixingSupported = false; - } - } - - // query channel infos for all available output channels - for ( int i = 0; i < lNumOutChan; i++ ) - { - // setup for output channels - channelInfosOutput[i].isInput = ASIOFalse; - channelInfosOutput[i].channel = i; - - ASIOGetChannelInfo ( &channelInfosOutput[i] ); + // no type conversion required, just copy operation + int16_t* pASIOBuf = static_cast ( pAsioSelInput ); - // Check supported sample formats. - // Actually, it would be enough to have at least two channels which - // support the required sample format. But since we have support for - // all known sample types, the following check should always pass and - // therefore we throw the error message on any channel which does not - // fulfill the sample format requirement (quick hack solution). - if ( !CheckSampleTypeSupported ( channelInfosOutput[i].type ) ) - { - // return error string - return tr ( "The selected audio device is incompatible since " - "the required audio sample format isn't available. Please use another device." ); - } - } - - // special case with 4 input channels: support adding channels - if ( ( lNumInChan == 4 ) && bInputChMixingSupported ) - { - // add four mixed channels (i.e. 4 normal, 4 mixed channels) - lNumInChanPlusAddChan = 8; - - for ( int iCh = 0; iCh < lNumInChanPlusAddChan; iCh++ ) - { - int iSelCH, iSelAddCH; - - GetSelCHAndAddCH ( iCh, lNumInChan, iSelCH, iSelAddCH ); - - if ( iSelAddCH >= 0 ) + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - // for mixed channels, show both audio channel names to be mixed - channelInputName[iCh] = channelInputName[iSelCH] + " + " + channelInputName[iSelAddCH]; + audioBuffer[2 * iCurSample + i] = pASIOBuf[iCurSample] * iInputGain; } - } - } - else - { - // regular case: no mixing input channels used - lNumInChanPlusAddChan = lNumInChan; - } - - // everything is ok, return empty string for "no error" case - return ""; -} - -void CSound::SetLeftInputChannel ( const int iNewChan ) -{ - // apply parameter after input parameter check - if ( ( iNewChan >= 0 ) && ( iNewChan < lNumInChanPlusAddChan ) ) - { - vSelectedInputChannels[0] = iNewChan; - } -} - -void CSound::SetRightInputChannel ( const int iNewChan ) -{ - // apply parameter after input parameter check - if ( ( iNewChan >= 0 ) && ( iNewChan < lNumInChanPlusAddChan ) ) - { - vSelectedInputChannels[1] = iNewChan; - } -} - -void CSound::SetLeftOutputChannel ( const int iNewChan ) -{ - // apply parameter after input parameter check - if ( ( iNewChan >= 0 ) && ( iNewChan < lNumOutChan ) ) - { - vSelectedOutputChannels[0] = iNewChan; - } -} - -void CSound::SetRightOutputChannel ( const int iNewChan ) -{ - // apply parameter after input parameter check - if ( ( iNewChan >= 0 ) && ( iNewChan < lNumOutChan ) ) - { - vSelectedOutputChannels[1] = iNewChan; - } -} -int CSound::GetActualBufferSize ( const int iDesiredBufferSizeMono ) -{ - int iActualBufferSizeMono; - - // query the usable buffer sizes - ASIOGetBufferSize ( &HWBufferInfo.lMinSize, &HWBufferInfo.lMaxSize, &HWBufferInfo.lPreferredSize, &HWBufferInfo.lGranularity ); - - // clang-format off -/* -// TEST -#include -QMessageBox::information ( 0, "APP_NAME", QString("lMinSize: %1, lMaxSize: %2, lPreferredSize: %3, lGranularity: %4"). - arg(HWBufferInfo.lMinSize).arg(HWBufferInfo.lMaxSize).arg(HWBufferInfo.lPreferredSize).arg(HWBufferInfo.lGranularity) ); -_exit(1); -*/ - // clang-format on - - // clang-format off -// TODO see https://github.com/EddieRingle/portaudio/blob/master/src/hostapi/asio/pa_asio.cpp#L1654 (SelectHostBufferSizeForUnspecifiedUserFramesPerBuffer) - // clang-format on - - // calculate "nearest" buffer size and set internal parameter accordingly - // first check minimum and maximum values - if ( iDesiredBufferSizeMono <= HWBufferInfo.lMinSize ) - { - iActualBufferSizeMono = HWBufferInfo.lMinSize; - } - else - { - if ( iDesiredBufferSizeMono >= HWBufferInfo.lMaxSize ) - { - iActualBufferSizeMono = HWBufferInfo.lMaxSize; - } - else - { - // ASIO SDK 2.2: "Notes: When minimum and maximum buffer size are - // equal, the preferred buffer size has to be the same value as - // well; granularity should be 0 in this case." - if ( HWBufferInfo.lMinSize == HWBufferInfo.lMaxSize ) - { - iActualBufferSizeMono = HWBufferInfo.lMinSize; - } - else + if ( pAsioAddInput ) { - if ( ( HWBufferInfo.lGranularity < -1 ) || ( HWBufferInfo.lGranularity == 0 ) ) - { - // Special case (seen for EMU audio cards): granularity is - // zero or less than zero (make sure to exclude the special - // case of -1). - // There is no definition of this case in the ASIO SDK - // document. We assume here that all buffer sizes in between - // minimum and maximum buffer sizes are allowed. - iActualBufferSizeMono = iDesiredBufferSizeMono; - } - else + // mix input channels case: + int16_t* pASIOBufAdd = static_cast ( pAsioAddInput ); + + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - // General case -------------------------------------------- - // initialization - int iTrialBufSize = HWBufferInfo.lMinSize; - int iLastTrialBufSize = HWBufferInfo.lMinSize; - bool bSizeFound = false; - - // test loop - while ( ( iTrialBufSize <= HWBufferInfo.lMaxSize ) && ( !bSizeFound ) ) - { - if ( iTrialBufSize >= iDesiredBufferSizeMono ) - { - // test which buffer size fits better: the old one or the - // current one - if ( ( iTrialBufSize - iDesiredBufferSizeMono ) > ( iDesiredBufferSizeMono - iLastTrialBufSize ) ) - { - iTrialBufSize = iLastTrialBufSize; - } - - // exit while loop - bSizeFound = true; - } - - if ( !bSizeFound ) - { - // store old trial buffer size - iLastTrialBufSize = iTrialBufSize; - - // increment trial buffer size (check for special - // case first) - if ( HWBufferInfo.lGranularity == -1 ) - { - // special case: buffer sizes are a power of 2 - iTrialBufSize *= 2; - } - else - { - iTrialBufSize += HWBufferInfo.lGranularity; - } - } - } - - // clip trial buffer size (it may happen in the while - // routine that "iTrialBufSize" is larger than "lMaxSize" in - // case "lMaxSize - lMinSize" is not divisible by the - // granularity) - if ( iTrialBufSize > HWBufferInfo.lMaxSize ) - { - iTrialBufSize = HWBufferInfo.lMaxSize; - } - - // set ASIO buffer size - iActualBufferSizeMono = iTrialBufSize; + audioBuffer[2 * iCurSample + i] += pASIOBufAdd[iCurSample] * iInputGain; } } + break; } - } - - return iActualBufferSizeMono; -} - -int CSound::Init ( const int iNewPrefMonoBufferSize ) -{ - ASIOMutex.lock(); // get mutex lock - { - // get the actual sound card buffer size which is supported - // by the audio hardware - iASIOBufferSizeMono = GetActualBufferSize ( iNewPrefMonoBufferSize ); - - // init base class - CSoundBase::Init ( iASIOBufferSizeMono ); - // set internal buffer size value and calculate stereo buffer size - iASIOBufferSizeStereo = 2 * iASIOBufferSizeMono; - - // set the sample rate - ASIOSetSampleRate ( SYSTEM_SAMPLE_RATE_HZ ); - - // create memory for intermediate audio buffer - vecsMultChanAudioSndCrd.Init ( iASIOBufferSizeStereo ); - - // create and activate ASIO buffers (buffer size in samples), - // dispose old buffers (if any) - ASIODisposeBuffers(); - - // prepare input channels - for ( int i = 0; i < lNumInChan; i++ ) - { - bufferInfos[i].isInput = ASIOTrue; - bufferInfos[i].channelNum = i; - bufferInfos[i].buffers[0] = 0; - bufferInfos[i].buffers[1] = 0; - } - - // prepare output channels - for ( int i = 0; i < lNumOutChan; i++ ) - { - bufferInfos[lNumInChan + i].isInput = ASIOFalse; - bufferInfos[lNumInChan + i].channelNum = i; - bufferInfos[lNumInChan + i].buffers[0] = 0; - bufferInfos[lNumInChan + i].buffers[1] = 0; - } - - ASIOCreateBuffers ( bufferInfos, lNumInChan + lNumOutChan, iASIOBufferSizeMono, &asioCallbacks ); - - // query the latency of the driver - long lInputLatency = 0; - long lOutputLatency = 0; - - if ( ASIOGetLatencies ( &lInputLatency, &lOutputLatency ) != ASE_NotPresent ) - { - // add the input and output latencies (returned in number of - // samples) and calculate the time in ms - fInOutLatencyMs = ( static_cast ( lInputLatency ) + lOutputLatency ) * 1000 / SYSTEM_SAMPLE_RATE_HZ; - } - else - { - // no latency available - fInOutLatencyMs = 0.0f; - } - - // check whether the driver requires the ASIOOutputReady() optimization - // (can be used by the driver to reduce output latency by one block) - bASIOPostOutput = ( ASIOOutputReady() == ASE_OK ); - } - ASIOMutex.unlock(); - - return iASIOBufferSizeMono; -} - -void CSound::Start() -{ - // start audio - ASIOStart(); - - // call base class - CSoundBase::Start(); -} - -void CSound::Stop() -{ - // stop audio - ASIOStop(); - - // call base class - CSoundBase::Stop(); - - // make sure the working thread is actually done - // (by checking the locked state) - if ( ASIOMutex.tryLock ( 5000 ) ) - { - ASIOMutex.unlock(); - } -} - -CSound::CSound ( void ( *fpNewCallback ) ( CVector& psData, void* arg ), - void* arg, - const QString& strMIDISetup, - const bool, - const QString& ) : - CSoundBase ( "ASIO", fpNewCallback, arg, strMIDISetup ), - lNumInChan ( 0 ), - lNumInChanPlusAddChan ( 0 ), - lNumOutChan ( 0 ), - fInOutLatencyMs ( 0.0f ), // "0.0" means that no latency value is available - vSelectedInputChannels ( NUM_IN_OUT_CHANNELS ), - vSelectedOutputChannels ( NUM_IN_OUT_CHANNELS ) -{ - int i; - - // init pointer to our sound object - pSound = this; - - // We assume NULL'd pointers in this structure indicate that buffers are not - // allocated yet (see UnloadCurrentDriver). - memset ( bufferInfos, 0, sizeof bufferInfos ); - - // get available ASIO driver names in system - for ( i = 0; i < MAX_NUMBER_SOUND_CARDS; i++ ) - { - // allocate memory for driver names - cDriverNames[i] = new char[32]; - } - - char cDummyName[] = "dummy"; - loadAsioDriver ( cDummyName ); // to initialize external object - lNumDevs = asioDrivers->getDriverNames ( cDriverNames, MAX_NUMBER_SOUND_CARDS ); - - // in case we do not have a driver available, throw error - if ( lNumDevs == 0 ) - { - throw CGenErr ( "" + tr ( "No ASIO audio device driver found." ) + "

" + - QString ( tr ( "Please install an ASIO driver before running %1. " - "If you own a device with ASIO support, install its official ASIO driver. " - "If not, you'll need to install a universal driver like ASIO4ALL." ) ) - .arg ( APP_NAME ) ); - } - asioDrivers->removeCurrentDriver(); - - // copy driver names to base class but internally we still have to use - // the char* variable because of the ASIO API :-( - for ( i = 0; i < lNumDevs; i++ ) - { - strDriverNames[i] = cDriverNames[i]; - } - - // init device index as not initialized (invalid) - strCurDevName = ""; - - // init channel mapping - ResetChannelMapping(); - - // set up the asioCallback structure - asioCallbacks.bufferSwitch = &bufferSwitch; - asioCallbacks.sampleRateDidChange = &sampleRateChanged; - asioCallbacks.asioMessage = &asioMessages; - asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo; -} - -void CSound::ResetChannelMapping() -{ - // init selected channel numbers with defaults: use first available - // channels for input and output - vSelectedInputChannels[0] = 0; - vSelectedInputChannels[1] = 1; - vSelectedOutputChannels[0] = 0; - vSelectedOutputChannels[1] = 1; -} - -// ASIO callbacks ------------------------------------------------------------- -ASIOTime* CSound::bufferSwitchTimeInfo ( ASIOTime*, long index, ASIOBool processNow ) -{ - bufferSwitch ( index, processNow ); - return 0L; -} - -bool CSound::CheckSampleTypeSupported ( const ASIOSampleType SamType ) -{ - // check for supported sample types - return ( ( SamType == ASIOSTInt16LSB ) || ( SamType == ASIOSTInt24LSB ) || ( SamType == ASIOSTInt32LSB ) || ( SamType == ASIOSTFloat32LSB ) || - ( SamType == ASIOSTFloat64LSB ) || ( SamType == ASIOSTInt32LSB16 ) || ( SamType == ASIOSTInt32LSB18 ) || - ( SamType == ASIOSTInt32LSB20 ) || ( SamType == ASIOSTInt32LSB24 ) || ( SamType == ASIOSTInt16MSB ) || ( SamType == ASIOSTInt24MSB ) || - ( SamType == ASIOSTInt32MSB ) || ( SamType == ASIOSTFloat32MSB ) || ( SamType == ASIOSTFloat64MSB ) || ( SamType == ASIOSTInt32MSB16 ) || - ( SamType == ASIOSTInt32MSB18 ) || ( SamType == ASIOSTInt32MSB20 ) || ( SamType == ASIOSTInt32MSB24 ) ); -} - -bool CSound::CheckSampleTypeSupportedForCHMixing ( const ASIOSampleType SamType ) -{ - // check for supported sample types for audio channel mixing (see bufferSwitch) - return ( ( SamType == ASIOSTInt16LSB ) || ( SamType == ASIOSTInt24LSB ) || ( SamType == ASIOSTInt32LSB ) ); -} - -void CSound::bufferSwitch ( long index, ASIOBool ) -{ - int iCurSample; - - // get references to class members - int& iASIOBufferSizeMono = pSound->iASIOBufferSizeMono; - CVector& vecsMultChanAudioSndCrd = pSound->vecsMultChanAudioSndCrd; - - // perform the processing for input and output - pSound->ASIOMutex.lock(); // get mutex lock - { - // CAPTURE ------------------------------------------------------------- - for ( int i = 0; i < NUM_IN_OUT_CHANNELS; i++ ) - { - int iSelCH, iSelAddCH; - - GetSelCHAndAddCH ( pSound->vSelectedInputChannels[i], pSound->lNumInChan, iSelCH, iSelAddCH ); - - // copy new captured block in thread transfer buffer (copy - // mono data interleaved in stereo buffer) - switch ( pSound->channelInfosInput[iSelCH].type ) - { - case ASIOSTInt16LSB: + case ASIOSTInt24LSB: + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - // no type conversion required, just copy operation - int16_t* pASIOBuf = static_cast ( pSound->bufferInfos[iSelCH].buffers[index] ); + int iCurSam = 0; + memcpy ( &iCurSam, ( (char*) pAsioSelInput ) + iCurSample * 3, 3 ); + iCurSam >>= 8; - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = pASIOBuf[iCurSample]; - } - - if ( iSelAddCH >= 0 ) - { - // mix input channels case: - int16_t* pASIOBufAdd = static_cast ( pSound->bufferInfos[iSelAddCH].buffers[index] ); - - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = - Float2Short ( (float) vecsMultChanAudioSndCrd[2 * iCurSample + i] + (float) pASIOBufAdd[iCurSample] ); - } - } - break; + audioBuffer[2 * iCurSample + i] = static_cast ( iCurSam ) * iInputGain; } - case ASIOSTInt24LSB: - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + if ( pAsioAddInput ) + { + // mix input channels case: + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { int iCurSam = 0; - memcpy ( &iCurSam, ( (char*) pSound->bufferInfos[iSelCH].buffers[index] ) + iCurSample * 3, 3 ); + memcpy ( &iCurSam, ( (char*) pAsioAddInput ) + iCurSample * 3, 3 ); iCurSam >>= 8; - vecsMultChanAudioSndCrd[2 * iCurSample + i] = static_cast ( iCurSam ); + audioBuffer[2 * iCurSample + i] += iCurSam * iInputGain; + ; } + } + break; - if ( iSelAddCH >= 0 ) - { - // mix input channels case: - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - int iCurSam = 0; - memcpy ( &iCurSam, ( (char*) pSound->bufferInfos[iSelAddCH].buffers[index] ) + iCurSample * 3, 3 ); - iCurSam >>= 8; - - vecsMultChanAudioSndCrd[2 * iCurSample + i] = - Float2Short ( (float) vecsMultChanAudioSndCrd[2 * iCurSample + i] + (float) static_cast ( iCurSam ) ); - } - } - break; + case ASIOSTInt32LSB: + { + int32_t* pASIOBuf = static_cast ( pAsioSelInput ); - case ASIOSTInt32LSB: + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - int32_t* pASIOBuf = static_cast ( pSound->bufferInfos[iSelCH].buffers[index] ); + audioBuffer[2 * iCurSample + i] = static_cast ( pASIOBuf[iCurSample] >> 16 ) * iInputGain; + } - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = static_cast ( pASIOBuf[iCurSample] >> 16 ); - } + if ( pAsioAddInput ) + { + // mix input channels case: + int32_t* pASIOBuf = static_cast ( pAsioSelInput ); - if ( iSelAddCH >= 0 ) + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - // mix input channels case: - int32_t* pASIOBufAdd = static_cast ( pSound->bufferInfos[iSelAddCH].buffers[index] ); - - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = Float2Short ( (float) vecsMultChanAudioSndCrd[2 * iCurSample + i] + - (float) static_cast ( pASIOBufAdd[iCurSample] >> 16 ) ); - } + audioBuffer[2 * iCurSample + i] += static_cast ( pASIOBuf[iCurSample] >> 16 ) * iInputGain; } - break; } + break; + } - case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - // clang-format off + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = static_cast ( static_cast ( pAsioSelInput )[iCurSample] * _MAXSHORT ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = - static_cast ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] * _MAXSHORT ); + audioBuffer[2 * iCurSample + i] += + static_cast ( static_cast ( pAsioAddInput )[iCurSample] * _MAXSHORT ) * iInputGain; } - break; + } + break; - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - // clang-format off + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] += + static_cast ( static_cast ( pAsioSelInput )[iCurSample] * _MAXSHORT ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = - static_cast ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] * _MAXSHORT ); + audioBuffer[2 * iCurSample + i] += + static_cast ( static_cast ( pAsioAddInput )[iCurSample] * _MAXSHORT ) * iInputGain; } - break; + } + break; - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - // clang-format off + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = static_cast ( static_cast ( pAsioSelInput )[iCurSample] & 0xFFFF ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = - static_cast ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] & 0xFFFF ); + audioBuffer[2 * iCurSample + i] += + static_cast ( static_cast ( pAsioAddInput )[iCurSample] & 0xFFFF ) * iInputGain; } - break; + } + break; - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - // clang-format off + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = + static_cast ( ( static_cast ( pAsioSelInput )[iCurSample] & 0x3FFFF ) >> 2 ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = - static_cast ( ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] & 0x3FFFF ) >> 2 ); + audioBuffer[2 * iCurSample + i] += + static_cast ( ( static_cast ( pAsioAddInput )[iCurSample] & 0x3FFFF ) >> 2 ) * iInputGain; } - break; + } + break; - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - // clang-format off + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = + static_cast ( ( static_cast ( pAsioSelInput )[iCurSample] & 0xFFFFF ) >> 4 ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = - static_cast ( ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] & 0xFFFFF ) >> 4 ); + audioBuffer[2 * iCurSample + i] += + static_cast ( ( static_cast ( pAsioAddInput )[iCurSample] & 0xFFFFF ) >> 4 ) * iInputGain; } - break; + } + break; - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - // clang-format off + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = + static_cast ( ( static_cast ( pAsioSelInput )[iCurSample] & 0xFFFFFF ) >> 8 ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = - static_cast ( ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] & 0xFFFFFF ) >> 8 ); + audioBuffer[2 * iCurSample + i] += + static_cast ( ( static_cast ( pAsioAddInput )[iCurSample] & 0xFFFFFF ) >> 8 ) * iInputGain; } - break; + } + break; - case ASIOSTInt16MSB: - // clang-format off + case ASIOSTInt16MSB: + // clang-format off // NOT YET TESTED - // clang-format on - // flip bits - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + // flip bits + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = Flip16Bits ( ( static_cast ( pAsioSelInput ) )[iCurSample] ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = - Flip16Bits ( ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] ) )[iCurSample] ); + audioBuffer[2 * iCurSample + i] += Flip16Bits ( ( static_cast ( pAsioAddInput ) )[iCurSample] ) * iInputGain; } - break; + } + break; - case ASIOSTInt24MSB: - // clang-format off + case ASIOSTInt24MSB: + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // because the bits are flipped, we do not have to perform the + // shift by 8 bits + int iCurSam = 0; + memcpy ( &iCurSam, ( (char*) pAsioSelInput ) + iCurSample * 3, 3 ); + + audioBuffer[2 * iCurSample + i] = Flip16Bits ( static_cast ( iCurSam ) ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { // because the bits are flipped, we do not have to perform the // shift by 8 bits int iCurSam = 0; - memcpy ( &iCurSam, ( (char*) pSound->bufferInfos[iSelCH].buffers[index] ) + iCurSample * 3, 3 ); + memcpy ( &iCurSam, ( (char*) pAsioAddInput ) + iCurSample * 3, 3 ); - vecsMultChanAudioSndCrd[2 * iCurSample + i] = Flip16Bits ( static_cast ( iCurSam ) ); + audioBuffer[2 * iCurSample + i] += Flip16Bits ( static_cast ( iCurSam ) ) * iInputGain; } - break; + } + break; - case ASIOSTInt32MSB: - // clang-format off + case ASIOSTInt32MSB: + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // flip bits and convert to 16 bit + audioBuffer[2 * iCurSample + i] = + static_cast ( Flip32Bits ( static_cast ( pAsioSelInput )[iCurSample] ) >> 16 ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { // flip bits and convert to 16 bit - vecsMultChanAudioSndCrd[2 * iCurSample + i] = static_cast ( - Flip32Bits ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] ) >> 16 ); + audioBuffer[2 * iCurSample + i] += + static_cast ( Flip32Bits ( static_cast ( pAsioAddInput )[iCurSample] ) >> 16 ) * iInputGain; } - break; + } + break; - case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - // clang-format off + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = + static_cast ( static_cast ( Flip32Bits ( static_cast ( pAsioSelInput )[iCurSample] ) ) * _MAXSHORT ) * + iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = static_cast ( - static_cast ( Flip32Bits ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] ) ) * - _MAXSHORT ); + audioBuffer[2 * iCurSample + i] += + static_cast ( static_cast ( Flip32Bits ( static_cast ( pAsioAddInput )[iCurSample] ) ) * + _MAXSHORT ) * + iInputGain; } - break; + } + break; - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - // clang-format off + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = + static_cast ( static_cast ( Flip64Bits ( static_cast ( pAsioSelInput )[iCurSample] ) ) * _MAXSHORT ) * + iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = static_cast ( - static_cast ( Flip64Bits ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] ) ) * - _MAXSHORT ); + audioBuffer[2 * iCurSample + i] += + static_cast ( static_cast ( Flip64Bits ( static_cast ( pAsioAddInput )[iCurSample] ) ) * + _MAXSHORT ) * + iInputGain; } - break; + } + break; - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - // clang-format off + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = + static_cast ( Flip32Bits ( static_cast ( pAsioSelInput )[iCurSample] ) & 0xFFFF ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = static_cast ( - Flip32Bits ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] ) & 0xFFFF ); + audioBuffer[2 * iCurSample + i] += + static_cast ( Flip32Bits ( static_cast ( pAsioAddInput )[iCurSample] ) & 0xFFFF ) * iInputGain; } - break; + } + break; - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - // clang-format off + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = + static_cast ( ( Flip32Bits ( static_cast ( pAsioSelInput )[iCurSample] ) & 0x3FFFF ) >> 2 ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = static_cast ( - ( Flip32Bits ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] ) & 0x3FFFF ) >> 2 ); + audioBuffer[2 * iCurSample + i] += + static_cast ( ( Flip32Bits ( static_cast ( pAsioAddInput )[iCurSample] ) & 0x3FFFF ) >> 2 ) * iInputGain; } - break; + } + break; - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - // clang-format off + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = + static_cast ( ( Flip32Bits ( static_cast ( pAsioSelInput )[iCurSample] ) & 0xFFFFF ) >> 4 ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = static_cast ( - ( Flip32Bits ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] ) & 0xFFFFF ) >> 4 ); + audioBuffer[2 * iCurSample + i] += + static_cast ( ( Flip32Bits ( static_cast ( pAsioAddInput )[iCurSample] ) & 0xFFFFF ) >> 4 ) * iInputGain; } - break; + } + break; - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - // clang-format off + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + audioBuffer[2 * iCurSample + i] = + static_cast ( ( Flip32Bits ( static_cast ( pAsioSelInput )[iCurSample] ) & 0xFFFFFF ) >> 8 ) * iInputGain; + } + + if ( pAsioAddInput ) + { + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - vecsMultChanAudioSndCrd[2 * iCurSample + i] = static_cast ( - ( Flip32Bits ( static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] ) & 0xFFFFFF ) >> 8 ); + audioBuffer[2 * iCurSample + i] += + static_cast ( ( Flip32Bits ( static_cast ( pAsioAddInput )[iCurSample] ) & 0xFFFFFF ) >> 8 ) * iInputGain; } - break; } + break; } + } - // call processing callback function - pSound->ProcessCallback ( vecsMultChanAudioSndCrd ); + // call processing callback function + processCallback ( audioBuffer ); - // PLAYBACK ------------------------------------------------------------ - for ( int i = 0; i < NUM_IN_OUT_CHANNELS; i++ ) + // PLAYBACK ------------------------------------------------------------ + for ( unsigned int i = 0; i < PROT_NUM_OUT_CHANNELS; i++ ) + { + const int iSelCH = lNumInChan + selectedOutputChannels[i]; + void* pAsioSelOutput = bufferInfos[iSelCH].buffers[index]; + + // copy data from sound card in output buffer (copy + // interleaved stereo data in mono sound card buffer) + switch ( asioDriver.outputChannelInfo ( selectedOutputChannels[i] ).type ) + { + case ASIOSTInt16LSB: { - const int iSelCH = pSound->lNumInChan + pSound->vSelectedOutputChannels[i]; + // no type conversion required, just copy operation + int16_t* pASIOBuf = static_cast ( pAsioSelOutput ); - // copy data from sound card in output buffer (copy - // interleaved stereo data in mono sound card buffer) - switch ( pSound->channelInfosOutput[pSound->vSelectedOutputChannels[i]].type ) + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) { - case ASIOSTInt16LSB: - { - // no type conversion required, just copy operation - int16_t* pASIOBuf = static_cast ( pSound->bufferInfos[iSelCH].buffers[index] ); - - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - pASIOBuf[iCurSample] = vecsMultChanAudioSndCrd[2 * iCurSample + i]; - } - break; + pASIOBuf[iCurSample] = audioBuffer[iCurSample * 2 + i]; } + break; + } - case ASIOSTInt24LSB: - // clang-format off + case ASIOSTInt24LSB: + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert current sample in 24 bit format - int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert current sample in 24 bit format + int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - iCurSam <<= 8; + iCurSam <<= 8; - memcpy ( ( (char*) pSound->bufferInfos[iSelCH].buffers[index] ) + iCurSample * 3, &iCurSam, 3 ); - } - break; + memcpy ( ( (char*) pAsioSelOutput ) + iCurSample * 3, &iCurSam, 3 ); + } + break; - case ASIOSTInt32LSB: - { - int32_t* pASIOBuf = static_cast ( pSound->bufferInfos[iSelCH].buffers[index] ); + case ASIOSTInt32LSB: + { + int32_t* pASIOBuf = static_cast ( pAsioSelOutput ); - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit - const int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit + const int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - pASIOBuf[iCurSample] = ( iCurSam << 16 ); - } - break; + pASIOBuf[iCurSample] = ( iCurSam << 16 ); } + break; + } - case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - // clang-format off + case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - const float fCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + const float fCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = fCurSam / _MAXSHORT; - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = fCurSam / _MAXSHORT; + } + break; - case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - // clang-format off + case ASIOSTFloat64LSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - const double fCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + const double fCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = fCurSam / _MAXSHORT; - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = fCurSam / _MAXSHORT; + } + break; - case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment - // clang-format off + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit - const int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit + const int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = iCurSam; - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = iCurSam; + } + break; - case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment - // clang-format off + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit - const int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit + const int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = ( iCurSam << 2 ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = ( iCurSam << 2 ); + } + break; - case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment - // clang-format off + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit - const int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit + const int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = ( iCurSam << 4 ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = ( iCurSam << 4 ); + } + break; - case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment - // clang-format off + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit - const int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit + const int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = ( iCurSam << 8 ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = ( iCurSam << 8 ); + } + break; - case ASIOSTInt16MSB: - // clang-format off + case ASIOSTInt16MSB: + // clang-format off // NOT YET TESTED - // clang-format on - // flip bits - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - ( (int16_t*) pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = - Flip16Bits ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); - } - break; + // clang-format on + // flip bits + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + ( (int16_t*) pAsioSelOutput )[iCurSample] = Flip16Bits ( audioBuffer[iCurSample * 2 + i] ); + } + break; - case ASIOSTInt24MSB: - // clang-format off + case ASIOSTInt24MSB: + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // because the bits are flipped, we do not have to perform the - // shift by 8 bits - int32_t iCurSam = static_cast ( Flip16Bits ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ) ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // because the bits are flipped, we do not have to perform the + // shift by 8 bits + int32_t iCurSam = static_cast ( Flip16Bits ( audioBuffer[iCurSample * 2 + i] ) ); - memcpy ( ( (char*) pSound->bufferInfos[iSelCH].buffers[index] ) + iCurSample * 3, &iCurSam, 3 ); - } - break; + memcpy ( ( (char*) pAsioSelOutput ) + iCurSample * 3, &iCurSam, 3 ); + } + break; - case ASIOSTInt32MSB: - // clang-format off + case ASIOSTInt32MSB: + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit and flip bits - int iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit and flip bits + int iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam << 16 ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = Flip32Bits ( iCurSam << 16 ); + } + break; - case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture - // clang-format off + case ASIOSTFloat32MSB: // IEEE 754 32 bit float, as found on Intel x86 architecture + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - const float fCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + const float fCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = - static_cast ( Flip32Bits ( static_cast ( fCurSam / _MAXSHORT ) ) ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = + static_cast ( Flip32Bits ( static_cast ( fCurSam / _MAXSHORT ) ) ); + } + break; - case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture - // clang-format off + case ASIOSTFloat64MSB: // IEEE 754 64 bit double float, as found on Intel x86 architecture + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - const double fCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + const double fCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = - static_cast ( Flip64Bits ( static_cast ( fCurSam / _MAXSHORT ) ) ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = + static_cast ( Flip64Bits ( static_cast ( fCurSam / _MAXSHORT ) ) ); + } + break; - case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment - // clang-format off + case ASIOSTInt32MSB16: // 32 bit data with 16 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit - const int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit + const int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = Flip32Bits ( iCurSam ); + } + break; - case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment - // clang-format off + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit - const int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit + const int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam << 2 ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = Flip32Bits ( iCurSam << 2 ); + } + break; - case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment - // clang-format off + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit - const int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit + const int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam << 4 ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = Flip32Bits ( iCurSam << 4 ); + } + break; - case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment - // clang-format off + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + // clang-format off // NOT YET TESTED - // clang-format on - for ( iCurSample = 0; iCurSample < iASIOBufferSizeMono; iCurSample++ ) - { - // convert to 32 bit - const int32_t iCurSam = static_cast ( vecsMultChanAudioSndCrd[2 * iCurSample + i] ); + // clang-format on + for ( unsigned int iCurSample = 0; iCurSample < iDeviceBufferSize; iCurSample++ ) + { + // convert to 32 bit + const int32_t iCurSam = static_cast ( audioBuffer[iCurSample * 2 + i] ); - static_cast ( pSound->bufferInfos[iSelCH].buffers[index] )[iCurSample] = Flip32Bits ( iCurSam << 8 ); - } - break; + static_cast ( pAsioSelOutput )[iCurSample] = Flip32Bits ( iCurSam << 8 ); } + break; } + } - // Finally if the driver supports the ASIOOutputReady() optimization, - // do it here, all data are in place ----------------------------------- - if ( pSound->bASIOPostOutput ) - { - ASIOOutputReady(); - } + // Finally if the driver supports the ASIOOutputReady() optimization, + // do it here, all data are in place now + if ( bASIOPostOutput ) + { + asioDriver.outputReady(); + } +} + +ASIOTime* CSound::onBufferSwitchTimeInfo ( ASIOTime*, long index, ASIOBool processNow ) +{ + onBufferSwitch ( index, processNow ); + return 0L; +} + +void CSound::onSampleRateChanged ( ASIOSampleRate sampleRate ) +{ + if ( sampleRate != SYSTEM_SAMPLE_RATE_HZ ) + { + Stop(); + // pgScorpio: implement error ??? + // how to notify CClient ?? } - pSound->ASIOMutex.unlock(); } -long CSound::asioMessages ( long selector, long, void*, double* ) +long CSound::onAsioMessages ( long selector, long, void*, double* ) { long ret = 0; @@ -1151,12 +805,12 @@ long CSound::asioMessages ( long selector, long, void*, double* ) // both messages might be send if the buffer size changes case kAsioBufferSizeChange: - pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART_AND_INIT ); + emit reinitRequest ( tSndCrdResetType::RS_ONLY_RESTART_AND_INIT ); ret = 1L; // 1L if request is accepted or 0 otherwise break; case kAsioResetRequest: - pSound->EmitReinitRequestSignal ( RS_RELOAD_RESTART_AND_INIT ); + emit reinitRequest ( tSndCrdResetType::RS_RELOAD_RESTART_AND_INIT ); ret = 1L; // 1L if request is accepted or 0 otherwise break; } @@ -1164,56 +818,461 @@ long CSound::asioMessages ( long selector, long, void*, double* ) return ret; } -int16_t CSound::Flip16Bits ( const int16_t iIn ) +//============================================================================ +// CSound Internal +//============================================================================ + +//==================================== +// ASIO Stuff +//==================================== + +void CSound::closeAllAsioDrivers() +{ + newAsioDriver.Close(); + closeCurrentDevice(); // Closes asioDriver and clears DeviceInfo + + // Just make sure all other driver in the list are closed too... + for ( long i = 0; i < lNumDevices; i++ ) + { + CloseAsioDriver ( &asioDriverData[i] ); + } +} + +bool CSound::prepareAsio ( bool bStartAsio ) +{ + bool ok = true; + + // dispose old buffers (if any) + asioDriver.disposeBuffers(); // implies ASIOStop() ! + + // create memory for intermediate audio buffer + audioBuffer.Init ( PROT_NUM_IN_CHANNELS * iDeviceBufferSize ); + + long numInputChannels = asioDriver.numInputChannels(); + long numOutputChannels = asioDriver.numOutputChannels(); + + long bufferIndex = 0; + + // prepare input channels + for ( int i = 0; i < numInputChannels; i++ ) + { + bufferInfos[bufferIndex].isInput = ASIO_INPUT; + bufferInfos[bufferIndex].channelNum = i; + bufferInfos[bufferIndex].buffers[0] = 0; + bufferInfos[bufferIndex].buffers[1] = 0; + bufferIndex++; + } + + // prepare output channels + for ( int i = 0; i < numOutputChannels; i++ ) + { + bufferInfos[bufferIndex].isInput = ASIO_OUTPUT; + bufferInfos[bufferIndex].channelNum = i; + bufferInfos[bufferIndex].buffers[0] = 0; + bufferInfos[bufferIndex].buffers[1] = 0; + bufferIndex++; + } + + // create and activate ASIO buffers (buffer size in samples), + if ( !asioDriver.createBuffers ( bufferInfos, bufferIndex, iDeviceBufferSize, &asioCallbacks ) ) + { + ok = false; + } + + // check whether the driver requires the ASIOOutputReady() optimization + // (can be used by the driver to reduce output latency by one block) + bASIOPostOutput = ( asioDriver.outputReady() == ASE_OK ); + + // set the sample rate (make sure we get the correct latency) + if ( !asioDriver.setSampleRate ( SYSTEM_SAMPLE_RATE_HZ ) ) + { + ok = false; + } + + // Query the latency of the driver + long lInputLatency = 0; + long lOutputLatency = 0; + // Latency is returned in number of samples + if ( !asioDriver.getLatencies ( &lInputLatency, &lOutputLatency ) ) + { + // No latency available + // Assume just the buffer delay + lInputLatency = lOutputLatency = iDeviceBufferSize; + } + + // Calculate total latency in ms + { + float fLatency; + + fLatency = lInputLatency; + fLatency += lOutputLatency; + fLatency *= 1000; + fLatency /= SYSTEM_SAMPLE_RATE_HZ; + + fInOutLatencyMs = fLatency; + } + + if ( ok && bStartAsio ) + { + return asioDriver.start(); + } + + return ok; +} + +unsigned int CSound::getDeviceBufferSize ( unsigned int iDesiredBufferSize ) +{ + int iActualBufferSize = iDesiredBufferSize; + + // Limits to be obtained from the asio driver. + // Init all to zero, just in case the asioDriver.getBufferSize call fails. + long lMinSize = 0; + long lMaxSize = 0; + long lPreferredSize = 0; + long lGranularity = 0; + + // query the asio driver + asioDriver.getBufferSize ( &lMinSize, &lMaxSize, &lPreferredSize, &lGranularity ); + + // calculate "nearest" buffer size within limits + // + // first check minimum and maximum limits + if ( iActualBufferSize < lMinSize ) + { + iActualBufferSize = lMinSize; + } + + if ( iActualBufferSize > lMaxSize ) + { + iActualBufferSize = lMaxSize; + } + + if ( iActualBufferSize == lPreferredSize ) + { + // No need to check any further + return iActualBufferSize; + } + + if ( lMaxSize > lMinSize ) // Do we have any choice ?? + { + if ( lGranularity == -1 ) + { + // Size must be a power of 2 + // So lMinSize and lMaxSize are expected be powers of 2 too! + // Start with lMinSize... + long lTrialBufSize = lMinSize; + do + { + // And double this size... + lTrialBufSize *= 2; + // Until we get above the maximum size or above the current actual (requested) size... + } while ( ( lTrialBufSize < lMaxSize ) && ( lTrialBufSize < iActualBufferSize ) ); + + // Now take the previous power of two... + iActualBufferSize = ( lTrialBufSize / 2 ); + } + else if ( lGranularity > 1 ) + { + // Size must be a multiple of lGranularity, + // Round to a lGranularity multiple by determining any remainder + long remainder = ( iActualBufferSize % lGranularity ); + if ( remainder ) + { + // Round down by subtracting the remainder... + iActualBufferSize -= remainder; + + // Use rounded down size if this is actually the preferred buffer size + if ( iActualBufferSize == lPreferredSize ) + { + return iActualBufferSize; + } + + // Otherwise see if we should, and could, have rounded up... + if ( ( remainder >= ( lGranularity / 2 ) ) && // Should we have rounded up ? + ( iActualBufferSize <= ( lMaxSize - lGranularity ) ) // And can we round up ? + ) + { + // Change rounded down size to rounded up size by adding 1 granularity... + iActualBufferSize += lGranularity; + } + } + } + // There are no definitions for other values of granularity in the ASIO SDK ! + // Assume everything else should never occur and is OK as long as the size is within min/max limits. + } + + return iActualBufferSize; +} + +bool CSound::checkNewDeviceCapabilities() +{ + // This function checks if our required input/output channel + // properties are supported by the selected device. If the return + // string is empty, the device can be used, otherwise the error + // message is returned. + bool ok = true; + + if ( !newAsioDriver.IsOpen() ) + { + strErrorList.append ( QString ( "Coding Error: Calling CheckDeviceCapabilities() with no newAsioDriver open! " ) ); + return false; + } + + // check the sample rate + if ( !newAsioDriver.canSampleRate ( SYSTEM_SAMPLE_RATE_HZ ) ) + { + ok = false; + // return error string + strErrorList.append ( tr ( "The selected audio device does not support a sample rate of %1 Hz. " ).arg ( SYSTEM_SAMPLE_RATE_HZ ) ); + } + else + { + // check if sample rate can be set + if ( !newAsioDriver.setSampleRate ( SYSTEM_SAMPLE_RATE_HZ ) ) + { + ok = false; + strErrorList.append ( tr ( "The audio devices sample rate could not be set to %2 Hz. " ).arg ( SYSTEM_SAMPLE_RATE_HZ ) ); + } + } + + if ( newAsioDriver.numInputChannels() < DRV_MIN_IN_CHANNELS ) + { + ok = false; + strErrorList.append ( tr ( "The selected audio device doesn not support at least" + "%1 %2 channel(s)." ) + .arg ( DRV_MIN_IN_CHANNELS ) + .arg ( tr ( "input" ) ) ); + } + + if ( newAsioDriver.numOutputChannels() < DRV_MIN_OUT_CHANNELS ) + { + ok = false; + strErrorList.append ( tr ( "The selected audio device doesn not support at least" + "%1 %2 channel(s)." ) + .arg ( DRV_MIN_OUT_CHANNELS ) + .arg ( tr ( "output" ) ) ); + } + + // query channel infos for all available input channels + bool channelOk = true; + for ( int i = 0; i < newAsioDriver.numInputChannels(); i++ ) + { + const ASIOChannelInfo& channel = newAsioDriver.inputChannelInfo ( i ); + + if ( !SampleTypeSupported ( channel.type ) ) + { + // return error string + channelOk = false; + } + } + + if ( !channelOk ) + { + ok = false; + strErrorList.append ( tr ( "The selected audio device is incompatible since " + "the required %1 audio sample format isn't available." ) + .arg ( tr ( "input)" ) ) ); + } + + // query channel infos for all available output channels + channelOk = true; + for ( int i = 0; i < newAsioDriver.numOutputChannels(); i++ ) + { + const ASIOChannelInfo& channel = newAsioDriver.outputChannelInfo ( i ); + + // Check supported sample formats. + // Actually, it would be enough to have at least two channels which + // support the required sample format. But since we have support for + // all known sample types, the following check should always pass and + // therefore we throw the error message on any channel which does not + // fulfill the sample format requirement (quick hack solution). + if ( !SampleTypeSupported ( channel.type ) ) + { + channelOk = false; + } + } + + if ( !channelOk ) + { + ok = false; + strErrorList.append ( tr ( "The selected audio device is incompatible since " + "the required %1 audio sample format isn't available." ) + .arg ( tr ( "output)" ) ) ); + } + + // special case with 4 input channels: support adding channels + // pgScorpio: What the hack is this for ??? Only used for Jack?? But mixing can be done in Jack! KISS! + if ( ( newAsioDriver.numInputChannels() > 2 ) ) + { + // Total name size for AddedChannelData + const long addednamesize = ( sizeof ( ASIOAddedChannelInfo::ChannelData.name ) + sizeof ( ASIOAddedChannelInfo::AddedName ) ); + + // add mixed channels (i.e. 4 normal, 4 mixed channels) + const long numInputChan = newAsioDriver.numInputChannels(); + const long numAddedChan = getNumInputChannelsToAdd ( numInputChan ); + ASIOAddedChannelInfo* addedChannel = newAsioDriver.setNumAddedInputChannels ( numAddedChan ); + + for ( long i = 0; i < numAddedChan; i++ ) + { + int iSelChan, iAddChan; + + getInputSelAndAddChannels ( numInputChan + i, numInputChan, numAddedChan, iSelChan, iAddChan ); + + char* combinedName = addedChannel[i].ChannelData.name; + const char* firstName = newAsioDriver.inputChannelInfo ( iSelChan ).name; + const char* secondName = newAsioDriver.inputChannelInfo ( iAddChan ).name; + + if ( ( iSelChan >= 0 ) && ( iAddChan >= 0 ) ) + { + strncpy_s ( combinedName, addednamesize, firstName, sizeof ( ASIOChannelInfo::name ) ); + + strcat_s ( combinedName, addednamesize, " + " ); + + strncat_s ( combinedName, addednamesize, secondName, sizeof ( ASIOChannelInfo::name ) ); + } + } + } + + newAsioDriver.openData.capabilitiesOk = ok; + + return ok; +} + +//============================================================================ +// CSoundBase internal +//============================================================================ + +long CSound::createDeviceList ( bool bRescan ) { - uint16_t iMask = ( 1 << 15 ); - int16_t iOut = 0; - for ( unsigned int i = 0; i < 16; i++ ) + if ( bRescan && asioDriversLoaded ) + { + closeAllAsioDrivers(); + asioDriversLoaded = false; + } + + if ( !asioDriversLoaded ) { - // copy current bit to correct position - iOut |= ( iIn & iMask ) ? 1 : 0; + strDeviceNames.clear(); + lNumDevices = GetAsioDriverDataList ( asioDriverData ); + for ( long i = 0; i < lNumDevices; i++ ) + { + strDeviceNames.append ( asioDriverData[i].drvname ); + } - // shift out value and mask by one bit - iOut <<= 1; - iMask >>= 1; + asioDriversLoaded = ( lNumDevices != 0 ); + + return lNumDevices; } - return iOut; + return lNumDevices; } -int32_t CSound::Flip32Bits ( const int32_t iIn ) +bool CSound::checkDeviceChange ( tDeviceChangeCheck mode, int iDriverIndex ) { - uint32_t iMask = ( static_cast ( 1 ) << 31 ); - int32_t iOut = 0; + if ( ( iDriverIndex < 0 ) || ( iDriverIndex >= lNumDevices ) ) + { + // Invalid index ! + return false; + } + + if ( ( mode != CSoundBase::tDeviceChangeCheck::CheckOpen ) && ( !newAsioDriver.IsOpen() ) ) + { + // All other modes are only valid if a newDriver is successfully opened first. + return false; + } - for ( unsigned int i = 0; i < 32; i++ ) + switch ( mode ) { - // copy current bit to correct position - iOut |= ( iIn & iMask ) ? 1 : 0; + case tDeviceChangeCheck::Abort: // Not changing to newDRiver ! + return newAsioDriver.Close(); + + case tDeviceChangeCheck::CheckOpen: // Open the device we are gona check + newAsioDriver.AssignFrom ( &asioDriverData[iDriverIndex] ); + return newAsioDriver.Open(); + + case tDeviceChangeCheck::Activate: // Actually changing current device ! + if ( asioDriver.AssignFrom ( &newAsioDriver ) ) + { + clearDeviceInfo(); + + lNumInChan = asioDriver.openData.lNumInChan; + lNumAddedInChan = asioDriver.openData.lNumAddedInChan; + + lNumOutChan = asioDriver.openData.lNumOutChan; + + asioDriver.GetInputChannelNames ( strInputChannelNames ); + asioDriver.GetOutputChannelNames ( strOutputChannelNames ); - // shift out value and mask by one bit - iOut <<= 1; - iMask >>= 1; + iCurrentDevice = asioDriver.index; + resetChannelMapping(); + // pgScorpio TODO: read ChannelMapping from ini settings ! + + return true; + } + return false; + + case tDeviceChangeCheck::CheckCapabilities: + return checkNewDeviceCapabilities(); + + default: + return false; + } +} + +void CSound::closeCurrentDevice() +{ + if ( IsStarted() ) + { + Stop(); } - return iOut; + asioDriver.disposeBuffers(); + asioDriver.Close(); + + clearDeviceInfo(); + memset ( bufferInfos, 0, sizeof ( bufferInfos ) ); } -int64_t CSound::Flip64Bits ( const int64_t iIn ) +//============================================================================ +// CSoundBase Interface to CClient: +//============================================================================ + +bool CSound::start() { - uint64_t iMask = ( static_cast ( 1 ) << 63 ); - int64_t iOut = 0; + if ( !IsStarted() ) + { + // prepare start and try to start asio + if ( prepareAsio ( true ) ) + { + strErrorList.clear(); + + return true; + } + else + { + // We failed!, asio is stopped ! + // notify base class + strErrorList.clear(); + strErrorList.append ( htmlBold ( tr ( "Failed to start your audio device!." ) ) ); + strErrorList.append ( tr ( "Please check your device settings..." ) ); + + return false; + } + } + + return IsStarted(); +} - for ( unsigned int i = 0; i < 64; i++ ) +bool CSound::stop() +{ + if ( IsStarted() ) { - // copy current bit to correct position - iOut |= ( iIn & iMask ) ? 1 : 0; + // stop audio + asioDriver.stop(); - // shift out value and mask by one bit - iOut <<= 1; - iMask >>= 1; + return true; } - return iOut; + return !IsStarted(); } diff --git a/windows/sound.h b/windows/sound.h index 1ca7ed43b2..15f8af302e 100644 --- a/windows/sound.h +++ b/windows/sound.h @@ -2,7 +2,7 @@ * Copyright (c) 2004-2022 * * Author(s): - * Volker Fischer + * Volker Fischer, revised and maintained by Peter Goderie (pgScorpio) * ****************************************************************************** * @@ -24,106 +24,107 @@ #pragma once -#include -#include +//============================================================================ +// System includes +//============================================================================ + #include "../src/util.h" #include "../src/global.h" #include "../src/soundbase.h" -// copy the ASIO SDK in the llcon/windows directory: "llcon/windows/ASIOSDK2" to -// get it work +//============================================================================ +// ASIO includes +//============================================================================ + #include "asiosys.h" -#include "asio.h" -#include "asiodrivers.h" +#include "asiodriver.h" -/* Definitions ****************************************************************/ -// stereo for input and output -#define NUM_IN_OUT_CHANNELS 2 +//============================================================================ +// CSound +//============================================================================ -/* Classes ********************************************************************/ class CSound : public CSoundBase { Q_OBJECT public: - CSound ( void ( *fpNewCallback ) ( CVector& psData, void* arg ), void* arg, const QString& strMIDISetup, const bool, const QString& ); + CSound ( void ( *fpProcessCallback ) ( CVector& psData, void* pCallbackArg ), void* pProcessCallBackParg ); - virtual ~CSound() { UnloadCurrentDriver(); } + virtual ~CSound() + { + closeCurrentDevice(); + closeAllAsioDrivers(); + } - virtual int Init ( const int iNewPrefMonoBufferSize ); - virtual void Start(); - virtual void Stop(); +protected: + // ASIO data + bool bASIOPostOutput; - virtual void OpenDriverSetup() { ASIOControlPanel(); } + bool asioDriversLoaded; + tVAsioDrvDataList asioDriverData; - // channel selection - virtual int GetNumInputChannels() { return static_cast ( lNumInChanPlusAddChan ); } - virtual QString GetInputChannelName ( const int iDiD ) { return channelInputName[iDiD]; } - virtual void SetLeftInputChannel ( const int iNewChan ); - virtual void SetRightInputChannel ( const int iNewChan ); - virtual int GetLeftInputChannel() { return vSelectedInputChannels[0]; } - virtual int GetRightInputChannel() { return vSelectedInputChannels[1]; } + CAsioDriver asioDriver; // The current selected asio driver + CAsioDriver newAsioDriver; // the new asio driver opened by checkDeviceChange(CheckOpen, ...), + // to be checked by checkDeviceChange(CheckCapabilities, ...) + // and set as asioDriver by checkDeviceChange(Activate, ...) or closed by checkDeviceChange(Abort, ...) - virtual int GetNumOutputChannels() { return static_cast ( lNumOutChan ); } - virtual QString GetOutputChannelName ( const int iDiD ) { return channelInfosOutput[iDiD].name; } - virtual void SetLeftOutputChannel ( const int iNewChan ); - virtual void SetRightOutputChannel ( const int iNewChan ); - virtual int GetLeftOutputChannel() { return vSelectedOutputChannels[0]; } - virtual int GetRightOutputChannel() { return vSelectedOutputChannels[1]; } + ASIOBufferInfo bufferInfos[DRV_MAX_NUM_IN_CHANNELS + DRV_MAX_NUM_OUT_CHANNELS]; + // for input + output buffers. pgScorpio: I think we actually only need 6: 2 outputs, 2 inputs + // and possibly 2 extra inputs when when mixing I think there is no need to create buffers for + // unused in- and outputs ! (if we re-create them on change of channel selection.) - virtual float GetInOutLatencyMs() { return fInOutLatencyMs; } +protected: + // ASIO callback implementations + void onBufferSwitch ( long index, ASIOBool processNow ); + ASIOTime* onBufferSwitchTimeInfo ( ASIOTime* timeInfo, long index, ASIOBool processNow ); + void onSampleRateChanged ( ASIOSampleRate sampleRate ); + long onAsioMessages ( long selector, long value, void* message, double* opt ); protected: - virtual QString LoadAndInitializeDriver ( QString strDriverName, bool bOpenDriverSetup ); - virtual void UnloadCurrentDriver(); - int GetActualBufferSize ( const int iDesiredBufferSizeMono ); - QString CheckDeviceCapabilities(); - bool CheckSampleTypeSupported ( const ASIOSampleType SamType ); - bool CheckSampleTypeSupportedForCHMixing ( const ASIOSampleType SamType ); - void ResetChannelMapping(); - - int iASIOBufferSizeMono; - int iASIOBufferSizeStereo; - - long lNumInChan; - long lNumInChanPlusAddChan; // includes additional "added" channels - long lNumOutChan; - float fInOutLatencyMs; - CVector vSelectedInputChannels; - CVector vSelectedOutputChannels; - - CVector vecsMultChanAudioSndCrd; - - QMutex ASIOMutex; - - // utility functions - static int16_t Flip16Bits ( const int16_t iIn ); - static int32_t Flip32Bits ( const int32_t iIn ); - static int64_t Flip64Bits ( const int64_t iIn ); - - // audio hardware buffer info - struct sHWBufferInfo + // callbacks + + static ASIOCallbacks asioCallbacks; + + static void _onBufferSwitch ( long index, ASIOBool processNow ) { pSound->onBufferSwitch ( index, processNow ); } + + static ASIOTime* _onBufferSwitchTimeInfo ( ASIOTime* timeInfo, long index, ASIOBool processNow ) { - long lMinSize; - long lMaxSize; - long lPreferredSize; - long lGranularity; - } HWBufferInfo; - - // ASIO stuff - ASIODriverInfo driverInfo; - ASIOBufferInfo bufferInfos[2 * MAX_NUM_IN_OUT_CHANNELS]; // for input and output buffers -> "2 *" - ASIOChannelInfo channelInfosInput[MAX_NUM_IN_OUT_CHANNELS]; - QString channelInputName[MAX_NUM_IN_OUT_CHANNELS]; - ASIOChannelInfo channelInfosOutput[MAX_NUM_IN_OUT_CHANNELS]; - bool bASIOPostOutput; - ASIOCallbacks asioCallbacks; + return pSound->onBufferSwitchTimeInfo ( timeInfo, index, processNow ); + } - // callbacks - static void bufferSwitch ( long index, ASIOBool processNow ); - static ASIOTime* bufferSwitchTimeInfo ( ASIOTime* timeInfo, long index, ASIOBool processNow ); - static void sampleRateChanged ( ASIOSampleRate ) {} - static long asioMessages ( long selector, long value, void* message, double* opt ); + static void _onSampleRateChanged ( ASIOSampleRate sampleRate ) { pSound->onSampleRateChanged ( sampleRate ); } + + static long _onAsioMessages ( long selector, long value, void* message, double* opt ) + { + return pSound->onAsioMessages ( selector, value, message, opt ); + } + +protected: + // CSound Internal + void closeAllAsioDrivers(); + bool prepareAsio ( bool bStartAsio ); // Called before starting + + bool checkNewDeviceCapabilities(); // used by checkDeviceChange( checkCapabilities, iDriverIndex) + + //============================================================================ + // Virtual interface to CSoundBase: + //============================================================================ +protected: // CSoundBase Mandatory pointer to instance (must be set to 'this' in the CSound constructor) + static CSound* pSound; + +public: // CSoundBase Mandatory functions. (but static functions can't be virtual) + static inline CSoundBase* pInstance() { return pSound; } + static inline const CSoundProperties& GetProperties() { return pSound->getSoundProperties(); } + +protected: + // CSoundBase internal + virtual long createDeviceList ( bool bRescan = false ); // Fills strDeviceList. Returns number of devices found + virtual bool checkDeviceChange ( CSoundBase::tDeviceChangeCheck mode, int iDriverIndex ); // Open device sequence handling.... + virtual unsigned int getDeviceBufferSize ( unsigned int iDesiredBufferSize ); + + virtual void closeCurrentDevice(); // Closes the current driver and Clears Device Info + virtual bool openDeviceSetup() { return asioDriver.controlPanel(); } - char* cDriverNames[MAX_NUMBER_SOUND_CARDS]; + virtual bool start(); + virtual bool stop(); };