@@ -122,10 +122,35 @@ public: | |||
bool isLooping; | |||
//============================================================================== | |||
bool operator== (const CurrentPositionInfo& other) const noexcept; | |||
bool operator!= (const CurrentPositionInfo& other) const noexcept; | |||
void resetToDefault(); | |||
bool operator== (const CurrentPositionInfo& other) const noexcept | |||
{ | |||
return timeInSamples == other.timeInSamples | |||
&& ppqPosition == other.ppqPosition | |||
&& editOriginTime == other.editOriginTime | |||
&& ppqPositionOfLastBarStart == other.ppqPositionOfLastBarStart | |||
&& frameRate == other.frameRate | |||
&& isPlaying == other.isPlaying | |||
&& isRecording == other.isRecording | |||
&& bpm == other.bpm | |||
&& timeSigNumerator == other.timeSigNumerator | |||
&& timeSigDenominator == other.timeSigDenominator | |||
&& ppqLoopStart == other.ppqLoopStart | |||
&& ppqLoopEnd == other.ppqLoopEnd | |||
&& isLooping == other.isLooping; | |||
} | |||
bool operator!= (const CurrentPositionInfo& other) const noexcept | |||
{ | |||
return ! operator== (other); | |||
} | |||
void resetToDefault() | |||
{ | |||
zerostruct (*this); | |||
timeSigNumerator = 4; | |||
timeSigDenominator = 4; | |||
bpm = 120; | |||
} | |||
}; | |||
//============================================================================== | |||
@@ -32,7 +32,7 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest | |||
{ | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
*unalignedPointerCast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
intData += destBytesPerSample; | |||
} | |||
} | |||
@@ -43,7 +43,7 @@ void AudioDataConverters::convertFloatToInt16LE (const float* source, void* dest | |||
for (int i = numSamples; --i >= 0;) | |||
{ | |||
intData -= destBytesPerSample; | |||
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
*unalignedPointerCast<uint16*> (intData) = ByteOrder::swapIfBigEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
} | |||
} | |||
} | |||
@@ -57,7 +57,7 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest | |||
{ | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
*unalignedPointerCast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
intData += destBytesPerSample; | |||
} | |||
} | |||
@@ -68,7 +68,7 @@ void AudioDataConverters::convertFloatToInt16BE (const float* source, void* dest | |||
for (int i = numSamples; --i >= 0;) | |||
{ | |||
intData -= destBytesPerSample; | |||
*reinterpret_cast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
*unalignedPointerCast<uint16*> (intData) = ByteOrder::swapIfLittleEndian ((uint16) (short) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
} | |||
} | |||
} | |||
@@ -132,7 +132,7 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest | |||
{ | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
*unalignedPointerCast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
intData += destBytesPerSample; | |||
} | |||
} | |||
@@ -143,7 +143,7 @@ void AudioDataConverters::convertFloatToInt32LE (const float* source, void* dest | |||
for (int i = numSamples; --i >= 0;) | |||
{ | |||
intData -= destBytesPerSample; | |||
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
*unalignedPointerCast<uint32*> (intData) = ByteOrder::swapIfBigEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
} | |||
} | |||
} | |||
@@ -157,7 +157,7 @@ void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest | |||
{ | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
*unalignedPointerCast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
intData += destBytesPerSample; | |||
} | |||
} | |||
@@ -168,7 +168,7 @@ void AudioDataConverters::convertFloatToInt32BE (const float* source, void* dest | |||
for (int i = numSamples; --i >= 0;) | |||
{ | |||
intData -= destBytesPerSample; | |||
*reinterpret_cast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
*unalignedPointerCast<uint32*> (intData) = ByteOrder::swapIfLittleEndian ((uint32) roundToInt (jlimit (-maxVal, maxVal, maxVal * source[i]))); | |||
} | |||
} | |||
} | |||
@@ -181,10 +181,10 @@ void AudioDataConverters::convertFloatToFloat32LE (const float* source, void* de | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
*reinterpret_cast<float*> (d) = source[i]; | |||
*unalignedPointerCast<float*> (d) = source[i]; | |||
#if JUCE_BIG_ENDIAN | |||
*reinterpret_cast<uint32*> (d) = ByteOrder::swap (*reinterpret_cast<uint32*> (d)); | |||
*unalignedPointerCast<uint32*> (d) = ByteOrder::swap (*unalignedPointerCast<uint32*> (d)); | |||
#endif | |||
d += destBytesPerSample; | |||
@@ -199,10 +199,10 @@ void AudioDataConverters::convertFloatToFloat32BE (const float* source, void* de | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
*reinterpret_cast<float*> (d) = source[i]; | |||
*unalignedPointerCast<float*> (d) = source[i]; | |||
#if JUCE_LITTLE_ENDIAN | |||
*reinterpret_cast<uint32*> (d) = ByteOrder::swap (*reinterpret_cast<uint32*> (d)); | |||
*unalignedPointerCast<uint32*> (d) = ByteOrder::swap (*unalignedPointerCast<uint32*> (d)); | |||
#endif | |||
d += destBytesPerSample; | |||
@@ -219,7 +219,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* source, float* dest | |||
{ | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint16*> (intData)); | |||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*unalignedPointerCast<const uint16*> (intData)); | |||
intData += srcBytesPerSample; | |||
} | |||
} | |||
@@ -230,7 +230,7 @@ void AudioDataConverters::convertInt16LEToFloat (const void* source, float* dest | |||
for (int i = numSamples; --i >= 0;) | |||
{ | |||
intData -= srcBytesPerSample; | |||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint16*> (intData)); | |||
dest[i] = scale * (short) ByteOrder::swapIfBigEndian (*unalignedPointerCast<const uint16*> (intData)); | |||
} | |||
} | |||
} | |||
@@ -244,7 +244,7 @@ void AudioDataConverters::convertInt16BEToFloat (const void* source, float* dest | |||
{ | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint16*> (intData)); | |||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*unalignedPointerCast<const uint16*> (intData)); | |||
intData += srcBytesPerSample; | |||
} | |||
} | |||
@@ -255,7 +255,7 @@ void AudioDataConverters::convertInt16BEToFloat (const void* source, float* dest | |||
for (int i = numSamples; --i >= 0;) | |||
{ | |||
intData -= srcBytesPerSample; | |||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint16*> (intData)); | |||
dest[i] = scale * (short) ByteOrder::swapIfLittleEndian (*unalignedPointerCast<const uint16*> (intData)); | |||
} | |||
} | |||
} | |||
@@ -319,7 +319,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* source, float* dest | |||
{ | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint32*> (intData)); | |||
dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*unalignedPointerCast<const uint32*> (intData)); | |||
intData += srcBytesPerSample; | |||
} | |||
} | |||
@@ -330,7 +330,7 @@ void AudioDataConverters::convertInt32LEToFloat (const void* source, float* dest | |||
for (int i = numSamples; --i >= 0;) | |||
{ | |||
intData -= srcBytesPerSample; | |||
dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*reinterpret_cast<const uint32*> (intData)); | |||
dest[i] = scale * (float) ByteOrder::swapIfBigEndian (*unalignedPointerCast<const uint32*> (intData)); | |||
} | |||
} | |||
} | |||
@@ -344,7 +344,7 @@ void AudioDataConverters::convertInt32BEToFloat (const void* source, float* dest | |||
{ | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint32*> (intData)); | |||
dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*unalignedPointerCast<const uint32*> (intData)); | |||
intData += srcBytesPerSample; | |||
} | |||
} | |||
@@ -355,7 +355,7 @@ void AudioDataConverters::convertInt32BEToFloat (const void* source, float* dest | |||
for (int i = numSamples; --i >= 0;) | |||
{ | |||
intData -= srcBytesPerSample; | |||
dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*reinterpret_cast<const uint32*> (intData)); | |||
dest[i] = scale * (float) ByteOrder::swapIfLittleEndian (*unalignedPointerCast<const uint32*> (intData)); | |||
} | |||
} | |||
} | |||
@@ -366,10 +366,10 @@ void AudioDataConverters::convertFloat32LEToFloat (const void* source, float* de | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
dest[i] = *reinterpret_cast<const float*> (s); | |||
dest[i] = *unalignedPointerCast<const float*> (s); | |||
#if JUCE_BIG_ENDIAN | |||
auto d = reinterpret_cast<uint32*> (dest + i); | |||
auto d = unalignedPointerCast<uint32*> (dest + i); | |||
*d = ByteOrder::swap (*d); | |||
#endif | |||
@@ -383,10 +383,10 @@ void AudioDataConverters::convertFloat32BEToFloat (const void* source, float* de | |||
for (int i = 0; i < numSamples; ++i) | |||
{ | |||
dest[i] = *reinterpret_cast<const float*> (s); | |||
dest[i] = *unalignedPointerCast<const float*> (s); | |||
#if JUCE_LITTLE_ENDIAN | |||
auto d = reinterpret_cast<uint32*> (dest + i); | |||
auto d = unalignedPointerCast<uint32*> (dest + i); | |||
*d = ByteOrder::swap (*d); | |||
#endif | |||
@@ -409,8 +409,8 @@ public: | |||
auto numSamplesToCopy = (size_t) jmin (newNumSamples, size); | |||
auto newChannels = reinterpret_cast<Type**> (newData.get()); | |||
auto newChan = reinterpret_cast<Type*> (newData + channelListSize); | |||
auto newChannels = unalignedPointerCast<Type**> (newData.get()); | |||
auto newChan = unalignedPointerCast<Type*> (newData + channelListSize); | |||
for (int j = 0; j < newNumChannels; ++j) | |||
{ | |||
@@ -442,10 +442,10 @@ public: | |||
{ | |||
allocatedBytes = newTotalBytes; | |||
allocatedData.allocate (newTotalBytes, clearExtraSpace || isClear); | |||
channels = reinterpret_cast<Type**> (allocatedData.get()); | |||
channels = unalignedPointerCast<Type**> (allocatedData.get()); | |||
} | |||
auto* chan = reinterpret_cast<Type*> (allocatedData + channelListSize); | |||
auto* chan = unalignedPointerCast<Type*> (allocatedData + channelListSize); | |||
for (int i = 0; i < newNumChannels; ++i) | |||
{ | |||
@@ -1127,7 +1127,7 @@ private: | |||
void allocateData() | |||
{ | |||
#if (! JUCE_GCC) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 | |||
#if ! JUCE_PROJUCER_LIVE_BUILD && (! JUCE_GCC || (__GNUC__ * 100 + __GNUC_MINOR__) >= 409) | |||
static_assert (alignof (Type) <= detail::maxAlignment, | |||
"AudioBuffer cannot hold types with alignment requirements larger than that guaranteed by malloc"); | |||
#endif | |||
@@ -1142,8 +1142,8 @@ private: | |||
allocatedBytes = (size_t) numChannels * (size_t) size * sizeof (Type) + channelListSize + 32; | |||
allocatedData.malloc (allocatedBytes); | |||
channels = reinterpret_cast<Type**> (allocatedData.get()); | |||
auto chan = reinterpret_cast<Type*> (allocatedData + channelListSize); | |||
channels = unalignedPointerCast<Type**> (allocatedData.get()); | |||
auto chan = unalignedPointerCast<Type*> (allocatedData + channelListSize); | |||
for (int i = 0; i < numChannels; ++i) | |||
{ | |||
@@ -1167,7 +1167,7 @@ private: | |||
else | |||
{ | |||
allocatedData.malloc (numChannels + 1, sizeof (Type*)); | |||
channels = reinterpret_cast<Type**> (allocatedData.get()); | |||
channels = unalignedPointerCast<Type**> (allocatedData.get()); | |||
} | |||
for (int i = 0; i < numChannels; ++i) | |||
@@ -32,7 +32,7 @@ | |||
ID: juce_audio_basics | |||
vendor: juce | |||
version: 6.0.0 | |||
version: 6.0.4 | |||
name: JUCE audio and MIDI data classes | |||
description: Classes for audio buffer manipulation, midi message handling, synthesis, etc. | |||
website: http://www.juce.com/juce | |||
@@ -136,7 +136,7 @@ public: | |||
//============================================================================== | |||
/** Receives events from a MidiKeyboardState object. */ | |||
class Listener | |||
class JUCE_API Listener | |||
{ | |||
public: | |||
//============================================================================== | |||
@@ -401,7 +401,7 @@ public: | |||
/** Returns true if the message is an aftertouch event. | |||
For aftertouch events, use the getNoteNumber() method to find out the key | |||
that it applies to, and getAftertouchValue() to find out the amount. Use | |||
that it applies to, and getAfterTouchValue() to find out the amount. Use | |||
getChannel() to find out the channel. | |||
@see getAftertouchValue, getNoteNumber | |||
@@ -177,8 +177,9 @@ static void addIfNotNull (OwnedArray<AudioIODeviceType>& list, AudioIODeviceType | |||
void AudioDeviceManager::createAudioDeviceTypes (OwnedArray<AudioIODeviceType>& list) | |||
{ | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (false)); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (true)); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::shared)); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::exclusive)); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode::sharedLowLatency)); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO()); | |||
addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio()); | |||
@@ -544,6 +545,8 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup | |||
else if (currentAudioDevice != nullptr) | |||
return {}; | |||
stopDevice(); | |||
if (getCurrentDeviceTypeObject() == nullptr | |||
|| (newSetup.inputDeviceName.isEmpty() && newSetup.outputDeviceName.isEmpty())) | |||
{ | |||
@@ -555,8 +558,6 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup | |||
return {}; | |||
} | |||
stopDevice(); | |||
String error; | |||
if (currentSetup.inputDeviceName != newSetup.inputDeviceName | |||
@@ -42,48 +42,105 @@ void AudioIODeviceType::callDeviceChangeListeners() | |||
} | |||
//============================================================================== | |||
#if ! JUCE_MAC | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } | |||
#if JUCE_MAC | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return new CoreAudioClasses::CoreAudioIODeviceType(); } | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } | |||
#endif | |||
#if ! JUCE_IOS | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; } | |||
#if JUCE_IOS | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return new iOSAudioIODeviceType(); } | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; } | |||
#endif | |||
#if ! (JUCE_WINDOWS && JUCE_WASAPI) | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; } | |||
#if JUCE_WINDOWS && JUCE_WASAPI | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode) | |||
{ | |||
auto windowsVersion = SystemStats::getOperatingSystemType(); | |||
if (windowsVersion < SystemStats::WinVista | |||
|| (WasapiClasses::isLowLatencyMode (deviceMode) && windowsVersion < SystemStats::Windows10)) | |||
return nullptr; | |||
return new WasapiClasses::WASAPIAudioIODeviceType (deviceMode); | |||
} | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool exclusiveMode) | |||
{ | |||
return createAudioIODeviceType_WASAPI (exclusiveMode ? WASAPIDeviceMode::exclusive | |||
: WASAPIDeviceMode::shared); | |||
} | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (WASAPIDeviceMode) { return nullptr; } | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI (bool) { return nullptr; } | |||
#endif | |||
#if ! (JUCE_WINDOWS && JUCE_DIRECTSOUND) | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; } | |||
#if JUCE_WINDOWS && JUCE_DIRECTSOUND | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return new DSoundAudioIODeviceType(); } | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; } | |||
#endif | |||
#if ! (JUCE_WINDOWS && JUCE_ASIO) | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; } | |||
#if JUCE_WINDOWS && JUCE_ASIO | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return new ASIOAudioIODeviceType(); } | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; } | |||
#endif | |||
#if ! (JUCE_LINUX && JUCE_ALSA) | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; } | |||
#if JUCE_LINUX && JUCE_ALSA | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return createAudioIODeviceType_ALSA_PCMDevices(); } | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; } | |||
#endif | |||
#if ! (JUCE_LINUX && JUCE_JACK) | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; } | |||
#if JUCE_LINUX && JUCE_JACK | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return new JackAudioIODeviceType(); } | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; } | |||
#endif | |||
#if ! (JUCE_LINUX && JUCE_BELA) | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; } | |||
#if JUCE_LINUX && JUCE_BELA | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return new BelaAudioIODeviceType(); } | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() { return nullptr; } | |||
#endif | |||
#if ! JUCE_ANDROID | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; } | |||
#if JUCE_ANDROID | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() | |||
{ | |||
#if JUCE_USE_ANDROID_OBOE | |||
if (isOboeAvailable()) | |||
return nullptr; | |||
#endif | |||
#if JUCE_USE_ANDROID_OPENSLES | |||
if (isOpenSLAvailable()) | |||
return nullptr; | |||
#endif | |||
return new AndroidAudioIODeviceType(); | |||
} | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; } | |||
#endif | |||
#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES) | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; } | |||
#if JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() | |||
{ | |||
return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr; | |||
} | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; } | |||
#endif | |||
#if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OBOE) | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; } | |||
#if JUCE_ANDROID && JUCE_USE_ANDROID_OBOE | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() | |||
{ | |||
return isOboeAvailable() ? new OboeAudioIODeviceType() : nullptr; | |||
} | |||
#else | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Oboe() { return nullptr; } | |||
#endif | |||
} // namespace juce |
@@ -149,8 +149,8 @@ public: | |||
static AudioIODeviceType* createAudioIODeviceType_CoreAudio(); | |||
/** Creates an iOS device type if it's available on this platform, or returns null. */ | |||
static AudioIODeviceType* createAudioIODeviceType_iOSAudio(); | |||
/** Creates a WASAPI device type if it's available on this platform, or returns null. */ | |||
static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode); | |||
/** Creates a WASAPI device type in the specified mode if it's available on this platform, or returns null. */ | |||
static AudioIODeviceType* createAudioIODeviceType_WASAPI (WASAPIDeviceMode deviceMode); | |||
/** Creates a DirectSound device type if it's available on this platform, or returns null. */ | |||
static AudioIODeviceType* createAudioIODeviceType_DirectSound(); | |||
/** Creates an ASIO device type if it's available on this platform, or returns null. */ | |||
@@ -168,6 +168,9 @@ public: | |||
/** Creates a Bela device type if it's available on this platform, or returns null. */ | |||
static AudioIODeviceType* createAudioIODeviceType_Bela(); | |||
/** This method has been deprecated. You should call the method which takes a WASAPIDeviceMode instead. */ | |||
JUCE_DEPRECATED (static AudioIODeviceType* createAudioIODeviceType_WASAPI (bool exclusiveMode)); | |||
protected: | |||
explicit AudioIODeviceType (const String& typeName); | |||
@@ -45,6 +45,8 @@ | |||
#include "juce_audio_devices.h" | |||
#include "native/juce_MidiDataConcatenator.h" | |||
//============================================================================== | |||
#if JUCE_MAC | |||
#define Point CarbonDummyPointName | |||
@@ -55,6 +57,9 @@ | |||
#undef Point | |||
#undef Component | |||
#include "native/juce_mac_CoreAudio.cpp" | |||
#include "native/juce_mac_CoreMidi.cpp" | |||
#elif JUCE_IOS | |||
#import <AudioToolbox/AudioToolbox.h> | |||
#import <AVFoundation/AVFoundation.h> | |||
@@ -64,13 +69,21 @@ | |||
#import <CoreMIDI/MIDINetworkSession.h> | |||
#endif | |||
#include "native/juce_ios_Audio.cpp" | |||
#include "native/juce_mac_CoreMidi.cpp" | |||
//============================================================================== | |||
#elif JUCE_WINDOWS | |||
#if JUCE_WASAPI | |||
#include <mmreg.h> | |||
#include "native/juce_win32_WASAPI.cpp" | |||
#endif | |||
#if JUCE_USE_WINRT_MIDI && JUCE_MSVC | |||
#if JUCE_DIRECTSOUND | |||
#include "native/juce_win32_DirectSound.cpp" | |||
#endif | |||
#if JUCE_USE_WINRT_MIDI && (JUCE_MSVC || JUCE_CLANG) | |||
/* If you cannot find any of the header files below then you are probably | |||
attempting to use the Windows 10 Bluetooth Low Energy API. For this to work you | |||
need to install version 10.0.14393.0 of the Windows Standalone SDK and you may | |||
@@ -93,6 +106,8 @@ | |||
JUCE_END_IGNORE_WARNINGS_MSVC | |||
#endif | |||
#include "native/juce_win32_Midi.cpp" | |||
#if JUCE_ASIO | |||
/* This is very frustrating - we only need to use a handful of definitions from | |||
a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy | |||
@@ -114,6 +129,7 @@ | |||
needed - so to simplify things, you could just copy these into your JUCE directory). | |||
*/ | |||
#include <iasiodrv.h> | |||
#include "native/juce_win32_ASIO.cpp" | |||
#endif | |||
//============================================================================== | |||
@@ -128,6 +144,7 @@ | |||
just set the JUCE_ALSA flag to 0. | |||
*/ | |||
#include <alsa/asoundlib.h> | |||
#include "native/juce_linux_ALSA.cpp" | |||
#endif | |||
#if JUCE_JACK | |||
@@ -140,6 +157,7 @@ | |||
JUCE with low latency audio support, just set the JUCE_JACK flag to 0. | |||
*/ | |||
#include <jack/jack.h> | |||
#include "native/juce_linux_JackAudio.cpp" | |||
#endif | |||
#if JUCE_BELA | |||
@@ -149,89 +167,18 @@ | |||
*/ | |||
#include <Bela.h> | |||
#include <Midi.h> | |||
#include "native/juce_linux_Bela.cpp" | |||
#endif | |||
#undef SIZEOF | |||
//============================================================================== | |||
#elif JUCE_ANDROID | |||
#if JUCE_USE_ANDROID_OPENSLES | |||
#include <SLES/OpenSLES.h> | |||
#include <SLES/OpenSLES_Android.h> | |||
#include <SLES/OpenSLES_AndroidConfiguration.h> | |||
#endif | |||
#if JUCE_USE_ANDROID_OBOE | |||
#if JUCE_USE_ANDROID_OPENSLES | |||
#error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES" | |||
#endif | |||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter", | |||
"-Wzero-as-null-pointer-constant", | |||
"-Winconsistent-missing-destructor-override", | |||
"-Wshadow-field-in-constructor", | |||
"-Wshadow-field") | |||
#include <oboe/Oboe.h> | |||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
#endif | |||
#endif | |||
#include "audio_io/juce_AudioDeviceManager.cpp" | |||
#include "audio_io/juce_AudioIODevice.cpp" | |||
#include "audio_io/juce_AudioIODeviceType.cpp" | |||
#include "midi_io/juce_MidiMessageCollector.cpp" | |||
#include "midi_io/juce_MidiDevices.cpp" | |||
#include "sources/juce_AudioSourcePlayer.cpp" | |||
#include "sources/juce_AudioTransportSource.cpp" | |||
#include "native/juce_MidiDataConcatenator.h" | |||
//============================================================================== | |||
#if JUCE_MAC | |||
#include "native/juce_mac_CoreAudio.cpp" | |||
#include "native/juce_mac_CoreMidi.cpp" | |||
//============================================================================== | |||
#elif JUCE_IOS | |||
#include "native/juce_ios_Audio.cpp" | |||
#include "native/juce_mac_CoreMidi.cpp" | |||
//============================================================================== | |||
#elif JUCE_WINDOWS | |||
#if JUCE_WASAPI | |||
#include "native/juce_win32_WASAPI.cpp" | |||
#endif | |||
#if JUCE_DIRECTSOUND | |||
#include "native/juce_win32_DirectSound.cpp" | |||
#endif | |||
#include "native/juce_win32_Midi.cpp" | |||
#if JUCE_ASIO | |||
#include "native/juce_win32_ASIO.cpp" | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_LINUX | |||
#if JUCE_ALSA | |||
#include "native/juce_linux_ALSA.cpp" | |||
#endif | |||
#if JUCE_JACK | |||
#include "native/juce_linux_JackAudio.cpp" | |||
#endif | |||
#if JUCE_BELA | |||
#include "native/juce_linux_Bela.cpp" | |||
#else | |||
#if ! JUCE_BELA | |||
#include "native/juce_linux_Midi.cpp" | |||
#endif | |||
//============================================================================== | |||
#elif JUCE_ANDROID | |||
#include "native/juce_android_Audio.cpp" | |||
#include "native/juce_android_Midi.cpp" | |||
@@ -239,10 +186,25 @@ | |||
#include "native/juce_android_HighPerformanceAudioHelpers.h" | |||
#if JUCE_USE_ANDROID_OPENSLES | |||
#include <SLES/OpenSLES.h> | |||
#include <SLES/OpenSLES_Android.h> | |||
#include <SLES/OpenSLES_AndroidConfiguration.h> | |||
#include "native/juce_android_OpenSL.cpp" | |||
#endif | |||
#if JUCE_USE_ANDROID_OBOE | |||
#if JUCE_USE_ANDROID_OPENSLES | |||
#error "Oboe cannot be enabled at the same time as openSL! Please disable JUCE_USE_ANDROID_OPENSLES" | |||
#endif | |||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunused-parameter", | |||
"-Wzero-as-null-pointer-constant", | |||
"-Winconsistent-missing-destructor-override", | |||
"-Wshadow-field-in-constructor", | |||
"-Wshadow-field") | |||
#include <oboe/Oboe.h> | |||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | |||
#include "native/juce_android_Oboe.cpp" | |||
#endif | |||
#endif | |||
@@ -259,3 +221,11 @@ namespace juce | |||
bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; } | |||
} | |||
#endif | |||
#include "audio_io/juce_AudioDeviceManager.cpp" | |||
#include "audio_io/juce_AudioIODevice.cpp" | |||
#include "audio_io/juce_AudioIODeviceType.cpp" | |||
#include "midi_io/juce_MidiMessageCollector.cpp" | |||
#include "midi_io/juce_MidiDevices.cpp" | |||
#include "sources/juce_AudioSourcePlayer.cpp" | |||
#include "sources/juce_AudioTransportSource.cpp" |
@@ -32,7 +32,7 @@ | |||
ID: juce_audio_devices | |||
vendor: juce | |||
version: 6.0.0 | |||
version: 6.0.4 | |||
name: JUCE audio and MIDI I/O device classes | |||
description: Classes to play and record from audio and MIDI I/O devices | |||
website: http://www.juce.com/juce | |||
@@ -88,21 +88,12 @@ | |||
#endif | |||
/** Config: JUCE_WASAPI | |||
Enables WASAPI audio devices (Windows Vista and above). See also the | |||
JUCE_WASAPI_EXCLUSIVE flag. | |||
Enables WASAPI audio devices (Windows Vista and above). | |||
*/ | |||
#ifndef JUCE_WASAPI | |||
#define JUCE_WASAPI 1 | |||
#endif | |||
/** Config: JUCE_WASAPI_EXCLUSIVE | |||
Enables WASAPI audio devices in exclusive mode (Windows Vista and above). | |||
*/ | |||
#ifndef JUCE_WASAPI_EXCLUSIVE | |||
#define JUCE_WASAPI_EXCLUSIVE 0 | |||
#endif | |||
/** Config: JUCE_DIRECTSOUND | |||
Enables DirectSound audio (MS Windows only). | |||
*/ | |||
@@ -174,6 +165,22 @@ | |||
//============================================================================== | |||
#include "midi_io/juce_MidiDevices.h" | |||
#include "midi_io/juce_MidiMessageCollector.h" | |||
namespace juce | |||
{ | |||
/** Available modes for the WASAPI audio device. | |||
Pass one of these to the AudioIODeviceType::createAudioIODeviceType_WASAPI() | |||
method to create a WASAPI AudioIODeviceType object in this mode. | |||
*/ | |||
enum class WASAPIDeviceMode | |||
{ | |||
shared, | |||
exclusive, | |||
sharedLowLatency | |||
}; | |||
} | |||
#include "audio_io/juce_AudioIODevice.h" | |||
#include "audio_io/juce_AudioIODeviceType.h" | |||
#include "audio_io/juce_SystemAudioVolume.h" | |||
@@ -164,12 +164,16 @@ public: | |||
/** Deprecated. */ | |||
static std::unique_ptr<MidiInput> openDevice (int, MidiInputCallback*); | |||
/** @internal */ | |||
class Pimpl; | |||
private: | |||
//============================================================================== | |||
explicit MidiInput (const String&, const String&); | |||
MidiDeviceInfo deviceInfo; | |||
void* internal = nullptr; | |||
std::unique_ptr<Pimpl> internal; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput) | |||
}; | |||
@@ -350,6 +354,9 @@ public: | |||
/** Deprecated. */ | |||
static std::unique_ptr<MidiOutput> openDevice (int); | |||
/** @internal */ | |||
class Pimpl; | |||
private: | |||
//============================================================================== | |||
struct PendingMessage | |||
@@ -368,7 +375,9 @@ private: | |||
void run() override; | |||
MidiDeviceInfo deviceInfo; | |||
void* internal = nullptr; | |||
std::unique_ptr<Pimpl> internal; | |||
CriticalSection lock; | |||
PendingMessage* firstMessage = nullptr; | |||
@@ -1299,9 +1299,4 @@ AudioIODeviceType* createAudioIODeviceType_ALSA_PCMDevices() | |||
return new ALSAAudioIODeviceType (false, "ALSA"); | |||
} | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() | |||
{ | |||
return createAudioIODeviceType_ALSA_PCMDevices(); | |||
} | |||
} // namespace juce |
@@ -24,12 +24,12 @@ namespace juce | |||
{ | |||
//============================================================================== | |||
class BelaMidiInput | |||
class MidiInput::Pimpl | |||
{ | |||
public: | |||
static Array<BelaMidiInput*> midiInputs; | |||
static Array<Pimpl*> midiInputs; | |||
BelaMidiInput (const String& port, MidiInput* input, MidiInputCallback* callback) | |||
Pimpl (const String& port, MidiInput* input, MidiInputCallback* callback) | |||
: midiInput (input), midiPort (port), midiCallback (callback) | |||
{ | |||
jassert (midiCallback != nullptr); | |||
@@ -38,7 +38,7 @@ public: | |||
buffer.resize (32); | |||
} | |||
~BelaMidiInput() | |||
~Pimpl() | |||
{ | |||
stop(); | |||
midiInputs.removeAllInstancesOf (this); | |||
@@ -76,7 +76,7 @@ public: | |||
} | |||
if (receivedBytes > 0) | |||
pushMidiData (receivedBytes); | |||
pushMidiData ((int) receivedBytes); | |||
} | |||
static Array<MidiDeviceInfo> getDevices (bool input) | |||
@@ -141,7 +141,7 @@ private: | |||
snd_rawmidi_info_t* info; | |||
snd_rawmidi_info_alloca (&info); | |||
snd_rawmidi_info_set_device (info, device); | |||
snd_rawmidi_info_set_device (info, (unsigned int) device); | |||
snd_rawmidi_info_set_stream (info, input ? SND_RAWMIDI_STREAM_INPUT | |||
: SND_RAWMIDI_STREAM_OUTPUT); | |||
@@ -173,10 +173,10 @@ private: | |||
Midi midi; | |||
MidiDataConcatenator concatenator { 512 }; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaMidiInput) | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl) | |||
}; | |||
Array<BelaMidiInput*> BelaMidiInput::midiInputs; | |||
Array<MidiInput::Pimpl*> MidiInput::Pimpl::midiInputs; | |||
//============================================================================== | |||
@@ -366,7 +366,7 @@ public: | |||
String getLastError() override { return lastError; } | |||
//============================================================================== | |||
int getCurrentBufferSizeSamples() override { return actualBufferSize; } | |||
int getCurrentBufferSizeSamples() override { return (int) actualBufferSize; } | |||
double getCurrentSampleRate() override { return 44100.0; } | |||
int getCurrentBitDepth() override { return 16; } | |||
BigInteger getActiveOutputChannels() const override { BigInteger b; b.setRange (0, actualNumberOfOutputs, true); return b; } | |||
@@ -384,8 +384,8 @@ private: | |||
bool setup (BelaContext& context) | |||
{ | |||
actualBufferSize = context.audioFrames; | |||
actualNumberOfInputs = context.audioInChannels + context.analogInChannels; | |||
actualNumberOfOutputs = context.audioOutChannels + context.analogOutChannels; | |||
actualNumberOfInputs = (int) (context.audioInChannels + context.analogInChannels); | |||
actualNumberOfOutputs = (int) (context.audioOutChannels + context.analogOutChannels); | |||
isBelaOpen = true; | |||
firstCallback = true; | |||
@@ -405,7 +405,7 @@ private: | |||
ScopedLock lock (callbackLock); | |||
// Check for and process and midi | |||
for (auto midiInput : BelaMidiInput::midiInputs) | |||
for (auto midiInput : MidiInput::Pimpl::midiInputs) | |||
midiInput->poll(); | |||
if (callback != nullptr) | |||
@@ -413,27 +413,29 @@ private: | |||
jassert (context.audioFrames <= actualBufferSize); | |||
jassert ((context.flags & BELA_FLAG_INTERLEAVED) == 0); | |||
using Frames = decltype (context.audioFrames); | |||
// Setup channelInBuffers | |||
for (int ch = 0; ch < actualNumberOfInputs; ++ch) | |||
{ | |||
if (ch < analogChannelStart) | |||
channelInBuffer[ch] = &context.audioIn[ch * context.audioFrames]; | |||
channelInBuffer[ch] = &context.audioIn[(Frames) ch * context.audioFrames]; | |||
else | |||
channelInBuffer[ch] = &context.analogIn[(ch - analogChannelStart) * context.analogFrames]; | |||
channelInBuffer[ch] = &context.analogIn[(Frames) (ch - analogChannelStart) * context.analogFrames]; | |||
} | |||
// Setup channelOutBuffers | |||
for (int ch = 0; ch < actualNumberOfOutputs; ++ch) | |||
{ | |||
if (ch < analogChannelStart) | |||
channelOutBuffer[ch] = &context.audioOut[ch * context.audioFrames]; | |||
channelOutBuffer[ch] = &context.audioOut[(Frames) ch * context.audioFrames]; | |||
else | |||
channelOutBuffer[ch] = &context.analogOut[(ch - analogChannelStart) * context.audioFrames]; | |||
channelOutBuffer[ch] = &context.analogOut[(Frames) (ch - analogChannelStart) * context.audioFrames]; | |||
} | |||
callback->audioDeviceIOCallback (channelInBuffer.getData(), actualNumberOfInputs, | |||
channelOutBuffer.getData(), actualNumberOfOutputs, | |||
context.audioFrames); | |||
(int) context.audioFrames); | |||
} | |||
} | |||
@@ -521,25 +523,19 @@ struct BelaAudioIODeviceType : public AudioIODeviceType | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaAudioIODeviceType) | |||
}; | |||
//============================================================================== | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela() | |||
{ | |||
return new BelaAudioIODeviceType(); | |||
} | |||
//============================================================================== | |||
MidiInput::MidiInput (const String& deviceName, const String& deviceID) | |||
: deviceInfo (deviceName, deviceID) | |||
{ | |||
} | |||
MidiInput::~MidiInput() { delete static_cast<BelaMidiInput*> (internal); } | |||
void MidiInput::start() { static_cast<BelaMidiInput*> (internal)->start(); } | |||
void MidiInput::stop() { static_cast<BelaMidiInput*> (internal)->stop(); } | |||
MidiInput::~MidiInput() = default; | |||
void MidiInput::start() { internal->start(); } | |||
void MidiInput::stop() { internal->stop(); } | |||
Array<MidiDeviceInfo> MidiInput::getAvailableDevices() | |||
{ | |||
return BelaMidiInput::getDevices (true); | |||
return Pimpl::getDevices (true); | |||
} | |||
MidiDeviceInfo MidiInput::getDefaultDevice() | |||
@@ -553,7 +549,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier | |||
return {}; | |||
std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceIdentifier, deviceIdentifier)); | |||
midiInput->internal = new BelaMidiInput (deviceIdentifier, midiInput.get(), callback); | |||
midiInput->internal = std::make_unique<Pimpl> (deviceIdentifier, midiInput.get(), callback); | |||
return midiInput; | |||
} | |||
@@ -587,7 +583,8 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (int index, MidiInputCallback* | |||
//============================================================================== | |||
// TODO: Add Bela MidiOutput support | |||
MidiOutput::~MidiOutput() {} | |||
class MidiOutput::Pimpl {}; | |||
MidiOutput::~MidiOutput() = default; | |||
void MidiOutput::sendMessageNow (const MidiMessage&) {} | |||
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() { return {}; } | |||
MidiDeviceInfo MidiOutput::getDefaultDevice() { return {}; } | |||
@@ -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,30 +48,35 @@ 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; \ | |||
} | |||
//============================================================================== | |||
JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status, ...), (client_name, options, status)); | |||
JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client)); | |||
JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client)); | |||
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_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)); | |||
JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func)); | |||
JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg)); | |||
JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags)); | |||
JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port)); | |||
JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port)); | |||
JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg)); | |||
JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id)); | |||
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 (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status, ...), (client_name, options, status)) | |||
JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client)) | |||
JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client)) | |||
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)) | |||
JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func)) | |||
JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg)) | |||
JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags)) | |||
JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port)) | |||
JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port)) | |||
JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg)) | |||
JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id)) | |||
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) | |||
@@ -179,10 +186,10 @@ public: | |||
const StringArray inputChannels (getInputChannelNames()); | |||
for (int i = 0; i < inputChannels.size(); ++i) | |||
{ | |||
String inputName; | |||
inputName << "in_" << ++totalNumberOfInputChannels; | |||
String inputChannelName; | |||
inputChannelName << "in_" << ++totalNumberOfInputChannels; | |||
inputPorts.add (juce::jack_port_register (client, inputName.toUTF8(), | |||
inputPorts.add (juce::jack_port_register (client, inputChannelName.toUTF8(), | |||
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)); | |||
} | |||
@@ -190,10 +197,10 @@ public: | |||
const StringArray outputChannels (getOutputChannelNames()); | |||
for (int i = 0; i < outputChannels.size(); ++i) | |||
{ | |||
String outputName; | |||
outputName << "out_" << ++totalNumberOfOutputChannels; | |||
String outputChannelName; | |||
outputChannelName << "out_" << ++totalNumberOfOutputChannels; | |||
outputPorts.add (juce::jack_port_register (client, outputName.toUTF8(), | |||
outputPorts.add (juce::jack_port_register (client, outputChannelName.toUTF8(), | |||
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)); | |||
} | |||
@@ -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 | |||
{ | |||
@@ -241,15 +248,29 @@ public: | |||
Array<int> sizes; | |||
if (client != nullptr) | |||
sizes.add (juce::jack_get_buffer_size (client)); | |||
sizes.add (static_cast<int> (juce::jack_get_buffer_size (client))); | |||
return sizes; | |||
} | |||
int getDefaultBufferSize() override { return getCurrentBufferSizeSamples(); } | |||
int getCurrentBufferSizeSamples() override { return client != nullptr ? juce::jack_get_buffer_size (client) : 0; } | |||
double getCurrentSampleRate() override { return client != nullptr ? juce::jack_get_sample_rate (client) : 0; } | |||
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), 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), 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,14 +469,14 @@ private: | |||
else | |||
{ | |||
for (int i = 0; i < numActiveOutChans; ++i) | |||
zeromem (outChans[i], sizeof (float) * numSamples); | |||
zeromem (outChans[i], static_cast<size_t> (numSamples) * sizeof (float)); | |||
} | |||
} | |||
static int processCallback (jack_nframes_t nframes, void* callbackArgument) | |||
{ | |||
if (callbackArgument != nullptr) | |||
((JackAudioIODevice*) callbackArgument)->process (nframes); | |||
((JackAudioIODevice*) callbackArgument)->process (static_cast<int> (nframes)); | |||
return 0; | |||
} | |||
@@ -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,81 +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); | |||
} | |||
@@ -580,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; | |||
} | |||
@@ -595,34 +654,17 @@ 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() | |||
{ | |||
return new JackAudioIODeviceType(); | |||
} | |||
} // namespace juce |
@@ -25,10 +25,6 @@ namespace juce | |||
#if JUCE_ALSA | |||
//============================================================================== | |||
namespace | |||
{ | |||
//============================================================================== | |||
class AlsaClient : public ReferenceCountedObject | |||
{ | |||
@@ -453,9 +449,23 @@ static AlsaClient::Port* iterateMidiDevices (bool forInput, | |||
return port; | |||
} | |||
} // namespace | |||
struct AlsaPortPtr | |||
{ | |||
explicit AlsaPortPtr (AlsaClient::Port* p) | |||
: ptr (p) {} | |||
~AlsaPortPtr() noexcept { AlsaClient::getInstance()->deletePort (ptr); } | |||
AlsaClient::Port* ptr = nullptr; | |||
}; | |||
//============================================================================== | |||
class MidiInput::Pimpl : public AlsaPortPtr | |||
{ | |||
public: | |||
using AlsaPortPtr::AlsaPortPtr; | |||
}; | |||
Array<MidiDeviceInfo> MidiInput::getAvailableDevices() | |||
{ | |||
Array<MidiDeviceInfo> devices; | |||
@@ -485,7 +495,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier | |||
std::unique_ptr<MidiInput> midiInput (new MidiInput (port->portName, deviceIdentifier)); | |||
port->setupInput (midiInput.get(), callback); | |||
midiInput->internal = port; | |||
midiInput->internal = std::make_unique<Pimpl> (port); | |||
return midiInput; | |||
} | |||
@@ -501,7 +511,7 @@ std::unique_ptr<MidiInput> MidiInput::createNewDevice (const String& deviceName, | |||
std::unique_ptr<MidiInput> midiInput (new MidiInput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId))); | |||
port->setupInput (midiInput.get(), callback); | |||
midiInput->internal = port; | |||
midiInput->internal = std::make_unique<Pimpl> (port); | |||
return midiInput; | |||
} | |||
@@ -536,20 +546,25 @@ MidiInput::MidiInput (const String& deviceName, const String& deviceIdentifier) | |||
MidiInput::~MidiInput() | |||
{ | |||
stop(); | |||
AlsaClient::getInstance()->deletePort (static_cast<AlsaClient::Port*> (internal)); | |||
} | |||
void MidiInput::start() | |||
{ | |||
static_cast<AlsaClient::Port*> (internal)->enableCallback (true); | |||
internal->ptr->enableCallback (true); | |||
} | |||
void MidiInput::stop() | |||
{ | |||
static_cast<AlsaClient::Port*> (internal)->enableCallback (false); | |||
internal->ptr->enableCallback (false); | |||
} | |||
//============================================================================== | |||
class MidiOutput::Pimpl : public AlsaPortPtr | |||
{ | |||
public: | |||
using AlsaPortPtr::AlsaPortPtr; | |||
}; | |||
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() | |||
{ | |||
Array<MidiDeviceInfo> devices; | |||
@@ -577,7 +592,7 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (const String& deviceIdentifi | |||
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (port->portName, deviceIdentifier)); | |||
port->setupOutput(); | |||
midiOutput->internal = port; | |||
midiOutput->internal = std::make_unique<Pimpl> (port); | |||
return midiOutput; | |||
} | |||
@@ -593,7 +608,7 @@ std::unique_ptr<MidiOutput> MidiOutput::createNewDevice (const String& deviceNam | |||
std::unique_ptr<MidiOutput> midiOutput (new MidiOutput (deviceName, getFormattedPortIdentifier (client->getId(), port->portId))); | |||
port->setupOutput(); | |||
midiOutput->internal = port; | |||
midiOutput->internal = std::make_unique<Pimpl> (port); | |||
return midiOutput; | |||
} | |||
@@ -623,17 +638,18 @@ std::unique_ptr<MidiOutput> MidiOutput::openDevice (int index) | |||
MidiOutput::~MidiOutput() | |||
{ | |||
stopBackgroundThread(); | |||
AlsaClient::getInstance()->deletePort (static_cast<AlsaClient::Port*> (internal)); | |||
} | |||
void MidiOutput::sendMessageNow (const MidiMessage& message) | |||
{ | |||
static_cast<AlsaClient::Port*> (internal)->sendMessageNow (message); | |||
internal->ptr->sendMessageNow (message); | |||
} | |||
//============================================================================== | |||
#else | |||
class MidiInput::Pimpl {}; | |||
// (These are just stub functions if ALSA is unavailable...) | |||
MidiInput::MidiInput (const String& deviceName, const String& deviceID) | |||
: deviceInfo (deviceName, deviceID) | |||
@@ -651,6 +667,8 @@ StringArray MidiInput::getDevices() | |||
int MidiInput::getDefaultDeviceIndex() { return 0;} | |||
std::unique_ptr<MidiInput> MidiInput::openDevice (int, MidiInputCallback*) { return {}; } | |||
class MidiOutput::Pimpl {}; | |||
MidiOutput::~MidiOutput() {} | |||
void MidiOutput::sendMessageNow (const MidiMessage&) {} | |||
Array<MidiDeviceInfo> MidiOutput::getAvailableDevices() { return {}; } | |||
@@ -2234,12 +2234,6 @@ private: | |||
}; | |||
//============================================================================== | |||
AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() | |||
{ | |||
return new CoreAudioClasses::CoreAudioIODeviceType(); | |||
} | |||
#undef JUCE_COREAUDIOLOG | |||
} // namespace juce |
@@ -393,6 +393,12 @@ namespace CoreMidiHelpers | |||
} | |||
} | |||
class MidiInput::Pimpl : public CoreMidiHelpers::MidiPortAndCallback | |||
{ | |||
public: | |||
using MidiPortAndCallback::MidiPortAndCallback; | |||
}; | |||
//============================================================================== | |||
Array<MidiDeviceInfo> MidiInput::getAvailableDevices() | |||
{ | |||
@@ -424,7 +430,7 @@ std::unique_ptr<MidiInput> MidiInput::openDevice (const String& deviceIdentifier | |||
if (CHECK_ERROR (MIDIObjectGetStringProperty (endpoint, kMIDIPropertyName, &cfName.cfString))) | |||
{ | |||
MIDIPortRef port; | |||
auto mpc = std::make_unique<MidiPortAndCallback> (*callback); | |||
auto mpc = std::make_unique<Pimpl> (*callback); | |||