The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

1013 lines
34KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. namespace
  19. {
  20. void getDeviceSampleRates (snd_pcm_t* handle, Array <int>& rates)
  21. {
  22. const int ratesToTry[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 };
  23. snd_pcm_hw_params_t* hwParams;
  24. snd_pcm_hw_params_alloca (&hwParams);
  25. for (int i = 0; ratesToTry[i] != 0; ++i)
  26. {
  27. if (snd_pcm_hw_params_any (handle, hwParams) >= 0
  28. && snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0)
  29. {
  30. rates.addIfNotAlreadyThere (ratesToTry[i]);
  31. }
  32. }
  33. }
  34. void getDeviceNumChannels (snd_pcm_t* handle, unsigned int* minChans, unsigned int* maxChans)
  35. {
  36. snd_pcm_hw_params_t *params;
  37. snd_pcm_hw_params_alloca (&params);
  38. if (snd_pcm_hw_params_any (handle, params) >= 0)
  39. {
  40. snd_pcm_hw_params_get_channels_min (params, minChans);
  41. snd_pcm_hw_params_get_channels_max (params, maxChans);
  42. }
  43. }
  44. void getDeviceProperties (const String& deviceID,
  45. unsigned int& minChansOut,
  46. unsigned int& maxChansOut,
  47. unsigned int& minChansIn,
  48. unsigned int& maxChansIn,
  49. Array <int>& rates)
  50. {
  51. if (deviceID.isEmpty())
  52. return;
  53. snd_ctl_t* handle;
  54. if (snd_ctl_open (&handle, deviceID.upToLastOccurrenceOf (",", false, false).toUTF8(), SND_CTL_NONBLOCK) >= 0)
  55. {
  56. snd_pcm_info_t* info;
  57. snd_pcm_info_alloca (&info);
  58. snd_pcm_info_set_stream (info, SND_PCM_STREAM_PLAYBACK);
  59. snd_pcm_info_set_device (info, deviceID.fromLastOccurrenceOf (",", false, false).getIntValue());
  60. snd_pcm_info_set_subdevice (info, 0);
  61. if (snd_ctl_pcm_info (handle, info) >= 0)
  62. {
  63. snd_pcm_t* pcmHandle;
  64. if (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC | SND_PCM_NONBLOCK) >= 0)
  65. {
  66. getDeviceNumChannels (pcmHandle, &minChansOut, &maxChansOut);
  67. getDeviceSampleRates (pcmHandle, rates);
  68. snd_pcm_close (pcmHandle);
  69. }
  70. }
  71. snd_pcm_info_set_stream (info, SND_PCM_STREAM_CAPTURE);
  72. if (snd_ctl_pcm_info (handle, info) >= 0)
  73. {
  74. snd_pcm_t* pcmHandle;
  75. if (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_CAPTURE, SND_PCM_ASYNC | SND_PCM_NONBLOCK) >= 0)
  76. {
  77. getDeviceNumChannels (pcmHandle, &minChansIn, &maxChansIn);
  78. if (rates.size() == 0)
  79. getDeviceSampleRates (pcmHandle, rates);
  80. snd_pcm_close (pcmHandle);
  81. }
  82. }
  83. snd_ctl_close (handle);
  84. }
  85. }
  86. }
  87. //==============================================================================
  88. class ALSADevice
  89. {
  90. public:
  91. ALSADevice (const String& deviceID, bool forInput)
  92. : handle (0),
  93. bitDepth (16),
  94. numChannelsRunning (0),
  95. latency (0),
  96. isInput (forInput),
  97. isInterleaved (true)
  98. {
  99. failed (snd_pcm_open (&handle, deviceID.toUTF8(),
  100. forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
  101. SND_PCM_ASYNC));
  102. }
  103. ~ALSADevice()
  104. {
  105. if (handle != 0)
  106. snd_pcm_close (handle);
  107. }
  108. bool setParameters (unsigned int sampleRate, int numChannels, int bufferSize)
  109. {
  110. if (handle == 0)
  111. return false;
  112. snd_pcm_hw_params_t* hwParams;
  113. snd_pcm_hw_params_alloca (&hwParams);
  114. if (failed (snd_pcm_hw_params_any (handle, hwParams)))
  115. return false;
  116. if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_NONINTERLEAVED) >= 0)
  117. isInterleaved = false;
  118. else if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED) >= 0)
  119. isInterleaved = true;
  120. else
  121. {
  122. jassertfalse;
  123. return false;
  124. }
  125. enum { isFloatBit = 1 << 16, isLittleEndianBit = 1 << 17 };
  126. const int formatsToTry[] = { SND_PCM_FORMAT_FLOAT_LE, 32 | isFloatBit | isLittleEndianBit,
  127. SND_PCM_FORMAT_FLOAT_BE, 32 | isFloatBit,
  128. SND_PCM_FORMAT_S32_LE, 32 | isLittleEndianBit,
  129. SND_PCM_FORMAT_S32_BE, 32,
  130. SND_PCM_FORMAT_S24_3LE, 24 | isLittleEndianBit,
  131. SND_PCM_FORMAT_S24_3BE, 24,
  132. SND_PCM_FORMAT_S16_LE, 16 | isLittleEndianBit,
  133. SND_PCM_FORMAT_S16_BE, 16 };
  134. bitDepth = 0;
  135. for (int i = 0; i < numElementsInArray (formatsToTry); i += 2)
  136. {
  137. if (snd_pcm_hw_params_set_format (handle, hwParams, (_snd_pcm_format) formatsToTry [i]) >= 0)
  138. {
  139. bitDepth = formatsToTry [i + 1] & 255;
  140. const bool isFloat = (formatsToTry [i + 1] & isFloatBit) != 0;
  141. const bool isLittleEndian = (formatsToTry [i + 1] & isLittleEndianBit) != 0;
  142. converter = createConverter (isInput, bitDepth, isFloat, isLittleEndian, numChannels);
  143. break;
  144. }
  145. }
  146. if (bitDepth == 0)
  147. {
  148. error = "device doesn't support a compatible PCM format";
  149. DBG ("ALSA error: " + error + "\n");
  150. return false;
  151. }
  152. int dir = 0;
  153. unsigned int periods = 4;
  154. snd_pcm_uframes_t samplesPerPeriod = bufferSize;
  155. if (failed (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0))
  156. || failed (snd_pcm_hw_params_set_channels (handle, hwParams, numChannels))
  157. || failed (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir))
  158. || failed (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir))
  159. || failed (snd_pcm_hw_params (handle, hwParams)))
  160. {
  161. return false;
  162. }
  163. snd_pcm_uframes_t frames = 0;
  164. if (failed (snd_pcm_hw_params_get_period_size (hwParams, &frames, &dir))
  165. || failed (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir)))
  166. latency = 0;
  167. else
  168. latency = frames * (periods - 1); // (this is the method JACK uses to guess the latency..)
  169. snd_pcm_sw_params_t* swParams;
  170. snd_pcm_sw_params_alloca (&swParams);
  171. snd_pcm_uframes_t boundary;
  172. if (failed (snd_pcm_sw_params_current (handle, swParams))
  173. || failed (snd_pcm_sw_params_get_boundary (swParams, &boundary))
  174. || failed (snd_pcm_sw_params_set_silence_threshold (handle, swParams, 0))
  175. || failed (snd_pcm_sw_params_set_silence_size (handle, swParams, boundary))
  176. || failed (snd_pcm_sw_params_set_start_threshold (handle, swParams, samplesPerPeriod))
  177. || failed (snd_pcm_sw_params_set_stop_threshold (handle, swParams, boundary))
  178. || failed (snd_pcm_sw_params (handle, swParams)))
  179. {
  180. return false;
  181. }
  182. #if 0
  183. // enable this to dump the config of the devices that get opened
  184. snd_output_t* out;
  185. snd_output_stdio_attach (&out, stderr, 0);
  186. snd_pcm_hw_params_dump (hwParams, out);
  187. snd_pcm_sw_params_dump (swParams, out);
  188. #endif
  189. numChannelsRunning = numChannels;
  190. return true;
  191. }
  192. //==============================================================================
  193. bool writeToOutputDevice (AudioSampleBuffer& outputChannelBuffer, const int numSamples)
  194. {
  195. jassert (numChannelsRunning <= outputChannelBuffer.getNumChannels());
  196. float** const data = outputChannelBuffer.getArrayOfChannels();
  197. snd_pcm_sframes_t numDone = 0;
  198. if (isInterleaved)
  199. {
  200. scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false);
  201. for (int i = 0; i < numChannelsRunning; ++i)
  202. converter->convertSamples (scratch.getData(), i, data[i], 0, numSamples);
  203. numDone = snd_pcm_writei (handle, scratch.getData(), numSamples);
  204. }
  205. else
  206. {
  207. for (int i = 0; i < numChannelsRunning; ++i)
  208. converter->convertSamples (data[i], data[i], numSamples);
  209. numDone = snd_pcm_writen (handle, (void**) data, numSamples);
  210. }
  211. if (failed (numDone))
  212. {
  213. if (numDone == -EPIPE)
  214. {
  215. if (failed (snd_pcm_prepare (handle)))
  216. return false;
  217. }
  218. else if (numDone != -ESTRPIPE)
  219. return false;
  220. }
  221. return true;
  222. }
  223. bool readFromInputDevice (AudioSampleBuffer& inputChannelBuffer, const int numSamples)
  224. {
  225. jassert (numChannelsRunning <= inputChannelBuffer.getNumChannels());
  226. float** const data = inputChannelBuffer.getArrayOfChannels();
  227. if (isInterleaved)
  228. {
  229. scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false);
  230. scratch.fillWith (0); // (not clearing this data causes warnings in valgrind)
  231. snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), numSamples);
  232. if (failed (num))
  233. {
  234. if (num == -EPIPE)
  235. {
  236. if (failed (snd_pcm_prepare (handle)))
  237. return false;
  238. }
  239. else if (num != -ESTRPIPE)
  240. return false;
  241. }
  242. for (int i = 0; i < numChannelsRunning; ++i)
  243. converter->convertSamples (data[i], 0, scratch.getData(), i, numSamples);
  244. }
  245. else
  246. {
  247. snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, numSamples);
  248. if (failed (num) && num != -EPIPE && num != -ESTRPIPE)
  249. return false;
  250. for (int i = 0; i < numChannelsRunning; ++i)
  251. converter->convertSamples (data[i], data[i], numSamples);
  252. }
  253. return true;
  254. }
  255. //==============================================================================
  256. snd_pcm_t* handle;
  257. String error;
  258. int bitDepth, numChannelsRunning, latency;
  259. //==============================================================================
  260. private:
  261. const bool isInput;
  262. bool isInterleaved;
  263. MemoryBlock scratch;
  264. ScopedPointer<AudioData::Converter> converter;
  265. //==============================================================================
  266. template <class SampleType>
  267. struct ConverterHelper
  268. {
  269. static AudioData::Converter* createConverter (const bool forInput, const bool isLittleEndian, const int numInterleavedChannels)
  270. {
  271. if (forInput)
  272. {
  273. typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DestType;
  274. if (isLittleEndian)
  275. return new AudioData::ConverterInstance <AudioData::Pointer <SampleType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, DestType> (numInterleavedChannels, 1);
  276. else
  277. return new AudioData::ConverterInstance <AudioData::Pointer <SampleType, AudioData::BigEndian, AudioData::Interleaved, AudioData::Const>, DestType> (numInterleavedChannels, 1);
  278. }
  279. else
  280. {
  281. typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SourceType;
  282. if (isLittleEndian)
  283. return new AudioData::ConverterInstance <SourceType, AudioData::Pointer <SampleType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> > (1, numInterleavedChannels);
  284. else
  285. return new AudioData::ConverterInstance <SourceType, AudioData::Pointer <SampleType, AudioData::BigEndian, AudioData::Interleaved, AudioData::NonConst> > (1, numInterleavedChannels);
  286. }
  287. }
  288. };
  289. static AudioData::Converter* createConverter (const bool forInput, const int bitDepth, const bool isFloat, const bool isLittleEndian, const int numInterleavedChannels)
  290. {
  291. switch (bitDepth)
  292. {
  293. case 16: return ConverterHelper <AudioData::Int16>::createConverter (forInput, isLittleEndian, numInterleavedChannels);
  294. case 24: return ConverterHelper <AudioData::Int24>::createConverter (forInput, isLittleEndian, numInterleavedChannels);
  295. case 32: return isFloat ? ConverterHelper <AudioData::Float32>::createConverter (forInput, isLittleEndian, numInterleavedChannels)
  296. : ConverterHelper <AudioData::Int32>::createConverter (forInput, isLittleEndian, numInterleavedChannels);
  297. default: jassertfalse; break; // unsupported format!
  298. }
  299. return nullptr;
  300. }
  301. //==============================================================================
  302. bool failed (const int errorNum)
  303. {
  304. if (errorNum >= 0)
  305. return false;
  306. error = snd_strerror (errorNum);
  307. DBG ("ALSA error: " + error + "\n");
  308. return true;
  309. }
  310. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSADevice);
  311. };
  312. //==============================================================================
  313. class ALSAThread : public Thread
  314. {
  315. public:
  316. ALSAThread (const String& inputId_,
  317. const String& outputId_)
  318. : Thread ("Juce ALSA"),
  319. sampleRate (0),
  320. bufferSize (0),
  321. outputLatency (0),
  322. inputLatency (0),
  323. callback (0),
  324. inputId (inputId_),
  325. outputId (outputId_),
  326. numCallbacks (0),
  327. inputChannelBuffer (1, 1),
  328. outputChannelBuffer (1, 1)
  329. {
  330. initialiseRatesAndChannels();
  331. }
  332. ~ALSAThread()
  333. {
  334. close();
  335. }
  336. void open (BigInteger inputChannels,
  337. BigInteger outputChannels,
  338. const double sampleRate_,
  339. const int bufferSize_)
  340. {
  341. close();
  342. error = String::empty;
  343. sampleRate = sampleRate_;
  344. bufferSize = bufferSize_;
  345. inputChannelBuffer.setSize (jmax ((int) minChansIn, inputChannels.getHighestBit()) + 1, bufferSize);
  346. inputChannelBuffer.clear();
  347. inputChannelDataForCallback.clear();
  348. currentInputChans.clear();
  349. if (inputChannels.getHighestBit() >= 0)
  350. {
  351. for (int i = 0; i <= jmax (inputChannels.getHighestBit(), (int) minChansIn); ++i)
  352. {
  353. if (inputChannels[i])
  354. {
  355. inputChannelDataForCallback.add (inputChannelBuffer.getSampleData (i));
  356. currentInputChans.setBit (i);
  357. }
  358. }
  359. }
  360. outputChannelBuffer.setSize (jmax ((int) minChansOut, outputChannels.getHighestBit()) + 1, bufferSize);
  361. outputChannelBuffer.clear();
  362. outputChannelDataForCallback.clear();
  363. currentOutputChans.clear();
  364. if (outputChannels.getHighestBit() >= 0)
  365. {
  366. for (int i = 0; i <= jmax (outputChannels.getHighestBit(), (int) minChansOut); ++i)
  367. {
  368. if (outputChannels[i])
  369. {
  370. outputChannelDataForCallback.add (outputChannelBuffer.getSampleData (i));
  371. currentOutputChans.setBit (i);
  372. }
  373. }
  374. }
  375. if (outputChannelDataForCallback.size() > 0 && outputId.isNotEmpty())
  376. {
  377. outputDevice = new ALSADevice (outputId, false);
  378. if (outputDevice->error.isNotEmpty())
  379. {
  380. error = outputDevice->error;
  381. outputDevice = nullptr;
  382. return;
  383. }
  384. currentOutputChans.setRange (0, minChansOut, true);
  385. if (! outputDevice->setParameters ((unsigned int) sampleRate,
  386. jlimit ((int) minChansOut, (int) maxChansOut, currentOutputChans.getHighestBit() + 1),
  387. bufferSize))
  388. {
  389. error = outputDevice->error;
  390. outputDevice = nullptr;
  391. return;
  392. }
  393. outputLatency = outputDevice->latency;
  394. }
  395. if (inputChannelDataForCallback.size() > 0 && inputId.isNotEmpty())
  396. {
  397. inputDevice = new ALSADevice (inputId, true);
  398. if (inputDevice->error.isNotEmpty())
  399. {
  400. error = inputDevice->error;
  401. inputDevice = nullptr;
  402. return;
  403. }
  404. currentInputChans.setRange (0, minChansIn, true);
  405. if (! inputDevice->setParameters ((unsigned int) sampleRate,
  406. jlimit ((int) minChansIn, (int) maxChansIn, currentInputChans.getHighestBit() + 1),
  407. bufferSize))
  408. {
  409. error = inputDevice->error;
  410. inputDevice = nullptr;
  411. return;
  412. }
  413. inputLatency = inputDevice->latency;
  414. }
  415. if (outputDevice == nullptr && inputDevice == nullptr)
  416. {
  417. error = "no channels";
  418. return;
  419. }
  420. if (outputDevice != nullptr && inputDevice != nullptr)
  421. {
  422. snd_pcm_link (outputDevice->handle, inputDevice->handle);
  423. }
  424. if (inputDevice != nullptr && failed (snd_pcm_prepare (inputDevice->handle)))
  425. return;
  426. if (outputDevice != nullptr && failed (snd_pcm_prepare (outputDevice->handle)))
  427. return;
  428. startThread (9);
  429. int count = 1000;
  430. while (numCallbacks == 0)
  431. {
  432. sleep (5);
  433. if (--count < 0 || ! isThreadRunning())
  434. {
  435. error = "device didn't start";
  436. break;
  437. }
  438. }
  439. }
  440. void close()
  441. {
  442. stopThread (6000);
  443. inputDevice = nullptr;
  444. outputDevice = nullptr;
  445. inputChannelBuffer.setSize (1, 1);
  446. outputChannelBuffer.setSize (1, 1);
  447. numCallbacks = 0;
  448. }
  449. void setCallback (AudioIODeviceCallback* const newCallback) noexcept
  450. {
  451. const ScopedLock sl (callbackLock);
  452. callback = newCallback;
  453. }
  454. void run()
  455. {
  456. while (! threadShouldExit())
  457. {
  458. if (inputDevice != nullptr)
  459. {
  460. if (! inputDevice->readFromInputDevice (inputChannelBuffer, bufferSize))
  461. {
  462. DBG ("ALSA: read failure");
  463. break;
  464. }
  465. }
  466. if (threadShouldExit())
  467. break;
  468. {
  469. const ScopedLock sl (callbackLock);
  470. ++numCallbacks;
  471. if (callback != nullptr)
  472. {
  473. callback->audioDeviceIOCallback ((const float**) inputChannelDataForCallback.getRawDataPointer(),
  474. inputChannelDataForCallback.size(),
  475. outputChannelDataForCallback.getRawDataPointer(),
  476. outputChannelDataForCallback.size(),
  477. bufferSize);
  478. }
  479. else
  480. {
  481. for (int i = 0; i < outputChannelDataForCallback.size(); ++i)
  482. zeromem (outputChannelDataForCallback[i], sizeof (float) * bufferSize);
  483. }
  484. }
  485. if (outputDevice != nullptr)
  486. {
  487. failed (snd_pcm_wait (outputDevice->handle, 2000));
  488. if (threadShouldExit())
  489. break;
  490. failed (snd_pcm_avail_update (outputDevice->handle));
  491. if (! outputDevice->writeToOutputDevice (outputChannelBuffer, bufferSize))
  492. {
  493. DBG ("ALSA: write failure");
  494. break;
  495. }
  496. }
  497. }
  498. }
  499. int getBitDepth() const noexcept
  500. {
  501. if (outputDevice != nullptr)
  502. return outputDevice->bitDepth;
  503. if (inputDevice != nullptr)
  504. return inputDevice->bitDepth;
  505. return 16;
  506. }
  507. //==============================================================================
  508. String error;
  509. double sampleRate;
  510. int bufferSize, outputLatency, inputLatency;
  511. BigInteger currentInputChans, currentOutputChans;
  512. Array <int> sampleRates;
  513. StringArray channelNamesOut, channelNamesIn;
  514. AudioIODeviceCallback* callback;
  515. private:
  516. //==============================================================================
  517. const String inputId, outputId;
  518. ScopedPointer<ALSADevice> outputDevice, inputDevice;
  519. int numCallbacks;
  520. CriticalSection callbackLock;
  521. AudioSampleBuffer inputChannelBuffer, outputChannelBuffer;
  522. Array<float*> inputChannelDataForCallback, outputChannelDataForCallback;
  523. unsigned int minChansOut, maxChansOut;
  524. unsigned int minChansIn, maxChansIn;
  525. bool failed (const int errorNum)
  526. {
  527. if (errorNum >= 0)
  528. return false;
  529. error = snd_strerror (errorNum);
  530. DBG ("ALSA error: " + error + "\n");
  531. return true;
  532. }
  533. void initialiseRatesAndChannels()
  534. {
  535. sampleRates.clear();
  536. channelNamesOut.clear();
  537. channelNamesIn.clear();
  538. minChansOut = 0;
  539. maxChansOut = 0;
  540. minChansIn = 0;
  541. maxChansIn = 0;
  542. unsigned int dummy = 0;
  543. getDeviceProperties (inputId, dummy, dummy, minChansIn, maxChansIn, sampleRates);
  544. getDeviceProperties (outputId, minChansOut, maxChansOut, dummy, dummy, sampleRates);
  545. unsigned int i;
  546. for (i = 0; i < maxChansOut; ++i)
  547. channelNamesOut.add ("channel " + String ((int) i + 1));
  548. for (i = 0; i < maxChansIn; ++i)
  549. channelNamesIn.add ("channel " + String ((int) i + 1));
  550. }
  551. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAThread);
  552. };
  553. //==============================================================================
  554. class ALSAAudioIODevice : public AudioIODevice
  555. {
  556. public:
  557. ALSAAudioIODevice (const String& deviceName,
  558. const String& inputId_,
  559. const String& outputId_)
  560. : AudioIODevice (deviceName, "ALSA"),
  561. inputId (inputId_),
  562. outputId (outputId_),
  563. isOpen_ (false),
  564. isStarted (false),
  565. internal (inputId_, outputId_)
  566. {
  567. }
  568. ~ALSAAudioIODevice()
  569. {
  570. close();
  571. }
  572. StringArray getOutputChannelNames() { return internal.channelNamesOut; }
  573. StringArray getInputChannelNames() { return internal.channelNamesIn; }
  574. int getNumSampleRates() { return internal.sampleRates.size(); }
  575. double getSampleRate (int index) { return internal.sampleRates [index]; }
  576. int getDefaultBufferSize() { return 512; }
  577. int getNumBufferSizesAvailable() { return 50; }
  578. int getBufferSizeSamples (int index)
  579. {
  580. int n = 16;
  581. for (int i = 0; i < index; ++i)
  582. n += n < 64 ? 16
  583. : (n < 512 ? 32
  584. : (n < 1024 ? 64
  585. : (n < 2048 ? 128 : 256)));
  586. return n;
  587. }
  588. String open (const BigInteger& inputChannels,
  589. const BigInteger& outputChannels,
  590. double sampleRate,
  591. int bufferSizeSamples)
  592. {
  593. close();
  594. if (bufferSizeSamples <= 0)
  595. bufferSizeSamples = getDefaultBufferSize();
  596. if (sampleRate <= 0)
  597. {
  598. for (int i = 0; i < getNumSampleRates(); ++i)
  599. {
  600. if (getSampleRate (i) >= 44100)
  601. {
  602. sampleRate = getSampleRate (i);
  603. break;
  604. }
  605. }
  606. }
  607. internal.open (inputChannels, outputChannels,
  608. sampleRate, bufferSizeSamples);
  609. isOpen_ = internal.error.isEmpty();
  610. return internal.error;
  611. }
  612. void close()
  613. {
  614. stop();
  615. internal.close();
  616. isOpen_ = false;
  617. }
  618. bool isOpen() { return isOpen_; }
  619. bool isPlaying() { return isStarted && internal.error.isEmpty(); }
  620. String getLastError() { return internal.error; }
  621. int getCurrentBufferSizeSamples() { return internal.bufferSize; }
  622. double getCurrentSampleRate() { return internal.sampleRate; }
  623. int getCurrentBitDepth() { return internal.getBitDepth(); }
  624. BigInteger getActiveOutputChannels() const { return internal.currentOutputChans; }
  625. BigInteger getActiveInputChannels() const { return internal.currentInputChans; }
  626. int getOutputLatencyInSamples() { return internal.outputLatency; }
  627. int getInputLatencyInSamples() { return internal.inputLatency; }
  628. void start (AudioIODeviceCallback* callback)
  629. {
  630. if (! isOpen_)
  631. callback = nullptr;
  632. if (callback != nullptr)
  633. callback->audioDeviceAboutToStart (this);
  634. internal.setCallback (callback);
  635. isStarted = (callback != nullptr);
  636. }
  637. void stop()
  638. {
  639. AudioIODeviceCallback* const oldCallback = internal.callback;
  640. start (0);
  641. if (oldCallback != nullptr)
  642. oldCallback->audioDeviceStopped();
  643. }
  644. String inputId, outputId;
  645. private:
  646. bool isOpen_, isStarted;
  647. ALSAThread internal;
  648. };
  649. //==============================================================================
  650. class ALSAAudioIODeviceType : public AudioIODeviceType
  651. {
  652. public:
  653. //==============================================================================
  654. ALSAAudioIODeviceType()
  655. : AudioIODeviceType ("ALSA"),
  656. hasScanned (false)
  657. {
  658. }
  659. ~ALSAAudioIODeviceType()
  660. {
  661. }
  662. //==============================================================================
  663. void scanForDevices()
  664. {
  665. if (hasScanned)
  666. return;
  667. hasScanned = true;
  668. inputNames.clear();
  669. inputIds.clear();
  670. outputNames.clear();
  671. outputIds.clear();
  672. /* void** hints = 0;
  673. if (snd_device_name_hint (-1, "pcm", &hints) >= 0)
  674. {
  675. for (void** hint = hints; *hint != 0; ++hint)
  676. {
  677. const String name (getHint (*hint, "NAME"));
  678. if (name.isNotEmpty())
  679. {
  680. const String ioid (getHint (*hint, "IOID"));
  681. String desc (getHint (*hint, "DESC"));
  682. if (desc.isEmpty())
  683. desc = name;
  684. desc = desc.replaceCharacters ("\n\r", " ");
  685. DBG ("name: " << name << "\ndesc: " << desc << "\nIO: " << ioid);
  686. if (ioid.isEmpty() || ioid == "Input")
  687. {
  688. inputNames.add (desc);
  689. inputIds.add (name);
  690. }
  691. if (ioid.isEmpty() || ioid == "Output")
  692. {
  693. outputNames.add (desc);
  694. outputIds.add (name);
  695. }
  696. }
  697. }
  698. snd_device_name_free_hint (hints);
  699. }
  700. */
  701. snd_ctl_t* handle = nullptr;
  702. snd_ctl_card_info_t* info = nullptr;
  703. snd_ctl_card_info_alloca (&info);
  704. int cardNum = -1;
  705. while (outputIds.size() + inputIds.size() <= 32)
  706. {
  707. snd_card_next (&cardNum);
  708. if (cardNum < 0)
  709. break;
  710. if (snd_ctl_open (&handle, ("hw:" + String (cardNum)).toUTF8(), SND_CTL_NONBLOCK) >= 0)
  711. {
  712. if (snd_ctl_card_info (handle, info) >= 0)
  713. {
  714. String cardId (snd_ctl_card_info_get_id (info));
  715. if (cardId.removeCharacters ("0123456789").isEmpty())
  716. cardId = String (cardNum);
  717. int device = -1;
  718. for (;;)
  719. {
  720. if (snd_ctl_pcm_next_device (handle, &device) < 0 || device < 0)
  721. break;
  722. String id, name;
  723. id << "hw:" << cardId << ',' << device;
  724. bool isInput, isOutput;
  725. if (testDevice (id, isInput, isOutput))
  726. {
  727. name << snd_ctl_card_info_get_name (info);
  728. if (name.isEmpty())
  729. name = id;
  730. if (isInput)
  731. {
  732. inputNames.add (name);
  733. inputIds.add (id);
  734. }
  735. if (isOutput)
  736. {
  737. outputNames.add (name);
  738. outputIds.add (id);
  739. }
  740. }
  741. }
  742. }
  743. snd_ctl_close (handle);
  744. }
  745. }
  746. inputNames.appendNumbersToDuplicates (false, true);
  747. outputNames.appendNumbersToDuplicates (false, true);
  748. }
  749. StringArray getDeviceNames (bool wantInputNames) const
  750. {
  751. jassert (hasScanned); // need to call scanForDevices() before doing this
  752. return wantInputNames ? inputNames : outputNames;
  753. }
  754. int getDefaultDeviceIndex (bool forInput) const
  755. {
  756. jassert (hasScanned); // need to call scanForDevices() before doing this
  757. return 0;
  758. }
  759. bool hasSeparateInputsAndOutputs() const { return true; }
  760. int getIndexOfDevice (AudioIODevice* device, bool asInput) const
  761. {
  762. jassert (hasScanned); // need to call scanForDevices() before doing this
  763. ALSAAudioIODevice* d = dynamic_cast <ALSAAudioIODevice*> (device);
  764. if (d == nullptr)
  765. return -1;
  766. return asInput ? inputIds.indexOf (d->inputId)
  767. : outputIds.indexOf (d->outputId);
  768. }
  769. AudioIODevice* createDevice (const String& outputDeviceName,
  770. const String& inputDeviceName)
  771. {
  772. jassert (hasScanned); // need to call scanForDevices() before doing this
  773. const int inputIndex = inputNames.indexOf (inputDeviceName);
  774. const int outputIndex = outputNames.indexOf (outputDeviceName);
  775. String deviceName (outputIndex >= 0 ? outputDeviceName
  776. : inputDeviceName);
  777. if (inputIndex >= 0 || outputIndex >= 0)
  778. return new ALSAAudioIODevice (deviceName,
  779. inputIds [inputIndex],
  780. outputIds [outputIndex]);
  781. return nullptr;
  782. }
  783. //==============================================================================
  784. private:
  785. StringArray inputNames, outputNames, inputIds, outputIds;
  786. bool hasScanned;
  787. static bool testDevice (const String& id, bool& isInput, bool& isOutput)
  788. {
  789. unsigned int minChansOut = 0, maxChansOut = 0;
  790. unsigned int minChansIn = 0, maxChansIn = 0;
  791. Array <int> rates;
  792. getDeviceProperties (id, minChansOut, maxChansOut, minChansIn, maxChansIn, rates);
  793. DBG ("ALSA device: " + id
  794. + " outs=" + String ((int) minChansOut) + "-" + String ((int) maxChansOut)
  795. + " ins=" + String ((int) minChansIn) + "-" + String ((int) maxChansIn)
  796. + " rates=" + String (rates.size()));
  797. isInput = maxChansIn > 0;
  798. isOutput = maxChansOut > 0;
  799. return (isInput || isOutput) && rates.size() > 0;
  800. }
  801. /*static String getHint (void* hint, const char* type)
  802. {
  803. char* const n = snd_device_name_get_hint (hint, type);
  804. const String s ((const char*) n);
  805. free (n);
  806. return s;
  807. }*/
  808. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAAudioIODeviceType);
  809. };
  810. //==============================================================================
  811. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA()
  812. {
  813. return new ALSAAudioIODeviceType();
  814. }