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.

983 lines
32KB

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