diff --git a/modules/juce_audio_devices/juce_audio_devices.cpp b/modules/juce_audio_devices/juce_audio_devices.cpp index 67f36912dd..9acaab9607 100644 --- a/modules/juce_audio_devices/juce_audio_devices.cpp +++ b/modules/juce_audio_devices/juce_audio_devices.cpp @@ -148,7 +148,9 @@ installed, or you've not got your paths set up correctly to find its header files. */ + #include #include + #include #endif #undef SIZEOF @@ -210,14 +212,14 @@ #include "native/juce_linux_ALSA.cpp" #endif - #include "native/juce_linux_Midi.cpp" - #if JUCE_JACK #include "native/juce_linux_JackAudio.cpp" #endif #if JUCE_BELA #include "native/juce_linux_Bela.cpp" + #else + #include "native/juce_linux_Midi.cpp" #endif //============================================================================== diff --git a/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h b/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h index c7f7f4f63a..92a302b5f1 100644 --- a/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h +++ b/modules/juce_audio_devices/native/juce_MidiDataConcatenator.h @@ -171,7 +171,7 @@ private: } } - uint8 currentMessage[3] = {}; + uint8 currentMessage[3]; int currentMessageLen = 0; MemoryBlock pendingSysexData; diff --git a/modules/juce_audio_devices/native/juce_linux_ALSA.cpp b/modules/juce_audio_devices/native/juce_linux_ALSA.cpp index b971afc725..d1ecf48333 100644 --- a/modules/juce_audio_devices/native/juce_linux_ALSA.cpp +++ b/modules/juce_audio_devices/native/juce_linux_ALSA.cpp @@ -1115,7 +1115,7 @@ private: if (cardNum < 0) break; - if (JUCE_CHECKED_RESULT (snd_ctl_open (&handle, ("hw:" + String (cardNum)).toUTF8(), SND_CTL_NONBLOCK)) >= 0) + if (JUCE_CHECKED_RESULT (snd_ctl_open (&handle, ("hw:" + String (cardNum)).toRawUTF8(), SND_CTL_NONBLOCK)) >= 0) { if (JUCE_CHECKED_RESULT (snd_ctl_card_info (handle, info)) >= 0) { diff --git a/modules/juce_audio_devices/native/juce_linux_Bela.cpp b/modules/juce_audio_devices/native/juce_linux_Bela.cpp index 50882a473a..a4d000eab4 100644 --- a/modules/juce_audio_devices/native/juce_linux_Bela.cpp +++ b/modules/juce_audio_devices/native/juce_linux_Bela.cpp @@ -23,6 +23,136 @@ namespace juce { +//============================================================================== +class BelaMidiInput +{ +public: + static Array midiInputs; + + BelaMidiInput (const String& port, MidiInput* input, MidiInputCallback* callback) + : midiInput (input), midiPort (port), midiCallback (callback) + { + jassert (midiCallback != nullptr); + midiInputs.add (this); + } + + ~BelaMidiInput() + { + stop(); + midiInputs.removeAllInstancesOf (this); + } + + void start() + { + midi.readFrom (midiPort.toRawUTF8()); + } + + void stop() + { + midi.enableParser (false); + } + + void poll() + { + for (;;) + { + auto data = midi.getInput(); + + if (data < 0) + break; + + auto byte = (uint8) data; + concatenator.pushMidiData (&byte, 1, 0.0, midiInput, *midiCallback); + } + } + + static StringArray getDevices (bool input) + { + StringArray devices; + + for (auto& card : findAllALSACardIDs()) + findMidiDevices (devices, input, card); + + return devices; + } + +private: + static Array findAllALSACardIDs() + { + Array cards; + int card = -1; + + for (;;) + { + auto status = snd_card_next (&card); + + if (status != 0 || card < 0) + break; + + cards.add (card); + } + + return cards; + } + + // Adds all midi devices to the devices array of the given input/output type on the given card + static void findMidiDevices (StringArray& devices, bool input, int cardNum) + { + snd_ctl_t* ctl = nullptr; + auto status = snd_ctl_open (&ctl, ("hw:" + String (cardNum)).toRawUTF8(), 0); + + if (status < 0) + return; + + int device = -1; + + for (;;) + { + status = snd_ctl_rawmidi_next_device (ctl, &device); + + if (status < 0 || device < 0) + break; + + snd_rawmidi_info_t* info; + snd_rawmidi_info_alloca (&info); + + snd_rawmidi_info_set_device (info, device); + snd_rawmidi_info_set_stream (info, input ? SND_RAWMIDI_STREAM_INPUT + : SND_RAWMIDI_STREAM_OUTPUT); + + snd_ctl_rawmidi_info (ctl, info); + + auto subCount = snd_rawmidi_info_get_subdevices_count (info); + + for (int sub = 0; sub < subCount; ++sub) + { + snd_rawmidi_info_set_subdevice (info, sub); + + status = snd_ctl_rawmidi_info (ctl, info); + + if (status == 0) + devices.add ("hw:" + String (cardNum) + "," + + String (device) + "," + + String (sub)); + } + } + + snd_ctl_close (ctl); + } + + String midiPort; + MidiInput* const midiInput; + MidiInputCallback* const midiCallback; + + Midi midi; + MidiDataConcatenator concatenator { 512 }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaMidiInput) +}; + +Array BelaMidiInput::midiInputs; + + //============================================================================== class BelaAudioIODevice : public AudioIODevice { @@ -226,6 +356,10 @@ private: ScopedLock lock (callbackLock); + // Check for and process and midi + for (auto midiInput : BelaMidiInput::midiInputs) + midiInput->poll(); + if (callback != nullptr) { jassert (context.audioFrames <= actualBufferSize); @@ -373,4 +507,58 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() return new BelaAudioIODeviceType(); } + +//============================================================================== +// TODO: Add Bela MidiOutput support + +StringArray MidiOutput::getDevices() { return {}; } +int MidiOutput::getDefaultDeviceIndex() { return 0; } +MidiOutput* MidiOutput::openDevice (int) { return {}; } +MidiOutput* MidiOutput::createNewDevice (const String&) { return {}; } +MidiOutput::~MidiOutput() {} +void MidiOutput::sendMessageNow (const MidiMessage&) {} + + +//============================================================================== +MidiInput::MidiInput (const String& nm) : name (nm) {} + +MidiInput::~MidiInput() +{ + delete static_cast (internal); +} + +void MidiInput::start() { static_cast (internal)->start(); } +void MidiInput::stop() { static_cast (internal)->stop(); } + +int MidiInput::getDefaultDeviceIndex() +{ + return 0; +} + +StringArray MidiInput::getDevices() +{ + return BelaMidiInput::getDevices (true); +} + +MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) +{ + auto devices = getDevices(); + + if (index >= 0 && index < devices.size()) + { + auto deviceName = devices[index]; + auto result = new MidiInput (deviceName); + result->internal = new BelaMidiInput (deviceName, result, callback); + return result; + } + + return {}; +} + +MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) +{ + jassertfalse; // N/A on Bela + return {}; +} + } // namespace juce