29#ifndef JUCE_ALSA_LOGGING
30 #define JUCE_ALSA_LOGGING 0
34 #define JUCE_ALSA_LOG(dbgtext) { juce::String tempDbgBuf ("ALSA: "); tempDbgBuf << dbgtext; Logger::writeToLog (tempDbgBuf); DBG (tempDbgBuf); }
35 #define JUCE_CHECKED_RESULT(x) (logErrorMessage (x, __LINE__))
45 #define JUCE_ALSA_LOG(x) {}
46 #define JUCE_CHECKED_RESULT(x) (x)
49#define JUCE_ALSA_FAILED(x) failed (x)
56 for (
const auto rateToTry : SampleRateHelpers::getAllSampleRates())
76 JUCE_ALSA_LOG (
"getDeviceNumChannels: " << (
int) *
minChans <<
" " << (
int) *
maxChans);
84 JUCE_ALSA_LOG (
"getDeviceNumChannels failed");
89 unsigned int& minChansOut,
90 unsigned int& maxChansOut,
91 unsigned int& minChansIn,
92 unsigned int& maxChansIn,
97 minChansOut = maxChansOut = minChansIn = maxChansIn = 0;
99 if (deviceID.isEmpty())
102 JUCE_ALSA_LOG (
"getDeviceProperties(" << deviceID.toUTF8().getAddress() <<
")");
128 if (
rates.size() == 0)
144static void silentErrorHandler (
const char*,
int,
const char*,
int,
const char*,...) {}
153 numChannelsRunning (0),
159 JUCE_ALSA_LOG (
"snd_pcm_open (" << deviceID.toUTF8().getAddress() <<
", forInput=" << (
int)
forInput <<
")");
167 error <<
"The device \"" << deviceID <<
"\" is busy (another application is using it).";
168 else if (-
err == ENOENT)
169 error <<
"The device \"" << deviceID <<
"\" is not available.";
171 error <<
"Could not open " << (
forInput ?
"input" :
"output") <<
" device \"" << deviceID
174 JUCE_ALSA_LOG (
"snd_pcm_open failed; " << error);
185 if (handle !=
nullptr)
192 bool setParameters (
unsigned int sampleRate,
int numChannels,
int bufferSize)
194 if (handle ==
nullptr)
197 JUCE_ALSA_LOG (
"ALSADevice::setParameters(" << deviceID <<
", "
198 << (
int) sampleRate <<
", " << numChannels <<
", " << bufferSize <<
")");
207 error =
"Broken configuration for this PCM: no configurations available";
212 isInterleaved =
true;
214 isInterleaved =
false;
239 bitDepth = type & 255;
253 error =
"device doesn't support a compatible PCM format";
254 JUCE_ALSA_LOG (
"Error: " + error);
279 JUCE_ALSA_LOG (
"frames: " << (
int)
frames <<
", periods: " << (
int)
periods
297 #if JUCE_ALSA_LOGGING
305 numChannelsRunning = numChannels;
313 jassert (numChannelsRunning <= outputChannelBuffer.getNumChannels());
314 float*
const*
const data = outputChannelBuffer.getArrayOfWritePointers();
319 scratch.ensureSize ((
size_t) ((
int)
sizeof (
float) * numSamples * numChannelsRunning),
false);
321 for (
int i = 0; i < numChannelsRunning; ++i)
322 converter->convertSamples (scratch.getData(), i, data[i], 0, numSamples);
328 for (
int i = 0; i < numChannelsRunning; ++i)
329 converter->convertSamples (data[i], data[i], numSamples);
344 JUCE_ALSA_LOG (
"Did not write all samples: numDone: " <<
numDone <<
", numSamples: " << numSamples);
351 jassert (numChannelsRunning <= inputChannelBuffer.getNumChannels());
352 float*
const*
const data = inputChannelBuffer.getArrayOfWritePointers();
356 scratch.ensureSize ((
size_t) ((
int)
sizeof (
float) * numSamples * numChannelsRunning),
false);
357 scratch.fillWith (0);
371 if (num < numSamples)
372 JUCE_ALSA_LOG (
"Did not read all samples: num: " << num <<
", numSamples: " << numSamples);
374 for (
int i = 0; i < numChannelsRunning; ++i)
375 converter->convertSamples (data[i], 0, scratch.getData(), i, numSamples);
390 if (num < numSamples)
391 JUCE_ALSA_LOG (
"Did not read all samples: num: " << num <<
", numSamples: " << numSamples);
393 for (
int i = 0; i < numChannelsRunning; ++i)
394 converter->convertSamples (data[i], data[i], numSamples);
403 int bitDepth, numChannelsRunning, latency;
404 int underrunCount = 0, overrunCount = 0;
415 template <
class SampleType>
416 struct ConverterHelper
427 template <
class InterleavedType>
432 using DestType = AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
435 return new AudioData::ConverterInstance <AudioData::Pointer <SampleType, AudioData::LittleEndian, InterleavedType, AudioData::Const>, DestType> (
numInterleavedChannels, 1);
437 return new AudioData::ConverterInstance <AudioData::Pointer <SampleType, AudioData::BigEndian, InterleavedType, AudioData::Const>, DestType> (
numInterleavedChannels, 1);
440 using SourceType = AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
443 return new AudioData::ConverterInstance <SourceType, AudioData::Pointer <SampleType, AudioData::LittleEndian, InterleavedType, AudioData::NonConst>> (1,
numInterleavedChannels);
445 return new AudioData::ConverterInstance <SourceType, AudioData::Pointer <SampleType, AudioData::BigEndian, InterleavedType, AudioData::NonConst>> (1,
numInterleavedChannels);
454 JUCE_ALSA_LOG (
"format: bitDepth=" << bitDepth <<
", isFloat=" << (
int)
isFloat
476 JUCE_ALSA_LOG (
"ALSA error: " << error);
484class ALSAThread
final :
public Thread
488 : Thread (
"JUCE ALSA"),
500 void open (BigInteger inputChannels,
501 BigInteger outputChannels,
515 inputChannelBuffer.clear();
516 inputChannelDataForCallback.clear();
517 currentInputChans.clear();
519 if (inputChannels.getHighestBit() >= 0)
523 if (inputChannels[i])
525 inputChannelDataForCallback.add (inputChannelBuffer.getReadPointer (i));
526 currentInputChans.setBit (i);
537 outputChannelBuffer.clear();
538 outputChannelDataForCallback.clear();
539 currentOutputChans.clear();
545 if (inputChannelDataForCallback.size() > 0 && inputId.isNotEmpty())
547 inputDevice.reset (
new ALSADevice (inputId,
true));
549 if (inputDevice->error.isNotEmpty())
551 error = inputDevice->error;
558 if (! inputDevice->setParameters ((
unsigned int) sampleRate,
559 jlimit ((
int) minChansIn, (
int) maxChansIn, currentInputChans.getHighestBit() + 1),
562 error = inputDevice->error;
567 inputLatency = inputDevice->latency;
570 if (outputChannels.getHighestBit() >= 0)
574 if (outputChannels[i])
576 outputChannelDataForCallback.add (outputChannelBuffer.getWritePointer (i));
577 currentOutputChans.setBit (i);
582 if (outputChannelDataForCallback.size() > 0 && outputId.isNotEmpty())
584 outputDevice.reset (
new ALSADevice (outputId,
false));
586 if (outputDevice->error.isNotEmpty())
588 error = outputDevice->error;
589 outputDevice.reset();
593 if (! outputDevice->setParameters ((
unsigned int) sampleRate,
594 jlimit ((
int) minChansOut, (
int) maxChansOut,
595 currentOutputChans.getHighestBit() + 1),
598 error = outputDevice->error;
599 outputDevice.reset();
603 outputLatency = outputDevice->latency;
606 if (outputDevice ==
nullptr && inputDevice ==
nullptr)
608 error =
"no channels";
612 if (outputDevice !=
nullptr && inputDevice !=
nullptr)
613 snd_pcm_link (outputDevice->handle, inputDevice->handle);
615 if (inputDevice !=
nullptr && JUCE_ALSA_FAILED (
snd_pcm_prepare (inputDevice->handle)))
618 if (outputDevice !=
nullptr && JUCE_ALSA_FAILED (
snd_pcm_prepare (outputDevice->handle)))
621 startThread (Priority::high);
625 while (numCallbacks == 0)
629 if (--count < 0 || ! isThreadRunning())
631 error =
"device didn't start";
639 if (isThreadRunning())
645 signalThreadShouldExit();
649 if ((! waitForThreadToExit (400)) && audioIoInProgress && numCallbacks ==
callbacksToStop)
651 JUCE_ALSA_LOG (
"Thread is stuck in i/o.. Is pulseaudio suspended?");
653 if (outputDevice !=
nullptr) outputDevice->closeNow();
654 if (inputDevice !=
nullptr) inputDevice->closeNow();
661 outputDevice.reset();
663 inputChannelBuffer.setSize (1, 1);
664 outputChannelBuffer.setSize (1, 1);
677 while (! threadShouldExit())
679 if (inputDevice !=
nullptr && inputDevice->handle !=
nullptr)
681 if (outputDevice ==
nullptr || outputDevice->handle ==
nullptr)
683 JUCE_ALSA_FAILED (
snd_pcm_wait (inputDevice->handle, 2000));
685 if (threadShouldExit())
694 audioIoInProgress =
true;
696 if (! inputDevice->readFromInputDevice (inputChannelBuffer, bufferSize))
698 JUCE_ALSA_LOG (
"Read failure");
702 audioIoInProgress =
false;
705 if (threadShouldExit())
712 if (callback !=
nullptr)
714 callback->audioDeviceIOCallbackWithContext (inputChannelDataForCallback.getRawDataPointer(),
715 inputChannelDataForCallback.size(),
716 outputChannelDataForCallback.getRawDataPointer(),
717 outputChannelDataForCallback.size(),
723 for (
int i = 0; i < outputChannelDataForCallback.size(); ++i)
724 zeromem (outputChannelDataForCallback[i], (
size_t) bufferSize *
sizeof (
float));
728 if (outputDevice !=
nullptr && outputDevice->handle !=
nullptr)
730 JUCE_ALSA_FAILED (
snd_pcm_wait (outputDevice->handle, 2000));
732 if (threadShouldExit())
740 audioIoInProgress =
true;
742 if (! outputDevice->writeToOutputDevice (outputChannelBuffer, bufferSize))
744 JUCE_ALSA_LOG (
"write failure");
748 audioIoInProgress =
false;
752 audioIoInProgress =
false;
757 if (outputDevice !=
nullptr)
758 return outputDevice->bitDepth;
760 if (inputDevice !=
nullptr)
761 return inputDevice->bitDepth;
766 int getXRunCount()
const noexcept
770 if (outputDevice !=
nullptr)
771 result += outputDevice->underrunCount;
773 if (inputDevice !=
nullptr)
774 result += inputDevice->overrunCount;
781 double sampleRate = 0;
782 int bufferSize = 0, outputLatency = 0, inputLatency = 0;
783 BigInteger currentInputChans, currentOutputChans;
785 Array<double> sampleRates;
786 StringArray channelNamesOut, channelNamesIn;
787 AudioIODeviceCallback* callback =
nullptr;
791 const String inputId, outputId;
796 CriticalSection callbackLock;
798 AudioBuffer<float> inputChannelBuffer, outputChannelBuffer;
802 unsigned int minChansOut = 0, maxChansOut = 0;
803 unsigned int minChansIn = 0, maxChansIn = 0;
811 JUCE_ALSA_LOG (
"ALSA error: " << error);
818 channelNamesOut.clear();
819 channelNamesIn.clear();
824 unsigned int dummy = 0;
829 for (
unsigned int i = 0; i < maxChansOut; ++i)
830 channelNamesOut.add (
"channel " + String ((
int) i + 1));
832 for (
unsigned int i = 0; i < maxChansIn; ++i)
833 channelNamesIn.add (
"channel " + String ((
int) i + 1));
841class ALSAAudioIODevice
final :
public AudioIODevice
860 StringArray getOutputChannelNames()
override {
return internal.channelNamesOut; }
861 StringArray getInputChannelNames()
override {
return internal.channelNamesIn; }
863 Array<double> getAvailableSampleRates()
override {
return internal.sampleRates; }
865 Array<int> getAvailableBufferSizes()
override
870 for (
int i = 0; i < 50; ++i)
876 : (n < 2048 ? 128 : 256)));
882 int getDefaultBufferSize()
override {
return 512; }
884 String
open (
const BigInteger& inputChannels,
885 const BigInteger& outputChannels,
896 for (
int i = 0; i <
internal.sampleRates.size(); ++i)
898 double rate =
internal.sampleRates[i];
908 internal.open (inputChannels, outputChannels,
915 void close()
override
922 bool isOpen()
override {
return isOpen_; }
923 bool isPlaying()
override {
return isStarted &&
internal.error.isEmpty(); }
924 String getLastError()
override {
return internal.error; }
926 int getCurrentBufferSizeSamples()
override {
return internal.bufferSize; }
927 double getCurrentSampleRate()
override {
return internal.sampleRate; }
928 int getCurrentBitDepth()
override {
return internal.getBitDepth(); }
930 BigInteger getActiveOutputChannels()
const override {
return internal.currentOutputChans; }
931 BigInteger getActiveInputChannels()
const override {
return internal.currentInputChans; }
933 int getOutputLatencyInSamples()
override {
return internal.outputLatency; }
934 int getInputLatencyInSamples()
override {
return internal.inputLatency; }
936 int getXRunCount()
const noexcept
override {
return internal.getXRunCount(); }
938 void start (AudioIODeviceCallback* callback)
override
943 if (callback !=
nullptr)
944 callback->audioDeviceAboutToStart (
this);
948 isStarted = (callback !=
nullptr);
961 String inputId, outputId;
964 bool isOpen_ =
false, isStarted =
false;
970class ALSAAudioIODeviceType
final :
public AudioIODeviceType
977 #if ! JUCE_ALSA_LOGGING
984 #if ! JUCE_ALSA_LOGGING
992 void scanForDevices()
override
1000 outputNames.clear();
1003 JUCE_ALSA_LOG (
"scanForDevices()");
1005 if (listOnlySoundcards)
1010 inputNames.appendNumbersToDuplicates (
false,
true);
1011 outputNames.appendNumbersToDuplicates (
false,
true);
1021 int getDefaultDeviceIndex (
bool forInput)
const override
1025 auto idx = (
forInput ? inputIds : outputIds).indexOf (
"default");
1026 return idx >= 0 ? idx : 0;
1029 bool hasSeparateInputsAndOutputs()
const override {
return true; }
1031 int getIndexOfDevice (AudioIODevice* device,
bool asInput)
const override
1035 if (
auto* d =
dynamic_cast<ALSAAudioIODevice*
> (device))
1036 return asInput ? inputIds.indexOf (d->inputId)
1037 : outputIds.indexOf (d->outputId);
1042 AudioIODevice* createDevice (
const String& outputDeviceName,
1043 const String& inputDeviceName)
override
1047 auto inputIndex = inputNames.indexOf (inputDeviceName);
1048 auto outputIndex = outputNames.indexOf (outputDeviceName);
1054 return new ALSAAudioIODevice (
deviceName, getTypeName(),
1063 StringArray inputNames, outputNames, inputIds, outputIds;
1064 bool hasScanned =
false;
1065 const bool listOnlySoundcards;
1069 unsigned int minChansOut = 0, maxChansOut = 0;
1070 unsigned int minChansIn = 0, maxChansIn = 0;
1071 Array<double>
rates;
1076 isInput = maxChansIn > 0;
1077 isOutput = maxChansOut > 0;
1079 if ((isInput || isOutput) &&
rates.size() > 0)
1081 JUCE_ALSA_LOG (
"testDevice: '" <<
id.toUTF8().getAddress() <<
"' -> isInput: "
1082 << (
int) isInput <<
", isOutput: " << (
int) isOutput);
1096 return isInput || isOutput;
1110 while (outputIds.size() + inputIds.size() <= 64)
1123 if (
cardId.removeCharacters (
"0123456789").isEmpty())
1152 if (! (isInput || isOutput))
1162 id <<
"hw:" <<
cardId <<
"," << device;
1172 JUCE_ALSA_LOG (
"Soundcard ID: " <<
id <<
", name: '" << name
1173 <<
", isInput:" << (
int) isInput
1174 <<
", isOutput:" << (
int) isOutput <<
"\n");
1178 inputNames.add (name);
1184 outputNames.add (name);
1202 void**
hints =
nullptr;
1206 for (
char** h = (
char**)
hints; *h; ++h)
1212 JUCE_ALSA_LOG (
"ID: " <<
id <<
"; desc: " << description <<
"; ioid: " <<
ioid);
1214 String
ss =
id.fromFirstOccurrenceOf (
"=",
false,
false)
1215 .upToFirstOccurrenceOf (
",",
false,
false);
1218 ||
id.startsWith (
"default:") ||
id.startsWith (
"sysdefault:")
1219 ||
id.startsWith (
"plughw:") ||
id ==
"null")
1222 String name (description.replace (
"\n",
"; "));
1227 bool isOutput = (
ioid !=
"Input");
1228 bool isInput = (
ioid !=
"Output");
1232 isInput = isInput && !
id.startsWith (
"dmix");
1233 isOutput = isOutput && !
id.startsWith (
"dsnoop");
1237 inputNames.add (name);
1243 outputNames.add (name);
1252 if (! outputIds.contains (
"default"))
1253 testDevice (
"default",
"Default ALSA Output",
"Default ALSA Input");
1256 if (! outputIds.contains (
"pulse"))
1257 testDevice (
"pulse",
"Pulseaudio output",
"Pulseaudio input");
1260 auto idx = outputIds.indexOf (
"pulse");
1261 outputIds.move (idx, 0);
1262 outputNames.move (idx, 0);
1264 idx = inputIds.indexOf (
"pulse");
1265 inputIds.move (idx, 0);
1266 inputNames.move (idx, 0);
1268 idx = outputIds.indexOf (
"default");
1269 outputIds.move (idx, 0);
1270 outputNames.move (idx, 0);
1272 idx = inputIds.indexOf (
"default");
1273 inputIds.move (idx, 0);
1274 inputNames.move (idx, 0);
1291static inline AudioIODeviceType* createAudioIODeviceType_ALSA_Soundcards()
1293 return new ALSAAudioIODeviceType (
true,
"ALSA HW");
1296static inline AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices()
1298 return new ALSAAudioIODeviceType (
false,
"ALSA");
static String fromUTF8(const char *utf8buffer, int bufferSizeBytes=-1)
Creates a String from a UTF-8 encoded buffer.
CriticalSection::ScopedLockType ScopedLock
Automatically locks and unlocks a CriticalSection object.
constexpr Type jmin(Type a, Type b)
Returns the smaller of two values.
constexpr Type jmax(Type a, Type b)
Returns the larger of two values.
Type jlimit(Type lowerLimit, Type upperLimit, Type valueToConstrain) noexcept
Constrains a value to keep it within a given range.
Type unalignedPointerCast(void *ptr) noexcept
Casts a pointer to another type via void*, which suppresses the cast-align warning which sometimes ar...
constexpr int numElementsInArray(Type(&)[N]) noexcept
Handy function for getting the number of elements in a simple const C array.
void zeromem(void *memory, size_t numBytes) noexcept
Fills a block of memory with zeros.