Jack2 1.9.10

JackPortAudioDevices.cpp

00001 /*
00002 Copyright (C) 2008-2011 Romain Moret at Grame
00003 
00004 This program is free software; you can redistribute it and/or modify
00005 it under the terms of the GNU General Public License as published by
00006 the Free Software Foundation; either version 2 of the License, or
00007 (at your option) any later version.
00008 
00009 This program is distributed in the hope that it will be useful,
00010 but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 GNU General Public License for more details.
00013 
00014 You should have received a copy of the GNU General Public License
00015 along with this program; if not, write to the Free Software
00016 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00017 
00018 */
00019 
00020 #include "JackPortAudioDevices.h"
00021 #include "JackError.h"
00022 #include <stdlib.h>
00023 
00024 using namespace std;
00025 
00026 PortAudioDevices::PortAudioDevices()
00027 {
00028     PaError err;
00029         PaDeviceIndex id;
00030         jack_log("Initializing PortAudio...");
00031     if ((err = Pa_Initialize()) == paNoError) {
00032         fNumHostApi = Pa_GetHostApiCount();
00033         fNumDevice = Pa_GetDeviceCount();
00034         fDeviceInfo = new PaDeviceInfo*[fNumDevice];
00035         for (id = 0; id < fNumDevice; id++) {
00036             fDeviceInfo[id] = const_cast<PaDeviceInfo*>(Pa_GetDeviceInfo(id));
00037         }
00038         fHostName = new string[fNumHostApi];
00039         for (id = 0; id < fNumHostApi; id++) {
00040             fHostName[id] = string(Pa_GetHostApiInfo(id)->name);
00041         }
00042     } else {
00043                 jack_error("JackPortAudioDriver::Pa_Initialize error = %s", Pa_GetErrorText(err));
00044     }
00045 }
00046 
00047 PortAudioDevices::~PortAudioDevices()
00048 {
00049     jack_log("Terminate PortAudio...");
00050     Pa_Terminate();
00051 
00052     delete[] fDeviceInfo;
00053     delete[] fHostName;
00054 }
00055 
00056 PaDeviceIndex PortAudioDevices::GetNumDevice()
00057 {
00058     return fNumDevice;
00059 }
00060 
00061 PaDeviceInfo* PortAudioDevices::GetDeviceInfo(PaDeviceIndex id)
00062 {
00063     return fDeviceInfo[id];
00064 }
00065 
00066 string PortAudioDevices::GetDeviceName(PaDeviceIndex id)
00067 {
00068     return string(fDeviceInfo[id]->name);
00069 }
00070 
00071 string PortAudioDevices::GetHostFromDevice(PaDeviceInfo* device)
00072 {
00073     return fHostName[device->hostApi];
00074 }
00075 
00076 string PortAudioDevices::GetHostFromDevice(PaDeviceIndex id)
00077 {
00078     return fHostName[fDeviceInfo[id]->hostApi];
00079 }
00080 
00081 string PortAudioDevices::GetFullName(PaDeviceIndex id)
00082 {
00083     string hostname = GetHostFromDevice(id);
00084     string devicename = GetDeviceName(id);
00085     //some hostname are quite long...use shortcuts
00086     if (hostname.compare("Windows DirectSound") == 0) {
00087         hostname = string("DirectSound");
00088     }
00089     return (hostname + "::" + devicename);
00090 }
00091 
00092 string PortAudioDevices::GetFullName(std::string hostname, std::string devicename)
00093 {
00094     //some hostname are quite long...use shortcuts
00095     if (hostname.compare("Windows DirectSound") == 0) {
00096         hostname = string("DirectSound");
00097     }
00098     return (hostname + "::" + devicename);
00099 }
00100 
00101 PaDeviceInfo* PortAudioDevices::GetDeviceFromFullName(string fullname, PaDeviceIndex& id, bool isInput)
00102 {
00103     PaDeviceInfo* ret = NULL;
00104     //no driver to find
00105     if (fullname.size() == 0) {
00106         return NULL;
00107     }
00108     //first get host and device names from fullname
00109     string::size_type separator = fullname.find("::", 0);
00110 
00111     if (separator == string::npos) {
00112         return NULL;
00113     }
00114 
00115     char* hostname = (char*)malloc(separator + 9);
00116     fill_n(hostname, separator + 9, 0);
00117     fullname.copy(hostname, separator);
00118 
00119     //we need the entire hostname, replace shortcuts
00120     if (strcmp(hostname, "DirectSound") == 0) {
00121         strcpy(hostname, "Windows DirectSound");
00122     }
00123     string devicename = fullname.substr(separator + 2);
00124     //then find the corresponding device
00125     for (PaDeviceIndex dev_id = 0; dev_id < fNumDevice; dev_id++) {
00126         bool flag = (isInput) ? (fDeviceInfo[dev_id]->maxInputChannels > 0) : (fDeviceInfo[dev_id]->maxOutputChannels > 0);
00127         if ((GetHostFromDevice(dev_id).compare(hostname) == 0)
00128             && (GetDeviceName(dev_id).compare(devicename) == 0)
00129             && flag) {
00130             id = dev_id;
00131             ret = fDeviceInfo[dev_id];
00132         }
00133     }
00134         free(hostname);
00135     return ret;
00136 }
00137 
00138 void PortAudioDevices::PrintSupportedStandardSampleRates(const PaStreamParameters* inputParameters, const PaStreamParameters* outputParameters)
00139 {
00140     static double standardSampleRates[] =
00141     {
00142         8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
00143         44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated  list */
00144     };
00145     int i, printCount;
00146     PaError err;
00147 
00148     printCount = 0;
00149     for (i = 0; standardSampleRates[i] > 0; i++) {
00150         err = Pa_IsFormatSupported(inputParameters, outputParameters, standardSampleRates[i]);
00151         if (err == paFormatIsSupported) {
00152             if (printCount == 0) {
00153                 jack_info("\t%8.2f", standardSampleRates[i]);
00154                 printCount = 1;
00155             } else if (printCount == 4) {
00156                 jack_info(",\n\t%8.2f", standardSampleRates[i]);
00157                 printCount = 1;
00158             } else {
00159                 jack_info(", %8.2f", standardSampleRates[i]);
00160                 ++printCount;
00161             }
00162         }
00163     }
00164     if (!printCount) {
00165         jack_info("None");
00166         } else {
00167         jack_info("\n");
00168         }
00169 }
00170 
00171 int PortAudioDevices::GetInputDeviceFromName(const char* devicename, PaDeviceIndex& id, int& max_input)
00172 {
00173     string fullname = string(devicename);
00174     PaDeviceInfo* device = GetDeviceFromFullName(fullname, id, true);
00175     if (device) {
00176         max_input = device->maxInputChannels;
00177     } else {
00178         id = Pa_GetDefaultInputDevice();
00179         if (fullname.size()) {
00180             jack_error("Can't open %s, PortAudio will use default input device.", devicename);
00181         }
00182         if (id == paNoDevice) {
00183             return -1;
00184         }
00185         max_input = GetDeviceInfo(id)->maxInputChannels;
00186     }
00187     return id;
00188 }
00189 
00190 int PortAudioDevices::GetOutputDeviceFromName(const char* devicename, PaDeviceIndex& id, int& max_output)
00191 {
00192     string fullname = string(devicename);
00193     PaDeviceInfo* device = GetDeviceFromFullName(fullname, id, false);
00194     if (device) {
00195         max_output = device->maxOutputChannels;
00196     } else {
00197         id = Pa_GetDefaultOutputDevice();
00198         if (fullname.size()) {
00199             jack_error("Can't open %s, PortAudio will use default output device.", devicename);
00200         }
00201         if (id == paNoDevice) {
00202             return -1;
00203         }
00204         max_output = GetDeviceInfo(id)->maxOutputChannels;
00205     }
00206     return id;
00207 }
00208 
00209 int PortAudioDevices::GetPreferredBufferSize(PaDeviceIndex id)
00210 {
00211 #if defined(WIN32) && defined(HAVE_ASIO)
00212     /* ASIO specific latency information */
00213     if (Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->type == paASIO) {
00214         long minLatency, maxLatency, preferredLatency, granularity;
00215 
00216         PaAsio_GetAvailableBufferSizes(id, &minLatency, &maxLatency, &preferredLatency, &granularity);
00217 
00218         jack_info("ASIO minimum buffer size    = %ld", minLatency);
00219         jack_info("ASIO maximum buffer size    = %ld", maxLatency);
00220         jack_info("ASIO preferred buffer size  = %ld", preferredLatency);
00221 
00222         if (granularity == -1) {
00223             jack_info("ASIO buffer granularity     = power of 2");
00224         } else {
00225             jack_info("ASIO buffer granularity     = %ld", granularity);
00226         }
00227         
00228         return preferredLatency;
00229     } else 
00230 #endif   
00231     {
00232         return 512; // Non ASIO driver, returns generic value
00233     }
00234 }
00235 
00236 void PortAudioDevices::DisplayDevicesNames()
00237 {
00238     PaDeviceIndex id;
00239     PaStreamParameters inputParameters, outputParameters;
00240     jack_info("********************** Devices list, %d detected **********************", fNumDevice);
00241 
00242     for (id = 0; id < fNumDevice; id++) {
00243         jack_info("-------- device #%d ------------------------------------------------", id);
00244 
00245         if (id == Pa_GetDefaultInputDevice()) {
00246             jack_info("[ Default Input ]");
00247         } else if (id == Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->defaultInputDevice) {
00248             const PaHostApiInfo *host_info = Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi);
00249             jack_info("[ Default %s Input ]", host_info->name);
00250         }
00251 
00252         if (id == Pa_GetDefaultOutputDevice()) {
00253             jack_info("[ Default Output ]");
00254         } else if (id == Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->defaultOutputDevice) {
00255             const PaHostApiInfo *host_info = Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi);
00256             jack_info("[ Default %s Output ]", host_info->name);
00257         }
00258 
00259         /* print device info fields */
00260         jack_info("Name                        = %s", GetFullName(id).c_str());
00261         jack_info("Max inputs                  = %d", fDeviceInfo[id]->maxInputChannels);
00262         jack_info("Max outputs                 = %d", fDeviceInfo[id]->maxOutputChannels);
00263         
00264     #if defined(WIN32) && defined(HAVE_ASIO)
00265         /* ASIO specific latency information */
00266         if (Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->type == paASIO) {
00267             long minLatency, maxLatency, preferredLatency, granularity;
00268 
00269             PaAsio_GetAvailableBufferSizes(id, &minLatency, &maxLatency, &preferredLatency, &granularity);
00270 
00271             jack_info("ASIO minimum buffer size    = %ld", minLatency);
00272             jack_info("ASIO maximum buffer size    = %ld", maxLatency);
00273             jack_info("ASIO preferred buffer size  = %ld", preferredLatency);
00274 
00275             if (granularity == -1) {
00276                 jack_info("ASIO buffer granularity     = power of 2");
00277             } else {
00278                 jack_info("ASIO buffer granularity     = %ld", granularity);
00279             }
00280         }
00281     #endif 
00282         jack_info("Default sample rate         = %8.2f", fDeviceInfo[id]->defaultSampleRate);
00283 
00284         /* poll for standard sample rates */
00285         inputParameters.device = id;
00286         inputParameters.channelCount = fDeviceInfo[id]->maxInputChannels;
00287         inputParameters.sampleFormat = paInt16;
00288         inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
00289         inputParameters.hostApiSpecificStreamInfo = NULL;
00290 
00291         outputParameters.device = id;
00292         outputParameters.channelCount = fDeviceInfo[id]->maxOutputChannels;
00293         outputParameters.sampleFormat = paInt16;
00294         outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
00295         outputParameters.hostApiSpecificStreamInfo = NULL;
00296     }
00297     jack_info("**************************** End of list ****************************");
00298 }
00299 
00300 bool PortAudioDevices::IsDuplex(PaDeviceIndex id)
00301 {
00302     //does the device has in and out facilities
00303     if (fDeviceInfo[id]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels) {
00304         return true;
00305     }
00306     //else is another complementary device ? (search in devices with the same name)
00307     for (PaDeviceIndex i = 0; i < fNumDevice; i++) {
00308         if ((i != id) && (GetDeviceName(i) == GetDeviceName(id))) {
00309             if ((fDeviceInfo[i]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels)
00310                     || (fDeviceInfo[i]->maxOutputChannels && fDeviceInfo[id]->maxInputChannels)) {
00311                 return true;
00312             }
00313         }
00314     }
00315     //then the device isn't full duplex
00316     return false;
00317 }