...
 
Commits (119)
......@@ -110,23 +110,22 @@ endif( )
if( ITA_DATA_SOURCES_WITH_NET_AUDIO )
list( APPEND ITADataSourcesHeader
"include/ITANetAudioClient.h"
"include/ITANetAudioMessage.h"
"include/ITANetAudioProtocol.h"
"include/ITANetAudioServer.h"
"include/ITANetAudioStream.h"
"include/ITANetAudioStreamingClient.h"
"include/ITANetAudioSampleServer.h"
"include/ITANetAudioStreamingServer.h"
"include/ITANetAudioStreamingClient.h"
)
list( APPEND ITADataSourcesSources
"src/ITANetAudioClient.cpp"
"src/ITANetAudioClient.h"
"src/ITANetAudioMessage.cpp"
"src/ITANetAudioProtocol.cpp"
"src/ITANetAudioMessage.h"
"src/ITANetAudioProtocol.h"
"src/ITANetAudioServer.cpp"
"src/ITANetAudioServer.h"
"src/ITANetAudioStream.cpp"
"src/ITANetAudioStreamingClient.cpp"
"src/ITANetAudioStreamingClient.h"
"src/ITANetAudioStreamingServer.cpp"
"src/ITANetAudioServer.cpp"
)
endif( )
......
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef INCLUDE_WATCHER_ITA_NET_AUDIO_SAMPLE_SERVER
#define INCLUDE_WATCHER_ITA_NET_AUDIO_SAMPLE_SERVER
#include <ITADataSourcesDefinitions.h>
#include <ITANetAudioStreamingServer.h>
#include <ITADataSourceRealization.h>
//! Sample-generation class with abstract method for providing samples
/*
* This ready-to-use class helps to provide samples for a NetAudio streaming server with
* a single method for processing that has to be implemented ...
* ... just derive and implement Process() method. Have a look at Zero() method
* for exemplary usage of sample buffer.
*/
class CITASampleProcessor : public ITADatasourceRealization
{
public:
//! Create a sample processor with streaming parameters
/*
* @param[in] iNumChannels Channels provided
* @param[in] dSampleRate Audio processing sampling rate
* @param[in] iBlockLength Audio processing block length / buffer size
*/
inline CITASampleProcessor( const int iNumChannels, const double dSampleRate, const int iBlockLength )
: ITADatasourceRealization( ( unsigned int ) ( iNumChannels ), dSampleRate, ( unsigned int ) ( iBlockLength ) )
{
m_vvfSampleBuffer.resize( iNumChannels );
for( size_t c = 0; c < iNumChannels; c++ )
m_vvfSampleBuffer[ c ].resize( iBlockLength );
Zero();
};
inline ~CITASampleProcessor()
{
};
//! Sets all channels and samples to zero
inline void Zero()
{
/*
* Use this as an example how to work with the buffer structure.
*/
// Iterate over channels
for( size_t c = 0; c < m_vvfSampleBuffer.size(); c++ )
{
std::vector< float >& vfSingleChannelSampleBuffer( m_vvfSampleBuffer[ c ] ); // One channel
// Iterate over samples of channel
for( size_t n = 0; n < vfSingleChannelSampleBuffer.size(); n++ )
{
float& fSample( vfSingleChannelSampleBuffer[ n ] ); // One sample
fSample = 0.0f; // -> Manipulation
}
}
};
//! Process samples (overwrite this virtual method)
/**
* Method that is called in audio streaming context and requests
* to produce or copy audio samples into the internal buffer m_vvfSampleBuffer
*
* @param[in] pStreamInfo Information over streaming status, i.e. sample count and time stamp
*
*/
virtual void Process( const ITAStreamInfo* pStreamInfo ) =0;
protected:
std::vector< std::vector< float > > m_vvfSampleBuffer; //!< Multi-channel sample buffer to be filled
private:
//! Delegate internal buffer to audio stream (ITADatasource)
inline void ProcessStream( const ITAStreamInfo* pInfo )
{
Process( pInfo );
for( size_t c = 0; c < m_vvfSampleBuffer.size(); c++ )
{
float* pfData = GetWritePointer( ( unsigned int ) ( c ) );
for( size_t n = 0; n < m_vvfSampleBuffer[ c ].size(); n++ )
pfData[ n ] = m_vvfSampleBuffer[ c ][ n ];
}
IncrementWritePointer();
};
};
//! Network audio sample server (for providing samples via derived generator class)
/**
* Audio sample transmitter for a networked sample callback function that can connect via TCP/IP.
*
* @sa CITANetAudioStream CITANetAudioStreamingServer CITASampleProcessor
* @note not thread-safe
*/
class CITANetAudioSampleServer : public CITANetAudioStreamingServer
{
public:
inline CITANetAudioSampleServer( CITASampleProcessor* pProcessor )
: m_pSampleProcessor( pProcessor )
{
SetInputStream( m_pSampleProcessor );
};
inline ~CITANetAudioSampleServer()
{};
private:
//! Prohibit public access to streaming context and delegate
inline void SetInputStream( ITADatasource* pDataSource )
{
CITANetAudioStreamingServer::SetInputStream( pDataSource );
};
//! Prohibit public access to streaming context and delegate
inline ITADatasource* GetInputStream() const
{
return CITANetAudioStreamingServer::GetInputStream();
};
CITASampleProcessor* m_pSampleProcessor; //!< Callback / sample processor
};
#endif // INCLUDE_WATCHER_ITA_NET_AUDIO_SAMPLE_SERVER
......@@ -26,9 +26,6 @@
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
using namespace std;
class CITANetAudioStreamingClient;
......@@ -38,7 +35,7 @@ class ITABufferedDataLoggerImplAudio;
//! Network audio stream
/**
* Audio streaming for a signal source that is connected via TCP/IP.
* Audio streaming for a signal source that is connected via TCP/IP or UDP.
* The network audio stream behaves like a client and receives samples
* from a network audio stream server, CITANetAudioStreamingSearver.
*
......@@ -46,11 +43,13 @@ class ITABufferedDataLoggerImplAudio;
* block the streaming processing, because it is decoupled from the
* network connection and forwards samples from an internal ring buffer.
* If the buffer runs out of samples, zeros will be return. If the buffer
* overruns, the sample server will be suspendeb by blocking the network
* overruns, the sample server will be suspended by blocking the network
* data flow.
*
* Latency can be managed by either providing a small ring buffer or
* oversizing the ring buffer and requesting a target latency.
* Latency can be managed by either providing a small ring buffer and
* constantly filling it uo, or by oversizing the internal ring buffer
* only pushing samples to meet a target latency. This has to be
* implemented by the server.
*
* \note not thread-safe
*/
......@@ -58,7 +57,21 @@ class ITA_DATA_SOURCES_API CITANetAudioStream : public ITADatasource
{
public:
//! Constructor of a network audio stream
CITANetAudioStream( int iChannels, double dSamplingRate, int iBufferSize, int iRingBufferCapacity = 2048 );
/**
* @param[in] iChannels Number of channels
* @param[in] dSamplingRate Sampling rate
* @param[in] iBufferSize Size of audio streaming buffer
* @param[in] iRingBufferCapacity Internal ring buffer
*
* The ring buffer capacity should be roughly 6-10 buffer sizes long for short audio streaming buffers,
* and can go down to one block in case of higher audio buffer sizes.
*
* The streaming parameters have to match with the server settings (yes also buffer size, that of the audio streaming context)
*
* @note Accept for more memory usage, oversizing the buffer does not require more CPU.
*/
CITANetAudioStream( const int iChannels, const double dSamplingRate, const int iBufferSize, const int iRingBufferCapacity = 2048 );
virtual ~CITANetAudioStream();
//! Network streaming status of client
......@@ -68,16 +81,20 @@ public:
STOPPED, //!< Client not connected to a server and streaming stopped, i.e. not receiving samples by choice
CONNECTED, //!< Client is connected to a sample server (and potentially receives samples)
STREAMING, //!<
BUFFER_UNDERRUN,
BUFFER_OVERRUN,
BUFFER_UNDERRUN, //!< Client internal audio buffer ran out of samples
BUFFER_OVERRUN, //!< Client internal audio ring buffer is full
};
//! Connect a streaming server
/**
* @sAddress[in] Server address IP, i.e. 127.0.0.1
* @sAddress[in] Server address IP (127.0.0.1, localhost, etc.)
* @iPort[in] Server socket port, defaults to 12480
* @return True, if connection could be established and streaming parameters match
*/
bool Connect( const std::string& sAddress, int iPort = 12480 );
bool Connect( const std::string& sAddress, const int iPort = 12480, const bool bUseUDP = false );
//! Disconnct safely from server
void Disconnect();
//! Returns the connection status
/**
......@@ -85,43 +102,47 @@ public:
*/
bool GetIsConnected() const;
//! Set allowed latency (s)
//! Returns the minimal latency possible (single block)
/**
* Sets the latency that will be used for reading and writing from ring buffer.
* New samples will be requested and send if the latency / ring buffer samples
* is lower than the target latency.
*/
void SetAllowedLatencySeconds( float fLatencySeconds );
void SetAllowedLatencySamples( int iLatencySamples );
float GetAllowedLatencySeconds() const;
int GetAllowedLatencySamples() const;
//! Sets the minimal latency possible
/**
* Real-time network audio is considered to process at lowest latency possible.
* However, this implementation requires at least one block. Hence latency is
* depending on sampling rate and block length.
*
* @sa GetMinimumLatencySamples()
* @sa GetMinimumLatencySamples()
* @return Minimum latency in seconds
*/
float GetMinimumLatencySeconds() const;
//! Returns the maximum latency possible (entire ring buffer used)
/**
* @return Maximum latency in seconds
*/
float GetMaximumLatencySeconds() const;
//! Returns the minimum latency possible (single block)
/**
* @return Minimum latency in samples
*/
int GetMinimumLatencySamples() const;
//! Returns the maximum latency possible (entire ring buffer used)
/**
* @return Maximum latency in samples
*/
int GetMaximumLatencySamples() const;
//! Sets the latency for real-time processing
//! Returns the NetAudio streaming logger base name
std::string GetNetAudioStreamLoggerBaseName() const;
//! Sets the NetAudio streaming logger base name
/**
* Real-time network audio is considered to process at lowest latency possible.
* However, this implementation requires at least one block. Hence latency is
* depending on sampling rate and block length. This method basically
* sets the minimum allowed latency to this value.
*
* @sa GetMinimumLatencySeconds()
* @sa SetAllowedLatencySeconds()
* If debugging is enabled, all debugging files will be named
* with this suffix.
* @param[in] sBaseName Base name string
*
*/
void SetLatencyForRealtime();
void SetNetAudioStreamingLoggerBaseName( const std::string& sBaseName );
//! Enabled/disables export of loggers
void SetDebuggingEnabled( bool bEnabled );
//! Logging export flag getter
bool GetIsDebuggingEnabled() const;
//! Returns (static) size of ring buffer
/**
......@@ -167,6 +188,7 @@ public:
*/
void IncrementBlockPointer();
protected:
//! This method is called by the networkg client and pushes samples into the ring buffer
/**
......@@ -180,7 +202,7 @@ protected:
*
* @note This method is not called out of the audio streaming context but out of the network context.
*/
int Transmit( const ITASampleFrame& sfNewSamples, int iNumSamples );
int Transmit( const ITASampleFrame& sfNewSamples, const int iNumSamples );
//! Returns samples that can be read from ring buffer
/**
......@@ -194,6 +216,9 @@ protected:
*/
int GetRingBufferFreeSamples() const;
//! Returns a string for the streaming status identifier
static std::string GetStreamingStatusString( const int iStreamingStatus );
private:
CITANetAudioStreamingClient* m_pNetAudioStreamingClient; //!< Audio streaming network client
......@@ -204,16 +229,17 @@ private:
int m_iWriteCursor; //!< Cursor where samples will be fed into ring buffer from net audio producer (always ahead)
bool m_bRingBufferFull; //!< Indicator if ring buffer is full (and read cursor equals write cursor)
ITASampleFrame m_sfRingBuffer; //!< Ring buffer
int m_iTargetSampleLatency; //!< Maximum allowed samples / target sample latency
int m_iStreamingStatus; //!< Current streaming status
double m_dLastStreamingTimeCode;
ITABufferedDataLoggerImplAudio* m_pAudioLogger; //!< Logging for the audio stream
ITABufferedDataLoggerImplStream* m_pStreamLogger; //!< Logging for the audio stream
ITABufferedDataLoggerImplNet* m_pNetLogger; //!< Logging for the network stream
int iAudioStreamingBlockID; //!< Audio streaming block id
int iNetStreamingBlockID; //!< Network streaming block id
ITABufferedDataLoggerImplStream* m_pAudioStreamLogger; //!< Logging for the audio stream
ITABufferedDataLoggerImplNet* m_pNetworkStreamLogger; //!< Logging for the network stream
std::string m_sNetAudioStreamLoggerBaseName;
bool m_bDebuggingEnabled;
int m_iAudioStreamingBlockID; //!< Audio streaming block id
int m_iNetStreamingBlockID; //!< Network streaming block id
friend class CITANetAudioStreamingClient;
};
......
......@@ -21,60 +21,72 @@
#include <ITADataSourcesDefinitions.h>
#include <ITANetAudioProtocol.h>
#include <ITASampleFrame.h>
#include <ITAStopWatch.h>
#include <VistaInterProcComm/Concurrency/VistaThreadLoop.h>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <ITANetAudioProtocol.h>
#include <VistaInterProcComm/Concurrency/VistaThreadLoop.h>
#include <ITASampleFrame.h>
#include <iostream>
class ITADatasource;
class CITANetAudioMessage;
class CITANetAudioProtocol;
class CITANetAudioServer;
class CITANetAudioMessage;
class VistaTCPSocket;
class CITABufferedDataLoggerImplServer;
class VistaConnectionIP;
//! Network audio sample server (for connecting a net audio stream)
//! Network audio streaming server (for connecting a net audio stream) with an ITADataSource connection
/**
* Audio sample transmitter for a networked signal source that can connect via TCP/IP.
*
* \sa CITANetAudioStream
* \note not thread-safe
*/
* Audio sample transmitter for a networked signal source that can connect via TCP/IP.
*
* @sa CITANetAudioStream
* @note not thread-safe
*/
class ITA_DATA_SOURCES_API CITANetAudioStreamingServer : public VistaThreadLoop
{
public:
enum UpdateStrategy
{
AUTO = 1, //!< Automatic update rate based on sample rate and block length of client (default)
ADAPTIVE, //!< Adaptive update rate, adjusts for drifting clocks
CONSTANT, //!< Set a user-defined update rate (may cause forced pausing of sample feeding or dropouts on client side)
};
CITANetAudioStreamingServer();
virtual ~CITANetAudioStreamingServer() {};
~CITANetAudioStreamingServer();
bool Start( const std::string& sAddress, int iPort );
//! Start to listen on a socket (blocking)
bool Start( const std::string& sAddress, const int iPort, const double dTimeIntervalCientSendStatus, const bool bUseUDP = false );
bool IsClientConnected() const;
std::string GetNetworkAddress() const;
int GetNetworkPort() const;
bool LoopBody();
void Stop();
void SetInputStream( ITADatasource* pInStream );
int GetNetStreamBlocklength() const;
int GetNetStreamNumberOfChannels() const;
double GetNetStreamSampleRate() const;
int GetNetStreamNumberOfChannels( ) const;
double GetNetStreamSampleRate( ) const;
double GetEstimatedCorrFactor( ) const;
void SetEstimatedCorrFactor( double dcorrFactor );
//! Enabled/disables export of loggers
void SetDebuggingEnabled( bool bEnabled );
//! Logging export flag getter
bool GetIsDebuggingEnabled() const;
int GetSendingBlockLength() const;
void SetSendingBlockLength( const int iSendingBlockLength );
void SetAutomaticUpdateRate();
void SetTargetLatencySamples( const int iTargetLatency );
int GetTargetLatencySamples() const;
void SetServerLogBaseName( const std::string& sBaseName );
std::string GetServerLogBaseName() const;
bool LoopBody();
protected:
ITADatasource* GetInputStream() const;
......@@ -85,12 +97,25 @@ private:
ITADatasource* m_pInputStream;
VistaConnectionIP* m_pConnection;
CITANetAudioProtocol::StreamingParameters m_oServerParams;
CITANetAudioMessage* m_pIncomingMessage;
CITANetAudioMessage* m_pOutgoingMessage;
CITANetAudioMessage* m_pMessage;
CITABufferedDataLoggerImplServer* m_pServerLogger;
std::string m_sServerLogBaseName;
ITAStopWatch m_swTryReadBlockStats, m_swTryReadAccessStats;
bool m_bDebuggingEnabled;
int m_iServerBlockId;
double m_dLastTimeStamp;
double m_dEstimatedCorrFactor;
int m_iTargetLatencySamples;
int m_iEstimatedClientRingBufferFreeSamples;
int m_iClientRingBufferSize;
int m_iSendingBlockLength;
int m_iMaxSendBlocks;
int m_iUpdateStrategy;
int m_iClientRingBufferFreeSamples;
double m_dStreamTimeStart; //!< Stream time start
long unsigned int m_nStreamSampleCounts; //!< Samples that has been streamed
friend class CITANetAudioServer;
};
......
// $Id: $
/*
* ----------------------------------------------------------------
*
* ITA core libs
* (c) Copyright Institute of Technical Acoustics (ITA)
* RWTH Aachen University, Germany, 2015-2017
*
* ----------------------------------------------------------------
* ____ __________ _______
* // / //__ ___/ // _ |
* // / // / // /_| |
* // / // / // ___ |
* //__/ //__/ //__/ |__|
*
* ----------------------------------------------------------------
*
*/
#ifndef __ITA_STREAM_INFO_H__
#define __ITA_STREAM_INFO_H__
#ifndef INCLUDE_WATCHER_ITA_STREAM_INFO
#define INCLUDE_WATCHER_ITA_STREAM_INFO
#include <ITATypes.h>
// Diese Datenklasse beschreibt den Zustand eines Audiostreams
class ITAStreamInfo {
//! Time code information for audio streams
class ITAStreamInfo
{
public:
// Anzahl der abgespielten Samples seit Beginn des Streamings
uint64_t nSamples;
uint64_t nSamples; //!< Number of samples processed
double dStreamTimeCode; //!< Stream time code (starts with zero)
double dSysTimeCode; //!< System time stamp code (begings with current time stamp of system)
// TODO: Beschreiben
double dTimecode;
inline ITAStreamInfo()
: nSamples( 0 )
, dStreamTimeCode( 0.0f )
, dSysTimeCode( 0.0f )
{};
//! Standard-Konstruktor (setzt alle Werte 0)
ITAStreamInfo() : nSamples(0), dTimecode(0) {}
//! Destruktor
virtual ~ITAStreamInfo() {};
inline virtual ~ITAStreamInfo() {};
};
#endif // __ITA_STREAM_INFO_H__
#endif // INCLUDE_WATCHER_ITA_STREAM_INFO
......@@ -220,7 +220,7 @@ private:
ITADatasource* pDatasource; //!< Datasource assigned to the input
std::vector< const float* > vpfInputData; //!< Pointers to the next stream blocks
inline InputDesc( const int iChannels, const int iBlockLength )
inline InputDesc( const int iChannels, const int )
: vpfInputData( iChannels, nullptr )
, iChannels( iChannels )
, fCurrentGain( 1.0f )
......
#include <ITAAsioInterface.h>
#include <ITAAtomicPrimitives.h>
#include <ITADatasource.h>
#include <ITAException.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>
......@@ -17,17 +21,14 @@
// Puffer-Vielfachheit (Doppelpuffer, Dreifachpuffer, etc.)
const unsigned int ASIOSOURCE_BUFFER_MULTIPLICITY = 16;
class ASIOSource : public ITADatasourceRealization {
class ASIOSource : public ITADatasourceRealization
{
public:
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);
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
......@@ -43,13 +44,14 @@ public:
// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
#if NATIVE_INT64
#define ASIO64toDouble(a) (a)
#define ASIO64toDouble(a) (a)
#else
const double twoRaisedTo32 = 4294967296.;
#define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
const double twoRaisedTo32 = 4294967296.;
#define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
#endif
typedef struct DriverInfo {
typedef struct DriverInfo
{
// Zugehörige ASIO-Treiberinfo (Achtung: Datentyp "ASIODriverInfo")
ASIODriverInfo driverInfo;
......@@ -76,11 +78,11 @@ typedef struct DriverInfo {
// 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
// ASIOBufferInfo bufferInfos[kMaxInputChannels + kMaxOutputChannels]; // buffer info's
std::vector<ASIOBufferInfo> bufferInfos; // buffer info's
// ASIOGetChannelInfo()
// ASIOChannelInfo channelInfos[kMaxInputChannels + kMaxOutputChannels]; // channel info's
// 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
......@@ -99,42 +101,45 @@ typedef struct DriverInfo {
} DriverInfo;
// Statusflags
const long LIBRAW = -1; // Bibliothek wurde noch nicht initialisiert
const long LOADED = 0; // Bibliothek wurde initialisiert
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
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?
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
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
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
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 ulOutputCounter=0;
unsigned long ulInputCounter=0;
unsigned long ulInputPresentCounter=0;
int iInputBufferNrOffset=0;
unsigned long ulOutputBlockCounter = 0;
unsigned long ulInputCounter = 0;
unsigned long ulInputPresentCounter = 0;
int iInputBufferNrOffset = 0;
long lBuffersize=0; // Ausgewählte Puffergröße
long lBuffersize = 0; // Ausgewählte Puffergröße
ITAAtomicInt iBufferswitchEntrances(0);
ITAAtomicInt iBufferswitchEntrances( 0 );
ITAStopWatch g_swStreamOutputProcessing;
// -= Externe Referenzen =-
extern AsioDrivers *asioDrivers;
......@@ -145,113 +150,113 @@ extern AsioDrivers *asioDrivers;
| ASIO-Callbacks |
| |
+------------------+
*/
*/
ASIOCallbacks asioCallbacks;
ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processNow) {
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.
hence be sure that you take care about thread synchronization.
This is omitted here for simplicity. */
if (++iBufferswitchEntrances > 1)
if( ++iBufferswitchEntrances > 1 )
std::cerr << "Problem: ASIO bufferswitch callback reentrance!\n" << std::endl;
// Internen Mutex in Besitz bringen
EnterCriticalSection(&csInternal);
EnterCriticalSection( &csInternal );
// Store the timeInfo for later use
asioDriverInfo.tInfo = *timeInfo;
lAsioBufferIndex = index;
abProcessNow = processNow;
long lCounter = 0;
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);
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);
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);
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);
*/
/*
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;
int iCount = 0;
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)
/* 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);
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 );
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) {
if( !bInputDataPresent ) {
// Noch keine Eingabesamples: Stille bereistellen
float* pfBuffer = pasInputDatasource->GetWritePointer(iChannelNumber);
for (j=0; j<lBuffersize; j++) pfBuffer[j] = 0;
} else {
float* pfBuffer = pasInputDatasource->GetWritePointer( iChannelNumber );
for( j = 0; j < lBuffersize; j++ ) pfBuffer[ j ] = 0;
}
else {
// Eingabesamples verfügbar. Samples konvertieren.
pfInputBuffer = pasInputDatasource->GetWritePointer(iChannelNumber);
pfInputBuffer = pasInputDatasource->GetWritePointer( iChannelNumber );
switch (asioDriverInfo.channelInfos[i].type) {
case ASIOSTInt16LSB:
switch( asioDriverInfo.channelInfos[ i ].type ) {
case ASIOSTInt16LSB:
__int16 *psAsioBufPtr;
psAsioBufPtr = (__int16*) asioDriverInfo.bufferInfos[i].buffers[lAsioBufferIndex];
psAsioBufPtr = ( __int16* ) asioDriverInfo.bufferInfos[ i ].buffers[ lAsioBufferIndex ];
// Konvertierung Int16 nach Float
for (j=0; j<lBuffersize; j++)
pfInputBuffer[j] = ((float) psAsioBufPtr[j]) / 32767.0F;
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];
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);
for( j = 0; j < lBuffersize; j++ ) {
memcpy( &i32Temp, pcAsioBufPtr, 3 );
pcAsioBufPtr += 3;
pfInputBuffer[j] = ((float) i32Temp) / 8388607.0F;
}
pfInputBuffer[ j ] = ( ( float ) i32Temp ) / 8388607.0F;
}
break;
case ASIOSTInt32LSB:
__int32 *piAsioBufPtr;
piAsioBufPtr = (__int32*) asioDriverInfo.bufferInfos[i].buffers[lAsioBufferIndex];
piAsioBufPtr = ( __int32* ) asioDriverInfo.bufferInfos[ i ].buffers[ lAsioBufferIndex ];
/*
* Bugfix [fwe 2008-09-24]:
......@@ -263,16 +268,16 @@ ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processN
* (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++)
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;
pfInputBuffer[ j ] = ( ( float ) piAsioBufPtr[ j ] ) / 2147483392.0F;
break;
}
......@@ -280,20 +285,25 @@ ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processN
}
}
if (pasInputDatasource) pasInputDatasource->IncrementWritePointer();
if( pasInputDatasource )
pasInputDatasource->IncrementWritePointer();
}
// -= Ausgabedaten schreiben =-
/* Hinweis: Falls iZeroBlocks != -1 ist wurde ASIOStop bereits ausgelöst und
jetzt müssen Nullblöcke abgespielt werden.
jetzt müssen Nullblöcke abgespielt werden.
iZeroBlocks muß heruntergezählt werden. */
// Output-Kanaele schreiben
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 );
// 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;
......@@ -302,49 +312,52 @@ ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processN
// TODO: Streaminfo zusammenstellen? TimeCode aus ASIO rein?
// Gibts eine Ausgabedatenquelle?
if (pidsOutputDatasource && (iZeroBlocks == -1)) {
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);
pfOutputData = pidsOutputDatasource->GetBlockPointer( ( unsigned int ) iChannelNumber, &siStreamInfo );
// Falls die Quelle keine Daten hat, Stille verwenden
if (!pfOutputData) pfOutputData = pfSilence;
if( !pfOutputData )
pfOutputData = pfSilence;
}
switch (asioDriverInfo.channelInfos[i].type)
switch( asioDriverInfo.channelInfos[ i ].type )
{
case ASIOSTInt16LSB:
#if DEBUG_OUT
std::cout << "ASIOSTInt16LSB nicht unterstuetzt " ;
#endif
#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);
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
#if DEBUG_OUT
std::cout << "ASIOSTInt24LSB nicht unterstuetzt ";
#endif
char *pcAsioBufPtr;
pcAsioBufPtr = (char*) asioDriverInfo.bufferInfos[i].buffers[lAsioBufferIndex];
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);
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
#if DEBUG_OUT
std::cout << "ASIOSTInt32LSB nicht unterstuetzt";
#endif
/*
* Bugfix [fwe 2008-09-24]:
......@@ -356,20 +369,20 @@ ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processN
* (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];
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;
for( j = 0; j < lBuffersize; j++ )
piAsioBufPtr[ j ] = ( __int32 ) ( pfOutputData[ j ] * fScaling );
break;
// Alles weitere wird noch nicht unterstützt!!
// Int
// Alles weitere wird noch nicht unterstützt!!
// Int
case ASIOSTInt32LSB16: // 32 bit data with 18 bit alignment
std::cout << "ASIOSTInt32LSB16 nicht unterstuetzt ";
break;
......@@ -392,8 +405,8 @@ ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processN
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
// 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;
......@@ -407,7 +420,7 @@ ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processN
std::cout << "ASIOSTInt32MSB24 nicht unterstuetzt ";
break;
// Float
// Float
case ASIOSTFloat32LSB: // IEEE 754 32 bit float, as found on Intel x86 architecture
std::cout << "ASIOSTFloat32LSB nicht unterstuetzt ";
break;
......@@ -422,34 +435,40 @@ ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processN
std::cout << "ASIOSTFloat64MSB nicht unterstuetzt";
break;
default:
std::cout << "- " ;
std::cout << "- ";
}
}
}
ulOutputCounter++;
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;
if (ulInputPresentCounter <= 1)
ulOutputBlockCounter++;
if( ulInputPresentCounter <= 1 )
ulInputPresentCounter++;
else
ulInputCounter++;
processedSamples += lBuffersize;
// Zwischenlüsung: Streaminfo einfach inkrementieren. Keinen Timcode.
siStreamInfo.nSamples += lBuffersize;
siStreamInfo.dTimecode = (double) (siStreamInfo.nSamples) / (double) asioDriverInfo.sampleRate;
//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();
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);
if( iZeroBlocks > 0 )
if( ( --iZeroBlocks ) == 0 ) SetEvent( hStopEvent );
// Internen Mutex freigeben
LeaveCriticalSection(&csInternal);
LeaveCriticalSection( &csInternal );
// Finally if the driver supports the ASIOOutputReady() optimization,
// do it here, all data are in place
//if (asioDriverInfo.postOutput)
......@@ -460,7 +479,7 @@ ASIOTime *bufferSwitchTimeInfo(ASIOTime *timeInfo, long index, ASIOBool processN
return 0L;
}
void bufferSwitch(long index, ASIOBool processNow) { // the actual processing callback.
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.
......@@ -471,20 +490,21 @@ void bufferSwitch(long index, ASIOBool processNow) { // the actual processing ca
// 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));
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)
if( ASIOGetSamplePosition( &timeInfo.timeInfo.samplePosition,
&timeInfo.timeInfo.systemTime ) == ASE_OK )
timeInfo.timeInfo.flags = kSystemTimeValid | kSamplePositionValid;
bufferSwitchTimeInfo(&timeInfo, index, processNow);
bufferSwitchTimeInfo( &timeInfo, index, processNow );
}
void sampleRateChanged(ASIOSampleRate sRate) {
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
......@@ -494,68 +514,69 @@ void sampleRateChanged(ASIOSampleRate sRate) {
}
long asioMessages(long selector, long value, void* message, double* opt) {
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
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
|| value == kAsioSupportsInputMonitor )
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;
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;
}
......@@ -566,18 +587,19 @@ long asioMessages(long selector, long value, void* message, double* opt) {
| Implementierung der öffentlichen Funktionen |
| |
+-----------------------------------------------+
*/
*/
ITASIO_API void ITAsioInitializeLibrary(void) {
ITASIO_API void ITAsioInitializeLibrary( void )
{
if (lState != LIBRAW)
ITA_EXCEPT1(MODAL_EXCEPTION, "ASIO library already initialized");
InitializeCriticalSection(&csInternal);
InitializeCriticalSection(&csExternal);
if( lState != LIBRAW )
ITA_EXCEPT1( MODAL_EXCEPTION, "ASIO library already initialized" );
if (FAILED(hStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
ITA_EXCEPT1(UNKNOWN, "Failed to create mutex");
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();
......@@ -590,40 +612,42 @@ ITASIO_API void ITAsioInitializeLibrary(void) {
// 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];
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);
asioDrivers->getDriverNames( asioDriverNames, lNumDrivers );
}
// Startzustand setzen
lState = LOADED;
}
ITASIO_API void ITAsioFinalizeLibrary(void) {
ITASIO_API void ITAsioFinalizeLibrary( void )
{
// Gar nicht initialisiert? => Kein Fehler
if (lState == LIBRAW) return;
if( lState == LIBRAW ) return;
if( lNumDrivers > 0 )
{
for( int i=0; i<(int)lNumDrivers; i++ ) delete[] asioDriverNames[i];
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();
if( lState > LOADED ) ITAsioFinalizeDriver();
// ASIO-Umgebung und Resourcen freigeben
delete asioDrivers;
CloseHandle(hStopEvent);
CloseHandle( hStopEvent );
DeleteCriticalSection(&csInternal);
DeleteCriticalSection(&csExternal);
DeleteCriticalSection( &csInternal );
DeleteCriticalSection( &csExternal );
lState = LIBRAW;
}
......@@ -634,24 +658,29 @@ ITASIO_API void ITAsioFinalizeLibrary(void) {
#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");
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");
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];
if( ( lDriverNr < 0 ) || ( lDriverNr >= lNumDrivers ) )
return &EMPTY_STRING;
return asioDriverNames[ lDriverNr ];
}
ITASIO_API const char* ITAsioGetErrorStr(ASIOError ae) {
switch (ae) {
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";
......@@ -677,21 +706,29 @@ ITASIO_API const char* ITAsioGetErrorStr(ASIOError ae) {
}
}
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");
ITASIO_API ASIOError ITAsioInitializeDriver( long lDriverNr )
{
if( lState < LOADED )