Browse Source

JACK: Fix bug where input and output devices were reversed

tags/2021-05-28
reuk 4 years ago
parent
commit
8fc1c1abae
No known key found for this signature in database GPG Key ID: 9ADCD339CFC98A11
3 changed files with 176 additions and 128 deletions
  1. +1
    -0
      examples/DemoRunner/CMakeLists.txt
  2. +174
    -127
      modules/juce_audio_devices/native/juce_linux_JackAudio.cpp
  3. +1
    -1
      modules/juce_dsp/containers/juce_SIMDRegister_test.cpp

+ 1
- 0
examples/DemoRunner/CMakeLists.txt View File

@@ -46,6 +46,7 @@ target_compile_definitions(DemoRunner PRIVATE
PIP_JUCE_EXAMPLES_DIRECTORY_STRING="${JUCE_SOURCE_DIR}/examples"
JUCE_ALLOW_STATIC_NULL_VARIABLES=0
JUCE_DEMO_RUNNER=1
JUCE_JACK=1
JUCE_STRICT_REFCOUNTEDPOINTER=1
JUCE_UNIT_TESTS=1
JUCE_USE_CAMERA=1


+ 174
- 127
modules/juce_audio_devices/native/juce_linux_JackAudio.cpp View File

@@ -36,9 +36,11 @@ static void* juce_loadJackFunction (const char* const name)
#define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \
return_type fn_name argument_types \
{ \
using ReturnType = return_type; \
typedef return_type (*fn_type) argument_types; \
static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \
return (fn != nullptr) ? ((*fn) arguments) : (return_type) 0; \
jassert (fn != nullptr); \
return (fn != nullptr) ? ((*fn) arguments) : ReturnType(); \
}
#define JUCE_DECL_VOID_JACK_FUNCTION(fn_name, argument_types, arguments) \
@@ -46,6 +48,7 @@ static void* juce_loadJackFunction (const char* const name)
{ \
typedef void (*fn_type) argument_types; \
static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \
jassert (fn != nullptr); \
if (fn != nullptr) (*fn) arguments; \
}
@@ -57,6 +60,7 @@ JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client)
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client))
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client))
JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function)(void* arg), void* arg), (client, function, arg))
JUCE_DECL_VOID_JACK_FUNCTION (jack_on_info_shutdown, (jack_client_t* client, JackInfoShutdownCallback function, void* arg), (client, function, arg))
JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes))
JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port))
JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size))
@@ -70,6 +74,9 @@ JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client,
JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port))
JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name))
JUCE_DECL_JACK_FUNCTION (int, jack_set_xrun_callback, (jack_client_t* client, JackXRunCallback xrun_callback, void* arg), (client, xrun_callback, arg))
JUCE_DECL_JACK_FUNCTION (int, jack_port_flags, (const jack_port_t* port), (port))
JUCE_DECL_JACK_FUNCTION (jack_port_t*, jack_port_by_name, (jack_client_t* client, const char* name), (client, name))
JUCE_DECL_VOID_JACK_FUNCTION (jack_free, (void* ptr), (ptr))
#if JUCE_DEBUG
#define JACK_LOGGING_ENABLED 1
@@ -115,56 +122,56 @@ namespace
struct JackPortIterator
{
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);
ports.reset (juce::jack_get_ports (client, nullptr, nullptr,
forInput ? JackPortIsInput : JackPortIsOutput));
}
bool next()
{
if (ports == nullptr || ports [index + 1] == nullptr)
if (ports == nullptr || ports.get()[index + 1] == nullptr)
return false;
name = CharPointer_UTF8 (ports[++index]);
clientName = name.upToFirstOccurrenceOf (":", false, false);
name = CharPointer_UTF8 (ports.get()[++index]);
return true;
}
const char** ports;
int index;
String getClientName() const
{
return name.upToFirstOccurrenceOf (":", false, false);
}
String getChannelName() const
{
return name.fromFirstOccurrenceOf (":", false, false);
}
struct Free
{
void operator() (const char** ptr) const noexcept { juce::jack_free (ptr); }
};
std::unique_ptr<const char*, Free> ports;
int index = -1;
String name;
String clientName;
};
class JackAudioIODeviceType;
static Array<JackAudioIODeviceType*> activeDeviceTypes;
//==============================================================================
class JackAudioIODevice : public AudioIODevice
{
public:
JackAudioIODevice (const String& deviceName,
const String& inId,
const String& outId)
: AudioIODevice (deviceName, "JACK"),
inputId (inId),
outputId (outId),
deviceIsOpen (false),
callback (nullptr),
totalNumberOfInputChannels (0),
totalNumberOfOutputChannels (0)
{
jassert (deviceName.isNotEmpty());
jack_status_t status;
JackAudioIODevice (const String& inName,
const String& outName,
std::function<void()> notifyIn)
: AudioIODevice (outName.isEmpty() ? inName : outName, "JACK"),
inputName (inName),
outputName (outName),
notifyChannelsChanged (std::move (notifyIn))
{
jassert (outName.isNotEmpty() || inName.isNotEmpty());
jack_status_t status = {};
client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status);
if (client == nullptr)
@@ -202,7 +209,7 @@ public:
}
}
~JackAudioIODevice()
~JackAudioIODevice() override
{
close();
if (client != nullptr)
@@ -212,19 +219,19 @@ public:
}
}
StringArray getChannelNames (bool forInput) const
StringArray getChannelNames (const String& clientName, bool forInput) const
{
StringArray names;
for (JackPortIterator i (client, forInput); i.next();)
if (i.clientName == getName())
names.add (i.name.fromFirstOccurrenceOf (":", false, false));
if (i.getClientName() == clientName)
names.add (i.getChannelName());
return names;
}
StringArray getOutputChannelNames() override { return getChannelNames (false); }
StringArray getInputChannelNames() override { return getChannelNames (true); }
StringArray getOutputChannelNames() override { return getChannelNames (outputName, true); }
StringArray getInputChannelNames() override { return getChannelNames (inputName, false); }
Array<double> getAvailableSampleRates() override
{
@@ -250,6 +257,20 @@ public:
int getCurrentBufferSizeSamples() override { return client != nullptr ? static_cast<int> (juce::jack_get_buffer_size (client)) : 0; }
double getCurrentSampleRate() override { return client != nullptr ? static_cast<int> (juce::jack_get_sample_rate (client)) : 0; }
template <typename Fn>
void forEachClientChannel (const String& clientName, bool isInput, Fn&& fn)
{
auto index = 0;
for (JackPortIterator i (client, isInput); i.next();)
{
if (i.getClientName() != clientName)
continue;
fn (i.ports.get()[i.index], index);
index += 1;
}
}
String open (const BigInteger& inputChannels, const BigInteger& outputChannels,
double /* sampleRate */, int /* bufferSizeSamples */) override
@@ -263,38 +284,55 @@ public:
lastError.clear();
close();
xruns = 0;
xruns.store (0, std::memory_order_relaxed);
juce::jack_set_process_callback (client, processCallback, this);
juce::jack_set_port_connect_callback (client, portConnectCallback, this);
juce::jack_on_shutdown (client, shutdownCallback, this);
juce::jack_on_info_shutdown (client, infoShutdownCallback, this);
juce::jack_set_xrun_callback (client, xrunCallback, this);
juce::jack_activate (client);
deviceIsOpen = true;
if (! inputChannels.isZero())
{
for (JackPortIterator i (client, true); i.next();)
forEachClientChannel (inputName, false, [&] (const char* portName, int index)
{
if (inputChannels [i.index] && i.clientName == getName())
{
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));
}
}
if (! inputChannels[index])
return;
jassert (index < inputPorts.size());
const auto* source = portName;
const auto* inputPort = inputPorts[index];
jassert (juce::jack_port_flags (juce::jack_port_by_name (client, source)) & JackPortIsOutput);
jassert (juce::jack_port_flags (inputPort) & JackPortIsInput);
auto error = juce::jack_connect (client, source, juce::jack_port_name (inputPort));
if (error != 0)
JUCE_JACK_LOG ("Cannot connect input port " + String (index) + " (" + portName + "), error " + String (error));
});
}
if (! outputChannels.isZero())
{
for (JackPortIterator i (client, false); i.next();)
forEachClientChannel (outputName, true, [&] (const char* portName, int index)
{
if (outputChannels [i.index] && i.clientName == getName())
{
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));
}
}
if (! outputChannels[index])
return;
jassert (index < outputPorts.size());
const auto* outputPort = outputPorts[index];
const auto* destination = portName;
jassert (juce::jack_port_flags (outputPort) & JackPortIsOutput);
jassert (juce::jack_port_flags (juce::jack_port_by_name (client, destination)) & JackPortIsInput);
auto error = juce::jack_connect (client, juce::jack_port_name (outputPort), destination);
if (error != 0)
JUCE_JACK_LOG ("Cannot connect output port " + String (index) + " (" + portName + "), error " + String (error));
});
}
updateActivePorts();
@@ -308,12 +346,15 @@ public:
if (client != nullptr)
{
juce::jack_deactivate (client);
const auto result = juce::jack_deactivate (client);
jassert (result == 0);
ignoreUnused (result);
juce::jack_set_xrun_callback (client, xrunCallback, nullptr);
juce::jack_set_process_callback (client, processCallback, nullptr);
juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr);
juce::jack_on_shutdown (client, shutdownCallback, nullptr);
juce::jack_on_info_shutdown (client, infoShutdownCallback, nullptr);
}
deviceIsOpen = false;
@@ -347,7 +388,7 @@ public:
bool isPlaying() override { return callback != nullptr; }
int getCurrentBitDepth() override { return 32; }
String getLastError() override { return lastError; }
int getXRunCount() const noexcept override { return xruns; }
int getXRunCount() const noexcept override { return xruns.load (std::memory_order_relaxed); }
BigInteger getActiveOutputChannels() const override { return activeOutputChannels; }
BigInteger getActiveInputChannels() const override { return activeInputChannels; }
@@ -357,7 +398,7 @@ public:
int latency = 0;
for (int i = 0; i < outputPorts.size(); i++)
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) outputPorts [i]));
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, outputPorts[i]));
return latency;
}
@@ -367,14 +408,36 @@ public:
int latency = 0;
for (int i = 0; i < inputPorts.size(); i++)
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) inputPorts [i]));
latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, inputPorts[i]));
return latency;
}
String inputId, outputId;
String inputName, outputName;
private:
//==============================================================================
class MainThreadDispatcher : private AsyncUpdater
{
public:
explicit MainThreadDispatcher (JackAudioIODevice& device) : ref (device) {}
~MainThreadDispatcher() override { cancelPendingUpdate(); }
void updateActivePorts()
{
if (MessageManager::getInstance()->isThisTheMessageThread())
handleAsyncUpdate();
else
triggerAsyncUpdate();
}
private:
void handleAsyncUpdate() override { ref.updateActivePorts(); }
JackAudioIODevice& ref;
};
//==============================================================================
void process (const int numSamples)
{
int numActiveInChans = 0, numActiveOutChans = 0;
@@ -382,17 +445,17 @@ private:
for (int i = 0; i < totalNumberOfInputChannels; ++i)
{
if (activeInputChannels[i])
if (jack_default_audio_sample_t* in
= (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) inputPorts.getUnchecked(i), static_cast<jack_nframes_t> (numSamples)))
inChans [numActiveInChans++] = (float*) in;
if (auto* in = (jack_default_audio_sample_t*) juce::jack_port_get_buffer (inputPorts.getUnchecked (i),
static_cast<jack_nframes_t> (numSamples)))
inChans[numActiveInChans++] = (float*) in;
}
for (int i = 0; i < totalNumberOfOutputChannels; ++i)
{
if (activeOutputChannels[i])
if (jack_default_audio_sample_t* out
= (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) outputPorts.getUnchecked(i), static_cast<jack_nframes_t> (numSamples)))
outChans [numActiveOutChans++] = (float*) out;
if (auto* out = (jack_default_audio_sample_t*) juce::jack_port_get_buffer (outputPorts.getUnchecked (i),
static_cast<jack_nframes_t> (numSamples)))
outChans[numActiveOutChans++] = (float*) out;
}
const ScopedLock sl (callbackLock);
@@ -406,7 +469,7 @@ private:
else
{
for (int i = 0; i < numActiveOutChans; ++i)
zeromem (outChans[i], sizeof (float) * static_cast<size_t> (numSamples));
zeromem (outChans[i], static_cast<size_t> (numSamples) * sizeof (float));
}
}
@@ -431,11 +494,11 @@ private:
BigInteger newOutputChannels, newInputChannels;
for (int i = 0; i < outputPorts.size(); ++i)
if (juce::jack_port_connected ((jack_port_t*) outputPorts.getUnchecked(i)))
if (juce::jack_port_connected (outputPorts.getUnchecked (i)))
newOutputChannels.setBit (i);
for (int i = 0; i < inputPorts.size(); ++i)
if (juce::jack_port_connected ((jack_port_t*) inputPorts.getUnchecked(i)))
if (juce::jack_port_connected (inputPorts.getUnchecked (i)))
newInputChannels.setBit (i);
if (newOutputChannels != activeOutputChannels
@@ -451,14 +514,15 @@ private:
if (oldCallback != nullptr)
start (oldCallback);
sendDeviceChangedCallback();
if (notifyChannelsChanged != nullptr)
notifyChannelsChanged();
}
}
static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg)
{
if (JackAudioIODevice* device = static_cast<JackAudioIODevice*> (arg))
device->updateActivePorts();
device->mainThreadDispatcher.updateActivePorts();
}
static void threadInitCallback (void* /* callbackArgument */)
@@ -477,82 +541,76 @@ private:
}
}
static void infoShutdownCallback (jack_status_t code, const char* reason, void* arg)
{
jassert (code == 0);
ignoreUnused (code);
JUCE_JACK_LOG ("Shutting down with message:");
JUCE_JACK_LOG (reason);
ignoreUnused (reason);
shutdownCallback (arg);
}
static void errorCallback (const char* msg)
{
JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (msg));
ignoreUnused (msg);
}
static void sendDeviceChangedCallback();
bool deviceIsOpen;
jack_client_t* client;
bool deviceIsOpen = false;
jack_client_t* client = nullptr;
String lastError;
AudioIODeviceCallback* callback;
AudioIODeviceCallback* callback = nullptr;
CriticalSection callbackLock;
HeapBlock<float*> inChans, outChans;
int totalNumberOfInputChannels;
int totalNumberOfOutputChannels;
Array<void*> inputPorts, outputPorts;
int totalNumberOfInputChannels = 0;
int totalNumberOfOutputChannels = 0;
Array<jack_port_t*> inputPorts, outputPorts;
BigInteger activeInputChannels, activeOutputChannels;
int xruns;
};
std::atomic<int> xruns { 0 };
std::function<void()> notifyChannelsChanged;
MainThreadDispatcher mainThreadDispatcher { *this };
};
//==============================================================================
class JackAudioIODeviceType;
class JackAudioIODeviceType : public AudioIODeviceType
{
public:
JackAudioIODeviceType()
: AudioIODeviceType ("JACK"),
hasScanned (false)
{
activeDeviceTypes.add (this);
}
~JackAudioIODeviceType()
{
activeDeviceTypes.removeFirstMatchingValue (this);
}
: AudioIODeviceType ("JACK")
{}
void scanForDevices()
{
hasScanned = true;
inputNames.clear();
inputIds.clear();
outputNames.clear();
outputIds.clear();
if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so.0", RTLD_LAZY);
if (juce_libjackHandle == nullptr) juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY);
if (juce_libjackHandle == nullptr) return;
jack_status_t status;
jack_status_t status = {};
// open a dummy client
if (jack_client_t* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status))
if (auto* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status))
{
// scan for output devices
for (JackPortIterator i (client, false); i.next();)
{
if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.clientName))
{
inputNames.add (i.clientName);
inputIds.add (i.ports [i.index]);
}
}
if (i.getClientName() != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.getClientName()))
inputNames.add (i.getClientName());
// scan for input devices
for (JackPortIterator i (client, true); i.next();)
{
if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.clientName))
{
outputNames.add (i.clientName);
outputIds.add (i.ports [i.index]);
}
}
if (i.getClientName() != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.getClientName()))
outputNames.add (i.getClientName());
juce::jack_client_close (client);
}
@@ -581,8 +639,8 @@ public:
jassert (hasScanned); // need to call scanForDevices() before doing this
if (JackAudioIODevice* d = dynamic_cast<JackAudioIODevice*> (device))
return asInput ? inputIds.indexOf (d->inputId)
: outputIds.indexOf (d->outputId);
return asInput ? inputNames.indexOf (d->inputName)
: outputNames.indexOf (d->outputName);
return -1;
}
@@ -596,30 +654,19 @@ public:
const int outputIndex = outputNames.indexOf (outputDeviceName);
if (inputIndex >= 0 || outputIndex >= 0)
return new JackAudioIODevice (outputIndex >= 0 ? outputDeviceName
: inputDeviceName,
inputIds [inputIndex],
outputIds [outputIndex]);
return new JackAudioIODevice (inputDeviceName, outputDeviceName,
[this] { callDeviceChangeListeners(); });
return nullptr;
}
void portConnectionChange() { callDeviceChangeListeners(); }
private:
StringArray inputNames, outputNames, inputIds, outputIds;
bool hasScanned;
StringArray inputNames, outputNames;
bool hasScanned = false;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType)
};
void JackAudioIODevice::sendDeviceChangedCallback()
{
for (int i = activeDeviceTypes.size(); --i >= 0;)
if (JackAudioIODeviceType* d = activeDeviceTypes[i])
d->portConnectionChange();
}
//==============================================================================
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK()
{


+ 1
- 1
modules/juce_dsp/containers/juce_SIMDRegister_test.cpp View File

@@ -750,7 +750,7 @@ public:
copy (a, inArray);
a = SIMDRegister<type>::abs (a);
auto calcAbs = [] (type x) -> type { return x >= type (0) ? x : -x; };
auto calcAbs = [] (type x) -> type { return x >= type (0) ? x : type (-x); };
for (size_t j = 0; j < SIMDRegister<type>::SIMDNumElements; ++j)
outArray[j] = calcAbs (inArray[j]);


Loading…
Cancel
Save