[DEV] rework continue (better integration of pulseaudio and low level devices

This commit is contained in:
Edouard DUPIN 2015-06-14 18:32:08 +02:00
parent 4b5bbd9626
commit 7b0316a8aa
12 changed files with 666 additions and 656 deletions

View File

@ -19,26 +19,22 @@ void audio::orchestra::DeviceInfo::display(int32_t _tabNumber) const {
for (int32_t iii=0; iii<_tabNumber; ++iii) { for (int32_t iii=0; iii<_tabNumber; ++iii) {
space += " "; space += " ";
} }
ATA_PRINT(space + "probe=" << probed); ATA_PRINT(space + "mode=" << (input==true?"input":"output"));
ATA_PRINT(space + "name=" << name); ATA_PRINT(space + "name=" << name);
ATA_PRINT(space + "outputChannels=" << outputChannels); ATA_PRINT(space + "desc=" << desc);
ATA_PRINT(space + "inputChannels=" << inputChannels); ATA_PRINT(space + "channel" << (channels.size()>1?"s":"") << "=" << channels.size() << " : " << channels);
ATA_PRINT(space + "duplexChannels=" << duplexChannels); ATA_PRINT(space + "rate" << (sampleRates.size()>1?"s":"") << "=" << sampleRates);
ATA_PRINT(space + "isDefaultOutput=" << (isDefaultOutput==true?"true":"false")); ATA_PRINT(space + "native Format" << (nativeFormats.size()>1?"s":"") << ": " << nativeFormats);
ATA_PRINT(space + "isDefaultInput=" << (isDefaultInput==true?"true":"false")); ATA_PRINT(space + "default=" << (isDefault==true?"true":"false"));
ATA_PRINT(space + "rates=" << sampleRates);
ATA_PRINT(space + "native Format: " << nativeFormats);
} }
std::ostream& audio::orchestra::operator <<(std::ostream& _os, const audio::orchestra::DeviceInfo& _obj) { std::ostream& audio::orchestra::operator <<(std::ostream& _os, const audio::orchestra::DeviceInfo& _obj) {
_os << "{"; _os << "{";
_os << "probe=" << _obj.probed << ", ";
_os << "name=" << _obj.name << ", "; _os << "name=" << _obj.name << ", ";
_os << "outputChannels=" << _obj.outputChannels << ", "; _os << "description=" << _obj.desc << ", ";
_os << "inputChannels=" << _obj.inputChannels << ", "; _os << "channels=" << _obj.channels << ", ";
_os << "duplexChannels=" << _obj.duplexChannels << ", "; _os << "default=" << _obj.isDefault << ", ";
_os << "isDefaultOutput=" << _obj.isDefaultOutput << ", ";
_os << "isDefaultInput=" << _obj.isDefaultInput << ", ";
_os << "rates=" << _obj.sampleRates << ", "; _os << "rates=" << _obj.sampleRates << ", ";
_os << "native Format: " << _obj.nativeFormats; _os << "native Format: " << _obj.nativeFormats;
_os << "}"; _os << "}";

View File

@ -9,6 +9,7 @@
#define __AUDIO_ORCHESTRA_DEVICE_INFO_H__ #define __AUDIO_ORCHESTRA_DEVICE_INFO_H__
#include <audio/format.h> #include <audio/format.h>
#include <audio/channel.h>
namespace audio { namespace audio {
@ -18,24 +19,22 @@ namespace audio {
*/ */
class DeviceInfo { class DeviceInfo {
public: public:
bool probed; //!< true if the device capabilities were successfully probed. bool input; //!< true if the device in an input; false: output.
std::string name; //!< Character string device identifier. std::string name; //!< Character string device identifier.
uint32_t outputChannels; //!< Maximum output channels supported by device. std::string desc; //!< description of the device
uint32_t inputChannels; //!< Maximum input channels supported by device. std::vector<audio::channel> channels; //!< Channels interfaces.
uint32_t duplexChannels; //!< Maximum simultaneous input/output channels supported by device.
bool isDefaultOutput; //!< true if this is the default output device.
bool isDefaultInput; //!< true if this is the default input device.
std::vector<uint32_t> sampleRates; //!< Supported sample rates (queried from list of standard rates). std::vector<uint32_t> sampleRates; //!< Supported sample rates (queried from list of standard rates).
std::vector<audio::format> nativeFormats; //!< Bit mask of supported data formats. std::vector<audio::format> nativeFormats; //!< Bit mask of supported data formats.
bool isDefault; //! is default input/output
// Default constructor. // Default constructor.
DeviceInfo() : DeviceInfo() :
probed(false), input(false),
outputChannels(0), name(),
inputChannels(0), desc(),
duplexChannels(0), channels(),
isDefaultOutput(false), sampleRates(),
isDefaultInput(false), nativeFormats(),
nativeFormats() {} isDefault(false) {}
void display(int32_t _tabNumber = 1) const; void display(int32_t _tabNumber = 1) const;
}; };
std::ostream& operator <<(std::ostream& _os, const audio::orchestra::DeviceInfo& _obj); std::ostream& operator <<(std::ostream& _os, const audio::orchestra::DeviceInfo& _obj);

View File

@ -26,14 +26,14 @@ std::vector<std::string> audio::orchestra::Interface::getListApi() {
void audio::orchestra::Interface::openApi(const std::string& _api) { void audio::orchestra::Interface::openApi(const std::string& _api) {
delete m_rtapi; delete m_api;
m_rtapi = nullptr; m_api = nullptr;
for (size_t iii=0; iii<m_apiAvaillable.size(); ++iii) { for (size_t iii=0; iii<m_apiAvaillable.size(); ++iii) {
ATA_INFO("try open " << m_apiAvaillable[iii].first); ATA_INFO("try open " << m_apiAvaillable[iii].first);
if (_api == m_apiAvaillable[iii].first) { if (_api == m_apiAvaillable[iii].first) {
ATA_INFO(" ==> call it"); ATA_INFO(" ==> call it");
m_rtapi = m_apiAvaillable[iii].second(); m_api = m_apiAvaillable[iii].second();
if (m_rtapi != nullptr) { if (m_api != nullptr) {
return; return;
} }
} }
@ -44,7 +44,7 @@ void audio::orchestra::Interface::openApi(const std::string& _api) {
audio::orchestra::Interface::Interface() : audio::orchestra::Interface::Interface() :
m_rtapi(nullptr) { m_api(nullptr) {
ATA_DEBUG("Add interface:"); ATA_DEBUG("Add interface:");
#if defined(ORCHESTRA_BUILD_JACK) #if defined(ORCHESTRA_BUILD_JACK)
addInterface(audio::orchestra::type_jack, audio::orchestra::api::Jack::create); addInterface(audio::orchestra::type_jack, audio::orchestra::api::Jack::create);
@ -84,18 +84,18 @@ void audio::orchestra::Interface::addInterface(const std::string& _api, Api* (*_
enum audio::orchestra::error audio::orchestra::Interface::clear() { enum audio::orchestra::error audio::orchestra::Interface::clear() {
ATA_INFO("Clear API ..."); ATA_INFO("Clear API ...");
if (m_rtapi == nullptr) { if (m_api == nullptr) {
ATA_WARNING("Interface NOT started!"); ATA_WARNING("Interface NOT started!");
return audio::orchestra::error_none; return audio::orchestra::error_none;
} }
delete m_rtapi; delete m_api;
m_rtapi = nullptr; m_api = nullptr;
return audio::orchestra::error_none; return audio::orchestra::error_none;
} }
enum audio::orchestra::error audio::orchestra::Interface::instanciate(const std::string& _api) { enum audio::orchestra::error audio::orchestra::Interface::instanciate(const std::string& _api) {
ATA_INFO("Instanciate API ..."); ATA_INFO("Instanciate API ...");
if (m_rtapi != nullptr) { if (m_api != nullptr) {
ATA_WARNING("Interface already started!"); ATA_WARNING("Interface already started!");
return audio::orchestra::error_none; return audio::orchestra::error_none;
} }
@ -103,15 +103,15 @@ enum audio::orchestra::error audio::orchestra::Interface::instanciate(const std:
ATA_INFO("API specified : " << _api); ATA_INFO("API specified : " << _api);
// Attempt to open the specified API. // Attempt to open the specified API.
openApi(_api); openApi(_api);
if (m_rtapi != nullptr) { if (m_api != nullptr) {
if (m_rtapi->getDeviceCount() != 0) { if (m_api->getDeviceCount() != 0) {
ATA_INFO(" ==> api open"); ATA_INFO(" ==> api open");
} }
return audio::orchestra::error_none; return audio::orchestra::error_none;
} }
// No compiled support for specified API value. Issue a debug // No compiled support for specified API value. Issue a debug
// warning and continue as if no API was specified. // warning and continue as if no API was specified.
ATA_ERROR("RtAudio: no compiled support for specified API argument!"); ATA_ERROR("API NOT Supported '" << _api << "' not in " << getListApi());
return audio::orchestra::error_fail; return audio::orchestra::error_fail;
} }
ATA_INFO("Auto choice API :"); ATA_INFO("Auto choice API :");
@ -122,26 +122,26 @@ enum audio::orchestra::error audio::orchestra::Interface::instanciate(const std:
for (size_t iii=0; iii<apis.size(); ++iii) { for (size_t iii=0; iii<apis.size(); ++iii) {
ATA_INFO("try open ..."); ATA_INFO("try open ...");
openApi(apis[iii]); openApi(apis[iii]);
if(m_rtapi == nullptr) { if(m_api == nullptr) {
ATA_ERROR(" ==> can not create ..."); ATA_ERROR(" ==> can not create ...");
continue; continue;
} }
if (m_rtapi->getDeviceCount() != 0) { if (m_api->getDeviceCount() != 0) {
ATA_INFO(" ==> api open"); ATA_INFO(" ==> api open");
break; break;
} }
} }
if (m_rtapi != nullptr) { if (m_api != nullptr) {
return audio::orchestra::error_none; return audio::orchestra::error_none;
} }
ATA_ERROR("RtAudio: no compiled API support found ... critical error!!"); ATA_ERROR("API NOT Supported '" << _api << "' not in " << getListApi());
return audio::orchestra::error_fail; return audio::orchestra::error_fail;
} }
audio::orchestra::Interface::~Interface() { audio::orchestra::Interface::~Interface() {
ATA_INFO("Remove interface"); ATA_INFO("Remove interface");
delete m_rtapi; delete m_api;
m_rtapi = nullptr; m_api = nullptr;
} }
enum audio::orchestra::error audio::orchestra::Interface::openStream(audio::orchestra::StreamParameters* _outputParameters, enum audio::orchestra::error audio::orchestra::Interface::openStream(audio::orchestra::StreamParameters* _outputParameters,
@ -151,10 +151,10 @@ enum audio::orchestra::error audio::orchestra::Interface::openStream(audio::orch
uint32_t* _bufferFrames, uint32_t* _bufferFrames,
audio::orchestra::AirTAudioCallback _callback, audio::orchestra::AirTAudioCallback _callback,
const audio::orchestra::StreamOptions& _options) { const audio::orchestra::StreamOptions& _options) {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return audio::orchestra::error_inputNull; return audio::orchestra::error_inputNull;
} }
return m_rtapi->openStream(_outputParameters, return m_api->openStream(_outputParameters,
_inputParameters, _inputParameters,
_format, _format,
_sampleRate, _sampleRate,
@ -164,22 +164,22 @@ enum audio::orchestra::error audio::orchestra::Interface::openStream(audio::orch
} }
bool audio::orchestra::Interface::isMasterOf(audio::orchestra::Interface& _interface) { bool audio::orchestra::Interface::isMasterOf(audio::orchestra::Interface& _interface) {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
ATA_ERROR("Current Master API is nullptr ..."); ATA_ERROR("Current Master API is nullptr ...");
return false; return false;
} }
if (_interface.m_rtapi == nullptr) { if (_interface.m_api == nullptr) {
ATA_ERROR("Current Slave API is nullptr ..."); ATA_ERROR("Current Slave API is nullptr ...");
return false; return false;
} }
if (m_rtapi->getCurrentApi() != _interface.m_rtapi->getCurrentApi()) { if (m_api->getCurrentApi() != _interface.m_api->getCurrentApi()) {
ATA_ERROR("Can not link 2 Interface with not the same Low level type (?)");//" << _interface.m_adac->getCurrentApi() << " != " << m_adac->getCurrentApi() << ")"); ATA_ERROR("Can not link 2 Interface with not the same Low level type (?)");//" << _interface.m_adac->getCurrentApi() << " != " << m_adac->getCurrentApi() << ")");
return false; return false;
} }
if (m_rtapi->getCurrentApi() != audio::orchestra::type_alsa) { if (m_api->getCurrentApi() != audio::orchestra::type_alsa) {
ATA_ERROR("Link 2 device together work only if the interafec is ?");// << audio::orchestra::type_alsa << " not for " << m_rtapi->getCurrentApi()); ATA_ERROR("Link 2 device together work only if the interafec is ?");// << audio::orchestra::type_alsa << " not for " << m_api->getCurrentApi());
return false; return false;
} }
return m_rtapi->isMasterOf(_interface.m_rtapi); return m_api->isMasterOf(_interface.m_api);
} }

View File

@ -40,14 +40,14 @@ namespace audio {
protected: protected:
std::vector<std::pair<std::string, Api* (*)()> > m_apiAvaillable; std::vector<std::pair<std::string, Api* (*)()> > m_apiAvaillable;
protected: protected:
audio::orchestra::Api *m_rtapi; audio::orchestra::Api *m_api;
public: public:
void setName(const std::string& _name) { void setName(const std::string& _name) {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return; return;
} }
m_rtapi->setName(_name); m_api->setName(_name);
} }
/** /**
* @brief Get the list of all availlable API in the system. * @brief Get the list of all availlable API in the system.
@ -84,10 +84,10 @@ namespace audio {
* @return the audio API specifier for the current instance of airtaudio. * @return the audio API specifier for the current instance of airtaudio.
*/ */
const std::string& getCurrentApi() { const std::string& getCurrentApi() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return audio::orchestra::type_undefined; return audio::orchestra::type_undefined;
} }
return m_rtapi->getCurrentApi(); return m_api->getCurrentApi();
} }
/** /**
* @brief A public function that queries for the number of audio devices available. * @brief A public function that queries for the number of audio devices available.
@ -97,10 +97,10 @@ namespace audio {
* a system error occurs during processing, a warning will be issued. * a system error occurs during processing, a warning will be issued.
*/ */
uint32_t getDeviceCount() { uint32_t getDeviceCount() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return 0; return 0;
} }
return m_rtapi->getDeviceCount(); return m_api->getDeviceCount();
} }
/** /**
* @brief Any device integer between 0 and getDeviceCount() - 1 is valid. * @brief Any device integer between 0 and getDeviceCount() - 1 is valid.
@ -114,17 +114,17 @@ namespace audio {
* @return An audio::orchestra::DeviceInfo structure for a specified device number. * @return An audio::orchestra::DeviceInfo structure for a specified device number.
*/ */
audio::orchestra::DeviceInfo getDeviceInfo(uint32_t _device) { audio::orchestra::DeviceInfo getDeviceInfo(uint32_t _device) {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return audio::orchestra::DeviceInfo(); return audio::orchestra::DeviceInfo();
} }
return m_rtapi->getDeviceInfo(_device); return m_api->getDeviceInfo(_device);
} }
audio::orchestra::DeviceInfo getDeviceInfo(const std::string& _deviceName) { audio::orchestra::DeviceInfo getDeviceInfo(const std::string& _deviceName) {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return audio::orchestra::DeviceInfo(); return audio::orchestra::DeviceInfo();
} }
audio::orchestra::DeviceInfo info; audio::orchestra::DeviceInfo info;
m_rtapi->getNamedDeviceInfo(_deviceName, info); m_api->getNamedDeviceInfo(_deviceName, info);
return info; return info;
} }
/** /**
@ -137,10 +137,10 @@ namespace audio {
* before attempting to open a stream. * before attempting to open a stream.
*/ */
uint32_t getDefaultOutputDevice() { uint32_t getDefaultOutputDevice() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return 0; return 0;
} }
return m_rtapi->getDefaultOutputDevice(); return m_api->getDefaultOutputDevice();
} }
/** /**
* @brief A function that returns the index of the default input device. * @brief A function that returns the index of the default input device.
@ -152,10 +152,10 @@ namespace audio {
* before attempting to open a stream. * before attempting to open a stream.
*/ */
uint32_t getDefaultInputDevice() { uint32_t getDefaultInputDevice() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return 0; return 0;
} }
return m_rtapi->getDefaultInputDevice(); return m_api->getDefaultInputDevice();
} }
/** /**
* @brief A public function for opening a stream with the specified parameters. * @brief A public function for opening a stream with the specified parameters.
@ -210,10 +210,10 @@ namespace audio {
* returns (no exception is thrown). * returns (no exception is thrown).
*/ */
enum audio::orchestra::error closeStream() { enum audio::orchestra::error closeStream() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return audio::orchestra::error_inputNull; return audio::orchestra::error_inputNull;
} }
return m_rtapi->closeStream(); return m_api->closeStream();
} }
/** /**
* @brief A function that starts a stream. * @brief A function that starts a stream.
@ -224,10 +224,10 @@ namespace audio {
* running. * running.
*/ */
enum audio::orchestra::error startStream() { enum audio::orchestra::error startStream() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return audio::orchestra::error_inputNull; return audio::orchestra::error_inputNull;
} }
return m_rtapi->startStream(); return m_api->startStream();
} }
/** /**
* @brief Stop a stream, allowing any samples remaining in the output queue to be played. * @brief Stop a stream, allowing any samples remaining in the output queue to be played.
@ -238,10 +238,10 @@ namespace audio {
* stopped. * stopped.
*/ */
enum audio::orchestra::error stopStream() { enum audio::orchestra::error stopStream() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return audio::orchestra::error_inputNull; return audio::orchestra::error_inputNull;
} }
return m_rtapi->stopStream(); return m_api->stopStream();
} }
/** /**
* @brief Stop a stream, discarding any samples remaining in the input/output queue. * @brief Stop a stream, discarding any samples remaining in the input/output queue.
@ -251,38 +251,38 @@ namespace audio {
* stopped. * stopped.
*/ */
enum audio::orchestra::error abortStream() { enum audio::orchestra::error abortStream() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return audio::orchestra::error_inputNull; return audio::orchestra::error_inputNull;
} }
return m_rtapi->abortStream(); return m_api->abortStream();
} }
/** /**
* @return true if a stream is open and false if not. * @return true if a stream is open and false if not.
*/ */
bool isStreamOpen() const { bool isStreamOpen() const {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return false; return false;
} }
return m_rtapi->isStreamOpen(); return m_api->isStreamOpen();
} }
/** /**
* @return true if the stream is running and false if it is stopped or not open. * @return true if the stream is running and false if it is stopped or not open.
*/ */
bool isStreamRunning() const { bool isStreamRunning() const {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return false; return false;
} }
return m_rtapi->isStreamRunning(); return m_api->isStreamRunning();
} }
/** /**
* @brief If a stream is not open, an RtError (type = INVALID_USE) will be thrown. * @brief If a stream is not open, an RtError (type = INVALID_USE) will be thrown.
* @return the number of elapsed seconds since the stream was started. * @return the number of elapsed seconds since the stream was started.
*/ */
audio::Time getStreamTime() { audio::Time getStreamTime() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return audio::Time(); return audio::Time();
} }
return m_rtapi->getStreamTime(); return m_api->getStreamTime();
} }
/** /**
* @brief The stream latency refers to delay in audio input and/or output * @brief The stream latency refers to delay in audio input and/or output
@ -294,10 +294,10 @@ namespace audio {
* @return The internal stream latency in sample frames. * @return The internal stream latency in sample frames.
*/ */
long getStreamLatency() { long getStreamLatency() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return 0; return 0;
} }
return m_rtapi->getStreamLatency(); return m_api->getStreamLatency();
} }
/** /**
* @brief On some systems, the sample rate used may be slightly different * @brief On some systems, the sample rate used may be slightly different
@ -306,10 +306,10 @@ namespace audio {
* @return Returns actual sample rate in use by the stream. * @return Returns actual sample rate in use by the stream.
*/ */
uint32_t getStreamSampleRate() { uint32_t getStreamSampleRate() {
if (m_rtapi == nullptr) { if (m_api == nullptr) {
return 0; return 0;
} }
return m_rtapi->getStreamSampleRate(); return m_api->getStreamSampleRate();
} }
bool isMasterOf(audio::orchestra::Interface& _interface); bool isMasterOf(audio::orchestra::Interface& _interface);
protected: protected:

