Skip to content
Snippets Groups Projects
Select Git revision
  • main
  • gitkeep
  • dev
  • ipynb
  • 81-add-id-to-figure-file-metadata
  • v0.3.2
  • v0.3.1
  • v0.3.0
  • v0.2.3
  • test_tag
  • v0.2.2
  • v.0.2.1
  • v0.2.1
  • v0.1.2
  • v0.1.1
  • v0.1.0
16 results

CITATION.cff

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    ITAAsioInterface.cpp 38.40 KiB
    #include <ITAAsioInterface.h>
    
    #include <ITAStreamInfo.h>
    #include <ITADatasource.h>
    #include <ITADatasourceRealization.h>
    
    #include <ITAAtomicPrimitives.h>
    #include <ITAClock.h>
    #include <ITADatasource.h>
    #include <ITAException.h>
    #include <ITAStopWatch.h>
    #include <ITAStringUtils.h>
    
    #include <host/asiodrivers.h>
    
    #include <windows.h>
    #include <iostream>
    #include <vector>
    
    
    // Puffer-Vielfachheit (Doppelpuffer, Dreifachpuffer, etc.)
    const unsigned int ASIOSOURCE_BUFFER_MULTIPLICITY = 16;
    
    class ASIOSource : public ITADatasourceRealization
    {
    public:
    	inline ASIOSource( unsigned int uiChannels, double dSamplerate, unsigned int uiBlocklength )
    		: ITADatasourceRealization( uiChannels, dSamplerate, uiBlocklength, ASIOSOURCE_BUFFER_MULTIPLICITY )
    	{};
    
    	friend ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool processNow );
    };
    
    // WICHTIG: Dieses Makro legt fest, wieviele Blöcke Eingangsdaten nach
    //          dem Start des Streamings durch ASIOStart() verworfen werden.
    #define NUM_INPUT_BLOCKS_TO_DISCARD 2
    
    // WICHTIG: Dieses Makro legt fest, wieviele Blöcke Nulldaten nach
    //          dem Stoppen des Streamings durch ASIOStop() noch abgespielt werden.
    #define NUM_ZERO_BLOCKS_AFTER_STOP 2
    
    #define DEBUG_OUT 0
    
    
    // conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
    #if NATIVE_INT64
    #define ASIO64toDouble(a)  (a)
    #else
    const double twoRaisedTo32 = 4294967296.;
    #define ASIO64toDouble(a)  ((a).lo + (a).hi * twoRaisedTo32)
    #endif
    
    typedef struct DriverInfo
    {
    	// Zugehörige ASIO-Treiberinfo (Achtung: Datentyp "ASIODriverInfo")
    	ASIODriverInfo driverInfo;
    
    	// Anzahl der Kanäle
    	long           inputChannels;
    	long           outputChannels;
    
    	// Puffergrößen
    	long           minSize;
    	long           maxSize;
    	long           preferredSize;
    	long           granularity;
    
    	// Samplerate
    	ASIOSampleRate sampleRate;
    
    	// ASIOOutputReady()
    	bool           postOutput;
    
    	// Latenzen
    	long           inputLatency;
    	long           outputLatency;
    
    	// Anzahl der Ein-/Ausgabepuffer
    	long inputBuffers;	// becomes number of actual created input buffers
    	long outputBuffers;	// becomes number of actual created output buffers
    	//	ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's
    	std::vector<ASIOBufferInfo> bufferInfos; // buffer info's
    
    	// ASIOGetChannelInfo()
    	//	ASIOChannelInfo channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's
    	std::vector<ASIOChannelInfo> channelInfos; // channel info's
    	// The above two arrays share the same indexing, as the data in them are linked together
    
    	// Information from ASIOGetSamplePosition()
    	// data is converted to double floats for easier use, however 64 bit integer can be used, too
    	double         nanoSeconds;
    	double         samples;
    	double         tcSamples;	// time code samples
    
    	// bufferSwitchTimeInfo()
    	ASIOTime       tInfo;			// time info state
    	unsigned long  sysRefTime;      // system reference time, when bufferSwitch() was called
    
    	// Signal the end of processing in this example
    	bool           stopped;
    } DriverInfo;
    
    // Statusflags
    const long LIBRAW = -1;	// Bibliothek wurde noch nicht initialisiert
    const long LOADED = 0;	// Bibliothek wurde initialisiert
    const long INITIALIZED = 1;	// ASIO-Treiber wurde initialisiert
    const long PREPARED = 2;	// ASIO-Streaming wurde vorbereitet
    const long RUNNING = 3;	// ASIO-Streaming läuft
    
    HANDLE hStopEvent = 0;				// Event, welches "Bereit zum Stoppen" signalisiert
    CRITICAL_SECTION csInternal;		// Kritischer Bereich: Interne Synchronisation
    CRITICAL_SECTION csExternal;		// Kritischer Bereich: Externe Synchronisation
    
    long lState = LIBRAW;					// Aktueller Zustand
    bool bPreInit = false;				// Treiber-Komponente initialisiert und aufgezählt?
    int iZeroBlocks;					// Anzahl der noch zu spielenden Nullblöcke (nach Stop)
    long lNumDrivers;					// Anzahl der installierten Treiber
    char** asioDriverNames = NULL;		// Zwischerspeicher für die Treibernamen
    DriverInfo asioDriverInfo;			// Eigene Treiberinfos
    float fOutputGain = 1.0;				// Verstärkungsfaktor für Wiedergabe
    long volatile lAsioBufferIndex;
    ASIOBool abProcessNow;
    
    float *pfSilence = 0;
    float *pfInputBuffer = 0;
    float *pfInputDummyBuffer = 0;	// Puffer für die zu verwerfenden ersten zwei Blöcke
    
    ASIOSource* pasInputDatasource = 0;		// Eingabedatenquelle
    ITADatasource* pidsOutputDatasource = 0;	// Ausgabedatenquelle
    ITAStreamInfo siStreamInfo;				// Zustandsinformationen des Streams
    double g_dStreamStartTimeStamp = ITAClock::getDefaultClock()->getTime(); //!< Time stamp at beginning of streaming
    
    unsigned long ulOutputBlockCounter = 0;
    unsigned long ulInputCounter = 0;
    unsigned long ulInputPresentCounter = 0;
    int iInputBufferNrOffset = 0;
    
    long lBuffersize = 0;	// Ausgewählte Puffergröße
    
    ITAAtomicInt iBufferswitchEntrances( 0 );
    
    ITAStopWatch g_swStreamOutputProcessing;
    
    // -= Externe Referenzen =-
    extern AsioDrivers *asioDrivers;
    
    /*
       +------------------+
       |                  |
       |  ASIO-Callbacks  |
       |                  |
       +------------------+
       */
    
    ASIOCallbacks asioCallbacks;
    
    ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool processNow )
    {
    	// The actual processing callback.
    	/* Beware that this is normally in a seperate thread,
    	   hence be sure that you take care about thread synchronization.
    	   This is omitted here for simplicity. */
    
    	if( ++iBufferswitchEntrances > 1 )
    		std::cerr << "Problem: ASIO bufferswitch callback reentrance!\n" << std::endl;
    
    	// Internen Mutex in Besitz bringen
    	EnterCriticalSection( &csInternal );
    
    	// Store the timeInfo for later use
    	asioDriverInfo.tInfo = *timeInfo;
    	lAsioBufferIndex = index;
    	abProcessNow = processNow;
    
    	static long processedSamples = 0;
    
    	// Get the time stamp of the buffer, not necessary if no
    	// synchronization to other media is required
    	if( timeInfo->timeInfo.flags & kSystemTimeValid )
    		asioDriverInfo.nanoSeconds = ASIO64toDouble( timeInfo->timeInfo.systemTime );
    	else
    		asioDriverInfo.nanoSeconds = 0;
    
    	if( timeInfo->timeInfo.flags & kSamplePositionValid )
    		asioDriverInfo.samples = ASIO64toDouble( timeInfo->timeInfo.samplePosition );
    	else
    		asioDriverInfo.samples = 0;
    
    	if( timeInfo->timeCode.flags & kTcValid )
    		asioDriverInfo.tcSamples = ASIO64toDouble( timeInfo->timeCode.timeCodeSamples );
    	else
    		asioDriverInfo.tcSamples = 0;
    
    	// Get the system reference time
    	asioDriverInfo.sysRefTime = timeGetTime();   	// link winmm.lib
    
    	/*
    		DEBUG_PRINTF("[ITAsioInterface] nanoSeconds = %0.1f, samples = %0.1f, tcSamples = %0.1f, sysRefTime = %0.1f\n",
    		asioDriverInfo.nanoSeconds, asioDriverInfo.samples, asioDriverInfo.tcSamples, asioDriverInfo.sysRefTime);
    		*/
    
    	// [Bugfix fwe]: Schwerer Bug! long buffSize = asioDriverInfo.preferredSize;
    	int j;
    
    	float fScaling = 0;
    	int iChannelNumber = 0;
    
    	// -= Eingabedaten lesen =-
    
    	/* Hinweis: Eingabedaten werden nur gelesen, falls ASIO-Stop noch
    				nicht aufgerufen wurde, d.h. iZeroBlocks == -1 ist. */
    	if( iZeroBlocks == -1 )
    	{
    		bool bInputDataPresent = ( ulInputPresentCounter >= NUM_INPUT_BLOCKS_TO_DISCARD ) && ( pasInputDatasource != 0 );
    
    		// Input-Kanaele lesen
    		for( int i = 0; i < asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers; i++ ) {
    			if( asioDriverInfo.bufferInfos[ i ].isInput == TRUE ) {
    				if( i >= asioDriverInfo.inputBuffers )
    					MessageBoxA( 0, "Error", "Input Buffer Overflow", MB_OK | MB_SYSTEMMODAL | MB_APPLMODAL );
    				else
    					iChannelNumber = i;
    
    				if( !bInputDataPresent ) {
    					// Noch keine Eingabesamples: Stille bereistellen
    					float* pfBuffer = pasInputDatasource->GetWritePointer( iChannelNumber );
    					for( j = 0; j < lBuffersize; j++ ) pfBuffer[ j ] = 0;
    				}
    				else {
    					// Eingabesamples verfügbar. Samples konvertieren.
    					pfInputBuffer = pasInputDatasource->GetWritePointer( iChannelNumber );
    
    					switch( asioDriverInfo.channelInfos[ i ].type ) {
    					case ASIOSTInt16LSB:
    						__int16 *psAsioBufPtr;
    						psAsioBufPtr = ( __int16* ) asioDriverInfo.bufferInfos[ i ].buffers[ lAsioBufferIndex ];
    
    						// Konvertierung Int16 nach Float
    						for( j = 0; j < lBuffersize; j++ )
    							pfInputBuffer[ j ] = ( ( float ) psAsioBufPtr[ j ] ) / 32767.0F;
    						break;
    
    					case ASIOSTInt24LSB: // used for 20 bits as well
    						char *pcAsioBufPtr;
    						pcAsioBufPtr = ( char* ) asioDriverInfo.bufferInfos[ i ].buffers[ lAsioBufferIndex ];
    						__int32 i32Temp;
    						i32Temp = 0;
    
    						// Konvertierung 20/24-Bit Integer nach Float
    						for( j = 0; j < lBuffersize; j++ ) {
    							memcpy( &i32Temp, pcAsioBufPtr, 3 );
    							pcAsioBufPtr += 3;
    							pfInputBuffer[ j ] = ( ( float ) i32Temp ) / 8388607.0F;
    						}
    						break;
    
    					case ASIOSTInt32LSB:
    						__int32 *piAsioBufPtr;
    						piAsioBufPtr = ( __int32* ) asioDriverInfo.bufferInfos[ i ].buffers[ lAsioBufferIndex ];
    
    						/*
    						 *  Bugfix [fwe 2008-09-24]:
    						 *
    						 *  Bei der Benutzung der Hammerfall fiel uns auf das
    						 *  diese nicht den vollständigen 32-Bit Dynamikbereich unterstützt
    						 *  (Karte arbeitet mit 24-Bit). Es scheint als müßte des
    						 *  Least Significant Byte (Bits 0-7) immer Null sein.
    						 *  (Entspricht wieder den 24 Bit nominell). Demnach ist
    						 *  der Skalierungsfaktor für Vollaussteuerung nicht
    						 *  2^31-1 = 2147483647, sondern (2^23-1)*2^8 = 2147483392
    						 *
    						 *  Klärung mit RME Audio steht noch aus...
    						 */
    
    						// Konvertierung 32-Bit Integer nach Float
    						for( j = 0; j < lBuffersize; j++ )
    							// Standard-Implementierung:
    							//pfInputBuffer[j] = ((float) piAsioBufPtr[j]) / 2147483647.0F;   
    							// RME-Hammerfall Safe Implementierung
    							pfInputBuffer[ j ] = ( ( float ) piAsioBufPtr[ j ] ) / 2147483392.0F;
    
    						break;
    					}
    				}
    			}
    		}
    
    		if( pasInputDatasource )
    			pasInputDatasource->IncrementWritePointer();
    	}
    
    	// -= Ausgabedaten schreiben =-
    
    	/* Hinweis: Falls iZeroBlocks != -1 ist wurde ASIOStop bereits ausgelöst und
    				jetzt müssen Nullblöcke abgespielt werden.
    				iZeroBlocks muß heruntergezählt werden. */
    
    	// Provide ouput data
    
    	g_swStreamOutputProcessing.start();
    	for( int i = 0; i < asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers; i++ )
    	{
    		if( asioDriverInfo.bufferInfos[ i ].isInput == FALSE )
    		{
    			if( i - asioDriverInfo.inputBuffers >= asioDriverInfo.outputBuffers )
    				MessageBoxA( 0, "Error", "Output Buffer Overflow", MB_OK | MB_SYSTEMMODAL | MB_APPLMODAL );
    			else
    				iChannelNumber = i - asioDriverInfo.inputBuffers;
    
    			const float* pfOutputData = pfSilence;
    
    			// TODO: Streaminfo zusammenstellen? TimeCode aus ASIO rein?
    
    			// Gibts eine Ausgabedatenquelle?
    			if( pidsOutputDatasource && ( iZeroBlocks == -1 ) )
    			{
    				// Dann Datenzeiger der Quelle abrufen
    				// Wichtig: false ist enorm wichtig, damit nicht auf Daten gewartet wird!
    				pfOutputData = pidsOutputDatasource->GetBlockPointer( ( unsigned int ) iChannelNumber, &siStreamInfo );
    
    				// Falls die Quelle keine Daten hat, Stille verwenden
    				if( !pfOutputData )
    					pfOutputData = pfSilence;
    			}
    
    			switch( asioDriverInfo.channelInfos[ i ].type )
    			{
    			case ASIOSTInt16LSB:
    #if DEBUG_OUT
    				std::cout << "ASIOSTInt16LSB nicht unterstuetzt ";
    #endif
    				__int16 *psAsioBufPtr;
    				psAsioBufPtr = ( __int16* ) asioDriverInfo.bufferInfos[ i ].buffers[ lAsioBufferIndex ];
    				fScaling = 32767.0F * fOutputGain;
    				for( j = 0; j < lBuffersize; j++ )
    					psAsioBufPtr[ j ] = ( __int16 ) ( pfOutputData[ j ] * fScaling );
    				break;
    
    			case ASIOSTInt24LSB: // used for 20 bits as well
    #if DEBUG_OUT
    				std::cout << "ASIOSTInt24LSB nicht unterstuetzt ";
    #endif
    				char *pcAsioBufPtr;
    				pcAsioBufPtr = ( char* ) asioDriverInfo.bufferInfos[ i ].buffers[ lAsioBufferIndex ];
    				__int32 i32Temp;
    				char *pcSourcePointer;
    				fScaling = 8388607.0F * fOutputGain;
    				for( j = 0; j < lBuffersize; j++ )
    				{
    					i32Temp = ( __int32 ) ( pfOutputData[ j ] * fScaling );
    					pcSourcePointer = ( char* ) &i32Temp;
    					memcpy( pcAsioBufPtr, pcSourcePointer, 3 );
    					pcAsioBufPtr += 3;
    				}
    				break;
    
    			case ASIOSTInt32LSB:
    #if DEBUG_OUT
    				std::cout << "ASIOSTInt32LSB nicht unterstuetzt";
    #endif
    
    				/*
    				 *  Bugfix [fwe 2008-09-24]:
    				 *
    				 *  Bei der Benutzung der Hammerfall fiel uns auf das
    				 *  diese nicht den vollständigen 32-Bit Dynamikbereich unterstützt
    				 *  (Karte arbeitet mit 24-Bit). Es scheint als müßte des
    				 *  Least Significant Byte (Bits 0-7) immer Null sein.
    				 *  (Entspricht wieder den 24 Bit nominell). Demnach ist
    				 *  der Skalierungsfaktor für Vollaussteuerung nicht
    				 *  2^31-1 = 2147483647, sondern (2^23-1)*2^8 = 2147483392
    				 *
    				 *  Klärung mit RME Audio steht noch aus...
    				 */
    
    				__int32 *piAsioBufPtr;
    				piAsioBufPtr = ( __int32* ) asioDriverInfo.bufferInfos[ i ].buffers[ lAsioBufferIndex ];
    				//fScaling = 2147483647.0F * fOutputGain;
    				fScaling = 2147483392.0F * fOutputGain;
    				for( j = 0; j < lBuffersize; j++ )
    					piAsioBufPtr[ j ] = ( __int32 ) ( pfOutputData[ j ] * fScaling );
    				break;
    
    				// Alles weitere wird noch nicht unterstützt!!
    				// Int			
    			case ASIOSTInt32LSB16:		// 32 bit data with 18 bit alignment
    				std::cout << "ASIOSTInt32LSB16 nicht unterstuetzt ";
    				break;
    			case ASIOSTInt32LSB18:		// 32 bit data with 18 bit alignment
    				std::cout << "ASIOSTInt32LSB18 nicht unterstuetzt ";
    				break;
    			case ASIOSTInt32LSB20:		// 32 bit data with 20 bit alignment
    				std::cout << "ASIOSTInt32LSB20 nicht unterstuetzt ";
    				break;
    			case ASIOSTInt32LSB24:		// 32 bit data with 24 bit alignment
    				std::cout << "ASIOSTInt32LSB24 nicht unterstuetzt ";
    				break;
    			case ASIOSTInt16MSB:
    				std::cout << "ASIOSTInt16MSB nicht unterstuetzt ";
    				break;
    			case ASIOSTInt24MSB:		// used for 20 bits as well
    				std::cout << "ASIOSTInt24MSB nicht unterstuetzt ";
    				break;
    			case ASIOSTInt32MSB:
    				std::cout << "ASIOSTInt32MSB nicht unterstuetzt ";
    				break;
    
    				// these are used for 32 bit data buffer, with different alignment of the data inside
    				// 32 bit PCI bus systems can more easily used with these
    			case ASIOSTInt32MSB16:		// 32 bit data with 18 bit alignment
    				std::cout << "ASIOSTInt32MSB16 nicht unterstuetzt ";
    				break;
    			case ASIOSTInt32MSB18:		// 32 bit data with 18 bit alignment
    				std::cout << "ASIOSTInt32MSB18 nicht unterstuetzt ";
    				break;
    			case ASIOSTInt32MSB20:		// 32 bit data with 20 bit alignment
    				std::cout << "ASIOSTInt32MSB20 nicht unterstuetzt ";
    				break;
    			case ASIOSTInt32MSB24:		// 32 bit data with 24 bit alignment
    				std::cout << "ASIOSTInt32MSB24 nicht unterstuetzt ";
    				break;
    
    				// Float
    			case ASIOSTFloat32LSB:		// IEEE 754 32 bit float, as found on Intel x86 architecture
    				std::cout << "ASIOSTFloat32LSB nicht unterstuetzt ";
    				break;
    			case ASIOSTFloat64LSB: 		// IEEE 754 64 bit double float, as found on Intel x86 architecture
    				std::cout << "ASIOSTFloat64LSB nicht unterstuetzt";				// these are used for 32 bit data buffer, with different alignment of the data inside
    				break;				    // 32 bit PCI bus systems can more easily used with these
    
    			case ASIOSTFloat32MSB:		// IEEE 754 32 bit float, as found on Intel x86 architecture
    				std::cout << "ASIOSTFloat32MSB nicht unterstuetzt ";
    				break;
    			case ASIOSTFloat64MSB: 		// IEEE 754 64 bit double float, as found on Intel x86 architecture
    				std::cout << "ASIOSTFloat64MSB nicht unterstuetzt";
    				break;
    			default:
    				std::cout << "- ";
    			}
    		}
    	}
    
    	const double dOutputProcessingTime = g_swStreamOutputProcessing.stop();
    	const double dAvailabelProcessingTime = double( lBuffersize ) / double( asioDriverInfo.sampleRate );
    	if( dOutputProcessingTime > dAvailabelProcessingTime )
    		std::cerr << "[ ITAAsioInterface ] Output stream panic, processing time exceeded ( took " << timeToString( dOutputProcessingTime ) << " but got only " << timeToString( dAvailabelProcessingTime ) << " )" << std::endl;
    
    	ulOutputBlockCounter++;
    
    	if( ulInputPresentCounter <= 1 )
    		ulInputPresentCounter++;
    	else
    		ulInputCounter++;
    
    	processedSamples += lBuffersize;
    
    	siStreamInfo.nSamples += lBuffersize;
    	//siStreamInfo.dStreamTimeCode = (double) (siStreamInfo.nSamples) / (double) asioDriverInfo.sampleRate;
    	siStreamInfo.dStreamTimeCode = ITAClock::getDefaultClock()->getTime() - g_dStreamStartTimeStamp;
    	siStreamInfo.dSysTimeCode = ITAClock::getDefaultClock()->getTime();
    
    	// Gibts eine Ausgabedatenquelle? Dann Blockzeiger weitersetzen
    	if( pidsOutputDatasource && ( iZeroBlocks == -1 ) ) pidsOutputDatasource->IncrementBlockPointer();
    
    	// Stop triggert? Herunterzählen? Bei 0 das Event für wartendes ITAAsioStop() setzen.
    	if( iZeroBlocks > 0 )
    		if( ( --iZeroBlocks ) == 0 ) SetEvent( hStopEvent );
    
    	// Internen Mutex freigeben
    	LeaveCriticalSection( &csInternal );
    
    	// Finally if the driver supports the ASIOOutputReady() optimization,
    	// do it here, all data are in place
    	//if (asioDriverInfo.postOutput)
    	ASIOOutputReady(); // Muss hier stehen, sonst spielts nicht
    
    	--iBufferswitchEntrances;
    
    	return 0L;
    }
    
    void bufferSwitch( long index, ASIOBool processNow ) { // the actual processing callback.
    	// Beware that this is normally in a seperate thread, hence be sure that
    	// you take care about thread synchronization. This is omitted here for
    	// simplicity.
    
    	// _RPTF0( _CRT_WARN, "Callback BufferSwitch \n");
    
    	// As this is a "back door" into the bufferSwitchTimeInfo a timeInfo needs 
    	// to be created though it will only set the timeInfo.samplePosition and
    	// timeInfo.systemTime fields and the according flags
    	ASIOTime timeInfo;
    	memset( &timeInfo, 0, sizeof( timeInfo ) );
    
    	// Get the time stamp of the buffer, not necessary if no
    	// synchronization to other media is required
    	if( ASIOGetSamplePosition( &timeInfo.timeInfo.samplePosition,
    		&timeInfo.timeInfo.systemTime ) == ASE_OK )
    		timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
    
    	bufferSwitchTimeInfo( &timeInfo, index, processNow );
    }
    
    void sampleRateChanged( ASIOSampleRate )
    {
    	// MessageBox (0, "Callback sampleRateChanged", "Callback sampleRateChanged", MB_OK|MB_SYSTEMMODAL| MB_APPLMODAL );
    
    	// Do whatever you need to do if the sample rate changed
    	// usually this only happens during external sync.
    	// Audio processing is not stopped by the driver, actual sample rate
    	// might not have even changed, maybe only the sample rate status of an
    	// AES/EBU or S/PDIF digital input at the audio device.
    	// You might have to update time/sample related conversion routines, etc.
    }
    
    
    long asioMessages( long selector, long value, void*, double* )
    {
    	// MessageBox (0, "Callback asioMessages", "Callback asioMessages", MB_OK|MB_SYSTEMMODAL| MB_APPLMODAL );
    
    	// Currently the parameters "value", "message" and "opt" are not used.
    	long ret = 0;
    	switch( selector ) {
    	case kAsioSelectorSupported:
    		if( value == kAsioResetRequest
    			|| value == kAsioEngineVersion
    			|| value == kAsioResyncRequest
    			|| value == kAsioLatenciesChanged
    			// the following three were added for ASIO 2.0, you don't necessarily have to support them
    			|| value == kAsioSupportsTimeInfo
    			|| value == kAsioSupportsTimeCode
    			|| value == kAsioSupportsInputMonitor )
    			ret = 1L;
    		break;
    	case kAsioResetRequest:
    		// defer the task and perform the reset of the driver during the next "safe" situation
    		// You cannot reset the driver right now, as this code is called from the driver.
    		// Reset the driver is done by completely destruct is. I.e. ASIOStop(), ASIODisposeBuffers(), Destruction
    		// Afterwards you initialize the driver again.
    		asioDriverInfo.stopped;  // In this sample the processing will just stop
    		ret = 1L;
    		break;
    	case kAsioResyncRequest:
    		// This informs the application, that the driver encountered some non fatal data loss.
    		// It is used for synchronization purposes of different media.
    		// Added mainly to work around the Win16Mutex problems in Windows 95/98 with the
    		// Windows Multimedia system, which could loose data because the Mutex was hold too long
    		// by another thread.
    		// However a driver can issue it in other situations, too.
    		ret = 1L;
    		break;
    	case kAsioLatenciesChanged:
    
    		//MessageBox (0, "Callback sampleRateChanged BEN", "Callback sampleRateChanged BEN ", MB_OK|MB_SYSTEMMODAL| MB_APPLMODAL );
    
    		// This will inform the host application that the drivers were latencies changed.
    		// Beware, it this does not mean that the buffer sizes have changed!
    		// You might need to update internal delay data.
    		ret = 1L;
    		break;
    	case kAsioEngineVersion:
    		// return the supported ASIO version of the host application
    		// If a host applications does not implement this selector, ASIO 1.0 is assumed
    		// by the driver
    		ret = 2L;
    		break;
    	case kAsioSupportsTimeInfo:
    		// informs the driver wether the asioCallbacks.bufferSwitchTimeInfo() callback
    		// is supported.
    		// For compatibility with ASIO 1.0 drivers the host application should always support
    		// the "old" bufferSwitch method, too.
    		ret = 1;
    		break;
    	case kAsioSupportsTimeCode:
    		// informs the driver wether application is interested in time code info.
    		// If an application does not need to know about time code, the driver has less work
    		// to do.
    		ret = 0;
    		break;
    	}
    	return ret;
    }
    
    /*
       +-----------------------------------------------+
       |                                               |
       |  Implementierung der öffentlichen Funktionen  |
       |                                               |
       +-----------------------------------------------+
       */
    
    ITASIO_API void ITAsioInitializeLibrary( void )
    {
    
    	if( lState != LIBRAW )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO library already initialized" );
    
    	InitializeCriticalSection( &csInternal );
    	InitializeCriticalSection( &csExternal );
    
    	if( FAILED( hStopEvent = CreateEvent( NULL, TRUE, FALSE, NULL ) ) )
    		ITA_EXCEPT1( UNKNOWN, "Failed to create mutex" );
    
    	// ASIO-Umgebung ermitteln
    	asioDrivers = new AsioDrivers();
    
    	// Setzen der Asio-Callbacks
    	asioCallbacks.bufferSwitch = &bufferSwitch;
    	asioCallbacks.sampleRateDidChange = &sampleRateChanged;
    	asioCallbacks.asioMessage = &asioMessages;
    	asioCallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;
    
    	// Treibernamen ermitteln und zwischenspeichern
    	lNumDrivers = asioDrivers->asioGetNumDev();
    	if( lNumDrivers > 0 ) 
    	{
    		asioDriverNames = new char*[ lNumDrivers ];
    		memset( asioDriverNames, 0, lNumDrivers*sizeof( char* ) );
    		for( int i = 0; i < ( int ) lNumDrivers; i++ )
    			asioDriverNames[ i ] = new char[ 32 ];
    
    		asioDrivers->getDriverNames( asioDriverNames, lNumDrivers );
    	}
    
    	// Startzustand setzen
    	lState = LOADED;
    }
    
    ITASIO_API void ITAsioFinalizeLibrary( void )
    {
    	// Gar nicht initialisiert? => Kein Fehler
    	if( lState == LIBRAW ) return;
    
    	if( lNumDrivers > 0 )
    	{
    		for( int i = 0; i<( int ) lNumDrivers; i++ ) delete[] asioDriverNames[ i ];
    		delete[] asioDriverNames;
    		asioDriverNames = NULL;
    	}
    
    	// Treiber noch initialisiert? => Implizit abrüumen
    	if( lState > LOADED ) ITAsioFinalizeDriver();
    
    	// ASIO-Umgebung und Resourcen freigeben
    	delete asioDrivers;
    
    	CloseHandle( hStopEvent );
    
    	DeleteCriticalSection( &csInternal );
    	DeleteCriticalSection( &csExternal );
    
    	lState = LIBRAW;
    }
    
    /*
    #define REQUIRE_LIBINIT()    { if (lState == LIBRAW) ITA_EXCEPT1(MODAL_EXCEPTION, "ASIO library not initialized"); }
    #define REQUIRE_DRVINIT()    { if (lState < INITIALIZED) ITA_EXCEPT1(MODAL_EXCEPTION, "No ASIO driver initialized"); }
    #define REQUIRE_STREAMPREP() { if (lState < PREPARED) ITA_EXCEPT1(MODAL_EXCEPTION, "ASIO streaming not prepared"); }
    */
    
    ITASIO_API long ITAsioGetNumDrivers( void )
    {
    	if( lState < LOADED ) ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO library not initialized" );
    	return lNumDrivers;
    }
    
    ITASIO_API const char* ITAsioGetDriverName( long lDriverNr )
    {
    	if( lState < LOADED ) ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO library not initialized" );
    
    	// BUGFIX [fwe, 2007-06-24]: Leerer String != NULL
    	static const char EMPTY_STRING = '\0';
    
    	// Leeren String zurückgeben, falls ungültiger Index
    	if( ( lDriverNr < 0 ) || ( lDriverNr >= lNumDrivers ) )
    		return &EMPTY_STRING;
    	return asioDriverNames[ lDriverNr ];
    }
    
    ITASIO_API const char* ITAsioGetErrorStr( ASIOError ae )
    {
    	switch( ae )
    	{
    	case ASE_NotPresent: // hardware input or output is not present or available
    		return "Hardware is not present or available";
    
    	case ASE_HWMalfunction:     // hardware is malfunctioning (can be returned by any ASIO function)
    		return "Hardware is malfunctioning";
    
    	case ASE_InvalidParameter:   // input parameter invalid
    		return "Invalid input parameter";
    
    	case ASE_InvalidMode:       // hardware is in a bad mode or used in a bad mode
    		return "Hardware is in a bad mode or used in a bad mode";
    
    	case ASE_SPNotAdvancing:     // hardware is not running when sample position is inquired
    		return "Hardware is not running when sample position is inquired";
    
    	case ASE_NoClock: // sample clock or rate cannot be determined or is not present
    		return "Sample clock or rate cannot be determined or is not present";
    
    	case ASE_NoMemory:            // not enough memory for completing the request
    		return "Not enough memory for completing the request";
    	default:
    		return "";
    	}
    }
    
    ITASIO_API ASIOError ITAsioInitializeDriver( long lDriverNr )
    {
    	if( lState < LOADED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO library not initialized" );
    
    	if( lState > LOADED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO driver already initialized" );
    
    	if( ( lDriverNr < 0 ) || ( lDriverNr >= lNumDrivers ) )
    		return ( ASE_InvalidParameter );
    	return ITAsioInitializeDriver( asioDriverNames[ lDriverNr ] );
    }
    
    ITASIO_API ASIOError ITAsioInitializeDriver( const char* pszDriverName )
    {
    	if( lState < LOADED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO library not initialized" );
    
    	if( lState > LOADED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO driver already initialized" );
    
    	// Mutex in Besitz bringen
    	EnterCriticalSection( &csExternal );
    
    	// Load the driver, this will setup all the necessary internal data structures
    	try
    	{
    		bool bLoadSuccess = asioDrivers->loadDriver( ( char* ) pszDriverName );
    		if( !bLoadSuccess )
    			return ASE_NotPresent;
    	}
    	catch( ... )
    	{
    		return ASE_NotPresent;
    	}
    
    	ASIOError aeResult = ASIOInit( &asioDriverInfo.driverInfo );
    	if( aeResult != ASE_OK ) {
    		LeaveCriticalSection( &csExternal );
    		return aeResult;
    	}
    
    	lState = INITIALIZED;
    
    	// Hier sollen alle Inhalte der Get-Funktionen rein,
    	// um später im Hauptprogramm die Struktur auf einmal abfragen zu können !!!
    
    	// TODO: Frage von fwefers: Sind die Sleeps nötig? Warum?
    
    	aeResult = ASIOGetChannels( &asioDriverInfo.inputChannels, &asioDriverInfo.outputChannels );
    	if( aeResult != ASE_OK ) {
    		ASIOExit();
    		lState = LOADED;
    		LeaveCriticalSection( &csExternal );
    		return aeResult;
    	}
    
    	Sleep( 50 );
    
    	if( ( aeResult = ASIOGetLatencies( &asioDriverInfo.inputLatency, &asioDriverInfo.outputLatency ) ) != ASE_OK )
    	{
    		/* Continue upon latency resolve failure (happens with Focusrite but is not harmful)
    		ASIOExit();
    		lState = LOADED;
    		LeaveCriticalSection(&csExternal);
    		return aeResult;
    		*/
    		asioDriverInfo.inputLatency = 0;
    		asioDriverInfo.outputLatency = 0;
    	}
    	Sleep( 50 );
    
    	aeResult = ASIOGetBufferSize( &asioDriverInfo.minSize, &asioDriverInfo.maxSize, &asioDriverInfo.preferredSize, &asioDriverInfo.granularity );
    	if( aeResult != ASE_OK ) {
    		ASIOExit();
    		lState = LOADED;
    		LeaveCriticalSection( &csExternal );
    		return aeResult;
    	}
    
    	Sleep( 50 );
    
    	aeResult = ASIOGetSampleRate( &asioDriverInfo.sampleRate );
    	if( aeResult != ASE_OK ) {
    		ASIOExit();
    		lState = LOADED;
    		LeaveCriticalSection( &csExternal );
    		return aeResult;
    	}
    
    	Sleep( 50 );
    
    	ASIOClockSource ClockSourceArray[ 3 ];
    	long numSources = 3;
    	aeResult = ASIOGetClockSources( ClockSourceArray, &numSources );
    	if( aeResult != ASE_OK ) {
    		ASIOExit();
    		lState = LOADED;
    		LeaveCriticalSection( &csExternal );
    		return aeResult;
    	}
    
    	// Retrieve total number of channels
    	long lNumInChannels, lNumOutChannels;
    	if( ( aeResult = ASIOGetChannels( &lNumInChannels, &lNumOutChannels ) ) != ASE_OK )
    	{
    		ASIOExit();
    		lState = LOADED;
    		LeaveCriticalSection( &csExternal );
    		return aeResult;
    	}
    
    	// Prepare channel info array
    	size_t nNumInOutChannels = size_t( lNumInChannels + lNumOutChannels );
    	asioDriverInfo.channelInfos.resize( nNumInOutChannels );
    
    	if( nNumInOutChannels == 0 )
    		return ASE_InvalidParameter;
    
    	// Retrieve channel info
    	ASIOChannelInfo* pInfo = reinterpret_cast< ASIOChannelInfo* >( &asioDriverInfo.channelInfos.front() );
    	if( ( aeResult = ASIOGetChannelInfo( pInfo ) ) != ASE_OK )
    	{
    		ASIOExit();
    		lState = LOADED;
    		LeaveCriticalSection( &csExternal );
    		return aeResult;
    	}
    
    	LeaveCriticalSection( &csExternal );
    
    	return aeResult;
    }
    
    ITASIO_API ASIOError ITAsioFinalizeDriver( void )
    {
    	if( lState < INITIALIZED ) ITA_EXCEPT1( MODAL_EXCEPTION, "No ASIO driver initialized" );
    
    	// Mutex in Besitz bringen
    	EnterCriticalSection( &csExternal );
    
    	// Ggf. DisposeBuffers implizieren!
    	if( lState > INITIALIZED ) ITAsioDisposeBuffers();
    
    	ASIOError aeResult = ASIOExit();
    
    	if( aeResult == ASE_OK ) lState = LOADED;
    	LeaveCriticalSection( &csExternal );
    
    	return aeResult;
    }
    
    ITASIO_API ASIOError ITAsioGetBufferSize( long *minSize, long *maxSize, long *preferredSize, long *granularity )
    {
    
    	if( lState < INITIALIZED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "No ASIO driver initialized" );
    
    	// TODO: Frage von fwefers: Warum werden die Werte oben zwischengespeichert
    	//                          und hier nochmal ausgelesen (DriverInfo)?
    	ASIOError aeResult = ASIOGetBufferSize( minSize, maxSize, preferredSize, granularity );
    	asioDriverInfo.minSize = *minSize;
    	asioDriverInfo.maxSize = *maxSize;
    	asioDriverInfo.preferredSize = *preferredSize;
    	asioDriverInfo.granularity = *granularity;
    	return aeResult;
    }
    
    ITASIO_API ASIOError ITAsioGetChannels( long *numInputChannels, long *numOutputChannels )
    {
    
    	if( lState < INITIALIZED ) ITA_EXCEPT1( MODAL_EXCEPTION, "No ASIO driver initialized" );
    
    	ASIOError aeResult = ASIOGetChannels( numInputChannels, numOutputChannels );
    	asioDriverInfo.inputChannels = *numInputChannels;
    	asioDriverInfo.outputChannels = *numOutputChannels;
    	return aeResult;
    }
    
    ITASIO_API ASIOError ITAsioGetLatencies( long *inputLatency, long *outputLatency ) {
    
    	if( lState < INITIALIZED ) ITA_EXCEPT1( MODAL_EXCEPTION, "No ASIO driver initialized" );
    
    	ASIOError aeResult = ASIOGetLatencies( inputLatency, outputLatency );
    	asioDriverInfo.inputLatency = *inputLatency;
    	asioDriverInfo.outputLatency = *outputLatency;
    	return aeResult;
    }
    
    ITASIO_API ASIOError ITAsioCreateBuffers( long lNumberInputChannels, long lNumberOutputChannels, long lBufferSize )
    {
    
    	if( lState < INITIALIZED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "No ASIO driver initialized" );
    	if( lState > INITIALIZED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO streaming already prepared" );
    
    	// Mutex in Besitz bringen
    	EnterCriticalSection( &csExternal );
    
    	// BUGFIX [fwe, 2007-06-24] Aufruf von CreateBuffers entfernt die gesetzte Datenquelle
    	pidsOutputDatasource = NULL;
    
    	asioDriverInfo.inputBuffers = lNumberInputChannels;
    	asioDriverInfo.outputBuffers = lNumberOutputChannels;
    
    	const long numChannels = asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers;
    	int i, j;
    
    	asioDriverInfo.bufferInfos.resize( numChannels );
    
    	for( i = 0; i < asioDriverInfo.inputBuffers; i++ ) {
    		asioDriverInfo.bufferInfos[ i ].buffers[ 0 ] = 0;
    		asioDriverInfo.bufferInfos[ i ].buffers[ 1 ] = 0;
    		asioDriverInfo.bufferInfos[ i ].channelNum = i;
    		// True, wenn Input, sonst False
    		asioDriverInfo.bufferInfos[ i ].isInput = ASIOTrue;
    	}
    
    	for( j = 0; j < asioDriverInfo.outputBuffers; j++ ) {
    		asioDriverInfo.bufferInfos[ i + j ].buffers[ 0 ] = 0;
    		asioDriverInfo.bufferInfos[ i + j ].buffers[ 1 ] = 0;
    		asioDriverInfo.bufferInfos[ i + j ].channelNum = j;
    		// True, wenn Input, sonst False
    		asioDriverInfo.bufferInfos[ i + j ].isInput = ASIOFalse;
    	}
    
    	ASIOError aeResult = ASIOCreateBuffers( reinterpret_cast< ASIOBufferInfo* >( &asioDriverInfo.bufferInfos.front() ),
    		numChannels, lBufferSize, &asioCallbacks );
    	if( aeResult != ASE_OK ) {
    		LeaveCriticalSection( &csExternal );
    		return aeResult;
    	}
    
    	asioDriverInfo.channelInfos.resize( numChannels );
    
    	for( i = 0; i < ( asioDriverInfo.inputBuffers + asioDriverInfo.outputBuffers ); i++ ) {
    		// Testen, ob man das in den oberen Schleifen erledigen kann,
    		// oder ob GetChannelInfo erst nach CreateBuffer funktioniert
    		asioDriverInfo.channelInfos[ i ].channel = asioDriverInfo.bufferInfos[ i ].channelNum;
    		asioDriverInfo.channelInfos[ i ].isInput = asioDriverInfo.bufferInfos[ i ].isInput;
    		ASIOError aeResult = ASIOGetChannelInfo( &asioDriverInfo.channelInfos[ i ] );
    		if( aeResult != ASE_OK ) {
    			LeaveCriticalSection( &csExternal );
    			return aeResult;
    		}
    	}
    
    	lBuffersize = lBufferSize;
    
    	// Eigene Buffer erzeugen
    	if( lNumberOutputChannels > 0 ) {
    		pfSilence = new float[ lBufferSize ];
    		for( long k = 0; k < lBufferSize; k++ ) pfSilence[ k ] = 0;
    	}
    
    	// Eingabedatenquelle erzeugen
    	// TODO: Fehlerbehandlung
    	if( lNumberInputChannels > 0 ) {
    		pasInputDatasource = new ASIOSource( ( unsigned int ) asioDriverInfo.inputBuffers,
    			( double ) asioDriverInfo.sampleRate,
    			( unsigned int ) lBufferSize );
    		// Eingabe-Dummy-Puffer erzeugen
    		pfInputDummyBuffer = new float[ lBufferSize ];
    	}
    
    	lState = PREPARED;
    
    	LeaveCriticalSection( &csExternal );
    
    	return aeResult;
    }
    
    ITASIO_API ASIOError ITAsioDisposeBuffers( void )
    {
    	if( lState < PREPARED ) ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO streaming has not been prepared" );
    
    	// Mutex in Besitz bringen
    	EnterCriticalSection( &csExternal );
    
    	// Ggf. ASIOStop implizieren!
    	if( lState > PREPARED )
    		ITAsioStop();
    
    	ASIOError aeResult = ASIODisposeBuffers();
    
    	// Datenquelle verwerfen
    	delete pasInputDatasource;
    	pasInputDatasource = 0;
    
    	// Eigene Buffer freigeben
    	delete[] pfInputDummyBuffer;
    	pfInputDummyBuffer = 0;
    	delete[] pfSilence;
    	pfSilence = 0;
    
    	lBuffersize = 0;
    
    	lState = INITIALIZED;
    
    	LeaveCriticalSection( &csExternal );
    
    	return aeResult;
    }
    
    ITASIO_API void ITAsioSetGain( double dGain )
    {
    	if( lState < INITIALIZED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "No ASIO driver initialized" );
    
    	EnterCriticalSection( &csInternal );
    	// Wert auf das Intervall [-1.0, +1.0] begrenzen
    	fOutputGain = ( dGain < 0.0 ? 0.0F : ( float ) dGain );
    	LeaveCriticalSection( &csInternal );
    }
    
    ITASIO_API ASIOError ITAsioCanSampleRate( ASIOSampleRate sampleRate )
    {
    	if( lState < INITIALIZED ) ITA_EXCEPT1( MODAL_EXCEPTION, "No ASIO driver initialized" );
    
    	return ASIOCanSampleRate( sampleRate );
    }
    
    ITASIO_API ASIOError ITAsioGetSampleRate( ASIOSampleRate *currentRate )
    {
    	if( lState < INITIALIZED ) ITA_EXCEPT1( MODAL_EXCEPTION, "No ASIO driver initialized" );
    
    	return ASIOGetSampleRate( currentRate );
    }
    
    ITASIO_API ASIOError ITAsioSetSampleRate( ASIOSampleRate sampleRate )
    {
    	if( lState < INITIALIZED ) ITA_EXCEPT1( MODAL_EXCEPTION, "No ASIO driver initialized" );
    
    	// BUGFIX [fwe, 2007-06-24]: Interne Aktualisierung der Samplerate
    	ASIOError aeResult = ASIOSetSampleRate( ASIOSampleRate( sampleRate ) );
    	if( aeResult == ASE_OK ) asioDriverInfo.sampleRate = sampleRate;
    	return aeResult;
    }
    
    ITASIO_API ASIOError ITAsioStart( void )
    {
    	if( lState < PREPARED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO streaming has not been prepared" );
    
    	if( lState > PREPARED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO streaming is already running" );
    
    	// Mutex in Besitz bringen
    	EnterCriticalSection( &csExternal );
    
    	// Update stream start time stamp
    	g_dStreamStartTimeStamp = ITAClock::getDefaultClock()->getTime();
    
    	ulInputPresentCounter = 0;
    	ulInputCounter = 0;
    	ulOutputBlockCounter = 0;
    
    	siStreamInfo.nSamples = 0;
    	siStreamInfo.dStreamTimeCode = 0;
    	siStreamInfo.dSysTimeCode = ITAClock::getDefaultClock()->getTime();
    
    	// Alle Eingangsdatenquellen zurücksetzen
    	if( asioDriverInfo.inputBuffers > 0 )
    		pasInputDatasource->Reset();
    
    	iZeroBlocks = -1;
    	ResetEvent( hStopEvent );
    
    	ASIOError aeResult = ASIOStart();
    	if( aeResult == ASE_OK ) lState = RUNNING;
    
    	LeaveCriticalSection( &csExternal );
    
    	return aeResult;
    }
    
    ITASIO_API ASIOError ITAsioStop( void )
    {
    	if( lState < RUNNING )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO streaming is not running" );
    
    	EnterCriticalSection( &csExternal );
    
    	// Zunächst die Nullblöcke spielen und warten bis diese abgespielt wurden
    	// [stienen] Hier wartet der Loopback Test bis unendlich! Wer hat das hInternalMutex und will es nicht hergeben?!
    	EnterCriticalSection( &csInternal );
    	iZeroBlocks = NUM_ZERO_BLOCKS_AFTER_STOP;
    	LeaveCriticalSection( &csInternal );
    
    	/* Wichtig: Hier muss ein TIME_OUT angegeben werden!
    				Falls der ASIO-Thread durch einen Crash stirbt,
    				würden wir hier bis an alle Ewigkeit warten...
    				Deshalb Timeout von 1s (das sollte ausreichen) */
    	WaitForSingleObject( hStopEvent, 1000 );
    
    	// Stop signalisieren
    	ASIOError aeResult = ASIOStop();
    
    	const double dAvailableProcessingTime = double( lBuffersize ) / double( asioDriverInfo.sampleRate );
    	if( g_swStreamOutputProcessing.cycles() && g_swStreamOutputProcessing.maximum() > dAvailableProcessingTime )
    		std::cout << "[ ITAAsioInterface ] Detected dropouts and presenting output processing statistics: " << g_swStreamOutputProcessing.ToString() << std::endl;
    
    	Sleep( 100 );	// <-- Sicherheits Waitstate (Aus ASIO-Beispiel von Steinberg)
    
    	EnterCriticalSection( &csInternal );
    	if( aeResult == ASE_OK ) lState = PREPARED;
    	LeaveCriticalSection( &csInternal );
    
    	LeaveCriticalSection( &csExternal );
    
    	return aeResult;
    }
    
    ITASIO_API ASIOError ITAsioSetPlaybackDatasource( ITADatasource* pidsDatasource )
    {
    	if( lState < PREPARED )
    		ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO streaming has not been prepared" );
    
    	if( pidsDatasource ) {
    		// Parameter überprüfen
    		if( ( long ) pidsDatasource->GetNumberOfChannels() != asioDriverInfo.outputBuffers )
    			ITA_EXCEPT1( MODAL_EXCEPTION, "Datasource has invalid number of channels" );
    
    		if( ASIOSampleRate( pidsDatasource->GetSampleRate() ) != asioDriverInfo.sampleRate )
    			ITA_EXCEPT1( MODAL_EXCEPTION, "Datasource has invalid samplerate" );
    
    		if( ( long ) pidsDatasource->GetBlocklength() != lBuffersize )
    			ITA_EXCEPT1( MODAL_EXCEPTION, "Datasource has invalid block length" );
    	}
    
    	EnterCriticalSection( &csInternal );
    	pidsOutputDatasource = pidsDatasource;
    	LeaveCriticalSection( &csInternal );
    
    	return ASE_OK;
    }
    
    ITASIO_API ITADatasource* ITAsioGetRecordDatasource() {
    	if( lState < PREPARED ) ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO streaming has not been prepared" );
    	return pasInputDatasource;
    }
    
    ITASIO_API ASIOError ITAsioControlPanel( void ) { return ASIOControlPanel(); }
    
    /*
       +-----------------------------+
       |                             |
       |  Einstiegsfunktion der DLL  |
       |                             |
       +-----------------------------+
       */
    
    BOOL APIENTRY DllMain( HANDLE, DWORD fdwReason, LPVOID )
    {
    
    	/*
    	   Wichtig: Durch "return FALSE" kommt es beim Starten der Client-Anwendung
    	   zu einer Windows-Fehlermeldung, das die DLL nicht geladen werden
    	   konnte und die Anwendung kann nicht gestartet werden!
    	   */
    
    	switch( fdwReason ) {
    	case DLL_PROCESS_ATTACH:
    		// Die DLL in den virtuellen Adressraum geladen. 
    		break;
    
    	case DLL_PROCESS_DETACH:
    		// Die DLL in den virtuellen Adressraum entfernt ("entladen").
    		break;
    	}
    
    	return TRUE;
    }