@@ -836,6 +836,9 @@ public: | |||
*/ | |||
float getOutputPeak(const unsigned int pluginId, const unsigned short id) const; | |||
// FIXME - remove once IPC audio is implemented | |||
void setPeaks(const unsigned int pluginId, float const inPeaks[MAX_PEAKS], float const outPeaks[MAX_PEAKS]); | |||
// ------------------------------------------------------------------- | |||
// Callback | |||
@@ -958,14 +961,6 @@ protected: | |||
*/ | |||
void proccessPendingEvents(); | |||
/*! | |||
* TODO. | |||
*/ | |||
public: | |||
// FIXME - remove once IPC audio is implemented | |||
void setPeaks(const unsigned int pluginId, float const inPeaks[MAX_PEAKS], float const outPeaks[MAX_PEAKS]); | |||
private: | |||
#ifndef BUILD_BRIDGE | |||
// Rack mode data | |||
EngineEvent* getRackEventBuffer(const bool isInput); | |||
@@ -999,8 +994,8 @@ private: | |||
}; | |||
static CarlaEngine* newRtAudio(const RtAudioApi api); | |||
static unsigned int getRtAudioApiCount(); | |||
static const char* getRtAudioApiName(unsigned int index); | |||
static size_t getRtAudioApiCount(); | |||
static const char* getRtAudioApiName(const unsigned int index); | |||
#endif | |||
public: | |||
@@ -32,7 +32,7 @@ CARLA_BACKEND_START_NAMESPACE | |||
#endif | |||
// ------------------------------------------------------------------- | |||
// Helpers, defined in carla_plugin.cpp | |||
// Helpers, defined in CarlaPlugin.cpp | |||
extern CarlaEngine* CarlaPluginGetEngine(CarlaPlugin* const plugin); | |||
extern CarlaEngineAudioPort* CarlaPluginGetAudioInPort(CarlaPlugin* const plugin, uint32_t index); | |||
@@ -242,7 +242,7 @@ public: | |||
fRetEvent.midi.data[0] = midiStatus; | |||
fRetEvent.midi.data[1] = jackEvent.buffer[1]; | |||
fRetEvent.midi.data[2] = jackEvent.buffer[2]; | |||
fRetEvent.midi.size = jackEvent.size; | |||
fRetEvent.midi.size = static_cast<uint8_t>(jackEvent.size); | |||
} | |||
return fRetEvent; | |||
@@ -257,7 +257,8 @@ public: | |||
CARLA_ASSERT(fJackBuffer != nullptr); | |||
CARLA_ASSERT(type != kEngineControlEventTypeNull); | |||
CARLA_ASSERT(channel < MAX_MIDI_CHANNELS); | |||
CARLA_ASSERT(value >= 0.0 && value <= 1.0); | |||
CARLA_ASSERT(param < MAX_MIDI_VALUE); | |||
CARLA_SAFE_ASSERT(value >= 0.0 && value <= 1.0); | |||
if (kIsInput) | |||
return; | |||
@@ -267,11 +268,15 @@ public: | |||
return; | |||
if (channel >= MAX_MIDI_CHANNELS) | |||
return; | |||
if (param >= MAX_MIDI_VALUE) | |||
return; | |||
if (type == kEngineControlEventTypeParameter) | |||
{ | |||
CARLA_ASSERT(! MIDI_IS_CONTROL_BANK_SELECT(param)); | |||
} | |||
const double fixedValue = carla_fixValue<double>(0.0, 1.0, value); | |||
uint8_t data[3] = { 0 }; | |||
uint8_t size = 0; | |||
@@ -281,19 +286,19 @@ public: | |||
break; | |||
case kEngineControlEventTypeParameter: | |||
data[0] = MIDI_STATUS_CONTROL_CHANGE + channel; | |||
data[1] = param; | |||
data[2] = value * 127; | |||
data[1] = static_cast<uint8_t>(param); | |||
data[2] = uint8_t(fixedValue * 127.0); | |||
size = 3; | |||
break; | |||
case kEngineControlEventTypeMidiBank: | |||
data[0] = MIDI_STATUS_CONTROL_CHANGE + channel; | |||
data[1] = MIDI_CONTROL_BANK_SELECT; | |||
data[2] = param; | |||
data[2] = static_cast<uint8_t>(param); | |||
size = 3; | |||
break; | |||
case kEngineControlEventTypeMidiProgram: | |||
data[0] = MIDI_STATUS_PROGRAM_CHANGE + channel; | |||
data[1] = param; | |||
data[1] = static_cast<uint8_t>(param); | |||
size = 2; | |||
break; | |||
case kEngineControlEventTypeAllSoundOff: | |||
@@ -467,7 +472,7 @@ public: | |||
return new CarlaEngineJackEventPort(isInput, kProcessMode, kClient, port); | |||
} | |||
qCritical("CarlaEngineJackClient::addPort(%s, \"%s\", %s) - invalid type", EnginePortType2Str(portType), name, bool2str(isInput)); | |||
carla_stderr("CarlaEngineJackClient::addPort(%s, \"%s\", %s) - invalid type", EnginePortType2Str(portType), name, bool2str(isInput)); | |||
return nullptr; | |||
} | |||
@@ -481,21 +486,6 @@ private: | |||
// ------------------------------------------------------------------------------------------------------------------- | |||
// Jack Engine | |||
#if 0 | |||
struct EnginePluginData { | |||
CarlaEngine* const engine; | |||
CarlaPlugin* const plugin; | |||
EnginePluginData(CarlaEngine* const engine_, CarlaPlugin* const plugin_) | |||
: engine(engine_), | |||
plugin(plugin_) {} | |||
EnginePluginData() = delete; | |||
EnginePluginData(EnginePlugin&) = delete; | |||
EnginePluginData(const EnginePlugin&) = delete; | |||
}; | |||
#endif | |||
class CarlaEngineJack : public CarlaEngine | |||
{ | |||
public: | |||
@@ -530,9 +520,7 @@ public: | |||
unsigned int maxClientNameSize() | |||
{ | |||
#ifndef BUILD_BRIDGE | |||
if (fOptions.processMode == PROCESS_MODE_SINGLE_CLIENT || fOptions.processMode == PROCESS_MODE_MULTIPLE_CLIENTS) | |||
#endif | |||
return static_cast<unsigned int>(jackbridge_client_name_size()); | |||
return CarlaEngine::maxClientNameSize(); | |||
@@ -540,9 +528,7 @@ public: | |||
unsigned int maxPortNameSize() | |||
{ | |||
#ifndef BUILD_BRIDGE | |||
if (fOptions.processMode == PROCESS_MODE_SINGLE_CLIENT || fOptions.processMode == PROCESS_MODE_MULTIPLE_CLIENTS) | |||
#endif | |||
return static_cast<unsigned int>(jackbridge_port_name_size()); | |||
return CarlaEngine::maxPortNameSize(); | |||
@@ -563,7 +549,7 @@ public: | |||
#ifndef BUILD_BRIDGE | |||
fClient = jackbridge_client_open(clientName, JackNullOption, nullptr); | |||
if (fClient) | |||
if (fClient != nullptr) | |||
{ | |||
fBufferSize = jackbridge_get_buffer_size(fClient); | |||
fSampleRate = jackbridge_get_sample_rate(fClient); | |||
@@ -587,14 +573,14 @@ public: | |||
if (jackbridge_activate(fClient) == 0) | |||
{ | |||
const char* const clientName = jackbridge_get_client_name(fClient); | |||
const char* const jackClientName = jackbridge_get_client_name(fClient); | |||
CarlaEngine::init(clientName); | |||
return true; | |||
return CarlaEngine::init(jackClientName); | |||
} | |||
else | |||
{ | |||
setLastError("Failed to activate the JACK client"); | |||
jackbridge_client_close(fClient); | |||
fClient = nullptr; | |||
} | |||
} | |||
@@ -603,23 +589,19 @@ public: | |||
return false; | |||
#else | |||
// open temp client to get initial buffer-size and sample-rate values | |||
if (fBufferSize == 0 || fSampleRate == 0.0) | |||
{ | |||
fClient = jackbridge_client_open(clientName, JackNullOption, nullptr); | |||
if (fClient) | |||
// open temp client to get initial buffer-size and sample-rate values | |||
if (jack_client_t* tmpClient = jackbridge_client_open(clientName, JackNullOption, nullptr)) | |||
{ | |||
fBufferSize = jackbridge_get_buffer_size(fClient); | |||
fSampleRate = jackbridge_get_sample_rate(fClient); | |||
fBufferSize = jackbridge_get_buffer_size(tmpClient); | |||
fSampleRate = jackbridge_get_sample_rate(tmpClient); | |||
jackbridge_client_close(fClient); | |||
fClient = nullptr; | |||
jackbridge_client_close(tmpClient); | |||
} | |||
} | |||
CarlaEngine::init(clientName); | |||
return true; | |||
return CarlaEngine::init(clientName); | |||
#endif | |||
} | |||
@@ -709,11 +691,7 @@ public: | |||
} | |||
#endif | |||
#ifdef BUILD_BRIDGE | |||
return new CarlaEngineJackClient(kEngineTypeJack, PROCESS_MODE_MULTIPLE_CLIENTS, client); | |||
#else | |||
return new CarlaEngineJackClient(kEngineTypeJack, fOptions.processMode, client); | |||
#endif | |||
} | |||
// ------------------------------------- | |||
@@ -746,11 +724,9 @@ protected: | |||
void handleJackProcessCallback(const uint32_t nframes) | |||
{ | |||
proccessPendingEvents(); | |||
#ifndef BUILD_BRIDGE | |||
if (kData->curPluginCount == 0) | |||
return; | |||
return proccessPendingEvents(); | |||
#endif | |||
fTransportPos.unique_1 = fTransportPos.unique_2 + 1; // invalidate | |||
@@ -974,17 +950,15 @@ protected: | |||
} | |||
#endif | |||
} | |||
#endif | |||
#endif // ! BUILD_BRIDGE | |||
proccessPendingEvents(); | |||
} | |||
void handleJackLatencyCallback(const jack_latency_callback_mode_t mode) | |||
{ | |||
#ifndef BUILD_BRIDGE | |||
if (fOptions.processMode != PROCESS_MODE_SINGLE_CLIENT) | |||
return; | |||
#endif | |||
for (unsigned int i=0; i < kData->curPluginCount; i++) | |||
{ | |||
@@ -1136,45 +1110,43 @@ private: | |||
// ------------------------------------- | |||
#define handlePtr ((CarlaEngineJack*)arg) | |||
static int carla_jack_srate_callback(jack_nframes_t newSampleRate, void* arg) | |||
{ | |||
if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) | |||
_this_->handleJackSampleRateCallback(newSampleRate); | |||
handlePtr->handleJackSampleRateCallback(newSampleRate); | |||
return 0; | |||
} | |||
static int carla_jack_bufsize_callback(jack_nframes_t newBufferSize, void* arg) | |||
{ | |||
if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) | |||
_this_->handleJackBufferSizeCallback(newBufferSize); | |||
handlePtr->handleJackBufferSizeCallback(newBufferSize); | |||
return 0; | |||
} | |||
static void carla_jack_freewheel_callback(int starting, void* arg) | |||
{ | |||
if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) | |||
_this_->handleJackFreewheelCallback(bool(starting)); | |||
handlePtr->handleJackFreewheelCallback(bool(starting)); | |||
} | |||
static int carla_jack_process_callback(jack_nframes_t nframes, void* arg) | |||
{ | |||
if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) | |||
_this_->handleJackProcessCallback(nframes); | |||
handlePtr->handleJackProcessCallback(nframes); | |||
return 0; | |||
} | |||
static void carla_jack_latency_callback(jack_latency_callback_mode_t mode, void* arg) | |||
{ | |||
if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) | |||
_this_->handleJackLatencyCallback(mode); | |||
handlePtr->handleJackLatencyCallback(mode); | |||
} | |||
static void carla_jack_shutdown_callback(void* arg) | |||
{ | |||
if (CarlaEngineJack* const _this_ = (CarlaEngineJack*)arg) | |||
_this_->handleJackShutdownCallback(); | |||
handlePtr->handleJackShutdownCallback(); | |||
} | |||
#undef handlePtr | |||
// ------------------------------------- | |||
#ifndef BUILD_BRIDGE |
@@ -0,0 +1,547 @@ | |||
/* | |||
* Carla RtAudio Engine | |||
* Copyright (C) 2012-2013 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 | |||
*/ | |||
#ifdef WANT_RTAUDIO | |||
#include "CarlaEngineInternal.hpp" | |||
#include "CarlaBackendUtils.hpp" | |||
#include "CarlaMIDI.h" | |||
#include "RtList.hpp" | |||
#include "RtAudio.h" | |||
#include "RtMidi.h" | |||
CARLA_BACKEND_START_NAMESPACE | |||
#if 0 | |||
} // Fix editor indentation | |||
#endif | |||
// ------------------------------------------------------------------------------------------------------------------- | |||
RtMidi::Api getMatchedAudioMidiAPi(const RtAudio::Api rtApi) | |||
{ | |||
switch (rtApi) | |||
{ | |||
case RtAudio::UNSPECIFIED: | |||
return RtMidi::UNSPECIFIED; | |||
case RtAudio::LINUX_ALSA: | |||
case RtAudio::LINUX_OSS: | |||
case RtAudio::LINUX_PULSE: | |||
return RtMidi::LINUX_ALSA; | |||
case RtAudio::UNIX_JACK: | |||
return RtMidi::UNIX_JACK; | |||
case RtAudio::MACOSX_CORE: | |||
return RtMidi::MACOSX_CORE; | |||
case RtAudio::WINDOWS_ASIO: | |||
case RtAudio::WINDOWS_DS: | |||
return RtMidi::WINDOWS_MM; | |||
case RtAudio::RTAUDIO_DUMMY: | |||
return RtMidi::RTMIDI_DUMMY; | |||
} | |||
return RtMidi::UNSPECIFIED; | |||
} | |||
// ------------------------------------------------------------------------------------------------------------------- | |||
// RtAudio Engine | |||
class CarlaEngineRtAudio : public CarlaEngine | |||
{ | |||
public: | |||
CarlaEngineRtAudio(const RtAudio::Api api) | |||
: CarlaEngine(), | |||
fAudio(api), | |||
fAudioIsInterleaved(false), | |||
fAudioIsReady(false), | |||
fAudioInBuf1(nullptr), | |||
fAudioInBuf2(nullptr), | |||
fAudioOutBuf1(nullptr), | |||
fAudioOutBuf2(nullptr), | |||
fMidiIn(getMatchedAudioMidiAPi(api)), | |||
fMidiOut(getMatchedAudioMidiAPi(api)) | |||
{ | |||
carla_debug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api); | |||
// just to make sure | |||
fOptions.forceStereo = true; | |||
fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK; | |||
} | |||
~CarlaEngineRtAudio() | |||
{ | |||
carla_debug("CarlaEngineRtAudio::~CarlaEngineRtAudio()"); | |||
CARLA_ASSERT(fAudioInBuf1 == nullptr); | |||
CARLA_ASSERT(fAudioInBuf2 == nullptr); | |||
CARLA_ASSERT(fAudioOutBuf1 == nullptr); | |||
CARLA_ASSERT(fAudioOutBuf2 == nullptr); | |||
} | |||
// ------------------------------------- | |||
bool init(const char* const clientName) | |||
{ | |||
carla_debug("CarlaEngineRtAudio::init(\"%s\")", clientName); | |||
CARLA_ASSERT(! fAudioIsReady); | |||
CARLA_ASSERT(fAudioInBuf1 == nullptr); | |||
CARLA_ASSERT(fAudioInBuf2 == nullptr); | |||
CARLA_ASSERT(fAudioOutBuf1 == nullptr); | |||
CARLA_ASSERT(fAudioOutBuf2 == nullptr); | |||
if (fAudio.getDeviceCount() == 0) | |||
{ | |||
setLastError("No audio devices available for this driver"); | |||
return false; | |||
} | |||
fBufferSize = fOptions.preferredBufferSize; | |||
// Audio | |||
{ | |||
RtAudio::StreamParameters iParams, oParams; | |||
iParams.deviceId = fAudio.getDefaultInputDevice(); | |||
oParams.deviceId = fAudio.getDefaultOutputDevice(); | |||
iParams.nChannels = 2; | |||
oParams.nChannels = 2; | |||
RtAudio::StreamOptions rtOptions; | |||
rtOptions.flags = RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_HOG_DEVICE | RTAUDIO_SCHEDULE_REALTIME; | |||
rtOptions.numberOfBuffers = 2; | |||
rtOptions.streamName = clientName; | |||
rtOptions.priority = 85; | |||
if (fAudio.getCurrentApi() != RtAudio::LINUX_PULSE) | |||
{ | |||
rtOptions.flags |= RTAUDIO_NONINTERLEAVED; | |||
fAudioIsInterleaved = false; | |||
if (fAudio.getCurrentApi() == RtAudio::LINUX_ALSA) | |||
rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT; | |||
} | |||
else | |||
fAudioIsInterleaved = true; | |||
try { | |||
fAudio.openStream(&oParams, &iParams, RTAUDIO_FLOAT32, fOptions.preferredSampleRate, &fBufferSize, carla_rtaudio_process_callback, this, &rtOptions); | |||
} | |||
catch (RtError& e) | |||
{ | |||
setLastError(e.what()); | |||
return false; | |||
} | |||
try { | |||
fAudio.startStream(); | |||
} | |||
catch (RtError& e) | |||
{ | |||
setLastError(e.what()); | |||
return false; | |||
} | |||
fAudioInBuf1 = new float[fBufferSize]; | |||
fAudioInBuf2 = new float[fBufferSize]; | |||
fAudioOutBuf1 = new float[fBufferSize]; | |||
fAudioOutBuf2 = new float[fBufferSize]; | |||
fSampleRate = fAudio.getStreamSampleRate(); | |||
} | |||
// MIDI | |||
{ | |||
fMidiIn.setCallback(carla_rtmidi_callback, this); | |||
fMidiIn.openVirtualPort("events-in"); | |||
fMidiOut.openVirtualPort("events-out"); | |||
} | |||
fAudioIsReady = true; | |||
return CarlaEngine::init(clientName); | |||
} | |||
bool close() | |||
{ | |||
carla_debug("CarlaEngineRtAudio::close()"); | |||
CARLA_ASSERT(fAudioIsReady); | |||
CARLA_ASSERT(fAudioInBuf1 != nullptr); | |||
CARLA_ASSERT(fAudioInBuf2 != nullptr); | |||
CARLA_ASSERT(fAudioOutBuf1 != nullptr); | |||
CARLA_ASSERT(fAudioOutBuf2 != nullptr); | |||
CarlaEngine::close(); | |||
fAudioIsReady = false; | |||
if (fAudio.isStreamRunning()) | |||
fAudio.stopStream(); | |||
if (fAudio.isStreamOpen()) | |||
fAudio.closeStream(); | |||
fMidiIn.cancelCallback(); | |||
fMidiIn.closePort(); | |||
fMidiOut.closePort(); | |||
if (fAudioInBuf1 != nullptr) | |||
{ | |||
delete[] fAudioInBuf1; | |||
fAudioInBuf1 = nullptr; | |||
} | |||
if (fAudioInBuf2 != nullptr) | |||
{ | |||
delete[] fAudioInBuf2; | |||
fAudioInBuf2 = nullptr; | |||
} | |||
if (fAudioOutBuf1 != nullptr) | |||
{ | |||
delete[] fAudioOutBuf1; | |||
fAudioOutBuf1 = nullptr; | |||
} | |||
if (fAudioOutBuf2 != nullptr) | |||
{ | |||
delete[] fAudioOutBuf2; | |||
fAudioOutBuf2 = nullptr; | |||
} | |||
fMidiInEvents.clear(); | |||
fMidiOutEvents.clear(); | |||
return true; | |||
} | |||
bool isRunning() const | |||
{ | |||
return fAudio.isStreamRunning(); | |||
} | |||
bool isOffline() const | |||
{ | |||
return false; | |||
} | |||
EngineType type() const | |||
{ | |||
return kEngineTypeRtAudio; | |||
} | |||
// ------------------------------------- | |||
protected: | |||
void handleAudioProcessCallback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status) | |||
{ | |||
// get buffers from RtAudio | |||
float* insPtr = (float*)inputBuffer; | |||
float* outsPtr = (float*)outputBuffer; | |||
// assert buffers | |||
CARLA_ASSERT(insPtr != nullptr); | |||
CARLA_ASSERT(outsPtr != nullptr); | |||
if (currentPluginCount() == 0 || ! fAudioIsReady) | |||
{ | |||
carla_zeroFloat(outsPtr, sizeof(float)*nframes*2); | |||
return; | |||
} | |||
// initialize audio input | |||
if (fAudioIsInterleaved) | |||
{ | |||
for (unsigned int i=0; i < nframes*2; i++) | |||
{ | |||
if (i % 2) | |||
fAudioInBuf1[i/2] = insPtr[i]; | |||
else | |||
fAudioInBuf2[i/2] = insPtr[i]; | |||
} | |||
} | |||
else | |||
{ | |||
std::memcpy(fAudioInBuf1, insPtr, sizeof(float)*nframes); | |||
std::memcpy(fAudioInBuf2, insPtr+nframes, sizeof(float)*nframes); | |||
//for (unsigned int i=0; i < nframes; i++) | |||
// fAudioInBuf1[i] = insPtr[i]; | |||
//for (unsigned int i=0, j=nframes; i < nframes; i++, j++) | |||
// fAudioInBuf2[i] = insPtr[j]; | |||
} | |||
// initialize audio output | |||
carla_zeroFloat(fAudioOutBuf1, fBufferSize); | |||
carla_zeroFloat(fAudioOutBuf2, fBufferSize); | |||
// initialize events input | |||
//memset(rackEventsIn, 0, sizeof(EngineEvent)*MAX_EVENTS); | |||
{ | |||
// TODO | |||
} | |||
// create audio buffers | |||
float* inBuf[2] = { fAudioInBuf1, fAudioInBuf2 }; | |||
float* outBuf[2] = { fAudioOutBuf1, fAudioOutBuf2 }; | |||
processRack(inBuf, outBuf, nframes); | |||
// output audio | |||
if (fAudioIsInterleaved) | |||
{ | |||
for (unsigned int i=0; i < nframes*2; i++) | |||
{ | |||
if (i % 2) | |||
outsPtr[i] = fAudioOutBuf1[i/2]; | |||
else | |||
outsPtr[i] = fAudioOutBuf2[i/2]; | |||
} | |||
} | |||
else | |||
{ | |||
std::memcpy(outsPtr, fAudioOutBuf1, sizeof(float)*nframes); | |||
std::memcpy(outsPtr+nframes, fAudioOutBuf2, sizeof(float)*nframes); | |||
//for (unsigned int i=0; i < nframes; i++) | |||
// outsPtr[i] = fAudioOutBuf1[i]; | |||
//for (unsigned int i=0, j=nframes; i < nframes; i++, j++) | |||
// outsPtr[j] = fAudioOutBuf2[i]; | |||
} | |||
// output events | |||
{ | |||
// TODO | |||
//fMidiOut.sendMessage(); | |||
} | |||
(void)streamTime; | |||
(void)status; | |||
} | |||
void handleMidiCallback(const double timeStamp, std::vector<unsigned char>* const message) | |||
{ | |||
const size_t messageSize = message->size(); | |||
if (messageSize == 0 || messageSize > 3) | |||
return; | |||
RtMidiEvent midiEvent; | |||
midiEvent.time = timeStamp; | |||
if (messageSize == 1) | |||
{ | |||
midiEvent.data[0] = message->at(0); | |||
midiEvent.data[1] = 0; | |||
midiEvent.data[2] = 0; | |||
} | |||
else if (messageSize == 2) | |||
{ | |||
midiEvent.data[0] = message->at(0); | |||
midiEvent.data[1] = message->at(1); | |||
midiEvent.data[2] = 0; | |||
} | |||
else | |||
{ | |||
midiEvent.data[0] = message->at(0); | |||
midiEvent.data[1] = message->at(1); | |||
midiEvent.data[2] = message->at(2); | |||
} | |||
fMidiInEvents.append(midiEvent); | |||
} | |||
// ------------------------------------- | |||
private: | |||
RtAudio fAudio; | |||
bool fAudioIsInterleaved; | |||
bool fAudioIsReady; | |||
float* fAudioInBuf1; | |||
float* fAudioInBuf2; | |||
float* fAudioOutBuf1; | |||
float* fAudioOutBuf2; | |||
RtMidiIn fMidiIn; | |||
RtMidiOut fMidiOut; | |||
struct RtMidiEvent { | |||
double time; | |||
unsigned char data[3]; | |||
}; | |||
struct RtMidiEvents { | |||
CarlaMutex mutex; | |||
RtList<RtMidiEvent>::Pool dataPool; | |||
RtList<RtMidiEvent> data; | |||
RtList<RtMidiEvent> dataPending; | |||
RtMidiEvents() | |||
: 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 trySplice() | |||
{ | |||
if (mutex.tryLock()) | |||
{ | |||
dataPending.splice(data, true); | |||
mutex.unlock(); | |||
} | |||
} | |||
}; | |||
RtMidiEvents fMidiInEvents; | |||
RtMidiEvents fMidiOutEvents; | |||
#define handlePtr ((CarlaEngineRtAudio*)userData) | |||
static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status, void* userData) | |||
{ | |||
handlePtr->handleAudioProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status); | |||
return 0; | |||
} | |||
static void carla_rtmidi_callback(double timeStamp, std::vector<unsigned char>* message, void* userData) | |||
{ | |||
handlePtr->handleMidiCallback(timeStamp, message); | |||
} | |||
#undef handlePtr | |||
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio) | |||
}; | |||
// ----------------------------------------- | |||
static std::vector<RtAudio::Api> sRtAudioApis; | |||
static void initRtApis() | |||
{ | |||
static bool initiated = false; | |||
if (! initiated) | |||
{ | |||
initiated = true; | |||
RtAudio::getCompiledApi(sRtAudioApis); | |||
} | |||
} | |||
CarlaEngine* CarlaEngine::newRtAudio(RtAudioApi api) | |||
{ | |||
RtAudio::Api rtApi = RtAudio::UNSPECIFIED; | |||
switch (api) | |||
{ | |||
case RTAUDIO_DUMMY: | |||
rtApi = RtAudio::RTAUDIO_DUMMY; | |||
break; | |||
case RTAUDIO_LINUX_ALSA: | |||
rtApi = RtAudio::LINUX_ALSA; | |||
break; | |||
case RTAUDIO_LINUX_PULSE: | |||
rtApi = RtAudio::LINUX_PULSE; | |||
break; | |||
case RTAUDIO_LINUX_OSS: | |||
rtApi = RtAudio::LINUX_OSS; | |||
break; | |||
case RTAUDIO_UNIX_JACK: | |||
rtApi = RtAudio::UNIX_JACK; | |||
break; | |||
case RTAUDIO_MACOSX_CORE: | |||
rtApi = RtAudio::MACOSX_CORE; | |||
break; | |||
case RTAUDIO_WINDOWS_ASIO: | |||
rtApi = RtAudio::WINDOWS_ASIO; | |||
break; | |||
case RTAUDIO_WINDOWS_DS: | |||
rtApi = RtAudio::WINDOWS_DS; | |||
break; | |||
} | |||
return new CarlaEngineRtAudio(rtApi); | |||
} | |||
size_t CarlaEngine::getRtAudioApiCount() | |||
{ | |||
initRtApis(); | |||
return sRtAudioApis.size(); | |||
} | |||
const char* CarlaEngine::getRtAudioApiName(const unsigned int index) | |||
{ | |||
initRtApis(); | |||
if (index < sRtAudioApis.size()) | |||
{ | |||
const RtAudio::Api& api(sRtAudioApis[index]); | |||
switch (api) | |||
{ | |||
case RtAudio::UNSPECIFIED: | |||
return "Unspecified"; | |||
case RtAudio::LINUX_ALSA: | |||
return "ALSA"; | |||
case RtAudio::LINUX_PULSE: | |||
return "PulseAudio"; | |||
case RtAudio::LINUX_OSS: | |||
return "OSS"; | |||
case RtAudio::UNIX_JACK: | |||
return "JACK (RtAudio)"; | |||
case RtAudio::MACOSX_CORE: | |||
return "CoreAudio"; | |||
case RtAudio::WINDOWS_ASIO: | |||
return "ASIO"; | |||
case RtAudio::WINDOWS_DS: | |||
return "DirectSound"; | |||
case RtAudio::RTAUDIO_DUMMY: | |||
return "Dummy"; | |||
} | |||
} | |||
return nullptr; | |||
} | |||
// ----------------------------------------- | |||
CARLA_BACKEND_END_NAMESPACE | |||
#endif // CARLA_ENGINE_RTAUDIO |
@@ -44,10 +44,10 @@ endif | |||
OBJS = \ | |||
CarlaEngine.cpp.o \ | |||
CarlaEngineJack.cpp.o \ | |||
CarlaEngineRtAudio.cpp.o \ | |||
CarlaEngineOsc.cpp.o \ | |||
CarlaEngineThread.cpp.o \ | |||
jack.cpp.o \ | |||
rtaudio.cpp.o | |||
CarlaEngineThread.cpp.o | |||
ifeq ($(CARLA_RTAUDIO_SUPPORT),true) | |||
OBJS += \ | |||
@@ -1,421 +0,0 @@ | |||
/* | |||
* Carla RtAudio Engine | |||
* Copyright (C) 2012-2013 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 | |||
*/ | |||
#ifdef WANT_RTAUDIO | |||
#include "CarlaEngineInternal.hpp" | |||
#include "CarlaBackendUtils.hpp" | |||
#include "CarlaMIDI.h" | |||
#include "RtAudio.h" | |||
#include "RtMidi.h" | |||
CARLA_BACKEND_START_NAMESPACE | |||
#if 0 | |||
} // Fix editor indentation | |||
#endif | |||
// ------------------------------------------------------------------------------------------------------------------- | |||
// RtAudio Engine | |||
class CarlaEngineRtAudio : public CarlaEngine | |||
{ | |||
public: | |||
CarlaEngineRtAudio(RtAudio::Api api) | |||
: CarlaEngine(), | |||
audio(api) | |||
{ | |||
qDebug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api); | |||
evIn = nullptr; | |||
evOut = nullptr; | |||
m_audioInterleaved = false; | |||
m_inBuf1 = nullptr; | |||
m_inBuf2 = nullptr; | |||
m_outBuf1 = nullptr; | |||
m_outBuf2 = nullptr; | |||
// just to make sure | |||
fOptions.forceStereo = true; | |||
fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK; | |||
} | |||
~CarlaEngineRtAudio() | |||
{ | |||
qDebug("CarlaEngineRtAudio::~CarlaEngineRtAudio()"); | |||
} | |||
// ------------------------------------- | |||
bool init(const char* const clientName) | |||
{ | |||
qDebug("CarlaEngineRtAudio::init(\"%s\")", clientName); | |||
if (audio.getDeviceCount() < 1) | |||
{ | |||
setLastError("No audio devices available for this driver"); | |||
return false; | |||
} | |||
fBufferSize = fOptions.preferredBufferSize; | |||
fSampleRate = fOptions.preferredSampleRate; | |||
RtAudio::StreamParameters iParams, oParams; | |||
iParams.deviceId = audio.getDefaultInputDevice(); | |||
oParams.deviceId = audio.getDefaultOutputDevice(); | |||
iParams.nChannels = 2; | |||
oParams.nChannels = 2; | |||
RtAudio::StreamOptions rtOptions; | |||
rtOptions.flags = RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_HOG_DEVICE | RTAUDIO_SCHEDULE_REALTIME; | |||
rtOptions.numberOfBuffers = 2; | |||
rtOptions.streamName = clientName; | |||
rtOptions.priority = 85; | |||
if (audio.getCurrentApi() != RtAudio::LINUX_PULSE) | |||
{ | |||
rtOptions.flags |= RTAUDIO_NONINTERLEAVED; | |||
m_audioInterleaved = false; | |||
if (audio.getCurrentApi() == RtAudio::LINUX_ALSA) | |||
rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT; | |||
} | |||
else | |||
m_audioInterleaved = true; | |||
try { | |||
audio.openStream(&oParams, &iParams, RTAUDIO_FLOAT32, fSampleRate, &fBufferSize, carla_rtaudio_process_callback, this, &rtOptions); | |||
} | |||
catch (RtError& e) | |||
{ | |||
setLastError(e.what()); | |||
return false; | |||
} | |||
try { | |||
audio.startStream(); | |||
} | |||
catch (RtError& e) | |||
{ | |||
setLastError(e.what()); | |||
return false; | |||
} | |||
fSampleRate = audio.getStreamSampleRate(); | |||
m_inBuf1 = new float[fBufferSize]; | |||
m_inBuf2 = new float[fBufferSize]; | |||
m_outBuf1 = new float[fBufferSize]; | |||
m_outBuf2 = new float[fBufferSize]; | |||
//midiIn = new MidiInAlsa(clientName, 512); | |||
//midiIn->openVirtualPort("control-in"); | |||
//midiIn->openVirtualPort("midi-in"); | |||
//midiOut = new MidiOutAlsa(clientName); | |||
//midiOut->openVirtualPort("control-out"); | |||
//midiOut->openVirtualPort("midi-out"); | |||
fName = clientName; | |||
fName.toBasic(); | |||
CarlaEngine::init(fName); | |||
return true; | |||
} | |||
bool close() | |||
{ | |||
qDebug("CarlaEngineRtAudio::close()"); | |||
CarlaEngine::close(); | |||
if (audio.isStreamRunning()) | |||
audio.stopStream(); | |||
if (audio.isStreamOpen()) | |||
audio.closeStream(); | |||
#if 0 | |||
if (midiIn) | |||
{ | |||
midiIn->cancelCallback(); | |||
midiIn->closePort(); | |||
delete midiIn; | |||
midiIn = nullptr; | |||
} | |||
if (midiOut) | |||
{ | |||
midiOut->closePort(); | |||
delete midiOut; | |||
midiOut = nullptr; | |||
} | |||
#endif | |||
if (m_inBuf1) | |||
{ | |||
delete[] m_inBuf1; | |||
m_inBuf1 = nullptr; | |||
} | |||
if (m_inBuf2) | |||
{ | |||
delete[] m_inBuf2; | |||
m_inBuf2 = nullptr; | |||
} | |||
if (m_outBuf1) | |||
{ | |||
delete[] m_outBuf1; | |||
m_outBuf1 = nullptr; | |||
} | |||
if (m_outBuf2) | |||
{ | |||
delete[] m_outBuf2; | |||
m_outBuf2 = nullptr; | |||
} | |||
return true; | |||
} | |||
bool isRunning() const | |||
{ | |||
return audio.isStreamRunning(); | |||
} | |||
bool isOffline() const | |||
{ | |||
return false; | |||
} | |||
EngineType type() const | |||
{ | |||
return kEngineTypeRtAudio; | |||
} | |||
// ------------------------------------- | |||
protected: | |||
void handleProcessCallback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status) | |||
{ | |||
if (maxPluginNumber() == 0) | |||
return; | |||
// get buffers from RtAudio | |||
float* insPtr = (float*)inputBuffer; | |||
float* outsPtr = (float*)outputBuffer; | |||
// assert buffers | |||
CARLA_ASSERT(insPtr); | |||
CARLA_ASSERT(outsPtr); | |||
// initialize audio input | |||
if (m_audioInterleaved) | |||
{ | |||
for (unsigned int i=0; i < nframes*2; i++) | |||
{ | |||
if (i % 2) | |||
m_inBuf2[i/2] = insPtr[i]; | |||
else | |||
m_inBuf1[i/2] = insPtr[i]; | |||
} | |||
} | |||
else | |||
{ | |||
for (unsigned int i=0; i < nframes; i++) | |||
m_inBuf1[i] = insPtr[i]; | |||
for (unsigned int i=0, j=nframes; i < nframes; i++, j++) | |||
m_inBuf2[i] = insPtr[j]; | |||
} | |||
// create audio buffers | |||
float* inBuf[2] = { m_inBuf1, m_inBuf2 }; | |||
float* outBuf[2] = { m_outBuf1, m_outBuf2 }; | |||
// initialize events input | |||
//memset(rackEventsIn, 0, sizeof(EngineEvent)*MAX_EVENTS); | |||
{ | |||
// TODO | |||
} | |||
processRack(inBuf, outBuf, nframes); | |||
// output audio | |||
if (m_audioInterleaved) | |||
{ | |||
for (unsigned int i=0; i < nframes*2; i++) | |||
{ | |||
if (i % 2) | |||
outsPtr[i] = m_outBuf2[i/2]; | |||
else | |||
outsPtr[i] = m_outBuf1[i/2]; | |||
} | |||
} | |||
else | |||
{ | |||
for (unsigned int i=0; i < nframes; i++) | |||
outsPtr[i] = m_outBuf1[i]; | |||
for (unsigned int i=0, j=nframes; i < nframes; i++, j++) | |||
outsPtr[j] = m_outBuf2[i]; | |||
} | |||
// output control | |||
{ | |||
// TODO | |||
} | |||
// output midi | |||
{ | |||
// TODO | |||
} | |||
Q_UNUSED(streamTime); | |||
Q_UNUSED(status); | |||
} | |||
// ------------------------------------- | |||
private: | |||
RtAudio audio; | |||
ScopedPointer<MidiInApi> evIn; | |||
ScopedPointer<MidiOutApi> evOut; | |||
bool m_audioInterleaved; | |||
float* m_inBuf1; | |||
float* m_inBuf2; | |||
float* m_outBuf1; | |||
float* m_outBuf2; | |||
static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status, void* userData) | |||
{ | |||
CarlaEngineRtAudio* const _this_ = (CarlaEngineRtAudio*)userData; | |||
_this_->handleProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status); | |||
return 0; | |||
} | |||
CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio) | |||
}; | |||
// ----------------------------------------- | |||
static std::vector<RtAudio::Api> rtApis; | |||
static void initRtApis() | |||
{ | |||
static bool initiated = false; | |||
if (! initiated) | |||
{ | |||
initiated = true; | |||
RtAudio::getCompiledApi(rtApis); | |||
} | |||
} | |||
CarlaEngine* CarlaEngine::newRtAudio(RtAudioApi api) | |||
{ | |||
RtAudio::Api rtApi = RtAudio::UNSPECIFIED; | |||
switch (api) | |||
{ | |||
case RTAUDIO_DUMMY: | |||
rtApi = RtAudio::RTAUDIO_DUMMY; | |||
break; | |||
case RTAUDIO_LINUX_ALSA: | |||
rtApi = RtAudio::LINUX_ALSA; | |||
break; | |||
case RTAUDIO_LINUX_PULSE: | |||
rtApi = RtAudio::LINUX_PULSE; | |||
break; | |||
case RTAUDIO_LINUX_OSS: | |||
rtApi = RtAudio::LINUX_OSS; | |||
break; | |||
case RTAUDIO_UNIX_JACK: | |||
rtApi = RtAudio::UNIX_JACK; | |||
break; | |||
case RTAUDIO_MACOSX_CORE: | |||
rtApi = RtAudio::MACOSX_CORE; | |||
break; | |||
case RTAUDIO_WINDOWS_ASIO: | |||
rtApi = RtAudio::WINDOWS_ASIO; | |||
break; | |||
case RTAUDIO_WINDOWS_DS: | |||
rtApi = RtAudio::WINDOWS_DS; | |||
break; | |||
} | |||
return new CarlaEngineRtAudio(rtApi); | |||
} | |||
unsigned int CarlaEngine::getRtAudioApiCount() | |||
{ | |||
initRtApis(); | |||
return rtApis.size(); | |||
} | |||
const char* CarlaEngine::getRtAudioApiName(unsigned int index) | |||
{ | |||
initRtApis(); | |||
if (index < rtApis.size()) | |||
{ | |||
const RtAudio::Api& api(rtApis[index]); | |||
switch (api) | |||
{ | |||
case RtAudio::UNSPECIFIED: | |||
return "Unspecified"; | |||
case RtAudio::LINUX_ALSA: | |||
return "ALSA"; | |||
case RtAudio::LINUX_PULSE: | |||
return "PulseAudio"; | |||
case RtAudio::LINUX_OSS: | |||
return "OSS"; | |||
case RtAudio::UNIX_JACK: | |||
return "JACK (RtAudio)"; | |||
case RtAudio::MACOSX_CORE: | |||
return "CoreAudio"; | |||
case RtAudio::WINDOWS_ASIO: | |||
return "ASIO"; | |||
case RtAudio::WINDOWS_DS: | |||
return "DirectSound"; | |||
case RtAudio::RTAUDIO_DUMMY: | |||
return "Dummy"; | |||
} | |||
} | |||
return nullptr; | |||
} | |||
// ----------------------------------------- | |||
CARLA_BACKEND_END_NAMESPACE | |||
#ifdef QTCREATOR_TEST | |||
int main() | |||
{ | |||
return 0; | |||
} | |||
#endif | |||
#endif // CARLA_ENGINE_RTAUDIO |
@@ -443,6 +443,7 @@ struct CarlaPluginProtectedData { | |||
void appendRT(const PluginPostRtEvent& event) | |||
{ | |||
// FIXME!! need lock? | |||
dataPendingRT.append(event); | |||
} | |||
@@ -16,7 +16,8 @@ | |||
*/ | |||
// still need qt classes check | |||
#include "engine/CarlaEngineThread.cpp" | |||
//#include "CarlaPlugin.hpp" | |||
#include "engine/RtAudio.cpp" | |||
#if 0 | |||
#include "CarlaDefines.hpp" | |||
@@ -8,9 +8,9 @@ include ../Makefile.mk | |||
# -------------------------------------------------------------- | |||
BUILD_CXX_FLAGS += -I../backend -I../includes -I../utils -Wall -Wextra | |||
BUILD_CXX_FLAGS += -I../backend -I../includes -I../libs -I../utils -Wall -Wextra | |||
BUILD_CXX_FLAGS += -DWANT_JACK -DWANT_RTAUDIO | |||
BUILD_CXX_FLAGS += -isystem /usr/include/qt4 | |||
BUILD_CXX_FLAGS += -isystem /usr/include/qt4 -isystem ../backend/engine/rtaudio-4.0.11 | |||
# BUILD_CXX_FLAGS += -D_FORTIFY_SOURCE=2 -fstack-protector | |||
# BUILD_CXX_FLAGS += -I/opt/mingw32/include | |||
@@ -237,7 +237,7 @@ const T& carla_fixValue(const T& min, const T& max, const T& value) | |||
template<typename T> | |||
static inline | |||
void carla_fill(T* data, const unsigned int size, const T v) | |||
void carla_fill(T* data, const size_t size, const T v) | |||
{ | |||
CARLA_ASSERT(data != nullptr); | |||
CARLA_ASSERT(size > 0); | |||
@@ -250,13 +250,13 @@ void carla_fill(T* data, const unsigned int size, const T v) | |||
} | |||
static inline | |||
void carla_zeroDouble(double* data, const unsigned size) | |||
void carla_zeroDouble(double* data, const size_t size) | |||
{ | |||
carla_fill<double>(data, size, 0.0); | |||
} | |||
static inline | |||
void carla_zeroFloat(float* data, const unsigned size) | |||
void carla_zeroFloat(float* data, const size_t size) | |||
{ | |||
carla_fill<float>(data, size, 0.0f); | |||
} | |||