VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_audio_devices / native / juce_linux_Bela.cpp
blobbf1ff16f390ba03166ea67917659760d3cf3b59a
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 The code included in this file is provided under the terms of the ISC license
11 http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12 To use, copy, modify, and/or distribute this software for any purpose with or
13 without fee is hereby granted provided that the above copyright notice and
14 this permission notice appear in all copies.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18 DISCLAIMED.
20 ==============================================================================
23 namespace juce
26 //==============================================================================
27 class MidiInput::Pimpl
29 public:
30 static Array<Pimpl*> midiInputs;
32 Pimpl (const String& port, MidiInput* input, MidiInputCallback* callback)
33 : midiInput (input), midiPort (port), midiCallback (callback)
35 jassert (midiCallback != nullptr);
36 midiInputs.add (this);
38 buffer.resize (32);
41 ~Pimpl()
43 stop();
44 midiInputs.removeAllInstancesOf (this);
47 void start()
49 midi.readFrom (midiPort.toRawUTF8());
52 void stop()
54 midi.enableParser (false);
57 void poll()
59 size_t receivedBytes = 0;
61 for (;;)
63 auto data = midi.getInput();
65 if (data < 0)
66 break;
68 buffer[receivedBytes] = (uint8) data;
69 receivedBytes++;
71 if (receivedBytes == buffer.size())
73 pushMidiData (static_cast<int> (receivedBytes));
74 receivedBytes = 0;
78 if (receivedBytes > 0)
79 pushMidiData ((int) receivedBytes);
82 static Array<MidiDeviceInfo> getDevices (bool input)
84 Array<MidiDeviceInfo> devices;
86 for (auto& card : findAllALSACardIDs())
87 findMidiDevices (devices, input, card);
89 return devices;
92 void pushMidiMessage (juce::MidiMessage& message)
94 concatenator.pushMidiData (message.getRawData(), message.getRawDataSize(), Time::getMillisecondCounter() * 0.001, midiInput, *midiCallback);
97 private:
98 void pushMidiData (int length)
100 concatenator.pushMidiData (buffer.data(), length, Time::getMillisecondCounter() * 0.001, midiInput, *midiCallback);
103 std::vector<uint8> buffer;
105 static Array<int> findAllALSACardIDs()
107 Array<int> cards;
108 int card = -1;
110 for (;;)
112 auto status = snd_card_next (&card);
114 if (status != 0 || card < 0)
115 break;
117 cards.add (card);
120 return cards;
123 // Adds all midi devices to the devices array of the given input/output type on the given card
124 static void findMidiDevices (Array<MidiDeviceInfo>& devices, bool input, int cardNum)
126 snd_ctl_t* ctl = nullptr;
127 auto status = snd_ctl_open (&ctl, ("hw:" + String (cardNum)).toRawUTF8(), 0);
129 if (status < 0)
130 return;
132 int device = -1;
134 for (;;)
136 status = snd_ctl_rawmidi_next_device (ctl, &device);
138 if (status < 0 || device < 0)
139 break;
141 snd_rawmidi_info_t* info;
142 snd_rawmidi_info_alloca (&info);
144 snd_rawmidi_info_set_device (info, (unsigned int) device);
145 snd_rawmidi_info_set_stream (info, input ? SND_RAWMIDI_STREAM_INPUT
146 : SND_RAWMIDI_STREAM_OUTPUT);
148 snd_ctl_rawmidi_info (ctl, info);
150 auto subCount = snd_rawmidi_info_get_subdevices_count (info);
152 for (size_t sub = 0; sub < subCount; ++sub)
154 snd_rawmidi_info_set_subdevice (info, sub);
156 status = snd_ctl_rawmidi_info (ctl, info);
158 if (status == 0)
160 String deviceName ("hw:" + String (cardNum) + "," + String (device) + "," + String (sub));
161 devices.add (MidiDeviceInfo (deviceName, deviceName));
166 snd_ctl_close (ctl);
169 MidiInput* const midiInput;
170 String midiPort;
171 MidiInputCallback* const midiCallback;
173 Midi midi;
174 MidiDataConcatenator concatenator { 512 };
176 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
179 Array<MidiInput::Pimpl*> MidiInput::Pimpl::midiInputs;
182 //==============================================================================
183 class BelaAudioIODevice : public AudioIODevice
185 public:
186 BelaAudioIODevice() : AudioIODevice (BelaAudioIODevice::belaTypeName,
187 BelaAudioIODevice::belaTypeName)
189 Bela_defaultSettings (&defaultSettings);
192 ~BelaAudioIODevice()
194 close();
197 //==============================================================================
198 StringArray getOutputChannelNames() override
200 StringArray result;
202 for (int i = 1; i <= actualNumberOfOutputs; i++)
203 result.add ("Out #" + std::to_string (i));
205 return result;
208 StringArray getInputChannelNames() override
210 StringArray result;
212 for (int i = 1; i <= actualNumberOfInputs; i++)
213 result.add ("In #" + std::to_string (i));
215 return result;
218 Array<double> getAvailableSampleRates() override { return { 44100.0 }; }
219 Array<int> getAvailableBufferSizes() override { /* TODO: */ return { getDefaultBufferSize() }; }
220 int getDefaultBufferSize() override { return defaultSettings.periodSize; }
222 //==============================================================================
223 String open (const BigInteger& inputChannels,
224 const BigInteger& outputChannels,
225 double sampleRate,
226 int bufferSizeSamples) override
228 if (sampleRate != 44100.0 && sampleRate != 0.0)
230 lastError = "Bela audio outputs only support 44.1 kHz sample rate";
231 return lastError;
234 settings = defaultSettings;
236 auto numIns = getNumContiguousSetBits (inputChannels);
237 auto numOuts = getNumContiguousSetBits (outputChannels);
239 // Input and Output channels are numbered as follows
241 // 0 .. 1 - audio
242 // 2 .. 9 - analog
244 if (numIns > 2 || numOuts > 2)
246 settings.useAnalog = true;
247 settings.numAnalogInChannels = std::max (numIns - 2, 8);
248 settings.numAnalogOutChannels = std::max (numOuts - 2, 8);
249 settings.uniformSampleRate = true;
252 settings.numAudioInChannels = std::max (numIns, 2);
253 settings.numAudioOutChannels = std::max (numOuts, 2);
255 settings.detectUnderruns = 1;
256 settings.setup = setupCallback;
257 settings.render = renderCallback;
258 settings.cleanup = cleanupCallback;
259 settings.interleave = 0;
261 if (bufferSizeSamples > 0)
262 settings.periodSize = bufferSizeSamples;
264 isBelaOpen = false;
265 isRunning = false;
266 callback = nullptr;
267 underruns = 0;
269 if (Bela_initAudio (&settings, this) != 0 || ! isBelaOpen)
271 lastError = "Bela_initAutio failed";
272 return lastError;
275 actualNumberOfInputs = jmin (numIns, actualNumberOfInputs);
276 actualNumberOfOutputs = jmin (numOuts, actualNumberOfOutputs);
278 channelInBuffer.calloc (actualNumberOfInputs);
279 channelOutBuffer.calloc (actualNumberOfOutputs);
281 return {};
284 void close() override
286 stop();
288 if (isBelaOpen)
290 Bela_cleanupAudio();
292 isBelaOpen = false;
293 callback = nullptr;
294 underruns = 0;
296 actualBufferSize = 0;
297 actualNumberOfInputs = 0;
298 actualNumberOfOutputs = 0;
300 channelInBuffer.free();
301 channelOutBuffer.free();
305 bool isOpen() override { return isBelaOpen; }
307 void start (AudioIODeviceCallback* newCallback) override
309 if (! isBelaOpen)
310 return;
312 if (isRunning)
314 if (newCallback != callback)
316 if (newCallback != nullptr)
317 newCallback->audioDeviceAboutToStart (this);
320 ScopedLock lock (callbackLock);
321 std::swap (callback, newCallback);
324 if (newCallback != nullptr)
325 newCallback->audioDeviceStopped();
328 else
330 callback = newCallback;
331 isRunning = (Bela_startAudio() == 0);
333 if (callback != nullptr)
335 if (isRunning)
337 callback->audioDeviceAboutToStart (this);
339 else
341 lastError = "Bela_StartAudio failed";
342 callback->audioDeviceError (lastError);
348 void stop() override
350 AudioIODeviceCallback* oldCallback = nullptr;
352 if (callback != nullptr)
354 ScopedLock lock (callbackLock);
355 std::swap (callback, oldCallback);
358 isRunning = false;
359 Bela_stopAudio();
361 if (oldCallback != nullptr)
362 oldCallback->audioDeviceStopped();
365 bool isPlaying() override { return isRunning; }
366 String getLastError() override { return lastError; }
368 //==============================================================================
369 int getCurrentBufferSizeSamples() override { return (int) actualBufferSize; }
370 double getCurrentSampleRate() override { return 44100.0; }
371 int getCurrentBitDepth() override { return 16; }
372 BigInteger getActiveOutputChannels() const override { BigInteger b; b.setRange (0, actualNumberOfOutputs, true); return b; }
373 BigInteger getActiveInputChannels() const override { BigInteger b; b.setRange (0, actualNumberOfInputs, true); return b; }
374 int getOutputLatencyInSamples() override { /* TODO */ return 0; }
375 int getInputLatencyInSamples() override { /* TODO */ return 0; }
376 int getXRunCount() const noexcept override { return underruns; }
378 //==============================================================================
379 static const char* const belaTypeName;
381 private:
383 //==============================================================================
384 bool setup (BelaContext& context)
386 actualBufferSize = context.audioFrames;
387 actualNumberOfInputs = (int) (context.audioInChannels + context.analogInChannels);
388 actualNumberOfOutputs = (int) (context.audioOutChannels + context.analogOutChannels);
389 isBelaOpen = true;
390 firstCallback = true;
392 ScopedLock lock (callbackLock);
394 if (callback != nullptr)
395 callback->audioDeviceAboutToStart (this);
397 return true;
400 void render (BelaContext& context)
402 // check for xruns
403 calculateXruns (context.audioFramesElapsed, context.audioFrames);
405 ScopedLock lock (callbackLock);
407 // Check for and process and midi
408 for (auto midiInput : MidiInput::Pimpl::midiInputs)
409 midiInput->poll();
411 if (callback != nullptr)
413 jassert (context.audioFrames <= actualBufferSize);
414 jassert ((context.flags & BELA_FLAG_INTERLEAVED) == 0);
416 using Frames = decltype (context.audioFrames);
418 // Setup channelInBuffers
419 for (int ch = 0; ch < actualNumberOfInputs; ++ch)
421 if (ch < analogChannelStart)
422 channelInBuffer[ch] = &context.audioIn[(Frames) ch * context.audioFrames];
423 else
424 channelInBuffer[ch] = &context.analogIn[(Frames) (ch - analogChannelStart) * context.analogFrames];
427 // Setup channelOutBuffers
428 for (int ch = 0; ch < actualNumberOfOutputs; ++ch)
430 if (ch < analogChannelStart)
431 channelOutBuffer[ch] = &context.audioOut[(Frames) ch * context.audioFrames];
432 else
433 channelOutBuffer[ch] = &context.analogOut[(Frames) (ch - analogChannelStart) * context.audioFrames];
436 callback->audioDeviceIOCallbackWithContext (channelInBuffer.getData(),
437 actualNumberOfInputs,
438 channelOutBuffer.getData(),
439 actualNumberOfOutputs,
440 (int) context.audioFrames,
441 {});
445 void cleanup (BelaContext&)
447 ScopedLock lock (callbackLock);
449 if (callback != nullptr)
450 callback->audioDeviceStopped();
453 const int analogChannelStart = 2;
455 //==============================================================================
456 uint64_t expectedElapsedAudioSamples = 0;
457 int underruns = 0;
458 bool firstCallback = false;
460 void calculateXruns (uint64_t audioFramesElapsed, uint32_t numSamples)
462 if (audioFramesElapsed > expectedElapsedAudioSamples && ! firstCallback)
463 ++underruns;
465 firstCallback = false;
466 expectedElapsedAudioSamples = audioFramesElapsed + numSamples;
469 //==============================================================================
470 static int getNumContiguousSetBits (const BigInteger& value) noexcept
472 int bit = 0;
474 while (value[bit])
475 ++bit;
477 return bit;
480 //==============================================================================
481 static bool setupCallback (BelaContext* context, void* userData) noexcept { return static_cast<BelaAudioIODevice*> (userData)->setup (*context); }
482 static void renderCallback (BelaContext* context, void* userData) noexcept { static_cast<BelaAudioIODevice*> (userData)->render (*context); }
483 static void cleanupCallback (BelaContext* context, void* userData) noexcept { static_cast<BelaAudioIODevice*> (userData)->cleanup (*context); }
485 //==============================================================================
486 BelaInitSettings defaultSettings, settings;
487 bool isBelaOpen = false, isRunning = false;
489 CriticalSection callbackLock;
490 AudioIODeviceCallback* callback = nullptr;
492 String lastError;
493 uint32_t actualBufferSize = 0;
494 int actualNumberOfInputs = 0, actualNumberOfOutputs = 0;
496 HeapBlock<const float*> channelInBuffer;
497 HeapBlock<float*> channelOutBuffer;
499 bool includeAnalogSupport;
501 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODevice)
504 const char* const BelaAudioIODevice::belaTypeName = "Bela Analog";
506 //==============================================================================
507 struct BelaAudioIODeviceType : public AudioIODeviceType
509 BelaAudioIODeviceType() : AudioIODeviceType ("Bela") {}
511 StringArray getDeviceNames (bool) const override { return StringArray (BelaAudioIODevice::belaTypeName); }
512 void scanForDevices() override {}
513 int getDefaultDeviceIndex (bool) const override { return 0; }
514 int getIndexOfDevice (AudioIODevice* device, bool) const override { return device != nullptr ? 0 : -1; }
515 bool hasSeparateInputsAndOutputs() const override { return false; }
517 AudioIODevice* createDevice (const String& outputName, const String& inputName) override
519 // TODO: switching whether to support analog/digital with possible multiple Bela device types?
520 if (outputName == BelaAudioIODevice::belaTypeName || inputName == BelaAudioIODevice::belaTypeName)
521 return new BelaAudioIODevice();
523 return nullptr;
526 JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODeviceType)
529 //==============================================================================
530 MidiInput::MidiInput (const String& deviceName, const String& deviceID)
531 : deviceInfo (deviceName, deviceID)
535 MidiInput::~MidiInput() = default;
536 void MidiInput::start() { internal->start(); }
537 void MidiInput::stop() { internal->stop(); }
539 Array<MidiDeviceInfo> MidiInput::getAvailableDevices()
541 return Pimpl::getDevices (true);
544 MidiDeviceInfo MidiInput::getDefaultDevice()
546 return getAvailableDevices().getFirst();
549 std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback)
551 if (deviceIdentifier.isEmpty())
552 return {};
554 std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceIdentifier, deviceIdentifier));
555 midiInput->internal = std::make_unique<Pimpl> (deviceIdentifier, midiInput.get(), callback);
557 return midiInput;
560 std::unique_ptr<MidiInput> MidiInput::createNewDevice (const String&, MidiInputCallback*)
562 // N/A on Bela
563 jassertfalse;
564 return {};
567 StringArray MidiInput::getDevices()
569 StringArray deviceNames;
571 for (auto& d : getAvailableDevices())
572 deviceNames.add (d.name);
574 return deviceNames;
577 int MidiInput::getDefaultDeviceIndex()
579 return 0;
582 std::unique_ptr<MidiInput> MidiInput::openDevice (int index, MidiInputCallback* callback)
584 return openDevice (getAvailableDevices()[index].identifier, callback);
587 //==============================================================================
588 // TODO: Add Bela MidiOutput support
589 class MidiOutput::Pimpl {};
590 MidiOutput::~MidiOutput() = default;
591 void MidiOutput::sendMessageNow (const MidiMessage&) {}
592 Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() { return {}; }
593 MidiDeviceInfo MidiOutput::getDefaultDevice() { return {}; }
594 std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String&) { return {}; }
595 std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String&) { return {}; }
596 StringArray MidiOutput::getDevices() { return {}; }
597 int MidiOutput::getDefaultDeviceIndex() { return 0;}
598 std::unique_ptr<MidiOutput> MidiOutput::openDevice (int) { return {}; }
600 } // namespace juce