Browse Source

Add juce engine class

Signed-off-by: falkTX <falktx@gmail.com>
tags/v2.1-alpha1-winvst
parent
commit
eb5d88ae3d
2 changed files with 1337 additions and 0 deletions
  1. +940
    -0
      source/backend/engine/CarlaEngineJuce.cpp
  2. +397
    -0
      source/modules/AppConfig.h

+ 940
- 0
source/backend/engine/CarlaEngineJuce.cpp View File

@@ -0,0 +1,940 @@
/*
* Carla Plugin Host
* Copyright (C) 2011-2017 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the GPL.txt file
*/

#include "CarlaEngineGraph.hpp"
#include "CarlaEngineInternal.hpp"
#include "CarlaBackendUtils.hpp"
#include "CarlaStringList.hpp"

#include "RtLinkedList.hpp"

#include "juce_audio_devices/juce_audio_devices.h"

CARLA_BACKEND_START_NAMESPACE

// -------------------------------------------------------------------------------------------------------------------

struct MidiInPort {
juce::MidiInput* port;
char name[STR_MAX+1];
};

struct MidiOutPort {
juce::MidiOutput* port;
char name[STR_MAX+1];
};

struct RtMidiEvent {
uint64_t time; // needs to compare to internal time
uint8_t size;
uint8_t data[EngineMidiEvent::kDataSize];
};

// -------------------------------------------------------------------------------------------------------------------
// Fallback data

static const MidiInPort kMidiInPortFallback = { nullptr, { '\0' } };
static /* */ MidiInPort kMidiInPortFallbackNC = { nullptr, { '\0' } };
static const MidiOutPort kMidiOutPortFallback = { nullptr, { '\0' } };
static /* */ MidiOutPort kMidiOutPortFallbackNC = { nullptr, { '\0' } };
static const RtMidiEvent kRtMidiEventFallback = { 0, 0, { 0 } };

// -------------------------------------------------------------------------------------------------------------------
// Global static data

static CharStringListPtr gDeviceNames;
static juce::OwnedArray<juce::AudioIODeviceType> gDeviceTypes;

struct JuceCleanup : public juce::DeletedAtShutdown {
JuceCleanup() noexcept {}
~JuceCleanup()
{
gDeviceTypes.clear(true);
}
};

// -------------------------------------------------------------------------------------------------------------------
// Cleanup

static void initJuceDevicesIfNeeded()
{
static juce::AudioDeviceManager sDeviceManager;

if (gDeviceTypes.size() != 0)
return;

sDeviceManager.createAudioDeviceTypes(gDeviceTypes);

CARLA_SAFE_ASSERT_RETURN(gDeviceTypes.size() != 0,);

new JuceCleanup();

// remove JACK from device list
for (int i=0, count=gDeviceTypes.size(); i < count; ++i)
{
if (gDeviceTypes[i]->getTypeName() == "JACK")
{
gDeviceTypes.remove(i, true);
break;
}
}
}

// -------------------------------------------------------------------------------------------------------------------
// Juce Engine

class CarlaEngineJuce : public CarlaEngine,
public juce::AudioIODeviceCallback,
public juce::MidiInputCallback
{
public:
CarlaEngineJuce(juce::AudioIODeviceType* const devType)
: CarlaEngine(),
juce::AudioIODeviceCallback(),
fDevice(),
fDeviceType(devType),
fMidiIns(),
fMidiInEvents(),
fMidiOuts(),
fMidiOutMutex()
{
carla_debug("CarlaEngineJuce::CarlaEngineJuce(%p)", devType);

// just to make sure
pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
}

~CarlaEngineJuce() override
{
carla_debug("CarlaEngineJuce::~CarlaEngineJuce()");
}

// -------------------------------------

bool init(const char* const clientName) override
{
CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
carla_debug("CarlaEngineJuce::init(\"%s\")", clientName);

if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
{
setLastError("Invalid process mode");
return false;
}

juce::String deviceName;

if (pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0')
{
deviceName = pData->options.audioDevice;
}
else
{
const int defaultIndex = fDeviceType->getDefaultDeviceIndex(false);
juce::StringArray deviceNames(fDeviceType->getDeviceNames());

if (defaultIndex >= 0 && defaultIndex < deviceNames.size())
deviceName = deviceNames[defaultIndex];
}

if (deviceName.isEmpty())
{
setLastError("Audio device has not been selected yet and a default one is not available");
return false;
}

fDevice = fDeviceType->createDevice(deviceName, deviceName);

if (fDevice == nullptr)
{
setLastError("Failed to create device");
return false;
}

juce::StringArray inputNames(fDevice->getInputChannelNames());
juce::StringArray outputNames(fDevice->getOutputChannelNames());

if (inputNames.size() < 0 || outputNames.size() <= 0)
{
setLastError("Selected device does not have any outputs");
return false;
}

juce::BigInteger inputChannels;
inputChannels.setRange(0, inputNames.size(), true);

juce::BigInteger outputChannels;
outputChannels.setRange(0, outputNames.size(), true);

juce::String error = fDevice->open(inputChannels, outputChannels, pData->options.audioSampleRate, static_cast<int>(pData->options.audioBufferSize));

if (error.isNotEmpty())
{
setLastError(error.toUTF8());
fDevice = nullptr;
return false;
}

if (! pData->init(clientName))
{
close();
setLastError("Failed to init internal data");
return false;
}

pData->bufferSize = static_cast<uint32_t>(fDevice->getCurrentBufferSizeSamples());
pData->sampleRate = fDevice->getCurrentSampleRate();
pData->initTime(pData->options.transportExtra);

pData->graph.create(static_cast<uint32_t>(inputNames.size()), static_cast<uint32_t>(outputNames.size()));

fDevice->start(this);

patchbayRefresh(false);

if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), false);

callback(ENGINE_CALLBACK_ENGINE_STARTED, 0, pData->options.processMode, pData->options.transportMode, 0.0f, getCurrentDriverName());
return true;
}

