jack2 codebase
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

318 lines
11KB

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