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.

1226 lines
43KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. namespace
  18. {
  19. #ifndef JUCE_ALSA_LOGGING
  20. #define JUCE_ALSA_LOGGING 0
  21. #endif
  22. #if JUCE_ALSA_LOGGING
  23. #define JUCE_ALSA_LOG(dbgtext) { juce::String tempDbgBuf ("ALSA: "); tempDbgBuf << dbgtext; Logger::writeToLog (tempDbgBuf); DBG (tempDbgBuf) }
  24. #define JUCE_CHECKED_RESULT(x) (logErrorMessage (x, __LINE__))
  25. static int logErrorMessage (int err, int lineNum)
  26. {
  27. if (err < 0)
  28. JUCE_ALSA_LOG ("Error: line " << lineNum << ": code " << err << " (" << snd_strerror (err) << ")");
  29. return err;
  30. }
  31. #else
  32. #define JUCE_ALSA_LOG(x)
  33. #define JUCE_CHECKED_RESULT(x) (x)
  34. #endif
  35. #define JUCE_ALSA_FAILED(x) failed (x)
  36. void getDeviceSampleRates (snd_pcm_t* handle, Array <int>& rates)
  37. {
  38. const int ratesToTry[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 };
  39. snd_pcm_hw_params_t* hwParams;
  40. snd_pcm_hw_params_alloca (&hwParams);
  41. for (int i = 0; ratesToTry[i] != 0; ++i)
  42. {
  43. if (snd_pcm_hw_params_any (handle, hwParams) >= 0
  44. && snd_pcm_hw_params_test_rate (handle, hwParams, ratesToTry[i], 0) == 0)
  45. {
  46. rates.addIfNotAlreadyThere (ratesToTry[i]);
  47. }
  48. }
  49. }
  50. void getDeviceNumChannels (snd_pcm_t* handle, unsigned int* minChans, unsigned int* maxChans)
  51. {
  52. snd_pcm_hw_params_t *params;
  53. snd_pcm_hw_params_alloca (&params);
  54. if (snd_pcm_hw_params_any (handle, params) >= 0)
  55. {
  56. snd_pcm_hw_params_get_channels_min (params, minChans);
  57. snd_pcm_hw_params_get_channels_max (params, maxChans);
  58. JUCE_ALSA_LOG ("getDeviceNumChannels: " << (int) *minChans << " " << (int) *maxChans);
  59. // some virtual devices (dmix for example) report 10000 channels , we have to clamp these values
  60. *maxChans = jmin (*maxChans, 32u);
  61. *minChans = jmin (*minChans, *maxChans);
  62. }
  63. else
  64. {
  65. JUCE_ALSA_LOG ("getDeviceNumChannels failed");
  66. }
  67. }
  68. void getDeviceProperties (const String& deviceID,
  69. unsigned int& minChansOut,
  70. unsigned int& maxChansOut,
  71. unsigned int& minChansIn,
  72. unsigned int& maxChansIn,
  73. Array <int>& rates,
  74. bool testOutput = true,
  75. bool testInput = true)
  76. {
  77. minChansOut = maxChansOut = minChansIn = maxChansIn = 0;
  78. if (deviceID.isEmpty())
  79. return;
  80. JUCE_ALSA_LOG ("getDeviceProperties(" << deviceID.toUTF8().getAddress() << ")");
  81. snd_pcm_info_t* info;
  82. snd_pcm_info_alloca (&info);
  83. if (testOutput)
  84. {
  85. snd_pcm_t* pcmHandle;
  86. if (JUCE_CHECKED_RESULT (snd_pcm_open (&pcmHandle, deviceID.toUTF8().getAddress(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) >= 0)
  87. {
  88. getDeviceNumChannels (pcmHandle, &minChansOut, &maxChansOut);
  89. getDeviceSampleRates (pcmHandle, rates);
  90. snd_pcm_close (pcmHandle);
  91. }
  92. }
  93. if (testInput)
  94. {
  95. snd_pcm_t* pcmHandle;
  96. if (JUCE_CHECKED_RESULT (snd_pcm_open (&pcmHandle, deviceID.toUTF8(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) >= 0))
  97. {
  98. getDeviceNumChannels (pcmHandle, &minChansIn, &maxChansIn);
  99. if (rates.size() == 0)
  100. getDeviceSampleRates (pcmHandle, rates);
  101. snd_pcm_close (pcmHandle);
  102. }
  103. }
  104. }
  105. static void silentErrorHandler (const char*, int, const char*, int, const char*,...) {}
  106. //==============================================================================
  107. class ALSADevice
  108. {
  109. public:
  110. ALSADevice (const String& devID, bool forInput)
  111. : handle (0),
  112. bitDepth (16),
  113. numChannelsRunning (0),
  114. latency (0),
  115. deviceID (devID),
  116. isInput (forInput),
  117. isInterleaved (true)
  118. {
  119. JUCE_ALSA_LOG ("snd_pcm_open (" << deviceID.toUTF8().getAddress() << ", forInput=" << forInput << ")");
  120. int err = snd_pcm_open (&handle, deviceID.toUTF8(),
  121. forInput ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
  122. SND_PCM_ASYNC);
  123. if (err < 0)
  124. {
  125. if (-err == EBUSY)
  126. error << "The device \"" << deviceID << "\" is busy (another application is using it).";
  127. else if (-err == ENOENT)
  128. error << "The device \"" << deviceID << "\" is not available.";
  129. else
  130. error << "Could not open " << (forInput ? "input" : "output") << " device \"" << deviceID
  131. << "\": " << snd_strerror(err) << " (" << err << ")";
  132. JUCE_ALSA_LOG ("snd_pcm_open failed; " << error);
  133. }
  134. }
  135. ~ALSADevice()
  136. {
  137. closeNow();
  138. }
  139. void closeNow()
  140. {
  141. if (handle != 0)
  142. {
  143. snd_pcm_close (handle);
  144. handle = 0;
  145. }
  146. }
  147. bool setParameters (unsigned int sampleRate, int numChannels, int bufferSize)
  148. {
  149. if (handle == 0)
  150. return false;
  151. JUCE_ALSA_LOG ("ALSADevice::setParameters(" << deviceID << ", "
  152. << (int) sampleRate << ", " << numChannels << ", " << bufferSize << ")");
  153. snd_pcm_hw_params_t* hwParams;
  154. snd_pcm_hw_params_alloca (&hwParams);
  155. if (snd_pcm_hw_params_any (handle, hwParams) < 0)
  156. {
  157. // this is the error message that aplay returns when an error happens here,
  158. // it is a bit more explicit that "Invalid parameter"
  159. error = "Broken configuration for this PCM: no configurations available";
  160. return false;
  161. }
  162. if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_INTERLEAVED) >= 0) // works better for plughw..
  163. isInterleaved = true;
  164. else if (snd_pcm_hw_params_set_access (handle, hwParams, SND_PCM_ACCESS_RW_NONINTERLEAVED) >= 0)
  165. isInterleaved = false;
  166. else
  167. {
  168. jassertfalse;
  169. return false;
  170. }
  171. enum { isFloatBit = 1 << 16, isLittleEndianBit = 1 << 17 };
  172. const int formatsToTry[] = { SND_PCM_FORMAT_FLOAT_LE, 32 | isFloatBit | isLittleEndianBit,
  173. SND_PCM_FORMAT_FLOAT_BE, 32 | isFloatBit,
  174. SND_PCM_FORMAT_S32_LE, 32 | isLittleEndianBit,
  175. SND_PCM_FORMAT_S32_BE, 32,
  176. SND_PCM_FORMAT_S24_3LE, 24 | isLittleEndianBit,
  177. SND_PCM_FORMAT_S24_3BE, 24,
  178. SND_PCM_FORMAT_S16_LE, 16 | isLittleEndianBit,
  179. SND_PCM_FORMAT_S16_BE, 16 };
  180. bitDepth = 0;
  181. for (int i = 0; i < numElementsInArray (formatsToTry); i += 2)
  182. {
  183. if (snd_pcm_hw_params_set_format (handle, hwParams, (_snd_pcm_format) formatsToTry [i]) >= 0)
  184. {
  185. bitDepth = formatsToTry [i + 1] & 255;
  186. const bool isFloat = (formatsToTry [i + 1] & isFloatBit) != 0;
  187. const bool isLittleEndian = (formatsToTry [i + 1] & isLittleEndianBit) != 0;
  188. converter = createConverter (isInput, bitDepth, isFloat, isLittleEndian, numChannels);
  189. JUCE_ALSA_LOG ("format: bitDepth=" << bitDepth << ", isFloat="
  190. << isFloat << ", isLittleEndian=" << isLittleEndian
  191. << ", numChannels=" << numChannels);
  192. break;
  193. }
  194. }
  195. if (bitDepth == 0)
  196. {
  197. error = "device doesn't support a compatible PCM format";
  198. JUCE_ALSA_LOG ("Error: " + error);
  199. return false;
  200. }
  201. int dir = 0;
  202. unsigned int periods = 4;
  203. snd_pcm_uframes_t samplesPerPeriod = bufferSize;
  204. if (JUCE_ALSA_FAILED (snd_pcm_hw_params_set_rate_near (handle, hwParams, &sampleRate, 0))
  205. || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_channels (handle, hwParams, numChannels))
  206. || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_periods_near (handle, hwParams, &periods, &dir))
  207. || JUCE_ALSA_FAILED (snd_pcm_hw_params_set_period_size_near (handle, hwParams, &samplesPerPeriod, &dir))
  208. || JUCE_ALSA_FAILED (snd_pcm_hw_params (handle, hwParams)))
  209. {
  210. return false;
  211. }
  212. snd_pcm_uframes_t frames = 0;
  213. if (JUCE_ALSA_FAILED (snd_pcm_hw_params_get_period_size (hwParams, &frames, &dir))
  214. || JUCE_ALSA_FAILED (snd_pcm_hw_params_get_periods (hwParams, &periods, &dir)))
  215. latency = 0;
  216. else
  217. latency = frames * (periods - 1); // (this is the method JACK uses to guess the latency..)
  218. JUCE_ALSA_LOG ("frames: " << (int) frames << ", periods: " << (int) periods
  219. << ", samplesPerPeriod: " << (int) samplesPerPeriod);
  220. snd_pcm_sw_params_t* swParams;
  221. snd_pcm_sw_params_alloca (&swParams);
  222. snd_pcm_uframes_t boundary;
  223. if (JUCE_ALSA_FAILED (snd_pcm_sw_params_current (handle, swParams))
  224. || JUCE_ALSA_FAILED (snd_pcm_sw_params_get_boundary (swParams, &boundary))
  225. || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_silence_threshold (handle, swParams, 0))
  226. || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_silence_size (handle, swParams, boundary))
  227. || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_start_threshold (handle, swParams, samplesPerPeriod))
  228. || JUCE_ALSA_FAILED (snd_pcm_sw_params_set_stop_threshold (handle, swParams, boundary))
  229. || JUCE_ALSA_FAILED (snd_pcm_sw_params (handle, swParams)))
  230. {
  231. return false;
  232. }
  233. #if JUCE_ALSA_LOGGING
  234. // enable this to dump the config of the devices that get opened
  235. snd_output_t* out;
  236. snd_output_stdio_attach (&out, stderr, 0);
  237. snd_pcm_hw_params_dump (hwParams, out);
  238. snd_pcm_sw_params_dump (swParams, out);
  239. #endif
  240. numChannelsRunning = numChannels;
  241. return true;
  242. }
  243. //==============================================================================
  244. bool writeToOutputDevice (AudioSampleBuffer& outputChannelBuffer, const int numSamples)
  245. {
  246. jassert (numChannelsRunning <= outputChannelBuffer.getNumChannels());
  247. float** const data = outputChannelBuffer.getArrayOfChannels();
  248. snd_pcm_sframes_t numDone = 0;
  249. if (isInterleaved)
  250. {
  251. scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false);
  252. for (int i = 0; i < numChannelsRunning; ++i)
  253. converter->convertSamples (scratch.getData(), i, data[i], 0, numSamples);
  254. numDone = snd_pcm_writei (handle, scratch.getData(), numSamples);
  255. }
  256. else
  257. {
  258. for (int i = 0; i < numChannelsRunning; ++i)
  259. converter->convertSamples (data[i], data[i], numSamples);
  260. numDone = snd_pcm_writen (handle, (void**) data, numSamples);
  261. }
  262. if (numDone < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, numDone, 1 /* silent */)))
  263. return false;
  264. if (numDone < numSamples)
  265. JUCE_ALSA_LOG ("Did not write all samples: numDone: " << numDone << ", numSamples: " << numSamples);
  266. return true;
  267. }
  268. bool readFromInputDevice (AudioSampleBuffer& inputChannelBuffer, const int numSamples)
  269. {
  270. jassert (numChannelsRunning <= inputChannelBuffer.getNumChannels());
  271. float** const data = inputChannelBuffer.getArrayOfChannels();
  272. if (isInterleaved)
  273. {
  274. scratch.ensureSize (sizeof (float) * numSamples * numChannelsRunning, false);
  275. scratch.fillWith (0); // (not clearing this data causes warnings in valgrind)
  276. snd_pcm_sframes_t num = snd_pcm_readi (handle, scratch.getData(), numSamples);
  277. if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, num, 1 /* silent */)))
  278. return false;
  279. if (num < numSamples)
  280. JUCE_ALSA_LOG ("Did not read all samples: num: " << num << ", numSamples: " << numSamples);
  281. for (int i = 0; i < numChannelsRunning; ++i)
  282. converter->convertSamples (data[i], 0, scratch.getData(), i, numSamples);
  283. }
  284. else
  285. {
  286. snd_pcm_sframes_t num = snd_pcm_readn (handle, (void**) data, numSamples);
  287. if (num < 0 && JUCE_ALSA_FAILED (snd_pcm_recover (handle, num, 1 /* silent */)))
  288. return false;
  289. if (num < numSamples)
  290. JUCE_ALSA_LOG ("Did not read all samples: num: " << num << ", numSamples: " << numSamples);
  291. for (int i = 0; i < numChannelsRunning; ++i)
  292. converter->convertSamples (data[i], data[i], numSamples);
  293. }
  294. return true;
  295. }
  296. //==============================================================================
  297. snd_pcm_t* handle;
  298. String error;
  299. int bitDepth, numChannelsRunning, latency;
  300. private:
  301. //==============================================================================
  302. String deviceID;
  303. const bool isInput;
  304. bool isInterleaved;
  305. MemoryBlock scratch;
  306. ScopedPointer<AudioData::Converter> converter;
  307. //==============================================================================
  308. template <class SampleType>
  309. struct ConverterHelper
  310. {
  311. static AudioData::Converter* createConverter (const bool forInput, const bool isLittleEndian, const int numInterleavedChannels)
  312. {
  313. if (forInput)
  314. {
  315. typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DestType;
  316. if (isLittleEndian)
  317. return new AudioData::ConverterInstance <AudioData::Pointer <SampleType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const>, DestType> (numInterleavedChannels, 1);
  318. return new AudioData::ConverterInstance <AudioData::Pointer <SampleType, AudioData::BigEndian, AudioData::Interleaved, AudioData::Const>, DestType> (numInterleavedChannels, 1);
  319. }
  320. else
  321. {
  322. typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SourceType;
  323. if (isLittleEndian)
  324. return new AudioData::ConverterInstance <SourceType, AudioData::Pointer <SampleType, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> > (1, numInterleavedChannels);
  325. return new AudioData::ConverterInstance <SourceType, AudioData::Pointer <SampleType, AudioData::BigEndian, AudioData::Interleaved, AudioData::NonConst> > (1, numInterleavedChannels);
  326. }
  327. }
  328. };
  329. static AudioData::Converter* createConverter (const bool forInput, const int bitDepth, const bool isFloat, const bool isLittleEndian, const int numInterleavedChannels)
  330. {
  331. switch (bitDepth)
  332. {
  333. case 16: return ConverterHelper <AudioData::Int16>::createConverter (forInput, isLittleEndian, numInterleavedChannels);
  334. case 24: return ConverterHelper <AudioData::Int24>::createConverter (forInput, isLittleEndian, numInterleavedChannels);
  335. case 32: return isFloat ? ConverterHelper <AudioData::Float32>::createConverter (forInput, isLittleEndian, numInterleavedChannels)
  336. : ConverterHelper <AudioData::Int32>::createConverter (forInput, isLittleEndian, numInterleavedChannels);
  337. default: jassertfalse; break; // unsupported format!
  338. }
  339. return nullptr;
  340. }
  341. //==============================================================================
  342. bool failed (const int errorNum)
  343. {
  344. if (errorNum >= 0)
  345. return false;
  346. error = snd_strerror (errorNum);
  347. JUCE_ALSA_LOG ("ALSA error: " << error);
  348. return true;
  349. }
  350. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSADevice)
  351. };
  352. //==============================================================================
  353. class ALSAThread : public Thread
  354. {
  355. public:
  356. ALSAThread (const String& inputId_,
  357. const String& outputId_)
  358. : Thread ("Juce ALSA"),
  359. sampleRate (0),
  360. bufferSize (0),
  361. outputLatency (0),
  362. inputLatency (0),
  363. callback (0),
  364. inputId (inputId_),
  365. outputId (outputId_),
  366. numCallbacks (0),
  367. audioIoInProgress (false),
  368. inputChannelBuffer (1, 1),
  369. outputChannelBuffer (1, 1)
  370. {
  371. initialiseRatesAndChannels();
  372. }
  373. ~ALSAThread()
  374. {
  375. close();
  376. }
  377. void open (BigInteger inputChannels,
  378. BigInteger outputChannels,
  379. const double sampleRate_,
  380. const int bufferSize_)
  381. {
  382. close();
  383. error = String::empty;
  384. sampleRate = sampleRate_;
  385. bufferSize = bufferSize_;
  386. inputChannelBuffer.setSize (jmax ((int) minChansIn, inputChannels.getHighestBit()) + 1, bufferSize);
  387. inputChannelBuffer.clear();
  388. inputChannelDataForCallback.clear();
  389. currentInputChans.clear();
  390. if (inputChannels.getHighestBit() >= 0)
  391. {
  392. for (int i = 0; i <= jmax (inputChannels.getHighestBit(), (int) minChansIn); ++i)
  393. {
  394. if (inputChannels[i])
  395. {
  396. inputChannelDataForCallback.add (inputChannelBuffer.getSampleData (i));
  397. currentInputChans.setBit (i);
  398. }
  399. }
  400. }
  401. outputChannelBuffer.setSize (jmax ((int) minChansOut, outputChannels.getHighestBit()) + 1, bufferSize);
  402. outputChannelBuffer.clear();
  403. outputChannelDataForCallback.clear();
  404. currentOutputChans.clear();
  405. if (outputChannels.getHighestBit() >= 0)
  406. {
  407. for (int i = 0; i <= jmax (outputChannels.getHighestBit(), (int) minChansOut); ++i)
  408. {
  409. if (outputChannels[i])
  410. {
  411. outputChannelDataForCallback.add (outputChannelBuffer.getSampleData (i));
  412. currentOutputChans.setBit (i);
  413. }
  414. }
  415. }
  416. if (outputChannelDataForCallback.size() > 0 && outputId.isNotEmpty())
  417. {
  418. outputDevice = new ALSADevice (outputId, false);
  419. if (outputDevice->error.isNotEmpty())
  420. {
  421. error = outputDevice->error;
  422. outputDevice = nullptr;
  423. return;
  424. }
  425. currentOutputChans.setRange (0, minChansOut, true);
  426. if (! outputDevice->setParameters ((unsigned int) sampleRate,
  427. jlimit ((int) minChansOut, (int) maxChansOut, currentOutputChans.getHighestBit() + 1),
  428. bufferSize))
  429. {
  430. error = outputDevice->error;
  431. outputDevice = nullptr;
  432. return;
  433. }
  434. outputLatency = outputDevice->latency;
  435. }
  436. if (inputChannelDataForCallback.size() > 0 && inputId.isNotEmpty())
  437. {
  438. inputDevice = new ALSADevice (inputId, true);
  439. if (inputDevice->error.isNotEmpty())
  440. {
  441. error = inputDevice->error;
  442. inputDevice = nullptr;
  443. return;
  444. }
  445. currentInputChans.setRange (0, minChansIn, true);
  446. if (! inputDevice->setParameters ((unsigned int) sampleRate,
  447. jlimit ((int) minChansIn, (int) maxChansIn, currentInputChans.getHighestBit() + 1),
  448. bufferSize))
  449. {
  450. error = inputDevice->error;
  451. inputDevice = nullptr;
  452. return;
  453. }
  454. inputLatency = inputDevice->latency;
  455. }
  456. if (outputDevice == nullptr && inputDevice == nullptr)
  457. {
  458. error = "no channels";
  459. return;
  460. }
  461. if (outputDevice != nullptr && inputDevice != nullptr)
  462. snd_pcm_link (outputDevice->handle, inputDevice->handle);
  463. if (inputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (inputDevice->handle)))
  464. return;
  465. if (outputDevice != nullptr && JUCE_ALSA_FAILED (snd_pcm_prepare (outputDevice->handle)))
  466. return;
  467. startThread (9);
  468. int count = 1000;
  469. while (numCallbacks == 0)
  470. {
  471. sleep (5);
  472. if (--count < 0 || ! isThreadRunning())
  473. {
  474. error = "device didn't start";
  475. break;
  476. }
  477. }
  478. }
  479. void close()
  480. {
  481. if (isThreadRunning())
  482. {
  483. // problem: when pulseaudio is suspended (with pasuspend) , the ALSAThread::run is just stuck in
  484. // snd_pcm_writei -- no error, no nothing it just stays stuck. So the only way I found to exit "nicely"
  485. // (that is without the "killing thread by force" of stopThread) , is to just call snd_pcm_close from
  486. // here which will cause the thread to resume, and exit
  487. signalThreadShouldExit();
  488. const int callbacksToStop = numCallbacks;
  489. if ((! waitForThreadToExit (400)) && audioIoInProgress && numCallbacks == callbacksToStop)
  490. {
  491. JUCE_ALSA_LOG ("Thread is stuck in i/o.. Is pulseaudio suspended?");
  492. if (outputDevice != nullptr) outputDevice->closeNow();
  493. if (inputDevice != nullptr) inputDevice->closeNow();
  494. }
  495. }
  496. stopThread (6000);
  497. inputDevice = nullptr;
  498. outputDevice = nullptr;
  499. inputChannelBuffer.setSize (1, 1);
  500. outputChannelBuffer.setSize (1, 1);
  501. numCallbacks = 0;
  502. }
  503. void setCallback (AudioIODeviceCallback* const newCallback) noexcept
  504. {
  505. const ScopedLock sl (callbackLock);
  506. callback = newCallback;
  507. }
  508. void run() override
  509. {
  510. while (! threadShouldExit())
  511. {
  512. if (inputDevice != nullptr && inputDevice->handle)
  513. {
  514. audioIoInProgress = true;
  515. if (! inputDevice->readFromInputDevice (inputChannelBuffer, bufferSize))
  516. {
  517. JUCE_ALSA_LOG ("Read failure");
  518. break;
  519. }
  520. audioIoInProgress = false;
  521. }
  522. if (threadShouldExit())
  523. break;
  524. {
  525. const ScopedLock sl (callbackLock);
  526. ++numCallbacks;
  527. if (callback != nullptr)
  528. {
  529. callback->audioDeviceIOCallback ((const float**) inputChannelDataForCallback.getRawDataPointer(),
  530. inputChannelDataForCallback.size(),
  531. outputChannelDataForCallback.getRawDataPointer(),
  532. outputChannelDataForCallback.size(),
  533. bufferSize);
  534. }
  535. else
  536. {
  537. for (int i = 0; i < outputChannelDataForCallback.size(); ++i)
  538. zeromem (outputChannelDataForCallback[i], sizeof (float) * bufferSize);
  539. }
  540. }
  541. if (outputDevice != nullptr && outputDevice->handle)
  542. {
  543. JUCE_ALSA_FAILED (snd_pcm_wait (outputDevice->handle, 2000));
  544. if (threadShouldExit())
  545. break;
  546. snd_pcm_sframes_t avail = snd_pcm_avail_update (outputDevice->handle);
  547. if (avail < 0)
  548. JUCE_ALSA_FAILED (snd_pcm_recover (outputDevice->handle, avail, 0));
  549. audioIoInProgress = true;
  550. if (! outputDevice->writeToOutputDevice (outputChannelBuffer, bufferSize))
  551. {
  552. JUCE_ALSA_LOG ("write failure");
  553. break;
  554. }
  555. audioIoInProgress = false;
  556. }
  557. }
  558. audioIoInProgress = false;
  559. }
  560. int getBitDepth() const noexcept
  561. {
  562. if (outputDevice != nullptr)
  563. return outputDevice->bitDepth;
  564. if (inputDevice != nullptr)
  565. return inputDevice->bitDepth;
  566. return 16;
  567. }
  568. //==============================================================================
  569. String error;
  570. double sampleRate;
  571. int bufferSize, outputLatency, inputLatency;
  572. BigInteger currentInputChans, currentOutputChans;
  573. Array <int> sampleRates;
  574. StringArray channelNamesOut, channelNamesIn;
  575. AudioIODeviceCallback* callback;
  576. private:
  577. //==============================================================================
  578. const String inputId, outputId;
  579. ScopedPointer<ALSADevice> outputDevice, inputDevice;
  580. int numCallbacks;
  581. bool audioIoInProgress;
  582. CriticalSection callbackLock;
  583. AudioSampleBuffer inputChannelBuffer, outputChannelBuffer;
  584. Array<float*> inputChannelDataForCallback, outputChannelDataForCallback;
  585. unsigned int minChansOut, maxChansOut;
  586. unsigned int minChansIn, maxChansIn;
  587. bool failed (const int errorNum)
  588. {
  589. if (errorNum >= 0)
  590. return false;
  591. error = snd_strerror (errorNum);
  592. JUCE_ALSA_LOG ("ALSA error: " << error);
  593. return true;
  594. }
  595. void initialiseRatesAndChannels()
  596. {
  597. sampleRates.clear();
  598. channelNamesOut.clear();
  599. channelNamesIn.clear();
  600. minChansOut = 0;
  601. maxChansOut = 0;
  602. minChansIn = 0;
  603. maxChansIn = 0;
  604. unsigned int dummy = 0;
  605. getDeviceProperties (inputId, dummy, dummy, minChansIn, maxChansIn, sampleRates, false, true);
  606. getDeviceProperties (outputId, minChansOut, maxChansOut, dummy, dummy, sampleRates, true, false);
  607. for (unsigned int i = 0; i < maxChansOut; ++i)
  608. channelNamesOut.add ("channel " + String ((int) i + 1));
  609. for (unsigned int i = 0; i < maxChansIn; ++i)
  610. channelNamesIn.add ("channel " + String ((int) i + 1));
  611. }
  612. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAThread)
  613. };
  614. //==============================================================================
  615. class ALSAAudioIODevice : public AudioIODevice
  616. {
  617. public:
  618. ALSAAudioIODevice (const String& deviceName,
  619. const String& typeName,
  620. const String& inputId_,
  621. const String& outputId_)
  622. : AudioIODevice (deviceName, typeName),
  623. inputId (inputId_),
  624. outputId (outputId_),
  625. isOpen_ (false),
  626. isStarted (false),
  627. internal (inputId_, outputId_)
  628. {
  629. }
  630. ~ALSAAudioIODevice()
  631. {
  632. close();
  633. }
  634. StringArray getOutputChannelNames() { return internal.channelNamesOut; }
  635. StringArray getInputChannelNames() { return internal.channelNamesIn; }
  636. int getNumSampleRates() { return internal.sampleRates.size(); }
  637. double getSampleRate (int index) { return internal.sampleRates [index]; }
  638. int getDefaultBufferSize() { return 512; }
  639. int getNumBufferSizesAvailable() { return 50; }
  640. int getBufferSizeSamples (int index)
  641. {
  642. int n = 16;
  643. for (int i = 0; i < index; ++i)
  644. n += n < 64 ? 16
  645. : (n < 512 ? 32
  646. : (n < 1024 ? 64
  647. : (n < 2048 ? 128 : 256)));
  648. return n;
  649. }
  650. String open (const BigInteger& inputChannels,
  651. const BigInteger& outputChannels,
  652. double sampleRate,
  653. int bufferSizeSamples)
  654. {
  655. close();
  656. if (bufferSizeSamples <= 0)
  657. bufferSizeSamples = getDefaultBufferSize();
  658. if (sampleRate <= 0)
  659. {
  660. for (int i = 0; i < getNumSampleRates(); ++i)
  661. {
  662. if (getSampleRate (i) >= 44100)
  663. {
  664. sampleRate = getSampleRate (i);
  665. break;
  666. }
  667. }
  668. }
  669. internal.open (inputChannels, outputChannels,
  670. sampleRate, bufferSizeSamples);
  671. isOpen_ = internal.error.isEmpty();
  672. return internal.error;
  673. }
  674. void close()
  675. {
  676. stop();
  677. internal.close();
  678. isOpen_ = false;
  679. }
  680. bool isOpen() { return isOpen_; }
  681. bool isPlaying() { return isStarted && internal.error.isEmpty(); }
  682. String getLastError() { return internal.error; }
  683. int getCurrentBufferSizeSamples() { return internal.bufferSize; }
  684. double getCurrentSampleRate() { return internal.sampleRate; }
  685. int getCurrentBitDepth() { return internal.getBitDepth(); }
  686. BigInteger getActiveOutputChannels() const { return internal.currentOutputChans; }
  687. BigInteger getActiveInputChannels() const { return internal.currentInputChans; }
  688. int getOutputLatencyInSamples() { return internal.outputLatency; }
  689. int getInputLatencyInSamples() { return internal.inputLatency; }
  690. void start (AudioIODeviceCallback* callback)
  691. {
  692. if (! isOpen_)
  693. callback = nullptr;
  694. if (callback != nullptr)
  695. callback->audioDeviceAboutToStart (this);
  696. internal.setCallback (callback);
  697. isStarted = (callback != nullptr);
  698. }
  699. void stop()
  700. {
  701. AudioIODeviceCallback* const oldCallback = internal.callback;
  702. start (nullptr);
  703. if (oldCallback != nullptr)
  704. oldCallback->audioDeviceStopped();
  705. }
  706. String inputId, outputId;
  707. private:
  708. bool isOpen_, isStarted;
  709. ALSAThread internal;
  710. };
  711. //==============================================================================
  712. class ALSAAudioIODeviceType : public AudioIODeviceType
  713. {
  714. public:
  715. ALSAAudioIODeviceType (bool onlySoundcards, const String &typeName)
  716. : AudioIODeviceType (typeName),
  717. hasScanned (false),
  718. listOnlySoundcards (onlySoundcards)
  719. {
  720. #if ! JUCE_ALSA_LOGGING
  721. snd_lib_error_set_handler (&silentErrorHandler);
  722. #endif
  723. }
  724. ~ALSAAudioIODeviceType()
  725. {
  726. #if ! JUCE_ALSA_LOGGING
  727. snd_lib_error_set_handler (nullptr);
  728. #endif
  729. snd_config_update_free_global(); // prevent valgrind from screaming about alsa leaks
  730. }
  731. //==============================================================================
  732. void scanForDevices()
  733. {
  734. if (hasScanned)
  735. return;
  736. hasScanned = true;
  737. inputNames.clear();
  738. inputIds.clear();
  739. outputNames.clear();
  740. outputIds.clear();
  741. JUCE_ALSA_LOG ("scanForDevices()");
  742. if (listOnlySoundcards)
  743. enumerateAlsaSoundcards();
  744. else
  745. enumerateAlsaPCMDevices();
  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. const int idx = (forInput ? inputIds : outputIds).indexOf ("default");
  758. return idx >= 0 ? idx : 0;
  759. }
  760. bool hasSeparateInputsAndOutputs() const { return true; }
  761. int getIndexOfDevice (AudioIODevice* device, bool asInput) const
  762. {
  763. jassert (hasScanned); // need to call scanForDevices() before doing this
  764. if (ALSAAudioIODevice* d = dynamic_cast <ALSAAudioIODevice*> (device))
  765. return asInput ? inputIds.indexOf (d->inputId)
  766. : outputIds.indexOf (d->outputId);
  767. return -1;
  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, getTypeName(),
  779. inputIds [inputIndex],
  780. outputIds [outputIndex]);
  781. return nullptr;
  782. }
  783. private:
  784. //==============================================================================
  785. StringArray inputNames, outputNames, inputIds, outputIds;
  786. bool hasScanned, listOnlySoundcards;
  787. bool testDevice (const String &id, const String &outputName, const String &inputName)
  788. {
  789. unsigned int minChansOut = 0, maxChansOut = 0;
  790. unsigned int minChansIn = 0, maxChansIn = 0;
  791. Array <int> rates;
  792. bool isInput = inputName.isNotEmpty(), isOutput = outputName.isNotEmpty();
  793. getDeviceProperties (id, minChansOut, maxChansOut, minChansIn, maxChansIn, rates, isOutput, isInput);
  794. isInput = maxChansIn > 0;
  795. isOutput = maxChansOut > 0;
  796. if ((isInput || isOutput) && rates.size() > 0)
  797. {
  798. JUCE_ALSA_LOG ("testDevice: '" << id.toUTF8().getAddress() << "' -> isInput: "
  799. << isInput << ", isOutput: " << isOutput);
  800. if (isInput)
  801. {
  802. inputNames.add (inputName);
  803. inputIds.add (id);
  804. }
  805. if (isOutput)
  806. {
  807. outputNames.add (outputName);
  808. outputIds.add (id);
  809. }
  810. return isInput || isOutput;
  811. }
  812. return false;
  813. }
  814. void enumerateAlsaSoundcards()
  815. {
  816. snd_ctl_t* handle = nullptr;
  817. snd_ctl_card_info_t* info = nullptr;
  818. snd_ctl_card_info_alloca (&info);
  819. int cardNum = -1;
  820. while (outputIds.size() + inputIds.size() <= 64)
  821. {
  822. snd_card_next (&cardNum);
  823. if (cardNum < 0)
  824. break;
  825. if (JUCE_CHECKED_RESULT (snd_ctl_open (&handle, ("hw:" + String (cardNum)).toUTF8(), SND_CTL_NONBLOCK)) >= 0)
  826. {
  827. if (JUCE_CHECKED_RESULT (snd_ctl_card_info (handle, info)) >= 0)
  828. {
  829. String cardId (snd_ctl_card_info_get_id (info));
  830. if (cardId.removeCharacters ("0123456789").isEmpty())
  831. cardId = String (cardNum);
  832. String cardName = snd_ctl_card_info_get_name (info);
  833. if (cardName.isEmpty())
  834. cardName = cardId;
  835. int device = -1;
  836. snd_pcm_info_t* pcmInfo;
  837. snd_pcm_info_alloca (&pcmInfo);
  838. for (;;)
  839. {
  840. if (snd_ctl_pcm_next_device (handle, &device) < 0 || device < 0)
  841. break;
  842. snd_pcm_info_set_device (pcmInfo, device);
  843. for (int subDevice = 0, nbSubDevice = 1; subDevice < nbSubDevice; ++subDevice)
  844. {
  845. snd_pcm_info_set_subdevice (pcmInfo, subDevice);
  846. snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_CAPTURE);
  847. const bool isInput = (snd_ctl_pcm_info (handle, pcmInfo) >= 0);
  848. snd_pcm_info_set_stream (pcmInfo, SND_PCM_STREAM_PLAYBACK);
  849. const bool isOutput = (snd_ctl_pcm_info (handle, pcmInfo) >= 0);
  850. if (! (isInput || isOutput))
  851. continue;
  852. if (nbSubDevice == 1)
  853. nbSubDevice = snd_pcm_info_get_subdevices_count (pcmInfo);
  854. String id, name;
  855. if (nbSubDevice == 1)
  856. {
  857. id << "hw:" << cardId << "," << device;
  858. name << cardName << ", " << snd_pcm_info_get_name (pcmInfo);
  859. }
  860. else
  861. {
  862. id << "hw:" << cardId << "," << device << "," << subDevice;
  863. name << cardName << ", " << snd_pcm_info_get_name (pcmInfo)
  864. << " {" << snd_pcm_info_get_subdevice_name (pcmInfo) << "}";
  865. }
  866. JUCE_ALSA_LOG ("Soundcard ID: " << id << ", name: '" << name
  867. << ", isInput:" << isInput << ", isOutput:" << isOutput << "\n");
  868. if (isInput)
  869. {
  870. inputNames.add (name);
  871. inputIds.add (id);
  872. }
  873. if (isOutput)
  874. {
  875. outputNames.add (name);
  876. outputIds.add (id);
  877. }
  878. }
  879. }
  880. }
  881. JUCE_CHECKED_RESULT (snd_ctl_close (handle));
  882. }
  883. }
  884. }
  885. /* Enumerates all ALSA output devices (as output by the command aplay -L)
  886. Does not try to open the devices (with "testDevice" for example),
  887. so that it also finds devices that are busy and not yet available.
  888. */
  889. void enumerateAlsaPCMDevices()
  890. {
  891. void** hints = nullptr;
  892. if (JUCE_CHECKED_RESULT (snd_device_name_hint (-1, "pcm", &hints)) == 0)
  893. {
  894. for (char** h = (char**) hints; *h; ++h)
  895. {
  896. const String id (hintToString (*h, "NAME"));
  897. const String description (hintToString (*h, "DESC"));
  898. const String ioid (hintToString (*h, "IOID"));
  899. JUCE_ALSA_LOG ("ID: " << id << "; desc: " << description << "; ioid: " << ioid);
  900. String ss = id.fromFirstOccurrenceOf ("=", false, false)
  901. .upToFirstOccurrenceOf (",", false, false);
  902. if (id.isEmpty()
  903. || id.startsWith ("default:") || id.startsWith ("sysdefault:")
  904. || id.startsWith ("plughw:") || id == "null")
  905. continue;
  906. String name (description.replace ("\n", "; "));
  907. if (name.isEmpty())
  908. name = id;
  909. bool isOutput = (ioid != "Input");
  910. bool isInput = (ioid != "Output");
  911. // alsa is stupid here, it advertises dmix and dsnoop as input/output devices, but
  912. // opening dmix as input, or dsnoop as output will trigger errors..
  913. isInput = isInput && ! id.startsWith ("dmix");
  914. isOutput = isOutput && ! id.startsWith ("dsnoop");
  915. if (isInput)
  916. {
  917. inputNames.add (name);
  918. inputIds.add (id);
  919. }
  920. if (isOutput)
  921. {
  922. outputNames.add (name);
  923. outputIds.add (id);
  924. }
  925. }
  926. snd_device_name_free_hint (hints);
  927. }
  928. // sometimes the "default" device is not listed, but it is nice to see it explicitely in the list
  929. if (! outputIds.contains ("default"))
  930. testDevice ("default", "Default ALSA Output", "Default ALSA Input");
  931. // same for the pulseaudio plugin
  932. if (! outputIds.contains ("pulse"))
  933. testDevice ("pulse", "Pulseaudio output", "Pulseaudio input");
  934. // make sure the default device is listed first, and followed by the pulse device (if present)
  935. int idx = outputIds.indexOf ("pulse");
  936. outputIds.move (idx, 0);
  937. outputNames.move (idx, 0);
  938. idx = inputIds.indexOf ("pulse");
  939. inputIds.move (idx, 0);
  940. inputNames.move (idx, 0);
  941. idx = outputIds.indexOf ("default");
  942. outputIds.move (idx, 0);
  943. outputNames.move (idx, 0);
  944. idx = inputIds.indexOf ("default");
  945. inputIds.move (idx, 0);
  946. inputNames.move (idx, 0);
  947. }
  948. static String hintToString (const void* hints, const char* type)
  949. {
  950. char* const hint = snd_device_name_get_hint (hints, type);
  951. const String s (hint);
  952. ::free (hint);
  953. return s;
  954. }
  955. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ALSAAudioIODeviceType)
  956. };
  957. }
  958. //==============================================================================
  959. AudioIODeviceType* createAudioIODeviceType_ALSA_Soundcards()
  960. {
  961. return new ALSAAudioIODeviceType (true, "ALSA HW");
  962. }
  963. AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices()
  964. {
  965. return new ALSAAudioIODeviceType (false, "ALSA");
  966. }
  967. AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA()
  968. {
  969. return createAudioIODeviceType_ALSA_PCMDevices();
  970. }