JackPortAudioDevices.cpp
/*
Copyright (C) 2008-2011 Romain Moret at Grame
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "JackPortAudioDevices.h"
#include "JackError.h"
#include <stdlib.h>
using namespace std;
PortAudioDevices::PortAudioDevices()
{
PaError err;
PaDeviceIndex id;
jack_log("Initializing PortAudio...");
if ((err = Pa_Initialize()) == paNoError) {
fNumHostApi = Pa_GetHostApiCount();
fNumDevice = Pa_GetDeviceCount();
fDeviceInfo = new PaDeviceInfo*[fNumDevice];
for (id = 0; id < fNumDevice; id++) {
fDeviceInfo[id] = const_cast<PaDeviceInfo*>(Pa_GetDeviceInfo(id));
}
fHostName = new string[fNumHostApi];
for (id = 0; id < fNumHostApi; id++) {
fHostName[id] = string(Pa_GetHostApiInfo(id)->name);
}
} else {
jack_error("JackPortAudioDriver::Pa_Initialize error = %s", Pa_GetErrorText(err));
}
}
PortAudioDevices::~PortAudioDevices()
{
jack_log("Terminate PortAudio...");
Pa_Terminate();
delete[] fDeviceInfo;
delete[] fHostName;
}
PaDeviceIndex PortAudioDevices::GetNumDevice()
{
return fNumDevice;
}
PaDeviceInfo* PortAudioDevices::GetDeviceInfo(PaDeviceIndex id)
{
return fDeviceInfo[id];
}
string PortAudioDevices::GetDeviceName(PaDeviceIndex id)
{
return string(fDeviceInfo[id]->name);
}
string PortAudioDevices::GetHostFromDevice(PaDeviceInfo* device)
{
return fHostName[device->hostApi];
}
string PortAudioDevices::GetHostFromDevice(PaDeviceIndex id)
{
return fHostName[fDeviceInfo[id]->hostApi];
}
string PortAudioDevices::GetFullName(PaDeviceIndex id)
{
string hostname = GetHostFromDevice(id);
string devicename = GetDeviceName(id);
//some hostname are quite long...use shortcuts
if (hostname.compare("Windows DirectSound") == 0) {
hostname = string("DirectSound");
}
return (hostname + "::" + devicename);
}
string PortAudioDevices::GetFullName(std::string hostname, std::string devicename)
{
//some hostname are quite long...use shortcuts
if (hostname.compare("Windows DirectSound") == 0) {
hostname = string("DirectSound");
}
return (hostname + "::" + devicename);
}
PaDeviceInfo* PortAudioDevices::GetDeviceFromFullName(string fullname, PaDeviceIndex& id, bool isInput)
{
PaDeviceInfo* ret = NULL;
//no driver to find
if (fullname.size() == 0) {
return NULL;
}
//first get host and device names from fullname
string::size_type separator = fullname.find("::", 0);
if (separator == string::npos) {
return NULL;
}
char* hostname = (char*)malloc(separator + 9);
fill_n(hostname, separator + 9, 0);
fullname.copy(hostname, separator);
//we need the entire hostname, replace shortcuts
if (strcmp(hostname, "DirectSound") == 0) {
strcpy(hostname, "Windows DirectSound");
}
string devicename = fullname.substr(separator + 2);
//then find the corresponding device
for (PaDeviceIndex dev_id = 0; dev_id < fNumDevice; dev_id++) {
bool flag = (isInput) ? (fDeviceInfo[dev_id]->maxInputChannels > 0) : (fDeviceInfo[dev_id]->maxOutputChannels > 0);
if ((GetHostFromDevice(dev_id).compare(hostname) == 0)
&& (GetDeviceName(dev_id).compare(devicename) == 0)
&& flag) {
id = dev_id;
ret = fDeviceInfo[dev_id];
}
}
free(hostname);
return ret;
}
void PortAudioDevices::PrintSupportedStandardSampleRates(const PaStreamParameters* inputParameters, const PaStreamParameters* outputParameters)
{
static double standardSampleRates[] =
{
8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated list */
};
int i, printCount;
PaError err;
printCount = 0;
for (i = 0; standardSampleRates[i] > 0; i++) {
err = Pa_IsFormatSupported(inputParameters, outputParameters, standardSampleRates[i]);
if (err == paFormatIsSupported) {
if (printCount == 0) {
jack_info("\t%8.2f", standardSampleRates[i]);
printCount = 1;
} else if (printCount == 4) {
jack_info(",\n\t%8.2f", standardSampleRates[i]);
printCount = 1;
} else {
jack_info(", %8.2f", standardSampleRates[i]);
++printCount;
}
}
}
if (!printCount) {
jack_info("None");
} else {
jack_info("\n");
}
}
int PortAudioDevices::GetInputDeviceFromName(const char* devicename, PaDeviceIndex& id, int& max_input)
{
string fullname = string(devicename);
PaDeviceInfo* device = GetDeviceFromFullName(fullname, id, true);
if (device) {
max_input = device->maxInputChannels;
} else {
id = Pa_GetDefaultInputDevice();
if (fullname.size()) {
jack_error("Can't open %s, PortAudio will use default input device.", devicename);
}
if (id == paNoDevice) {
return -1;
}
max_input = GetDeviceInfo(id)->maxInputChannels;
}
return id;
}
int PortAudioDevices::GetOutputDeviceFromName(const char* devicename, PaDeviceIndex& id, int& max_output)
{
string fullname = string(devicename);
PaDeviceInfo* device = GetDeviceFromFullName(fullname, id, false);
if (device) {
max_output = device->maxOutputChannels;
} else {
id = Pa_GetDefaultOutputDevice();
if (fullname.size()) {
jack_error("Can't open %s, PortAudio will use default output device.", devicename);
}
if (id == paNoDevice) {
return -1;
}
max_output = GetDeviceInfo(id)->maxOutputChannels;
}
return id;
}
int PortAudioDevices::GetPreferredBufferSize(PaDeviceIndex id)
{
#if defined(WIN32) && defined(HAVE_ASIO)
/* ASIO specific latency information */
if (Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->type == paASIO) {
long minLatency, maxLatency, preferredLatency, granularity;
PaAsio_GetAvailableBufferSizes(id, &minLatency, &maxLatency, &preferredLatency, &granularity);
jack_info("ASIO minimum buffer size = %ld", minLatency);
jack_info("ASIO maximum buffer size = %ld", maxLatency);
jack_info("ASIO preferred buffer size = %ld", preferredLatency);
if (granularity == -1) {
jack_info("ASIO buffer granularity = power of 2");
} else {
jack_info("ASIO buffer granularity = %ld", granularity);
}
return preferredLatency;
} else
#endif
{
return 512; // Non ASIO driver, returns generic value
}
}
void PortAudioDevices::DisplayDevicesNames()
{
PaDeviceIndex id;
PaStreamParameters inputParameters, outputParameters;
jack_info("********************** Devices list, %d detected **********************", fNumDevice);
for (id = 0; id < fNumDevice; id++) {
jack_info("-------- device #%d ------------------------------------------------", id);
if (id == Pa_GetDefaultInputDevice()) {
jack_info("[ Default Input ]");
} else if (id == Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->defaultInputDevice) {
const PaHostApiInfo *host_info = Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi);
jack_info("[ Default %s Input ]", host_info->name);
}
if (id == Pa_GetDefaultOutputDevice()) {
jack_info("[ Default Output ]");
} else if (id == Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->defaultOutputDevice) {
const PaHostApiInfo *host_info = Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi);
jack_info("[ Default %s Output ]", host_info->name);
}
/* print device info fields */
jack_info("Name = %s", GetFullName(id).c_str());
jack_info("Max inputs = %d", fDeviceInfo[id]->maxInputChannels);
jack_info("Max outputs = %d", fDeviceInfo[id]->maxOutputChannels);
#if defined(WIN32) && defined(HAVE_ASIO)
/* ASIO specific latency information */
if (Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->type == paASIO) {
long minLatency, maxLatency, preferredLatency, granularity;
PaAsio_GetAvailableBufferSizes(id, &minLatency, &maxLatency, &preferredLatency, &granularity);
jack_info("ASIO minimum buffer size = %ld", minLatency);
jack_info("ASIO maximum buffer size = %ld", maxLatency);
jack_info("ASIO preferred buffer size = %ld", preferredLatency);
if (granularity == -1) {
jack_info("ASIO buffer granularity = power of 2");
} else {
jack_info("ASIO buffer granularity = %ld", granularity);
}
}
#endif
jack_info("Default sample rate = %8.2f", fDeviceInfo[id]->defaultSampleRate);
/* poll for standard sample rates */
inputParameters.device = id;
inputParameters.channelCount = fDeviceInfo[id]->maxInputChannels;
inputParameters.sampleFormat = paInt16;
inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
inputParameters.hostApiSpecificStreamInfo = NULL;
outputParameters.device = id;
outputParameters.channelCount = fDeviceInfo[id]->maxOutputChannels;
outputParameters.sampleFormat = paInt16;
outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
outputParameters.hostApiSpecificStreamInfo = NULL;
}
jack_info("**************************** End of list ****************************");
}
bool PortAudioDevices::IsDuplex(PaDeviceIndex id)
{
//does the device has in and out facilities
if (fDeviceInfo[id]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels) {
return true;
}
//else is another complementary device ? (search in devices with the same name)
for (PaDeviceIndex i = 0; i < fNumDevice; i++) {
if ((i != id) && (GetDeviceName(i) == GetDeviceName(id))) {
if ((fDeviceInfo[i]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels)
|| (fDeviceInfo[i]->maxOutputChannels && fDeviceInfo[id]->maxInputChannels)) {
return true;
}
}
}
//then the device isn't full duplex
return false;
}
Pa_Initialize
PaError Pa_Initialize(void)
Pa_GetDefaultInputDevice
PaDeviceIndex Pa_GetDefaultInputDevice(void)
Pa_GetDeviceCount
PaDeviceIndex Pa_GetDeviceCount(void)
PaDeviceInfo
Definition: portaudio.h:437
std
jack_error
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:92
PaHostApiInfo::name
const char * name
Definition: portaudio.h:242
jack_info
SERVER_EXPORT void jack_info(const char *fmt,...)
Definition: JackError.cpp:100
PaHostApiInfo
Definition: portaudio.h:236
Pa_IsFormatSupported
PaError Pa_IsFormatSupported(const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate)
PaDeviceIndex
int PaDeviceIndex
Definition: portaudio.h:153
PaStreamParameters::hostApiSpecificStreamInfo
void * hostApiSpecificStreamInfo
Definition: portaudio.h:515
PaAsio_GetAvailableBufferSizes
PaError PaAsio_GetAvailableBufferSizes(PaDeviceIndex device, long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity)
PaStreamParameters::sampleFormat
PaSampleFormat sampleFormat
Definition: portaudio.h:495
PaError
int PaError
Definition: portaudio.h:63
paInt16
#define paInt16
Definition: portaudio.h:427
PaStreamParameters::suggestedLatency
PaTime suggestedLatency
Definition: portaudio.h:508
PaStreamParameters
Definition: portaudio.h:475
paNoDevice
#define paNoDevice
Definition: portaudio.h:161
Pa_GetHostApiInfo
const PaHostApiInfo * Pa_GetHostApiInfo(PaHostApiIndex hostApi)
Pa_GetHostApiCount
PaHostApiIndex Pa_GetHostApiCount(void)
Pa_GetDeviceInfo
const PaDeviceInfo * Pa_GetDeviceInfo(PaDeviceIndex device)
Pa_GetDefaultOutputDevice
PaDeviceIndex Pa_GetDefaultOutputDevice(void)
PaStreamParameters::device
PaDeviceIndex device
Definition: portaudio.h:482
paFormatIsSupported
#define paFormatIsSupported
Definition: portaudio.h:522
Pa_GetErrorText
const char * Pa_GetErrorText(PaError errorCode)
PaStreamParameters::channelCount
int channelCount
Definition: portaudio.h:489
jack_log
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:108
Pa_Terminate
PaError Pa_Terminate(void)