bool close() override
{
carla_debug("CarlaEngineJuce::close()");

bool hasError = false;

// stop stream first
if (fDevice != nullptr && fDevice->isPlaying())
fDevice->stop();

// clear engine data
CarlaEngine::close();

pData->graph.destroy();

for (LinkedList<MidiInPort>::Itenerator it = fMidiIns.begin2(); it.valid(); it.next())
{
MidiInPort& inPort(it.getValue(kMidiInPortFallbackNC));
CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);

inPort.port->stop();
delete inPort.port;
}

fMidiIns.clear();
fMidiInEvents.clear();

fMidiOutMutex.lock();

for (LinkedList<MidiOutPort>::Itenerator it = fMidiOuts.begin2(); it.valid(); it.next())
{
MidiOutPort& outPort(it.getValue(kMidiOutPortFallbackNC));
CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);

outPort.port->stopBackgroundThread();
delete outPort.port;
}

fMidiOuts.clear();
fMidiOutMutex.unlock();

// close stream
if (fDevice != nullptr)
{
if (fDevice->isOpen())
fDevice->close();

fDevice = nullptr;
}

return !hasError;
}

bool isRunning() const noexcept override
{
return fDevice != nullptr && fDevice->isOpen();
}

bool isOffline() const noexcept override
{
return false;
}

EngineType getType() const noexcept override
{
return kEngineTypeJuce;
}

const char* getCurrentDriverName() const noexcept override
{
return fDeviceType->getTypeName().toRawUTF8();
}

// -------------------------------------------------------------------
// Patchbay

template<class Graph>
bool refreshExternalGraphPorts(Graph* const graph, const bool sendCallback)
{
CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);

char strBuf[STR_MAX+1];
strBuf[STR_MAX] = '\0';

ExternalGraph& extGraph(graph->extGraph);

// ---------------------------------------------------------------
// clear last ports

extGraph.clear();

// ---------------------------------------------------------------
// fill in new ones

// Audio In
{
juce::StringArray inputNames(fDevice->getInputChannelNames());

for (int i=0, count=inputNames.size(); i<count; ++i)
{
PortNameToId portNameToId;
portNameToId.setData(kExternalGraphGroupAudioIn, uint(i+1), inputNames[i].toRawUTF8(), "");

extGraph.audioPorts.ins.append(portNameToId);
}
}

// Audio Out
{
juce::StringArray outputNames(fDevice->getOutputChannelNames());

for (int i=0, count=outputNames.size(); i<count; ++i)
{
PortNameToId portNameToId;
portNameToId.setData(kExternalGraphGroupAudioOut, uint(i+1), outputNames[i].toRawUTF8(), "");
}
}

// MIDI In
{
juce::StringArray midiIns(juce::MidiInput::getDevices());

for (int i=0, count=midiIns.size(); i<count; ++i)
{
PortNameToId portNameToId;
portNameToId.setData(kExternalGraphGroupMidiIn, uint(i+1), midiIns[i].toRawUTF8(), "");

extGraph.midiPorts.ins.append(portNameToId);
}
}

// MIDI Out
{
juce::StringArray midiOuts(juce::MidiOutput::getDevices());

for (int i=0, count=midiOuts.size(); i<count; ++i)
{
PortNameToId portNameToId;
portNameToId.setData(kExternalGraphGroupMidiOut, uint(i+1), midiOuts[i].toRawUTF8(), "");

extGraph.midiPorts.outs.append(portNameToId);
}
}

// ---------------------------------------------------------------
// now refresh

if (sendCallback)
{
juce::String deviceName(fDevice->getName());

if (deviceName.isNotEmpty())
deviceName = deviceName.dropLastCharacters(deviceName.fromFirstOccurrenceOf(", ", true, false).length());

graph->refresh(deviceName.toRawUTF8());
}

// ---------------------------------------------------------------
// add midi connections

for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin2(); it.valid(); it.next())
{
const MidiInPort& inPort(it.getValue(kMidiInPortFallback));
CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);

const uint portId(extGraph.midiPorts.getPortId(true, inPort.name));
CARLA_SAFE_ASSERT_CONTINUE(portId < extGraph.midiPorts.ins.count());

