/* 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 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(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; }