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
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
20 ==============================================================================
26 //==============================================================================
27 class MidiInput::Pimpl
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);
44 midiInputs
.removeAllInstancesOf (this);
49 midi
.readFrom (midiPort
.toRawUTF8());
54 midi
.enableParser (false);
59 size_t receivedBytes
= 0;
63 auto data
= midi
.getInput();
68 buffer
[receivedBytes
] = (uint8
) data
;
71 if (receivedBytes
== buffer
.size())
73 pushMidiData (static_cast<int> (receivedBytes
));
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
);
92 void pushMidiMessage (juce::MidiMessage
& message
)
94 concatenator
.pushMidiData (message
.getRawData(), message
.getRawDataSize(), Time::getMillisecondCounter() * 0.001, midiInput
, *midiCallback
);
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()
112 auto status
= snd_card_next (&card
);
114 if (status
!= 0 || card
< 0)
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);
136 status
= snd_ctl_rawmidi_next_device (ctl
, &device
);
138 if (status
< 0 || device
< 0)
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
);
160 String
deviceName ("hw:" + String (cardNum
) + "," + String (device
) + "," + String (sub
));
161 devices
.add (MidiDeviceInfo (deviceName
, deviceName
));
169 MidiInput
* const midiInput
;
171 MidiInputCallback
* const midiCallback
;
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
186 BelaAudioIODevice() : AudioIODevice (BelaAudioIODevice::belaTypeName
,
187 BelaAudioIODevice::belaTypeName
)
189 Bela_defaultSettings (&defaultSettings
);
197 //==============================================================================
198 StringArray
getOutputChannelNames() override
202 for (int i
= 1; i
<= actualNumberOfOutputs
; i
++)
203 result
.add ("Out #" + std::to_string (i
));
208 StringArray
getInputChannelNames() override
212 for (int i
= 1; i
<= actualNumberOfInputs
; i
++)
213 result
.add ("In #" + std::to_string (i
));
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
,
226 int bufferSizeSamples
) override
228 if (sampleRate
!= 44100.0 && sampleRate
!= 0.0)
230 lastError
= "Bela audio outputs only support 44.1 kHz sample rate";
234 settings
= defaultSettings
;
236 auto numIns
= getNumContiguousSetBits (inputChannels
);
237 auto numOuts
= getNumContiguousSetBits (outputChannels
);
239 // Input and Output channels are numbered as follows
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
;
269 if (Bela_initAudio (&settings
, this) != 0 || ! isBelaOpen
)
271 lastError
= "Bela_initAutio failed";
275 actualNumberOfInputs
= jmin (numIns
, actualNumberOfInputs
);
276 actualNumberOfOutputs
= jmin (numOuts
, actualNumberOfOutputs
);
278 channelInBuffer
.calloc (actualNumberOfInputs
);
279 channelOutBuffer
.calloc (actualNumberOfOutputs
);
284 void close() override
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
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();
330 callback
= newCallback
;
331 isRunning
= (Bela_startAudio() == 0);
333 if (callback
!= nullptr)
337 callback
->audioDeviceAboutToStart (this);
341 lastError
= "Bela_StartAudio failed";
342 callback
->audioDeviceError (lastError
);
350 AudioIODeviceCallback
* oldCallback
= nullptr;
352 if (callback
!= nullptr)
354 ScopedLock
lock (callbackLock
);
355 std::swap (callback
, oldCallback
);
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
;
383 //==============================================================================
384 bool setup (BelaContext
& context
)
386 actualBufferSize
= context
.audioFrames
;
387 actualNumberOfInputs
= (int) (context
.audioInChannels
+ context
.analogInChannels
);
388 actualNumberOfOutputs
= (int) (context
.audioOutChannels
+ context
.analogOutChannels
);
390 firstCallback
= true;
392 ScopedLock
lock (callbackLock
);
394 if (callback
!= nullptr)
395 callback
->audioDeviceAboutToStart (this);
400 void render (BelaContext
& context
)
403 calculateXruns (context
.audioFramesElapsed
, context
.audioFrames
);
405 ScopedLock
lock (callbackLock
);
407 // Check for and process and midi
408 for (auto midiInput
: MidiInput::Pimpl::midiInputs
)
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
];
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
];
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
,
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;
458 bool firstCallback
= false;
460 void calculateXruns (uint64_t audioFramesElapsed
, uint32_t numSamples
)
462 if (audioFramesElapsed
> expectedElapsedAudioSamples
&& ! firstCallback
)
465 firstCallback
= false;
466 expectedElapsedAudioSamples
= audioFramesElapsed
+ numSamples
;
469 //==============================================================================
470 static int getNumContiguousSetBits (const BigInteger
& value
) noexcept
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;
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();
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())
554 std::unique_ptr
<MidiInput
> midiInput (new MidiInput (deviceIdentifier
, deviceIdentifier
));
555 midiInput
->internal
= std::make_unique
<Pimpl
> (deviceIdentifier
, midiInput
.get(), callback
);
560 std::unique_ptr
<MidiInput
> MidiInput::createNewDevice (const String
&, MidiInputCallback
*)
567 StringArray
MidiInput::getDevices()
569 StringArray deviceNames
;
571 for (auto& d
: getAvailableDevices())
572 deviceNames
.add (d
.name
);
577 int MidiInput::getDefaultDeviceIndex()
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 {}; }