Jack2  1.9.8
JackPortAudioDevices.cpp
1 /*
2 Copyright (C) 2008-2011 Romain Moret at Grame
3 
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13 
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #include "JackPortAudioDevices.h"
21 #include "JackError.h"
22 #include <stdlib.h>
23 
24 using namespace std;
25 
26 PortAudioDevices::PortAudioDevices()
27 {
28  PaError err;
29  PaDeviceIndex id;
30  jack_log("Initializing PortAudio...");
31  if ((err = Pa_Initialize()) == paNoError) {
32  fNumHostApi = Pa_GetHostApiCount();
33  fNumDevice = Pa_GetDeviceCount();
34  fDeviceInfo = new PaDeviceInfo*[fNumDevice];
35  for (id = 0; id < fNumDevice; id++) {
36  fDeviceInfo[id] = const_cast<PaDeviceInfo*>(Pa_GetDeviceInfo(id));
37  }
38  fHostName = new string[fNumHostApi];
39  for (id = 0; id < fNumHostApi; id++) {
40  fHostName[id] = string (Pa_GetHostApiInfo(id)->name);
41  }
42  } else {
43  jack_error("JackPortAudioDriver::Pa_Initialize error = %s", Pa_GetErrorText(err));
44  }
45 }
46 
47 PortAudioDevices::~PortAudioDevices()
48 {
49  // Desactivate for now: crash the server..
50  //Pa_Terminate();
51 
52  delete[] fDeviceInfo;
53  delete[] fHostName;
54 }
55 
56 PaDeviceIndex PortAudioDevices::GetNumDevice()
57 {
58  return fNumDevice;
59 }
60 
61 PaDeviceInfo* PortAudioDevices::GetDeviceInfo(PaDeviceIndex id)
62 {
63  return fDeviceInfo[id];
64 }
65 
66 string PortAudioDevices::GetDeviceName(PaDeviceIndex id)
67 {
68  return string(fDeviceInfo[id]->name);
69 }
70 
71 string PortAudioDevices::GetHostFromDevice(PaDeviceInfo* device)
72 {
73  return fHostName[device->hostApi];
74 }
75 
76 string PortAudioDevices::GetHostFromDevice(PaDeviceIndex id)
77 {
78  return fHostName[fDeviceInfo[id]->hostApi];
79 }
80 
81 string PortAudioDevices::GetFullName(PaDeviceIndex id)
82 {
83  string hostname = GetHostFromDevice(id);
84  string devicename = GetDeviceName(id);
85  //some hostname are quite long...use shortcuts
86  if (hostname.compare("Windows DirectSound") == 0) {
87  hostname = string("DirectSound");
88  }
89  return (hostname + "::" + devicename);
90 }
91 
92 string PortAudioDevices::GetFullName(std::string hostname, std::string devicename)
93 {
94  //some hostname are quite long...use shortcuts
95  if (hostname.compare("Windows DirectSound") == 0) {
96  hostname = string("DirectSound");
97  }
98  return (hostname + "::" + devicename);
99 }
100 
101 PaDeviceInfo* PortAudioDevices::GetDeviceFromFullName (string fullname, PaDeviceIndex& id, bool isInput)
102 {
103  PaDeviceInfo* ret = NULL;
104  //no driver to find
105  if (fullname.size() == 0) {
106  return NULL;
107  }
108  //first get host and device names from fullname
109  string::size_type separator = fullname.find ("::", 0);
110  if (separator == 0) {
111  return NULL;
112  }
113  char* hostname = (char*)malloc(separator + 9);
114  fill_n (hostname, separator + 9, 0);
115  fullname.copy (hostname, separator);
116 
117  //we need the entire hostname, replace shortcuts
118  if (strcmp (hostname, "DirectSound") == 0) {
119  strcpy (hostname, "Windows DirectSound");
120  }
121  string devicename = fullname.substr (separator + 2);
122  //then find the corresponding device
123  for (PaDeviceIndex dev_id = 0; dev_id < fNumDevice; dev_id++) {
124  bool flag = (isInput) ? (fDeviceInfo[dev_id]->maxInputChannels > 0) : (fDeviceInfo[dev_id]->maxOutputChannels > 0);
125  if ((GetHostFromDevice(dev_id).compare(hostname) == 0)
126  && (GetDeviceName(dev_id).compare(devicename) == 0)
127  && flag) {
128  id = dev_id;
129  ret = fDeviceInfo[dev_id];
130  }
131  }
132  free(hostname);
133  return ret;
134 }
135 
136 void PortAudioDevices::PrintSupportedStandardSampleRates(const PaStreamParameters* inputParameters, const PaStreamParameters* outputParameters)
137 {
138  static double standardSampleRates[] =
139  {
140  8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
141  44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated list */
142  };
143  int i, printCount;
144  PaError err;
145 
146  printCount = 0;
147  for (i = 0; standardSampleRates[i] > 0; i++) {
148  err = Pa_IsFormatSupported(inputParameters, outputParameters, standardSampleRates[i]);
149  if (err == paFormatIsSupported) {
150  if (printCount == 0) {
151  jack_info("\t%8.2f", standardSampleRates[i]);
152  printCount = 1;
153  } else if (printCount == 4) {
154  jack_info(",\n\t%8.2f", standardSampleRates[i]);
155  printCount = 1;
156  } else {
157  jack_info(", %8.2f", standardSampleRates[i]);
158  ++printCount;
159  }
160  }
161  }
162  if (!printCount) {
163  jack_info("None");
164  } else {
165  jack_info("\n");
166  }
167 }
168 
169 int PortAudioDevices::GetInputDeviceFromName(const char* devicename, PaDeviceIndex& id, int& max_input)
170 {
171  string fullname = string (devicename);
172  PaDeviceInfo* device = GetDeviceFromFullName (fullname, id, true);
173  if (device) {
174  max_input = device->maxInputChannels;
175  } else {
177  if (fullname.size()) {
178  jack_error("Can't open %s, PortAudio will use default input device.", devicename);
179  }
180  if (id == paNoDevice) {
181  return -1;
182  }
183  max_input = GetDeviceInfo(id)->maxInputChannels;
184  }
185  return id;
186 }
187 
188 int PortAudioDevices::GetOutputDeviceFromName(const char* devicename, PaDeviceIndex& id, int& max_output)
189 {
190  string fullname = string (devicename);
191  PaDeviceInfo* device = GetDeviceFromFullName (fullname, id, false);
192  if (device) {
193  max_output = device->maxOutputChannels;
194  } else {
196  if (fullname.size()) {
197  jack_error("Can't open %s, PortAudio will use default output device.", devicename);
198  }
199  if (id == paNoDevice) {
200  return -1;
201  }
202  max_output = GetDeviceInfo(id)->maxOutputChannels;
203  }
204  return id;
205 }
206 
207 void PortAudioDevices::DisplayDevicesNames()
208 {
209  PaDeviceIndex id;
210  PaStreamParameters inputParameters, outputParameters;
211  jack_info ("********************** Devices list, %d detected **********************", fNumDevice);
212 
213  for (id = 0; id < fNumDevice; id++) {
214  jack_info ("-------- device #%d ------------------------------------------------", id);
215 
216  if (id == Pa_GetDefaultInputDevice()) {
217  jack_info("[ Default Input ]");
218  } else if (id == Pa_GetHostApiInfo (fDeviceInfo[id]->hostApi)->defaultInputDevice) {
219  const PaHostApiInfo *host_info = Pa_GetHostApiInfo (fDeviceInfo[id]->hostApi);
220  jack_info("[ Default %s Input ]", host_info->name);
221  }
222 
223  if (id == Pa_GetDefaultOutputDevice()) {
224  jack_info ("[ Default Output ]");
225  } else if (id == Pa_GetHostApiInfo (fDeviceInfo[id]->hostApi)->defaultOutputDevice) {
226  const PaHostApiInfo *host_info = Pa_GetHostApiInfo (fDeviceInfo[id]->hostApi);
227  jack_info("[ Default %s Output ]", host_info->name);
228  }
229 
230  /* print device info fields */
231  jack_info ("Name = %s", GetFullName (id).c_str());
232  jack_info ("Max inputs = %d", fDeviceInfo[id]->maxInputChannels);
233  jack_info ("Max outputs = %d", fDeviceInfo[id]->maxOutputChannels);
234 
235 #ifdef WIN32
236  /* ASIO specific latency information */
237  if (Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->type == paASIO) {
238  long minLatency, maxLatency, preferredLatency, granularity;
239 
240  PaAsio_GetAvailableLatencyValues (id, &minLatency, &maxLatency, &preferredLatency, &granularity);
241 
242  jack_info ("ASIO minimum buffer size = %ld", minLatency);
243  jack_info ("ASIO maximum buffer size = %ld", maxLatency);
244  jack_info ("ASIO preferred buffer size = %ld", preferredLatency);
245 
246  if (granularity == -1) {
247  jack_info ("ASIO buffer granularity = power of 2");
248  } else {
249  jack_info ("ASIO buffer granularity = %ld", granularity);
250  }
251  }
252 #endif
253 
254  jack_info ("Default sample rate = %8.2f", fDeviceInfo[id]->defaultSampleRate);
255 
256  /* poll for standard sample rates */
257  inputParameters.device = id;
258  inputParameters.channelCount = fDeviceInfo[id]->maxInputChannels;
259  inputParameters.sampleFormat = paInt16;
260  inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
261  inputParameters.hostApiSpecificStreamInfo = NULL;
262 
263  outputParameters.device = id;
264  outputParameters.channelCount = fDeviceInfo[id]->maxOutputChannels;
265  outputParameters.sampleFormat = paInt16;
266  outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
267  outputParameters.hostApiSpecificStreamInfo = NULL;
268  }
269  jack_info("**************************** End of list ****************************");
270 }
271 
272 bool PortAudioDevices::IsDuplex (PaDeviceIndex id)
273 {
274  //does the device has in and out facilities
275  if (fDeviceInfo[id]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels) {
276  return true;
277  }
278  //else is another complementary device ? (search in devices with the same name)
279  for (PaDeviceIndex i = 0; i < fNumDevice; i++) {
280  if ((i != id) && (GetDeviceName (i) == GetDeviceName (id))) {
281  if ((fDeviceInfo[i]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels)
282  || (fDeviceInfo[i]->maxOutputChannels && fDeviceInfo[id]->maxInputChannels)) {
283  return true;
284  }
285  }
286  }
287  //then the device isn't full duplex
288  return false;
289 }
290