| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2020 - Raw Material Software Limited
 - 
 -    JUCE is an open source library subject to commercial or open-source
 -    licensing.
 - 
 -    The code included in this file is provided under the terms of the ISC license
 -    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
 -    To use, copy, modify, and/or distribute this software for any purpose with or
 -    without fee is hereby granted provided that the above copyright notice and
 -    this permission notice appear in all copies.
 - 
 -    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
 -    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
 -    DISCLAIMED.
 - 
 -   ==============================================================================
 - */
 - 
 - namespace juce
 - {
 - 
 - //==============================================================================
 - class MidiInput::Pimpl
 - {
 - public:
 -     static Array<Pimpl*> midiInputs;
 - 
 -     Pimpl (const String& port, MidiInput* input, MidiInputCallback* callback)
 -         : midiInput (input), midiPort (port), midiCallback (callback)
 -     {
 -         jassert (midiCallback != nullptr);
 -         midiInputs.add (this);
 - 
 -         buffer.resize (32);
 -     }
 - 
 -     ~Pimpl()
 -     {
 -         stop();
 -         midiInputs.removeAllInstancesOf (this);
 -     }
 - 
 -     void start()
 -     {
 -         midi.readFrom (midiPort.toRawUTF8());
 -     }
 - 
 -     void stop()
 -     {
 -         midi.enableParser (false);
 -     }
 - 
 -     void poll()
 -     {
 -         size_t receivedBytes = 0;
 - 
 -         for (;;)
 -         {
 -             auto data = midi.getInput();
 - 
 -             if (data < 0)
 -                 break;
 - 
 -             buffer[receivedBytes] = (uint8) data;
 -             receivedBytes++;
 - 
 -             if (receivedBytes == buffer.size())
 -             {
 -                 pushMidiData (static_cast<int> (receivedBytes));
 -                 receivedBytes = 0;
 -             }
 -         }
 - 
 -         if (receivedBytes > 0)
 -             pushMidiData ((int) receivedBytes);
 -     }
 - 
 -     static Array<MidiDeviceInfo> getDevices (bool input)
 -     {
 -         Array<MidiDeviceInfo> devices;
 - 
 -         for (auto& card : findAllALSACardIDs())
 -             findMidiDevices (devices, input, card);
 - 
 -         return devices;
 -     }
 - 
 -     void pushMidiMessage (juce::MidiMessage& message)
 -     {
 -         concatenator.pushMidiData (message.getRawData(), message.getRawDataSize(), Time::getMillisecondCounter() * 0.001, midiInput, *midiCallback);
 -     }
 - 
 - private:
 -     void pushMidiData (int length)
 -     {
 -         concatenator.pushMidiData (buffer.data(), length, Time::getMillisecondCounter() * 0.001, midiInput, *midiCallback);
 -     }
 - 
 -     std::vector<uint8> buffer;
 - 
 -     static Array<int> findAllALSACardIDs()
 -     {
 -         Array<int> 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 (Array<MidiDeviceInfo>& 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, (unsigned int) 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 (size_t sub = 0; sub < subCount; ++sub)
 -             {
 -                 snd_rawmidi_info_set_subdevice (info, sub);
 - 
 -                 status = snd_ctl_rawmidi_info (ctl, info);
 - 
 -                 if (status == 0)
 -                 {
 -                     String deviceName ("hw:" + String (cardNum) + "," + String (device) + "," + String (sub));
 -                     devices.add (MidiDeviceInfo (deviceName, deviceName));
 -                 }
 -             }
 -         }
 - 
 -         snd_ctl_close (ctl);
 -     }
 - 
 -     MidiInput* const midiInput;
 -     String midiPort;
 -     MidiInputCallback* const midiCallback;
 - 
 -     Midi midi;
 -     MidiDataConcatenator concatenator { 512 };
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
 - };
 - 
 - Array<MidiInput::Pimpl*> MidiInput::Pimpl::midiInputs;
 - 
 - 
 - //==============================================================================
 - class BelaAudioIODevice   : public AudioIODevice
 - {
 - public:
 -     BelaAudioIODevice()  : AudioIODevice (BelaAudioIODevice::belaTypeName,
 -                                           BelaAudioIODevice::belaTypeName)
 -     {
 -         Bela_defaultSettings (&defaultSettings);
 -     }
 - 
 -     ~BelaAudioIODevice()
 -     {
 -         close();
 -     }
 - 
 -     //==============================================================================
 -     StringArray getOutputChannelNames() override
 -     {
 -         StringArray result;
 - 
 -         for (int i = 1; i <= actualNumberOfOutputs; i++)
 -             result.add ("Out #" + std::to_string (i));
 - 
 -         return result;
 -     }
 - 
 -     StringArray getInputChannelNames() override
 -     {
 -         StringArray result;
 - 
 -         for (int i = 1; i <= actualNumberOfInputs; i++)
 -             result.add ("In #" + std::to_string (i));
 - 
 -         return result;
 -     }
 - 
 -     Array<double> getAvailableSampleRates() override       { return { 44100.0 }; }
 -     Array<int> getAvailableBufferSizes() override          { /* TODO: */ return { getDefaultBufferSize() }; }
 -     int getDefaultBufferSize() override                    { return defaultSettings.periodSize; }
 - 
 -     //==============================================================================
 -     String open (const BigInteger& inputChannels,
 -                  const BigInteger& outputChannels,
 -                  double sampleRate,
 -                  int bufferSizeSamples) override
 -     {
 -         if (sampleRate != 44100.0 && sampleRate != 0.0)
 -         {
 -             lastError = "Bela audio outputs only support 44.1 kHz sample rate";
 -             return lastError;
 -         }
 - 
 -         settings = defaultSettings;
 - 
 -         auto numIns = getNumContiguousSetBits (inputChannels);
 -         auto numOuts = getNumContiguousSetBits (outputChannels);
 - 
 -         // Input and Output channels are numbered as follows
 -         //
 -         // 0  .. 1  - audio
 -         // 2  .. 9  - analog
 - 
 -         if (numIns > 2 || numOuts > 2)
 -         {
 -             settings.useAnalog            = true;
 -             settings.numAnalogInChannels  = std::max (numIns - 2, 8);
 -             settings.numAnalogOutChannels = std::max (numOuts - 2, 8);
 -             settings.uniformSampleRate    = true;
 -         }
 - 
 -         settings.numAudioInChannels   = std::max (numIns, 2);
 -         settings.numAudioOutChannels  = std::max (numOuts, 2);
 - 
 -         settings.detectUnderruns      = 1;
 -         settings.setup                = setupCallback;
 -         settings.render               = renderCallback;
 -         settings.cleanup              = cleanupCallback;
 -         settings.interleave           = 0;
 - 
 -         if (bufferSizeSamples > 0)
 -             settings.periodSize = bufferSizeSamples;
 - 
 -         isBelaOpen = false;
 -         isRunning  = false;
 -         callback   = nullptr;
 -         underruns  = 0;
 - 
 -         if (Bela_initAudio (&settings, this) != 0 || ! isBelaOpen)
 -         {
 -             lastError = "Bela_initAutio failed";
 -             return lastError;
 -         }
 - 
 -         actualNumberOfInputs  = jmin (numIns, actualNumberOfInputs);
 -         actualNumberOfOutputs = jmin (numOuts, actualNumberOfOutputs);
 - 
 -         channelInBuffer.calloc (actualNumberOfInputs);
 -         channelOutBuffer.calloc (actualNumberOfOutputs);
 - 
 -         return {};
 -     }
 - 
 -     void close() override
 -     {
 -         stop();
 - 
 -         if (isBelaOpen)
 -         {
 -             Bela_cleanupAudio();
 - 
 -             isBelaOpen = false;
 -             callback = nullptr;
 -             underruns = 0;
 - 
 -             actualBufferSize = 0;
 -             actualNumberOfInputs = 0;
 -             actualNumberOfOutputs = 0;
 - 
 -             channelInBuffer.free();
 -             channelOutBuffer.free();
 -         }
 -     }
 - 
 -     bool isOpen() override   { return isBelaOpen; }
 - 
 -     void start (AudioIODeviceCallback* newCallback) override
 -     {
 -         if (! isBelaOpen)
 -             return;
 - 
 -         if (isRunning)
 -         {
 -             if (newCallback != callback)
 -             {
 -                 if (newCallback != nullptr)
 -                     newCallback->audioDeviceAboutToStart (this);
 - 
 -                 {
 -                     ScopedLock lock (callbackLock);
 -                     std::swap (callback, newCallback);
 -                 }
 - 
 -                 if (newCallback != nullptr)
 -                     newCallback->audioDeviceStopped();
 -             }
 -         }
 -         else
 -         {
 -             callback = newCallback;
 -             isRunning = (Bela_startAudio() == 0);
 - 
 -             if (callback != nullptr)
 -             {
 -                 if (isRunning)
 -                 {
 -                     callback->audioDeviceAboutToStart (this);
 -                 }
 -                 else
 -                 {
 -                     lastError = "Bela_StartAudio failed";
 -                     callback->audioDeviceError (lastError);
 -                 }
 -             }
 -         }
 -     }
 - 
 -     void stop() override
 -     {
 -         AudioIODeviceCallback* oldCallback = nullptr;
 - 
 -         if (callback != nullptr)
 -         {
 -             ScopedLock lock (callbackLock);
 -             std::swap (callback, oldCallback);
 -         }
 - 
 -         isRunning = false;
 -         Bela_stopAudio();
 - 
 -         if (oldCallback != nullptr)
 -             oldCallback->audioDeviceStopped();
 -     }
 - 
 -     bool isPlaying() override         { return isRunning; }
 -     String getLastError() override    { return lastError; }
 - 
 -     //==============================================================================
 -     int getCurrentBufferSizeSamples() override            { return (int) actualBufferSize; }
 -     double getCurrentSampleRate() override                { return 44100.0; }
 -     int getCurrentBitDepth() override                     { return 16; }
 -     BigInteger getActiveOutputChannels() const override   { BigInteger b; b.setRange (0, actualNumberOfOutputs, true); return b; }
 -     BigInteger getActiveInputChannels() const override    { BigInteger b; b.setRange (0, actualNumberOfInputs, true);  return b; }
 -     int getOutputLatencyInSamples() override              { /* TODO */ return 0; }
 -     int getInputLatencyInSamples() override               { /* TODO */ return 0; }
 -     int getXRunCount() const noexcept override            { return underruns; }
 - 
 -     //==============================================================================
 -     static const char* const belaTypeName;
 - 
 - private:
 - 
 -     //==============================================================================
 -     bool setup (BelaContext& context)
 -     {
 -         actualBufferSize      = context.audioFrames;
 -         actualNumberOfInputs  = (int) (context.audioInChannels + context.analogInChannels);
 -         actualNumberOfOutputs = (int) (context.audioOutChannels + context.analogOutChannels);
 -         isBelaOpen = true;
 -         firstCallback = true;
 - 
 -         ScopedLock lock (callbackLock);
 - 
 -         if (callback != nullptr)
 -             callback->audioDeviceAboutToStart (this);
 - 
 -         return true;
 -     }
 - 
 -     void render (BelaContext& context)
 -     {
 -         // check for xruns
 -         calculateXruns (context.audioFramesElapsed, context.audioFrames);
 - 
 -         ScopedLock lock (callbackLock);
 - 
 -         // Check for and process and midi
 -         for (auto midiInput : MidiInput::Pimpl::midiInputs)
 -             midiInput->poll();
 - 
 -         if (callback != nullptr)
 -         {
 -             jassert (context.audioFrames <= actualBufferSize);
 -             jassert ((context.flags & BELA_FLAG_INTERLEAVED) == 0);
 - 
 -             using Frames = decltype (context.audioFrames);
 - 
 -             // Setup channelInBuffers
 -             for (int ch = 0; ch < actualNumberOfInputs; ++ch)
 -             {
 -                 if (ch < analogChannelStart)
 -                     channelInBuffer[ch] = &context.audioIn[(Frames) ch * context.audioFrames];
 -                 else
 -                     channelInBuffer[ch] = &context.analogIn[(Frames) (ch - analogChannelStart) * context.analogFrames];
 -             }
 - 
 -             // Setup channelOutBuffers
 -             for (int ch = 0; ch < actualNumberOfOutputs; ++ch)
 -             {
 -                 if (ch < analogChannelStart)
 -                     channelOutBuffer[ch] = &context.audioOut[(Frames) ch * context.audioFrames];
 -                 else
 -                     channelOutBuffer[ch] = &context.analogOut[(Frames) (ch - analogChannelStart) * context.audioFrames];
 -             }
 - 
 -             callback->audioDeviceIOCallback (channelInBuffer.getData(), actualNumberOfInputs,
 -                                              channelOutBuffer.getData(), actualNumberOfOutputs,
 -                                              (int) context.audioFrames);
 -         }
 -     }
 - 
 -     void cleanup (BelaContext&)
 -     {
 -         ScopedLock lock (callbackLock);
 - 
 -         if (callback != nullptr)
 -             callback->audioDeviceStopped();
 -     }
 - 
 -     const int analogChannelStart  = 2;
 - 
 -     //==============================================================================
 -     uint64_t expectedElapsedAudioSamples = 0;
 -     int underruns = 0;
 -     bool firstCallback = false;
 - 
 -     void calculateXruns (uint64_t audioFramesElapsed, uint32_t numSamples)
 -     {
 -         if (audioFramesElapsed > expectedElapsedAudioSamples && ! firstCallback)
 -             ++underruns;
 - 
 -         firstCallback = false;
 -         expectedElapsedAudioSamples = audioFramesElapsed + numSamples;
 -     }
 - 
 -     //==============================================================================
 -     static int getNumContiguousSetBits (const BigInteger& value) noexcept
 -     {
 -         int bit = 0;
 - 
 -         while (value[bit])
 -             ++bit;
 - 
 -         return bit;
 -     }
 - 
 -     //==============================================================================
 -     static bool setupCallback   (BelaContext* context, void* userData) noexcept    { return static_cast<BelaAudioIODevice*> (userData)->setup (*context); }
 -     static void renderCallback  (BelaContext* context, void* userData) noexcept    { static_cast<BelaAudioIODevice*> (userData)->render (*context); }
 -     static void cleanupCallback (BelaContext* context, void* userData) noexcept    { static_cast<BelaAudioIODevice*> (userData)->cleanup (*context); }
 - 
 -     //==============================================================================
 -     BelaInitSettings defaultSettings, settings;
 -     bool isBelaOpen = false, isRunning = false;
 - 
 -     CriticalSection callbackLock;
 -     AudioIODeviceCallback* callback = nullptr;
 - 
 -     String lastError;
 -     uint32_t actualBufferSize = 0;
 -     int actualNumberOfInputs = 0, actualNumberOfOutputs = 0;
 - 
 -     HeapBlock<const float*> channelInBuffer;
 -     HeapBlock<float*> channelOutBuffer;
 - 
 -     bool includeAnalogSupport;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODevice)
 - };
 - 
 - const char* const BelaAudioIODevice::belaTypeName = "Bela Analog";
 - 
 - //==============================================================================
 - struct BelaAudioIODeviceType  : public AudioIODeviceType
 - {
 -     BelaAudioIODeviceType() : AudioIODeviceType ("Bela") {}
 - 
 -     StringArray getDeviceNames (bool) const override                       { return StringArray (BelaAudioIODevice::belaTypeName); }
 -     void scanForDevices() override                                         {}
 -     int getDefaultDeviceIndex (bool) const override                        { return 0; }
 -     int getIndexOfDevice (AudioIODevice* device, bool) const override      { return device != nullptr ? 0 : -1; }
 -     bool hasSeparateInputsAndOutputs() const override                      { return false; }
 - 
 -     AudioIODevice* createDevice (const String& outputName, const String& inputName) override
 -     {
 -         // TODO: switching whether to support analog/digital with possible multiple Bela device types?
 -         if (outputName == BelaAudioIODevice::belaTypeName || inputName == BelaAudioIODevice::belaTypeName)
 -             return new BelaAudioIODevice();
 - 
 -         return nullptr;
 -     }
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODeviceType)
 - };
 - 
 - //==============================================================================
 - MidiInput::MidiInput (const String& deviceName, const String& deviceID)
 -     : deviceInfo (deviceName, deviceID)
 - {
 - }
 - 
 - MidiInput::~MidiInput() = default;
 - void MidiInput::start()   { internal->start(); }
 - void MidiInput::stop()    { internal->stop(); }
 - 
 - Array<MidiDeviceInfo> MidiInput::getAvailableDevices()
 - {
 -     return Pimpl::getDevices (true);
 - }
 - 
 - MidiDeviceInfo MidiInput::getDefaultDevice()
 - {
 -     return getAvailableDevices().getFirst();
 - }
 - 
 - std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier, MidiInputCallback* callback)
 - {
 -     if (deviceIdentifier.isEmpty())
 -         return {};
 - 
 -     std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceIdentifier, deviceIdentifier));
 -     midiInput->internal = std::make_unique<Pimpl> (deviceIdentifier, midiInput.get(), callback);
 - 
 -     return midiInput;
 - }
 - 
 - std::unique_ptr<MidiInput> MidiInput::createNewDevice (const String&, MidiInputCallback*)
 - {
 -     // N/A on Bela
 -     jassertfalse;
 -     return {};
 - }
 - 
 - StringArray MidiInput::getDevices()
 - {
 -     StringArray deviceNames;
 - 
 -     for (auto& d : getAvailableDevices())
 -         deviceNames.add (d.name);
 - 
 -     return deviceNames;
 - }
 - 
 - int MidiInput::getDefaultDeviceIndex()
 - {
 -     return 0;
 - }
 - 
 - std::unique_ptr<MidiInput> MidiInput::openDevice (int index, MidiInputCallback* callback)
 - {
 -     return openDevice (getAvailableDevices()[index].identifier, callback);
 - }
 - 
 - //==============================================================================
 - // TODO: Add Bela MidiOutput support
 - class MidiOutput::Pimpl {};
 - MidiOutput::~MidiOutput() = default;
 - void MidiOutput::sendMessageNow (const MidiMessage&)                     {}
 - Array<MidiDeviceInfo> MidiOutput::getAvailableDevices()                  { return {}; }
 - MidiDeviceInfo MidiOutput::getDefaultDevice()                            { return {}; }
 - std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String&)       { return {}; }
 - std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String&)  { return {}; }
 - StringArray MidiOutput::getDevices()                                     { return {}; }
 - int MidiOutput::getDefaultDeviceIndex()                                  { return 0;}
 - std::unique_ptr<MidiOutput> MidiOutput::openDevice (int)                 { return {}; }
 - 
 - } // namespace juce
 
 
  |