ConnectionToId connectionToId;
connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupMidiIn, portId, kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiIn);

std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);

callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);

extGraph.connections.list.append(connectionToId);
}

fMidiOutMutex.lock();

for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
{
const MidiOutPort& outPort(it.getValue(kMidiOutPortFallback));
CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);

const uint portId(extGraph.midiPorts.getPortId(false, outPort.name));
CARLA_SAFE_ASSERT_CONTINUE(portId < extGraph.midiPorts.outs.count());

ConnectionToId connectionToId;
connectionToId.setData(++(extGraph.connections.lastId), kExternalGraphGroupCarla, kExternalGraphCarlaPortMidiOut, kExternalGraphGroupMidiOut, portId);

std::snprintf(strBuf, STR_MAX, "%i:%i:%i:%i", connectionToId.groupA, connectionToId.portA, connectionToId.groupB, connectionToId.portB);

callback(ENGINE_CALLBACK_PATCHBAY_CONNECTION_ADDED, connectionToId.id, 0, 0, 0.0f, strBuf);

extGraph.connections.list.append(connectionToId);
}

fMidiOutMutex.unlock();

return true;
}

bool patchbayRefresh(const bool external) override
{
CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);

if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
{
return refreshExternalGraphPorts<RackGraph>(pData->graph.getRackGraph(), true);
}
else
{
pData->graph.setUsingExternal(external);

if (external)
return refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), true);
else
return CarlaEngine::patchbayRefresh(false);
}

return false;
}

// -------------------------------------------------------------------

protected:
void audioDeviceIOCallback(const float** inputChannelData, int numInputChannels, float** outputChannelData,
int numOutputChannels, int numSamples) override
{
const PendingRtEventsRunner prt(this, numSamples);

// assert juce buffers
CARLA_SAFE_ASSERT_RETURN(numInputChannels >= 0,);
CARLA_SAFE_ASSERT_RETURN(numOutputChannels > 0,);
CARLA_SAFE_ASSERT_RETURN(outputChannelData != nullptr,);
CARLA_SAFE_ASSERT_RETURN(numSamples == static_cast<int>(pData->bufferSize),);

const uint32_t nframes(static_cast<uint32_t>(numSamples));

// initialize juce output
for (int i=0; i < numOutputChannels; ++i)
carla_zeroFloats(outputChannelData[i], numSamples);

// initialize events
carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount);
carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount);

if (fMidiInEvents.mutex.tryLock())
{
uint32_t engineEventIndex = 0;
fMidiInEvents.splice();

for (LinkedList<RtMidiEvent>::Itenerator it = fMidiInEvents.data.begin2(); it.valid(); it.next())
{
const RtMidiEvent& midiEvent(it.getValue(kRtMidiEventFallback));
CARLA_SAFE_ASSERT_CONTINUE(midiEvent.size > 0);

EngineEvent& engineEvent(pData->events.in[engineEventIndex++]);

if (midiEvent.time < pData->timeInfo.frame)
{
engineEvent.time = 0;
}
else if (midiEvent.time >= pData->timeInfo.frame + nframes)
{
carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent.time, pData->timeInfo.frame);
engineEvent.time = static_cast<uint32_t>(pData->timeInfo.frame) + nframes - 1;
}
else
engineEvent.time = static_cast<uint32_t>(midiEvent.time - pData->timeInfo.frame);

engineEvent.fillFromMidiData(midiEvent.size, midiEvent.data, 0);

if (engineEventIndex >= kMaxEngineEventInternalCount)
break;
}

fMidiInEvents.data.clear();
fMidiInEvents.mutex.unlock();
}

pData->graph.process(pData, inputChannelData, outputChannelData, nframes);

fMidiOutMutex.lock();

if (fMidiOuts.count() > 0)
{
uint8_t size = 0;
uint8_t data[3] = { 0, 0, 0 };
const uint8_t* dataPtr = data;

for (ushort i=0; i < kMaxEngineEventInternalCount; ++i)
{
const EngineEvent& engineEvent(pData->events.out[i]);

if (engineEvent.type == kEngineEventTypeNull)
break;

else if (engineEvent.type == kEngineEventTypeControl)
{
const EngineControlEvent& ctrlEvent(engineEvent.ctrl);
ctrlEvent.convertToMidiData(engineEvent.channel, data);
dataPtr = data;
}
else if (engineEvent.type == kEngineEventTypeMidi)
{
const EngineMidiEvent& midiEvent(engineEvent.midi);

size = midiEvent.size;

if (size > EngineMidiEvent::kDataSize && midiEvent.dataExt != nullptr)
dataPtr = midiEvent.dataExt;
else
dataPtr = midiEvent.data;
}
else
{
continue;
}

if (size > 0)
{
juce::MidiMessage message(static_cast<const void*>(dataPtr), static_cast<int>(size), static_cast<double>(engineEvent.time)/nframes);

for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
{
MidiOutPort& outPort(it.getValue(kMidiOutPortFallbackNC));
CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);

outPort.port->sendMessageNow(message);
}
}
}
}

fMidiOutMutex.unlock();
}

