Updated RtMidi and RtAudio files after new releases.

This commit is contained in:
Gary Scavone
2016-02-22 20:05:28 -05:00
parent 83b75ed339
commit 126ff9d9e1
4 changed files with 330 additions and 234 deletions

View File

@@ -10,7 +10,7 @@
RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
RtAudio: realtime audio i/o C++ classes RtAudio: realtime audio i/o C++ classes
Copyright (c) 2001-2014 Gary P. Scavone Copyright (c) 2001-2016 Gary P. Scavone
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files obtaining a copy of this software and associated documentation files
@@ -45,7 +45,7 @@
#ifndef __RTAUDIO_H #ifndef __RTAUDIO_H
#define __RTAUDIO_H #define __RTAUDIO_H
#define RTAUDIO_VERSION "4.1.1" #define RTAUDIO_VERSION "4.1.2"
#include <string> #include <string>
#include <vector> #include <vector>
@@ -286,12 +286,13 @@ class RtAudio
bool isDefaultOutput; /*!< true if this is the default output device. */ bool isDefaultOutput; /*!< true if this is the default output device. */
bool isDefaultInput; /*!< true if this is the default input device. */ bool isDefaultInput; /*!< true if this is the default input device. */
std::vector<unsigned int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */ std::vector<unsigned int> sampleRates; /*!< Supported sample rates (queried from list of standard rates). */
unsigned int preferredSampleRate; /*!< Preferred sample rate, eg. for WASAPI the system sample rate. */
RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */ RtAudioFormat nativeFormats; /*!< Bit mask of supported data formats. */
// Default constructor. // Default constructor.
DeviceInfo() DeviceInfo()
:probed(false), outputChannels(0), inputChannels(0), duplexChannels(0), :probed(false), outputChannels(0), inputChannels(0), duplexChannels(0),
isDefaultOutput(false), isDefaultInput(false), nativeFormats(0) {} isDefaultOutput(false), isDefaultInput(false), preferredSampleRate(0), nativeFormats(0) {}
}; };
//! The structure for specifying input or ouput stream parameters. //! The structure for specifying input or ouput stream parameters.

View File

@@ -8,7 +8,7 @@
RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
RtMidi: realtime MIDI i/o C++ classes RtMidi: realtime MIDI i/o C++ classes
Copyright (c) 2003-2014 Gary P. Scavone Copyright (c) 2003-2016 Gary P. Scavone
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files obtaining a copy of this software and associated documentation files
@@ -43,7 +43,7 @@
#ifndef RTMIDI_H #ifndef RTMIDI_H
#define RTMIDI_H #define RTMIDI_H
#define RTMIDI_VERSION "2.1.0" #define RTMIDI_VERSION "2.1.1"
#include <exception> #include <exception>
#include <iostream> #include <iostream>
@@ -109,7 +109,7 @@ class RtMidiError : public std::exception
Note that class behaviour is undefined after a critical error (not Note that class behaviour is undefined after a critical error (not
a warning) is reported. a warning) is reported.
*/ */
typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText ); typedef void (*RtMidiErrorCallback)( RtMidiError::Type type, const std::string &errorText, void *userData );
class MidiApi; class MidiApi;
@@ -161,7 +161,7 @@ class RtMidi
The callback function will be called whenever an error has occured. It is best The callback function will be called whenever an error has occured. It is best
to set the error callback function before opening a port. to set the error callback function before opening a port.
*/ */
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL ) = 0; virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 ) = 0;
protected: protected:
@@ -322,7 +322,7 @@ class RtMidiIn : public RtMidi
The callback function will be called whenever an error has occured. It is best The callback function will be called whenever an error has occured. It is best
to set the error callback function before opening a port. to set the error callback function before opening a port.
*/ */
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL ); virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );
protected: protected:
void openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit ); void openMidiApi( RtMidi::Api api, const std::string clientName, unsigned int queueSizeLimit );
@@ -413,7 +413,7 @@ class RtMidiOut : public RtMidi
The callback function will be called whenever an error has occured. It is best The callback function will be called whenever an error has occured. It is best
to set the error callback function before opening a port. to set the error callback function before opening a port.
*/ */
virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL ); virtual void setErrorCallback( RtMidiErrorCallback errorCallback = NULL, void *userData = 0 );
protected: protected:
void openMidiApi( RtMidi::Api api, const std::string clientName ); void openMidiApi( RtMidi::Api api, const std::string clientName );
@@ -448,7 +448,7 @@ class MidiApi
virtual std::string getPortName( unsigned int portNumber ) = 0; virtual std::string getPortName( unsigned int portNumber ) = 0;
inline bool isPortOpen() const { return connected_; } inline bool isPortOpen() const { return connected_; }
void setErrorCallback( RtMidiErrorCallback errorCallback ); void setErrorCallback( RtMidiErrorCallback errorCallback, void *userData );
//! A basic error reporting function for RtMidi classes. //! A basic error reporting function for RtMidi classes.
void error( RtMidiError::Type type, std::string errorString ); void error( RtMidiError::Type type, std::string errorString );
@@ -460,6 +460,8 @@ protected:
bool connected_; bool connected_;
std::string errorString_; std::string errorString_;
RtMidiErrorCallback errorCallback_; RtMidiErrorCallback errorCallback_;
bool firstErrorOccurred_;
void *errorCallbackUserData_;
}; };
class MidiInApi : public MidiApi class MidiInApi : public MidiApi
@@ -547,7 +549,7 @@ inline unsigned int RtMidiIn :: getPortCount( void ) { return rtapi_->getPortCou
inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } inline std::string RtMidiIn :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); }
inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { ((MidiInApi *)rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); } inline void RtMidiIn :: ignoreTypes( bool midiSysex, bool midiTime, bool midiSense ) { ((MidiInApi *)rtapi_)->ignoreTypes( midiSysex, midiTime, midiSense ); }
inline double RtMidiIn :: getMessage( std::vector<unsigned char> *message ) { return ((MidiInApi *)rtapi_)->getMessage( message ); } inline double RtMidiIn :: getMessage( std::vector<unsigned char> *message ) { return ((MidiInApi *)rtapi_)->getMessage( message ); }
inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback ) { rtapi_->setErrorCallback(errorCallback); } inline void RtMidiIn :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); }
inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); } inline RtMidi::Api RtMidiOut :: getCurrentApi( void ) throw() { return rtapi_->getCurrentApi(); }
inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); } inline void RtMidiOut :: openPort( unsigned int portNumber, const std::string portName ) { rtapi_->openPort( portNumber, portName ); }
@@ -557,7 +559,7 @@ inline bool RtMidiOut :: isPortOpen() const { return rtapi_->isPortOpen(); }
inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); } inline unsigned int RtMidiOut :: getPortCount( void ) { return rtapi_->getPortCount(); }
inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); } inline std::string RtMidiOut :: getPortName( unsigned int portNumber ) { return rtapi_->getPortName( portNumber ); }
inline void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ) { ((MidiOutApi *)rtapi_)->sendMessage( message ); } inline void RtMidiOut :: sendMessage( std::vector<unsigned char> *message ) { ((MidiOutApi *)rtapi_)->sendMessage( message ); }
inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback ) { rtapi_->setErrorCallback(errorCallback); } inline void RtMidiOut :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData ) { rtapi_->setErrorCallback(errorCallback, userData); }
// **************************************************************** // // **************************************************************** //
// //

