|
- /*
- * 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:
- #if defined(CARLA_OS_WIN)
- return RtMidi::WINDOWS_MM;
- #elif defined(CARLA_OS_MAC)
- return RtMidi::MACOSX_CORE;
- #elif defined(CARLA_OS_LINUX)
- return RtMidi::LINUX_ALSA;
- #else
- return RtMidi::UNIX_JACK;
- #endif
-
- 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), "Carla"),
- fMidiOut(getMatchedAudioMidiAPi(api), "Carla"),
- fLastConnectionId(0)
- {
- carla_debug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api);
-
- // just to make sure
- fOptions.forceStereo = true;
- fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK;
- }
-
- ~CarlaEngineRtAudio() override
- {
- carla_debug("CarlaEngineRtAudio::~CarlaEngineRtAudio()");
- CARLA_ASSERT(fAudioInBuf1 == nullptr);
- CARLA_ASSERT(fAudioInBuf2 == nullptr);
- CARLA_ASSERT(fAudioOutBuf1 == nullptr);
- CARLA_ASSERT(fAudioOutBuf2 == nullptr);
-
- fUsedPortNames.clear();
- fUsedConnections.clear();
- }
-
- // -------------------------------------
-
- bool init(const char* const clientName) override
- {
- 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);
- CARLA_ASSERT(clientName != 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)
- {
- carla_stderr2("RtAudio::openStream() failed");
- if (e.getType() == RtError::SYSTEM_ERROR)
- setLastError("Stream cannot be opened with the specified parameters");
- else if (e.getType() == RtError::INVALID_USE)
- setLastError("Invalid device ID");
- else
- setLastError("Unknown error");
- return false;
- }
-
- try {
- fAudio.startStream();
- }
- catch (RtError& e)
- {
- carla_stderr2("RtAudio::startStream() failed");
- setLastError(e.what());
- fAudio.closeStream();
- 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);
- fUsedMidiPortIn.clear();
- fUsedMidiPortOut.clear();
- }
-
- fAudioIsReady = true;
- patchbayRefresh();
-
- return CarlaEngine::init(clientName);
- }
-
- bool close() override
- {
- 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())
- {
- try {
- fAudio.stopStream();
- }
- catch (...) {}
- }
-
- if (fAudio.isStreamOpen())
- {
- try {
- fAudio.closeStream();
- }
- catch (...) {}
- }
-
- fMidiIn.cancelCallback();
- disconnectMidiPort(true, false);
- disconnectMidiPort(false, false);
-
- 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 override
- {
- return fAudio.isStreamRunning();
- }
-
- bool isOffline() const override
- {
- return false;
- }
-
- EngineType type() const override
- {
- return kEngineTypeRtAudio;
- }
-
- // -------------------------------------------------------------------
- // Patchbay
-
- bool patchbayConnect(int portA, int portB) override
- {
- CARLA_ASSERT(fAudioIsReady);
- CARLA_ASSERT(portA > PATCHBAY_PORT_MAX);
- CARLA_ASSERT(portB > PATCHBAY_PORT_MAX);
-
- if (! fAudioIsReady)
- {
- setLastError("Engine not ready");
- return false;
- }
- if (portA < PATCHBAY_PORT_MAX)
- {
- setLastError("Invalid output port");
- return false;
- }
- if (portB < PATCHBAY_PORT_MAX)
- {
- setLastError("Invalid input port");
- return false;
- }
-
- // only allow connections between Carla and other ports
- if (portA < 0 && portB < 0)
- {
- setLastError("Invalid connection");
- return false;
- }
- if (portA >= 0 && portB >= 0)
- {
- setLastError("Invalid connection");
- return false;
- }
-
- const int carlaPort = (portA < 0) ? portA : portB;
- const int targetPort = (carlaPort == portA) ? portB : portA;
- bool makeConnection = false;
-
- switch (carlaPort)
- {
- case PATCHBAY_PORT_AUDIO_IN1:
- case PATCHBAY_PORT_AUDIO_IN2:
- case PATCHBAY_PORT_AUDIO_OUT1:
- case PATCHBAY_PORT_AUDIO_OUT2:
- break;
-
- case PATCHBAY_PORT_MIDI_IN:
- CARLA_ASSERT(targetPort >= PATCHBAY_GROUP_MIDI_IN*1000);
- CARLA_ASSERT(targetPort <= PATCHBAY_GROUP_MIDI_IN*1000 + 999);
- disconnectMidiPort(true, true);
-
- for (unsigned int i=0, count=fMidiIn.getPortCount(); i < count; ++i)
- {
- const char* const portName(fMidiIn.getPortName(i).c_str());
-
- if (getPatchbayPortId(portName) == targetPort)
- {
- fUsedMidiPortIn = portName;
- fMidiIn.openPort(i, "midi-in");
- makeConnection = true;
- break;
- }
- }
- break;
-
- case PATCHBAY_PORT_MIDI_OUT:
- CARLA_ASSERT(targetPort >= PATCHBAY_GROUP_MIDI_OUT*1000);
- CARLA_ASSERT(targetPort <= PATCHBAY_GROUP_MIDI_OUT*1000 + 999);
- disconnectMidiPort(false, true);
-
- for (unsigned int i=0, count=fMidiOut.getPortCount(); i < count; ++i)
- {
- const char* const portName(fMidiOut.getPortName(i).c_str());
-
- if (getPatchbayPortId(portName) == targetPort)
- {
- fUsedMidiPortOut = portName;
- fMidiOut.openPort(i, "midi-out");
- makeConnection = true;
- break;
- }
- }
- break;
- }
-
- if (makeConnection)
- {
- setLastError("Invalid conenction");
- return false;
- }
-
- ConnectionToId connectionToId;
- connectionToId.id = fLastConnectionId;
- connectionToId.portOut = portA;
- connectionToId.portIn = portB;
-
- fUsedConnections.append(connectionToId);
- callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, portA, portB, nullptr);
- fLastConnectionId++;
-
- return true;
- }
-
- bool patchbayDisconnect(int connectionId) override
- {
- CARLA_ASSERT(fAudioIsReady);
-
- if (! fAudioIsReady)
- {
- setLastError("Engine not ready");
- return false;
- }
-
- for (int i=0, count=fUsedConnections.count(); i < count; ++i)
- {
- const ConnectionToId& connection(fUsedConnections.at(i));
-
- if (connection.id == connectionId)
- {
- const int targetPort = (connection.portOut >= 0) ? connection.portOut : connection.portIn;
-
- if (targetPort >= PATCHBAY_GROUP_MIDI_OUT*1000)
- {
- fMidiOut.closePort();
- fUsedMidiPortOut.clear();
- }
- else if (targetPort >= PATCHBAY_GROUP_MIDI_IN*1000)
- {
- fMidiIn.closePort();
- fUsedMidiPortIn.clear();
- }
-
- callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, connection.id, 0, 0.0f, nullptr);
- fUsedConnections.takeAt(i);
- }
- }
-
- return true;
- }
-
- void patchbayRefresh() override
- {
- CARLA_ASSERT(fAudioIsReady);
-
- if (! fAudioIsReady)
- return;
-
- fLastConnectionId = 0;
- fUsedPortNames.clear();
- fUsedConnections.clear();
-
- // Main
- callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, PATCHBAY_GROUP_CARLA, 0, 0.0f, "Carla");
- {
- callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_IN1, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_INPUT, "audio-in1");
- callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_IN2, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_INPUT, "audio-in2");
- callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_OUT1, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_OUTPUT, "audio-out1");
- callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_AUDIO_OUT2, PATCHBAY_PORT_IS_AUDIO|PATCHBAY_PORT_IS_OUTPUT, "audio-out2");
- callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_MIDI_IN, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_INPUT, "midi-in");
- callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_CARLA, PATCHBAY_PORT_MIDI_OUT, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_OUTPUT, "midi-out");
- }
-
- // Audio In
- {
- // TODO
- }
-
- // Audio Out
- {
- // TODO
- }
-
- // MIDI In
- callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, PATCHBAY_GROUP_MIDI_IN, 0, 0.0f, "Readable MIDI ports");
- {
- const unsigned int portCount = fMidiIn.getPortCount();
-
- for (unsigned int i=0; i < portCount; ++i)
- {
- PortNameToId portNameToId;
- portNameToId.portId = PATCHBAY_GROUP_MIDI_IN*1000 + i;
- portNameToId.name = fMidiIn.getPortName(i).c_str();
- fUsedPortNames.append(portNameToId);
-
- const char* const portName(portNameToId.name.toUtf8().constData());
- callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_MIDI_IN, portNameToId.portId, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_OUTPUT, portName);
- }
- }
-
- // MIDI Out
- callback(CALLBACK_PATCHBAY_CLIENT_ADDED, 0, PATCHBAY_GROUP_MIDI_OUT, 0, 0.0f, "Writable MIDI ports");
- {
- const unsigned int portCount = fMidiOut.getPortCount();
-
- for (unsigned int i=0; i < portCount; ++i)
- {
- PortNameToId portNameToId;
- portNameToId.portId = PATCHBAY_GROUP_MIDI_OUT*1000 + i;
- portNameToId.name = fMidiOut.getPortName(i).c_str();
- fUsedPortNames.append(portNameToId);
-
- const char* const portName(portNameToId.name.toUtf8().constData());
- callback(CALLBACK_PATCHBAY_PORT_ADDED, 0, PATCHBAY_GROUP_MIDI_OUT, portNameToId.portId, PATCHBAY_PORT_IS_MIDI|PATCHBAY_PORT_IS_INPUT, portName);
- }
- }
-
- // Connections
- if (! fUsedMidiPortIn.isEmpty())
- {
- ConnectionToId connectionToId;
- connectionToId.id = fLastConnectionId;
- connectionToId.portOut = getPatchbayPortId(fUsedMidiPortIn);
- connectionToId.portIn = PATCHBAY_PORT_MIDI_IN;
-
- fUsedConnections.append(connectionToId);
- callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, connectionToId.portOut, connectionToId.portIn, nullptr);
- fLastConnectionId++;
- }
- if (! fUsedMidiPortOut.isEmpty())
- {
- ConnectionToId connectionToId;
- connectionToId.id = fLastConnectionId;
- connectionToId.portOut = PATCHBAY_PORT_MIDI_OUT;
- connectionToId.portIn = getPatchbayPortId(fUsedMidiPortOut);
-
- fUsedConnections.append(connectionToId);
- callback(CALLBACK_PATCHBAY_CONNECTION_ADDED, 0, fLastConnectionId, connectionToId.portOut, connectionToId.portIn, nullptr);
- fLastConnectionId++;
- }
- }
-
- // -------------------------------------------------------------------
-
- 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 (! fAudioIsReady)
- {
- carla_zeroFloat(outsPtr, nframes*2);
- return proccessPendingEvents();
- }
-
- if (kData->curPluginCount == 0)
- {
- // pass-through
- if (fOptions.processMode == PROCESS_MODE_CONTINUOUS_RACK)
- carla_copyFloat(outsPtr, insPtr, nframes*2);
-
- return proccessPendingEvents();
- }
-
- // initialize audio input
- if (fAudioIsInterleaved)
- {
- for (unsigned int i=0; i < nframes*2; ++i)
- {
- if (i % 2 == 0)
- fAudioInBuf1[i/2] = insPtr[i];
- else
- fAudioInBuf2[i/2] = insPtr[i];
- }
- }
- else
- {
- carla_copyFloat(fAudioInBuf1, insPtr, nframes);
- carla_copyFloat(fAudioInBuf2, insPtr+nframes, nframes);
- }
-
- // initialize audio output
- carla_zeroFloat(fAudioOutBuf1, fBufferSize);
- carla_zeroFloat(fAudioOutBuf2, fBufferSize);
-
- // initialize input events
- carla_zeroMem(kData->rack.in, sizeof(EngineEvent)*RACK_EVENT_COUNT);
-
- if (fMidiInEvents.mutex.tryLock())
- {
- uint32_t engineEventIndex = 0;
- fMidiInEvents.splice();
-
- while (! fMidiInEvents.data.isEmpty())
- {
- const RtMidiEvent& midiEvent = fMidiInEvents.data.getFirst(true);
-
- EngineEvent* const engineEvent = &kData->rack.in[engineEventIndex++];
- engineEvent->clear();
-
- const uint8_t midiStatus = MIDI_GET_STATUS_FROM_DATA(midiEvent.data);
- const uint8_t midiChannel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.data);
-
- engineEvent->channel = midiChannel;
-
- if (midiEvent.time < fTimeInfo.frame)
- engineEvent->time = 0;
- else if (midiEvent.time >= fTimeInfo.frame + nframes)
- {
- engineEvent->time = fTimeInfo.frame + nframes-1;
- carla_stderr("MIDI Event in the future!, %i vs %i", engineEvent->time, fTimeInfo.frame);
- }
- else
- engineEvent->time = midiEvent.time - fTimeInfo.frame;
-
- //carla_stdout("Got midi, time %f vs %i", midiEvent.time, engineEvent->time);
-
- if (MIDI_IS_STATUS_CONTROL_CHANGE(midiStatus))
- {
- const uint8_t midiControl = midiEvent.data[1];
- engineEvent->type = kEngineEventTypeControl;
-
- if (MIDI_IS_CONTROL_BANK_SELECT(midiControl))
- {
- const uint8_t midiBank = midiEvent.data[2];
-
- engineEvent->ctrl.type = kEngineControlEventTypeMidiBank;
- engineEvent->ctrl.param = midiBank;
- engineEvent->ctrl.value = 0.0;
- }
- else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF)
- {
- engineEvent->ctrl.type = kEngineControlEventTypeAllSoundOff;
- engineEvent->ctrl.param = 0;
- engineEvent->ctrl.value = 0.0;
- }
- else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF)
- {
- engineEvent->ctrl.type = kEngineControlEventTypeAllNotesOff;
- engineEvent->ctrl.param = 0;
- engineEvent->ctrl.value = 0.0;
- }
- else
- {
- const uint8_t midiValue = midiEvent.data[2];
-
- engineEvent->ctrl.type = kEngineControlEventTypeParameter;
- engineEvent->ctrl.param = midiControl;
- engineEvent->ctrl.value = double(midiValue)/127.0;
- }
- }
- else if (MIDI_IS_STATUS_PROGRAM_CHANGE(midiStatus))
- {
- const uint8_t midiProgram = midiEvent.data[1];
- engineEvent->type = kEngineEventTypeControl;
-
- engineEvent->ctrl.type = kEngineControlEventTypeMidiProgram;
- engineEvent->ctrl.param = midiProgram;
- engineEvent->ctrl.value = 0.0;
- }
- else
- {
- engineEvent->type = kEngineEventTypeMidi;
-
- engineEvent->midi.data[0] = midiStatus;
- engineEvent->midi.data[1] = midiEvent.data[1];
- engineEvent->midi.data[2] = midiEvent.data[2];
- engineEvent->midi.size = midiEvent.size;
- }
-
- if (engineEventIndex >= RACK_EVENT_COUNT)
- break;
- }
-
- fMidiInEvents.mutex.unlock();
- }
-
- // 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 == 0)
- outsPtr[i] = fAudioOutBuf1[i/2];
- else
- outsPtr[i] = fAudioOutBuf2[i/2];
- }
- }
- else
- {
- carla_copyFloat(outsPtr, fAudioOutBuf1, nframes);
- carla_copyFloat(outsPtr+nframes, fAudioOutBuf2, nframes);
- }
-
- // output events
- {
- // TODO
- //fMidiOut.sendMessage();
- }
-
- proccessPendingEvents();
-
- return;
-
- // unused
- (void)streamTime;
- (void)status;
- }
-
- void handleMidiCallback(double timeStamp, std::vector<unsigned char>* const message)
- {
- const size_t messageSize = message->size();
- static uint32_t lastTime = 0;
-
- if (messageSize == 0 || messageSize > 4)
- return;
-
- timeStamp /= 2;
-
- if (timeStamp > 0.95)
- timeStamp = 0.95;
-
- RtMidiEvent midiEvent;
- midiEvent.time = fTimeInfo.frame + (timeStamp*(double)fBufferSize);
- carla_stdout("Put midi, frame:%09i/%09i, stamp:%g", fTimeInfo.frame, midiEvent.time, timeStamp);
-
- if (midiEvent.time < lastTime)
- midiEvent.time = lastTime;
- else
- lastTime = midiEvent.time;
-
- if (messageSize == 1)
- {
- midiEvent.data[0] = message->at(0);
- midiEvent.data[1] = 0;
- midiEvent.data[2] = 0;
- midiEvent.data[3] = 0;
- midiEvent.size = 1;
- }
- else if (messageSize == 2)
- {
- midiEvent.data[0] = message->at(0);
- midiEvent.data[1] = message->at(1);
- midiEvent.data[2] = 0;
- midiEvent.data[3] = 0;
- midiEvent.size = 2;
- }
- else if (messageSize == 3)
- {
- midiEvent.data[0] = message->at(0);
- midiEvent.data[1] = message->at(1);
- midiEvent.data[2] = message->at(2);
- midiEvent.data[3] = 0;
- midiEvent.size = 3;
- }
- else
- {
- midiEvent.data[0] = message->at(0);
- midiEvent.data[1] = message->at(1);
- midiEvent.data[2] = message->at(2);
- midiEvent.data[3] = message->at(3);
- midiEvent.size = 4;
- }
-
- fMidiInEvents.append(midiEvent);
- }
-
- // -------------------------------------
-
- void disconnectMidiPort(const bool isInput, const bool doPatchbay)
- {
- carla_debug("CarlaEngineRtAudio::disconnectMidiPort(%s, %s)", bool2str(isInput), bool2str(doPatchbay));
-
- if (isInput)
- {
- if (! fUsedMidiPortIn.isEmpty())
- {
- fUsedMidiPortIn.clear();
- fMidiIn.closePort();
- }
- }
- else
- {
- if (! fUsedMidiPortOut.isEmpty())
- {
- fUsedMidiPortOut.clear();
- fMidiIn.closePort();
- }
- }
-
- if (! doPatchbay)
- return;
-
- for (int i=0, count=fUsedConnections.count(); i < count; ++i)
- {
- const ConnectionToId& connection(fUsedConnections.at(i));
-
- const int targetPort = (connection.portOut >= 0) ? connection.portOut : connection.portIn;
-
- if (targetPort >= PATCHBAY_GROUP_MIDI_OUT*1000)
- {
- if (isInput)
- continue;
-
- callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, connection.id, 0, 0.0f, nullptr);
- fUsedConnections.takeAt(i);
- break;
- }
- else if (targetPort >= PATCHBAY_GROUP_MIDI_IN*1000)
- {
- if (! isInput)
- continue;
-
- callback(CALLBACK_PATCHBAY_CONNECTION_REMOVED, 0, connection.id, 0, 0.0f, nullptr);
- fUsedConnections.takeAt(i);
- break;
- }
- }
- }
-
- int getPatchbayPortId(const QString& name)
- {
- carla_debug("CarlaEngineRtAudio::getPatchbayPortId(\"%s\")", name.toUtf8().constData());
-
- for (int i=0, count=fUsedPortNames.count(); i < count; ++i)
- {
- carla_debug("CarlaEngineRtAudio::getPatchbayPortId(\"%s\") VS \"%s\"", name.toUtf8().constData(), fUsedPortNames[i].name.toUtf8().constData());
-
- if (fUsedPortNames[i].name == name)
- return fUsedPortNames[i].portId;
- }
-
- return PATCHBAY_PORT_MAX;
- }
-
- // -------------------------------------
-
- private:
- RtAudio fAudio;
- bool fAudioIsInterleaved;
- bool fAudioIsReady;
- float* fAudioInBuf1;
- float* fAudioInBuf2;
- float* fAudioOutBuf1;
- float* fAudioOutBuf2;
-
- RtMidiIn fMidiIn;
- RtMidiOut fMidiOut;
-
- enum PatchbayGroupIds {
- PATCHBAY_GROUP_CARLA = -1,
- PATCHBAY_GROUP_AUDIO_IN = 0,
- PATCHBAY_GROUP_AUDIO_OUT = 1,
- PATCHBAY_GROUP_MIDI_IN = 2,
- PATCHBAY_GROUP_MIDI_OUT = 3,
- PATCHBAY_GROUP_MAX = 4
- };
-
- enum PatchbayPortIds {
- PATCHBAY_PORT_AUDIO_IN1 = -1,
- PATCHBAY_PORT_AUDIO_IN2 = -2,
- PATCHBAY_PORT_AUDIO_OUT1 = -3,
- PATCHBAY_PORT_AUDIO_OUT2 = -4,
- PATCHBAY_PORT_MIDI_IN = -5,
- PATCHBAY_PORT_MIDI_OUT = -6,
- PATCHBAY_PORT_MAX = -7
- };
-
- struct PortNameToId {
- int portId;
- QString name;
- };
-
- struct ConnectionToId {
- int id;
- int portOut;
- int portIn;
- };
-
- int fLastConnectionId;
- QList<PortNameToId> fUsedPortNames;
- QList<ConnectionToId> fUsedConnections;
- QString fUsedMidiPortIn;
- QString fUsedMidiPortOut;
-
- struct RtMidiEvent {
- uint32_t time;
- unsigned char data[4];
- unsigned char size;
- };
-
- 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 splice()
- {
- dataPending.spliceAppend(data, true);
- }
- };
-
- 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:
- #if defined(CARLA_OS_WIN)
- return "JACK with WinMM";
- #elif defined(CARLA_OS_MAC)
- return "JACK with CoreMidi";
- #elif defined(CARLA_OS_LINUX)
- return "JACK with ALSA-MIDI";
- #else
- return "JACK (RtAudio)";
- #endif
- 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
|