| @@ -83,19 +83,23 @@ namespace | |||
| std::cerr << s << std::endl; | |||
| } | |||
| void dumpJackErrorMessage (const jack_status_t status) | |||
| const char* getJackErrorMessage (const jack_status_t status) | |||
| { | |||
| if (status & JackServerFailed || status & JackServerError) jack_Log ("Unable to connect to JACK server"); | |||
| if (status & JackVersionError) jack_Log ("Client's protocol version does not match"); | |||
| if (status & JackInvalidOption) jack_Log ("The operation contained an invalid or unsupported option"); | |||
| if (status & JackNameNotUnique) jack_Log ("The desired client name was not unique"); | |||
| if (status & JackNoSuchClient) jack_Log ("Requested client does not exist"); | |||
| if (status & JackInitFailure) jack_Log ("Unable to initialize client"); | |||
| if (status & JackServerFailed | |||
| || status & JackServerError) return "Unable to connect to JACK server"; | |||
| if (status & JackVersionError) return "Client's protocol version does not match"; | |||
| if (status & JackInvalidOption) return "The operation contained an invalid or unsupported option"; | |||
| if (status & JackNameNotUnique) return "The desired client name was not unique"; | |||
| if (status & JackNoSuchClient) return "Requested client does not exist"; | |||
| if (status & JackInitFailure) return "Unable to initialize client"; | |||
| return nullptr; | |||
| } | |||
| } | |||
| #define JUCE_JACK_LOG_STATUS(x) { if (const char* m = getJackErrorMessage (x)) jack_Log (m); } | |||
| #define JUCE_JACK_LOG(x) jack_Log(x) | |||
| #else | |||
| #define dumpJackErrorMessage(a) {} | |||
| #define jack_Log(...) {} | |||
| #define JUCE_JACK_LOG_STATUS(x) {} | |||
| #define JUCE_JACK_LOG(x) {} | |||
| #endif | |||
| @@ -104,14 +108,37 @@ namespace | |||
| #define JUCE_JACK_CLIENT_NAME "JUCEJack" | |||
| #endif | |||
| static const char** getJackPorts (jack_client_t* const client, const bool forInput) | |||
| struct JackPortIterator | |||
| { | |||
| if (client != nullptr) | |||
| return juce::jack_get_ports (client, nullptr, nullptr, | |||
| forInput ? JackPortIsOutput : JackPortIsInput); | |||
| // (NB: This looks like it's the wrong way round, but it is correct!) | |||
| return nullptr; | |||
| } | |||
| JackPortIterator (jack_client_t* const client, const bool forInput) | |||
| : ports (nullptr), index (-1) | |||
| { | |||
| if (client != nullptr) | |||
| ports = juce::jack_get_ports (client, nullptr, nullptr, | |||
| forInput ? JackPortIsOutput : JackPortIsInput); | |||
| // (NB: This looks like it's the wrong way round, but it is correct!) | |||
| } | |||
| ~JackPortIterator() | |||
| { | |||
| ::free (ports); | |||
| } | |||
| bool next() | |||
| { | |||
| if (ports == nullptr || ports [index + 1] == nullptr) | |||
| return false; | |||
| name = CharPointer_UTF8 (ports[++index]); | |||
| clientName = name.upToFirstOccurrenceOf (":", false, false); | |||
| return true; | |||
| } | |||
| const char** ports; | |||
| int index; | |||
| String name; | |||
| String clientName; | |||
| }; | |||
| class JackAudioIODeviceType; | |||
| static Array<JackAudioIODeviceType*> activeDeviceTypes; | |||
| @@ -126,7 +153,7 @@ public: | |||
| : AudioIODevice (deviceName, "JACK"), | |||
| inputId (inId), | |||
| outputId (outId), | |||
| isOpen_ (false), | |||
| deviceIsOpen (false), | |||
| callback (nullptr), | |||
| totalNumberOfInputChannels (0), | |||
| totalNumberOfOutputChannels (0) | |||
| @@ -138,7 +165,7 @@ public: | |||
| if (client == nullptr) | |||
| { | |||
| dumpJackErrorMessage (status); | |||
| JUCE_JACK_LOG_STATUS (status); | |||
| } | |||
| else | |||
| { | |||
| @@ -184,18 +211,10 @@ public: | |||
| StringArray getChannelNames (bool forInput) const | |||
| { | |||
| StringArray names; | |||
| if (const char** const ports = getJackPorts (client, forInput)) | |||
| { | |||
| for (int j = 0; ports[j] != nullptr; ++j) | |||
| { | |||
| const String portName (ports [j]); | |||
| if (portName.upToFirstOccurrenceOf (":", false, false) == getName()) | |||
| names.add (portName.fromFirstOccurrenceOf (":", false, false)); | |||
| } | |||
| free (ports); | |||
| } | |||
| for (JackPortIterator i (client, forInput); i.next();) | |||
| if (i.clientName == getName()) | |||
| names.add (i.name.fromFirstOccurrenceOf (":", false, false)); | |||
| return names; | |||
| } | |||
| @@ -224,49 +243,31 @@ public: | |||
| juce::jack_set_port_connect_callback (client, portConnectCallback, this); | |||
| juce::jack_on_shutdown (client, shutdownCallback, this); | |||
| juce::jack_activate (client); | |||
| isOpen_ = true; | |||
| deviceIsOpen = true; | |||
| if (! inputChannels.isZero()) | |||
| { | |||
| if (const char** const ports = getJackPorts (client, true)) | |||
| for (JackPortIterator i (client, true); i.next();) | |||
| { | |||
| const int numInputChannels = inputChannels.getHighestBit() + 1; | |||
| for (int i = 0; i < numInputChannels; ++i) | |||
| if (inputChannels [i.index] && i.clientName == getName()) | |||
| { | |||
| const String portName (ports[i]); | |||
| if (inputChannels[i] && portName.upToFirstOccurrenceOf (":", false, false) == getName()) | |||
| { | |||
| int error = juce::jack_connect (client, ports[i], juce::jack_port_name ((jack_port_t*) inputPorts[i])); | |||
| if (error != 0) | |||
| jack_Log ("Cannot connect input port " + String (i) + " (" + String (ports[i]) + "), error " + String (error)); | |||
| } | |||
| int error = juce::jack_connect (client, i.ports[i.index], juce::jack_port_name ((jack_port_t*) inputPorts[i.index])); | |||
| if (error != 0) | |||
| JUCE_JACK_LOG ("Cannot connect input port " + String (i.index) + " (" + i.name + "), error " + String (error)); | |||
| } | |||
| free (ports); | |||
| } | |||
| } | |||
| if (! outputChannels.isZero()) | |||
| { | |||
| if (const char** const ports = getJackPorts (client, false)) | |||
| for (JackPortIterator i (client, false); i.next();) | |||
| { | |||
| const int numOutputChannels = outputChannels.getHighestBit() + 1; | |||
| for (int i = 0; i < numOutputChannels; ++i) | |||
| if (outputChannels [i.index] && i.clientName == getName()) | |||
| { | |||
| const String portName (ports[i]); | |||
| if (outputChannels[i] && portName.upToFirstOccurrenceOf (":", false, false) == getName()) | |||
| { | |||
| int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i]), ports[i]); | |||
| if (error != 0) | |||
| jack_Log ("Cannot connect output port " + String (i) + " (" + String (ports[i]) + "), error " + String (error)); | |||
| } | |||
| int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i.index]), i.ports[i.index]); | |||
| if (error != 0) | |||
| JUCE_JACK_LOG ("Cannot connect output port " + String (i.index) + " (" + i.name + "), error " + String (error)); | |||
| } | |||
| free (ports); | |||
| } | |||
| } | |||
| @@ -285,12 +286,12 @@ public: | |||
| juce::jack_on_shutdown (client, shutdownCallback, nullptr); | |||
| } | |||
| isOpen_ = false; | |||
| deviceIsOpen = false; | |||
| } | |||
| void start (AudioIODeviceCallback* newCallback) | |||
| { | |||
| if (isOpen_ && newCallback != callback) | |||
| if (deviceIsOpen && newCallback != callback) | |||
| { | |||
| if (newCallback != nullptr) | |||
| newCallback->audioDeviceAboutToStart (this); | |||
| @@ -312,7 +313,7 @@ public: | |||
| start (nullptr); | |||
| } | |||
| bool isOpen() { return isOpen_; } | |||
| bool isOpen() { return deviceIsOpen; } | |||
| bool isPlaying() { return callback != nullptr; } | |||
| int getCurrentBufferSizeSamples() { return getBufferSizeSamples (0); } | |||
| double getCurrentSampleRate() { return getSampleRate (0); } | |||
| @@ -425,12 +426,12 @@ private: | |||
| static void threadInitCallback (void* /* callbackArgument */) | |||
| { | |||
| jack_Log ("JackAudioIODevice::initialise"); | |||
| JUCE_JACK_LOG ("JackAudioIODevice::initialise"); | |||
| } | |||
| static void shutdownCallback (void* callbackArgument) | |||
| { | |||
| jack_Log ("JackAudioIODevice::shutdown"); | |||
| JUCE_JACK_LOG ("JackAudioIODevice::shutdown"); | |||
| if (JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument) | |||
| { | |||
| @@ -441,12 +442,12 @@ private: | |||
| static void errorCallback (const char* msg) | |||
| { | |||
| jack_Log ("JackAudioIODevice::errorCallback " + String (msg)); | |||
| JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (msg)); | |||
| } | |||
| static void sendDeviceChangedCallback(); | |||
| bool isOpen_; | |||
| bool deviceIsOpen; | |||
| jack_client_t* client; | |||
| String lastError; | |||
| AudioIODeviceCallback* callback; | |||
| @@ -498,46 +499,30 @@ public: | |||
| if (jack_client_t* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status)) | |||
| { | |||
| // scan for output devices | |||
| if (const char** const ports = getJackPorts (client, false)) | |||
| for (JackPortIterator i (client, false); i.next();) | |||
| { | |||
| for (int j = 0; ports[j] != nullptr; ++j) | |||
| if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.clientName)) | |||
| { | |||
| String clientName (ports[j]); | |||
| clientName = clientName.upToFirstOccurrenceOf (":", false, false); | |||
| if (clientName != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (clientName)) | |||
| { | |||
| inputNames.add (clientName); | |||
| inputIds.add (ports [j]); | |||
| } | |||
| inputNames.add (i.clientName); | |||
| inputIds.add (i.ports [i.index]); | |||
| } | |||
| free (ports); | |||
| } | |||
| // scan for input devices | |||
| if (const char** const ports = getJackPorts (client, true)) | |||
| for (JackPortIterator i (client, true); i.next();) | |||
| { | |||
| for (int j = 0; ports[j] != nullptr; ++j) | |||
| if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.clientName)) | |||
| { | |||
| String clientName (ports[j]); | |||
| clientName = clientName.upToFirstOccurrenceOf (":", false, false); | |||
| if (clientName != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (clientName)) | |||
| { | |||
| outputNames.add (clientName); | |||
| outputIds.add (ports [j]); | |||
| } | |||
| outputNames.add (i.clientName); | |||
| outputIds.add (i.ports [i.index]); | |||
| } | |||
| free (ports); | |||
| } | |||
| juce::jack_client_close (client); | |||
| } | |||
| else | |||
| { | |||
| dumpJackErrorMessage (status); | |||
| JUCE_JACK_LOG_STATUS (status); | |||
| } | |||
| } | |||