View File

@ -104,7 +104,8 @@ uint32_t audio::orchestra::api::Alsa::getDeviceCount() {
if (subdevice < 0) { if (subdevice < 0) {
break; break;
} }
nDevices++; // input/output
nDevices+=2;
} }
nextcard: nextcard:
snd_ctl_close(handle); snd_ctl_close(handle);
@ -113,7 +114,7 @@ nextcard:
return nDevices; return nDevices;
} }
bool audio::orchestra::api::Alsa::getNamedDeviceInfoLocal(const std::string& _deviceName, audio::orchestra::DeviceInfo& _info, int32_t _cardId, int32_t _subdevice, int32_t _localDeviceId) { bool audio::orchestra::api::Alsa::getNamedDeviceInfoLocal(const std::string& _deviceName, audio::orchestra::DeviceInfo& _info, int32_t _cardId, int32_t _subdevice, int32_t _localDeviceId, bool _input) {
int32_t result; int32_t result;
snd_ctl_t *chandle; snd_ctl_t *chandle;
int32_t openMode = SND_PCM_ASYNC; int32_t openMode = SND_PCM_ASYNC;
@ -124,7 +125,12 @@ bool audio::orchestra::api::Alsa::getNamedDeviceInfoLocal(const std::string& _de
snd_pcm_hw_params_t *params; snd_pcm_hw_params_t *params;
snd_pcm_hw_params_alloca(&params); snd_pcm_hw_params_alloca(&params);
// First try for playback unless default _device (which has subdev -1) // First try for playback unless default _device (which has subdev -1)
stream = SND_PCM_STREAM_PLAYBACK; _info.input = _input;
if (_input == true) {
stream = SND_PCM_STREAM_CAPTURE;
} else {
stream = SND_PCM_STREAM_PLAYBACK;
}
snd_pcm_info_set_stream(pcminfo, stream); snd_pcm_info_set_stream(pcminfo, stream);
std::vector<std::string> listElement = etk::split(_deviceName, ','); std::vector<std::string> listElement = etk::split(_deviceName, ',');
if (listElement.size() == 0) { if (listElement.size() == 0) {
@ -146,39 +152,14 @@ bool audio::orchestra::api::Alsa::getNamedDeviceInfoLocal(const std::string& _de
snd_pcm_info_set_subdevice(pcminfo, 0); snd_pcm_info_set_subdevice(pcminfo, 0);
result = snd_ctl_pcm_info(chandle, pcminfo); result = snd_ctl_pcm_info(chandle, pcminfo);
if (result < 0) { if (result < 0) {
// Device probably doesn't support playback. // Device probably doesn't support IO.
goto captureProbe; return false;
} }
} }
result = snd_pcm_open(&phandle, _deviceName.c_str(), stream, openMode | SND_PCM_NONBLOCK); // ALSA doesn't provide default devices so we'll use the first available one.
if (result < 0) { if (_localDeviceId == 0) {
ATA_ERROR("snd_pcm_open error for device (" << _deviceName << "), " << snd_strerror(result) << ". (seems to be already open)"); _info.isDefault = true;
// TODO : Return audio::orchestra::error_warning;
goto captureProbe;
} }
// The device is open ... fill the parameter structure.
result = snd_pcm_hw_params_any(phandle, params);
if (result < 0) {
snd_pcm_close(phandle);
ATA_ERROR("snd_pcm_hw_params error for device (" << _deviceName << "), " << snd_strerror(result) << ".");
// TODO : Return audio::orchestra::error_warning;
goto captureProbe;
}
// Get output channel information.
uint32_t value;
result = snd_pcm_hw_params_get_channels_max(params, &value);
if (result < 0) {
snd_pcm_close(phandle);
ATA_ERROR("error getting device (" << _deviceName << ") output channels, " << snd_strerror(result) << ".");
// TODO : Return audio::orchestra::error_warning;
goto captureProbe;
}
ATA_ERROR("Output channel = " << value);
_info.outputChannels = value;
snd_pcm_close(phandle);
captureProbe:
stream = SND_PCM_STREAM_CAPTURE;
snd_pcm_info_set_stream(pcminfo, stream); snd_pcm_info_set_stream(pcminfo, stream);
// Now try for capture unless default device (with subdev = -1) // Now try for capture unless default device (with subdev = -1)
if (_subdevice != -1) { if (_subdevice != -1) {
@ -186,82 +167,32 @@ captureProbe:
snd_ctl_close(chandle); snd_ctl_close(chandle);
if (result < 0) { if (result < 0) {
// Device probably doesn't support capture. // Device probably doesn't support capture.
if (_info.outputChannels == 0) { return false;
return true;
}
goto probeParameters;
} }
} }
// open device:
result = snd_pcm_open(&phandle, _deviceName.c_str(), stream, openMode | SND_PCM_NONBLOCK); result = snd_pcm_open(&phandle, _deviceName.c_str(), stream, openMode | SND_PCM_NONBLOCK);
if (result < 0) { if (result < 0) {
ATA_ERROR("snd_pcm_open error for device (" << _deviceName << "), " << snd_strerror(result) << ". (seems to be already open)"); ATA_ERROR("snd_pcm_open error for device (" << _deviceName << "), " << snd_strerror(result) << ". (seems to be already open)");
// TODO : Return audio::orchestra::error_warning; return true;
if (_info.outputChannels == 0) {
return true;
}
goto probeParameters;
} }
// The device is open ... fill the parameter structure. // The device is open ... fill the parameter structure.
result = snd_pcm_hw_params_any(phandle, params); result = snd_pcm_hw_params_any(phandle, params);
if (result < 0) { if (result < 0) {
snd_pcm_close(phandle); snd_pcm_close(phandle);
ATA_ERROR("snd_pcm_hw_params error for device (" << _deviceName << "), " << snd_strerror(result) << "."); ATA_ERROR("snd_pcm_hw_params error for device (" << _deviceName << "), " << snd_strerror(result) << ".");
// TODO : Return audio::orchestra::error_warning; return false;
if (_info.outputChannels == 0) {
return true;
}
goto probeParameters;
} }
unsigned int value;
result = snd_pcm_hw_params_get_channels_max(params, &value); result = snd_pcm_hw_params_get_channels_max(params, &value);
if (result < 0) { if (result < 0) {
snd_pcm_close(phandle); snd_pcm_close(phandle);
ATA_ERROR("error getting device (" << _deviceName << ") input channels, " << snd_strerror(result) << "."); ATA_ERROR("error getting device (" << _deviceName << ") input channels, " << snd_strerror(result) << ".");
// TODO : Return audio::orchestra::error_warning; return false;
if (_info.outputChannels == 0) {
return true;
}
goto probeParameters;
} }
ATA_ERROR("Input channel = " << value); ATA_ERROR("Input channel = " << value);
_info.inputChannels = value; for (int32_t iii=0; iii<value; ++iii) {
snd_pcm_close(phandle); _info.channels.push_back(audio::channel_unknow);
// ALSA does not support duplex, but synchronization between I/Os
_info.duplexChannels = 0;
// ALSA doesn't provide default devices so we'll use the first available one.
if ( _localDeviceId == 0
&& _info.outputChannels > 0) {
_info.isDefaultOutput = true;
}
if ( _localDeviceId == 0
&& _info.inputChannels > 0) {
_info.isDefaultInput = true;
}
probeParameters:
// At this point, we just need to figure out the supported data
// formats and sample rates. We'll proceed by opening the device in
// the direction with the maximum number of channels, or playback if
// they are equal. This might limit our sample rate options, but so
// be it.
if (_info.outputChannels >= _info.inputChannels) {
stream = SND_PCM_STREAM_PLAYBACK;
} else {
stream = SND_PCM_STREAM_CAPTURE;
}
snd_pcm_info_set_stream(pcminfo, stream);
result = snd_pcm_open(&phandle, _deviceName.c_str(), stream, openMode | SND_PCM_NONBLOCK);
if (result < 0) {
ATA_ERROR("snd_pcm_open error for device (" << _deviceName << "), " << snd_strerror(result) << ".");
// TODO : Return audio::orchestra::error_warning;
return false;
}
// The device is open ... fill the parameter structure.
result = snd_pcm_hw_params_any(phandle, params);
if (result < 0) {
snd_pcm_close(phandle);
ATA_ERROR("snd_pcm_hw_params error for device (" << _deviceName << "), " << snd_strerror(result) << ".");
// TODO : Return audio::orchestra::error_warning;
return false;
} }
// Test our discrete set of sample rate values. // Test our discrete set of sample rate values.
_info.sampleRates.clear(); _info.sampleRates.clear();
@ -275,7 +206,6 @@ probeParameters:
if (_info.sampleRates.size() == 0) { if (_info.sampleRates.size() == 0) {
snd_pcm_close(phandle); snd_pcm_close(phandle);
ATA_ERROR("no supported sample rates found for device (" << _deviceName << ")."); ATA_ERROR("no supported sample rates found for device (" << _deviceName << ").");
// TODO : Return audio::orchestra::error_warning;
return false; return false;
} }
// Probe the supported data formats ... we don't care about endian-ness just yet // Probe the supported data formats ... we don't care about endian-ness just yet
@ -325,24 +255,16 @@ probeParameters:
} }
// That's all ... close the device and return // That's all ... close the device and return
snd_pcm_close(phandle); snd_pcm_close(phandle);
_info.probed = true;
return true; return true;
} }
audio::orchestra::DeviceInfo audio::orchestra::api::Alsa::getDeviceInfo(uint32_t _device) { audio::orchestra::DeviceInfo audio::orchestra::api::Alsa::getDeviceInfo(uint32_t _device) {
audio::orchestra::DeviceInfo info; audio::orchestra::DeviceInfo info;
/*
ATA_WARNING("plop");
getDeviceInfo("hw:0,0,0", info);
info.display();
getDeviceInfo("hw:0,0,1", info);
info.display();
*/
info.probed = false;
unsigned nDevices = 0; unsigned nDevices = 0;
int32_t result = -1; int32_t result = -1;
int32_t subdevice = -1; int32_t subdevice = -1;
int32_t card = -1; int32_t card = -1;
bool isInput = false;
char name[64]; char name[64];
snd_ctl_t *chandle; snd_ctl_t *chandle;
// Count cards and devices // Count cards and devices
@ -366,10 +288,18 @@ audio::orchestra::DeviceInfo audio::orchestra::api::Alsa::getDeviceInfo(uint32_t
break; break;
} }
if (nDevices == _device) { if (nDevices == _device) {
// for input
sprintf(name, "hw:%d,%d", card, subdevice); sprintf(name, "hw:%d,%d", card, subdevice);
isInput = true;
goto foundDevice; goto foundDevice;
} }
nDevices++; if (nDevices+1 == _device) {
// for output
sprintf(name, "hw:%d,%d", card, subdevice);
isInput = false;
goto foundDevice;
}
nDevices+=2;
} }
nextcard: nextcard:
snd_ctl_close(chandle); snd_ctl_close(chandle);
@ -398,9 +328,9 @@ foundDevice:
// TODO : return audio::orchestra::error_warning; // TODO : return audio::orchestra::error_warning;
return info; return info;
} }
return m_devices[ _device ]; return m_devices[_device];
} }
bool ret = audio::orchestra::api::Alsa::getNamedDeviceInfoLocal(name, info, card, subdevice, _device); bool ret = audio::orchestra::api::Alsa::getNamedDeviceInfoLocal(name, info, card, subdevice, _device, isInput);
if (ret == false) { if (ret == false) {
// TODO : ... // TODO : ...
return info; return info;
@ -418,13 +348,13 @@ void audio::orchestra::api::Alsa::saveDeviceInfo() {
} }
bool audio::orchestra::api::Alsa::probeDeviceOpen(uint32_t _device, bool audio::orchestra::api::Alsa::probeDeviceOpen(uint32_t _device,
audio::orchestra::mode _mode, audio::orchestra::mode _mode,
uint32_t _channels, uint32_t _channels,
uint32_t _firstChannel, uint32_t _firstChannel,
uint32_t _sampleRate, uint32_t _sampleRate,
audio::format _format, audio::format _format,
uint32_t *_bufferSize, uint32_t *_bufferSize,
const audio::orchestra::StreamOptions& _options) { const audio::orchestra::StreamOptions& _options) {
// I'm not using the "plug" interface ... too much inconsistent behavior. // I'm not using the "plug" interface ... too much inconsistent behavior.
unsigned nDevices = 0; unsigned nDevices = 0;
int32_t result, subdevice, card; int32_t result, subdevice, card;

View File

@ -27,7 +27,8 @@ namespace audio {
audio::orchestra::DeviceInfo& _info, audio::orchestra::DeviceInfo& _info,
int32_t _cardId=-1, // Alsa card ID int32_t _cardId=-1, // Alsa card ID
int32_t _subdevice=-1, // alsa subdevice ID int32_t _subdevice=-1, // alsa subdevice ID
int32_t _localDeviceId=-1); // local ID of device fined int32_t _localDeviceId=-1,// local ID of device find
bool _input=false);
public: public:
bool getNamedDeviceInfo(const std::string& _deviceName, audio::orchestra::DeviceInfo& _info) { bool getNamedDeviceInfo(const std::string& _deviceName, audio::orchestra::DeviceInfo& _info) {
return getNamedDeviceInfoLocal(_deviceName, _info); return getNamedDeviceInfoLocal(_deviceName, _info);
@ -48,7 +49,6 @@ namespace audio {
void callbackEventOneCycleMMAPWrite(); void callbackEventOneCycleMMAPWrite();
private: private:
static void alsaCallbackEvent(void* _userData); static void alsaCallbackEvent(void* _userData);
static void alsaCallbackEventMMap(void* _userData);
private: private:
std11::shared_ptr<AlsaPrivate> m_private; std11::shared_ptr<AlsaPrivate> m_private;
std::vector<audio::orchestra::DeviceInfo> m_devices; std::vector<audio::orchestra::DeviceInfo> m_devices;

View File

@ -9,6 +9,7 @@
#if defined(ORCHESTRA_BUILD_DS) #if defined(ORCHESTRA_BUILD_DS)
#include <audio/orchestra/Interface.h> #include <audio/orchestra/Interface.h>
#include <audio/orchestra/debug.h> #include <audio/orchestra/debug.h>
#include <etk/thread/tools.h>
#undef __class__ #undef __class__
#define __class__ "api::Ds" #define __class__ "api::Ds"
@ -58,15 +59,13 @@ static inline DWORD dsPointerBetween(DWORD _pointer, DWORD _laterPointer, DWORD
class DsDevice { class DsDevice {
public: public:
LPGUID id[2]; LPGUID id;
bool validId[2]; bool input;
bool found;
std::string name; std::string name;
DsDevice() : DsDevice() :
found(false) { id(0),
validId[0] = false; input(false) {
validId[1] = false;
} }
}; };
@ -105,12 +104,6 @@ namespace audio {
} }
} }
// Declarations for utility functions, callbacks, and structures
// specific to the DirectSound implementation.
static BOOL CALLBACK deviceQueryCallback(LPGUID _lpguid,
LPCTSTR _description,
LPCTSTR _module,
LPVOID _lpContext);
static const char* getErrorString(int32_t _code); static const char* getErrorString(int32_t _code);
@ -139,220 +132,248 @@ audio::orchestra::api::Ds::~Ds() {
} }
} }
// The DirectSound default output is always the first device.
uint32_t audio::orchestra::api::Ds::getDefaultOutputDevice() { #include "tchar.h"
return 0; static std::string convertTChar(LPCTSTR _name) {
#if defined(UNICODE) || defined(_UNICODE)
int32_t length = WideCharToMultiByte(CP_UTF8, 0, _name, -1, nullptr, 0, nullptr, nullptr);
std::string s(length-1, '\0');
WideCharToMultiByte(CP_UTF8, 0, _name, -1, &s[0], length, nullptr, nullptr);
#else
std::string s(_name);
#endif
return s;
} }
// The DirectSound default input is always the first input device, static BOOL CALLBACK deviceQueryCallback(LPGUID _lpguid,
// which is the first capture device enumerated. LPCTSTR _description,
uint32_t audio::orchestra::api::Ds::getDefaultInputDevice() { LPCTSTR _module,
return 0; LPVOID _lpContext) {
struct DsProbeData& probeInfo = *(struct DsProbeData*) _lpContext;
std::vector<DsDevice>& dsDevices = *probeInfo.dsDevices;
HRESULT hr;
bool validDevice = false;
if (probeInfo.isInput == true) {
DSCCAPS caps;
LPDIRECTSOUNDCAPTURE object;
hr = DirectSoundCaptureCreate(_lpguid, &object, nullptr);
if (hr != DS_OK) {
return TRUE;
}
caps.dwSize = sizeof(caps);
hr = object->GetCaps(&caps);
if (hr == DS_OK) {
if (caps.dwChannels > 0 && caps.dwFormats > 0) {
validDevice = true;
}
}
object->Release();
} else {
DSCAPS caps;
LPDIRECTSOUND object;
hr = DirectSoundCreate(_lpguid, &object, nullptr);
if (hr != DS_OK) {
return TRUE;
}
caps.dwSize = sizeof(caps);
hr = object->GetCaps(&caps);
if (hr == DS_OK) {
if ( caps.dwFlags & DSCAPS_PRIMARYMONO
|| caps.dwFlags & DSCAPS_PRIMARYSTEREO) {
validDevice = true;
}
}
object->Release();
}
if (validDevice == false) {
return TRUE;
}
// If good device, then save its name and guid.
std::string name = convertTChar(_description);
//if (name == "Primary Sound Driver" || name == "Primary Sound Capture Driver")
if (_lpguid == nullptr) {
name = "Default Device";
}
DsDevice device;
device.name = name;
device.input = probeInfo.isInput;
device.id = _lpguid;
dsDevices.push_back(device);
return TRUE;
} }
uint32_t audio::orchestra::api::Ds::getDeviceCount() { uint32_t audio::orchestra::api::Ds::getDeviceCount() {
// Set query flag for previously found devices to false, so that we
// can check for any devices that have disappeared.
for (size_t iii=0; iii<m_private->dsDevices.size(); ++iii) {
m_private->dsDevices[iii].found = false;
}
// Query DirectSound devices. // Query DirectSound devices.
struct DsProbeData probeInfo; struct DsProbeData probeInfo;
probeInfo.isInput = false; probeInfo.isInput = false;
probeInfo.dsDevices = &m_private->dsDevices; probeInfo.dsDevices = &m_private->dsDevices;
HRESULT result = DirectSoundEnumerate((LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo); HRESULT result = DirectSoundEnumerate((LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") enumerating output devices!"); ATA_ERROR(getErrorString(result) << ": enumerating output devices!");
return 0; return 0;
} }
// Query DirectSoundCapture devices. // Query DirectSoundCapture devices.
probeInfo.isInput = true; probeInfo.isInput = true;
result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo); result = DirectSoundCaptureEnumerate((LPDSENUMCALLBACK) deviceQueryCallback, &probeInfo);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") enumerating input devices!"); ATA_ERROR(getErrorString(result) << ": enumerating input devices!");
return 0; return 0;
} }
// Clean out any devices that may have disappeared.
std::vector< int32_t > indices;
for (uint32_t i=0; i<m_private->dsDevices.size(); i++) {
if (m_private->dsDevices[i].found == false) {
indices.push_back(i);
}
}
uint32_t nErased = 0;
for (uint32_t i=0; i<indices.size(); i++) {
m_private->dsDevices.erase(m_private->dsDevices.begin()-nErased++);
}
return m_private->dsDevices.size(); return m_private->dsDevices.size();
} }
audio::orchestra::DeviceInfo audio::orchestra::api::Ds::getDeviceInfo(uint32_t _device) { audio::orchestra::DeviceInfo audio::orchestra::api::Ds::getDeviceInfo(uint32_t _device) {
audio::orchestra::DeviceInfo info; audio::orchestra::DeviceInfo info;
info.probed = false;
if (m_private->dsDevices.size() == 0) {
// Force a query of all devices
getDeviceCount();
if (m_private->dsDevices.size() == 0) {
ATA_ERROR("no devices found!");
return info;
}
}
if (_device >= m_private->dsDevices.size()) { if (_device >= m_private->dsDevices.size()) {
ATA_ERROR("device ID is invalid!"); ATA_ERROR("device ID is invalid!");
return info; return info;
} }
HRESULT result; HRESULT result;
if (m_private->dsDevices[ _device ].validId[0] == false) { if (m_private->dsDevices[_device].input == false) {
goto probeInput; LPDIRECTSOUND output;
} DSCAPS outCaps;
LPDIRECTSOUND output; result = DirectSoundCreate(m_private->dsDevices[_device].id, &output, nullptr);
DSCAPS outCaps; if (FAILED(result)) {
result = DirectSoundCreate(m_private->dsDevices[ _device ].id[0], &output, nullptr); ATA_ERROR(getErrorString(result) << ": opening output device (" << m_private->dsDevices[_device].name << ")!");
if (FAILED(result)) { return info;
ATA_ERROR("error (" << getErrorString(result) << ") opening output device (" << m_private->dsDevices[ _device ].name << ")!");
goto probeInput;
}
outCaps.dwSize = sizeof(outCaps);
result = output->GetCaps(&outCaps);
if (FAILED(result)) {
output->Release();
ATA_ERROR("error (" << getErrorString(result) << ") getting capabilities!");
goto probeInput;
}
// Get output channel information.
info.outputChannels = (outCaps.dwFlags & DSCAPS_PRIMARYSTEREO) ? 2 : 1;
// Get sample rate information.
info.sampleRates.clear();
for (auto &it : audio::orchestra::genericSampleRate()) {
if ( it >= outCaps.dwMinSecondarySampleRate
&& it <= outCaps.dwMaxSecondarySampleRate) {
info.sampleRates.push_back(it);
} }
} outCaps.dwSize = sizeof(outCaps);
// Get format information. result = output->GetCaps(&outCaps);
if (outCaps.dwFlags & DSCAPS_PRIMARY16BIT) { if (FAILED(result)) {
info.nativeFormats.push_back(audio::format_int16); output->Release();
} ATA_ERROR(getErrorString(result) << ": getting capabilities!");
if (outCaps.dwFlags & DSCAPS_PRIMARY8BIT) { return info;
info.nativeFormats.push_back(audio::format_int8);
}
output->Release();
if (getDefaultOutputDevice() == _device) {
info.isDefaultOutput = true;
}
if (m_private->dsDevices[ _device ].validId[1] == false) {
info.name = m_private->dsDevices[ _device ].name;
info.probed = true;
return info;
}
probeInput:
LPDIRECTSOUNDCAPTURE input;
result = DirectSoundCaptureCreate(m_private->dsDevices[ _device ].id[1], &input, nullptr);
if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") opening input device (" << m_private->dsDevices[ _device ].name << ")!");
return info;
}
DSCCAPS inCaps;
inCaps.dwSize = sizeof(inCaps);
result = input->GetCaps(&inCaps);
if (FAILED(result)) {
input->Release();
ATA_ERROR("error (" << getErrorString(result) << ") getting object capabilities (" << m_private->dsDevices[ _device ].name << ")!");
return info;
}
// Get input channel information.
info.inputChannels = inCaps.dwChannels;
// Get sample rate and format information.
std::vector<uint32_t> rates;
if (inCaps.dwChannels >= 2) {
if ( (inCaps.dwFormats & WAVE_FORMAT_1S16)
|| (inCaps.dwFormats & WAVE_FORMAT_2S16)
|| (inCaps.dwFormats & WAVE_FORMAT_4S16)
|| (inCaps.dwFormats & WAVE_FORMAT_96S16) ) {
info.nativeFormats.push_back(audio::format_int16);
} }
if ( (inCaps.dwFormats & WAVE_FORMAT_1S08) // Get output channel information.
|| (inCaps.dwFormats & WAVE_FORMAT_2S08) if (outCaps.dwFlags & DSCAPS_PRIMARYSTEREO) {
|| (inCaps.dwFormats & WAVE_FORMAT_4S08) info.channels.push_back(audio::channel_unknow);
|| (inCaps.dwFormats & WAVE_FORMAT_96S08) ) { info.channels.push_back(audio::channel_unknow);
info.nativeFormats.push_back(audio::format_int8); } else {
info.channels.push_back(audio::channel_unknow);
} }
if ( (inCaps.dwFormats & WAVE_FORMAT_1S16) // Get sample rate information.
|| (inCaps.dwFormats & WAVE_FORMAT_1S08) ){ for (auto &it : audio::orchestra::genericSampleRate()) {
rates.push_back(11025); if ( it >= outCaps.dwMinSecondarySampleRate
} && it <= outCaps.dwMaxSecondarySampleRate) {
if ( (inCaps.dwFormats & WAVE_FORMAT_2S16) info.sampleRates.push_back(it);
|| (inCaps.dwFormats & WAVE_FORMAT_2S08) ){
rates.push_back(22050);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_4S16)
|| (inCaps.dwFormats & WAVE_FORMAT_4S08) ){
rates.push_back(44100);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_96S16)
|| (inCaps.dwFormats & WAVE_FORMAT_96S08) ){
rates.push_back(96000);
}
} else if (inCaps.dwChannels == 1) {
if ( (inCaps.dwFormats & WAVE_FORMAT_1M16)
|| (inCaps.dwFormats & WAVE_FORMAT_2M16)
|| (inCaps.dwFormats & WAVE_FORMAT_4M16)
|| (inCaps.dwFormats & WAVE_FORMAT_96M16) ) {
info.nativeFormats.push_back(audio::format_int16);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_1M08)
|| (inCaps.dwFormats & WAVE_FORMAT_2M08)
|| (inCaps.dwFormats & WAVE_FORMAT_4M08)
|| (inCaps.dwFormats & WAVE_FORMAT_96M08) ) {
info.nativeFormats.push_back(audio::format_int8);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_1M16)
|| (inCaps.dwFormats & WAVE_FORMAT_1M08) ){
rates.push_back(11025);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_2M16)
|| (inCaps.dwFormats & WAVE_FORMAT_2M08) ){
rates.push_back(22050);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_4M16)
|| (inCaps.dwFormats & WAVE_FORMAT_4M08) ){
rates.push_back(44100);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_96M16)
|| (inCaps.dwFormats & WAVE_FORMAT_96M08) ){
rates.push_back(96000);
}
} else {
// technically, this would be an error
info.inputChannels = 0;
}
input->Release();
if (info.inputChannels == 0) {
return info;
}
// Copy the supported rates to the info structure but avoid duplication.
bool found;
for (uint32_t i=0; i<rates.size(); i++) {
found = false;
for (uint32_t j=0; j<info.sampleRates.size(); j++) {
if (rates[i] == info.sampleRates[j]) {
found = true;
break;
} }
} }
if (found == false) info.sampleRates.push_back(rates[i]); // Get format information.
if (outCaps.dwFlags & DSCAPS_PRIMARY16BIT) {
info.nativeFormats.push_back(audio::format_int16);
}
if (outCaps.dwFlags & DSCAPS_PRIMARY8BIT) {
info.nativeFormats.push_back(audio::format_int8);
}
output->Release();
info.name = m_private->dsDevices[_device].name;
return info;
} else {
LPDIRECTSOUNDCAPTURE input;
result = DirectSoundCaptureCreate(m_private->dsDevices[_device].id, &input, nullptr);
if (FAILED(result)) {
ATA_ERROR(getErrorString(result) << ": opening input device (" << m_private->dsDevices[_device].name << ")!");
return info;
}
DSCCAPS inCaps;
inCaps.dwSize = sizeof(inCaps);
result = input->GetCaps(&inCaps);
if (FAILED(result)) {
input->Release();
ATA_ERROR(getErrorString(result) << ": getting object capabilities (" << m_private->dsDevices[_device].name << ")!");
return info;
}
// Get input channel information.
for (int32_t iii=0; iii<inCaps.dwChannels; ++iii) {
info.channels.push_back(audio::channel_unknow);
}
// Get sample rate and format information.
std::vector<uint32_t> rates;
if (inCaps.dwChannels >= 2) {
if ( (inCaps.dwFormats & WAVE_FORMAT_1S16)
|| (inCaps.dwFormats & WAVE_FORMAT_2S16)
|| (inCaps.dwFormats & WAVE_FORMAT_4S16)
|| (inCaps.dwFormats & WAVE_FORMAT_96S16) ) {
info.nativeFormats.push_back(audio::format_int16);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_1S08)
|| (inCaps.dwFormats & WAVE_FORMAT_2S08)
|| (inCaps.dwFormats & WAVE_FORMAT_4S08)
|| (inCaps.dwFormats & WAVE_FORMAT_96S08) ) {
info.nativeFormats.push_back(audio::format_int8);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_1S16)
|| (inCaps.dwFormats & WAVE_FORMAT_1S08) ){
rates.push_back(11025);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_2S16)
|| (inCaps.dwFormats & WAVE_FORMAT_2S08) ){
rates.push_back(22050);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_4S16)
|| (inCaps.dwFormats & WAVE_FORMAT_4S08) ){
rates.push_back(44100);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_96S16)
|| (inCaps.dwFormats & WAVE_FORMAT_96S08) ){
rates.push_back(96000);
}
} else if (inCaps.dwChannels == 1) {
if ( (inCaps.dwFormats & WAVE_FORMAT_1M16)
|| (inCaps.dwFormats & WAVE_FORMAT_2M16)
|| (inCaps.dwFormats & WAVE_FORMAT_4M16)
|| (inCaps.dwFormats & WAVE_FORMAT_96M16) ) {
info.nativeFormats.push_back(audio::format_int16);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_1M08)
|| (inCaps.dwFormats & WAVE_FORMAT_2M08)
|| (inCaps.dwFormats & WAVE_FORMAT_4M08)
|| (inCaps.dwFormats & WAVE_FORMAT_96M08) ) {
info.nativeFormats.push_back(audio::format_int8);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_1M16)
|| (inCaps.dwFormats & WAVE_FORMAT_1M08) ){
rates.push_back(11025);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_2M16)
|| (inCaps.dwFormats & WAVE_FORMAT_2M08) ){
rates.push_back(22050);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_4M16)
|| (inCaps.dwFormats & WAVE_FORMAT_4M08) ){
rates.push_back(44100);
}
if ( (inCaps.dwFormats & WAVE_FORMAT_96M16)
|| (inCaps.dwFormats & WAVE_FORMAT_96M08) ){
rates.push_back(96000);
}
} else {
// technically, this would be an error
info.channels.clear();
}
input->Release();
if (info.channels.size() == 0) {
return info;
}
// Copy the supported rates to the info structure but avoid duplication.
bool found;
for (uint32_t i=0; i<rates.size(); i++) {
found = false;
for (uint32_t j=0; j<info.sampleRates.size(); j++) {
if (rates[i] == info.sampleRates[j]) {
found = true;
break;
}
}
if (found == false) {
info.sampleRates.push_back(rates[i]);
}
}
std::sort(info.sampleRates.begin(), info.sampleRates.end());
// Copy name and return.
info.name = m_private->dsDevices[_device].name;
return info;
} }
std::sort(info.sampleRates.begin(), info.sampleRates.end());
// If device opens for both playback and capture, we determine the channels.
if (info.outputChannels > 0 && info.inputChannels > 0) {
info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
}
if (_device == 0) {
info.isDefaultInput = true;
}
// Copy name and return.
info.name = m_private->dsDevices[ _device ].name;
info.probed = true;
return info; return info;
} }
@ -379,17 +400,6 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
ATA_ERROR("device ID is invalid!"); ATA_ERROR("device ID is invalid!");
return false; return false;
} }
if (_mode == audio::orchestra::mode_output) {
if (m_private->dsDevices[ _device ].validId[0] == false) {
ATA_ERROR("device (" << _device << ") does not support output!");
return false;
}
} else { // _mode == audio::orchestra::mode_input
if (m_private->dsDevices[ _device ].validId[1] == false) {
ATA_ERROR("device (" << _device << ") does not support input!");
return false;
}
}
// According to a note in PortAudio, using GetDesktopWindow() // According to a note in PortAudio, using GetDesktopWindow()
// instead of GetForegroundWindow() is supposed to avoid problems // instead of GetForegroundWindow() is supposed to avoid problems
// that occur when the application's window is not the foreground // that occur when the application's window is not the foreground
@ -433,9 +443,9 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
HRESULT result; HRESULT result;
if (_mode == audio::orchestra::mode_output) { if (_mode == audio::orchestra::mode_output) {
LPDIRECTSOUND output; LPDIRECTSOUND output;
result = DirectSoundCreate(m_private->dsDevices[ _device ].id[0], &output, nullptr); result = DirectSoundCreate(m_private->dsDevices[_device].id, &output, nullptr);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") opening output device (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": opening output device (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
DSCAPS outCaps; DSCAPS outCaps;
@ -443,12 +453,12 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
result = output->GetCaps(&outCaps); result = output->GetCaps(&outCaps);
if (FAILED(result)) { if (FAILED(result)) {
output->Release(); output->Release();
ATA_ERROR("error (" << getErrorString(result) << ") getting capabilities (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": getting capabilities (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
// Check channel information. // Check channel information.
if (_channels + _firstChannel == 2 && !(outCaps.dwFlags & DSCAPS_PRIMARYSTEREO)) { if (_channels + _firstChannel == 2 && !(outCaps.dwFlags & DSCAPS_PRIMARYSTEREO)) {
ATA_ERROR("the output device (" << m_private->dsDevices[ _device ].name << ") does not support stereo playback."); ATA_ERROR("the output device (" << m_private->dsDevices[_device].name << ") does not support stereo playback.");
return false; return false;
} }
// Check format information. Use 16-bit format unless not // Check format information. Use 16-bit format unless not
@ -477,7 +487,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
result = output->SetCooperativeLevel(hWnd, DSSCL_PRIORITY); result = output->SetCooperativeLevel(hWnd, DSSCL_PRIORITY);
if (FAILED(result)) { if (FAILED(result)) {
output->Release(); output->Release();
ATA_ERROR("error (" << getErrorString(result) << ") setting cooperative level (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": setting cooperative level (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
// Even though we will write to the secondary buffer, we need to // Even though we will write to the secondary buffer, we need to
@ -493,14 +503,14 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
result = output->CreateSoundBuffer(&bufferDescription, &buffer, nullptr); result = output->CreateSoundBuffer(&bufferDescription, &buffer, nullptr);
if (FAILED(result)) { if (FAILED(result)) {
output->Release(); output->Release();
ATA_ERROR("error (" << getErrorString(result) << ") accessing primary buffer (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": accessing primary buffer (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
// Set the primary DS buffer sound format. // Set the primary DS buffer sound format.
result = buffer->SetFormat(&waveFormat); result = buffer->SetFormat(&waveFormat);
if (FAILED(result)) { if (FAILED(result)) {
output->Release(); output->Release();
ATA_ERROR("error (" << getErrorString(result) << ") setting primary buffer format (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": setting primary buffer format (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
// Setup the secondary DS buffer description. // Setup the secondary DS buffer description.
@ -523,7 +533,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
result = output->CreateSoundBuffer(&bufferDescription, &buffer, nullptr); result = output->CreateSoundBuffer(&bufferDescription, &buffer, nullptr);
if (FAILED(result)) { if (FAILED(result)) {
output->Release(); output->Release();
ATA_ERROR("error (" << getErrorString(result) << ") creating secondary buffer (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": creating secondary buffer (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
} }
@ -534,7 +544,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
if (FAILED(result)) { if (FAILED(result)) {
output->Release(); output->Release();
buffer->Release(); buffer->Release();
ATA_ERROR("error (" << getErrorString(result) << ") getting buffer settings (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": getting buffer settings (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
dsBufferSize = dsbcaps.dwBufferBytes; dsBufferSize = dsbcaps.dwBufferBytes;
@ -545,7 +555,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
if (FAILED(result)) { if (FAILED(result)) {
output->Release(); output->Release();
buffer->Release(); buffer->Release();
ATA_ERROR("error (" << getErrorString(result) << ") locking buffer (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": locking buffer (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
// Zero the DS buffer // Zero the DS buffer
@ -555,7 +565,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
if (FAILED(result)) { if (FAILED(result)) {
output->Release(); output->Release();
buffer->Release(); buffer->Release();
ATA_ERROR("error (" << getErrorString(result) << ") unlocking buffer (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": unlocking buffer (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
ohandle = (void *) output; ohandle = (void *) output;
@ -563,9 +573,9 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
} }
if (_mode == audio::orchestra::mode_input) { if (_mode == audio::orchestra::mode_input) {
LPDIRECTSOUNDCAPTURE input; LPDIRECTSOUNDCAPTURE input;
result = DirectSoundCaptureCreate(m_private->dsDevices[ _device ].id[1], &input, nullptr); result = DirectSoundCaptureCreate(m_private->dsDevices[_device].id, &input, nullptr);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") opening input device (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": opening input device (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
DSCCAPS inCaps; DSCCAPS inCaps;
@ -573,7 +583,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
result = input->GetCaps(&inCaps); result = input->GetCaps(&inCaps);
if (FAILED(result)) { if (FAILED(result)) {
input->Release(); input->Release();
ATA_ERROR("error (" << getErrorString(result) << ") getting input capabilities (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": getting input capabilities (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
// Check channel information. // Check channel information.
@ -626,7 +636,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
result = input->CreateCaptureBuffer(&bufferDescription, &buffer, nullptr); result = input->CreateCaptureBuffer(&bufferDescription, &buffer, nullptr);
if (FAILED(result)) { if (FAILED(result)) {
input->Release(); input->Release();
ATA_ERROR("error (" << getErrorString(result) << ") creating input buffer (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": creating input buffer (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
// Get the buffer size ... might be different from what we specified. // Get the buffer size ... might be different from what we specified.
@ -636,7 +646,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
if (FAILED(result)) { if (FAILED(result)) {
input->Release(); input->Release();
buffer->Release(); buffer->Release();
ATA_ERROR("error (" << getErrorString(result) << ") getting buffer settings (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": getting buffer settings (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
dsBufferSize = dscbcaps.dwBufferBytes; dsBufferSize = dscbcaps.dwBufferBytes;
@ -651,7 +661,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
if (FAILED(result)) { if (FAILED(result)) {
input->Release(); input->Release();
buffer->Release(); buffer->Release();
ATA_ERROR("error (" << getErrorString(result) << ") locking input buffer (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": locking input buffer (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
// Zero the buffer // Zero the buffer
@ -661,7 +671,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
if (FAILED(result)) { if (FAILED(result)) {
input->Release(); input->Release();
buffer->Release(); buffer->Release();
ATA_ERROR("error (" << getErrorString(result) << ") unlocking input buffer (" << m_private->dsDevices[ _device ].name << ")!"); ATA_ERROR(getErrorString(result) << ": unlocking input buffer (" << m_private->dsDevices[_device].name << ")!");
return false; return false;
} }
ohandle = (void *) input; ohandle = (void *) input;
@ -749,7 +759,7 @@ bool audio::orchestra::api::Ds::probeDeviceOpen(uint32_t _device,
goto error; goto error;
} }
// Boost DS thread priority // Boost DS thread priority
SetThreadPriority((HANDLE)m_private->thread, THREAD_PRIORITY_HIGHEST); etk::thread::setPriority(*m_private->thread, -6);
} }
return true; return true;
error: error:
@ -788,8 +798,10 @@ enum audio::orchestra::error audio::orchestra::api::Ds::closeStream() {
} }
// Stop the callback thread. // Stop the callback thread.
m_private->threadRunning = false; m_private->threadRunning = false;
WaitForSingleObject((HANDLE) m_private->thread, INFINITE); if (m_private->thread != nullptr) {
CloseHandle((HANDLE) m_private->thread); m_private->thread->join();
m_private->thread = nullptr;
}
if (m_private->buffer[0]) { // the object pointer can be nullptr and valid if (m_private->buffer[0]) { // the object pointer can be nullptr and valid
LPDIRECTSOUND object = (LPDIRECTSOUND) m_private->id[0]; LPDIRECTSOUND object = (LPDIRECTSOUND) m_private->id[0];
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
@ -846,7 +858,7 @@ enum audio::orchestra::error audio::orchestra::api::Ds::startStream() {
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
result = buffer->Play(0, 0, DSBPLAY_LOOPING); result = buffer->Play(0, 0, DSBPLAY_LOOPING);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") starting output buffer!"); ATA_ERROR(getErrorString(result) << ": starting output buffer!");
goto unlock; goto unlock;
} }
} }
@ -855,7 +867,7 @@ enum audio::orchestra::error audio::orchestra::api::Ds::startStream() {
LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1];
result = buffer->Start(DSCBSTART_LOOPING); result = buffer->Start(DSCBSTART_LOOPING);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") starting input buffer!"); ATA_ERROR(getErrorString(result) << ": starting input buffer!");
goto unlock; goto unlock;
} }
} }
@ -892,14 +904,14 @@ enum audio::orchestra::error audio::orchestra::api::Ds::stopStream() {
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
result = buffer->Stop(); result = buffer->Stop();
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") stopping output buffer!"); ATA_ERROR(getErrorString(result) << ": stopping output buffer!");
goto unlock; goto unlock;
} }
// Lock the buffer and clear it so that if we start to play again, // Lock the buffer and clear it so that if we start to play again,
// we won't have old data playing. // we won't have old data playing.
result = buffer->Lock(0, m_private->dsBufferSize[0], &audioPtr, &dataLen, nullptr, nullptr, 0); result = buffer->Lock(0, m_private->dsBufferSize[0], &audioPtr, &dataLen, nullptr, nullptr, 0);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") locking output buffer!"); ATA_ERROR(getErrorString(result) << ": locking output buffer!");
goto unlock; goto unlock;
} }
// Zero the DS buffer // Zero the DS buffer
@ -907,7 +919,7 @@ enum audio::orchestra::error audio::orchestra::api::Ds::stopStream() {
// Unlock the DS buffer // Unlock the DS buffer
result = buffer->Unlock(audioPtr, dataLen, nullptr, 0); result = buffer->Unlock(audioPtr, dataLen, nullptr, 0);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") unlocking output buffer!"); ATA_ERROR(getErrorString(result) << ": unlocking output buffer!");
goto unlock; goto unlock;
} }
// If we start playing again, we must begin at beginning of buffer. // If we start playing again, we must begin at beginning of buffer.
@ -921,14 +933,14 @@ enum audio::orchestra::error audio::orchestra::api::Ds::stopStream() {
m_state = audio::orchestra::state_stopped; m_state = audio::orchestra::state_stopped;
result = buffer->Stop(); result = buffer->Stop();
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") stopping input buffer!"); ATA_ERROR(getErrorString(result) << ": stopping input buffer!");
goto unlock; goto unlock;
} }
// Lock the buffer and clear it so that if we start to play again, // Lock the buffer and clear it so that if we start to play again,
// we won't have old data playing. // we won't have old data playing.
result = buffer->Lock(0, m_private->dsBufferSize[1], &audioPtr, &dataLen, nullptr, nullptr, 0); result = buffer->Lock(0, m_private->dsBufferSize[1], &audioPtr, &dataLen, nullptr, nullptr, 0);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") locking input buffer!"); ATA_ERROR(getErrorString(result) << ": locking input buffer!");
goto unlock; goto unlock;
} }
// Zero the DS buffer // Zero the DS buffer
@ -936,7 +948,7 @@ enum audio::orchestra::error audio::orchestra::api::Ds::stopStream() {
// Unlock the DS buffer // Unlock the DS buffer
result = buffer->Unlock(audioPtr, dataLen, nullptr, 0); result = buffer->Unlock(audioPtr, dataLen, nullptr, 0);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") unlocking input buffer!"); ATA_ERROR(getErrorString(result) << ": unlocking input buffer!");
goto unlock; goto unlock;
} }
// If we start recording again, we must begin at beginning of buffer. // If we start recording again, we must begin at beginning of buffer.
@ -963,6 +975,7 @@ enum audio::orchestra::error audio::orchestra::api::Ds::abortStream() {
} }
void audio::orchestra::api::Ds::callbackEvent() { void audio::orchestra::api::Ds::callbackEvent() {
etk::thread::setName("DS IO-" + m_name);
if (m_state == audio::orchestra::state_stopped || m_state == audio::orchestra::state_stopping) { if (m_state == audio::orchestra::state_stopped || m_state == audio::orchestra::state_stopping) {
Sleep(50); // sleep 50 milliseconds Sleep(50); // sleep 50 milliseconds
return; return;
@ -985,23 +998,23 @@ void audio::orchestra::api::Ds::callbackEvent() {
// draining stream. // draining stream.
if (m_private->drainCounter == 0) { if (m_private->drainCounter == 0) {
audio::Time streamTime = getStreamTime(); audio::Time streamTime = getStreamTime();
audio::orchestra::status status = audio::orchestra::status_ok; std::vector<audio::orchestra::status> status;
if ( m_mode != audio::orchestra::mode_input if ( m_mode != audio::orchestra::mode_input
&& m_private->xrun[0] == true) { && m_private->xrun[0] == true) {
status = audio::orchestra::status_underflow; status.push_back(audio::orchestra::status_underflow);
m_private->xrun[0] = false; m_private->xrun[0] = false;
} }
if ( m_mode != audio::orchestra::mode_output if ( m_mode != audio::orchestra::mode_output
&& m_private->xrun[1] == true) { && m_private->xrun[1] == true) {
status = audio::orchestra::status_overflow; status.push_back(audio::orchestra::status_overflow);
m_private->xrun[1] = false; m_private->xrun[1] = false;
} }
int32_t cbReturnValue = info->callback(&m_userBuffer[1][0], int32_t cbReturnValue = m_callback(&m_userBuffer[1][0],
streamTime, streamTime,
&m_userBuffer[0][0], &m_userBuffer[0][0],
streamTime, streamTime,
m_bufferSize, m_bufferSize,
status); status);
if (cbReturnValue == 2) { if (cbReturnValue == 2) {
m_state = audio::orchestra::state_stopping; m_state = audio::orchestra::state_stopping;
m_private->drainCounter = 2; m_private->drainCounter = 2;
@ -1042,23 +1055,23 @@ void audio::orchestra::api::Ds::callbackEvent() {
DWORD startSafeWritePointer, startSafeReadPointer; DWORD startSafeWritePointer, startSafeReadPointer;
result = dsWriteBuffer->GetCurrentPosition(nullptr, &startSafeWritePointer); result = dsWriteBuffer->GetCurrentPosition(nullptr, &startSafeWritePointer);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") getting current write position!"); ATA_ERROR(getErrorString(result) << ": getting current write position!");
return; return;
} }
result = dsCaptureBuffer->GetCurrentPosition(nullptr, &startSafeReadPointer); result = dsCaptureBuffer->GetCurrentPosition(nullptr, &startSafeReadPointer);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") getting current read position!"); ATA_ERROR(getErrorString(result) << ": getting current read position!");
return; return;
} }
while (true) { while (true) {
result = dsWriteBuffer->GetCurrentPosition(nullptr, &safeWritePointer); result = dsWriteBuffer->GetCurrentPosition(nullptr, &safeWritePointer);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") getting current write position!"); ATA_ERROR(getErrorString(result) << ": getting current write position!");
return; return;
} }
result = dsCaptureBuffer->GetCurrentPosition(nullptr, &safeReadPointer); result = dsCaptureBuffer->GetCurrentPosition(nullptr, &safeReadPointer);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") getting current read position!"); ATA_ERROR(getErrorString(result) << ": getting current read position!");
return; return;
} }
if ( safeWritePointer != startSafeWritePointer if ( safeWritePointer != startSafeWritePointer
@ -1078,7 +1091,7 @@ void audio::orchestra::api::Ds::callbackEvent() {
LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0]; LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
result = dsWriteBuffer->GetCurrentPosition(&currentWritePointer, &safeWritePointer); result = dsWriteBuffer->GetCurrentPosition(&currentWritePointer, &safeWritePointer);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") getting current write position!"); ATA_ERROR(getErrorString(result) << ": getting current write position!");
return; return;
} }
m_private->bufferPointer[0] = safeWritePointer + m_private->dsPointerLeadTime[0]; m_private->bufferPointer[0] = safeWritePointer + m_private->dsPointerLeadTime[0];
@ -1123,7 +1136,7 @@ void audio::orchestra::api::Ds::callbackEvent() {
// Find out where the read and "safe write" pointers are. // Find out where the read and "safe write" pointers are.
result = dsBuffer->GetCurrentPosition(&currentWritePointer, &safeWritePointer); result = dsBuffer->GetCurrentPosition(&currentWritePointer, &safeWritePointer);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") getting current write position!"); ATA_ERROR(getErrorString(result) << ": getting current write position!");
return; return;
} }
// We will copy our output buffer into the region between // We will copy our output buffer into the region between
@ -1172,7 +1185,7 @@ void audio::orchestra::api::Ds::callbackEvent() {
&bufferSize2, &bufferSize2,
0); 0);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") locking buffer during playback!"); ATA_ERROR(getErrorString(result) << ": locking buffer during playback!");
return; return;
} }
// Copy our buffer into the DS buffer // Copy our buffer into the DS buffer
@ -1183,7 +1196,7 @@ void audio::orchestra::api::Ds::callbackEvent() {
// Update our buffer offset and unlock sound buffer // Update our buffer offset and unlock sound buffer
dsBuffer->Unlock(buffer1, bufferSize1, buffer2, bufferSize2); dsBuffer->Unlock(buffer1, bufferSize1, buffer2, bufferSize2);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") unlocking buffer during playback!"); ATA_ERROR(getErrorString(result) << ": unlocking buffer during playback!");
return; return;
} }
nextWritePointer = (nextWritePointer + bufferSize1 + bufferSize2) % dsBufferSize; nextWritePointer = (nextWritePointer + bufferSize1 + bufferSize2) % dsBufferSize;
@ -1211,7 +1224,7 @@ void audio::orchestra::api::Ds::callbackEvent() {
// Find out where the write and "safe read" pointers are. // Find out where the write and "safe read" pointers are.
result = dsBuffer->GetCurrentPosition(&currentReadPointer, &safeReadPointer); result = dsBuffer->GetCurrentPosition(&currentReadPointer, &safeReadPointer);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") getting current read position!"); ATA_ERROR(getErrorString(result) << ": getting current read position!");
return; return;
} }
if (safeReadPointer < (DWORD)nextReadPointer) { if (safeReadPointer < (DWORD)nextReadPointer) {
@ -1271,7 +1284,7 @@ void audio::orchestra::api::Ds::callbackEvent() {
// Wake up and find out where we are now. // Wake up and find out where we are now.
result = dsBuffer->GetCurrentPosition(&currentReadPointer, &safeReadPointer); result = dsBuffer->GetCurrentPosition(&currentReadPointer, &safeReadPointer);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") getting current read position!"); ATA_ERROR(getErrorString(result) << ": getting current read position!");
return; return;
} }
if (safeReadPointer < (DWORD)nextReadPointer) { if (safeReadPointer < (DWORD)nextReadPointer) {
@ -1289,7 +1302,7 @@ void audio::orchestra::api::Ds::callbackEvent() {
&bufferSize2, &bufferSize2,
0); 0);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") locking capture buffer!"); ATA_ERROR(getErrorString(result) << ": locking capture buffer!");
return; return;
} }
if (m_duplexPrerollBytes <= 0) { if (m_duplexPrerollBytes <= 0) {
@ -1309,7 +1322,7 @@ void audio::orchestra::api::Ds::callbackEvent() {
nextReadPointer = (nextReadPointer + bufferSize1 + bufferSize2) % dsBufferSize; nextReadPointer = (nextReadPointer + bufferSize1 + bufferSize2) % dsBufferSize;
dsBuffer->Unlock(buffer1, bufferSize1, buffer2, bufferSize2); dsBuffer->Unlock(buffer1, bufferSize1, buffer2, bufferSize2);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") unlocking capture buffer!"); ATA_ERROR(getErrorString(result) << ": unlocking capture buffer!");
return; return;
} }
m_private->bufferPointer[1] = nextReadPointer; m_private->bufferPointer[1] = nextReadPointer;
@ -1330,100 +1343,12 @@ unlock:
} }
void audio::orchestra::api::Ds::dsCallbackEvent(void *_userData) { void audio::orchestra::api::Ds::dsCallbackEvent(void *_userData) {
etk::thread::setName("DS IO-" + m_name);
audio::orchestra::api::Ds* myClass = reinterpret_cast<audio::orchestra::api::Ds*>(_userData); audio::orchestra::api::Ds* myClass = reinterpret_cast<audio::orchestra::api::Ds*>(_userData);
while (myClass->m_private->threadRunning == true) { while (myClass->m_private->threadRunning == true) {
myClass->callbackEvent(); myClass->callbackEvent();
} }
} }
#include "tchar.h"
static std::string convertTChar(LPCTSTR _name) {
#if defined(UNICODE) || defined(_UNICODE)
int32_t length = WideCharToMultiByte(CP_UTF8, 0, _name, -1, nullptr, 0, nullptr, nullptr);
std::string s(length-1, '\0');
WideCharToMultiByte(CP_UTF8, 0, _name, -1, &s[0], length, nullptr, nullptr);
#else
std::string s(_name);
#endif
return s;
}
static BOOL CALLBACK deviceQueryCallback(LPGUID _lpguid,
LPCTSTR _description,
LPCTSTR _module,
LPVOID _lpContext) {
struct DsProbeData& probeInfo = *(struct DsProbeData*) _lpContext;
std::vector<DsDevice>& dsDevices = *probeInfo.dsDevices;
HRESULT hr;
bool validDevice = false;
if (probeInfo.isInput == true) {
DSCCAPS caps;
LPDIRECTSOUNDCAPTURE object;
hr = DirectSoundCaptureCreate(_lpguid, &object, nullptr);
if (hr != DS_OK) {
return TRUE;
}
caps.dwSize = sizeof(caps);
hr = object->GetCaps(&caps);
if (hr == DS_OK) {
if (caps.dwChannels > 0 && caps.dwFormats > 0) {
validDevice = true;
}
}
object->Release();
} else {
DSCAPS caps;
LPDIRECTSOUND object;
hr = DirectSoundCreate(_lpguid, &object, nullptr);
if (hr != DS_OK) {
return TRUE;
}
caps.dwSize = sizeof(caps);
hr = object->GetCaps(&caps);
if (hr == DS_OK) {
if ( caps.dwFlags & DSCAPS_PRIMARYMONO
|| caps.dwFlags & DSCAPS_PRIMARYSTEREO) {
validDevice = true;
}
}
object->Release();
}
// If good device, then save its name and guid.
std::string name = convertTChar(_description);
//if (name == "Primary Sound Driver" || name == "Primary Sound Capture Driver")
if (_lpguid == nullptr) {
name = "Default Device";
}
if (validDevice) {
for (size_t i=0; i<dsDevices.size(); i++) {
if (dsDevices[i].name == name) {
dsDevices[i].found = true;
if (probeInfo.isInput) {
dsDevices[i].id[1] = _lpguid;
dsDevices[i].validId[1] = true;
} else {
dsDevices[i].id[0] = _lpguid;
dsDevices[i].validId[0] = true;
}
return TRUE;
}
}
DsDevice device;
device.name = name;
device.found = true;
if (probeInfo.isInput) {
device.id[1] = _lpguid;
device.validId[1] = true;
} else {
device.id[0] = _lpguid;
device.validId[0] = true;
}
dsDevices.push_back(device);
}
return TRUE;
}
static const char* getErrorString(int32_t code) { static const char* getErrorString(int32_t code) {
switch (code) { switch (code) {
case DSERR_ALLOCATED: case DSERR_ALLOCATED:

View File

@ -23,8 +23,6 @@ namespace audio {
return audio::orchestra::type_ds; return audio::orchestra::type_ds;
} }
uint32_t getDeviceCount(); uint32_t getDeviceCount();
uint32_t getDefaultOutputDevice();
uint32_t getDefaultInputDevice();
audio::orchestra::DeviceInfo getDeviceInfo(uint32_t _device); audio::orchestra::DeviceInfo getDeviceInfo(uint32_t _device);
enum audio::orchestra::error closeStream(); enum audio::orchestra::error closeStream();
enum audio::orchestra::error startStream(); enum audio::orchestra::error startStream();

View File

@ -125,12 +125,11 @@ uint32_t audio::orchestra::api::Jack::getDeviceCount() {
free(ports); free(ports);
} }
jack_client_close(client); jack_client_close(client);
return nDevices; return nDevices*2;
} }
audio::orchestra::DeviceInfo audio::orchestra::api::Jack::getDeviceInfo(uint32_t _device) { audio::orchestra::DeviceInfo audio::orchestra::api::Jack::getDeviceInfo(uint32_t _device) {
audio::orchestra::DeviceInfo info; audio::orchestra::DeviceInfo info;
info.probed = false;
jack_options_t options = (jack_options_t) (JackNoStartServer); //JackNullOption jack_options_t options = (jack_options_t) (JackNoStartServer); //JackNullOption
jack_status_t *status = nullptr; jack_status_t *status = nullptr;
jack_client_t *client = jack_client_open("orchestraJackInfo", options, status); jack_client_t *client = jack_client_open("orchestraJackInfo", options, status);
@ -143,16 +142,18 @@ audio::orchestra::DeviceInfo audio::orchestra::api::Jack::getDeviceInfo(uint32_t
std::string port, previousPort; std::string port, previousPort;
uint32_t nPorts = 0, nDevices = 0; uint32_t nPorts = 0, nDevices = 0;
ports = jack_get_ports(client, nullptr, nullptr, 0); ports = jack_get_ports(client, nullptr, nullptr, 0);
int32_t deviceID = _device/2;
info.input = _device%2==0?true:false; // note that jack sens are inverted
if (ports) { if (ports) {
// Parse the port names up to the first colon (:). // Parse the port names up to the first colon (:).
size_t iColon = 0; size_t iColon = 0;
do { do {
port = (char *) ports[ nPorts ]; port = (char *) ports[nPorts];
iColon = port.find(":"); iColon = port.find(":");
if (iColon != std::string::npos) { if (iColon != std::string::npos) {
port = port.substr(0, iColon); port = port.substr(0, iColon);
if (port != previousPort) { if (port != previousPort) {
if (nDevices == _device) { if (nDevices == deviceID) {
info.name = port; info.name = port;
} }
nDevices++; nDevices++;
@ -162,7 +163,7 @@ audio::orchestra::DeviceInfo audio::orchestra::api::Jack::getDeviceInfo(uint32_t
} while (ports[++nPorts]); } while (ports[++nPorts]);
free(ports); free(ports);
} }
if (_device >= nDevices) { if (deviceID >= nDevices) {
jack_client_close(client); jack_client_close(client);
ATA_ERROR("device ID is invalid!"); ATA_ERROR("device ID is invalid!");
// TODO : audio::orchestra::error_invalidUse; // TODO : audio::orchestra::error_invalidUse;
@ -171,50 +172,42 @@ audio::orchestra::DeviceInfo audio::orchestra::api::Jack::getDeviceInfo(uint32_t
// 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.sampleRates.push_back(jack_get_sample_rate(client));
// Count the available ports containing the client name as device if (info.input == true) {
// channels. Jack "input ports" equal RtAudio output channels. ports = jack_get_ports(client, info.name.c_str(), nullptr, JackPortIsOutput);
uint32_t nChannels = 0; if (ports) {
ports = jack_get_ports(client, info.name.c_str(), nullptr, JackPortIsInput); int32_t iii=0;
if (ports) { while (ports[iii]) {
while (ports[ nChannels ]) { ATA_ERROR(" ploppp='" << ports[iii] << "'");
nChannels++; info.channels.push_back(audio::channel_unknow);
iii++;
}
free(ports);
} }
free(ports); } else {
info.outputChannels = nChannels; ports = jack_get_ports(client, info.name.c_str(), nullptr, JackPortIsInput);
} if (ports) {
// Jack "output ports" equal RtAudio input channels. int32_t iii=0;
nChannels = 0; while (ports[iii]) {
ports = jack_get_ports(client, info.name.c_str(), nullptr, JackPortIsOutput); ATA_ERROR(" ploppp='" << ports[iii] << "'");
if (ports) { info.channels.push_back(audio::channel_unknow);
while (ports[ nChannels ]) { iii++;
nChannels++; }
free(ports);
} }
free(ports);
info.inputChannels = nChannels;
} }
if (info.outputChannels == 0 && info.inputChannels == 0) { if (info.channels.size() == 0) {
jack_client_close(client); jack_client_close(client);
ATA_ERROR("error determining Jack input/output channels!"); ATA_ERROR("error determining Jack input/output channels!");
// TODO : audio::orchestra::error_warning; // TODO : audio::orchestra::error_warning;
return info; return info;
} }
// If device opens for both playback and capture, we determine the channels.
if (info.outputChannels > 0 && info.inputChannels > 0) {
info.duplexChannels = (info.outputChannels > info.inputChannels) ? info.inputChannels : info.outputChannels;
}
// Jack always uses 32-bit floats. // Jack always uses 32-bit floats.
info.nativeFormats.push_back(audio::format_float); info.nativeFormats.push_back(audio::format_float);
// Jack doesn't provide default devices so we'll use the first available one. // Jack doesn't provide default devices so we'll use the first available one.
if ( _device == 0 if (deviceID == 0) {
&& info.outputChannels > 0) { info.isDefault = true;
info.isDefaultOutput = true;
}
if ( _device == 0
&& info.inputChannels > 0) {
info.isDefaultInput = true;
} }
jack_client_close(client); jack_client_close(client);
info.probed = true;
return info; return info;
} }
@ -295,6 +288,8 @@ bool audio::orchestra::api::Jack::probeDeviceOpen(uint32_t _device,
const char **ports; const char **ports;
std::string port, previousPort, deviceName; std::string port, previousPort, deviceName;
uint32_t nPorts = 0, nDevices = 0; uint32_t nPorts = 0, nDevices = 0;
int32_t deviceID = _device/2;
bool isInput = _device%2==0?true:false;
ports = jack_get_ports(client, nullptr, nullptr, 0); ports = jack_get_ports(client, nullptr, nullptr, 0);
if (ports) { if (ports) {
// Parse the port names up to the first colon (:). // Parse the port names up to the first colon (:).
@ -305,7 +300,7 @@ bool audio::orchestra::api::Jack::probeDeviceOpen(uint32_t _device,
if (iColon != std::string::npos) { if (iColon != std::string::npos) {
port = port.substr(0, iColon); port = port.substr(0, iColon);
if (port != previousPort) { if (port != previousPort) {
if (nDevices == _device) { if (nDevices == deviceID) {
deviceName = port; deviceName = port;
} }
nDevices++; nDevices++;
@ -323,7 +318,9 @@ bool audio::orchestra::api::Jack::probeDeviceOpen(uint32_t _device,
// channels. Jack "input ports" equal RtAudio output channels. // channels. Jack "input ports" equal RtAudio output channels.
uint32_t nChannels = 0; uint32_t nChannels = 0;
uint64_t flag = JackPortIsInput; uint64_t flag = JackPortIsInput;
if (_mode == audio::orchestra::mode_input) flag = JackPortIsOutput; if (_mode == audio::orchestra::mode_input) {
flag = JackPortIsOutput;
}
ports = jack_get_ports(client, deviceName.c_str(), nullptr, flag); ports = jack_get_ports(client, deviceName.c_str(), nullptr, flag);
if (ports) { if (ports) {
while (ports[ nChannels ]) { while (ports[ nChannels ]) {

View File

@ -81,8 +81,8 @@ audio::orchestra::api::Pulse::~Pulse() {
} }
uint32_t audio::orchestra::api::Pulse::getDeviceCount() { uint32_t audio::orchestra::api::Pulse::getDeviceCount() {
#if 0 #if 1
std::vector<audio::orchestra::api::pulse::Element> list = audio::orchestra::api::pulse::getDeviceList(); std::vector<audio::orchestra::DeviceInfo> list = audio::orchestra::api::pulse::getDeviceList();
return list.size(); return list.size();
#else #else
return 1; return 1;
@ -90,23 +90,12 @@ uint32_t audio::orchestra::api::Pulse::getDeviceCount() {
} }
audio::orchestra::DeviceInfo audio::orchestra::api::Pulse::getDeviceInfo(uint32_t _device) { audio::orchestra::DeviceInfo audio::orchestra::api::Pulse::getDeviceInfo(uint32_t _device) {
audio::orchestra::DeviceInfo info; std::vector<audio::orchestra::DeviceInfo> list = audio::orchestra::api::pulse::getDeviceList();
//std::vector<audio::orchestra::api::pulse::Element> list = audio::orchestra::api::pulse::getDeviceList(); if (_device >= list.size()) {
// TODO : Do it better... it is a little poor ... ATA_ERROR("Request device out of IDs:" << _device << " >= " << list.size());
info.probed = true; return audio::orchestra::DeviceInfo();
info.name = "PulseAudio";
info.outputChannels = 2;
info.inputChannels = 2;
info.duplexChannels = 0;
info.isDefaultOutput = true;
info.isDefaultInput = true;
for (const uint32_t *sr = SUPPORTED_SAMPLERATES; *sr; ++sr) {
info.sampleRates.push_back(*sr);
} }
info.nativeFormats.push_back(audio::format_int16); return list[_device];
info.nativeFormats.push_back(audio::format_int32);
info.nativeFormats.push_back(audio::format_float);
return info;
} }
static void pulseaudio_callback(void* _userData) { static void pulseaudio_callback(void* _userData) {

View File

@ -9,6 +9,10 @@
#include <pulse/pulseaudio.h> #include <pulse/pulseaudio.h>
#include <audio/orchestra/api/PulseDeviceList.h> #include <audio/orchestra/api/PulseDeviceList.h>
#include <audio/orchestra/debug.h> #include <audio/orchestra/debug.h>
#include <audio/Time.h>
#include <audio/Duration.h>
#include <audio/format.h>
#include <etk/stdTools.h>
// This callback gets called when our context changes state. We really only // This callback gets called when our context changes state. We really only
// care about when it's ready or if it has failed // care about when it's ready or if it has failed
@ -19,87 +23,259 @@ static void callbackStateMachine(pa_context* _contex, void *_userdata) {
switch (state) { switch (state) {
// There are just here for reference // There are just here for reference
case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_UNCONNECTED:
ATA_INFO("pulse state: PA_CONTEXT_UNCONNECTED"); ATA_VERBOSE("pulse state: PA_CONTEXT_UNCONNECTED");
break; break;
case PA_CONTEXT_CONNECTING: case PA_CONTEXT_CONNECTING:
ATA_INFO("pulse state: PA_CONTEXT_CONNECTING"); ATA_VERBOSE("pulse state: PA_CONTEXT_CONNECTING");
break; break;
case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_AUTHORIZING:
ATA_INFO("pulse state: PA_CONTEXT_AUTHORIZING"); ATA_VERBOSE("pulse state: PA_CONTEXT_AUTHORIZING");
break; break;
case PA_CONTEXT_SETTING_NAME: case PA_CONTEXT_SETTING_NAME:
ATA_INFO("pulse state: PA_CONTEXT_SETTING_NAME"); ATA_VERBOSE("pulse state: PA_CONTEXT_SETTING_NAME");
break; break;
default: default:
ATA_INFO("pulse state: default"); ATA_VERBOSE("pulse state: default");
break; break;
case PA_CONTEXT_FAILED: case PA_CONTEXT_FAILED:
*pulseAudioReady = 2; *pulseAudioReady = 2;
ATA_INFO("pulse state: PA_CONTEXT_FAILED"); ATA_VERBOSE("pulse state: PA_CONTEXT_FAILED");
break; break;
case PA_CONTEXT_TERMINATED: case PA_CONTEXT_TERMINATED:
*pulseAudioReady = 2; *pulseAudioReady = 2;
ATA_INFO("pulse state: PA_CONTEXT_TERMINATED"); ATA_VERBOSE("pulse state: PA_CONTEXT_TERMINATED");
break; break;
case PA_CONTEXT_READY: case PA_CONTEXT_READY:
*pulseAudioReady = 1; *pulseAudioReady = 1;
ATA_INFO("pulse state: PA_CONTEXT_READY"); ATA_VERBOSE("pulse state: PA_CONTEXT_READY");
break; break;
} }
} }
static audio::format getFormatFromPulseFormat(enum pa_sample_format _format) {
switch (_format) {
case PA_SAMPLE_U8:
return audio::format_int8;
break;
case PA_SAMPLE_ALAW:
ATA_ERROR("Not supported: uint8_t a-law");
return audio::format_unknow;
case PA_SAMPLE_ULAW:
ATA_ERROR("Not supported: uint8_t mu-law");
return audio::format_unknow;
case PA_SAMPLE_S16LE:
return audio::format_int16;
break;
case PA_SAMPLE_S16BE:
return audio::format_int16;
break;
case PA_SAMPLE_FLOAT32LE:
return audio::format_float;
break;
case PA_SAMPLE_FLOAT32BE:
return audio::format_float;
break;
case PA_SAMPLE_S32LE:
return audio::format_int32;
break;
case PA_SAMPLE_S32BE:
return audio::format_int32;
break;
case PA_SAMPLE_S24LE:
return audio::format_int24;
break;
case PA_SAMPLE_S24BE:
return audio::format_int24;
break;
case PA_SAMPLE_S24_32LE:
return audio::format_int24_on_int32;
break;
case PA_SAMPLE_S24_32BE:
return audio::format_int24_on_int32;
break;
case PA_SAMPLE_INVALID:
case PA_SAMPLE_MAX:
ATA_ERROR("Not supported: invalid");
return audio::format_unknow;
}
ATA_ERROR("Not supported: UNKNOW flag...");
return audio::format_unknow;
}
static std::vector<audio::channel> getChannelOrderFromPulseChannel(const struct pa_channel_map& _map) {
std::vector<audio::channel> out;
for (int32_t iii=0; iii<_map.channels; ++iii) {
switch(_map.map[iii]) {
default:
case PA_CHANNEL_POSITION_MAX:
case PA_CHANNEL_POSITION_INVALID:
out.push_back(audio::channel_unknow);
break;
case PA_CHANNEL_POSITION_MONO:
case PA_CHANNEL_POSITION_FRONT_CENTER:
out.push_back(audio::channel_frontCenter);
break;
case PA_CHANNEL_POSITION_FRONT_LEFT:
out.push_back(audio::channel_frontLeft);
break;
case PA_CHANNEL_POSITION_FRONT_RIGHT:
out.push_back(audio::channel_frontRight);
break;
case PA_CHANNEL_POSITION_REAR_CENTER:
out.push_back(audio::channel_rearCenter);
break;
case PA_CHANNEL_POSITION_REAR_LEFT:
out.push_back(audio::channel_rearLeft);
break;
case PA_CHANNEL_POSITION_REAR_RIGHT:
out.push_back(audio::channel_rearRight);
break;
case PA_CHANNEL_POSITION_LFE:
out.push_back(audio::channel_lfe);
break;
case PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER:
out.push_back(audio::channel_centerLeft);
break;
case PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER:
out.push_back(audio::channel_centerRight);
break;
case PA_CHANNEL_POSITION_SIDE_LEFT:
out.push_back(audio::channel_topCenterLeft);
break;
case PA_CHANNEL_POSITION_SIDE_RIGHT:
out.push_back(audio::channel_topCenterRight);
break;
case PA_CHANNEL_POSITION_TOP_CENTER:
case PA_CHANNEL_POSITION_TOP_FRONT_CENTER:
out.push_back(audio::channel_topFrontCenter);
break;
case PA_CHANNEL_POSITION_TOP_FRONT_LEFT:
out.push_back(audio::channel_topFrontLeft);
break;
case PA_CHANNEL_POSITION_TOP_FRONT_RIGHT:
out.push_back(audio::channel_topFrontRight);
break;
case PA_CHANNEL_POSITION_TOP_REAR_LEFT:
out.push_back(audio::channel_topRearLeft);
break;
case PA_CHANNEL_POSITION_TOP_REAR_RIGHT:
out.push_back(audio::channel_topRearRight);
break;
case PA_CHANNEL_POSITION_TOP_REAR_CENTER:
out.push_back(audio::channel_topRearCenter);
break;
case PA_CHANNEL_POSITION_AUX0: out.push_back(audio::channel_aux0); break;
case PA_CHANNEL_POSITION_AUX1: out.push_back(audio::channel_aux1); break;
case PA_CHANNEL_POSITION_AUX2: out.push_back(audio::channel_aux2); break;
case PA_CHANNEL_POSITION_AUX3: out.push_back(audio::channel_aux3); break;
case PA_CHANNEL_POSITION_AUX4: out.push_back(audio::channel_aux4); break;
case PA_CHANNEL_POSITION_AUX5: out.push_back(audio::channel_aux5); break;
case PA_CHANNEL_POSITION_AUX6: out.push_back(audio::channel_aux6); break;
case PA_CHANNEL_POSITION_AUX7: out.push_back(audio::channel_aux7); break;
case PA_CHANNEL_POSITION_AUX8: out.push_back(audio::channel_aux8); break;
case PA_CHANNEL_POSITION_AUX9: out.push_back(audio::channel_aux9); break;
case PA_CHANNEL_POSITION_AUX10: out.push_back(audio::channel_aux10); break;
case PA_CHANNEL_POSITION_AUX11: out.push_back(audio::channel_aux11); break;
case PA_CHANNEL_POSITION_AUX12: out.push_back(audio::channel_aux12); break;
case PA_CHANNEL_POSITION_AUX13: out.push_back(audio::channel_aux13); break;
case PA_CHANNEL_POSITION_AUX14: out.push_back(audio::channel_aux14); break;
case PA_CHANNEL_POSITION_AUX15: out.push_back(audio::channel_aux15); break;
case PA_CHANNEL_POSITION_AUX16: out.push_back(audio::channel_aux16); break;
case PA_CHANNEL_POSITION_AUX17: out.push_back(audio::channel_aux17); break;
case PA_CHANNEL_POSITION_AUX18: out.push_back(audio::channel_aux18); break;
case PA_CHANNEL_POSITION_AUX19: out.push_back(audio::channel_aux19); break;
case PA_CHANNEL_POSITION_AUX20: out.push_back(audio::channel_aux20); break;
case PA_CHANNEL_POSITION_AUX21: out.push_back(audio::channel_aux21); break;
case PA_CHANNEL_POSITION_AUX22: out.push_back(audio::channel_aux22); break;
case PA_CHANNEL_POSITION_AUX23: out.push_back(audio::channel_aux23); break;
case PA_CHANNEL_POSITION_AUX24: out.push_back(audio::channel_aux24); break;
case PA_CHANNEL_POSITION_AUX25: out.push_back(audio::channel_aux25); break;
case PA_CHANNEL_POSITION_AUX26: out.push_back(audio::channel_aux26); break;
case PA_CHANNEL_POSITION_AUX27: out.push_back(audio::channel_aux27); break;
case PA_CHANNEL_POSITION_AUX28: out.push_back(audio::channel_aux28); break;
case PA_CHANNEL_POSITION_AUX29: out.push_back(audio::channel_aux29); break;
case PA_CHANNEL_POSITION_AUX30: out.push_back(audio::channel_aux30); break;
case PA_CHANNEL_POSITION_AUX31: out.push_back(audio::channel_aux31); break;
}
}
return out;
}
// Callback on getting data from pulseaudio: // Callback on getting data from pulseaudio:
static void callbackGetSinkList(pa_context* _contex, const pa_sink_info* _info, int _eol, void* _userdata) { static void callbackGetSinkList(pa_context* _contex, const pa_sink_info* _info, int _eol, void* _userdata) {
std::vector<audio::orchestra::api::pulse::Element>* list = static_cast<std::vector<audio::orchestra::api::pulse::Element>*>(_userdata); std::vector<audio::orchestra::DeviceInfo>* list = static_cast<std::vector<audio::orchestra::DeviceInfo>*>(_userdata);
// If eol is set to a positive number, you're at the end of the list // If eol is set to a positive number, you're at the end of the list
if (_eol > 0) { if (_eol > 0) {
return; return;
} }
ATA_INFO("find output : " << _info->name); audio::orchestra::DeviceInfo info;
list->push_back(audio::orchestra::api::pulse::Element(_info->index, false, _info->name, _info->description)); info.input = false;
info.name = _info->name;
info.desc = _info->description;
info.sampleRates.push_back(_info->sample_spec.rate);
info.nativeFormats.push_back(getFormatFromPulseFormat(_info->sample_spec.format));
info.channels = getChannelOrderFromPulseChannel(_info->channel_map);
ATA_VERBOSE("plop=" << _info->index << " " << _info->name);
//ATA_DEBUG(" ports=" << _info->n_ports);
list->push_back(info);
} }
// allback to get data from pulseaudio: // allback to get data from pulseaudio:
static void callbackGetSourceList(pa_context* _contex, const pa_source_info* _info, int _eol, void* _userdata) { static void callbackGetSourceList(pa_context* _contex, const pa_source_info* _info, int _eol, void* _userdata) {
std::vector<audio::orchestra::api::pulse::Element>* list = static_cast<std::vector<audio::orchestra::api::pulse::Element>*>(_userdata); std::vector<audio::orchestra::DeviceInfo>* list = static_cast<std::vector<audio::orchestra::DeviceInfo>*>(_userdata);
if (_eol > 0) { if (_eol > 0) {
return; return;
} }
ATA_INFO("find input : " << _info->name); audio::orchestra::DeviceInfo info;
list->push_back(audio::orchestra::api::pulse::Element(_info->index, true, _info->name, _info->description)); info.input = true;
info.name = _info->name;
info.desc = _info->description;
info.sampleRates.push_back(_info->sample_spec.rate);
info.nativeFormats.push_back(getFormatFromPulseFormat(_info->sample_spec.format));
info.channels = getChannelOrderFromPulseChannel(_info->channel_map);
ATA_VERBOSE("plop=" << _info->index << " " << _info->name);
list->push_back(info);
} }
std::vector<audio::orchestra::api::pulse::Element> audio::orchestra::api::pulse::getDeviceList() { // to not update all the time ...
static std::vector<audio::orchestra::DeviceInfo> pulseAudioListOfDevice;
static audio::Time pulseAudioListOfDeviceTime;
std::vector<audio::orchestra::DeviceInfo> audio::orchestra::api::pulse::getDeviceList() {
audio::Duration delta = audio::Time::now() - pulseAudioListOfDeviceTime;
if (delta < audio::Duration(30,0)) {
return pulseAudioListOfDevice;
}
// Define our pulse audio loop and connection variables // Define our pulse audio loop and connection variables
pa_mainloop* pulseAudioMainLoop; pa_mainloop* pulseAudioMainLoop;
pa_mainloop_api* pulseAudioMainLoopAPI; pa_mainloop_api* pulseAudioMainLoopAPI;
pa_operation* pulseAudioOperation; pa_operation* pulseAudioOperation;
pa_context* pulseAudioContex; pa_context* pulseAudioContex;
pa_context_flags_t pulseAudioFlags; pa_context_flags_t pulseAudioFlags = PA_CONTEXT_NOAUTOSPAWN;
std::vector<audio::orchestra::api::pulse::Element> out; std::vector<audio::orchestra::DeviceInfo>& out = pulseAudioListOfDevice;
out.clear();
// We'll need these state variables to keep track of our requests // We'll need these state variables to keep track of our requests
int state = 0; int state = 0;
int pulseAudioReady = 0; int pulseAudioReady = 0;
// Create a mainloop API and connection to the default server // Create a mainloop API and connection to the default server
pulseAudioMainLoop = pa_mainloop_new(); pulseAudioMainLoop = pa_mainloop_new();
pulseAudioMainLoopAPI = pa_mainloop_get_api(pulseAudioMainLoop); pulseAudioMainLoopAPI = pa_mainloop_get_api(pulseAudioMainLoop);
pulseAudioContex = pa_context_new(pulseAudioMainLoopAPI, "test"); pulseAudioContex = pa_context_new(pulseAudioMainLoopAPI, "orchestraPulseCount");
// This function connects to the pulse server
pa_context_connect(pulseAudioContex, NULL, pulseAudioFlags, NULL);
// If there's an error, the callback will set pulseAudioReady // If there's an error, the callback will set pulseAudioReady
pa_context_set_state_callback(pulseAudioContex, callbackStateMachine, &pulseAudioReady); pa_context_set_state_callback(pulseAudioContex, callbackStateMachine, &pulseAudioReady);
ATA_INFO("start main loop..."); // This function connects to the pulse server
while (true) { pa_context_connect(pulseAudioContex, NULL, pulseAudioFlags, NULL);
ATA_INFO("loop"); bool playLoop = true;
while (playLoop == true) {
// We can't do anything until PA is ready, so just iterate the mainloop // We can't do anything until PA is ready, so just iterate the mainloop
// and continue // and continue
if (pulseAudioReady == 0) { if (pulseAudioReady == 0) {
ATA_INFO("Pulse not ready");
pa_mainloop_iterate(pulseAudioMainLoop, 1, nullptr); pa_mainloop_iterate(pulseAudioMainLoop, 1, nullptr);
continue; continue;
} }
// We couldn't get a connection to the server, so exit out // We couldn't get a connection to the server, so exit out
if (pulseAudioReady == 2) { if (pulseAudioReady == 2) {
ATA_INFO("pulse not ready");
pa_context_disconnect(pulseAudioContex); pa_context_disconnect(pulseAudioContex);
pa_context_unref(pulseAudioContex); pa_context_unref(pulseAudioContex);
pa_mainloop_free(pulseAudioMainLoop); pa_mainloop_free(pulseAudioMainLoop);
@ -111,7 +287,7 @@ std::vector<audio::orchestra::api::pulse::Element> audio::orchestra::api::pulse:
switch (state) { switch (state) {
// State 0: we haven't done anything yet // State 0: we haven't done anything yet
case 0: case 0:
ATA_INFO("Request sink list"); ATA_DEBUG("Request sink list");
pulseAudioOperation = pa_context_get_sink_info_list(pulseAudioContex, pulseAudioOperation = pa_context_get_sink_info_list(pulseAudioContex,
callbackGetSinkList, callbackGetSinkList,
&out); &out);
@ -123,7 +299,7 @@ std::vector<audio::orchestra::api::pulse::Element> audio::orchestra::api::pulse:
// along to the next state // along to the next state
if (pa_operation_get_state(pulseAudioOperation) == PA_OPERATION_DONE) { if (pa_operation_get_state(pulseAudioOperation) == PA_OPERATION_DONE) {
pa_operation_unref(pulseAudioOperation); pa_operation_unref(pulseAudioOperation);
ATA_INFO("Request sources list"); ATA_DEBUG("Request sources list");
pulseAudioOperation = pa_context_get_source_info_list(pulseAudioContex, pulseAudioOperation = pa_context_get_source_info_list(pulseAudioContex,
callbackGetSourceList, callbackGetSourceList,
&out); &out);
@ -132,13 +308,14 @@ std::vector<audio::orchestra::api::pulse::Element> audio::orchestra::api::pulse:
break; break;
case 2: case 2:
if (pa_operation_get_state(pulseAudioOperation) == PA_OPERATION_DONE) { if (pa_operation_get_state(pulseAudioOperation) == PA_OPERATION_DONE) {
ATA_INFO("All is done"); ATA_DEBUG("All is done");
// Now we're done, clean up and disconnect and return // Now we're done, clean up and disconnect and return
pa_operation_unref(pulseAudioOperation); pa_operation_unref(pulseAudioOperation);
pa_context_disconnect(pulseAudioContex); pa_context_disconnect(pulseAudioContex);
pa_context_unref(pulseAudioContex); pa_context_unref(pulseAudioContex);
pa_mainloop_free(pulseAudioMainLoop); pa_mainloop_free(pulseAudioMainLoop);
return out; playLoop = false;
break;
} }
break; break;
default: default:
@ -147,7 +324,32 @@ std::vector<audio::orchestra::api::pulse::Element> audio::orchestra::api::pulse:
return out; return out;
} }
// Iterate the main loop .. // Iterate the main loop ..
pa_mainloop_iterate(pulseAudioMainLoop, 1, nullptr); if (playLoop == true) {
pa_mainloop_iterate(pulseAudioMainLoop, 1, nullptr);
}
}
// TODO: need to do it better ...
// set default device:
int32_t idInput = -1;
int32_t idOutput = -1;
for (int32_t iii=0; iii<out.size(); ++iii) {
if (out[iii].input == true) {
if (idInput != -1) {
continue;
}
if (etk::end_with(out[iii].name, ".monitor", false) == false) {
idInput = iii;
out[iii].isDefault = true;
}
} else {
if (idOutput != -1) {
continue;
}
if (etk::end_with(out[iii].name, ".monitor", false) == false) {
idOutput = iii;
out[iii].isDefault = true;
}
}
} }
return out; return out;
} }

View File

@ -9,39 +9,13 @@
#define __AUDIO_ORCHESTRA_API_PULSE_DEVICE_H__ #define __AUDIO_ORCHESTRA_API_PULSE_DEVICE_H__
#include <etk/types.h> #include <etk/types.h>
#include <audio/orchestra/DeviceInfo.h>
namespace audio { namespace audio {
namespace orchestra { namespace orchestra {
namespace api { namespace api {
namespace pulse { namespace pulse {
class Element { std::vector<audio::orchestra::DeviceInfo> getDeviceList();
private:
size_t m_index;
bool m_input;
std::string m_name;
std::string m_description;
public:
Element(size_t _index, bool _input, const std::string& _name, const std::string& _desc) :
m_index(_index),
m_input(_input),
m_name(_name),
m_description(_desc) {
// nothing to do...
}
size_t getIndex() const {
return m_index;
}
bool isInput() const {
return m_input;
}
const std::string& getName() const {
return m_name;
}
const std::string& getDescription() const {
return m_description;
}
};
std::vector<audio::orchestra::api::pulse::Element> getDeviceList();
} }
} }
} }