void audioDeviceAboutToStart(juce::AudioIODevice* /*device*/) override
{
}

void audioDeviceStopped() override
{
}

void audioDeviceError(const juce::String& errorMessage) override
{
callback(ENGINE_CALLBACK_ERROR, 0, 0, 0, 0.0f, errorMessage.toRawUTF8());
}

// -------------------------------------------------------------------

void handleIncomingMidiMessage(juce::MidiInput* /*source*/, const juce::MidiMessage& message) override
{
const int messageSize(message.getRawDataSize());

if (messageSize <= 0 || messageSize > EngineMidiEvent::kDataSize)
return;

const uint8_t* const messageData(message.getRawData());

RtMidiEvent midiEvent;
midiEvent.time = 0; // TODO

midiEvent.size = static_cast<uint8_t>(messageSize);

int i=0;
for (; i < messageSize; ++i)
midiEvent.data[i] = messageData[i];
for (; i < EngineMidiEvent::kDataSize; ++i)
midiEvent.data[i] = 0;

fMidiInEvents.append(midiEvent);
}

// -------------------------------------------------------------------

bool connectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
{
CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
carla_stdout("CarlaEngineJuce::connectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);

switch (connectionType)
{
case kExternalGraphConnectionAudioIn1:
case kExternalGraphConnectionAudioIn2:
case kExternalGraphConnectionAudioOut1:
case kExternalGraphConnectionAudioOut2:
return CarlaEngine::connectExternalGraphPort(connectionType, portId, portName);

case kExternalGraphConnectionMidiInput: {
juce::StringArray midiIns(juce::MidiInput::getDevices());

if (! midiIns.contains(portName))
return false;

juce::MidiInput* const juceMidiIn(juce::MidiInput::openDevice(midiIns.indexOf(portName), this));
juceMidiIn->start();

MidiInPort midiPort;
midiPort.port = juceMidiIn;

std::strncpy(midiPort.name, portName, STR_MAX);
midiPort.name[STR_MAX] = '\0';

fMidiIns.append(midiPort);
return true;
} break;

case kExternalGraphConnectionMidiOutput: {
juce::StringArray midiOuts(juce::MidiOutput::getDevices());

if (! midiOuts.contains(portName))
return false;

juce::MidiOutput* const juceMidiOut(juce::MidiOutput::openDevice(midiOuts.indexOf(portName)));
juceMidiOut->startBackgroundThread();

MidiOutPort midiPort;
midiPort.port = juceMidiOut;

std::strncpy(midiPort.name, portName, STR_MAX);
midiPort.name[STR_MAX] = '\0';

const CarlaMutexLocker cml(fMidiOutMutex);

fMidiOuts.append(midiPort);
return true;
} break;
}

return false;
}

bool disconnectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
{
CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
carla_debug("CarlaEngineJuce::disconnectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);

switch (connectionType)
{
case kExternalGraphConnectionAudioIn1:
case kExternalGraphConnectionAudioIn2:
case kExternalGraphConnectionAudioOut1:
case kExternalGraphConnectionAudioOut2:
return CarlaEngine::disconnectExternalGraphPort(connectionType, portId, portName);

case kExternalGraphConnectionMidiInput:
for (LinkedList<MidiInPort>::Itenerator it=fMidiIns.begin2(); it.valid(); it.next())
{
MidiInPort& inPort(it.getValue(kMidiInPortFallbackNC));
CARLA_SAFE_ASSERT_CONTINUE(inPort.port != nullptr);

if (std::strcmp(inPort.name, portName) != 0)
continue;

inPort.port->stop();
delete inPort.port;

fMidiIns.remove(it);
return true;
}
break;

case kExternalGraphConnectionMidiOutput: {
const CarlaMutexLocker cml(fMidiOutMutex);

for (LinkedList<MidiOutPort>::Itenerator it=fMidiOuts.begin2(); it.valid(); it.next())
{
MidiOutPort& outPort(it.getValue(kMidiOutPortFallbackNC));
CARLA_SAFE_ASSERT_CONTINUE(outPort.port != nullptr);

if (std::strcmp(outPort.name, portName) != 0)
continue;

outPort.port->stopBackgroundThread();
delete outPort.port;

fMidiOuts.remove(it);
return true;
}
} break;
}

return false;
}

// -------------------------------------

private:
ScopedPointer<juce::AudioIODevice> fDevice;
juce::AudioIODeviceType* const fDeviceType;

struct RtMidiEvents {
CarlaMutex mutex;
RtLinkedList<RtMidiEvent>::Pool dataPool;
RtLinkedList<RtMidiEvent> data;
RtLinkedList<RtMidiEvent> dataPending;

RtMidiEvents()
: mutex(),
dataPool(512, 512),
data(dataPool),
dataPending(dataPool) {}

~RtMidiEvents()
{
clear();
}

void append(const RtMidiEvent& event)
{
mutex.lock();
dataPending.append(event);
mutex.unlock();
}

void clear()
{
mutex.lock();
data.clear();
dataPending.clear();
mutex.unlock();
}

void splice()
{
if (dataPending.count() > 0)
dataPending.moveTo(data, true /* append */);
}
};

LinkedList<MidiInPort> fMidiIns;
RtMidiEvents fMidiInEvents;

LinkedList<MidiOutPort> fMidiOuts;
CarlaMutex fMidiOutMutex;

CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineJuce)
};

