mirror of
https://github.com/thestk/stk
synced 2026-02-07 09:46:16 +00:00
Updated RtMidi and RtAudio files after new releases.
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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); }
|
||||||
|
|
||||||
// **************************************************************** //
|
// **************************************************************** //
|
||||||
//
|
//
|
||||||
|
|||||||
381
src/RtAudio.cpp
381
src/RtAudio.cpp
@@ -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( ¤tRate );
|
result = ASIOGetSampleRate( ¤tRate );
|
||||||
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 ) {
|
||||||
|
|||||||
156
src/RtMidi.cpp
156
src/RtMidi.cpp
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user