mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Added support for Bela in the form of an AudioIODeviceType
This commit is contained in:
parent
26e31ff7d6
commit
5a122e124a
6 changed files with 400 additions and 0 deletions
|
|
@ -158,6 +158,7 @@ void AudioDeviceManager::createAudioDeviceTypes (OwnedArray<AudioIODeviceType>&
|
|||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_iOSAudio());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ALSA());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_JACK());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Bela());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Oboe());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_OpenSLES());
|
||||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Android());
|
||||
|
|
|
|||
|
|
@ -70,6 +70,10 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA()
|
|||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! (JUCE_LINUX && JUCE_BELA)
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; }
|
||||
#endif
|
||||
|
||||
#if ! JUCE_ANDROID
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; }
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -165,6 +165,8 @@ public:
|
|||
static AudioIODeviceType* createAudioIODeviceType_OpenSLES();
|
||||
/** Creates an Oboe device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_Oboe();
|
||||
/** Creates a Bela device type if it's available on this platform, or returns null. */
|
||||
static AudioIODeviceType* createAudioIODeviceType_Bela();
|
||||
|
||||
protected:
|
||||
explicit AudioIODeviceType (const String& typeName);
|
||||
|
|
|
|||
|
|
@ -142,6 +142,15 @@
|
|||
*/
|
||||
#include <jack/jack.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_BELA
|
||||
/* Got an include error here? If so, you've either not got the bela headers
|
||||
installed, or you've not got your paths set up correctly to find its header
|
||||
files.
|
||||
*/
|
||||
#include <Bela.h>
|
||||
#endif
|
||||
|
||||
#undef SIZEOF
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -207,6 +216,10 @@
|
|||
#include "native/juce_linux_JackAudio.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_BELA
|
||||
#include "native/juce_linux_Bela.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#elif JUCE_ANDROID
|
||||
#include "native/juce_android_Audio.cpp"
|
||||
|
|
|
|||
|
|
@ -108,6 +108,13 @@
|
|||
#define JUCE_JACK 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_BELA
|
||||
Enables Bela audio devices on Bela boards.
|
||||
*/
|
||||
#ifndef JUCE_BELA
|
||||
#define JUCE_BELA 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_ANDROID_OBOE
|
||||
***
|
||||
DEVELOPER PREVIEW - Oboe is currently in developer preview and
|
||||
|
|
|
|||
373
modules/juce_audio_devices/native/juce_linux_Bela.cpp
Normal file
373
modules/juce_audio_devices/native/juce_linux_Bela.cpp
Normal file
|
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2017 - ROLI Ltd.
|
||||
|
||||
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 BelaAudioIODevice : public AudioIODevice
|
||||
{
|
||||
public:
|
||||
BelaAudioIODevice() : AudioIODevice (BelaAudioIODevice::belaTypeName,
|
||||
BelaAudioIODevice::belaTypeName)
|
||||
{
|
||||
Bela_defaultSettings (&defaultSettings);
|
||||
}
|
||||
|
||||
~BelaAudioIODevice() {}
|
||||
|
||||
//==============================================================================
|
||||
StringArray getOutputChannelNames() override { return { "Out #1", "Out #2" }; }
|
||||
StringArray getInputChannelNames() override { return { "In #1", "In #2" }; }
|
||||
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);
|
||||
|
||||
settings.useAnalog = 0;
|
||||
settings.useDigital = 0;
|
||||
settings.numAudioInChannels = numIns;
|
||||
settings.numAudioOutChannels = numOuts;
|
||||
settings.detectUnderruns = 1;
|
||||
settings.setup = setupCallback;
|
||||
settings.render = renderCallback;
|
||||
settings.cleanup = cleanupCallback;
|
||||
settings.interleave = 1;
|
||||
|
||||
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);
|
||||
|
||||
audioInBuffer.setSize (actualNumberOfInputs, actualBufferSize);
|
||||
channelInBuffer.calloc (actualNumberOfInputs);
|
||||
|
||||
audioOutBuffer.setSize (actualNumberOfOutputs, actualBufferSize);
|
||||
channelOutBuffer.calloc (actualNumberOfOutputs);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void close() override
|
||||
{
|
||||
stop();
|
||||
|
||||
if (isBelaOpen)
|
||||
{
|
||||
Bela_cleanupAudio();
|
||||
|
||||
isBelaOpen = false;
|
||||
callback = nullptr;
|
||||
underruns = 0;
|
||||
|
||||
actualBufferSize = 0;
|
||||
actualNumberOfInputs = 0;
|
||||
actualNumberOfOutputs = 0;
|
||||
|
||||
audioInBuffer.setSize (0, 0);
|
||||
channelInBuffer.free();
|
||||
|
||||
audioOutBuffer.setSize (0, 0);
|
||||
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
|
||||
{
|
||||
audioInBuffer.clear();
|
||||
audioOutBuffer.clear();
|
||||
|
||||
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 actualBufferSize; }
|
||||
double getCurrentSampleRate() override { return 44100.0; }
|
||||
int getCurrentBitDepth() override { return 24; }
|
||||
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 { return underruns; }
|
||||
|
||||
//==============================================================================
|
||||
static const char* const belaTypeName;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
bool setup (BelaContext& context)
|
||||
{
|
||||
actualBufferSize = context.audioFrames;
|
||||
actualNumberOfInputs = context.audioInChannels;
|
||||
actualNumberOfOutputs = context.audioOutChannels;
|
||||
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);
|
||||
|
||||
if (callback != nullptr)
|
||||
{
|
||||
jassert (context.audioFrames <= actualBufferSize);
|
||||
auto numSamples = jmin (context.audioFrames, actualBufferSize);
|
||||
auto interleaved = ((context.flags & BELA_FLAG_INTERLEAVED) != 0);
|
||||
auto numIns = jmin (actualNumberOfInputs, (int) context.audioInChannels);
|
||||
auto numOuts = jmin (actualNumberOfOutputs, (int) context.audioOutChannels);
|
||||
|
||||
int ch;
|
||||
|
||||
if (interleaved && context.audioInChannels > 1)
|
||||
{
|
||||
for (ch = 0; ch < numIns; ++ch)
|
||||
{
|
||||
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst>;
|
||||
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const>;
|
||||
|
||||
channelInBuffer[ch] = audioInBuffer.getWritePointer (ch);
|
||||
DstSampleType dstData (audioInBuffer.getWritePointer (ch));
|
||||
SrcSampleType srcData (context.audioIn + ch, context.audioInChannels);
|
||||
dstData.convertSamples (srcData, numSamples);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ch = 0; ch < numIns; ++ch)
|
||||
channelInBuffer[ch] = context.audioIn + (ch * numSamples);
|
||||
}
|
||||
|
||||
for (; ch < actualNumberOfInputs; ++ch)
|
||||
{
|
||||
channelInBuffer[ch] = audioInBuffer.getWritePointer(ch);
|
||||
zeromem (audioInBuffer.getWritePointer (ch), sizeof (float) * numSamples);
|
||||
}
|
||||
|
||||
for (int i = 0; i < actualNumberOfOutputs; ++i)
|
||||
channelOutBuffer[i] = ((interleaved && context.audioOutChannels > 1) || i >= context.audioOutChannels ? audioOutBuffer.getWritePointer (i)
|
||||
: context.audioOut + (i * numSamples));
|
||||
|
||||
callback->audioDeviceIOCallback (channelInBuffer.getData(), actualNumberOfInputs,
|
||||
channelOutBuffer.getData(), actualNumberOfOutputs,
|
||||
numSamples);
|
||||
|
||||
if (interleaved && context.audioOutChannels > 1)
|
||||
{
|
||||
for (int i = 0; i < numOuts; ++i)
|
||||
{
|
||||
using DstSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst>;
|
||||
using SrcSampleType = AudioData::Pointer<AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const>;
|
||||
|
||||
SrcSampleType srcData (channelOutBuffer[i]);
|
||||
DstSampleType dstData (context.audioOut + i, context.audioOutChannels);
|
||||
|
||||
dstData.convertSamples (srcData, numSamples);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cleanup (BelaContext&)
|
||||
{
|
||||
ScopedLock lock (callbackLock);
|
||||
|
||||
if (callback != nullptr)
|
||||
callback->audioDeviceStopped();
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
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;
|
||||
|
||||
AudioBuffer<float> audioInBuffer, audioOutBuffer;
|
||||
HeapBlock<const float*> channelInBuffer;
|
||||
HeapBlock<float*> channelOutBuffer;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODevice)
|
||||
};
|
||||
|
||||
const char* const BelaAudioIODevice::belaTypeName = "Bela Analog";
|
||||
|
||||
//==============================================================================
|
||||
struct BelaAudioIODeviceType : public AudioIODeviceType
|
||||
{
|
||||
BelaAudioIODeviceType() : AudioIODeviceType ("Bela") {}
|
||||
|
||||
// TODO: support analog outputs
|
||||
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
|
||||
{
|
||||
if (outputName == BelaAudioIODevice::belaTypeName || inputName == BelaAudioIODevice::belaTypeName)
|
||||
return new BelaAudioIODevice();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODeviceType)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela()
|
||||
{
|
||||
return new BelaAudioIODeviceType();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
Loading…
Add table
Add a link
Reference in a new issue