// -----------------------------------------

CarlaEngine* CarlaEngine::newJuce(const AudioApi api)
{
initJuceDevicesIfNeeded();

juce::String juceApi;

switch (api)
{
case AUDIO_API_NULL:
case AUDIO_API_OSS:
case AUDIO_API_PULSEAUDIO:
case AUDIO_API_WASAPI:
break;
case AUDIO_API_JACK:
juceApi = "JACK";
break;
case AUDIO_API_ALSA:
juceApi = "ALSA";
break;
case AUDIO_API_COREAUDIO:
juceApi = "CoreAudio";
break;
case AUDIO_API_ASIO:
juceApi = "ASIO";
break;
case AUDIO_API_DIRECTSOUND:
juceApi = "DirectSound";
break;
}

if (juceApi.isEmpty())
return nullptr;

juce::AudioIODeviceType* deviceType = nullptr;

for (int i=0, count=gDeviceTypes.size(); i < count; ++i)
{
deviceType = gDeviceTypes[i];

if (deviceType == nullptr || deviceType->getTypeName() == juceApi)
break;
}

if (deviceType == nullptr)
return nullptr;

deviceType->scanForDevices();

return new CarlaEngineJuce(deviceType);
}

uint CarlaEngine::getJuceApiCount()
{
initJuceDevicesIfNeeded();

return static_cast<uint>(gDeviceTypes.size());
}

const char* CarlaEngine::getJuceApiName(const uint uindex)
{
initJuceDevicesIfNeeded();

const int index(static_cast<int>(uindex));

CARLA_SAFE_ASSERT_RETURN(index < gDeviceTypes.size(), nullptr);

juce::AudioIODeviceType* const deviceType(gDeviceTypes[index]);
CARLA_SAFE_ASSERT_RETURN(deviceType != nullptr, nullptr);

return deviceType->getTypeName().toRawUTF8();
}

const char* const* CarlaEngine::getJuceApiDeviceNames(const uint uindex)
{
initJuceDevicesIfNeeded();

const int index(static_cast<int>(uindex));

CARLA_SAFE_ASSERT_RETURN(index < gDeviceTypes.size(), nullptr);

juce::AudioIODeviceType* const deviceType(gDeviceTypes[index]);
CARLA_SAFE_ASSERT_RETURN(deviceType != nullptr, nullptr);

deviceType->scanForDevices();

juce::StringArray juceDeviceNames(deviceType->getDeviceNames());
const int juceDeviceNameCount(juceDeviceNames.size());

if (juceDeviceNameCount <= 0)
return nullptr;

CarlaStringList devNames;

for (int i=0; i < juceDeviceNameCount; ++i)
devNames.append(juceDeviceNames[i].toRawUTF8());

gDeviceNames = devNames.toCharStringListPtr();

return gDeviceNames;
}

const EngineDriverDeviceInfo* CarlaEngine::getJuceDeviceInfo(const uint uindex, const char* const deviceName)
{
initJuceDevicesIfNeeded();

const int index(static_cast<int>(uindex));

CARLA_SAFE_ASSERT_RETURN(index < gDeviceTypes.size(), nullptr);

juce::AudioIODeviceType* const deviceType(gDeviceTypes[index]);
CARLA_SAFE_ASSERT_RETURN(deviceType != nullptr, nullptr);

deviceType->scanForDevices();

ScopedPointer<juce::AudioIODevice> device(deviceType->createDevice(deviceName, deviceName));

if (device == nullptr)
return nullptr;

static EngineDriverDeviceInfo devInfo = { 0x0, nullptr, nullptr };
static uint32_t dummyBufferSizes[11] = { 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 0 };
static double dummySampleRates[14] = { 22050.0, 32000.0, 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 0.0 };

// reset
devInfo.hints = ENGINE_DRIVER_DEVICE_VARIABLE_BUFFER_SIZE | ENGINE_DRIVER_DEVICE_VARIABLE_SAMPLE_RATE;

// cleanup
if (devInfo.bufferSizes != nullptr && devInfo.bufferSizes != dummyBufferSizes)
{
delete[] devInfo.bufferSizes;
devInfo.bufferSizes = nullptr;
}

if (devInfo.sampleRates != nullptr && devInfo.sampleRates != dummySampleRates)
{
delete[] devInfo.sampleRates;
devInfo.sampleRates = nullptr;
}

if (device->hasControlPanel())
devInfo.hints |= ENGINE_DRIVER_DEVICE_HAS_CONTROL_PANEL;

juce::Array<int> juceBufferSizes = device->getAvailableBufferSizes();
if (int bufferSizesCount = juceBufferSizes.size())
{
uint32_t* const bufferSizes(new uint32_t[bufferSizesCount+1]);

for (int i=0; i < bufferSizesCount; ++i)
bufferSizes[i] = static_cast<uint32_t>(juceBufferSizes[i]);
bufferSizes[bufferSizesCount] = 0;

devInfo.bufferSizes = bufferSizes;
}
else
{
devInfo.bufferSizes = dummyBufferSizes;
}

juce::Array<double> juceSampleRates = device->getAvailableSampleRates();
if (int sampleRatesCount = juceSampleRates.size())
{
double* const sampleRates(new double[sampleRatesCount+1]);

for (int i=0; i < sampleRatesCount; ++i)
sampleRates[i] = juceSampleRates[i];
sampleRates[sampleRatesCount] = 0.0;

devInfo.sampleRates = sampleRates;
}
else
{
devInfo.sampleRates = dummySampleRates;
}

return &devInfo;
}

