Audio plugin host https://kx.studio/carla
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.

juce_linux_ALSA.cpp 46KB

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