& 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