// -----------------------------------------

CARLA_BACKEND_END_NAMESPACE

+ 397
- 0
source/modules/AppConfig.h View File

@@ -0,0 +1,397 @@
#ifndef CARLA_JUCE_APPCONFIG_H_INCLUDED
#define CARLA_JUCE_APPCONFIG_H_INCLUDED
// --------------------------------------------------------------------------------------------------------------------
// Check OS
#if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
# define APPCONFIG_OS_WIN64
#elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__)
# define APPCONFIG_OS_WIN32
#elif defined(__APPLE__)
# define APPCONFIG_OS_MAC
#elif defined(__linux__) || defined(__linux)
# define APPCONFIG_OS_LINUX
#else
# error Unsupported platform!
#endif
#if defined(APPCONFIG_OS_WIN32) || defined(APPCONFIG_OS_WIN64)
# define APPCONFIG_OS_WIN
#elif defined(APPCONFIG_OS_LINUX) || defined(APPCONFIG_OS_MAC)
# define APPCONFIG_OS_UNIX
#endif
// --------------------------------------------------------------------------------------------------------------------
// always enabled
#define JUCE_MODULE_AVAILABLE_juce_audio_basics 1
#define JUCE_MODULE_AVAILABLE_juce_audio_devices 1
#define JUCE_MODULE_AVAILABLE_juce_core 1
#define JUCE_MODULE_AVAILABLE_juce_data_structures 1
#define JUCE_MODULE_AVAILABLE_juce_events 1
// always disabled
#define JUCE_MODULE_AVAILABLE_juce_audio_formats 0
#define JUCE_MODULE_AVAILABLE_juce_audio_plugin_client 0
#define JUCE_MODULE_AVAILABLE_juce_audio_utils 0
#define JUCE_MODULE_AVAILABLE_juce_cryptography 0
#define JUCE_MODULE_AVAILABLE_juce_opengl 0
#define JUCE_MODULE_AVAILABLE_juce_video 0
// conditional
#if defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN)
# define JUCE_MODULE_AVAILABLE_juce_audio_processors 1
# define JUCE_MODULE_AVAILABLE_juce_graphics 1
# define JUCE_MODULE_AVAILABLE_juce_gui_basics 1
# define JUCE_MODULE_AVAILABLE_juce_gui_extra 1
#else
# define JUCE_MODULE_AVAILABLE_juce_audio_processors 0
# define JUCE_MODULE_AVAILABLE_juce_graphics 0
# define JUCE_MODULE_AVAILABLE_juce_gui_basics 0
# define JUCE_MODULE_AVAILABLE_juce_gui_extra 0
#endif
// misc
#define JUCE_DISABLE_JUCE_VERSION_PRINTING 1
#define JUCE_GLOBAL_MODULE_SETTINGS_INCLUDED 1
#define JUCE_STANDALONE_APPLICATION 0
#define JUCE_REPORT_APP_USAGE 0
#define JUCE_DISPLAY_SPLASH_SCREEN 0
#define JUCE_USE_DARK_SPLASH_SCREEN 0
#define JUCE_STRING_UTF_TYPE 8
#define JUCE_USE_VFORK 1
#if ! (defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN))
# define JUCE_MODAL_LOOPS_PERMITTED 0
# define JUCE_AUDIOPROCESSOR_NO_GUI 1
#endif
// --------------------------------------------------------------------------------------------------------------------
// juce_audio_basics
// nothing here
// --------------------------------------------------------------------------------------------------------------------
// juce_audio_devices
//=============================================================================
/** Config: JUCE_ASIO
Enables ASIO audio devices (MS Windows only).
Turning this on means that you'll need to have the Steinberg ASIO SDK installed
on your Windows build machine.
See the comments in the ASIOAudioIODevice class's header file for more
info about this.
*/
#ifdef APPCONFIG_OS_WIN
#define JUCE_ASIO 1
#else
#define JUCE_ASIO 0
#endif
/** Config: JUCE_WASAPI
Enables WASAPI audio devices (Windows Vista and above).
*/
#define JUCE_WASAPI 0
/** Config: JUCE_DIRECTSOUND
Enables DirectSound audio (MS Windows only).
*/
#ifdef APPCONFIG_OS_WIN
#define JUCE_DIRECTSOUND 1
#else
#define JUCE_DIRECTSOUND 0
#endif
/** Config: JUCE_ALSA
Enables ALSA audio devices (Linux only).
*/
#ifdef APPCONFIG_OS_LINUX
#define JUCE_ALSA 1
#define JUCE_ALSA_MIDI_INPUT_NAME "Carla"
#define JUCE_ALSA_MIDI_OUTPUT_NAME "Carla"
#define JUCE_ALSA_MIDI_INPUT_PORT_NAME "Midi In"
#define JUCE_ALSA_MIDI_OUTPUT_PORT_NAME "Midi Out"
#else
#define JUCE_ALSA 0
#endif
/** Config: JUCE_JACK
Enables JACK audio devices (Linux only).
*/
#ifdef APPCONFIG_OS_LINUX
#define JUCE_JACK 1
#define JUCE_JACK_CLIENT_NAME "Carla"
#else
#define JUCE_JACK 0
#endif
//=============================================================================
/** Config: JUCE_USE_CDREADER
Enables the AudioCDReader class (on supported platforms).
*/
#define JUCE_USE_CDREADER 0
/** Config: JUCE_USE_CDBURNER
Enables the AudioCDBurner class (on supported platforms).
*/
#define JUCE_USE_CDBURNER 0
// --------------------------------------------------------------------------------------------------------------------
// juce_audio_formats
//=============================================================================
/** Config: JUCE_USE_FLAC
Enables the FLAC audio codec classes (available on all platforms).
If your app doesn't need to read FLAC files, you might want to disable this to
reduce the size of your codebase and build time.
*/
#define JUCE_USE_FLAC 1
/** Config: JUCE_USE_OGGVORBIS
Enables the Ogg-Vorbis audio codec classes (available on all platforms).
If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to
reduce the size of your codebase and build time.
*/
#define JUCE_USE_OGGVORBIS 1
/** Config: JUCE_USE_MP3AUDIOFORMAT
Enables the software-based MP3AudioFormat class.
IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile
this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing
that Raw Material Software is in no way responsible for any patent, copyright, or other
legal issues that you may suffer as a result.
The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party
intellectual property. If you wish to use it, please seek your own independent advice about the
legality of doing so. If you are not willing to accept full responsibility for the consequences
of using this code, then do not enable this setting.
*/
#define JUCE_USE_MP3AUDIOFORMAT 0
/** Config: JUCE_USE_LAME_AUDIO_FORMAT
Enables the LameEncoderAudioFormat class.
*/
#define JUCE_USE_LAME_AUDIO_FORMAT 1
/** Config: JUCE_USE_WINDOWS_MEDIA_FORMAT
Enables the Windows Media SDK codecs.
*/
#define JUCE_USE_WINDOWS_MEDIA_FORMAT 0
// --------------------------------------------------------------------------------------------------------------------
// juce_audio_processors
//=============================================================================
/** Config: JUCE_PLUGINHOST_VST
Enables the VST audio plugin hosting classes. This requires the Steinberg VST SDK to be
installed on your machine.
@see VSTPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_AU
*/
#define JUCE_PLUGINHOST_VST 1
/** Config: JUCE_PLUGINHOST_VST3
Enables the VST3 audio plugin hosting classes. This requires the Steinberg VST3 SDK to be
installed on your machine.
@see VSTPluginFormat, VST3PluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST, JUCE_PLUGINHOST_AU
*/
#if defined(APPCONFIG_OS_MAC) || defined(APPCONFIG_OS_WIN)
# define JUCE_PLUGINHOST_VST3 1
#else
# define JUCE_PLUGINHOST_VST3 0
#endif
/** Config: JUCE_PLUGINHOST_AU
Enables the AudioUnit plugin hosting classes. This is Mac-only, of course.
@see AudioUnitPluginFormat, AudioPluginFormat, AudioPluginFormatManager, JUCE_PLUGINHOST_VST
*/
#ifdef APPCONFIG_OS_MAC
# define JUCE_PLUGINHOST_AU 1
#else
# define JUCE_PLUGINHOST_AU 0
#endif
#define JUCE_PLUGINHOST_LADSPA 0
// --------------------------------------------------------------------------------------------------------------------
// juce_core
//=============================================================================
/** Config: JUCE_FORCE_DEBUG
Normally, JUCE_DEBUG is set to 1 or 0 based on compiler and project settings,
but if you define this value, you can override this to force it to be true or false.
*/
#define JUCE_FORCE_DEBUG 0
//=============================================================================
/** Config: JUCE_LOG_ASSERTIONS
If this flag is enabled, the the jassert and jassertfalse macros will always use Logger::writeToLog()
to write a message when an assertion happens.
Enabling it will also leave this turned on in release builds. When it's disabled,
however, the jassert and jassertfalse macros will not be compiled in a
release build.
@see jassert, jassertfalse, Logger
*/
#define JUCE_LOG_ASSERTIONS 1
//=============================================================================
/** Config: JUCE_CHECK_MEMORY_LEAKS
Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector
class and the JUCE_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes.
*/
#ifdef DEBUG
#define JUCE_CHECK_MEMORY_LEAKS 1
#else
#define JUCE_CHECK_MEMORY_LEAKS 0
#endif
//=============================================================================
/** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES
In a Visual C++ build, this can be used to stop the required system libs being
automatically added to the link stage.
*/
#define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0
/** Config: JUCE_INCLUDE_ZLIB_CODE
This can be used to disable Juce's embedded 3rd-party zlib code.
You might need to tweak this if you're linking to an external zlib library in your app,
but for normal apps, this option should be left alone.
If you disable this, you might also want to set a value for JUCE_ZLIB_INCLUDE_PATH, to
specify the path where your zlib headers live.
*/
#define JUCE_INCLUDE_ZLIB_CODE 1
/** Config: JUCE_USE_CURL
Enables http/https support via libcurl (Linux only). Enabling this will add an additional
run-time dynmic dependency to libcurl.
If you disable this then https/ssl support will not be available on linux.
*/
#define JUCE_USE_CURL 0
/* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS
If enabled, this will add some exception-catching code to forward unhandled exceptions
to your JUCEApplicationBase::unhandledException() callback.
*/
#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0
/** Config: JUCE_ALLOW_STATIC_NULL_VARIABLES
If disabled, this will turn off dangerous static globals like String::empty, var::null, etc
which can cause nasty order-of-initialisation problems if they are referenced during static
constructor code.
*/
#define JUCE_ALLOW_STATIC_NULL_VARIABLES 0
// --------------------------------------------------------------------------------------------------------------------
// juce_data_structures
// nothing here
// --------------------------------------------------------------------------------------------------------------------
// juce_events
// nothing here
// --------------------------------------------------------------------------------------------------------------------
// juce_graphics
//=============================================================================
/** Config: JUCE_USE_COREIMAGE_LOADER
On OSX, enabling this flag means that the CoreImage codecs will be used to load
PNG/JPEG/GIF files. It is enabled by default, but you may want to disable it if
you'd rather use libpng, libjpeg, etc.
*/
#define JUCE_USE_COREIMAGE_LOADER 1
/** Config: JUCE_USE_DIRECTWRITE
Enabling this flag means that DirectWrite will be used when available for font
management and layout.
*/
#define JUCE_USE_DIRECTWRITE 0
#define JUCE_INCLUDE_PNGLIB_CODE 1
#define JUCE_INCLUDE_JPEGLIB_CODE 1
#ifdef APPCONFIG_OS_MAC
# define USE_COREGRAPHICS_RENDERING 1
#else
# define USE_COREGRAPHICS_RENDERING 0
#endif
// --------------------------------------------------------------------------------------------------------------------
// juce_gui_basics
//=============================================================================
/** Config: JUCE_ENABLE_REPAINT_DEBUGGING
If this option is turned on, each area of the screen that gets repainted will
flash in a random colour, so that you can see exactly which bits of your
components are being drawn.
*/
#define JUCE_ENABLE_REPAINT_DEBUGGING 0
/** JUCE_USE_XRANDR: Enables Xrandr multi-monitor support (Linux only).
Unless you specifically want to disable this, it's best to leave this option turned on.
Note that your users do not need to have Xrandr installed for your JUCE app to run, as
the availability of Xrandr is queried during runtime.
*/
#define JUCE_USE_XRANDR 0
/** JUCE_USE_XINERAMA: Enables Xinerama multi-monitor support (Linux only).
Unless you specifically want to disable this, it's best to leave this option turned on.
This will be used as a fallback if JUCE_USE_XRANDR not set or libxrandr cannot be found.
Note that your users do not need to have Xrandr installed for your JUCE app to run, as
the availability of Xinerama is queried during runtime.
*/
#define JUCE_USE_XINERAMA 0
/** Config: JUCE_USE_XSHM
Enables X shared memory for faster rendering on Linux. This is best left turned on
unless you have a good reason to disable it.
*/
#define JUCE_USE_XSHM 1
/** Config: JUCE_USE_XRENDER
Enables XRender to allow semi-transparent windowing on Linux.
*/
#define JUCE_USE_XRENDER 0
/** Config: JUCE_USE_XCURSOR
Uses XCursor to allow ARGB cursor on Linux. This is best left turned on unless you have
a good reason to disable it.
*/
#define JUCE_USE_XCURSOR 1
// --------------------------------------------------------------------------------------------------------------------
// juce_gui_extra
//=============================================================================
/** Config: JUCE_WEB_BROWSER
This lets you disable the WebBrowserComponent class (Mac and Windows).
If you're not using any embedded web-pages, turning this off may reduce your code size.
*/
#define JUCE_WEB_BROWSER 0
/** Config: JUCE_ENABLE_LIVE_CONSTANT_EDITOR
This lets you turn on the JUCE_ENABLE_LIVE_CONSTANT_EDITOR support. See the documentation
for that macro for more details.
*/
#define JUCE_ENABLE_LIVE_CONSTANT_EDITOR 0
// --------------------------------------------------------------------------------------------------------------------
#endif // CARLA_JUCE_APPCONFIG_H_INCLUDED

Loading…
Cancel
Save