View File

@@ -10,7 +10,7 @@
RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/ RtAudio WWW site: http://www.music.mcgill.ca/~gary/rtaudio/
RtAudio: realtime audio i/o C++ classes RtAudio: realtime audio i/o C++ classes
Copyright (c) 2001-2014 Gary P. Scavone Copyright (c) 2001-2016 Gary P. Scavone
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files obtaining a copy of this software and associated documentation files
@@ -38,13 +38,14 @@
*/ */
/************************************************************************/ /************************************************************************/
// RtAudio: Version 4.1.1 // RtAudio: Version 4.1.2
#include "RtAudio.h" #include "RtAudio.h"
#include <iostream> #include <iostream>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <climits> #include <climits>
#include <algorithm>
// Static variable definitions. // Static variable definitions.
const unsigned int RtApi::MAX_SAMPLE_RATES = 14; const unsigned int RtApi::MAX_SAMPLE_RATES = 14;
@@ -58,6 +59,22 @@ const unsigned int RtApi::SAMPLE_RATES[] = {
#define MUTEX_DESTROY(A) DeleteCriticalSection(A) #define MUTEX_DESTROY(A) DeleteCriticalSection(A)
#define MUTEX_LOCK(A) EnterCriticalSection(A) #define MUTEX_LOCK(A) EnterCriticalSection(A)
#define MUTEX_UNLOCK(A) LeaveCriticalSection(A) #define MUTEX_UNLOCK(A) LeaveCriticalSection(A)
#include "tchar.h"
static std::string convertCharPointerToStdString(const char *text)
{
return std::string(text);
}
static std::string convertCharPointerToStdString(const wchar_t *text)
{
int length = WideCharToMultiByte(CP_UTF8, 0, text, -1, NULL, 0, NULL, NULL);
std::string s( length-1, '\0' );
WideCharToMultiByte(CP_UTF8, 0, text, -1, &s[0], length, NULL, NULL);
return s;
}
#elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__) #elif defined(__LINUX_ALSA__) || defined(__LINUX_PULSE__) || defined(__UNIX_JACK__) || defined(__LINUX_OSS__) || defined(__MACOSX_CORE__)
// pthread API // pthread API
#define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL) #define MUTEX_INITIALIZE(A) pthread_mutex_init(A, NULL)
@@ -179,7 +196,7 @@ RtAudio :: RtAudio( RtAudio::Api api )
getCompiledApi( apis ); getCompiledApi( apis );
for ( unsigned int i=0; i<apis.size(); i++ ) { for ( unsigned int i=0; i<apis.size(); i++ ) {
openRtApi( apis[i] ); openRtApi( apis[i] );
if ( rtapi_->getDeviceCount() ) break; if ( rtapi_ && rtapi_->getDeviceCount() ) break;
} }
if ( rtapi_ ) return; if ( rtapi_ ) return;
@@ -761,9 +778,14 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
bool haveValueRange = false; bool haveValueRange = false;
info.sampleRates.clear(); info.sampleRates.clear();
for ( UInt32 i=0; i<nRanges; i++ ) { for ( UInt32 i=0; i<nRanges; i++ ) {
if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) if ( rangeList[i].mMinimum == rangeList[i].mMaximum ) {
info.sampleRates.push_back( (unsigned int) rangeList[i].mMinimum ); unsigned int tmpSr = (unsigned int) rangeList[i].mMinimum;
else { info.sampleRates.push_back( tmpSr );
if ( !info.preferredSampleRate || ( tmpSr <= 48000 && tmpSr > info.preferredSampleRate ) )
info.preferredSampleRate = tmpSr;
} else {
haveValueRange = true; haveValueRange = true;
if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum; if ( rangeList[i].mMinimum > minimumRate ) minimumRate = rangeList[i].mMinimum;
if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum; if ( rangeList[i].mMaximum < maximumRate ) maximumRate = rangeList[i].mMaximum;
@@ -772,8 +794,12 @@ RtAudio::DeviceInfo RtApiCore :: getDeviceInfo( unsigned int device )
if ( haveValueRange ) { if ( haveValueRange ) {
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) { for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) if ( SAMPLE_RATES[k] >= (unsigned int) minimumRate && SAMPLE_RATES[k] <= (unsigned int) maximumRate ) {
info.sampleRates.push_back( SAMPLE_RATES[k] ); info.sampleRates.push_back( SAMPLE_RATES[k] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[k];
}
} }
} }
@@ -1380,6 +1406,18 @@ void RtApiCore :: closeStream( void )
CoreHandle *handle = (CoreHandle *) stream_.apiHandle; CoreHandle *handle = (CoreHandle *) stream_.apiHandle;
if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) { if ( stream_.mode == OUTPUT || stream_.mode == DUPLEX ) {
if (handle) {
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
property.mSelector = kAudioDeviceProcessorOverload;
property.mScope = kAudioObjectPropertyScopeGlobal;
if (AudioObjectRemovePropertyListener( handle->id[0], &property, xrunListener, (void *) handle ) != noErr) {
errorText_ = "RtApiCore::closeStream(): error removing property listener!";
error( RtAudioError::WARNING );
}
}
if ( stream_.state == STREAM_RUNNING ) if ( stream_.state == STREAM_RUNNING )
AudioDeviceStop( handle->id[0], callbackHandler ); AudioDeviceStop( handle->id[0], callbackHandler );
#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
@@ -1391,6 +1429,18 @@ void RtApiCore :: closeStream( void )
} }
if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) { if ( stream_.mode == INPUT || ( stream_.mode == DUPLEX && stream_.device[0] != stream_.device[1] ) ) {
if (handle) {
AudioObjectPropertyAddress property = { kAudioHardwarePropertyDevices,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster };
property.mSelector = kAudioDeviceProcessorOverload;
property.mScope = kAudioObjectPropertyScopeGlobal;
if (AudioObjectRemovePropertyListener( handle->id[1], &property, xrunListener, (void *) handle ) != noErr) {
errorText_ = "RtApiCore::closeStream(): error removing property listener!";
error( RtAudioError::WARNING );
}
}
if ( stream_.state == STREAM_RUNNING ) if ( stream_.state == STREAM_RUNNING )
AudioDeviceStop( handle->id[1], callbackHandler ); AudioDeviceStop( handle->id[1], callbackHandler );
#if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 ) #if defined( MAC_OS_X_VERSION_10_5 ) && ( MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 )
@@ -1984,7 +2034,9 @@ RtAudio::DeviceInfo RtApiJack :: getDeviceInfo( unsigned int device )
// Get the current jack server sample rate. // Get the current jack server sample rate.
info.sampleRates.clear(); info.sampleRates.clear();
info.sampleRates.push_back( jack_get_sample_rate( client ) );
info.preferredSampleRate = jack_get_sample_rate( client );
info.sampleRates.push_back( info.preferredSampleRate );
// Count the available ports containing the client name as device // Count the available ports containing the client name as device
// channels. Jack "input ports" equal RtAudio output channels. // channels. Jack "input ports" equal RtAudio output channels.
@@ -2764,8 +2816,12 @@ RtAudio::DeviceInfo RtApiAsio :: getDeviceInfo( unsigned int device )
info.sampleRates.clear(); info.sampleRates.clear();
for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) { for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] ); result = ASIOCanSampleRate( (ASIOSampleRate) SAMPLE_RATES[i] );
if ( result == ASE_OK ) if ( result == ASE_OK ) {
info.sampleRates.push_back( SAMPLE_RATES[i] ); info.sampleRates.push_back( SAMPLE_RATES[i] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[i];
}
} }
// Determine supported data types ... just check first channel and assume rest are the same. // Determine supported data types ... just check first channel and assume rest are the same.
@@ -2824,9 +2880,12 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
unsigned int firstChannel, unsigned int sampleRate, unsigned int firstChannel, unsigned int sampleRate,
RtAudioFormat format, unsigned int *bufferSize, RtAudioFormat format, unsigned int *bufferSize,
RtAudio::StreamOptions *options ) RtAudio::StreamOptions *options )
{ {////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool isDuplexInput = mode == INPUT && stream_.mode == OUTPUT;
// For ASIO, a duplex stream MUST use the same driver. // For ASIO, a duplex stream MUST use the same driver.
if ( mode == INPUT && stream_.mode == OUTPUT && stream_.device[0] != device ) { if ( isDuplexInput && stream_.device[0] != device ) {
errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!"; errorText_ = "RtApiAsio::probeDeviceOpen: an ASIO duplex stream must use the same device for input and output!";
return FAILURE; return FAILURE;
} }
@@ -2840,7 +2899,7 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
} }
// Only load the driver once for duplex stream. // Only load the driver once for duplex stream.
if ( mode != INPUT || stream_.mode != OUTPUT ) { if ( !isDuplexInput ) {
// The getDeviceInfo() function will not work when a stream is open // The getDeviceInfo() function will not work when a stream is open
// because ASIO does not allow multiple devices to run at the same // because ASIO does not allow multiple devices to run at the same
// time. Thus, we'll probe the system before opening a stream and // time. Thus, we'll probe the system before opening a stream and
@@ -2861,22 +2920,26 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
} }
} }
// keep them before any "goto error", they are used for error cleanup + goto device boundary checks
bool buffersAllocated = false;
AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
unsigned int nChannels;
// Check the device channel count. // Check the device channel count.
long inputChannels, outputChannels; long inputChannels, outputChannels;
result = ASIOGetChannels( &inputChannels, &outputChannels ); result = ASIOGetChannels( &inputChannels, &outputChannels );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: error (" << getAsioErrorString( result ) << ") getting channel count (" << driverName << ").";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) || if ( ( mode == OUTPUT && (channels+firstChannel) > (unsigned int) outputChannels) ||
( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) { ( mode == INPUT && (channels+firstChannel) > (unsigned int) inputChannels) ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested channel count (" << channels << ") + offset (" << firstChannel << ").";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
stream_.nDeviceChannels[mode] = channels; stream_.nDeviceChannels[mode] = channels;
stream_.nUserChannels[mode] = channels; stream_.nUserChannels[mode] = channels;
@@ -2885,30 +2948,27 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
// Verify the sample rate is supported. // Verify the sample rate is supported.
result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate ); result = ASIOCanSampleRate( (ASIOSampleRate) sampleRate );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") does not support requested sample rate (" << sampleRate << ").";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
// Get the current sample rate // Get the current sample rate
ASIOSampleRate currentRate; ASIOSampleRate currentRate;
result = ASIOGetSampleRate( &currentRate ); result = ASIOGetSampleRate( &currentRate );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error getting sample rate.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
// Set the sample rate only if necessary // Set the sample rate only if necessary
if ( currentRate != sampleRate ) { if ( currentRate != sampleRate ) {
result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate ); result = ASIOSetSampleRate( (ASIOSampleRate) sampleRate );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error setting sample rate (" << sampleRate << ").";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
} }
@@ -2919,10 +2979,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
else channelInfo.isInput = true; else channelInfo.isInput = true;
result = ASIOGetChannelInfo( &channelInfo ); result = ASIOGetChannelInfo( &channelInfo );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting data format.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
// Assuming WINDOWS host is always little-endian. // Assuming WINDOWS host is always little-endian.
@@ -2951,10 +3010,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
} }
if ( stream_.deviceFormat[mode] == 0 ) { if ( stream_.deviceFormat[mode] == 0 ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") data format not supported by RtAudio.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
// Set the buffer size. For a duplex stream, this will end up // Set the buffer size. For a duplex stream, this will end up
@@ -2963,49 +3021,63 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
long minSize, maxSize, preferSize, granularity; long minSize, maxSize, preferSize, granularity;
result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity ); result = ASIOGetBufferSize( &minSize, &maxSize, &preferSize, &granularity );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
drivers.removeCurrentDriver();
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting buffer size.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
return FAILURE; goto error;
} }
if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize; if ( isDuplexInput ) {
else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; // When this is the duplex input (output was opened before), then we have to use the same
else if ( granularity == -1 ) { // buffersize as the output, because it might use the preferred buffer size, which most
// Make sure bufferSize is a power of two. // likely wasn't passed as input to this. The buffer sizes have to be identically anyway,
int log2_of_min_size = 0; // So instead of throwing an error, make them equal. The caller uses the reference
int log2_of_max_size = 0; // to the "bufferSize" param as usual to set up processing buffers.
for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) { *bufferSize = stream_.bufferSize;
if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
}
long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) ); } else {
int min_delta_num = log2_of_min_size; if ( *bufferSize == 0 ) *bufferSize = preferSize;
else if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
if (current_delta < min_delta) {
min_delta = current_delta;
min_delta_num = i;
}
}
*bufferSize = ( (unsigned int)1 << min_delta_num );
if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize; else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
} else if ( granularity == -1 ) {
else if ( granularity != 0 ) { // Make sure bufferSize is a power of two.
// Set to an even multiple of granularity, rounding up. int log2_of_min_size = 0;
*bufferSize = (*bufferSize + granularity-1) / granularity * granularity; int log2_of_max_size = 0;
for ( unsigned int i = 0; i < sizeof(long) * 8; i++ ) {
if ( minSize & ((long)1 << i) ) log2_of_min_size = i;
if ( maxSize & ((long)1 << i) ) log2_of_max_size = i;
}
long min_delta = std::abs( (long)*bufferSize - ((long)1 << log2_of_min_size) );
int min_delta_num = log2_of_min_size;
for (int i = log2_of_min_size + 1; i <= log2_of_max_size; i++) {
long current_delta = std::abs( (long)*bufferSize - ((long)1 << i) );
if (current_delta < min_delta) {
min_delta = current_delta;
min_delta_num = i;
}
}
*bufferSize = ( (unsigned int)1 << min_delta_num );
if ( *bufferSize < (unsigned int) minSize ) *bufferSize = (unsigned int) minSize;
else if ( *bufferSize > (unsigned int) maxSize ) *bufferSize = (unsigned int) maxSize;
}
else if ( granularity != 0 ) {
// Set to an even multiple of granularity, rounding up.
*bufferSize = (*bufferSize + granularity-1) / granularity * granularity;
}
} }
if ( mode == INPUT && stream_.mode == OUTPUT && stream_.bufferSize != *bufferSize ) { /*
drivers.removeCurrentDriver(); // we don't use it anymore, see above!
// Just left it here for the case...
if ( isDuplexInput && stream_.bufferSize != *bufferSize ) {
errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!"; errorText_ = "RtApiAsio::probeDeviceOpen: input/output buffersize discrepancy!";
return FAILURE; goto error;
} }
*/
stream_.bufferSize = *bufferSize; stream_.bufferSize = *bufferSize;
stream_.nBuffers = 2; stream_.nBuffers = 2;
@@ -3017,16 +3089,13 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
stream_.deviceInterleaved[mode] = false; stream_.deviceInterleaved[mode] = false;
// Allocate, if necessary, our AsioHandle structure for the stream. // Allocate, if necessary, our AsioHandle structure for the stream.
AsioHandle *handle = (AsioHandle *) stream_.apiHandle;
if ( handle == 0 ) { if ( handle == 0 ) {
try { try {
handle = new AsioHandle; handle = new AsioHandle;
} }
catch ( std::bad_alloc& ) { catch ( std::bad_alloc& ) {
//if ( handle == NULL ) {
drivers.removeCurrentDriver();
errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory."; errorText_ = "RtApiAsio::probeDeviceOpen: error allocating AsioHandle memory.";
return FAILURE; goto error;
} }
handle->bufferInfos = 0; handle->bufferInfos = 0;
@@ -3041,15 +3110,14 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
// Create the ASIO internal buffers. Since RtAudio sets up input // Create the ASIO internal buffers. Since RtAudio sets up input
// and output separately, we'll have to dispose of previously // and output separately, we'll have to dispose of previously
// created output buffers for a duplex stream. // created output buffers for a duplex stream.
long inputLatency, outputLatency;
if ( mode == INPUT && stream_.mode == OUTPUT ) { if ( mode == INPUT && stream_.mode == OUTPUT ) {
ASIODisposeBuffers(); ASIODisposeBuffers();
if ( handle->bufferInfos ) free( handle->bufferInfos ); if ( handle->bufferInfos ) free( handle->bufferInfos );
} }
// Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
bool buffersAllocated = false; unsigned int i;
unsigned int i, nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1]; nChannels = stream_.nDeviceChannels[0] + stream_.nDeviceChannels[1];
handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) ); handle->bufferInfos = (ASIOBufferInfo *) malloc( nChannels * sizeof(ASIOBufferInfo) );
if ( handle->bufferInfos == NULL ) { if ( handle->bufferInfos == NULL ) {
errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ")."; errorStream_ << "RtApiAsio::probeDeviceOpen: error allocating bufferInfo memory for driver (" << driverName << ").";
@@ -3070,18 +3138,37 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
infos->buffers[0] = infos->buffers[1] = 0; infos->buffers[0] = infos->buffers[1] = 0;
} }
// prepare for callbacks
stream_.sampleRate = sampleRate;
stream_.device[mode] = device;
stream_.mode = isDuplexInput ? DUPLEX : mode;
// store this class instance before registering callbacks, that are going to use it
asioCallbackInfo = &stream_.callbackInfo;
stream_.callbackInfo.object = (void *) this;
// Set up the ASIO callback structure and create the ASIO data buffers. // Set up the ASIO callback structure and create the ASIO data buffers.
asioCallbacks.bufferSwitch = &bufferSwitch; asioCallbacks.bufferSwitch = &bufferSwitch;
asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.sampleRateDidChange = &sampleRateChanged;
asioCallbacks.asioMessage = &asioMessages; asioCallbacks.asioMessage = &asioMessages;
asioCallbacks.bufferSwitchTimeInfo = NULL; asioCallbacks.bufferSwitchTimeInfo = NULL;
result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks ); result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
if ( result != ASE_OK ) {
// Standard method failed. This can happen with strict/misbehaving drivers that return valid buffer size ranges
// but only accept the preferred buffer size as parameter for ASIOCreateBuffers. eg. Creatives ASIO driver
// in that case, let's be naïve and try that instead
*bufferSize = preferSize;
stream_.bufferSize = *bufferSize;
result = ASIOCreateBuffers( handle->bufferInfos, nChannels, stream_.bufferSize, &asioCallbacks );
}
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") creating buffers.";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
goto error; goto error;
} }
buffersAllocated = true; buffersAllocated = true;
stream_.state = STREAM_STOPPED;
// Set flags for buffer conversion. // Set flags for buffer conversion.
stream_.doConvertBuffer[mode] = false; stream_.doConvertBuffer[mode] = false;
@@ -3104,11 +3191,9 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
bool makeBuffer = true; bool makeBuffer = true;
bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] ); bufferBytes = stream_.nDeviceChannels[mode] * formatBytes( stream_.deviceFormat[mode] );
if ( mode == INPUT ) { if ( isDuplexInput && stream_.deviceBuffer ) {
if ( stream_.mode == OUTPUT && stream_.deviceBuffer ) { unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] );
unsigned long bytesOut = stream_.nDeviceChannels[0] * formatBytes( stream_.deviceFormat[0] ); if ( bufferBytes <= bytesOut ) makeBuffer = false;
if ( bufferBytes <= bytesOut ) makeBuffer = false;
}
} }
if ( makeBuffer ) { if ( makeBuffer ) {
@@ -3122,18 +3207,8 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
} }
} }
stream_.sampleRate = sampleRate;
stream_.device[mode] = device;
stream_.state = STREAM_STOPPED;
asioCallbackInfo = &stream_.callbackInfo;
stream_.callbackInfo.object = (void *) this;
if ( stream_.mode == OUTPUT && mode == INPUT )
// We had already set up an output stream.
stream_.mode = DUPLEX;
else
stream_.mode = mode;
// Determine device latencies // Determine device latencies
long inputLatency, outputLatency;
result = ASIOGetLatencies( &inputLatency, &outputLatency ); result = ASIOGetLatencies( &inputLatency, &outputLatency );
if ( result != ASE_OK ) { if ( result != ASE_OK ) {
errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency."; errorStream_ << "RtApiAsio::probeDeviceOpen: driver (" << driverName << ") error (" << getAsioErrorString( result ) << ") getting latency.";
@@ -3153,32 +3228,38 @@ bool RtApiAsio :: probeDeviceOpen( unsigned int device, StreamMode mode, unsigne
return SUCCESS; return SUCCESS;
error: error:
if ( buffersAllocated ) if ( !isDuplexInput ) {
ASIODisposeBuffers(); // the cleanup for error in the duplex input, is done by RtApi::openStream
drivers.removeCurrentDriver(); // So we clean up for single channel only
if ( handle ) { if ( buffersAllocated )
CloseHandle( handle->condition ); ASIODisposeBuffers();
if ( handle->bufferInfos )
free( handle->bufferInfos );
delete handle;
stream_.apiHandle = 0;
}
for ( int i=0; i<2; i++ ) { drivers.removeCurrentDriver();
if ( stream_.userBuffer[i] ) {
free( stream_.userBuffer[i] ); if ( handle ) {
stream_.userBuffer[i] = 0; CloseHandle( handle->condition );
if ( handle->bufferInfos )
free( handle->bufferInfos );
delete handle;
stream_.apiHandle = 0;
}
if ( stream_.userBuffer[mode] ) {
free( stream_.userBuffer[mode] );
stream_.userBuffer[mode] = 0;
}
if ( stream_.deviceBuffer ) {
free( stream_.deviceBuffer );
stream_.deviceBuffer = 0;
} }
} }
if ( stream_.deviceBuffer ) {
free( stream_.deviceBuffer );
stream_.deviceBuffer = 0;
}
return FAILURE; return FAILURE;
} }////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void RtApiAsio :: closeStream() void RtApiAsio :: closeStream()
{ {
@@ -3630,12 +3711,12 @@ public:
outIndex_( 0 ) {} outIndex_( 0 ) {}
~WasapiBuffer() { ~WasapiBuffer() {
delete buffer_; free( buffer_ );
} }
// sets the length of the internal ring buffer // sets the length of the internal ring buffer
void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) { void setBufferSize( unsigned int bufferSize, unsigned int formatBytes ) {
delete buffer_; free( buffer_ );
buffer_ = ( char* ) calloc( bufferSize, formatBytes ); buffer_ = ( char* ) calloc( bufferSize, formatBytes );
@@ -3794,7 +3875,7 @@ void convertBufferWasapi( char* outBuffer,
float sampleStep = 1.0f / sampleRatio; float sampleStep = 1.0f / sampleRatio;
float inSampleFraction = 0.0f; float inSampleFraction = 0.0f;
outSampleCount = ( unsigned int ) ( inSampleCount * sampleRatio ); outSampleCount = ( unsigned int ) roundf( inSampleCount * sampleRatio );
// frame-by-frame, copy each relative input sample into it's corresponding output sample // frame-by-frame, copy each relative input sample into it's corresponding output sample
for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ ) for ( unsigned int outSample = 0; outSample < outSampleCount; outSample++ )
@@ -3940,7 +4021,6 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
RtAudio::DeviceInfo info; RtAudio::DeviceInfo info;
unsigned int captureDeviceCount = 0; unsigned int captureDeviceCount = 0;
unsigned int renderDeviceCount = 0; unsigned int renderDeviceCount = 0;
std::wstring deviceName;
std::string defaultDeviceName; std::string defaultDeviceName;
bool isCaptureDevice = false; bool isCaptureDevice = false;
@@ -4043,8 +4123,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
goto Exit; goto Exit;
} }
deviceName = defaultDeviceNameProp.pwszVal; defaultDeviceName = convertCharPointerToStdString(defaultDeviceNameProp.pwszVal);
defaultDeviceName = std::string( deviceName.begin(), deviceName.end() );
// name // name
hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore ); hr = devicePtr->OpenPropertyStore( STGM_READ, &devicePropStore );
@@ -4061,8 +4140,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
goto Exit; goto Exit;
} }
deviceName = deviceNameProp.pwszVal; info.name =convertCharPointerToStdString(deviceNameProp.pwszVal);
info.name = std::string( deviceName.begin(), deviceName.end() );
// is default // is default
if ( isCaptureDevice ) { if ( isCaptureDevice ) {
@@ -4105,6 +4183,7 @@ RtAudio::DeviceInfo RtApiWasapi::getDeviceInfo( unsigned int device )
for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) { for ( unsigned int i = 0; i < MAX_SAMPLE_RATES; i++ ) {
info.sampleRates.push_back( SAMPLE_RATES[i] ); info.sampleRates.push_back( SAMPLE_RATES[i] );
} }
info.preferredSampleRate = deviceFormat->nSamplesPerSec;
// native format // native format
info.nativeFormats = 0; info.nativeFormats = 0;
@@ -5079,10 +5158,10 @@ void RtApiWasapi::wasapiThread()
// if the callback buffer was pushed renderBuffer reset callbackPulled flag // if the callback buffer was pushed renderBuffer reset callbackPulled flag
if ( callbackPushed ) { if ( callbackPushed ) {
callbackPulled = false; callbackPulled = false;
// tick stream time
RtApi::tickStreamTime();
} }
// tick stream time
RtApi::tickStreamTime();
} }
Exit: Exit:
@@ -5240,14 +5319,11 @@ unsigned int RtApiDs :: getDeviceCount( void )
error( RtAudioError::WARNING ); error( RtAudioError::WARNING );
} }
// Clean out any devices that may have disappeared. // Clean out any devices that may have disappeared (code update submitted by Eli Zehngut).
std::vector< int > indices; for ( unsigned int i=0; i<dsDevices.size(); ) {
for ( unsigned int i=0; i<dsDevices.size(); i++ ) if ( dsDevices[i].found == false ) dsDevices.erase( dsDevices.begin() + i );
if ( dsDevices[i].found == false ) indices.push_back( i ); else i++;
//unsigned int nErased = 0; }
for ( unsigned int i=0; i<indices.size(); i++ )
dsDevices.erase( dsDevices.begin()+indices[i] );
//dsDevices.erase( dsDevices.begin()-nErased++ );
return static_cast<unsigned int>(dsDevices.size()); return static_cast<unsigned int>(dsDevices.size());
} }
@@ -5303,8 +5379,12 @@ RtAudio::DeviceInfo RtApiDs :: getDeviceInfo( unsigned int device )
info.sampleRates.clear(); info.sampleRates.clear();
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) { for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate && if ( SAMPLE_RATES[k] >= (unsigned int) outCaps.dwMinSecondarySampleRate &&
SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) SAMPLE_RATES[k] <= (unsigned int) outCaps.dwMaxSecondarySampleRate ) {
info.sampleRates.push_back( SAMPLE_RATES[k] ); info.sampleRates.push_back( SAMPLE_RATES[k] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[k];
}
} }
// Get format information. // Get format information.
@@ -6259,6 +6339,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6266,6 +6347,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6274,6 +6356,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6281,6 +6364,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6302,6 +6386,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6353,6 +6438,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current write position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6394,6 +6480,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking buffer during playback!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6407,6 +6494,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking buffer during playback!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6443,6 +6531,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6504,6 +6593,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") getting current read position!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6518,6 +6608,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") locking capture buffer!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6539,6 +6630,7 @@ void RtApiDs :: callbackEvent()
if ( FAILED( result ) ) { if ( FAILED( result ) ) {
errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!"; errorStream_ << "RtApiDs::callbackEvent: error (" << getErrorString( result ) << ") unlocking capture buffer!";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
MUTEX_UNLOCK( &stream_.mutex );
error( RtAudioError::SYSTEM_ERROR ); error( RtAudioError::SYSTEM_ERROR );
return; return;
} }
@@ -6577,21 +6669,6 @@ static unsigned __stdcall callbackHandler( void *ptr )
return 0; return 0;
} }
#include "tchar.h"
static std::string convertTChar( LPCTSTR name )
{
#if defined( UNICODE ) || defined( _UNICODE )
int length = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);
std::string s( length-1, '\0' );
WideCharToMultiByte(CP_UTF8, 0, name, -1, &s[0], length, NULL, NULL);
#else
std::string s( name );
#endif
return s;
}
static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid, static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
LPCTSTR description, LPCTSTR description,
LPCTSTR /*module*/, LPCTSTR /*module*/,
@@ -6633,7 +6710,7 @@ static BOOL CALLBACK deviceQueryCallback( LPGUID lpguid,
} }
// If good device, then save its name and guid. // If good device, then save its name and guid.
std::string name = convertTChar( description ); std::string name = convertCharPointerToStdString( description );
//if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" ) //if ( name == "Primary Sound Driver" || name == "Primary Sound Capture Driver" )
if ( lpguid == NULL ) if ( lpguid == NULL )
name = "Default Device"; name = "Default Device";
@@ -6815,6 +6892,7 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
// Count cards and devices // Count cards and devices
card = -1; card = -1;
subdevice = -1;
snd_card_next( &card ); snd_card_next( &card );
while ( card >= 0 ) { while ( card >= 0 ) {
sprintf( name, "hw:%d", card ); sprintf( name, "hw:%d", card );
@@ -7028,8 +7106,12 @@ RtAudio::DeviceInfo RtApiAlsa :: getDeviceInfo( unsigned int device )
// Test our discrete set of sample rate values. // Test our discrete set of sample rate values.
info.sampleRates.clear(); info.sampleRates.clear();
for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) { for ( unsigned int i=0; i<MAX_SAMPLE_RATES; i++ ) {
if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) if ( snd_pcm_hw_params_test_rate( phandle, params, SAMPLE_RATES[i], 0 ) == 0 ) {
info.sampleRates.push_back( SAMPLE_RATES[i] ); info.sampleRates.push_back( SAMPLE_RATES[i] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[i] <= 48000 && SAMPLE_RATES[i] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[i];
}
} }
if ( info.sampleRates.size() == 0 ) { if ( info.sampleRates.size() == 0 ) {
snd_pcm_close( phandle ); snd_pcm_close( phandle );
@@ -7954,6 +8036,8 @@ void RtApiAlsa :: callbackEvent()
errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << "."; errorStream_ << "RtApiAlsa::callbackEvent: error preparing device after underrun, " << snd_strerror( result ) << ".";
errorText_ = errorStream_.str(); errorText_ = errorStream_.str();
} }
else
errorText_ = "RtApiAlsa::callbackEvent: audio write error, underrun.";
} }
else { else {
errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << "."; errorStream_ << "RtApiAlsa::callbackEvent: error, current state is " << snd_pcm_state_name( state ) << ", " << snd_strerror( result ) << ".";
@@ -7987,7 +8071,7 @@ static void *alsaCallbackHandler( void *ptr )
bool *isRunning = &info->isRunning; bool *isRunning = &info->isRunning;
#ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread) #ifdef SCHED_RR // Undefined with some OSes (eg: NetBSD 1.6.x with GNU Pthread)
if ( &info->doRealtime ) { if ( info->doRealtime ) {
pthread_t tID = pthread_self(); // ID of this thread pthread_t tID = pthread_self(); // ID of this thread
sched_param prio = { info->priority }; // scheduling priority of thread sched_param prio = { info->priority }; // scheduling priority of thread
pthread_setschedparam( tID, SCHED_RR, &prio ); pthread_setschedparam( tID, SCHED_RR, &prio );
@@ -8062,6 +8146,7 @@ RtAudio::DeviceInfo RtApiPulse::getDeviceInfo( unsigned int /*device*/ )
for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr ) for ( const unsigned int *sr = SUPPORTED_SAMPLERATES; *sr; ++sr )
info.sampleRates.push_back( *sr ); info.sampleRates.push_back( *sr );
info.preferredSampleRate = 48000;
info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32; info.nativeFormats = RTAUDIO_SINT16 | RTAUDIO_SINT32 | RTAUDIO_FLOAT32;
return info; return info;
@@ -8424,7 +8509,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
pah = static_cast<PulseAudioHandle *>( stream_.apiHandle ); pah = static_cast<PulseAudioHandle *>( stream_.apiHandle );
int error; int error;
if ( !options->streamName.empty() ) streamName = options->streamName; if ( options && !options->streamName.empty() ) streamName = options->streamName;
switch ( mode ) { switch ( mode ) {
case INPUT: case INPUT:
pa_buffer_attr buffer_attr; pa_buffer_attr buffer_attr;
@@ -8438,7 +8523,7 @@ bool RtApiPulse::probeDeviceOpen( unsigned int device, StreamMode mode,
} }
break; break;
case OUTPUT: case OUTPUT:
pah->s_play = pa_simple_new( NULL, "RtAudio", PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error ); pah->s_play = pa_simple_new( NULL, streamName.c_str(), PA_STREAM_PLAYBACK, NULL, "Playback", &ss, NULL, NULL, &error );
if ( !pah->s_play ) { if ( !pah->s_play ) {
errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server."; errorText_ = "RtApiPulse::probeDeviceOpen: error connecting output to PulseAudio server.";
goto error; goto error;
@@ -8630,6 +8715,10 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) { for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
if ( ainfo.rates[i] == SAMPLE_RATES[k] ) { if ( ainfo.rates[i] == SAMPLE_RATES[k] ) {
info.sampleRates.push_back( SAMPLE_RATES[k] ); info.sampleRates.push_back( SAMPLE_RATES[k] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[k];
break; break;
} }
} }
@@ -8638,8 +8727,12 @@ RtAudio::DeviceInfo RtApiOss :: getDeviceInfo( unsigned int device )
else { else {
// Check min and max rate values; // Check min and max rate values;
for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) { for ( unsigned int k=0; k<MAX_SAMPLE_RATES; k++ ) {
if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) if ( ainfo.min_rate <= (int) SAMPLE_RATES[k] && ainfo.max_rate >= (int) SAMPLE_RATES[k] ) {
info.sampleRates.push_back( SAMPLE_RATES[k] ); info.sampleRates.push_back( SAMPLE_RATES[k] );
if ( !info.preferredSampleRate || ( SAMPLE_RATES[k] <= 48000 && SAMPLE_RATES[k] > info.preferredSampleRate ) )
info.preferredSampleRate = SAMPLE_RATES[k];
}
} }
} }
@@ -10050,8 +10143,8 @@ void RtApi :: convertBuffer( char *outBuffer, char *inBuffer, ConvertInfo &info
void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format ) void RtApi :: byteSwapBuffer( char *buffer, unsigned int samples, RtAudioFormat format )
{ {
register char val; char val;
register char *ptr; char *ptr;
ptr = buffer; ptr = buffer;
if ( format == RTAUDIO_SINT16 ) { if ( format == RTAUDIO_SINT16 ) {

View File

@@ -8,7 +8,7 @@
RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/ RtMidi WWW site: http://music.mcgill.ca/~gary/rtmidi/
RtMidi: realtime MIDI i/o C++ classes RtMidi: realtime MIDI i/o C++ classes
Copyright (c) 2003-2014 Gary P. Scavone Copyright (c) 2003-2016 Gary P. Scavone
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files obtaining a copy of this software and associated documentation files
@@ -39,6 +39,13 @@
#include "RtMidi.h" #include "RtMidi.h"
#include <sstream> #include <sstream>
#if defined(__MACOSX_CORE__)
#if TARGET_OS_IPHONE
#define AudioGetCurrentHostTime CAHostTimeBase::GetCurrentTime
#define AudioConvertHostTimeToNanos CAHostTimeBase::ConvertToNanos
#endif
#endif
//*********************************************************************// //*********************************************************************//
// RtMidi Definitions // RtMidi Definitions
//*********************************************************************// //*********************************************************************//
@@ -224,7 +231,7 @@ RtMidiOut :: ~RtMidiOut() throw()
//*********************************************************************// //*********************************************************************//
MidiApi :: MidiApi( void ) MidiApi :: MidiApi( void )
: apiData_( 0 ), connected_( false ), errorCallback_(0) : apiData_( 0 ), connected_( false ), errorCallback_(0), errorCallbackUserData_(0)
{ {
} }
@@ -232,24 +239,24 @@ MidiApi :: ~MidiApi( void )
{ {
} }
void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback ) void MidiApi :: setErrorCallback( RtMidiErrorCallback errorCallback, void *userData = 0 )
{ {
errorCallback_ = errorCallback; errorCallback_ = errorCallback;
errorCallbackUserData_ = userData;
} }
void MidiApi :: error( RtMidiError::Type type, std::string errorString ) void MidiApi :: error( RtMidiError::Type type, std::string errorString )
{ {
if ( errorCallback_ ) { if ( errorCallback_ ) {
static bool firstErrorOccured = false;
if ( firstErrorOccured ) if ( firstErrorOccurred_ )
return; return;
firstErrorOccured = true; firstErrorOccurred_ = true;
const std::string errorMessage = errorString; const std::string errorMessage = errorString;
errorCallback_( type, errorMessage ); errorCallback_( type, errorMessage, errorCallbackUserData_);
firstErrorOccured = false; firstErrorOccurred_ = false;
return; return;
} }
@@ -567,7 +574,8 @@ void MidiInCore :: initialize( const std::string& clientName )
{ {
// Set up our client. // Set up our client.
MIDIClientRef client; MIDIClientRef client;
OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
if ( result != noErr ) { if ( result != noErr ) {
errorString_ = "MidiInCore::initialize: error creating OS-X MIDI client object."; errorString_ = "MidiInCore::initialize: error creating OS-X MIDI client object.";
error( RtMidiError::DRIVER_ERROR, errorString_ ); error( RtMidiError::DRIVER_ERROR, errorString_ );
@@ -580,6 +588,7 @@ void MidiInCore :: initialize( const std::string& clientName )
data->endpoint = 0; data->endpoint = 0;
apiData_ = (void *) data; apiData_ = (void *) data;
inputData_.apiData = (void *) data; inputData_.apiData = (void *) data;
CFRelease(name);
} }
void MidiInCore :: openPort( unsigned int portNumber, const std::string portName ) void MidiInCore :: openPort( unsigned int portNumber, const std::string portName )
@@ -665,11 +674,17 @@ void MidiInCore :: openVirtualPort( const std::string portName )
void MidiInCore :: closePort( void ) void MidiInCore :: closePort( void )
{ {
if ( connected_ ) { CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
MIDIPortDispose( data->port ); if ( data->endpoint ) {
connected_ = false; MIDIEndpointDispose( data->endpoint );
} }
if ( data->port ) {
MIDIPortDispose( data->port );
}
connected_ = false;
} }
unsigned int MidiInCore :: getPortCount() unsigned int MidiInCore :: getPortCount()
@@ -798,6 +813,8 @@ static CFStringRef ConnectedEndpointName( MIDIEndpointRef endpoint )
if ( anyStrings ) if ( anyStrings )
return result; return result;
CFRelease( result );
// Here, either the endpoint had no connections, or we failed to obtain names // Here, either the endpoint had no connections, or we failed to obtain names
return EndpointName( endpoint, false ); return EndpointName( endpoint, false );
} }
@@ -852,7 +869,8 @@ void MidiOutCore :: initialize( const std::string& clientName )
{ {
// Set up our client. // Set up our client.
MIDIClientRef client; MIDIClientRef client;
OSStatus result = MIDIClientCreate( CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII ), NULL, NULL, &client ); CFStringRef name = CFStringCreateWithCString( NULL, clientName.c_str(), kCFStringEncodingASCII );
OSStatus result = MIDIClientCreate(name, NULL, NULL, &client );
if ( result != noErr ) { if ( result != noErr ) {
errorString_ = "MidiOutCore::initialize: error creating OS-X MIDI client object."; errorString_ = "MidiOutCore::initialize: error creating OS-X MIDI client object.";
error( RtMidiError::DRIVER_ERROR, errorString_ ); error( RtMidiError::DRIVER_ERROR, errorString_ );
@@ -864,6 +882,7 @@ void MidiOutCore :: initialize( const std::string& clientName )
data->client = client; data->client = client;
data->endpoint = 0; data->endpoint = 0;
apiData_ = (void *) data; apiData_ = (void *) data;
CFRelease( name );
} }
unsigned int MidiOutCore :: getPortCount() unsigned int MidiOutCore :: getPortCount()
@@ -950,11 +969,17 @@ void MidiOutCore :: openPort( unsigned int portNumber, const std::string portNam
void MidiOutCore :: closePort( void ) void MidiOutCore :: closePort( void )
{ {
if ( connected_ ) { CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
MIDIPortDispose( data->port ); if ( data->endpoint ) {
connected_ = false; MIDIEndpointDispose( data->endpoint );
} }
if ( data->port ) {
MIDIPortDispose( data->port );
}
connected_ = false;
} }
void MidiOutCore :: openVirtualPort( std::string portName ) void MidiOutCore :: openVirtualPort( std::string portName )
@@ -982,13 +1007,6 @@ void MidiOutCore :: openVirtualPort( std::string portName )
data->endpoint = endpoint; data->endpoint = endpoint;
} }
// Not necessary if we don't treat sysex messages any differently than
// normal messages ... see below.
//static void sysexCompletionProc( MIDISysexSendRequest *sreq )
//{
// free( sreq );
//}
void MidiOutCore :: sendMessage( std::vector<unsigned char> *message ) void MidiOutCore :: sendMessage( std::vector<unsigned char> *message )
{ {
// We use the MIDISendSysex() function to asynchronously send sysex // We use the MIDISendSysex() function to asynchronously send sysex
@@ -1000,58 +1018,29 @@ void MidiOutCore :: sendMessage( std::vector<unsigned char> *message )
return; return;
} }
// unsigned int packetBytes, bytesLeft = nBytes;
// unsigned int messageIndex = 0;
MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); MIDITimeStamp timeStamp = AudioGetCurrentHostTime();
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_); CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
OSStatus result; OSStatus result;
/* if ( message->at(0) != 0xF0 && nBytes > 3 ) {
// I don't think this code is necessary. We can send sysex
// messages through the normal mechanism. In addition, this avoids
// the problem of virtual ports not receiving sysex messages.
if ( message->at(0) == 0xF0 ) {
// Apple's fantastic API requires us to free the allocated data in
// the completion callback but trashes the pointer and size before
// we get a chance to free it!! This is a somewhat ugly hack
// submitted by ptarabbia that puts the sysex buffer data right at
// the end of the MIDISysexSendRequest structure. This solution
// does not require that we wait for a previous sysex buffer to be
// sent before sending a new one, which was the old way we did it.
MIDISysexSendRequest *newRequest = (MIDISysexSendRequest *) malloc(sizeof(struct MIDISysexSendRequest) + nBytes);
char * sysexBuffer = ((char *) newRequest) + sizeof(struct MIDISysexSendRequest);
// Copy data to buffer.
for ( unsigned int i=0; i<nBytes; ++i ) sysexBuffer[i] = message->at(i);
newRequest->destination = data->destinationId;
newRequest->data = (Byte *)sysexBuffer;
newRequest->bytesToSend = nBytes;
newRequest->complete = 0;
newRequest->completionProc = sysexCompletionProc;
newRequest->completionRefCon = newRequest;
result = MIDISendSysex(newRequest);
if ( result != noErr ) {
free( newRequest );
errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
error( RtMidiError::WARNING, errorString_ );
return;
}
return;
}
else if ( nBytes > 3 ) {
errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?"; errorString_ = "MidiOutCore::sendMessage: message format problem ... not sysex but > 3 bytes?";
error( RtMidiError::WARNING, errorString_ ); error( RtMidiError::WARNING, errorString_ );
return; return;
} }
*/
MIDIPacketList packetList; Byte buffer[nBytes+(sizeof(MIDIPacketList))];
MIDIPacket *packet = MIDIPacketListInit( &packetList ); ByteCount listSize = sizeof(buffer);
packet = MIDIPacketListAdd( &packetList, sizeof(packetList), packet, timeStamp, nBytes, (const Byte *) &message->at( 0 ) ); MIDIPacketList *packetList = (MIDIPacketList*)buffer;
MIDIPacket *packet = MIDIPacketListInit( packetList );
ByteCount remainingBytes = nBytes;
while (remainingBytes && packet) {
ByteCount bytesForPacket = remainingBytes > 65535 ? 65535 : remainingBytes; // 65535 = maximum size of a MIDIPacket
const Byte* dataStartPtr = (const Byte *) &message->at( nBytes - remainingBytes );
packet = MIDIPacketListAdd( packetList, listSize, packet, timeStamp, bytesForPacket, dataStartPtr);
remainingBytes -= bytesForPacket;
}
if ( !packet ) { if ( !packet ) {
errorString_ = "MidiOutCore::sendMessage: could not allocate packet list"; errorString_ = "MidiOutCore::sendMessage: could not allocate packet list";
error( RtMidiError::DRIVER_ERROR, errorString_ ); error( RtMidiError::DRIVER_ERROR, errorString_ );
@@ -1060,7 +1049,7 @@ void MidiOutCore :: sendMessage( std::vector<unsigned char> *message )
// Send to any destinations that may have connected to us. // Send to any destinations that may have connected to us.
if ( data->endpoint ) { if ( data->endpoint ) {
result = MIDIReceived( data->endpoint, &packetList ); result = MIDIReceived( data->endpoint, packetList );
if ( result != noErr ) { if ( result != noErr ) {
errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations."; errorString_ = "MidiOutCore::sendMessage: error sending MIDI to virtual destinations.";
error( RtMidiError::WARNING, errorString_ ); error( RtMidiError::WARNING, errorString_ );
@@ -1069,7 +1058,7 @@ void MidiOutCore :: sendMessage( std::vector<unsigned char> *message )
// And send to an explicit destination port if we're connected. // And send to an explicit destination port if we're connected.
if ( connected_ ) { if ( connected_ ) {
result = MIDISend( data->port, data->destinationId, &packetList ); result = MIDISend( data->port, data->destinationId, packetList );
if ( result != noErr ) { if ( result != noErr ) {
errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port."; errorString_ = "MidiOutCore::sendMessage: error sending MIDI message to port.";
error( RtMidiError::WARNING, errorString_ ); error( RtMidiError::WARNING, errorString_ );
@@ -1415,7 +1404,8 @@ unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int
snd_seq_port_info_set_port( pinfo, -1 ); snd_seq_port_info_set_port( pinfo, -1 );
while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) {
unsigned int atyp = snd_seq_port_info_get_type( pinfo ); unsigned int atyp = snd_seq_port_info_get_type( pinfo );
if ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) continue; if ( ( ( atyp & SND_SEQ_PORT_TYPE_MIDI_GENERIC ) == 0 ) &&
( ( atyp & SND_SEQ_PORT_TYPE_SYNTH ) == 0 ) ) continue;
unsigned int caps = snd_seq_port_info_get_capability( pinfo ); unsigned int caps = snd_seq_port_info_get_capability( pinfo );
if ( ( caps & type ) != type ) continue; if ( ( caps & type ) != type ) continue;
if ( count == portNumber ) return 1; if ( count == portNumber ) return 1;
@@ -1494,6 +1484,7 @@ void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName
snd_seq_addr_t sender, receiver; snd_seq_addr_t sender, receiver;
sender.client = snd_seq_port_info_get_client( src_pinfo ); sender.client = snd_seq_port_info_get_client( src_pinfo );
sender.port = snd_seq_port_info_get_port( src_pinfo ); sender.port = snd_seq_port_info_get_port( src_pinfo );
receiver.client = snd_seq_client_id( data->seq );
snd_seq_port_info_t *pinfo; snd_seq_port_info_t *pinfo;
snd_seq_port_info_alloca( &pinfo ); snd_seq_port_info_alloca( &pinfo );
@@ -1523,7 +1514,6 @@ void MidiInAlsa :: openPort( unsigned int portNumber, const std::string portName
data->vport = snd_seq_port_info_get_port(pinfo); data->vport = snd_seq_port_info_get_port(pinfo);
} }
receiver.client = snd_seq_port_info_get_client( pinfo );
receiver.port = data->vport; receiver.port = data->vport;
if ( !data->subscription ) { if ( !data->subscription ) {
@@ -2285,6 +2275,14 @@ std::string MidiOutWinMM :: getPortName( unsigned int portNumber )
stringName = std::string( deviceCaps.szPname ); stringName = std::string( deviceCaps.szPname );
#endif #endif
// Next lines added to add the portNumber to the name so that
// the device's names are sure to be listed with individual names
// even when they have the same brand name
std::ostringstream os;
os << " ";
os << portNumber;
stringName += os.str();
return stringName; return stringName;
} }
@@ -2699,6 +2697,10 @@ void MidiOutJack :: connect()
JackMidiData *data = static_cast<JackMidiData *> (apiData_); JackMidiData *data = static_cast<JackMidiData *> (apiData_);
if ( data->client ) if ( data->client )
return; return;
// Initialize output ringbuffers
data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
// Initialize JACK client // Initialize JACK client
if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) { if (( data->client = jack_client_open( clientName.c_str(), JackNoStartServer, NULL )) == 0) {
@@ -2708,8 +2710,6 @@ void MidiOutJack :: connect()
} }
jack_set_process_callback( data->client, jackProcessOut, data ); jack_set_process_callback( data->client, jackProcessOut, data );
data->buffSize = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
data->buffMessage = jack_ringbuffer_create( JACK_RINGBUFFER_SIZE );
jack_activate( data->client ); jack_activate( data->client );
} }
@@ -2717,12 +2717,12 @@ MidiOutJack :: ~MidiOutJack()
{ {
JackMidiData *data = static_cast<JackMidiData *> (apiData_); JackMidiData *data = static_cast<JackMidiData *> (apiData_);
closePort(); closePort();
// Cleanup
jack_ringbuffer_free( data->buffSize );
jack_ringbuffer_free( data->buffMessage );
if ( data->client ) { if ( data->client ) {
// Cleanup
jack_client_close( data->client ); jack_client_close( data->client );
jack_ringbuffer_free( data->buffSize );
jack_ringbuffer_free( data->buffMessage );
} }
delete data; delete data;