Jack2  1.9.9
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  Pa_Terminate();
50 
51  delete[] fDeviceInfo;
52  delete[] fHostName;
53 }
54 
55 PaDeviceIndex PortAudioDevices::GetNumDevice()
56 {
57  return fNumDevice;
58 }
59 
60 PaDeviceInfo* PortAudioDevices::GetDeviceInfo(PaDeviceIndex id)
61 {
62  return fDeviceInfo[id];
63 }
64 
65 string PortAudioDevices::GetDeviceName(PaDeviceIndex id)
66 {
67  return string(fDeviceInfo[id]->name);
68 }
69 
70 string PortAudioDevices::GetHostFromDevice(PaDeviceInfo* device)
71 {
72  return fHostName[device->hostApi];
73 }
74 
75 string PortAudioDevices::GetHostFromDevice(PaDeviceIndex id)
76 {
77  return fHostName[fDeviceInfo[id]->hostApi];
78 }
79 
80 string PortAudioDevices::GetFullName(PaDeviceIndex id)
81 {
82  string hostname = GetHostFromDevice(id);
83  string devicename = GetDeviceName(id);
84  //some hostname are quite long...use shortcuts
85  if (hostname.compare("Windows DirectSound") == 0) {
86  hostname = string("DirectSound");
87  }
88  return (hostname + "::" + devicename);
89 }
90 
91 string PortAudioDevices::GetFullName(std::string hostname, std::string devicename)
92 {
93  //some hostname are quite long...use shortcuts
94  if (hostname.compare("Windows DirectSound") == 0) {
95  hostname = string("DirectSound");
96  }
97  return (hostname + "::" + devicename);
98 }
99 
100 PaDeviceInfo* PortAudioDevices::GetDeviceFromFullName(string fullname, PaDeviceIndex& id, bool isInput)
101 {
102  PaDeviceInfo* ret = NULL;
103  //no driver to find
104  if (fullname.size() == 0) {
105  return NULL;
106  }
107  //first get host and device names from fullname
108  string::size_type separator = fullname.find("::", 0);
109 
110  if (separator == string::npos) {
111  return NULL;
112  }
113 
114  char* hostname = (char*)malloc(separator + 9);
115  fill_n(hostname, separator + 9, 0);
116  fullname.copy(hostname, separator);
117 
118  //we need the entire hostname, replace shortcuts
119  if (strcmp(hostname, "DirectSound") == 0) {
120  strcpy(hostname, "Windows DirectSound");
121  }
122  string devicename = fullname.substr(separator + 2);
123  //then find the corresponding device
124  for (PaDeviceIndex dev_id = 0; dev_id < fNumDevice; dev_id++) {
125  bool flag = (isInput) ? (fDeviceInfo[dev_id]->maxInputChannels > 0) : (fDeviceInfo[dev_id]->maxOutputChannels > 0);
126  if ((GetHostFromDevice(dev_id).compare(hostname) == 0)
127  && (GetDeviceName(dev_id).compare(devicename) == 0)
128  && flag) {
129  id = dev_id;
130  ret = fDeviceInfo[dev_id];
131  }
132  }
133  free(hostname);
134  return ret;
135 }
136 
137 void PortAudioDevices::PrintSupportedStandardSampleRates(const PaStreamParameters* inputParameters, const PaStreamParameters* outputParameters)
138 {
139  static double standardSampleRates[] =
140  {
141  8000.0, 9600.0, 11025.0, 12000.0, 16000.0, 22050.0, 24000.0, 32000.0,
142  44100.0, 48000.0, 88200.0, 96000.0, 192000.0, -1 /* negative terminated list */
143  };
144  int i, printCount;
145  PaError err;
146 
147  printCount = 0;
148  for (i = 0; standardSampleRates[i] > 0; i++) {
149  err = Pa_IsFormatSupported(inputParameters, outputParameters, standardSampleRates[i]);
150  if (err == paFormatIsSupported) {
151  if (printCount == 0) {
152  jack_info("\t%8.2f", standardSampleRates[i]);
153  printCount = 1;
154  } else if (printCount == 4) {
155  jack_info(",\n\t%8.2f", standardSampleRates[i]);
156  printCount = 1;
157  } else {
158  jack_info(", %8.2f", standardSampleRates[i]);
159  ++printCount;
160  }
161  }
162  }
163  if (!printCount) {
164  jack_info("None");
165  } else {
166  jack_info("\n");
167  }
168 }
169 
170 int PortAudioDevices::GetInputDeviceFromName(const char* devicename, PaDeviceIndex& id, int& max_input)
171 {
172  string fullname = string(devicename);
173  PaDeviceInfo* device = GetDeviceFromFullName(fullname, id, true);
174  if (device) {
175  max_input = device->maxInputChannels;
176  } else {
178  if (fullname.size()) {
179  jack_error("Can't open %s, PortAudio will use default input device.", devicename);
180  }
181  if (id == paNoDevice) {
182  return -1;
183  }
184  max_input = GetDeviceInfo(id)->maxInputChannels;
185  }
186  return id;
187 }
188 
189 int PortAudioDevices::GetOutputDeviceFromName(const char* devicename, PaDeviceIndex& id, int& max_output)
190 {
191  string fullname = string(devicename);
192  PaDeviceInfo* device = GetDeviceFromFullName(fullname, id, false);
193  if (device) {
194  max_output = device->maxOutputChannels;
195  } else {
197  if (fullname.size()) {
198  jack_error("Can't open %s, PortAudio will use default output device.", devicename);
199  }
200  if (id == paNoDevice) {
201  return -1;
202  }
203  max_output = GetDeviceInfo(id)->maxOutputChannels;
204  }
205  return id;
206 }
207 
208 int PortAudioDevices::GetPreferredBufferSize(PaDeviceIndex id)
209 {
210 #ifdef WIN32
211  /* ASIO specific latency information */
212  if (Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->type == paASIO) {
213  long minLatency, maxLatency, preferredLatency, granularity;
214 
215  PaAsio_GetAvailableBufferSizes(id, &minLatency, &maxLatency, &preferredLatency, &granularity);
216 
217  jack_info("ASIO minimum buffer size = %ld", minLatency);
218  jack_info("ASIO maximum buffer size = %ld", maxLatency);
219  jack_info("ASIO preferred buffer size = %ld", preferredLatency);
220 
221  if (granularity == -1) {
222  jack_info("ASIO buffer granularity = power of 2");
223  } else {
224  jack_info("ASIO buffer granularity = %ld", granularity);
225  }
226 
227  return preferredLatency;
228  } else
229 #endif
230  {
231  return 512; // Non ASIO driver, returns generic value
232  }
233 }
234 
235 void PortAudioDevices::DisplayDevicesNames()
236 {
237  PaDeviceIndex id;
238  PaStreamParameters inputParameters, outputParameters;
239  jack_info("********************** Devices list, %d detected **********************", fNumDevice);
240 
241  for (id = 0; id < fNumDevice; id++) {
242  jack_info("-------- device #%d ------------------------------------------------", id);
243 
244  if (id == Pa_GetDefaultInputDevice()) {
245  jack_info("[ Default Input ]");
246  } else if (id == Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->defaultInputDevice) {
247  const PaHostApiInfo *host_info = Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi);
248  jack_info("[ Default %s Input ]", host_info->name);
249  }
250 
251  if (id == Pa_GetDefaultOutputDevice()) {
252  jack_info("[ Default Output ]");
253  } else if (id == Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->defaultOutputDevice) {
254  const PaHostApiInfo *host_info = Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi);
255  jack_info("[ Default %s Output ]", host_info->name);
256  }
257 
258  /* print device info fields */
259  jack_info("Name = %s", GetFullName(id).c_str());
260  jack_info("Max inputs = %d", fDeviceInfo[id]->maxInputChannels);
261  jack_info("Max outputs = %d", fDeviceInfo[id]->maxOutputChannels);
262 
263  #ifdef WIN32
264  /* ASIO specific latency information */
265  if (Pa_GetHostApiInfo(fDeviceInfo[id]->hostApi)->type == paASIO) {
266  long minLatency, maxLatency, preferredLatency, granularity;
267 
268  PaAsio_GetAvailableBufferSizes(id, &minLatency, &maxLatency, &preferredLatency, &granularity);
269 
270  jack_info("ASIO minimum buffer size = %ld", minLatency);
271  jack_info("ASIO maximum buffer size = %ld", maxLatency);
272  jack_info("ASIO preferred buffer size = %ld", preferredLatency);
273 
274  if (granularity == -1) {
275  jack_info("ASIO buffer granularity = power of 2");
276  } else {
277  jack_info("ASIO buffer granularity = %ld", granularity);
278  }
279  }
280  #endif
281  jack_info("Default sample rate = %8.2f", fDeviceInfo[id]->defaultSampleRate);
282 
283  /* poll for standard sample rates */
284  inputParameters.device = id;
285  inputParameters.channelCount = fDeviceInfo[id]->maxInputChannels;
286  inputParameters.sampleFormat = paInt16;
287  inputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
288  inputParameters.hostApiSpecificStreamInfo = NULL;
289 
290  outputParameters.device = id;
291  outputParameters.channelCount = fDeviceInfo[id]->maxOutputChannels;
292  outputParameters.sampleFormat = paInt16;
293  outputParameters.suggestedLatency = 0; /* ignored by Pa_IsFormatSupported() */
294  outputParameters.hostApiSpecificStreamInfo = NULL;
295  }
296  jack_info("**************************** End of list ****************************");
297 }
298 
299 bool PortAudioDevices::IsDuplex(PaDeviceIndex id)
300 {
301  //does the device has in and out facilities
302  if (fDeviceInfo[id]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels) {
303  return true;
304  }
305  //else is another complementary device ? (search in devices with the same name)
306  for (PaDeviceIndex i = 0; i < fNumDevice; i++) {
307  if ((i != id) && (GetDeviceName(i) == GetDeviceName(id))) {
308  if ((fDeviceInfo[i]->maxInputChannels && fDeviceInfo[id]->maxOutputChannels)
309  || (fDeviceInfo[i]->maxOutputChannels && fDeviceInfo[id]->maxInputChannels)) {
310  return true;
311  }
312  }
313  }
314  //then the device isn't full duplex
315  return false;
316 }
PaError Pa_Initialize(void)
PaDeviceIndex Pa_GetDefaultInputDevice(void)
PaDeviceIndex Pa_GetDeviceCount(void)
SERVER_EXPORT void jack_error(const char *fmt,...)
Definition: JackError.cpp:91
const char * name
Definition: portaudio.h:242
SERVER_EXPORT void jack_info(const char *fmt,...)
Definition: JackError.cpp:99
PaError Pa_IsFormatSupported(const PaStreamParameters *inputParameters, const PaStreamParameters *outputParameters, double sampleRate)
int PaDeviceIndex
Definition: portaudio.h:153
void * hostApiSpecificStreamInfo
Definition: portaudio.h:515
PaError PaAsio_GetAvailableBufferSizes(PaDeviceIndex device, long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity)
PaSampleFormat sampleFormat
Definition: portaudio.h:495
int PaError
Definition: portaudio.h:63
#define paInt16
Definition: portaudio.h:427
PaTime suggestedLatency
Definition: portaudio.h:508
#define paNoDevice
Definition: portaudio.h:161
const PaHostApiInfo * Pa_GetHostApiInfo(PaHostApiIndex hostApi)
PaHostApiIndex Pa_GetHostApiCount(void)
const PaDeviceInfo * Pa_GetDeviceInfo(PaDeviceIndex device)
PaDeviceIndex Pa_GetDefaultOutputDevice(void)
PaDeviceIndex device
Definition: portaudio.h:482
#define paFormatIsSupported
Definition: portaudio.h:522
const char * Pa_GetErrorText(PaError errorCode)
SERVER_EXPORT void jack_log(const char *fmt,...)
Definition: JackError.cpp:107
PaError Pa_Terminate(void)