Jack2 1.9.6

JackPortAudioDevices.cpp

00001 /*
00002 Copyright (C) 2008 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 
00023 using namespace std;
00024 
00025 PortAudioDevices::PortAudioDevices()
00026 {
00027     PaError err;
00028         PaDeviceIndex id;
00029         jack_log("Initializing PortAudio...");
00030     if ( ( err = Pa_Initialize() ) == paNoError )
00031     {
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         fHostName = new string[fNumHostApi];
00038         for ( id = 0; id < fNumHostApi; id++ )
00039             fHostName[id] = string ( Pa_GetHostApiInfo(id)->name );
00040     }
00041     else
00042                 jack_error("JackPortAudioDriver::Pa_Initialize error = %s", Pa_GetErrorText(err));
00043 }
00044 
00045 PortAudioDevices::~PortAudioDevices()
00046 {
00047     // Desactivate for now: crash the server..
00048     //Pa_Terminate();
00049 
00050     delete[] fDeviceInfo;
00051     delete[] fHostName;
00052 }
00053 
00054 PaDeviceIndex PortAudioDevices::GetNumDevice()
00055 {
00056     return fNumDevice;
00057 }
00058 
00059 PaDeviceInfo* PortAudioDevices::GetDeviceInfo ( PaDeviceIndex id )
00060 {
00061     return fDeviceInfo[id];
00062 }
00063 
00064 string PortAudioDevices::GetDeviceName ( PaDeviceIndex id )
00065 {
00066     return string ( fDeviceInfo[id]->name );
00067 }
00068 
00069 string PortAudioDevices::GetHostFromDevice ( PaDeviceInfo* device )
00070 {
00071     return fHostName[device->hostApi];
00072 }
00073 
00074 string PortAudioDevices::GetHostFromDevice ( PaDeviceIndex id )
00075 {
00076     return fHostName[fDeviceInfo[id]->hostApi];
00077 }
00078 
00079 string PortAudioDevices::GetFullName ( PaDeviceIndex id )
00080 {
00081     string hostname = GetHostFromDevice ( id );
00082     string devicename = GetDeviceName ( id );
00083     //some hostname are quite long...use shortcuts
00084     if ( hostname.compare ( "Windows DirectSound" ) == 0 )
00085         hostname = string ( "DirectSound" );
00086     return ( hostname + "::" + devicename );
00087 }
00088 
00089 string PortAudioDevices::GetFullName ( std::string hostname, std::string devicename )
00090 {
00091     //some hostname are quite long...use shortcuts
00092     if ( hostname.compare ( "Windows DirectSound" ) == 0 )
00093         hostname = string ( "DirectSound" );
00094     return ( hostname + "::" + devicename );
00095 }
00096 
00097 PaDeviceInfo* PortAudioDevices::GetDeviceFromFullName ( string fullname, PaDeviceIndex& id, bool isInput )
00098 {
00099     PaDeviceInfo* ret = NULL;
00100     //no driver to find
00101     if ( fullname.size() == 0 )
00102         return NULL;
00103     //first get host and device names from fullname
00104     string::size_type separator = fullname.find ( "::", 0 );
00105     if ( separator == 0 )
00106         return NULL;
00107     char* hostname = (char*)malloc(separator + 9);
00108     fill_n ( hostname, separator + 9, 0 );
00109     fullname.copy ( hostname, separator );
00110 
00111     //we need the entire hostname, replace shortcuts
00112     if ( strcmp ( hostname, "DirectSound" ) == 0 )
00113         strcpy ( hostname, "Windows DirectSound" );
00114     string devicename = fullname.substr ( separator + 2 );
00115     //then find the corresponding device
00116     for ( PaDeviceIndex dev_id = 0; dev_id < fNumDevice; dev_id++ )
00117     {
00118         bool flag = (isInput) ? (fDeviceInfo[dev_id]->maxInputChannels > 0) : (fDeviceInfo[dev_id]->maxOutputChannels > 0);
00119         if ( ( GetHostFromDevice(dev_id).compare(hostname) == 0 )
00120             && ( GetDeviceName(dev_id).compare(devicename) == 0 )
00121             && flag )
00122         {
00123             id = dev_id;
00124             ret = fDeviceInfo[dev_id];
00125         }
00126     }
00127         free(hostname);
00128     return ret;
00129 }
00130 
00131 void PortAudioDevices::PrintSupportedStandardSampleRates(const PaStreamParameters* inputParameters, const PaStreamParameters* outputParameters)
00132 {
00133     static double standardSampleRates[] =
00134     {
00135         8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
00136         44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated  list */
00137     };
00138     int i, printCount;
00139     PaError err;
00140 
00141     printCount = 0;
00142     for (i = 0; standardSampleRates[i] > 0; i++)
00143     {
00144         err = Pa_IsFormatSupported(inputParameters, outputParameters, standardSampleRates[i]);
00145         if (err == paFormatIsSupported)
00146         {
00147             if (printCount == 0)
00148             {
00149                 jack_info("\t%8.2f", standardSampleRates[i]);
00150                 printCount = 1;
00151             }
00152             else if (printCount == 4)
00153             {
00154                 jack_info(",\n\t%8.2f", standardSampleRates[i]);
00155                 printCount = 1;
00156             }
00157             else
00158             {
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     {
00179         id = Pa_GetDefaultInputDevice();
00180         if ( fullname.size() )
00181             jack_error("Can't open %s, PortAudio will use default input device.", devicename);
00182         if ( id == paNoDevice )
00183             return -1;
00184         max_input = GetDeviceInfo(id)->maxInputChannels;
00185     }
00186     return id;
00187 }
00188 
00189 int PortAudioDevices::GetOutputDeviceFromName(const char* devicename, PaDeviceIndex& id, int& max_output)
00190 {
00191     string fullname = string ( devicename );
00192     PaDeviceInfo* device = GetDeviceFromFullName ( fullname, id, false );
00193     if ( device )
00194         max_output = device->maxOutputChannels;
00195     else
00196     {
00197         id = Pa_GetDefaultOutputDevice();
00198         if ( fullname.size() )
00199             jack_error("Can't open %s, PortAudio will use default output device.", devicename);
00200         if ( id == paNoDevice )
00201             return -1;
00202         max_output = GetDeviceInfo(id)->maxOutputChannels;
00203     }
00204     return id;
00205 }
00206 
00207 void PortAudioDevices::DisplayDevicesNames()
00208 {
00209     PaDeviceIndex id;
00210     PaStreamParameters inputParameters, outputParameters;
00211     jack_info ( "********************** Devices list, %d detected **********************", fNumDevice );
00212 
00213     for ( id = 0; id < fNumDevice; id++ )
00214     {
00215         jack_info ( "-------- device #%d ------------------------------------------------", id );
00216 
00217         if ( id == Pa_GetDefaultInputDevice() )
00218         {
00219             jack_info("[ Default Input ]");
00220         }
00221         else if ( id == Pa_GetHostApiInfo ( fDeviceInfo[id]->hostApi)->defaultInputDevice )
00222         {
00223             const PaHostApiInfo *host_info = Pa_GetHostApiInfo ( fDeviceInfo[id]->hostApi );
00224             jack_info ( "[ Default %s Input ]", host_info->name );
00225         }
00226 
00227         if ( id == Pa_GetDefaultOutputDevice() )
00228         {
00229             jack_info ( "[ Default Output ]" );
00230         }
00231         else if ( id == Pa_GetHostApiInfo ( fDeviceInfo[id]->hostApi )->defaultOutputDevice )
00232         {
00233             const PaHostApiInfo *host_info = Pa_GetHostApiInfo ( fDeviceInfo[id]->hostApi );
00234             jack_info ( "[ Default %s Output ]", host_info->name );
00235         }
00236 
00237         /* print device info fields */
00238         jack_info ( "Name                        = %s", GetFullName ( id ).c_str() );
00239         jack_info ( "Max inputs                  = %d", fDeviceInfo[id]->maxInputChannels );
00240         jack_info ( "Max outputs                 = %d", fDeviceInfo[id]->maxOutputChannels );
00241 
00242 #ifdef WIN32
00243         /* ASIO specific latency information */
00244         if ( Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->type == paASIO )
00245         {
00246             long minLatency, maxLatency, preferredLatency, granularity;
00247 
00248             PaAsio_GetAvailableLatencyValues ( id, &minLatency, &maxLatency, &preferredLatency, &granularity );
00249 
00250             jack_info ( "ASIO minimum buffer size    = %ld", minLatency );
00251             jack_info ( "ASIO maximum buffer size    = %ld", maxLatency );
00252             jack_info ( "ASIO preferred buffer size  = %ld", preferredLatency );
00253 
00254             if ( granularity == -1 )
00255                 jack_info ( "ASIO buffer granularity     = power of 2" );
00256             else
00257                 jack_info ( "ASIO buffer granularity     = %ld", granularity );
00258         }
00259 #endif
00260 
00261         jack_info ( "Default sample rate         = %8.2f", fDeviceInfo[id]->defaultSampleRate );
00262 
00263         /* poll for standard sample rates */
00264         inputParameters.device = id;
00265         inputParameters.channelCount = fDeviceInfo[id]->maxInputChannels;
00266         inputParameters.sampleFormat = paInt16;
00267         inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
00268         inputParameters.hostApiSpecificStreamInfo = NULL;
00269 
00270         outputParameters.device = id;
00271         outputParameters.channelCount = fDeviceInfo[id]->maxOutputChannels;
00272         outputParameters.sampleFormat = paInt16;
00273         outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
00274         outputParameters.hostApiSpecificStreamInfo = NULL;
00275     }
00276     jack_info ( "**************************** End of list ****************************" );
00277 }
00278 
00279 bool PortAudioDevices::IsDuplex ( PaDeviceIndex id )
00280 {
00281     //does the device has in and out facilities
00282     if ( fDeviceInfo[id]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels )
00283         return true;
00284     //else is another complementary device ? (search in devices with the same name)
00285     for ( PaDeviceIndex i = 0; i < fNumDevice; i++ )
00286         if ( ( i != id ) && ( GetDeviceName ( i ) == GetDeviceName ( id ) ) )
00287             if ( ( fDeviceInfo[i]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels )
00288                     || ( fDeviceInfo[i]->maxOutputChannels && fDeviceInfo[id]->maxInputChannels ) )
00289                 return true;
00290     //then the device isn't full duplex
00291     return false;
00292 }