upgrade to JUCE 5.4.3. Remove (probably) unused JUCE modules. Remove VST2 target (it's been end-of-life'd by Steinberg and by JUCE)
This commit is contained in:
@ -23,14 +23,6 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
AudioDeviceManager::AudioDeviceSetup::AudioDeviceSetup()
|
||||
: sampleRate (0),
|
||||
bufferSize (0),
|
||||
useDefaultInputChannels (true),
|
||||
useDefaultOutputChannels (true)
|
||||
{
|
||||
}
|
||||
|
||||
bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const
|
||||
{
|
||||
return outputDeviceName == other.outputDeviceName
|
||||
@ -132,10 +124,36 @@ void AudioDeviceManager::audioDeviceListChanged()
|
||||
{
|
||||
if (currentAudioDevice != nullptr)
|
||||
{
|
||||
currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
|
||||
currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
|
||||
currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
|
||||
currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
|
||||
auto isCurrentDeviceStillAvailable = [&]
|
||||
{
|
||||
for (auto* dt : availableDeviceTypes)
|
||||
if (currentAudioDevice->getTypeName() == dt->getTypeName())
|
||||
for (auto& dn : dt->getDeviceNames())
|
||||
if (currentAudioDevice->getName() == dn)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (! isCurrentDeviceStillAvailable())
|
||||
{
|
||||
closeAudioDevice();
|
||||
|
||||
std::unique_ptr<XmlElement> e (createStateXml());
|
||||
|
||||
if (e == nullptr)
|
||||
initialiseDefault (preferredDeviceName, ¤tSetup);
|
||||
else
|
||||
initialiseFromXML (*e, true, preferredDeviceName, ¤tSetup);
|
||||
}
|
||||
|
||||
if (currentAudioDevice != nullptr)
|
||||
{
|
||||
currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate();
|
||||
currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples();
|
||||
currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels();
|
||||
currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels();
|
||||
}
|
||||
}
|
||||
|
||||
sendChangeMessage();
|
||||
@ -197,12 +215,13 @@ String AudioDeviceManager::initialise (const int numInputChannelsNeeded,
|
||||
|
||||
numInputChansNeeded = numInputChannelsNeeded;
|
||||
numOutputChansNeeded = numOutputChannelsNeeded;
|
||||
preferredDeviceName = preferredDefaultDeviceName;
|
||||
|
||||
if (xml != nullptr && xml->hasTagName ("DEVICESETUP"))
|
||||
return initialiseFromXML (*xml, selectDefaultDeviceOnFailure,
|
||||
preferredDefaultDeviceName, preferredSetupOptions);
|
||||
preferredDeviceName, preferredSetupOptions);
|
||||
|
||||
return initialiseDefault (preferredDefaultDeviceName, preferredSetupOptions);
|
||||
return initialiseDefault (preferredDeviceName, preferredSetupOptions);
|
||||
}
|
||||
|
||||
String AudioDeviceManager::initialiseDefault (const String& preferredDefaultDeviceName,
|
||||
@ -367,6 +386,11 @@ AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const
|
||||
return {};
|
||||
}
|
||||
|
||||
AudioDeviceManager::AudioDeviceSetup AudioDeviceManager::getAudioDeviceSetup() const
|
||||
{
|
||||
return currentSetup;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup) const
|
||||
{
|
||||
setup = currentSetup;
|
||||
@ -428,16 +452,15 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
|
||||
|
||||
stopDevice();
|
||||
|
||||
if (! newSetup.useDefaultInputChannels) numInputChansNeeded = newSetup.inputChannels.countNumberOfSetBits();
|
||||
if (! newSetup.useDefaultOutputChannels) numOutputChansNeeded = newSetup.outputChannels.countNumberOfSetBits();
|
||||
if (! newSetup.useDefaultInputChannels)
|
||||
numInputChansNeeded = newSetup.inputChannels.countNumberOfSetBits();
|
||||
|
||||
auto newInputDeviceName (numInputChansNeeded == 0 ? String() : newSetup.inputDeviceName);
|
||||
auto newOutputDeviceName (numOutputChansNeeded == 0 ? String() : newSetup.outputDeviceName);
|
||||
if (! newSetup.useDefaultOutputChannels)
|
||||
numOutputChansNeeded = newSetup.outputChannels.countNumberOfSetBits();
|
||||
|
||||
String error;
|
||||
auto* type = getCurrentDeviceTypeObject();
|
||||
|
||||
if (type == nullptr || (newInputDeviceName.isEmpty() && newOutputDeviceName.isEmpty()))
|
||||
if (type == nullptr)
|
||||
{
|
||||
deleteCurrentDevice();
|
||||
|
||||
@ -447,20 +470,22 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
|
||||
return {};
|
||||
}
|
||||
|
||||
if (currentSetup.inputDeviceName != newInputDeviceName
|
||||
|| currentSetup.outputDeviceName != newOutputDeviceName
|
||||
|| currentAudioDevice == nullptr)
|
||||
String error;
|
||||
|
||||
if (currentSetup.inputDeviceName != newSetup.inputDeviceName
|
||||
|| currentSetup.outputDeviceName != newSetup.outputDeviceName
|
||||
|| currentAudioDevice == nullptr)
|
||||
{
|
||||
deleteCurrentDevice();
|
||||
scanDevicesIfNeeded();
|
||||
|
||||
if (newOutputDeviceName.isNotEmpty() && ! deviceListContains (type, false, newOutputDeviceName))
|
||||
return "No such device: " + newOutputDeviceName;
|
||||
if (newSetup.outputDeviceName.isNotEmpty() && ! deviceListContains (type, false, newSetup.outputDeviceName))
|
||||
return "No such device: " + newSetup.outputDeviceName;
|
||||
|
||||
if (newInputDeviceName.isNotEmpty() && ! deviceListContains (type, true, newInputDeviceName))
|
||||
return "No such device: " + newInputDeviceName;
|
||||
if (newSetup.inputDeviceName.isNotEmpty() && ! deviceListContains (type, true, newSetup.inputDeviceName))
|
||||
return "No such device: " + newSetup.inputDeviceName;
|
||||
|
||||
currentAudioDevice.reset (type->createDevice (newOutputDeviceName, newInputDeviceName));
|
||||
currentAudioDevice.reset (type->createDevice (newSetup.outputDeviceName, newSetup.inputDeviceName));
|
||||
|
||||
if (currentAudioDevice == nullptr)
|
||||
error = "Can't open the audio device!\n\n"
|
||||
@ -487,15 +512,26 @@ String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup
|
||||
outputChannels.setRange (0, numOutputChansNeeded, true);
|
||||
}
|
||||
|
||||
if (newInputDeviceName.isEmpty()) inputChannels.clear();
|
||||
if (newOutputDeviceName.isEmpty()) outputChannels.clear();
|
||||
if (newSetup.inputDeviceName.isEmpty()) inputChannels.clear();
|
||||
if (newSetup.outputDeviceName.isEmpty()) outputChannels.clear();
|
||||
}
|
||||
|
||||
if (! newSetup.useDefaultInputChannels) inputChannels = newSetup.inputChannels;
|
||||
if (! newSetup.useDefaultOutputChannels) outputChannels = newSetup.outputChannels;
|
||||
if (! newSetup.useDefaultInputChannels)
|
||||
inputChannels = newSetup.inputChannels;
|
||||
|
||||
if (! newSetup.useDefaultOutputChannels)
|
||||
outputChannels = newSetup.outputChannels;
|
||||
|
||||
currentSetup = newSetup;
|
||||
|
||||
if (inputChannels.isZero() && outputChannels.isZero())
|
||||
{
|
||||
if (treatAsChosenDevice)
|
||||
updateXml();
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
currentSetup.sampleRate = chooseBestSampleRate (newSetup.sampleRate);
|
||||
currentSetup.bufferSize = chooseBestBufferSize (newSetup.bufferSize);
|
||||
|
||||
@ -582,7 +618,7 @@ void AudioDeviceManager::closeAudioDevice()
|
||||
{
|
||||
stopDevice();
|
||||
currentAudioDevice.reset();
|
||||
cpuUsageMs = 0;
|
||||
loadMeasurer.reset();
|
||||
}
|
||||
|
||||
void AudioDeviceManager::restartLastAudioDevice()
|
||||
@ -694,7 +730,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat
|
||||
|
||||
if (callbacks.size() > 0)
|
||||
{
|
||||
auto callbackStartTime = Time::getMillisecondCounterHiRes();
|
||||
AudioProcessLoadMeasurer::ScopedTimer timer (loadMeasurer);
|
||||
|
||||
tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true);
|
||||
|
||||
@ -716,13 +752,6 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat
|
||||
dst[j] += src[j];
|
||||
}
|
||||
}
|
||||
|
||||
auto msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime;
|
||||
const double filterAmount = 0.2;
|
||||
cpuUsageMs += filterAmount * (msTaken - cpuUsageMs);
|
||||
|
||||
if (msTaken > msPerBlock)
|
||||
++xruns;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -748,17 +777,8 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat
|
||||
|
||||
void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device)
|
||||
{
|
||||
cpuUsageMs = 0;
|
||||
xruns = 0;
|
||||
|
||||
auto sampleRate = device->getCurrentSampleRate();
|
||||
auto blockSize = device->getCurrentBufferSizeSamples();
|
||||
|
||||
if (sampleRate > 0.0 && blockSize > 0)
|
||||
{
|
||||
msPerBlock = 1000.0 * blockSize / sampleRate;
|
||||
timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0;
|
||||
}
|
||||
loadMeasurer.reset (device->getCurrentSampleRate(),
|
||||
device->getCurrentBufferSizeSamples());
|
||||
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
@ -772,13 +792,12 @@ void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device
|
||||
|
||||
void AudioDeviceManager::audioDeviceStoppedInt()
|
||||
{
|
||||
cpuUsageMs = 0;
|
||||
timeToCpuScale = 0;
|
||||
xruns = 0;
|
||||
sendChangeMessage();
|
||||
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
|
||||
loadMeasurer.reset();
|
||||
|
||||
for (int i = callbacks.size(); --i >= 0;)
|
||||
callbacks.getUnchecked(i)->audioDeviceStopped();
|
||||
}
|
||||
@ -793,7 +812,7 @@ void AudioDeviceManager::audioDeviceErrorInt (const String& message)
|
||||
|
||||
double AudioDeviceManager::getCpuUsage() const
|
||||
{
|
||||
return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs);
|
||||
return loadMeasurer.getLoadAsProportion();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -980,7 +999,7 @@ void AudioDeviceManager::playTestSound()
|
||||
|
||||
auto phasePerSample = MathConstants<double>::twoPi / (sampleRate / frequency);
|
||||
|
||||
auto* newSound = new AudioBuffer<float> (1, soundLength);
|
||||
std::unique_ptr<AudioBuffer<float>> newSound (new AudioBuffer<float> (1, soundLength));
|
||||
|
||||
for (int i = 0; i < soundLength; ++i)
|
||||
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
|
||||
@ -988,15 +1007,17 @@ void AudioDeviceManager::playTestSound()
|
||||
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
|
||||
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
|
||||
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
testSound.reset (newSound);
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
std::swap (testSound, newSound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int AudioDeviceManager::getXRunCount() const noexcept
|
||||
{
|
||||
auto deviceXRuns = (currentAudioDevice != nullptr ? currentAudioDevice->getXRunCount() : -1);
|
||||
return jmax (0, deviceXRuns) + xruns;
|
||||
return jmax (0, deviceXRuns) + loadMeasurer.getXRunCount();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -76,7 +76,7 @@ public:
|
||||
AudioDeviceManager();
|
||||
|
||||
/** Destructor. */
|
||||
~AudioDeviceManager();
|
||||
~AudioDeviceManager() override;
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
@ -89,17 +89,6 @@ public:
|
||||
*/
|
||||
struct JUCE_API AudioDeviceSetup
|
||||
{
|
||||
/** Creates an AudioDeviceSetup object.
|
||||
|
||||
The default constructor sets all the member variables to indicate default values.
|
||||
You can then fill-in any values you want to before passing the object to
|
||||
AudioDeviceManager::initialise().
|
||||
*/
|
||||
AudioDeviceSetup();
|
||||
|
||||
bool operator== (const AudioDeviceSetup& other) const;
|
||||
bool operator!= (const AudioDeviceSetup& other) const;
|
||||
|
||||
/** The name of the audio device used for output.
|
||||
The name has to be one of the ones listed by the AudioDeviceManager's currently
|
||||
selected device type.
|
||||
@ -119,13 +108,13 @@ public:
|
||||
A value of 0 indicates that you don't care what rate is used, and the
|
||||
device will choose a sensible rate for you.
|
||||
*/
|
||||
double sampleRate;
|
||||
double sampleRate = 0;
|
||||
|
||||
/** The buffer size, in samples.
|
||||
This buffer size is used for both the input and output devices.
|
||||
A value of 0 indicates the default buffer size.
|
||||
*/
|
||||
int bufferSize;
|
||||
int bufferSize = 0;
|
||||
|
||||
/** The set of active input channels.
|
||||
The bits that are set in this array indicate the channels of the
|
||||
@ -138,7 +127,7 @@ public:
|
||||
should be ignored, and instead, the device's default channels
|
||||
should be used.
|
||||
*/
|
||||
bool useDefaultInputChannels;
|
||||
bool useDefaultInputChannels = true;
|
||||
|
||||
/** The set of active output channels.
|
||||
The bits that are set in this array indicate the channels of the
|
||||
@ -151,7 +140,10 @@ public:
|
||||
should be ignored, and instead, the device's default channels
|
||||
should be used.
|
||||
*/
|
||||
bool useDefaultOutputChannels;
|
||||
bool useDefaultOutputChannels = true;
|
||||
|
||||
bool operator== (const AudioDeviceSetup&) const;
|
||||
bool operator!= (const AudioDeviceSetup&) const;
|
||||
};
|
||||
|
||||
|
||||
@ -211,6 +203,13 @@ public:
|
||||
/** Returns the current device properties that are in use.
|
||||
@see setAudioDeviceSetup
|
||||
*/
|
||||
AudioDeviceSetup getAudioDeviceSetup() const;
|
||||
|
||||
/** Returns the current device properties that are in use.
|
||||
This is an old method, kept around for compatibility, but you should prefer the new
|
||||
version which returns the result rather than taking an out-parameter.
|
||||
@see getAudioDeviceSetup()
|
||||
*/
|
||||
void getAudioDeviceSetup (AudioDeviceSetup& result) const;
|
||||
|
||||
/** Changes the current device or its settings.
|
||||
@ -232,8 +231,7 @@ public:
|
||||
|
||||
@see getAudioDeviceSetup
|
||||
*/
|
||||
String setAudioDeviceSetup (const AudioDeviceSetup& newSetup,
|
||||
bool treatAsChosenDevice);
|
||||
String setAudioDeviceSetup (const AudioDeviceSetup& newSetup, bool treatAsChosenDevice);
|
||||
|
||||
|
||||
/** Returns the currently-active audio device. */
|
||||
@ -257,8 +255,7 @@ public:
|
||||
|
||||
For a list of types, see getAvailableDeviceTypes().
|
||||
*/
|
||||
void setCurrentAudioDeviceType (const String& type,
|
||||
bool treatAsChosenDevice);
|
||||
void setCurrentAudioDeviceType (const String& type, bool treatAsChosenDevice);
|
||||
|
||||
/** Closes the currently-open device.
|
||||
You can call restartLastAudioDevice() later to reopen it in the same state
|
||||
@ -435,10 +432,10 @@ public:
|
||||
*/
|
||||
LevelMeter::Ptr getInputLevelGetter() noexcept { return inputLevelGetter; }
|
||||
|
||||
/** Returns a reference-counted object that can be used to get the current input level.
|
||||
/** Returns a reference-counted object that can be used to get the current output level.
|
||||
|
||||
You need to store this object locally to ensure that the reference count is incremented
|
||||
and decremented properly. The current input level value can be read using getCurrentLevel().
|
||||
and decremented properly. The current output level value can be read using getCurrentLevel().
|
||||
*/
|
||||
LevelMeter::Ptr getOutputLevelGetter() noexcept { return outputLevelGetter; }
|
||||
|
||||
@ -473,7 +470,7 @@ private:
|
||||
std::unique_ptr<AudioIODevice> currentAudioDevice;
|
||||
Array<AudioIODeviceCallback*> callbacks;
|
||||
int numInputChansNeeded = 0, numOutputChansNeeded = 2;
|
||||
String currentDeviceType;
|
||||
String preferredDeviceName, currentDeviceType;
|
||||
BigInteger inputChannels, outputChannels;
|
||||
std::unique_ptr<XmlElement> lastExplicitSettings;
|
||||
mutable bool listNeedsScanning = true;
|
||||
@ -496,16 +493,13 @@ private:
|
||||
std::unique_ptr<AudioBuffer<float>> testSound;
|
||||
int testSoundPosition = 0;
|
||||
|
||||
double cpuUsageMs = 0, timeToCpuScale = 0, msPerBlock = 0;
|
||||
int xruns = 0;
|
||||
AudioProcessLoadMeasurer loadMeasurer;
|
||||
|
||||
LevelMeter::Ptr inputLevelGetter { new LevelMeter() },
|
||||
outputLevelGetter { new LevelMeter() };
|
||||
|
||||
//==============================================================================
|
||||
class CallbackHandler;
|
||||
friend class CallbackHandler;
|
||||
friend struct ContainerDeletePolicy<CallbackHandler>;
|
||||
std::unique_ptr<CallbackHandler> callbackHandler;
|
||||
|
||||
void audioDeviceIOCallbackInt (const float** inputChannelData, int totalNumInputChannels,
|
||||
|
@ -43,7 +43,7 @@ class JUCE_API AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~AudioIODeviceCallback() {}
|
||||
virtual ~AudioIODeviceCallback() = default;
|
||||
|
||||
/** Processes a block of incoming and outgoing audio data.
|
||||
|
||||
|
@ -126,7 +126,7 @@ public:
|
||||
class Listener
|
||||
{
|
||||
public:
|
||||
virtual ~Listener() {}
|
||||
virtual ~Listener() = default;
|
||||
|
||||
/** Called when the list of available audio devices changes. */
|
||||
virtual void audioDeviceListChanged() = 0;
|
||||
|
@ -70,12 +70,12 @@
|
||||
#include <mmreg.h>
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_WINRT_MIDI
|
||||
#if JUCE_USE_WINRT_MIDI && JUCE_MSVC
|
||||
/* 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 add the
|
||||
path to the WinRT headers to your build system. This path should have the form
|
||||
"C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
need to install version 10.0.14393.0 of the Windows Standalone SDK and you may
|
||||
need to add the path to the WinRT headers to your build system. This path should
|
||||
have the form "C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
|
||||
Also please note that Microsoft's Bluetooth MIDI stack has multiple issues, so
|
||||
this API is EXPERIMENTAL - use at your own risk!
|
||||
@ -83,15 +83,16 @@
|
||||
#include <windows.devices.h>
|
||||
#include <windows.devices.midi.h>
|
||||
#include <windows.devices.enumeration.h>
|
||||
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4265)
|
||||
#include <wrl/event.h>
|
||||
#if JUCE_MSVC
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4467)
|
||||
#endif
|
||||
#pragma warning (pop)
|
||||
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4467)
|
||||
#include <robuffer.h>
|
||||
#if JUCE_MSVC
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#if JUCE_ASIO
|
||||
@ -148,7 +149,9 @@
|
||||
installed, or you've not got your paths set up correctly to find its header
|
||||
files.
|
||||
*/
|
||||
#include <rtdk.h>
|
||||
#include <Bela.h>
|
||||
#include <Midi.h>
|
||||
#endif
|
||||
|
||||
#undef SIZEOF
|
||||
@ -163,6 +166,10 @@
|
||||
#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
|
||||
|
||||
#include <oboe/Oboe.h>
|
||||
#endif
|
||||
|
||||
@ -210,14 +217,14 @@
|
||||
#include "native/juce_linux_ALSA.cpp"
|
||||
#endif
|
||||
|
||||
#include "native/juce_linux_Midi.cpp"
|
||||
|
||||
#if JUCE_JACK
|
||||
#include "native/juce_linux_JackAudio.cpp"
|
||||
#endif
|
||||
|
||||
#if JUCE_BELA
|
||||
#include "native/juce_linux_Bela.cpp"
|
||||
#else
|
||||
#include "native/juce_linux_Midi.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
ID: juce_audio_devices
|
||||
vendor: juce
|
||||
version: 5.3.2
|
||||
version: 5.4.3
|
||||
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
|
||||
@ -59,6 +59,22 @@
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
/** Config: JUCE_USE_WINRT_MIDI
|
||||
Enables the use of the Windows Runtime API for MIDI, allowing connections
|
||||
to Bluetooth Low Energy devices on Windows 10 version 1809 (October 2018
|
||||
Update) and later. If you enable this flag then older, unsupported,
|
||||
versions of Windows will automatically fall back to using the regualar
|
||||
Win32 MIDI API.
|
||||
|
||||
You will need version 10.0.14393.0 of the Windows Standalone SDK to compile
|
||||
and you may need to add the path to the WinRT headers. The path to the
|
||||
headers will be something similar to
|
||||
"C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
*/
|
||||
#ifndef JUCE_USE_WINRT_MIDI
|
||||
#define JUCE_USE_WINRT_MIDI 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_ASIO
|
||||
Enables ASIO audio devices (MS Windows only).
|
||||
Turning this on means that you'll need to have the Steinberg ASIO SDK installed
|
||||
@ -146,25 +162,6 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_USE_WINRT_MIDI
|
||||
***
|
||||
EXPERIMENTAL - Microsoft's Bluetooth MIDI stack has multiple issues,
|
||||
use at your own risk!
|
||||
***
|
||||
|
||||
Enables the use of the Windows Runtime API for MIDI, which supports
|
||||
Bluetooth Low Energy connections on computers with the Anniversary Update
|
||||
of Windows 10.
|
||||
|
||||
To compile with this flag requires version 10.0.14393.0 of the Windows
|
||||
Standalone SDK and you must add the path to the WinRT headers. This path
|
||||
should be something similar to
|
||||
"C:\Program Files (x86)\Windows Kits\10\Include\10.0.14393.0\winrt".
|
||||
*/
|
||||
#ifndef JUCE_USE_WINRT_MIDI
|
||||
#define JUCE_USE_WINRT_MIDI 0
|
||||
#endif
|
||||
|
||||
/** Config: JUCE_DISABLE_AUDIO_MIXING_WITH_OTHER_APPS
|
||||
Turning this on gives your app exclusive access to the system's audio
|
||||
on platforms which support it (currently iOS only).
|
||||
|
@ -41,7 +41,7 @@ class JUCE_API MidiInputCallback
|
||||
{
|
||||
public:
|
||||
/** Destructor. */
|
||||
virtual ~MidiInputCallback() {}
|
||||
virtual ~MidiInputCallback() = default;
|
||||
|
||||
|
||||
/** Receives an incoming message.
|
||||
@ -89,7 +89,7 @@ public:
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiInput
|
||||
class JUCE_API MidiInput final
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
MidiMessageCollector();
|
||||
|
||||
/** Destructor. */
|
||||
~MidiMessageCollector();
|
||||
~MidiMessageCollector() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Clears any messages from the queue.
|
||||
|
@ -42,7 +42,7 @@ void MidiOutput::sendBlockOfMessagesNow (const MidiBuffer& buffer)
|
||||
{
|
||||
MidiBuffer::Iterator i (buffer);
|
||||
MidiMessage message;
|
||||
int samplePosition; // Note: not actually used, so no need to initialise.
|
||||
int samplePosition; // Note: Not actually used, so no need to initialise.
|
||||
|
||||
while (i.getNextEvent (message, samplePosition))
|
||||
sendMessageNow (message);
|
||||
|
@ -34,7 +34,7 @@ namespace juce
|
||||
|
||||
@tags{Audio}
|
||||
*/
|
||||
class JUCE_API MidiOutput : private Thread
|
||||
class JUCE_API MidiOutput final : private Thread
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
@ -81,7 +81,7 @@ public:
|
||||
|
||||
//==============================================================================
|
||||
/** Destructor. */
|
||||
~MidiOutput();
|
||||
~MidiOutput() override;
|
||||
|
||||
/** Returns the name of this device. */
|
||||
const String& getName() const noexcept { return name; }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -33,79 +33,68 @@ namespace juce
|
||||
class MidiDataConcatenator
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
MidiDataConcatenator (int initialBufferSize)
|
||||
: pendingData ((size_t) initialBufferSize)
|
||||
: pendingSysexData ((size_t) initialBufferSize)
|
||||
{
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
pendingBytes = 0;
|
||||
runningStatus = 0;
|
||||
pendingDataTime = 0;
|
||||
currentMessageLen = 0;
|
||||
pendingSysexSize = 0;
|
||||
pendingSysexTime = 0;
|
||||
}
|
||||
|
||||
template <typename UserDataType, typename CallbackType>
|
||||
void pushMidiData (const void* inputData, int numBytes, double time,
|
||||
UserDataType* input, CallbackType& callback)
|
||||
{
|
||||
const uint8* d = static_cast<const uint8*> (inputData);
|
||||
auto d = static_cast<const uint8*> (inputData);
|
||||
|
||||
while (numBytes > 0)
|
||||
{
|
||||
if (pendingBytes > 0 || d[0] == 0xf0)
|
||||
auto nextByte = *d;
|
||||
|
||||
if (pendingSysexSize != 0 || nextByte == 0xf0)
|
||||
{
|
||||
processSysex (d, numBytes, time, input, callback);
|
||||
runningStatus = 0;
|
||||
currentMessageLen = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
++d;
|
||||
--numBytes;
|
||||
|
||||
if (isRealtimeMessage (nextByte))
|
||||
{
|
||||
callback.handleIncomingMidiMessage (input, MidiMessage (nextByte, time));
|
||||
// These can be embedded in the middle of a normal message, so we won't
|
||||
// reset the currentMessageLen here.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isInitialByte (nextByte))
|
||||
{
|
||||
currentMessage[0] = nextByte;
|
||||
currentMessageLen = 1;
|
||||
}
|
||||
else if (currentMessageLen > 0 && currentMessageLen < 3)
|
||||
{
|
||||
currentMessage[currentMessageLen++] = nextByte;
|
||||
}
|
||||
else
|
||||
{
|
||||
int len = 0;
|
||||
uint8 data[3];
|
||||
// message is too long or invalid MIDI - abandon it and start again with the next byte
|
||||
currentMessageLen = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
while (numBytes > 0)
|
||||
{
|
||||
// If there's a realtime message embedded in the middle of
|
||||
// the normal message, handle it now..
|
||||
if (*d >= 0xf8 && *d <= 0xfe)
|
||||
{
|
||||
callback.handleIncomingMidiMessage (input, MidiMessage (*d++, time));
|
||||
--numBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (len == 0 && *d < 0x80 && runningStatus >= 0x80)
|
||||
data[len++] = runningStatus;
|
||||
auto expectedLength = MidiMessage::getMessageLengthFromFirstByte (currentMessage[0]);
|
||||
|
||||
data[len++] = *d++;
|
||||
--numBytes;
|
||||
|
||||
const uint8 firstByte = data[0];
|
||||
|
||||
if (firstByte < 0x80 || firstByte == 0xf7)
|
||||
{
|
||||
len = 0;
|
||||
break; // ignore this malformed MIDI message..
|
||||
}
|
||||
|
||||
if (len >= MidiMessage::getMessageLengthFromFirstByte (firstByte))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (len > 0)
|
||||
{
|
||||
int used = 0;
|
||||
const MidiMessage m (data, len, used, 0, time);
|
||||
|
||||
if (used <= 0)
|
||||
break; // malformed message..
|
||||
|
||||
jassert (used == len);
|
||||
callback.handleIncomingMidiMessage (input, m);
|
||||
runningStatus = data[0];
|
||||
}
|
||||
if (expectedLength == currentMessageLen)
|
||||
{
|
||||
callback.handleIncomingMidiMessage (input, MidiMessage (currentMessage, expectedLength, time));
|
||||
currentMessageLen = 1; // reset, but leave the first byte to use as the running status byte
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -117,22 +106,22 @@ private:
|
||||
{
|
||||
if (*d == 0xf0)
|
||||
{
|
||||
pendingBytes = 0;
|
||||
pendingDataTime = time;
|
||||
pendingSysexSize = 0;
|
||||
pendingSysexTime = time;
|
||||
}
|
||||
|
||||
pendingData.ensureSize ((size_t) (pendingBytes + numBytes), false);
|
||||
uint8* totalMessage = static_cast<uint8*> (pendingData.getData());
|
||||
uint8* dest = totalMessage + pendingBytes;
|
||||
pendingSysexData.ensureSize ((size_t) (pendingSysexSize + numBytes), false);
|
||||
auto totalMessage = static_cast<uint8*> (pendingSysexData.getData());
|
||||
auto dest = totalMessage + pendingSysexSize;
|
||||
|
||||
do
|
||||
{
|
||||
if (pendingBytes > 0 && *d >= 0x80)
|
||||
if (pendingSysexSize > 0 && isInitialByte (*d))
|
||||
{
|
||||
if (*d == 0xf7)
|
||||
{
|
||||
*dest++ = *d++;
|
||||
++pendingBytes;
|
||||
++pendingSysexSize;
|
||||
--numBytes;
|
||||
break;
|
||||
}
|
||||
@ -145,7 +134,7 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
pendingBytes = 0;
|
||||
pendingSysexSize = 0;
|
||||
int used = 0;
|
||||
const MidiMessage m (d, numBytes, used, 0, time);
|
||||
|
||||
@ -162,30 +151,35 @@ private:
|
||||
else
|
||||
{
|
||||
*dest++ = *d++;
|
||||
++pendingBytes;
|
||||
++pendingSysexSize;
|
||||
--numBytes;
|
||||
}
|
||||
}
|
||||
while (numBytes > 0);
|
||||
|
||||
if (pendingBytes > 0)
|
||||
if (pendingSysexSize > 0)
|
||||
{
|
||||
if (totalMessage [pendingBytes - 1] == 0xf7)
|
||||
if (totalMessage [pendingSysexSize - 1] == 0xf7)
|
||||
{
|
||||
callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime));
|
||||
pendingBytes = 0;
|
||||
callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingSysexSize, pendingSysexTime));
|
||||
pendingSysexSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
callback.handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime);
|
||||
callback.handlePartialSysexMessage (input, totalMessage, pendingSysexSize, pendingSysexTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MemoryBlock pendingData;
|
||||
double pendingDataTime = 0;
|
||||
int pendingBytes = 0;
|
||||
uint8 runningStatus = 0;
|
||||
static bool isRealtimeMessage (uint8 byte) { return byte >= 0xf8 && byte <= 0xfe; }
|
||||
static bool isInitialByte (uint8 byte) { return byte >= 0x80 && byte != 0xf7; }
|
||||
|
||||
uint8 currentMessage[3];
|
||||
int currentMessageLen = 0;
|
||||
|
||||
MemoryBlock pendingSysexData;
|
||||
double pendingSysexTime = 0;
|
||||
int pendingSysexSize = 0;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator)
|
||||
};
|
||||
|
@ -23,7 +23,7 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
|
||||
STATICMETHOD (getNativeOutputSampleRate, "getNativeOutputSampleRate", "(I)I") \
|
||||
METHOD (constructor, "<init>", "(IIIIII)V") \
|
||||
@ -34,11 +34,11 @@ namespace juce
|
||||
METHOD (flush, "flush", "()V") \
|
||||
METHOD (write, "write", "([SII)I") \
|
||||
|
||||
DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack");
|
||||
DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \
|
||||
METHOD (constructor, "<init>", "(IIIII)V") \
|
||||
METHOD (getState, "getState", "()I") \
|
||||
@ -47,14 +47,7 @@ DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack");
|
||||
METHOD (read, "read", "([SII)I") \
|
||||
METHOD (release, "release", "()V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
STATICFIELD (SDK_INT, "SDK_INT", "I") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidBuildVersion, "android/os/Build$VERSION");
|
||||
DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
@ -198,11 +191,11 @@ public:
|
||||
if (numClientOutputChannels > 0)
|
||||
{
|
||||
numDeviceOutputChannels = 2;
|
||||
outputDevice = GlobalRef (env->NewObject (AudioTrack, AudioTrack.constructor,
|
||||
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT,
|
||||
(jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast<int> (sizeof (int16))), MODE_STREAM));
|
||||
outputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioTrack, AudioTrack.constructor,
|
||||
STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT,
|
||||
(jint) (minBufferSizeOut * numDeviceOutputChannels * static_cast<int> (sizeof (int16))), MODE_STREAM)));
|
||||
|
||||
const bool supportsUnderrunCount = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 24);
|
||||
const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24);
|
||||
getUnderrunCount = supportsUnderrunCount ? env->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : 0;
|
||||
|
||||
int outputDeviceState = env->CallIntMethod (outputDevice, AudioTrack.getState);
|
||||
@ -232,11 +225,11 @@ public:
|
||||
else
|
||||
{
|
||||
numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable);
|
||||
inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor,
|
||||
0 /* (default audio source) */, sampleRate,
|
||||
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO,
|
||||
ENCODING_PCM_16BIT,
|
||||
(jint) (minBufferSizeIn * numDeviceInputChannels * static_cast<int> (sizeof (int16)))));
|
||||
inputDevice = GlobalRef (LocalRef<jobject>(env->NewObject (AudioRecord, AudioRecord.constructor,
|
||||
0 /* (default audio source) */, sampleRate,
|
||||
numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO,
|
||||
ENCODING_PCM_16BIT,
|
||||
(jint) (minBufferSizeIn * numDeviceInputChannels * static_cast<int> (sizeof (int16))))));
|
||||
|
||||
int inputDeviceState = env->CallIntMethod (inputDevice, AudioRecord.getState);
|
||||
if (inputDeviceState > 0)
|
||||
|
@ -23,24 +23,346 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
METHOD (getJuceAndroidMidiInputDevices, "getJuceAndroidMidiInputDevices", "()[Ljava/lang/String;") \
|
||||
METHOD (getJuceAndroidMidiOutputDevices, "getJuceAndroidMidiOutputDevices", "()[Ljava/lang/String;") \
|
||||
METHOD (openMidiInputPortWithJuceIndex, "openMidiInputPortWithJuceIndex", "(IJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort;") \
|
||||
METHOD (openMidiOutputPortWithJuceIndex, "openMidiOutputPortWithJuceIndex", "(I)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort;") \
|
||||
METHOD (getInputPortNameForJuceIndex, "getInputPortNameForJuceIndex", "(I)Ljava/lang/String;") \
|
||||
METHOD (getOutputPortNameForJuceIndex, "getOutputPortNameForJuceIndex", "(I)Ljava/lang/String;")
|
||||
DECLARE_JNI_CLASS (MidiDeviceManager, JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager")
|
||||
//==============================================================================
|
||||
// This byte-code is generated from native/java/com/roli/juce/JuceMidiSupport.java with min sdk version 23
|
||||
// See juce_core/native/java/README.txt on how to generate this byte-code.
|
||||
static const uint8 javaMidiByteCode[] =
|
||||
{31,139,8,8,173,175,226,91,0,3,106,117,99,101,95,97,117,100,105,111,95,100,101,118,105,99,101,115,46,100,101,120,0,149,
|
||||
124,11,124,220,69,181,255,153,223,99,119,179,217,164,155,77,218,164,105,178,217,164,73,179,133,60,155,182,164,77,26,154,
|
||||
164,105,155,118,251,160,217,22,105,144,186,77,182,237,150,100,55,236,110,250,0,175,148,135,180,32,42,42,143,42,92,254,40,
|
||||
175,130,168,232,5,46,34,42,8,87,20,69,184,92,212,122,125,1,194,223,130,200,67,65,68,196,222,239,153,153,125,164,45,84,
|
||||
203,231,187,103,126,103,206,156,153,57,115,230,204,153,223,46,25,141,238,113,183,117,44,160,7,183,254,71,91,73,96,244,
|
||||
225,231,218,251,238,107,108,49,191,55,239,138,244,25,191,14,80,247,85,235,137,38,136,104,207,166,249,62,210,255,30,90,71,
|
||||
116,150,80,252,1,224,25,155,104,19,232,17,7,81,0,244,117,55,209,93,76,11,137,10,64,215,151,128,223,3,121,104,216,209,76,
|
||||
180,19,24,7,146,192,36,112,61,112,35,112,8,184,27,184,7,120,0,120,8,120,26,40,110,33,90,7,156,11,196,129,36,176,27,56,8,
|
||||
124,23,248,30,240,3,224,151,192,107,192,123,64,105,43,209,12,160,18,168,1,234,129,83,128,22,160,3,88,4,108,1,46,5,110,7,
|
||||
158,6,204,54,162,54,96,24,216,7,124,25,248,9,240,6,224,111,39,234,7,206,1,46,4,190,2,60,13,188,3,84,207,35,90,11,236,7,
|
||||
238,0,126,2,188,1,56,58,136,170,128,5,192,32,240,97,32,9,252,16,120,23,104,153,79,180,21,184,10,248,13,80,191,128,232,76,
|
||||
224,227,192,109,192,227,192,203,64,209,66,162,38,96,37,176,5,72,2,251,129,235,129,175,1,223,7,94,0,204,211,136,102,2,109,
|
||||
192,48,144,0,46,1,14,2,119,1,15,3,63,7,142,0,127,3,90,59,137,150,2,67,192,78,224,223,128,135,128,103,128,231,129,130,69,
|
||||
68,30,192,11,76,7,102,1,179,129,70,160,9,152,7,44,2,122,128,126,96,16,88,7,108,2,62,12,68,128,81,96,7,48,6,76,0,87,3,143,
|
||||
0,47,1,255,0,74,23,195,55,128,38,96,1,176,24,232,7,206,0,206,1,118,2,73,224,82,224,38,224,30,224,251,192,175,128,87,129,
|
||||
119,0,179,11,227,3,170,129,122,96,30,176,24,88,5,156,1,124,8,136,0,113,96,15,240,113,224,83,192,13,192,205,192,109,192,
|
||||
55,128,7,128,71,129,159,0,191,6,94,6,222,1,236,110,216,0,168,2,234,129,86,96,49,176,19,72,3,215,1,247,3,223,5,158,0,126,
|
||||
1,252,22,120,25,120,13,120,23,40,95,66,52,31,88,13,124,4,56,31,248,12,112,61,240,85,224,155,192,15,129,159,2,191,1,10,
|
||||
176,95,188,64,121,143,218,59,61,192,89,192,94,224,11,192,189,192,19,192,207,128,183,0,227,116,162,18,224,20,96,62,208,15,
|
||||
132,129,109,64,2,248,24,176,31,248,12,112,29,240,37,224,86,224,78,224,235,192,127,2,223,1,126,8,252,12,248,29,240,10,240,
|
||||
22,96,44,133,31,0,51,128,90,160,21,152,15,116,3,43,129,51,128,51,129,17,96,23,112,25,240,57,224,58,224,6,224,139,192,237,
|
||||
192,87,129,251,129,239,0,143,0,63,2,126,11,252,21,40,238,37,106,0,122,128,245,192,8,144,4,46,6,62,15,220,9,60,12,60,14,
|
||||
28,6,94,6,172,62,34,31,16,0,130,64,15,48,4,108,1,226,192,167,128,175,1,143,3,255,11,188,8,188,6,252,13,16,253,216,87,64,
|
||||
25,48,23,24,0,206,1,70,129,113,96,15,112,49,112,37,112,29,240,69,224,110,224,1,224,113,224,73,224,247,192,107,192,187,
|
||||
128,107,25,246,6,112,42,48,31,232,6,250,128,21,192,102,32,1,236,1,46,6,174,0,174,2,238,2,238,3,30,3,158,1,94,0,222,0,222,
|
||||
6,254,1,20,32,184,86,0,1,96,14,208,6,116,1,189,192,32,16,6,62,12,164,129,79,3,119,0,119,3,211,16,115,203,128,58,96,54,80,
|
||||
15,52,0,115,128,70,32,8,204,5,78,1,78,5,154,0,132,89,66,216,36,132,65,66,184,35,132,53,66,8,35,132,41,66,104,34,132,33,
|
||||
66,136,33,132,13,66,104,32,108,93,194,214,35,108,15,130,123,19,92,150,224,42,132,101,33,152,151,96,18,90,166,207,135,229,
|
||||
192,10,96,37,48,8,172,2,86,3,33,96,13,176,22,88,199,231,4,112,6,176,1,24,2,194,164,206,149,15,1,155,129,179,129,115,128,
|
||||
45,64,4,24,1,70,129,40,112,46,112,1,240,49,224,66,96,31,112,17,112,49,112,9,41,155,100,254,121,53,125,22,147,47,209,229,
|
||||
35,40,87,130,26,250,153,203,166,46,215,232,242,179,90,198,210,252,90,93,126,93,243,93,121,242,56,2,233,239,154,95,168,
|
||||
249,179,128,34,110,211,164,248,197,121,125,77,203,43,251,242,228,203,180,60,151,43,242,218,86,230,245,85,165,199,198,50,
|
||||
126,45,83,163,203,204,175,209,114,30,173,167,78,203,84,235,114,89,147,146,229,114,149,110,91,159,215,182,65,183,229,126,
|
||||
216,135,130,122,12,45,121,227,108,205,27,91,91,222,216,184,220,214,164,242,2,46,119,54,229,248,25,123,182,231,233,105,
|
||||
207,27,63,151,151,230,149,51,242,157,121,250,217,15,87,234,126,23,107,62,251,194,18,93,30,211,101,214,57,174,203,235,81,
|
||||
142,235,242,135,80,78,232,242,104,147,202,105,184,156,70,121,183,46,127,20,229,243,116,249,0,202,73,93,190,10,229,73,93,
|
||||
190,1,229,93,186,124,75,94,249,238,60,157,15,230,149,61,121,229,71,243,202,63,206,235,247,153,60,254,179,121,229,35,121,
|
||||
253,190,158,199,255,107,94,91,222,208,123,50,125,53,231,228,43,80,222,171,203,129,230,92,219,165,121,122,218,242,244,119,
|
||||
230,143,225,212,92,185,169,57,215,215,124,148,211,25,61,40,159,175,203,43,155,115,182,90,143,114,74,151,207,110,86,123,
|
||||
181,71,175,209,71,117,153,215,232,223,116,57,157,87,110,203,43,103,124,160,87,183,229,114,95,158,63,244,231,249,195,50,
|
||||
205,159,165,203,150,244,243,54,186,143,20,93,42,184,205,52,250,132,108,219,78,159,146,244,52,250,140,164,46,234,17,236,
|
||||
183,21,116,5,219,10,189,191,36,169,160,87,37,109,160,217,178,126,14,53,11,142,5,101,82,174,86,243,107,53,127,182,126,102,
|
||||
122,134,224,125,101,209,181,196,212,75,127,145,84,213,215,235,250,6,61,158,6,68,219,107,36,237,163,59,37,45,167,55,37,
|
||||
157,79,239,232,122,191,80,52,32,212,190,60,68,76,123,232,15,164,158,231,10,142,247,53,116,21,49,109,160,183,137,227,155,
|
||||
139,190,39,169,73,143,74,106,211,255,18,199,55,39,221,40,105,29,61,168,233,255,176,205,112,82,220,160,233,87,37,181,232,
|
||||
191,36,93,67,11,161,223,6,223,73,28,251,86,208,42,193,116,1,173,21,156,247,43,190,59,75,221,116,157,164,5,180,28,245,30,
|
||||
173,167,72,215,23,129,115,157,164,133,180,76,40,58,32,56,46,22,209,119,137,105,45,253,146,56,118,171,241,120,17,61,127,
|
||||
44,233,52,154,41,152,122,169,90,112,60,87,227,230,184,254,51,77,127,69,42,166,254,72,210,51,232,176,164,37,244,11,205,
|
||||
231,250,50,173,183,12,167,83,47,244,76,215,227,42,199,105,244,152,164,173,84,46,152,46,162,10,73,187,105,158,164,93,180,
|
||||
73,112,108,86,237,43,224,225,255,174,41,219,107,166,214,83,137,241,127,139,56,134,250,232,126,226,216,107,208,45,210,15,
|
||||
151,201,250,26,172,163,162,130,30,150,180,129,126,40,105,152,254,91,210,229,100,74,127,61,133,74,37,61,149,202,36,93,79,
|
||||
115,36,29,164,213,146,14,208,70,233,151,167,75,125,1,61,46,166,255,33,105,51,253,90,210,16,189,162,249,211,164,252,106,
|
||||
154,46,233,42,234,23,138,63,168,233,26,233,207,61,82,95,173,214,87,171,245,213,106,61,181,186,93,173,110,87,171,219,213,
|
||||
105,249,58,45,87,167,229,234,180,92,157,150,155,77,75,165,254,217,200,58,44,249,220,65,182,166,14,73,219,201,41,233,124,
|
||||
114,105,90,160,249,94,77,75,36,109,35,159,166,51,228,190,234,149,122,235,209,255,231,37,173,163,135,36,117,208,15,72,157,
|
||||
115,143,75,58,151,186,244,126,114,202,253,165,230,215,0,143,184,71,210,153,116,175,164,106,125,26,224,23,223,151,116,54,
|
||||
61,33,233,70,250,137,164,97,122,82,211,167,36,45,165,167,181,220,51,146,214,211,79,37,157,69,63,151,180,147,220,178,223,
|
||||
211,168,80,83,143,80,252,34,73,23,83,177,80,251,191,82,210,25,52,75,210,10,170,146,116,45,213,75,218,66,13,66,197,139,
|
||||
249,146,14,80,88,198,133,38,57,159,57,200,176,190,166,227,194,111,101,60,152,11,11,40,234,148,116,58,125,91,210,74,250,
|
||||
14,241,121,126,138,228,183,106,249,86,120,236,135,4,159,219,74,190,77,219,167,13,30,253,8,241,249,172,244,183,195,206,47,
|
||||
19,231,141,253,82,174,3,30,206,126,63,95,183,155,15,185,253,250,249,122,253,252,255,36,157,67,127,212,207,29,66,229,156,
|
||||
43,37,29,162,33,193,249,103,35,93,73,156,131,42,61,11,117,251,133,144,191,73,210,26,217,207,66,100,183,127,146,52,64,237,
|
||||
66,241,89,223,105,186,221,105,186,255,211,116,63,167,233,126,78,211,253,116,98,252,191,33,166,200,136,4,231,23,106,92,
|
||||
139,53,237,210,122,186,144,205,158,46,56,247,85,207,221,218,191,248,12,2,91,190,247,32,185,255,113,94,115,162,141,36,247,
|
||||
189,53,42,199,18,142,92,142,196,245,46,156,105,11,215,170,231,128,110,207,252,167,78,85,180,2,116,173,174,175,213,245,
|
||||
109,121,245,109,160,151,233,250,217,90,175,157,167,127,37,234,191,170,235,235,53,191,59,175,254,67,168,127,81,215,55,104,
|
||||
253,211,129,195,90,255,14,80,207,58,85,63,71,183,203,31,255,221,168,63,160,235,27,243,198,151,169,127,8,245,55,233,122,
|
||||
206,175,127,129,196,255,217,144,146,251,131,166,127,11,229,234,10,215,228,202,229,107,84,125,93,30,239,84,93,94,8,186,36,
|
||||
175,188,114,141,202,211,89,102,8,229,115,116,219,152,166,231,107,250,9,77,111,210,244,94,77,127,156,215,199,111,53,239,
|
||||
101,169,211,144,229,111,14,168,187,195,132,183,8,207,117,240,153,9,47,251,238,176,215,66,84,31,246,26,52,236,51,112,46,
|
||||
177,60,235,121,124,64,229,254,97,212,156,231,189,148,248,212,139,7,118,96,141,221,50,223,183,180,220,127,15,168,123,193,
|
||||
121,178,23,143,136,7,12,236,35,200,122,109,249,204,113,205,68,29,203,254,102,64,157,105,225,128,69,225,90,11,50,95,66,
|
||||
141,91,204,46,89,6,221,55,99,124,30,248,224,50,41,99,203,83,30,119,121,180,153,1,154,244,222,134,62,61,34,233,189,133,
|
||||
219,24,157,70,17,120,183,162,204,109,60,228,243,197,219,22,193,131,130,175,23,235,145,17,189,51,160,236,192,119,21,135,
|
||||
156,25,158,151,171,187,160,175,100,94,153,77,190,218,142,178,18,140,163,4,253,121,176,111,10,41,220,206,227,226,155,145,
|
||||
199,136,7,110,130,207,250,122,59,202,106,16,191,166,83,165,177,147,206,11,180,131,151,107,225,59,166,197,23,101,173,165,
|
||||
109,209,141,8,90,44,231,194,125,7,150,171,187,74,190,173,122,161,101,17,250,85,250,191,161,245,251,196,52,17,110,87,150,
|
||||
23,82,242,124,105,169,224,91,110,104,98,237,109,208,117,38,207,163,220,231,208,250,160,199,77,149,22,244,216,69,82,79,24,
|
||||
125,199,229,133,209,35,22,137,76,157,71,215,5,255,212,89,176,128,234,12,55,60,129,109,86,105,89,232,175,141,173,108,197,
|
||||
3,94,196,226,58,179,8,117,62,88,46,30,40,67,6,204,252,233,184,181,122,44,95,3,151,194,52,59,189,20,61,76,67,107,143,189,
|
||||
198,182,28,231,121,175,83,237,189,165,104,229,177,227,75,11,169,247,227,193,111,199,3,30,220,104,131,223,164,172,127,141,
|
||||
46,87,247,204,169,254,245,111,240,175,98,228,97,14,57,163,241,229,234,110,57,225,109,65,155,225,217,5,52,92,239,160,225,
|
||||
6,55,109,158,227,130,229,207,14,56,229,218,218,210,191,4,93,180,92,197,16,159,25,238,117,80,167,112,18,211,184,247,84,
|
||||
212,133,123,11,192,41,144,52,220,231,70,95,31,133,157,135,251,161,179,223,1,45,69,122,5,124,122,5,130,71,84,28,98,221,66,
|
||||
52,227,248,18,114,76,215,162,15,142,153,113,126,193,69,9,239,229,218,191,212,46,35,186,113,185,218,135,62,88,69,104,222,
|
||||
45,203,115,126,88,140,185,241,93,251,206,229,106,223,44,41,244,208,208,69,46,114,238,115,126,78,220,34,238,181,190,191,
|
||||
203,181,84,203,90,250,182,254,64,94,123,67,239,165,239,47,87,113,46,236,45,80,94,232,197,140,33,177,209,235,148,126,192,
|
||||
207,241,64,19,246,148,207,123,182,215,57,165,237,147,31,208,182,51,219,182,153,219,82,166,45,143,133,199,112,88,175,219,
|
||||
132,247,0,71,15,225,161,97,163,144,134,225,41,197,217,117,56,146,183,14,133,122,29,10,97,177,217,114,29,60,122,29,60,88,
|
||||
135,162,236,58,64,79,127,225,191,176,14,239,101,215,97,229,9,215,193,94,161,215,1,30,228,212,163,47,4,175,148,231,221,
|
||||
174,71,5,26,95,138,44,43,154,235,183,79,232,126,223,86,239,71,116,191,238,204,90,214,173,200,173,69,134,23,204,227,153,
|
||||
50,218,17,181,172,80,239,84,134,197,52,216,138,45,54,108,20,203,248,170,222,204,44,206,107,147,89,231,101,39,224,173,95,
|
||||
145,31,195,44,57,167,179,87,168,56,234,11,116,216,211,176,190,241,64,1,180,134,3,188,219,93,188,239,16,47,46,144,183,140,
|
||||
156,158,115,79,160,123,247,9,120,151,156,128,247,233,99,230,199,255,174,63,1,239,214,60,158,45,45,71,244,181,21,188,179,
|
||||
217,14,165,176,195,157,210,14,56,111,204,18,26,182,120,132,150,180,162,73,15,173,80,239,100,170,140,122,170,54,124,98,
|
||||
184,221,135,85,253,50,106,224,143,237,94,172,215,52,73,227,240,71,161,75,28,37,88,210,139,231,18,72,184,37,141,123,43,52,
|
||||
191,132,252,6,238,96,194,111,52,138,34,17,124,155,103,51,19,117,53,114,124,166,204,23,28,210,103,231,94,216,220,52,87,
|
||||
151,13,250,245,10,149,59,86,153,24,139,25,222,0,221,198,108,98,26,247,206,228,88,39,226,222,58,121,10,249,230,119,244,
|
||||
205,0,183,150,163,180,81,105,125,22,254,220,138,200,201,103,146,137,189,228,202,158,12,126,179,4,168,196,242,5,255,94,
|
||||
132,82,163,161,206,251,57,104,217,172,125,73,32,223,205,248,239,187,43,84,125,216,235,149,107,109,104,111,19,43,51,231,
|
||||
178,26,141,140,155,94,245,214,78,157,203,106,15,58,87,242,253,14,115,16,152,131,8,183,249,56,39,167,112,27,230,130,252,
|
||||
142,159,195,243,160,33,144,70,196,245,11,246,127,191,104,36,213,103,169,236,171,12,251,70,157,193,190,149,234,125,166,
|
||||
207,154,240,54,192,114,195,225,50,26,222,84,6,139,148,81,165,249,22,180,148,227,230,227,49,106,140,114,26,222,80,14,62,
|
||||
110,154,56,87,42,13,236,40,115,187,220,201,243,176,214,117,198,66,248,192,65,142,229,27,102,226,105,62,158,62,45,159,42,
|
||||
166,212,85,78,121,154,46,245,197,189,115,216,242,84,107,250,140,5,243,220,124,15,199,105,155,68,28,59,108,24,88,91,41,
|
||||
211,54,151,54,88,193,31,187,244,25,115,250,74,149,43,134,71,42,208,254,115,188,51,44,206,31,44,114,155,157,102,155,204,
|
||||
31,44,57,110,63,133,17,176,106,76,175,180,169,153,91,97,211,183,160,99,232,247,71,245,10,155,149,182,90,225,161,236,10,
|
||||
255,226,168,94,97,51,30,248,4,206,72,214,252,228,209,18,195,103,4,255,225,200,156,117,43,213,187,234,112,95,37,244,223,
|
||||
192,243,48,146,222,219,53,189,5,148,91,205,146,227,49,164,230,86,10,247,67,54,112,189,28,75,13,214,48,238,253,136,28,1,
|
||||
247,50,36,229,95,56,90,34,124,34,248,15,142,51,202,123,46,92,169,222,123,87,217,13,84,109,135,211,60,235,107,120,182,214,
|
||||
236,190,62,142,243,105,101,7,204,217,33,100,38,229,64,93,167,53,67,246,236,128,246,26,211,79,135,17,239,194,184,194,212,
|
||||
88,202,26,124,158,175,177,12,33,68,240,247,126,187,196,40,178,252,118,163,197,126,209,34,123,109,37,151,244,19,131,238,
|
||||
91,169,238,62,170,255,9,111,140,117,90,195,163,51,200,111,215,153,106,181,77,140,35,222,118,30,237,194,220,59,141,82,204,
|
||||
113,2,123,128,51,139,171,112,235,58,12,7,207,212,6,223,80,61,241,28,76,158,67,47,103,137,215,194,59,61,86,141,133,44,81,
|
||||
142,129,163,65,67,118,255,240,205,88,237,148,63,106,155,87,25,24,139,17,238,85,51,103,11,242,188,161,79,116,138,10,57,
|
||||
111,83,90,220,47,111,156,53,66,205,89,230,92,222,26,153,115,117,156,113,228,168,223,224,88,226,163,224,123,42,154,168,
|
||||
125,210,40,123,10,18,231,130,220,175,99,16,209,141,251,116,163,79,119,167,183,140,124,115,121,54,55,20,121,176,94,107,41,
|
||||
252,240,76,204,224,11,200,143,185,119,39,44,225,119,151,144,111,102,240,213,13,237,179,104,34,16,199,125,212,227,236,116,
|
||||
46,162,240,46,140,197,129,168,231,232,128,84,39,124,113,67,123,53,218,206,66,46,204,118,43,64,6,31,32,231,195,214,11,187,
|
||||
28,242,77,39,191,107,129,246,58,171,3,122,62,137,53,139,183,253,59,181,91,126,119,240,41,140,216,221,40,84,251,74,110,
|
||||
239,234,116,189,116,180,14,103,220,196,210,21,244,80,103,240,121,191,27,51,123,144,228,157,186,31,51,226,239,56,84,246,
|
||||
178,66,250,22,123,241,126,204,141,223,253,249,28,225,20,118,90,224,20,248,122,149,133,121,90,225,212,116,216,236,243,124,
|
||||
2,165,148,127,155,210,202,183,203,12,144,173,109,75,127,45,151,214,182,165,63,183,42,89,248,119,169,244,58,246,239,45,
|
||||
144,15,190,84,100,250,173,70,211,103,14,163,222,15,221,51,143,213,152,183,131,43,243,118,240,28,146,178,208,56,91,106,
|
||||
236,64,187,79,201,118,53,102,29,228,214,178,246,223,13,239,154,9,185,19,69,131,186,140,46,229,11,166,160,78,68,3,166,53,
|
||||
166,37,79,29,51,239,201,33,159,50,17,163,130,117,255,82,233,253,12,202,156,141,112,214,196,247,232,185,176,223,124,82,
|
||||
119,32,129,155,165,186,11,24,244,237,65,229,159,190,64,149,128,21,225,109,87,242,72,140,69,70,0,43,184,13,235,192,235,
|
||||
140,27,138,215,207,39,223,188,233,210,115,250,228,205,199,137,88,29,252,139,138,216,19,129,237,90,214,98,238,31,252,194,
|
||||
39,115,38,142,73,179,208,99,128,248,62,207,125,87,203,236,129,253,246,233,65,149,87,251,106,217,59,125,38,223,99,156,114,
|
||||
39,26,242,198,198,239,106,130,239,229,242,197,231,7,149,111,248,2,19,129,115,229,141,171,36,91,119,36,83,231,205,213,101,
|
||||
250,121,53,211,79,201,7,247,227,211,54,249,7,228,183,18,199,179,18,218,232,170,162,78,215,90,220,111,212,110,114,33,159,
|
||||
9,23,206,160,218,7,125,46,113,249,130,31,174,194,9,80,88,224,51,244,46,117,113,155,13,69,179,168,227,240,34,172,2,191,
|
||||
233,246,20,213,254,198,231,90,112,164,157,150,91,69,174,184,215,207,246,117,116,236,154,131,250,106,185,234,178,205,180,
|
||||
106,234,248,115,21,120,85,188,250,54,175,3,252,21,123,164,56,115,174,187,42,11,222,147,81,255,32,234,107,236,118,222,181,
|
||||
182,175,49,120,207,97,151,75,4,159,58,236,42,16,226,242,224,127,250,221,149,72,84,131,127,46,114,97,191,185,56,227,219,
|
||||
128,214,31,201,198,166,45,176,133,41,215,37,182,74,125,247,232,43,245,89,62,17,126,12,179,218,35,150,47,184,108,30,45,39,
|
||||
167,131,71,143,243,164,160,246,11,98,96,193,173,245,180,220,40,176,121,244,56,37,10,59,126,57,147,58,158,155,78,117,246,
|
||||
156,204,169,239,232,88,82,40,103,132,122,107,98,233,199,40,218,239,28,42,49,69,127,240,47,135,17,179,15,219,182,8,254,
|
||||
250,176,141,128,191,41,248,164,207,12,254,89,221,221,121,28,95,90,165,214,135,207,58,206,179,124,237,29,94,19,126,27,198,
|
||||
121,238,19,241,165,65,106,243,5,223,225,247,202,134,204,151,238,130,252,77,28,223,138,176,34,69,105,49,147,119,175,139,
|
||||
199,225,194,56,124,78,95,1,223,202,194,158,74,220,232,246,115,236,47,102,159,125,133,220,158,69,158,115,100,47,144,243,
|
||||
248,122,58,94,105,67,212,99,235,186,200,227,169,44,86,103,234,43,210,186,56,83,109,117,131,70,164,176,148,62,229,251,33,
|
||||
180,237,244,116,83,142,119,61,120,30,103,141,211,202,227,221,8,94,93,97,189,228,184,176,23,92,56,99,38,206,60,135,10,107,
|
||||
143,27,27,34,228,43,133,117,197,93,56,151,14,97,214,157,5,109,84,234,121,202,121,209,67,182,151,243,67,236,236,165,119,
|
||||
208,119,188,254,162,226,204,120,60,172,131,119,231,67,228,113,119,186,225,145,119,33,211,122,168,6,214,116,34,110,187,
|
||||
100,156,112,202,248,224,164,52,206,151,82,242,23,5,159,43,242,248,139,26,61,165,158,81,10,62,93,228,9,190,205,123,99,18,
|
||||
30,193,223,101,85,235,239,84,84,222,118,80,92,216,124,163,56,40,56,199,51,100,94,221,182,90,125,231,85,229,132,205,157,
|
||||
124,54,185,113,122,179,205,113,118,27,225,107,213,124,12,94,7,216,232,50,172,67,167,3,209,245,90,196,162,192,103,233,54,
|
||||
60,47,114,52,208,84,185,235,33,231,113,212,56,56,234,70,228,185,63,181,254,70,212,179,134,58,151,159,38,218,230,209,33,
|
||||
147,79,231,203,200,239,44,38,61,2,139,87,143,243,136,74,151,90,189,203,100,92,199,234,137,42,105,45,33,243,82,73,29,108,
|
||||
177,24,86,183,211,134,111,13,169,24,218,105,186,116,84,85,209,148,163,168,155,130,79,22,57,130,79,20,57,252,206,70,135,
|
||||
58,99,199,97,247,9,29,63,119,203,189,132,8,122,97,115,122,23,114,132,66,25,53,4,237,131,141,62,204,54,194,76,171,165,109,
|
||||
220,228,182,249,60,61,75,190,163,89,147,141,213,97,129,222,133,69,190,50,223,244,14,36,57,62,43,124,141,58,97,108,121,86,
|
||||
221,174,169,60,179,176,162,175,30,213,103,150,60,97,54,52,206,34,173,221,213,177,255,213,163,178,45,172,217,32,61,87,157,
|
||||
56,182,44,171,19,7,209,187,44,248,227,78,225,210,183,22,117,99,9,95,195,107,115,53,106,253,14,228,221,182,223,209,104,
|
||||
171,185,158,37,227,196,230,236,251,171,71,86,79,189,251,241,61,248,71,171,179,241,118,253,94,154,23,246,200,236,195,144,
|
||||
117,63,93,173,238,156,252,190,204,103,76,108,216,75,253,94,174,47,144,251,29,247,154,213,234,183,12,136,59,6,175,141,75,
|
||||
174,17,191,153,230,44,208,164,105,134,58,209,249,61,159,19,22,236,180,113,210,88,193,183,112,95,177,26,141,112,114,58,
|
||||
233,86,182,207,193,187,103,131,35,156,44,3,111,134,204,100,125,211,235,28,179,225,47,67,180,203,25,95,138,19,49,234,65,
|
||||
150,131,21,207,107,39,91,9,191,37,230,5,31,231,59,234,126,10,254,77,189,151,116,99,166,252,93,231,169,210,6,21,217,24,
|
||||
229,8,169,123,126,38,38,53,34,38,169,59,167,178,210,180,144,178,71,216,59,83,238,126,126,127,164,252,194,162,138,144,250,
|
||||
189,5,207,209,45,119,6,71,55,181,147,194,215,170,168,114,155,228,227,206,114,173,138,40,183,201,181,134,127,26,106,213,
|
||||
12,185,106,134,174,191,17,245,124,226,222,33,189,25,153,172,119,43,123,138,204,94,236,236,46,163,236,46,98,207,135,44,
|
||||
172,11,223,235,159,114,195,112,196,3,147,216,21,42,123,240,59,131,247,40,175,47,18,149,14,220,154,92,165,242,214,116,25,
|
||||
113,188,230,179,100,25,172,178,86,223,181,207,208,241,226,76,157,255,26,52,116,97,243,166,161,236,59,154,177,80,254,59,
|
||||
154,179,196,44,58,219,168,162,179,204,106,249,246,73,221,48,63,26,82,239,203,125,98,17,98,238,52,228,31,87,200,76,138,41,
|
||||
124,216,236,152,247,246,81,231,250,26,228,230,27,250,170,105,3,218,118,204,123,245,232,198,190,42,218,104,86,161,124,228,
|
||||
232,134,190,89,224,227,196,157,247,252,81,95,73,240,183,153,247,112,68,159,12,169,239,10,234,112,55,216,208,59,139,158,
|
||||
246,237,3,173,166,82,115,31,45,104,47,149,229,187,107,39,2,31,231,24,235,189,140,87,223,216,216,139,243,30,59,197,247,
|
||||
198,55,106,167,137,82,113,33,5,95,131,214,191,169,219,48,209,23,245,58,251,96,179,26,192,143,185,100,222,5,29,10,169,124,
|
||||
71,205,183,88,199,82,131,190,18,210,239,161,68,238,13,169,73,37,66,191,19,69,206,244,238,209,42,163,17,247,131,173,194,
|
||||
79,139,4,178,106,81,131,53,235,64,134,30,3,199,47,249,193,35,153,156,191,68,234,16,250,123,12,33,179,43,83,247,245,88,72,
|
||||
125,127,82,73,234,62,108,200,222,44,249,93,109,21,242,180,106,177,21,245,139,136,51,247,6,244,49,138,182,60,19,191,228,7,
|
||||
95,205,220,191,139,116,31,213,217,62,170,228,111,79,229,221,95,219,130,231,250,81,159,250,254,231,82,208,43,179,191,110,
|
||||
85,255,14,234,231,140,60,255,182,233,22,240,238,58,134,63,168,249,247,31,211,254,209,99,158,159,242,169,62,51,191,41,226,
|
||||
119,135,207,250,212,111,122,94,247,169,239,73,254,234,83,191,91,56,226,83,191,105,98,189,219,88,184,84,253,94,5,14,77,
|
||||
222,210,169,122,171,142,121,150,223,149,104,58,67,83,254,237,140,33,105,187,236,127,26,245,200,88,199,117,85,121,115,17,
|
||||
164,114,25,67,63,153,154,246,80,238,119,81,153,239,118,12,73,219,228,243,28,205,239,207,202,121,116,91,245,11,15,213,215,
|
||||
233,217,246,66,219,65,97,134,140,89,220,46,99,159,204,239,171,20,207,161,121,14,201,83,101,103,86,87,129,166,94,77,125,
|
||||
90,198,167,245,10,101,62,202,124,167,101,72,58,139,50,223,177,177,44,127,199,59,71,247,203,223,171,206,209,123,176,1,255,
|
||||
89,154,122,117,92,88,164,219,44,210,119,19,150,235,214,123,168,71,215,157,174,199,111,101,203,114,214,65,50,131,131,184,
|
||||
203,204,165,250,182,142,190,206,182,129,5,189,205,3,203,6,58,155,231,247,117,116,52,247,158,182,160,189,121,97,255,64,
|
||||
199,252,129,254,249,253,167,181,193,180,200,211,186,71,198,98,241,88,186,135,28,221,138,26,61,93,100,245,116,205,221,196,
|
||||
159,40,123,251,198,38,163,233,68,34,189,99,77,36,30,217,30,77,210,226,99,57,129,104,50,153,72,46,14,140,36,38,199,70,3,
|
||||
241,68,58,176,61,154,14,100,165,2,161,129,64,106,36,18,143,163,237,233,255,92,219,209,232,182,200,228,88,190,142,200,104,
|
||||
100,34,13,5,149,203,38,199,199,247,102,249,43,34,233,116,127,100,108,108,107,100,4,23,155,65,50,6,67,100,14,134,66,84,51,
|
||||
184,46,48,176,103,36,58,145,142,37,226,129,221,59,98,99,209,192,200,88,34,21,139,111,15,76,36,146,105,106,24,92,247,126,
|
||||
245,227,177,209,24,134,176,43,54,18,37,177,138,172,85,27,251,7,168,100,213,228,72,116,13,106,6,227,19,147,233,245,172,
|
||||
194,151,97,173,155,76,103,120,158,12,79,62,149,101,158,134,38,39,184,215,150,157,145,93,17,18,33,50,66,131,100,134,6,229,
|
||||
7,122,192,7,50,10,12,219,12,225,195,10,133,54,135,168,62,20,137,143,38,19,177,209,214,173,153,217,182,102,231,221,171,
|
||||
204,209,69,179,63,72,106,153,156,67,23,213,126,144,16,155,176,139,230,158,76,36,99,229,46,106,61,169,232,142,72,50,50,
|
||||
130,225,197,82,233,216,72,23,157,122,178,6,203,162,169,145,100,108,34,157,72,158,120,32,99,209,156,124,40,58,164,124,233,
|
||||
196,115,135,40,215,231,70,251,62,250,88,104,121,108,12,131,172,239,155,140,141,141,178,190,19,153,105,138,232,7,138,108,
|
||||
136,166,224,178,39,158,173,22,25,138,166,211,112,176,84,174,203,15,152,66,70,184,139,102,102,133,70,18,241,116,52,158,
|
||||
110,237,103,186,7,157,213,100,171,198,163,163,177,72,43,187,110,43,59,92,102,233,155,62,88,96,48,190,45,81,207,174,202,
|
||||
133,252,225,188,175,116,23,53,124,176,208,80,58,146,158,196,168,235,222,79,44,187,129,242,93,233,24,25,29,29,234,149,202,
|
||||
220,106,158,118,178,6,235,226,170,201,186,137,104,60,58,26,130,7,70,165,175,4,78,210,240,3,230,158,219,221,249,235,127,
|
||||
140,208,134,232,72,52,182,139,245,148,102,69,18,169,214,190,201,248,232,24,150,161,44,159,185,50,194,76,136,150,231,115,
|
||||
215,71,146,35,209,177,141,147,177,209,46,242,101,43,38,211,177,177,214,80,98,251,113,188,245,145,88,50,175,175,44,175,
|
||||
139,54,30,207,236,62,137,155,156,52,62,224,32,104,11,141,36,198,91,147,137,177,88,235,78,68,181,214,99,66,91,253,177,145,
|
||||
189,139,218,79,210,226,184,136,218,69,243,254,201,38,249,107,210,244,79,182,81,210,161,147,72,231,172,146,245,193,247,61,
|
||||
113,186,104,217,191,172,45,199,97,23,13,71,82,231,158,220,80,199,105,57,249,164,51,19,94,31,73,239,224,48,241,129,210,
|
||||
188,89,71,35,99,187,98,231,182,34,180,38,176,129,113,40,182,14,196,245,129,216,63,22,73,97,67,251,79,32,51,200,145,88,
|
||||
215,215,158,160,126,77,116,124,171,22,136,66,164,250,4,34,67,177,237,113,68,140,36,118,73,229,9,170,195,59,146,137,221,
|
||||
104,58,61,196,103,103,107,44,209,154,119,112,119,81,137,98,143,69,226,219,91,245,56,74,243,88,131,136,147,210,94,190,60,
|
||||
230,186,173,59,163,35,233,169,188,161,116,18,51,205,118,35,121,178,235,200,86,222,191,85,121,236,100,116,91,235,153,209,
|
||||
200,185,27,162,219,162,201,104,28,73,66,245,7,213,242,230,151,213,114,55,246,38,147,145,189,28,150,50,61,77,229,242,121,
|
||||
117,2,118,247,241,35,237,201,142,63,39,154,154,202,91,25,73,97,51,78,100,12,146,207,59,94,16,199,205,113,130,224,77,29,
|
||||
253,32,14,193,136,60,166,167,229,113,229,116,188,199,48,186,168,227,24,78,247,73,207,206,158,169,122,101,247,37,121,140,
|
||||
112,108,156,215,114,250,177,44,181,139,74,142,219,38,212,123,28,235,196,249,102,222,65,16,72,237,197,153,49,30,72,69,147,
|
||||
50,1,244,29,191,97,201,147,191,187,168,33,255,180,110,233,239,13,133,250,122,251,87,111,9,159,181,126,96,203,154,222,112,
|
||||
255,202,45,161,117,67,97,18,155,200,216,132,132,111,19,82,84,107,211,224,230,65,114,108,90,133,20,112,21,216,72,252,54,
|
||||
33,35,180,54,113,74,104,111,146,92,112,228,7,75,135,84,37,202,54,127,174,82,4,105,228,166,205,36,144,57,66,153,129,148,
|
||||
209,24,238,163,186,225,147,103,49,205,195,255,82,86,80,255,79,136,99,219,13,159,96,139,77,97,102,246,88,97,100,100,36,
|
||||
154,74,45,31,139,108,79,145,27,153,226,100,100,76,166,203,206,76,150,111,70,70,71,249,105,52,9,57,242,232,222,7,227,163,
|
||||
209,61,104,173,158,100,11,119,100,98,66,39,67,228,136,164,148,39,110,61,38,75,166,202,44,39,52,32,195,158,90,219,141,27,
|
||||
7,151,145,111,235,113,153,101,158,134,140,35,149,229,56,217,105,167,242,228,182,232,235,66,193,214,116,175,30,181,107,
|
||||
107,90,201,65,76,151,82,124,22,195,4,228,216,154,230,115,132,236,173,156,8,146,103,68,31,40,225,189,19,81,114,96,20,200,
|
||||
4,168,120,100,74,30,77,246,200,88,52,146,100,146,72,69,201,137,92,48,14,27,83,161,46,72,133,46,206,16,35,177,120,74,178,
|
||||
101,105,117,116,175,20,150,54,242,232,66,56,177,17,58,108,236,130,120,154,196,40,185,71,179,41,56,57,244,92,92,138,194,
|
||||
70,153,210,40,21,101,74,74,65,225,104,214,1,82,153,186,140,201,220,234,81,230,41,5,163,177,36,134,136,136,13,118,44,149,
|
||||
25,186,35,122,30,150,62,69,5,114,83,246,39,70,97,192,104,38,182,83,203,182,8,110,101,163,129,116,34,48,146,140,70,210,
|
||||
209,192,214,201,49,125,29,84,186,3,219,146,137,241,64,198,77,92,219,98,241,200,88,236,252,40,213,162,52,154,91,168,229,
|
||||
137,100,222,197,73,9,215,176,72,102,67,159,72,192,222,22,75,194,153,60,219,96,162,209,204,130,187,185,67,229,198,100,109,
|
||||
103,131,23,240,167,50,134,137,72,66,110,124,100,84,84,114,89,57,235,113,151,232,89,185,186,227,195,214,116,174,156,152,
|
||||
24,139,141,200,51,48,227,224,165,96,31,55,206,138,124,102,126,6,46,181,28,127,109,34,23,216,242,164,164,18,148,150,169,
|
||||
155,118,102,167,20,72,150,92,254,226,108,81,45,175,59,251,156,34,39,202,210,223,230,162,176,114,114,156,35,56,246,46,142,
|
||||
74,101,156,19,26,20,162,240,37,73,70,165,6,214,75,85,178,160,147,185,181,145,113,94,45,78,79,212,102,159,137,90,126,58,
|
||||
206,84,41,242,31,95,37,245,100,234,107,142,175,87,89,99,70,128,117,51,247,216,145,162,106,134,174,90,150,117,97,140,74,
|
||||
143,154,199,72,213,40,228,114,208,227,134,93,136,234,76,5,21,101,30,38,57,23,162,114,253,200,103,199,148,70,46,93,145,82,
|
||||
45,146,137,137,104,50,29,195,104,166,225,113,67,116,60,145,142,102,2,10,24,67,242,152,210,145,76,14,76,6,143,162,29,242,
|
||||
114,161,175,35,228,220,17,73,173,101,223,113,161,176,67,238,48,107,71,2,126,93,192,159,202,111,69,140,204,216,232,30,178,
|
||||
99,114,24,86,140,151,197,142,201,69,47,136,101,223,118,20,198,82,89,75,241,67,191,218,196,81,88,37,150,26,24,159,72,239,
|
||||
229,130,92,2,174,206,189,38,113,197,116,214,64,46,78,62,87,202,238,119,102,167,237,217,153,255,198,196,60,23,225,202,129,
|
||||
15,206,71,220,99,9,68,70,37,230,28,215,155,195,226,211,135,220,227,217,229,161,146,241,227,118,80,241,248,148,213,163,
|
||||
194,241,60,207,49,198,199,201,28,79,109,199,71,122,146,172,56,47,146,205,159,136,33,241,232,110,222,62,48,83,156,205,102,
|
||||
38,182,238,36,71,98,219,182,20,134,227,75,196,251,34,233,145,29,185,140,37,69,229,216,158,83,194,52,158,226,219,97,148,
|
||||
178,99,43,120,135,208,244,99,185,103,38,97,29,169,69,153,19,219,93,246,175,212,144,55,17,207,189,28,145,26,74,242,57,170,
|
||||
117,81,66,95,122,225,192,232,185,56,49,229,14,204,125,230,63,47,139,142,69,246,130,61,45,195,102,215,218,149,47,167,226,
|
||||
71,102,34,206,68,124,249,216,100,106,7,121,18,241,53,233,201,12,27,35,227,241,40,191,220,144,74,197,168,130,57,99,49,142,
|
||||
2,114,92,253,137,241,9,196,107,200,162,165,76,63,100,60,207,60,41,11,194,184,200,157,226,210,94,218,153,83,203,248,132,
|
||||
192,93,26,178,165,216,4,241,99,194,27,185,153,169,203,69,92,206,249,154,159,31,167,220,41,207,140,165,119,228,246,88,77,
|
||||
166,62,183,121,167,10,204,204,8,28,95,85,204,85,121,175,250,10,248,89,109,88,87,34,147,26,22,100,74,8,120,24,49,159,131,
|
||||
137,92,19,59,177,155,67,112,233,4,124,242,216,89,85,158,128,57,148,142,78,132,119,39,168,124,74,93,46,50,145,53,193,25,
|
||||
168,147,47,113,131,216,194,5,178,160,162,201,132,206,223,84,73,70,162,130,76,41,165,152,50,125,45,202,148,84,52,144,21,
|
||||
50,148,20,103,74,225,196,114,132,6,50,121,103,207,72,70,183,243,75,149,228,212,55,51,228,72,74,47,34,183,162,42,98,168,
|
||||
178,202,212,102,38,113,216,71,83,233,156,159,175,79,198,18,240,147,189,220,86,186,130,51,169,55,21,24,233,93,145,49,178,
|
||||
146,236,87,102,114,50,78,37,169,108,254,170,95,158,81,105,42,47,239,206,48,157,153,55,205,174,212,200,142,232,40,18,6,
|
||||
114,164,162,72,56,70,201,74,177,159,85,242,167,122,197,187,35,50,26,24,92,23,200,101,28,46,174,99,235,210,52,236,247,254,
|
||||
252,164,172,16,12,246,218,53,28,66,139,249,65,231,144,147,177,81,84,238,224,235,4,246,13,38,106,165,56,5,177,83,242,161,
|
||||
64,18,110,72,69,170,152,78,76,200,71,71,74,157,210,86,10,28,244,156,225,23,192,105,50,139,155,222,17,131,49,248,179,190,
|
||||
13,21,184,234,160,209,248,4,57,211,9,121,223,163,233,147,241,19,185,210,204,99,216,121,14,83,49,25,127,159,21,180,97,241,
|
||||
73,156,24,146,172,219,70,93,226,102,225,44,54,222,160,174,61,198,167,47,236,106,166,144,184,28,12,90,46,201,126,147,174,
|
||||
17,22,255,191,111,197,116,171,16,100,89,223,52,22,143,56,139,143,154,116,191,81,180,217,38,186,92,136,175,179,252,39,133,
|
||||
241,121,113,191,225,44,62,55,100,210,237,194,106,62,104,211,146,61,33,7,181,92,123,62,196,118,75,117,7,164,186,150,61,1,
|
||||
58,67,252,192,112,54,65,244,83,194,108,49,252,187,141,109,213,33,83,92,37,10,90,174,104,217,108,26,223,54,10,175,219,108,
|
||||
154,223,49,138,87,111,94,242,200,224,122,219,176,77,186,84,72,37,215,210,189,194,122,87,92,38,190,102,60,143,199,238,102,
|
||||
252,235,166,223,10,114,86,135,182,172,222,219,220,108,236,170,246,155,244,13,209,66,15,129,89,220,221,77,143,49,165,183,
|
||||
229,231,27,194,250,187,184,196,184,69,252,15,6,219,124,43,253,93,152,234,25,117,79,176,196,35,155,151,208,207,50,133,3,
|
||||
134,169,186,82,29,209,125,198,9,186,249,140,161,186,57,200,148,14,25,89,133,107,182,24,23,84,215,40,161,27,101,229,205,
|
||||
242,243,77,195,160,119,81,223,220,221,76,151,154,198,3,226,122,238,253,98,211,228,210,19,232,139,46,201,43,191,107,160,
|
||||
252,132,177,15,229,37,171,175,163,143,115,213,109,170,234,178,188,242,229,92,126,79,149,15,112,249,91,134,44,239,231,14,
|
||||
100,233,162,108,233,179,166,69,119,137,219,197,183,208,239,102,158,215,245,38,198,181,164,27,11,242,128,113,122,104,243,
|
||||
112,207,218,115,122,154,109,50,246,118,57,136,30,144,149,161,152,41,254,191,40,221,251,136,92,196,230,115,108,178,197,
|
||||
172,154,69,244,35,174,165,39,229,231,79,165,228,254,61,254,42,250,157,201,158,85,109,220,96,117,25,47,94,208,212,252,104,
|
||||
200,40,222,13,155,237,217,179,103,111,12,238,35,250,149,190,197,61,182,160,95,216,114,105,133,207,107,25,111,139,154,222,
|
||||
253,249,93,61,198,61,217,6,189,164,133,102,120,77,58,36,218,32,115,147,209,112,136,43,233,57,39,247,123,192,52,126,47,
|
||||
250,177,40,47,8,33,156,54,153,2,133,71,44,147,53,10,195,22,14,18,133,54,57,68,141,109,54,75,141,119,88,226,110,182,106,
|
||||
172,102,216,50,191,98,44,216,140,182,47,162,173,101,26,95,54,26,155,119,25,23,236,134,122,248,94,191,131,28,134,195,196,
|
||||
136,143,138,1,122,205,41,238,192,64,150,8,223,52,139,88,249,115,1,155,102,213,156,66,15,88,230,139,226,57,241,10,87,118,
|
||||
155,5,95,55,68,200,52,161,169,117,191,209,208,100,132,171,109,211,46,88,224,48,29,5,59,45,231,87,208,174,101,181,233,184,
|
||||
93,148,180,192,69,222,16,179,91,118,154,198,205,198,204,102,12,149,70,108,3,91,231,182,118,116,111,218,14,219,105,156,
|
||||
135,133,64,75,135,195,185,211,116,29,17,211,165,148,48,29,100,120,170,33,4,17,219,85,67,239,194,254,213,195,75,134,197,
|
||||
140,105,166,128,141,254,203,22,139,187,107,248,201,248,147,104,132,121,109,163,133,141,140,29,90,211,27,218,115,254,229,
|
||||
54,13,119,211,95,45,54,108,245,146,157,177,133,198,158,234,253,237,157,177,150,26,250,155,45,94,100,239,48,137,77,17,162,
|
||||
27,45,233,191,242,243,18,199,212,186,59,156,226,16,236,184,215,116,98,202,75,238,149,139,231,55,141,55,133,184,237,54,
|
||||
211,250,178,225,195,60,239,54,4,166,107,222,105,136,157,171,77,251,46,99,94,76,184,109,187,197,97,55,240,82,96,150,150,
|
||||
109,219,14,99,164,218,118,46,112,8,88,219,226,201,26,23,116,161,194,97,148,175,98,41,246,54,167,113,183,241,186,112,54,
|
||||
249,77,113,135,33,16,81,208,215,109,188,94,190,157,205,7,62,201,203,37,4,175,150,177,187,186,214,216,221,180,200,54,12,
|
||||
239,42,68,160,79,58,48,42,103,241,5,85,244,176,67,60,37,247,146,233,66,91,12,237,144,16,126,211,249,128,176,107,204,130,
|
||||
151,196,233,207,239,245,63,98,218,28,168,86,155,214,213,98,222,109,194,101,59,155,49,150,234,221,60,200,123,204,66,248,
|
||||
221,189,162,196,107,23,250,141,209,106,12,212,190,213,114,191,195,235,185,179,165,251,244,165,182,123,161,154,12,175,137,
|
||||
93,176,136,231,225,112,57,10,28,133,198,100,151,93,200,242,244,174,30,3,92,22,189,63,122,224,40,111,88,251,90,163,230,22,
|
||||
219,244,35,24,162,103,140,237,17,83,168,97,16,134,177,223,178,101,31,33,135,217,210,221,39,29,195,14,181,74,227,89,116,
|
||||
141,154,221,146,3,22,105,199,130,215,151,172,49,133,156,149,117,159,113,250,18,63,111,28,118,131,3,162,100,154,191,14,
|
||||
158,208,221,51,140,230,176,188,191,10,6,122,208,161,98,195,31,45,131,157,24,165,63,89,114,153,233,74,27,195,112,54,169,
|
||||
97,24,51,118,24,227,213,75,98,70,113,19,166,115,171,240,122,101,39,45,215,183,204,164,67,82,176,56,70,111,217,210,33,54,
|
||||
211,87,212,158,53,130,59,140,225,234,37,7,100,44,178,209,241,217,182,1,47,128,29,121,114,176,64,55,142,139,213,150,245,
|
||||
78,110,236,231,247,217,38,124,28,134,132,15,192,243,97,181,26,83,112,36,125,194,48,239,20,95,21,87,168,224,223,66,207,27,
|
||||
226,98,195,89,221,49,243,190,102,147,190,100,52,210,47,57,200,98,115,154,244,59,209,246,13,155,186,17,195,110,118,97,103,
|
||||
52,211,28,118,223,207,186,196,151,224,239,8,118,251,133,105,120,154,224,42,231,46,89,189,7,222,117,169,172,41,222,223,96,
|
||||
156,95,173,56,70,80,252,220,168,104,52,230,26,151,10,171,224,61,81,94,104,156,10,78,149,85,254,225,114,187,124,168,220,
|
||||
165,30,237,114,81,254,17,48,154,202,11,164,104,125,129,3,178,21,185,102,51,140,38,150,19,21,243,114,188,114,165,188,65,
|
||||
113,12,112,138,115,197,29,57,185,109,198,41,220,214,168,168,175,152,157,233,110,88,246,94,159,235,159,25,103,148,47,207,
|
||||
48,156,229,103,130,209,3,180,65,202,157,97,178,212,202,242,141,248,92,150,97,186,48,244,161,114,51,43,171,21,56,202,141,
|
||||
60,150,28,139,27,99,137,169,177,56,42,26,42,230,84,212,86,4,42,106,42,234,68,185,37,76,81,96,86,26,248,39,140,142,125,
|
||||
251,172,91,230,204,23,207,204,17,226,239,192,161,70,33,126,5,28,12,10,113,23,112,104,174,16,111,2,247,240,143,174,237,
|
||||
181,23,185,72,28,3,108,98,151,67,255,230,70,208,146,139,246,89,135,79,53,47,54,168,71,28,104,18,214,43,77,134,184,178,89,
|
||||
136,155,128,123,128,103,129,55,129,187,91,16,241,29,133,234,215,242,104,243,74,203,74,113,85,171,176,222,108,21,226,234,
|
||||
54,244,11,188,2,236,107,39,67,184,139,13,113,85,96,8,98,87,183,135,197,61,237,66,252,0,248,21,240,58,112,203,60,33,238,7,
|
||||
126,12,60,11,188,9,28,232,32,75,216,94,76,80,112,211,115,208,244,134,142,45,226,209,14,33,158,154,47,196,51,11,160,29,
|
||||
184,122,33,57,157,165,101,74,76,255,55,10,217,103,23,26,198,193,78,97,220,180,200,48,14,44,6,186,76,227,234,238,66,227,
|
||||
192,146,168,245,66,143,41,238,233,53,196,221,125,134,120,182,15,86,3,61,216,143,33,1,119,15,96,40,203,133,56,2,252,106,
|
||||
16,163,15,9,113,120,13,250,2,14,172,53,196,161,181,224,175,67,27,224,224,122,88,192,152,37,248,223,199,208,231,85,27,46,
|
||||
20,247,108,16,226,170,33,254,13,158,223,45,220,23,137,3,251,172,23,134,4,42,247,133,69,193,65,224,210,141,148,251,251,69,
|
||||
249,191,225,201,252,109,62,254,109,74,230,239,243,241,239,82,50,127,163,143,127,151,18,32,245,119,250,248,183,57,153,191,
|
||||
213,231,160,220,223,235,51,189,234,183,52,242,119,83,1,245,183,154,234,75,33,19,80,50,252,255,180,11,175,250,253,61,255,
|
||||
127,232,70,64,245,203,127,223,207,212,242,252,255,75,91,1,245,187,27,254,127,170,237,128,26,31,255,127,240,164,245,200,
|
||||
31,228,121,21,159,255,174,224,255,1,51,160,178,16,144,80,0,0};
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/roli/juce/JuceMidiSupport$MidiDeviceManager;") \
|
||||
STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/roli/juce/JuceMidiSupport$BluetoothManager;")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/roli/juce/JuceMidiSupport", 23, javaMidiByteCode, sizeof (javaMidiByteCode))
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (getJuceAndroidMidiInputDevices, "getJuceAndroidMidiInputDevices", "()[Ljava/lang/String;") \
|
||||
METHOD (getJuceAndroidMidiOutputDevices, "getJuceAndroidMidiOutputDevices", "()[Ljava/lang/String;") \
|
||||
METHOD (openMidiInputPortWithJuceIndex, "openMidiInputPortWithJuceIndex", "(IJ)Lcom/roli/juce/JuceMidiSupport$JuceMidiPort;") \
|
||||
METHOD (openMidiOutputPortWithJuceIndex, "openMidiOutputPortWithJuceIndex", "(I)Lcom/roli/juce/JuceMidiSupport$JuceMidiPort;") \
|
||||
METHOD (getInputPortNameForJuceIndex, "getInputPortNameForJuceIndex", "(I)Ljava/lang/String;") \
|
||||
METHOD (getOutputPortNameForJuceIndex, "getOutputPortNameForJuceIndex", "(I)Ljava/lang/String;")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_MIN_SDK (MidiDeviceManager, "com/roli/juce/JuceMidiSupport$MidiDeviceManager", 23)
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (start, "start", "()V" )\
|
||||
METHOD (stop, "stop", "()V") \
|
||||
METHOD (close, "close", "()V") \
|
||||
METHOD (sendMidi, "sendMidi", "([BII)V")
|
||||
DECLARE_JNI_CLASS (JuceMidiPort, JUCE_ANDROID_ACTIVITY_CLASSPATH "$JuceMidiPort")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiPort, "com/roli/juce/JuceMidiSupport$JuceMidiPort", 23)
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
class AndroidMidiInput
|
||||
@ -51,10 +373,10 @@ public:
|
||||
: juceMidiInput (midiInput),
|
||||
callback (midiInputCallback),
|
||||
midiConcatenator (2048),
|
||||
javaMidiDevice (getEnv()->CallObjectMethod (deviceManager,
|
||||
MidiDeviceManager.openMidiInputPortWithJuceIndex,
|
||||
(jint) portIdx,
|
||||
(jlong) this))
|
||||
javaMidiDevice (LocalRef<jobject>(getEnv()->CallObjectMethod (deviceManager,
|
||||
MidiDeviceManager.openMidiInputPortWithJuceIndex,
|
||||
(jint) portIdx,
|
||||
(jlong) this)))
|
||||
{
|
||||
}
|
||||
|
||||
@ -86,10 +408,12 @@ public:
|
||||
callback = nullptr;
|
||||
}
|
||||
|
||||
void receive (jbyteArray byteArray, jlong offset, jint len, jlong timestamp)
|
||||
void handleMidi (jbyteArray byteArray, jlong offset, jint len, jlong timestamp)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
jassert (byteArray != nullptr);
|
||||
jbyte* data = getEnv()->GetByteArrayElements (byteArray, nullptr);
|
||||
jbyte* data = env->GetByteArrayElements (byteArray, nullptr);
|
||||
|
||||
HeapBlock<uint8> buffer (static_cast<size_t> (len));
|
||||
std::memcpy (buffer.get(), data + offset, static_cast<size_t> (len));
|
||||
@ -98,7 +422,15 @@ public:
|
||||
len, static_cast<double> (timestamp) * 1.0e-9,
|
||||
juceMidiInput, *callback);
|
||||
|
||||
getEnv()->ReleaseByteArrayElements (byteArray, data, 0);
|
||||
env->ReleaseByteArrayElements (byteArray, data, 0);
|
||||
}
|
||||
|
||||
static void handleReceive (JNIEnv*, jobject, jlong host, jbyteArray byteArray,
|
||||
jint offset, jint len, jlong timestamp)
|
||||
{
|
||||
auto* myself = reinterpret_cast<AndroidMidiInput*> (host);
|
||||
|
||||
myself->handleMidi (byteArray, offset, len, timestamp);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -112,7 +444,7 @@ private:
|
||||
class AndroidMidiOutput
|
||||
{
|
||||
public:
|
||||
AndroidMidiOutput (jobject midiDevice)
|
||||
AndroidMidiOutput (const LocalRef<jobject>& midiDevice)
|
||||
: javaMidiDevice (midiDevice)
|
||||
{
|
||||
}
|
||||
@ -138,24 +470,19 @@ private:
|
||||
GlobalRef javaMidiDevice;
|
||||
};
|
||||
|
||||
JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024JuceMidiInputPort), handleReceive,
|
||||
void, (JNIEnv* env, jobject, jlong host, jbyteArray byteArray,
|
||||
jint offset, jint count, jlong timestamp))
|
||||
{
|
||||
// Java may create a Midi thread which JUCE doesn't know about and this callback may be
|
||||
// received on this thread. Java will have already created a JNI Env for this new thread,
|
||||
// which we need to tell JUCE about
|
||||
setEnv (env);
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
CALLBACK (AndroidMidiInput::handleReceive, "handleReceive", "(J[BIIJ)V" )
|
||||
|
||||
reinterpret_cast<AndroidMidiInput*> (host)->receive (byteArray, offset, count, timestamp);
|
||||
}
|
||||
DECLARE_JNI_CLASS_WITH_MIN_SDK (JuceMidiInputPort, "com/roli/juce/JuceMidiSupport$JuceMidiInputPort", 23)
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
class AndroidMidiDeviceManager
|
||||
{
|
||||
public:
|
||||
AndroidMidiDeviceManager()
|
||||
: deviceManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidMidiDeviceManager))
|
||||
: deviceManager (LocalRef<jobject>(getEnv()->CallStaticObjectMethod (JuceMidiSupport, JuceMidiSupport.getAndroidMidiDeviceManager, getAppContext().get())))
|
||||
{
|
||||
}
|
||||
|
||||
@ -187,7 +514,7 @@ public:
|
||||
{
|
||||
jobjectArray jDevices
|
||||
= (jobjectArray) getEnv()->CallObjectMethod (dm, input ? MidiDeviceManager.getJuceAndroidMidiInputDevices
|
||||
: MidiDeviceManager.getJuceAndroidMidiOutputDevices);
|
||||
: MidiDeviceManager.getJuceAndroidMidiOutputDevices);
|
||||
|
||||
// Create a local reference as converting this
|
||||
// to a JUCE string will call into JNI
|
||||
@ -215,7 +542,7 @@ public:
|
||||
{
|
||||
if (jobject dm = deviceManager.get())
|
||||
if (jobject javaMidiPort = getEnv()->CallObjectMethod (dm, MidiDeviceManager.openMidiOutputPortWithJuceIndex, (jint) idx))
|
||||
return new AndroidMidiOutput (javaMidiPort);
|
||||
return new AndroidMidiOutput (LocalRef<jobject>(javaMidiPort));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@ -227,18 +554,23 @@ private:
|
||||
//==============================================================================
|
||||
StringArray MidiOutput::getDevices()
|
||||
{
|
||||
AndroidMidiDeviceManager manager;
|
||||
return manager.getDevices (false);
|
||||
if (getAndroidSDKVersion() >= 23)
|
||||
{
|
||||
AndroidMidiDeviceManager manager;
|
||||
return manager.getDevices (false);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int MidiOutput::getDefaultDeviceIndex()
|
||||
{
|
||||
return 0;
|
||||
return (getAndroidSDKVersion() >= 23 ? 0 : -1);
|
||||
}
|
||||
|
||||
MidiOutput* MidiOutput::openDevice (int index)
|
||||
{
|
||||
if (index < 0)
|
||||
if (index < 0 || getAndroidSDKVersion() < 23)
|
||||
return nullptr;
|
||||
|
||||
AndroidMidiDeviceManager manager;
|
||||
@ -295,18 +627,23 @@ MidiInput::MidiInput (const String& nm) : name (nm)
|
||||
|
||||
StringArray MidiInput::getDevices()
|
||||
{
|
||||
AndroidMidiDeviceManager manager;
|
||||
return manager.getDevices (true);
|
||||
if (getAndroidSDKVersion() >= 23)
|
||||
{
|
||||
AndroidMidiDeviceManager manager;
|
||||
return manager.getDevices (true);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int MidiInput::getDefaultDeviceIndex()
|
||||
{
|
||||
return 0;
|
||||
return (getAndroidSDKVersion() >= 23 ? 0 : -1);
|
||||
}
|
||||
|
||||
MidiInput* MidiInput::openDevice (int index, juce::MidiInputCallback* callback)
|
||||
{
|
||||
if (index < 0)
|
||||
if (getAndroidSDKVersion() < 23 || index < 0)
|
||||
return nullptr;
|
||||
|
||||
AndroidMidiDeviceManager manager;
|
||||
|
@ -125,6 +125,12 @@ struct OboeAudioIODeviceBufferHelpers<float>
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
static String getOboeString (const Type& value)
|
||||
{
|
||||
return String (oboe::convertToText (value));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class OboeAudioIODevice : public AudioIODevice
|
||||
{
|
||||
@ -272,7 +278,7 @@ public:
|
||||
// by default. We need to be more conservative on other devices
|
||||
// as they may be low-latency, but still have a crappy CPU.
|
||||
return (isProAudioDevice() ? 1 : 6)
|
||||
* defaultBufferSizeIsMultipleOfNative * getNativeBufferSize();
|
||||
* getNativeBufferSize();
|
||||
}
|
||||
|
||||
double getCurrentSampleRate() override
|
||||
@ -391,9 +397,9 @@ private:
|
||||
// Setting nullptr callback is allowed only when playback is stopped.
|
||||
jassert (callbackToUse != nullptr);
|
||||
|
||||
while (true)
|
||||
for (;;)
|
||||
{
|
||||
AudioIODeviceCallback* old = callback.get();
|
||||
auto old = callback.get();
|
||||
|
||||
if (old == callbackToUse)
|
||||
break;
|
||||
@ -409,7 +415,7 @@ private:
|
||||
void process (const float** inputChannelData, int numInputChannels,
|
||||
float** outputChannelData, int numOutputChannels, int32_t numFrames)
|
||||
{
|
||||
if (AudioIODeviceCallback* cb = callback.exchange (nullptr))
|
||||
if (auto* cb = callback.exchange (nullptr))
|
||||
{
|
||||
cb->audioDeviceIOCallback (inputChannelData, numInputChannels,
|
||||
outputChannelData, numOutputChannels, numFrames);
|
||||
@ -438,8 +444,8 @@ private:
|
||||
|
||||
~OboeStream()
|
||||
{
|
||||
// AudioStreamCallback can only be deleted when stream has been closed
|
||||
close();
|
||||
delete stream;
|
||||
}
|
||||
|
||||
bool openedOk() const noexcept
|
||||
@ -458,37 +464,47 @@ private:
|
||||
int64 timeoutNanos = 1000 * oboe::kNanosPerMillisecond;
|
||||
|
||||
auto startResult = stream->requestStart();
|
||||
JUCE_OBOE_LOG ("Requested Oboe stream start with result: " + String (oboe::convertToText (startResult)));
|
||||
JUCE_OBOE_LOG ("Requested Oboe stream start with result: " + getOboeString (startResult));
|
||||
|
||||
startResult = stream->waitForStateChange (expectedState, &nextState, timeoutNanos);
|
||||
JUCE_OBOE_LOG ("Starting Oboe stream with result: " + String (oboe::convertToText (startResult));
|
||||
+ "\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?"))
|
||||
+ "\nDirection = " + (stream != nullptr ? String (oboe::convertToText (stream->getDirection())) : String ("?"))
|
||||
+ "\nSharingMode = " + (stream != nullptr ? String (oboe::convertToText (stream->getSharingMode())) : String ("?"))
|
||||
+ "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?"))
|
||||
+ "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?"))
|
||||
+ "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?"))
|
||||
+ "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?"))
|
||||
+ "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?"))
|
||||
+ "\nFramesPerBurst = " + (stream != nullptr ? String (stream->getFramesPerBurst()) : String ("?"))
|
||||
+ "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?"))
|
||||
+ "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?"))
|
||||
+ "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?"))
|
||||
+ "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency))
|
||||
+ "\ngetDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?")));
|
||||
|
||||
JUCE_OBOE_LOG ("Starting Oboe stream with result: " + getOboeString (startResult);
|
||||
+ "\nUses AAudio = " + String ((int) stream->usesAAudio())
|
||||
+ "\nDirection = " + getOboeString (stream->getDirection())
|
||||
+ "\nSharingMode = " + getOboeString (stream->getSharingMode())
|
||||
+ "\nChannelCount = " + String (stream->getChannelCount())
|
||||
+ "\nFormat = " + getOboeString (stream->getFormat())
|
||||
+ "\nSampleRate = " + String (stream->getSampleRate())
|
||||
+ "\nBufferSizeInFrames = " + String (stream->getBufferSizeInFrames())
|
||||
+ "\nBufferCapacityInFrames = " + String (stream->getBufferCapacityInFrames())
|
||||
+ "\nFramesPerBurst = " + String (stream->getFramesPerBurst())
|
||||
+ "\nFramesPerCallback = " + String (stream->getFramesPerCallback())
|
||||
+ "\nBytesPerFrame = " + String (stream->getBytesPerFrame())
|
||||
+ "\nBytesPerSample = " + String (stream->getBytesPerSample())
|
||||
+ "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency)
|
||||
+ "\ngetDeviceId = " + String (stream->getDeviceId()));
|
||||
}
|
||||
}
|
||||
|
||||
oboe::AudioStream* getNativeStream()
|
||||
oboe::AudioStream* getNativeStream() const
|
||||
{
|
||||
jassert (openedOk());
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
int getXRunCount() const
|
||||
{
|
||||
return stream != nullptr ? stream->getXRunCount() : 0;
|
||||
if (stream != nullptr)
|
||||
{
|
||||
auto count = stream->getXRunCount();
|
||||
|
||||
if (count)
|
||||
return count.value();
|
||||
|
||||
JUCE_OBOE_LOG ("Failed to get Xrun count: " + getOboeString (count.error()));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -498,50 +514,50 @@ private:
|
||||
int32 sampleRate, int32 bufferSize,
|
||||
oboe::AudioStreamCallback* callback = nullptr)
|
||||
{
|
||||
oboe::DefaultStreamValues::FramesPerBurst = getDefaultFramesPerBurst();
|
||||
|
||||
oboe::AudioStreamBuilder builder;
|
||||
|
||||
if (deviceId != -1)
|
||||
builder.setDeviceId (deviceId);
|
||||
|
||||
static int defaultFramesPerBurst = getDefaultFramesPerBurst();
|
||||
|
||||
// Note: letting OS to choose the buffer capacity & frames per callback.
|
||||
builder.setDirection (direction);
|
||||
builder.setSharingMode (sharingMode);
|
||||
builder.setChannelCount (channelCount);
|
||||
builder.setFormat (format);
|
||||
builder.setSampleRate (sampleRate);
|
||||
builder.setDefaultFramesPerBurst ((int32) defaultFramesPerBurst);
|
||||
builder.setPerformanceMode (oboe::PerformanceMode::LowLatency);
|
||||
builder.setCallback (callback);
|
||||
|
||||
JUCE_OBOE_LOG (String ("Preparing Oboe stream with params:")
|
||||
+ "\nAAudio supported = " + String (int (builder.isAAudioSupported()))
|
||||
+ "\nAPI = " + String (oboe::convertToText (builder.getAudioApi()))
|
||||
+ "\nAPI = " + getOboeString (builder.getAudioApi())
|
||||
+ "\nDeviceId = " + String (deviceId)
|
||||
+ "\nDirection = " + String (oboe::convertToText (direction))
|
||||
+ "\nSharingMode = " + String (oboe::convertToText (sharingMode))
|
||||
+ "\nDirection = " + getOboeString (direction)
|
||||
+ "\nSharingMode = " + getOboeString (sharingMode)
|
||||
+ "\nChannelCount = " + String (channelCount)
|
||||
+ "\nFormat = " + String (oboe::convertToText (format))
|
||||
+ "\nFormat = " + getOboeString (format)
|
||||
+ "\nSampleRate = " + String (sampleRate)
|
||||
+ "\nBufferSizeInFrames = " + String (bufferSize)
|
||||
+ "\nFramesPerBurst = " + String (defaultFramesPerBurst)
|
||||
+ "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency)));
|
||||
+ "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency));
|
||||
|
||||
openResult = builder.openStream (&stream);
|
||||
JUCE_OBOE_LOG ("Building Oboe stream with result: " + String (oboe::convertToText (openResult))
|
||||
+ "\nStream state = " + (stream != nullptr ? String (oboe::convertToText (stream->getState())) : String ("?")));
|
||||
JUCE_OBOE_LOG ("Building Oboe stream with result: " + getOboeString (openResult)
|
||||
+ "\nStream state = " + (stream != nullptr ? getOboeString (stream->getState()) : String ("?")));
|
||||
|
||||
if (stream != nullptr)
|
||||
if (stream != nullptr && bufferSize != 0)
|
||||
{
|
||||
JUCE_OBOE_LOG ("Setting the bufferSizeInFrames to " + String (bufferSize));
|
||||
stream->setBufferSizeInFrames (bufferSize);
|
||||
}
|
||||
|
||||
JUCE_OBOE_LOG (String ("Stream details:")
|
||||
+ "\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?"))
|
||||
+ "\nDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?"))
|
||||
+ "\nDirection = " + (stream != nullptr ? String (oboe::convertToText (stream->getDirection())) : String ("?"))
|
||||
+ "\nSharingMode = " + (stream != nullptr ? String (oboe::convertToText (stream->getSharingMode())) : String ("?"))
|
||||
+ "\nDirection = " + (stream != nullptr ? getOboeString (stream->getDirection()) : String ("?"))
|
||||
+ "\nSharingMode = " + (stream != nullptr ? getOboeString (stream->getSharingMode()) : String ("?"))
|
||||
+ "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?"))
|
||||
+ "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?"))
|
||||
+ "\nFormat = " + (stream != nullptr ? getOboeString (stream->getFormat()) : String ("?"))
|
||||
+ "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?"))
|
||||
+ "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?"))
|
||||
+ "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?"))
|
||||
@ -549,7 +565,7 @@ private:
|
||||
+ "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?"))
|
||||
+ "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?"))
|
||||
+ "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?"))
|
||||
+ "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency)));
|
||||
+ "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency));
|
||||
}
|
||||
|
||||
void close()
|
||||
@ -557,27 +573,10 @@ private:
|
||||
if (stream != nullptr)
|
||||
{
|
||||
oboe::Result result = stream->close();
|
||||
JUCE_OBOE_LOG ("Requested Oboe stream close with result: " + String (oboe::convertToText (result)));
|
||||
JUCE_OBOE_LOG ("Requested Oboe stream close with result: " + getOboeString (result));
|
||||
}
|
||||
}
|
||||
|
||||
int getDefaultFramesPerBurst() const
|
||||
{
|
||||
// NB: this function only works for inbuilt speakers and headphones
|
||||
auto* env = getEnv();
|
||||
|
||||
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
|
||||
JuceAppActivity.getSystemService,
|
||||
javaString ("audio").get()));
|
||||
|
||||
auto propertyJavaString = javaString ("android.media.property.OUTPUT_FRAMES_PER_BUFFER");
|
||||
|
||||
auto framesPerBurstString = LocalRef<jstring> ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
|
||||
propertyJavaString.get()));
|
||||
|
||||
return framesPerBurstString != 0 ? env->CallStaticIntMethod (JavaInteger, JavaInteger.parseInt, framesPerBurstString.get(), 10) : 192;
|
||||
}
|
||||
|
||||
oboe::AudioStream* stream = nullptr;
|
||||
oboe::Result openResult;
|
||||
};
|
||||
@ -669,20 +668,14 @@ private:
|
||||
void checkStreamSetup (OboeStream* stream, int deviceId, int numChannels, int sampleRate,
|
||||
int bufferSize, oboe::AudioFormat format)
|
||||
{
|
||||
auto* nativeStream = stream != nullptr ? stream->getNativeStream() : nullptr;
|
||||
|
||||
if (nativeStream != nullptr)
|
||||
if (auto* nativeStream = stream != nullptr ? stream->getNativeStream() : nullptr)
|
||||
{
|
||||
ignoreUnused (deviceId, numChannels, sampleRate, bufferSize);
|
||||
ignoreUnused (streamFormat, bitDepth);
|
||||
|
||||
jassert (deviceId = nativeStream->getDeviceId());
|
||||
jassert (numChannels = nativeStream->getChannelCount());
|
||||
jassert (sampleRate == nativeStream->getSampleRate());
|
||||
jassert (numChannels == nativeStream->getChannelCount());
|
||||
jassert (sampleRate == 0 || sampleRate == nativeStream->getSampleRate());
|
||||
jassert (format == nativeStream->getFormat());
|
||||
|
||||
if (nativeStream->usesAAudio())
|
||||
jassert (bufferSize == nativeStream->getBufferSizeInFrames());
|
||||
}
|
||||
}
|
||||
|
||||
@ -737,7 +730,8 @@ private:
|
||||
|
||||
outputStream->start();
|
||||
|
||||
checkIsOutputLatencyDetectionSupported();
|
||||
isInputLatencyDetectionSupported = isLatencyDetectionSupported (inputStream.get());
|
||||
isOutputLatencyDetectionSupported = isLatencyDetectionSupported (outputStream.get());
|
||||
}
|
||||
|
||||
void stop() override
|
||||
@ -752,25 +746,20 @@ private:
|
||||
}
|
||||
|
||||
int getOutputLatencyInSamples() override { return outputLatency; }
|
||||
int getInputLatencyInSamples() override { return -1; }
|
||||
int getInputLatencyInSamples() override { return inputLatency; }
|
||||
|
||||
private:
|
||||
void checkIsOutputLatencyDetectionSupported()
|
||||
bool isLatencyDetectionSupported (OboeStream* stream)
|
||||
{
|
||||
if (! openedOk())
|
||||
{
|
||||
isOutputLatencyDetectionSupported = false;
|
||||
return;
|
||||
}
|
||||
if (stream == nullptr || ! openedOk())
|
||||
return false;
|
||||
|
||||
auto result = outputStream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC, 0, 0);
|
||||
isOutputLatencyDetectionSupported = result != oboe::Result::ErrorUnimplemented;
|
||||
auto result = stream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC, 0, 0);
|
||||
return result != oboe::Result::ErrorUnimplemented;
|
||||
}
|
||||
|
||||
oboe::DataCallbackResult onAudioReady (oboe::AudioStream* stream, void* audioData, int32_t numFrames) override
|
||||
{
|
||||
attachAndroidJNI();
|
||||
|
||||
if (audioCallbackGuard.compareAndSetBool (1, 0))
|
||||
{
|
||||
if (stream == nullptr)
|
||||
@ -790,26 +779,28 @@ private:
|
||||
|
||||
if (nativeInputStream->getFormat() != oboe::AudioFormat::I16 && nativeInputStream->getFormat() != oboe::AudioFormat::Float)
|
||||
{
|
||||
JUCE_OBOE_LOG ("Unsupported input stream audio format: " + String (oboe::convertToText (nativeInputStream->getFormat())));
|
||||
JUCE_OBOE_LOG ("Unsupported input stream audio format: " + getOboeString (nativeInputStream->getFormat()));
|
||||
jassertfalse;
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
auto result = inputStream->getNativeStream()->read (inputStreamNativeBuffer.getData(), numFrames, 0);
|
||||
|
||||
if (result >= 0)
|
||||
if (result)
|
||||
{
|
||||
OboeAudioIODeviceBufferHelpers<SampleType>::referAudioBufferDirectlyToOboeIfPossible (inputStreamNativeBuffer.get(),
|
||||
inputStreamSampleBuffer,
|
||||
result);
|
||||
result.value());
|
||||
|
||||
OboeAudioIODeviceBufferHelpers<SampleType>::convertFromOboe (inputStreamNativeBuffer.get(), inputStreamSampleBuffer, result);
|
||||
OboeAudioIODeviceBufferHelpers<SampleType>::convertFromOboe (inputStreamNativeBuffer.get(), inputStreamSampleBuffer, result.value());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failed to read from input stream.
|
||||
jassertfalse;
|
||||
JUCE_OBOE_LOG ("Failed to read from input stream: " + getOboeString (result.error()));
|
||||
}
|
||||
|
||||
if (isInputLatencyDetectionSupported)
|
||||
inputLatency = getLatencyFor (*inputStream);
|
||||
}
|
||||
|
||||
//-----------------
|
||||
@ -832,7 +823,7 @@ private:
|
||||
OboeAudioIODeviceBufferHelpers<SampleType>::convertToOboe (outputStreamSampleBuffer, static_cast<SampleType*> (audioData), numFrames);
|
||||
|
||||
if (isOutputLatencyDetectionSupported)
|
||||
calculateOutputLatency();
|
||||
outputLatency = getLatencyFor (*outputStream);
|
||||
|
||||
audioCallbackGuard.set (0);
|
||||
}
|
||||
@ -845,10 +836,10 @@ private:
|
||||
ignoreUnused (stream);
|
||||
|
||||
JUCE_OBOE_LOG ("\nUses AAudio = " + (stream != nullptr ? String ((int) stream->usesAAudio()) : String ("?"))
|
||||
+ "\nDirection = " + (stream != nullptr ? String (oboe::convertToText (stream->getDirection())) : String ("?"))
|
||||
+ "\nSharingMode = " + (stream != nullptr ? String (oboe::convertToText (stream->getSharingMode())) : String ("?"))
|
||||
+ "\nDirection = " + (stream != nullptr ? getOboeString (stream->getDirection()) : String ("?"))
|
||||
+ "\nSharingMode = " + (stream != nullptr ? getOboeString (stream->getSharingMode()) : String ("?"))
|
||||
+ "\nChannelCount = " + (stream != nullptr ? String (stream->getChannelCount()) : String ("?"))
|
||||
+ "\nFormat = " + (stream != nullptr ? String (oboe::convertToText (stream->getFormat())) : String ("?"))
|
||||
+ "\nFormat = " + (stream != nullptr ? getOboeString (stream->getFormat()) : String ("?"))
|
||||
+ "\nSampleRate = " + (stream != nullptr ? String (stream->getSampleRate()) : String ("?"))
|
||||
+ "\nBufferSizeInFrames = " + (stream != nullptr ? String (stream->getBufferSizeInFrames()) : String ("?"))
|
||||
+ "\nBufferCapacityInFrames = " + (stream != nullptr ? String (stream->getBufferCapacityInFrames()) : String ("?"))
|
||||
@ -856,34 +847,48 @@ private:
|
||||
+ "\nFramesPerCallback = " + (stream != nullptr ? String (stream->getFramesPerCallback()) : String ("?"))
|
||||
+ "\nBytesPerFrame = " + (stream != nullptr ? String (stream->getBytesPerFrame()) : String ("?"))
|
||||
+ "\nBytesPerSample = " + (stream != nullptr ? String (stream->getBytesPerSample()) : String ("?"))
|
||||
+ "\nPerformanceMode = " + String (oboe::convertToText (oboe::PerformanceMode::LowLatency))
|
||||
+ "\nPerformanceMode = " + getOboeString (oboe::PerformanceMode::LowLatency)
|
||||
+ "\ngetDeviceId = " + (stream != nullptr ? String (stream->getDeviceId()) : String ("?")));
|
||||
}
|
||||
|
||||
void calculateOutputLatency()
|
||||
int getLatencyFor (OboeStream& stream)
|
||||
{
|
||||
// Sadly, Oboe uses non-portable int64_t (a.k.a. long on LP64 and long long on LLP64)
|
||||
int64_t lastWrittenAndPresentedFrameIndex = 0;
|
||||
int64_t lastFramePresentationTimeNanos = 0;
|
||||
auto& nativeStream = *stream.getNativeStream();
|
||||
|
||||
auto result = outputStream->getNativeStream()->getTimestamp (CLOCK_MONOTONIC,
|
||||
&lastWrittenAndPresentedFrameIndex,
|
||||
&lastFramePresentationTimeNanos);
|
||||
if (auto latency = nativeStream.calculateLatencyMillis())
|
||||
return static_cast<int> ((latency.value() * sampleRate) / 1000);
|
||||
|
||||
// Get the time that a known audio frame was presented.
|
||||
int64_t hardwareFrameIndex = 0;
|
||||
int64_t hardwareFrameHardwareTime = 0;
|
||||
|
||||
auto result = nativeStream.getTimestamp (CLOCK_MONOTONIC,
|
||||
&hardwareFrameIndex,
|
||||
&hardwareFrameHardwareTime);
|
||||
|
||||
if (result != oboe::Result::OK)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
int64_t currentNumFramesWritten = outputStream->getNativeStream()->getFramesWritten();
|
||||
int64_t framesDelta = currentNumFramesWritten - lastWrittenAndPresentedFrameIndex;
|
||||
int64_t timeDeltaNanos = framesDelta * oboe::kNanosPerSecond / sampleRate;
|
||||
// Get counter closest to the app.
|
||||
const bool isOutput = nativeStream.getDirection() == oboe::Direction::Output;
|
||||
const int64_t appFrameIndex = isOutput ? nativeStream.getFramesWritten() : nativeStream.getFramesRead();
|
||||
|
||||
int64_t nextPresentationTimeNanos = lastFramePresentationTimeNanos + timeDeltaNanos;
|
||||
int64_t nextFrameWriteTimeNanos = getCurrentTimeNanos();
|
||||
// Assume that the next frame will be processed at the current time
|
||||
using namespace std::chrono;
|
||||
int64_t appFrameAppTime = getCurrentTimeNanos();//duration_cast<nanoseconds> (steady_clock::now().time_since_epoch()).count();
|
||||
int64_t appFrameAppTime2 = duration_cast<nanoseconds> (steady_clock::now().time_since_epoch()).count();
|
||||
|
||||
if (nextFrameWriteTimeNanos < 0)
|
||||
return;
|
||||
// Calculate the number of frames between app and hardware
|
||||
int64_t frameIndexDelta = appFrameIndex - hardwareFrameIndex;
|
||||
|
||||
outputLatency = (int) ((nextPresentationTimeNanos - nextFrameWriteTimeNanos) * sampleRate / oboe::kNanosPerSecond);
|
||||
// Calculate the time which the next frame will be or was presented
|
||||
int64_t frameTimeDelta = (frameIndexDelta * oboe::kNanosPerSecond) / sampleRate;
|
||||
int64_t appFrameHardwareTime = hardwareFrameHardwareTime + frameTimeDelta;
|
||||
|
||||
// Calculate latency as a difference in time between when the current frame is at the app
|
||||
// and when it is at the hardware.
|
||||
auto latencyNanos = isOutput ? (appFrameHardwareTime - appFrameAppTime) : (appFrameAppTime - appFrameHardwareTime);
|
||||
return static_cast<int> ((latencyNanos * sampleRate) / oboe::kNanosPerSecond);
|
||||
}
|
||||
|
||||
int64_t getCurrentTimeNanos()
|
||||
@ -901,7 +906,7 @@ private:
|
||||
// only output stream should be the master stream receiving callbacks
|
||||
jassert (stream->getDirection() == oboe::Direction::Output);
|
||||
|
||||
JUCE_OBOE_LOG ("Oboe stream onErrorBeforeClose(): " + String (oboe::convertToText (error)));
|
||||
JUCE_OBOE_LOG ("Oboe stream onErrorBeforeClose(): " + getOboeString (error));
|
||||
printStreamDebugInfo (stream);
|
||||
}
|
||||
|
||||
@ -910,7 +915,7 @@ private:
|
||||
// only output stream should be the master stream receiving callbacks
|
||||
jassert (stream->getDirection() == oboe::Direction::Output);
|
||||
|
||||
JUCE_OBOE_LOG ("Oboe stream onErrorAfterClose(): " + String (oboe::convertToText (error)));
|
||||
JUCE_OBOE_LOG ("Oboe stream onErrorAfterClose(): " + getOboeString (error));
|
||||
|
||||
if (error == oboe::Result::ErrorDisconnected)
|
||||
{
|
||||
@ -946,7 +951,10 @@ private:
|
||||
Atomic<int> audioCallbackGuard { 0 },
|
||||
streamRestartGuard { 0 };
|
||||
|
||||
bool isOutputLatencyDetectionSupported = true;
|
||||
bool isInputLatencyDetectionSupported = false;
|
||||
int inputLatency = -1;
|
||||
|
||||
bool isOutputLatencyDetectionSupported = false;
|
||||
int outputLatency = -1;
|
||||
};
|
||||
|
||||
@ -972,32 +980,7 @@ private:
|
||||
|
||||
bool running = false;
|
||||
|
||||
enum
|
||||
{
|
||||
// These at the moment correspond to OpenSL settings.
|
||||
bufferSizeMultForLowLatency = 4,
|
||||
bufferSizeMultForSlowAudio = 8,
|
||||
defaultBufferSizeIsMultipleOfNative = 1
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static String audioManagerGetProperty (const String& property)
|
||||
{
|
||||
const LocalRef<jstring> jProperty (javaString (property));
|
||||
const LocalRef<jstring> text ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
|
||||
jProperty.get()));
|
||||
if (text.get() != 0)
|
||||
return juceString (text);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static bool androidHasSystemFeature (const String& property)
|
||||
{
|
||||
const LocalRef<jstring> jProperty (javaString (property));
|
||||
return android.activity.callBooleanMethod (JuceAppActivity.hasSystemFeature, jProperty.get());
|
||||
}
|
||||
|
||||
static double getNativeSampleRate()
|
||||
{
|
||||
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
|
||||
@ -1014,6 +997,14 @@ private:
|
||||
return androidHasSystemFeature ("android.hardware.audio.pro");
|
||||
}
|
||||
|
||||
static int getDefaultFramesPerBurst()
|
||||
{
|
||||
// NB: this function only works for inbuilt speakers and headphones
|
||||
auto framesPerBurstString = javaString (audioManagerGetProperty ("android.media.property.OUTPUT_FRAMES_PER_BUFFER"));
|
||||
|
||||
return framesPerBurstString != 0 ? getEnv()->CallStaticIntMethod (JavaInteger, JavaInteger.parseInt, framesPerBurstString.get(), 10) : 192;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OboeAudioIODevice)
|
||||
};
|
||||
|
||||
@ -1028,7 +1019,7 @@ OboeAudioIODevice::OboeSessionBase* OboeAudioIODevice::OboeSessionBase::create (
|
||||
{
|
||||
|
||||
std::unique_ptr<OboeSessionBase> session;
|
||||
auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
|
||||
auto sdkVersion = getAndroidSDKVersion();
|
||||
|
||||
// SDK versions 21 and higher should natively support floating point...
|
||||
if (sdkVersion >= 21)
|
||||
@ -1096,7 +1087,7 @@ public:
|
||||
forInput ? oboe::Direction::Input : oboe::Direction::Output,
|
||||
oboe::SharingMode::Shared,
|
||||
forInput ? 1 : 2,
|
||||
getSdkVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
|
||||
getAndroidSDKVersion() >= 21 ? oboe::AudioFormat::Float : oboe::AudioFormat::I16,
|
||||
(int) OboeAudioIODevice::getNativeSampleRate(),
|
||||
OboeAudioIODevice::getNativeBufferSize(),
|
||||
nullptr);
|
||||
@ -1115,18 +1106,17 @@ public:
|
||||
|
||||
int getIndexOfDevice (AudioIODevice* device, bool asInput) const override
|
||||
{
|
||||
if (device == nullptr)
|
||||
return -1;
|
||||
if (auto oboeDevice = static_cast<OboeAudioIODevice*> (device))
|
||||
{
|
||||
auto oboeDeviceId = asInput ? oboeDevice->inputDeviceId
|
||||
: oboeDevice->outputDeviceId;
|
||||
|
||||
auto* oboeDevice = static_cast<OboeAudioIODevice*> (device);
|
||||
auto oboeDeviceId = asInput ? oboeDevice->inputDeviceId
|
||||
: oboeDevice->outputDeviceId;
|
||||
auto& devices = asInput ? inputDevices : outputDevices;
|
||||
|
||||
auto& devices = asInput ? inputDevices : outputDevices;
|
||||
|
||||
for (int i = 0; i < devices.size(); ++i)
|
||||
if (devices.getReference (i).id == oboeDeviceId)
|
||||
return i;
|
||||
for (int i = 0; i < devices.size(); ++i)
|
||||
if (devices.getReference (i).id == oboeDeviceId)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -1188,8 +1178,8 @@ public:
|
||||
if (audioManagerClass == 0)
|
||||
return;
|
||||
|
||||
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (android.activity,
|
||||
JuceAppActivity.getSystemService,
|
||||
auto audioManager = LocalRef<jobject> (env->CallObjectMethod (getAppContext().get(),
|
||||
AndroidContext.getSystemService,
|
||||
javaString ("audio").get()));
|
||||
|
||||
static jmethodID getDevicesMethod = env->GetMethodID (audioManagerClass, "getDevices",
|
||||
@ -1231,16 +1221,10 @@ public:
|
||||
|
||||
bool supportsDevicesInfo() const
|
||||
{
|
||||
static auto result = getSdkVersion() >= 23;
|
||||
static auto result = getAndroidSDKVersion() >= 23;
|
||||
return result;
|
||||
}
|
||||
|
||||
int getSdkVersion() const
|
||||
{
|
||||
static auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
|
||||
return sdkVersion;
|
||||
}
|
||||
|
||||
void addDevice (const LocalRef<jobject>& device, JNIEnv* env)
|
||||
{
|
||||
auto deviceClass = LocalRef<jclass> ((jclass) env->FindClass ("android/media/AudioDeviceInfo"));
|
||||
@ -1397,7 +1381,7 @@ public:
|
||||
return testStream != nullptr && testStream->openedOk();
|
||||
}
|
||||
|
||||
pthread_t startThread (void* (*entry) (void*), void* userPtr)
|
||||
pthread_t startThread (void*(*entry)(void*), void* userPtr)
|
||||
{
|
||||
pthread_mutex_lock (&threadReadyMutex);
|
||||
|
||||
@ -1437,26 +1421,16 @@ public:
|
||||
|
||||
void onErrorBeforeClose (oboe::AudioStream*, oboe::Result error) override
|
||||
{
|
||||
JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorBeforeClose(): " + String (oboe::convertToText (error)));
|
||||
JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorBeforeClose(): " + getOboeString (error));
|
||||
ignoreUnused (error);
|
||||
jassertfalse; // Should never get here!
|
||||
}
|
||||
|
||||
void onErrorAfterClose (oboe::AudioStream* stream, oboe::Result error) override
|
||||
void onErrorAfterClose (oboe::AudioStream*, oboe::Result error) override
|
||||
{
|
||||
JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorAfterClose(): " + String (oboe::convertToText (error)));
|
||||
|
||||
if (error == oboe::Result::ErrorDisconnected)
|
||||
{
|
||||
testStream.reset();
|
||||
testStream.reset (new OboeStream (-1,
|
||||
oboe::Direction::Output,
|
||||
oboe::SharingMode::Exclusive,
|
||||
1,
|
||||
formatUsed,
|
||||
(int) OboeAudioIODevice::getNativeSampleRate(),
|
||||
OboeAudioIODevice::getNativeBufferSize(),
|
||||
this));
|
||||
testStream->start();
|
||||
}
|
||||
JUCE_OBOE_LOG ("OboeRealtimeThread: Oboe stream onErrorAfterClose(): " + getOboeString (error));
|
||||
ignoreUnused (error);
|
||||
jassertfalse; // Should never get here!
|
||||
}
|
||||
|
||||
private:
|
||||
@ -1480,10 +1454,7 @@ pthread_t juce_createRealtimeAudioThread (void* (*entry) (void*), void* userPtr)
|
||||
return {};
|
||||
|
||||
auto threadID = thread->startThread (entry, userPtr);
|
||||
|
||||
// the thread will de-allocate itself
|
||||
thread.release();
|
||||
|
||||
thread.release(); // the thread will de-allocate itself
|
||||
return threadID;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,10 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
DECLARE_JNI_CLASS (AndroidAudioManager, "android/media/AudioManager")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
//==============================================================================
|
||||
#ifndef SL_ANDROID_DATAFORMAT_PCM_EX
|
||||
#define SL_ANDROID_DATAFORMAT_PCM_EX ((SLuint32) 0x00000004)
|
||||
@ -60,6 +64,22 @@ SLInterfaceID_ IntfIID<SLRecordItf_>::iid = { 0xc5657aa0, 0xdd
|
||||
SLInterfaceID_ IntfIID<SLAndroidSimpleBufferQueueItf_>::iid = { 0x198e4940, 0xc5d7, 0x11df, 0xa2a6, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b} };
|
||||
SLInterfaceID_ IntfIID<SLAndroidConfigurationItf_>::iid = { 0x89f6a7e0, 0xbeac, 0x11df, 0x8b5c, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b} };
|
||||
|
||||
template <typename SLObjectType>
|
||||
static void destroyObject (SLObjectType object)
|
||||
{
|
||||
if (object != nullptr && *object != nullptr)
|
||||
(*object)->Destroy (object);
|
||||
}
|
||||
|
||||
template <>
|
||||
struct ContainerDeletePolicy<const SLObjectItf_* const>
|
||||
{
|
||||
static void destroy (SLObjectItf object)
|
||||
{
|
||||
destroyObject (object);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Some life-time and type management of OpenSL objects
|
||||
class SlObjectRef
|
||||
@ -67,27 +87,34 @@ class SlObjectRef
|
||||
public:
|
||||
//==============================================================================
|
||||
SlObjectRef() noexcept {}
|
||||
SlObjectRef (const SlObjectRef& obj) noexcept : cb (obj.cb) {}
|
||||
SlObjectRef (SlObjectRef&& obj) noexcept : cb (static_cast<ReferenceCountedObjectPtr<ControlBlock>&&> (obj.cb)) { obj.cb = nullptr; }
|
||||
SlObjectRef (const SlObjectRef& obj) noexcept : cb (obj.cb) {}
|
||||
SlObjectRef (SlObjectRef&& obj) noexcept : cb (std::move (obj.cb)) { obj.cb = nullptr; }
|
||||
explicit SlObjectRef (SLObjectItf o) : cb (new ControlBlock (o)) {}
|
||||
|
||||
//==============================================================================
|
||||
SlObjectRef& operator=(const SlObjectRef& r) noexcept { cb = r.cb; return *this; }
|
||||
SlObjectRef& operator=(SlObjectRef&& r) noexcept { cb = static_cast<ReferenceCountedObjectPtr<ControlBlock>&&> (r.cb); r.cb = nullptr; return *this; }
|
||||
SlObjectRef& operator=(std::nullptr_t) noexcept { cb = nullptr; return *this; }
|
||||
SlObjectRef& operator= (const SlObjectRef& r) noexcept { cb = r.cb; return *this; }
|
||||
SlObjectRef& operator= (SlObjectRef&& r) noexcept { cb = std::move (r.cb); r.cb = nullptr; return *this; }
|
||||
SlObjectRef& operator= (std::nullptr_t) noexcept { cb = nullptr; return *this; }
|
||||
|
||||
//==============================================================================
|
||||
const SLObjectItf_* const operator*() noexcept { return *cb->ptr.get(); }
|
||||
SLObjectItf operator->() noexcept { return (cb == nullptr ? nullptr : cb->ptr.get()); }
|
||||
operator SLObjectItf() noexcept { return (cb == nullptr ? nullptr : cb->ptr.get()); }
|
||||
const SLObjectItf_* operator*() noexcept { return *cb->ptr.get(); }
|
||||
SLObjectItf operator->() noexcept { return (cb == nullptr ? nullptr : cb->ptr.get()); }
|
||||
operator SLObjectItf() noexcept { return (cb == nullptr ? nullptr : cb->ptr.get()); }
|
||||
|
||||
//==============================================================================
|
||||
bool operator== (nullptr_t) const noexcept { return (cb == nullptr || cb->ptr == nullptr); }
|
||||
bool operator!= (nullptr_t) const noexcept { return (cb != nullptr && cb->ptr != nullptr); }
|
||||
bool operator== (nullptr_t) const noexcept { return (cb == nullptr || cb->ptr == nullptr); }
|
||||
bool operator!= (nullptr_t) const noexcept { return (cb != nullptr && cb->ptr != nullptr); }
|
||||
|
||||
private:
|
||||
|
||||
//==============================================================================
|
||||
struct ControlBlock : ReferenceCountedObject { std::unique_ptr<const SLObjectItf_* const> ptr; ControlBlock() {} ControlBlock (SLObjectItf o) : ptr (o) {} };
|
||||
struct ControlBlock : ReferenceCountedObject
|
||||
{
|
||||
ControlBlock() = default;
|
||||
ControlBlock (SLObjectItf o) : ptr (o) {}
|
||||
|
||||
std::unique_ptr<const SLObjectItf_* const> ptr;
|
||||
};
|
||||
|
||||
ReferenceCountedObjectPtr<ControlBlock> cb;
|
||||
};
|
||||
|
||||
@ -96,53 +123,53 @@ class SlRef : public SlObjectRef
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
SlRef() noexcept : type (nullptr) {}
|
||||
SlRef (SlRef& r) noexcept : SlObjectRef (r), type (r.type) {}
|
||||
SlRef (SlRef&& r) noexcept : SlObjectRef (static_cast<SlRef&&> (r)), type (r.type) { r.type = nullptr; }
|
||||
SlRef() noexcept {}
|
||||
SlRef (const SlRef& r) noexcept : SlObjectRef (r), type (r.type) {}
|
||||
SlRef (SlRef&& r) noexcept : SlObjectRef (std::move (r)), type (r.type) { r.type = nullptr; }
|
||||
|
||||
//==============================================================================
|
||||
SlRef& operator= (const SlRef& r) noexcept { SlObjectRef::operator= (r); type = r.type; return *this; }
|
||||
SlRef& operator= (SlRef&& r) noexcept { SlObjectRef::operator= (static_cast<SlObjectRef&&> (r)); type = r.type; r.type = nullptr; return *this; }
|
||||
SlRef& operator= (std::nullptr_t) noexcept { SlObjectRef::operator= (nullptr); type = nullptr; return *this; }
|
||||
SlRef& operator= (SlRef&& r) noexcept { SlObjectRef::operator= (std::move (r)); type = r.type; r.type = nullptr; return *this; }
|
||||
SlRef& operator= (std::nullptr_t) noexcept { SlObjectRef::operator= (nullptr); type = nullptr; return *this; }
|
||||
|
||||
//==============================================================================
|
||||
T* const operator*() noexcept { return *type; }
|
||||
T* const * operator->() noexcept { return type; }
|
||||
operator T* const *() noexcept { return type; }
|
||||
T* const operator*() noexcept { return *type; }
|
||||
T* const* operator->() noexcept { return type; }
|
||||
operator T* const*() noexcept { return type; }
|
||||
|
||||
//==============================================================================
|
||||
static SlRef cast (SlObjectRef& base) { return SlRef (base); }
|
||||
static SlRef cast (SlObjectRef&& base) { return SlRef (static_cast<SlObjectRef&&> (base)); }
|
||||
static SlRef cast (SlObjectRef& base) { return SlRef (base); }
|
||||
static SlRef cast (SlObjectRef&& base) { return SlRef (std::move (base)); }
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
SlRef (SlObjectRef& base) : SlObjectRef (base)
|
||||
{
|
||||
SLObjectItf obj = SlObjectRef::operator->();
|
||||
SLresult err = (*obj)->GetInterface (obj, &IntfIID<T>::iid, &type);
|
||||
if (type == nullptr || err != SL_RESULT_SUCCESS)
|
||||
*this = nullptr;
|
||||
if (auto obj = SlObjectRef::operator->())
|
||||
{
|
||||
auto err = (*obj)->GetInterface (obj, &IntfIID<T>::iid, &type);
|
||||
|
||||
if (type != nullptr && err == SL_RESULT_SUCCESS)
|
||||
return;
|
||||
}
|
||||
|
||||
*this = nullptr;
|
||||
}
|
||||
|
||||
SlRef (SlObjectRef&& base) : SlObjectRef (static_cast<SlObjectRef&&> (base))
|
||||
SlRef (SlObjectRef&& base) : SlObjectRef (std::move (base))
|
||||
{
|
||||
SLObjectItf obj = SlObjectRef::operator->();
|
||||
SLresult err = (*obj)->GetInterface (obj, &IntfIID<T>::iid, &type);
|
||||
base = nullptr;
|
||||
if (auto obj = SlObjectRef::operator->())
|
||||
{
|
||||
auto err = (*obj)->GetInterface (obj, &IntfIID<T>::iid, &type);
|
||||
base = nullptr;
|
||||
|
||||
if (type == nullptr || err != SL_RESULT_SUCCESS)
|
||||
*this = nullptr;
|
||||
}
|
||||
T* const * type;
|
||||
};
|
||||
if (type != nullptr && err == SL_RESULT_SUCCESS)
|
||||
return;
|
||||
}
|
||||
|
||||
template <>
|
||||
struct ContainerDeletePolicy<const SLObjectItf_* const>
|
||||
{
|
||||
static void destroy (SLObjectItf object)
|
||||
{
|
||||
if (object != nullptr)
|
||||
(*object)->Destroy (object);
|
||||
*this = nullptr;
|
||||
}
|
||||
|
||||
T* const* type = nullptr;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
@ -281,8 +308,7 @@ public:
|
||||
numChannels (numChannelsToUse),
|
||||
nativeBuffer (static_cast<size_t> (numChannels * owner.bufferSize * owner.numBuffers)),
|
||||
scratchBuffer (numChannelsToUse, owner.bufferSize),
|
||||
sampleBuffer (scratchBuffer.getArrayOfWritePointers(), numChannelsToUse, owner.bufferSize),
|
||||
nextBlock (0), numBlocksOut (0)
|
||||
sampleBuffer (scratchBuffer.getArrayOfWritePointers(), numChannelsToUse, owner.bufferSize)
|
||||
{}
|
||||
|
||||
~OpenSLQueueRunner()
|
||||
@ -297,10 +323,11 @@ public:
|
||||
bool init()
|
||||
{
|
||||
runner = crtp().createPlayerOrRecorder();
|
||||
|
||||
if (runner == nullptr)
|
||||
return false;
|
||||
|
||||
const bool supportsJavaProxy = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 24);
|
||||
const bool supportsJavaProxy = (getAndroidSDKVersion() >= 24);
|
||||
|
||||
if (supportsJavaProxy)
|
||||
{
|
||||
@ -314,18 +341,18 @@ public:
|
||||
&audioRoutingJni);
|
||||
|
||||
if (status == SL_RESULT_SUCCESS && audioRoutingJni != 0)
|
||||
javaProxy = GlobalRef (audioRoutingJni);
|
||||
javaProxy = GlobalRef (LocalRef<jobject>(getEnv()->NewLocalRef (audioRoutingJni)));
|
||||
}
|
||||
}
|
||||
|
||||
queue = SlRef<SLAndroidSimpleBufferQueueItf_>::cast (runner);
|
||||
|
||||
if (queue == nullptr)
|
||||
return false;
|
||||
|
||||
return ((*queue)->RegisterCallback (queue, staticFinished, this) == SL_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
void clear()
|
||||
{
|
||||
nextBlock.set (0);
|
||||
@ -349,8 +376,6 @@ public:
|
||||
|
||||
void finished (SLAndroidSimpleBufferQueueItf)
|
||||
{
|
||||
attachAndroidJNI();
|
||||
|
||||
--numBlocksOut;
|
||||
owner.doSomeWorkOnAudioThread();
|
||||
}
|
||||
@ -376,7 +401,7 @@ public:
|
||||
HeapBlock<T> nativeBuffer;
|
||||
AudioBuffer<float> scratchBuffer, sampleBuffer;
|
||||
|
||||
Atomic<int> nextBlock, numBlocksOut;
|
||||
Atomic<int> nextBlock { 0 }, numBlocksOut { 0 };
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
@ -385,35 +410,36 @@ public:
|
||||
{
|
||||
using Base = OpenSLQueueRunner<T, OpenSLQueueRunnerPlayer<T>, SLPlayItf_>;
|
||||
|
||||
enum { isPlayer = 1 };
|
||||
|
||||
OpenSLQueueRunnerPlayer (OpenSLSessionT<T>& sessionToUse, int numChannelsToUse)
|
||||
: Base (sessionToUse, numChannelsToUse)
|
||||
{}
|
||||
|
||||
SlRef<SLPlayItf_> createPlayerOrRecorder()
|
||||
{
|
||||
SLDataLocator_AndroidSimpleBufferQueue queueLocator = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast<SLuint32> (Base::owner.numBuffers)};
|
||||
SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, Base::owner.outputMix};
|
||||
SLDataLocator_AndroidSimpleBufferQueue queueLocator = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast<SLuint32> (Base::owner.numBuffers) };
|
||||
SLDataLocator_OutputMix outputMix = { SL_DATALOCATOR_OUTPUTMIX, Base::owner.outputMix };
|
||||
|
||||
PCMDataFormatEx dataFormat;
|
||||
BufferHelpers<T>::initPCMDataFormat (dataFormat, Base::numChannels, Base::owner.sampleRate);
|
||||
|
||||
SLDataSource source = {&queueLocator, &dataFormat};
|
||||
SLDataSink sink = {&outputMix, nullptr};
|
||||
SLDataSource source = { &queueLocator, &dataFormat };
|
||||
SLDataSink sink = { &outputMix, nullptr };
|
||||
|
||||
SLInterfaceID queueInterfaces[] = { &IntfIID<SLAndroidSimpleBufferQueueItf_>::iid, &IntfIID<SLAndroidConfigurationItf_>::iid };
|
||||
SLboolean interfaceRequired[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE};
|
||||
|
||||
SLObjectItf obj = nullptr;
|
||||
|
||||
SLresult status = (*Base::owner.engine)->CreateAudioPlayer (Base::owner.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired);
|
||||
if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
if (auto e = *Base::owner.engine)
|
||||
{
|
||||
if (obj != nullptr)
|
||||
(*obj)->Destroy (obj);
|
||||
auto status = e->CreateAudioPlayer (Base::owner.engine, &obj, &source, &sink, 2,
|
||||
queueInterfaces, interfaceRequired);
|
||||
|
||||
return SlRef<SLPlayItf_>();
|
||||
if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize(obj, 0) != SL_RESULT_SUCCESS)
|
||||
{
|
||||
destroyObject (obj);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
return SlRef<SLPlayItf_>::cast (SlObjectRef (obj));
|
||||
@ -423,58 +449,56 @@ public:
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct OpenSLQueueRunnerRecorder : OpenSLQueueRunner<T, OpenSLQueueRunnerRecorder<T>, SLRecordItf_>
|
||||
struct OpenSLQueueRunnerRecorder : public OpenSLQueueRunner<T, OpenSLQueueRunnerRecorder<T>, SLRecordItf_>
|
||||
{
|
||||
using Base = OpenSLQueueRunner<T, OpenSLQueueRunnerRecorder<T>, SLRecordItf_>;
|
||||
|
||||
enum { isPlayer = 0 };
|
||||
|
||||
OpenSLQueueRunnerRecorder (OpenSLSessionT<T>& sessionToUse, int numChannelsToUse)
|
||||
: Base (sessionToUse, numChannelsToUse)
|
||||
{}
|
||||
|
||||
SlRef<SLRecordItf_> createPlayerOrRecorder()
|
||||
{
|
||||
SLDataLocator_IODevice ioDeviceLocator = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr};
|
||||
SLDataLocator_AndroidSimpleBufferQueue queueLocator = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast<SLuint32> (Base::owner.numBuffers)};
|
||||
SLDataLocator_IODevice ioDeviceLocator = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr };
|
||||
SLDataLocator_AndroidSimpleBufferQueue queueLocator = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, static_cast<SLuint32> (Base::owner.numBuffers) };
|
||||
|
||||
PCMDataFormatEx dataFormat;
|
||||
BufferHelpers<T>::initPCMDataFormat (dataFormat, Base::numChannels, Base::owner.sampleRate);
|
||||
|
||||
SLDataSource source = {&ioDeviceLocator, nullptr};
|
||||
SLDataSink sink = {&queueLocator, &dataFormat};
|
||||
SLDataSource source = { &ioDeviceLocator, nullptr };
|
||||
SLDataSink sink = { &queueLocator, &dataFormat };
|
||||
|
||||
SLInterfaceID queueInterfaces[] = { &IntfIID<SLAndroidSimpleBufferQueueItf_>::iid, &IntfIID<SLAndroidConfigurationItf_>::iid };
|
||||
SLboolean interfaceRequired[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE};
|
||||
SLboolean interfaceRequired[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
|
||||
|
||||
SLObjectItf obj = nullptr;
|
||||
|
||||
SLresult status = (*Base::owner.engine)->CreateAudioRecorder (Base::owner.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired);
|
||||
if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
if (auto e = *Base::owner.engine)
|
||||
{
|
||||
if (obj != nullptr)
|
||||
(*obj)->Destroy (obj);
|
||||
auto status = e->CreateAudioRecorder (Base::owner.engine, &obj, &source, &sink, 2, queueInterfaces, interfaceRequired);
|
||||
|
||||
return SlRef<SLRecordItf_>();
|
||||
if (status != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
{
|
||||
destroyObject (obj);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
SlRef<SLRecordItf_> recorder = SlRef<SLRecordItf_>::cast (SlObjectRef (obj));
|
||||
|
||||
return recorder;
|
||||
return SlRef<SLRecordItf_>::cast (SlObjectRef (obj));
|
||||
}
|
||||
|
||||
bool setAudioPreprocessingEnabled (bool shouldEnable)
|
||||
{
|
||||
if (Base::config != nullptr)
|
||||
{
|
||||
const bool supportsUnprocessed = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 25);
|
||||
const bool supportsUnprocessed = (getAndroidSDKVersion() >= 25);
|
||||
const SLuint32 recordingPresetValue
|
||||
= (shouldEnable ? SL_ANDROID_RECORDING_PRESET_GENERIC
|
||||
: (supportsUnprocessed ? SL_ANDROID_RECORDING_PRESET_UNPROCESSED
|
||||
: SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION));
|
||||
|
||||
SLresult status = (*Base::config)->SetConfiguration (Base::config, SL_ANDROID_KEY_RECORDING_PRESET,
|
||||
&recordingPresetValue, sizeof (recordingPresetValue));
|
||||
auto status = (*Base::config)->SetConfiguration (Base::config, SL_ANDROID_KEY_RECORDING_PRESET,
|
||||
&recordingPresetValue, sizeof (recordingPresetValue));
|
||||
|
||||
return (status == SL_RESULT_SUCCESS);
|
||||
}
|
||||
@ -482,7 +506,8 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
void setState (bool running) { (*Base::runner)->SetRecordState (Base::runner, running ? SL_RECORDSTATE_RECORDING : SL_RECORDSTATE_STOPPED); }
|
||||
void setState (bool running) { (*Base::runner)->SetRecordState (Base::runner, running ? SL_RECORDSTATE_RECORDING
|
||||
: SL_RECORDSTATE_STOPPED); }
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
@ -494,21 +519,19 @@ public:
|
||||
double samleRateToUse, int bufferSizeToUse,
|
||||
int numBuffersToUse)
|
||||
: inputChannels (numInputChannels), outputChannels (numOutputChannels),
|
||||
sampleRate (samleRateToUse), bufferSize (bufferSizeToUse), numBuffers (numBuffersToUse),
|
||||
running (false), audioProcessingEnabled (true), callback (nullptr)
|
||||
sampleRate (samleRateToUse), bufferSize (bufferSizeToUse), numBuffers (numBuffersToUse)
|
||||
{
|
||||
jassert (numInputChannels > 0 || numOutputChannels > 0);
|
||||
|
||||
if (CreateEngineFunc createEngine = (CreateEngineFunc) slLibraryToUse.getFunction ("slCreateEngine"))
|
||||
if (auto createEngine = (CreateEngineFunc) slLibraryToUse.getFunction ("slCreateEngine"))
|
||||
{
|
||||
SLObjectItf obj = nullptr;
|
||||
auto err = createEngine (&obj, 0, nullptr, 0, nullptr, nullptr);
|
||||
|
||||
SLresult err = createEngine (&obj, 0, nullptr, 0, nullptr, nullptr);
|
||||
if (err != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
if (err != SL_RESULT_SUCCESS || obj == nullptr || *obj == nullptr
|
||||
|| (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
{
|
||||
if (obj != nullptr)
|
||||
(*obj)->Destroy (obj);
|
||||
|
||||
destroyObject (obj);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -518,13 +541,12 @@ public:
|
||||
if (outputChannels > 0)
|
||||
{
|
||||
SLObjectItf obj = nullptr;
|
||||
auto err = (*engine)->CreateOutputMix (engine, &obj, 0, nullptr, nullptr);
|
||||
|
||||
SLresult err = (*engine)->CreateOutputMix (engine, &obj, 0, nullptr, nullptr);
|
||||
if (err != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
if (err != SL_RESULT_SUCCESS || obj == nullptr || *obj == nullptr
|
||||
|| (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
{
|
||||
if (obj != nullptr)
|
||||
(*obj)->Destroy (obj);
|
||||
|
||||
destroyObject (obj);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -537,6 +559,7 @@ public:
|
||||
virtual bool openedOK() const { return (engine != nullptr && (outputChannels == 0 || (outputMix != nullptr))); }
|
||||
virtual void start() { stop(); jassert (callback.get() != nullptr); running = true; }
|
||||
virtual void stop() { running = false; }
|
||||
|
||||
virtual bool setAudioPreprocessingEnabled (bool shouldEnable) = 0;
|
||||
virtual bool supportsFloatingPoint() const noexcept = 0;
|
||||
virtual int getXRunCount() const noexcept = 0;
|
||||
@ -553,9 +576,10 @@ public:
|
||||
jassert (callbackToUse != nullptr);
|
||||
|
||||
// spin-lock until we can set the callback
|
||||
while (true)
|
||||
for (;;)
|
||||
{
|
||||
AudioIODeviceCallback* old = callback.get();
|
||||
auto old = callback.get();
|
||||
|
||||
if (old == callbackToUse)
|
||||
break;
|
||||
|
||||
@ -568,7 +592,7 @@ public:
|
||||
|
||||
void process (const float** inputChannelData, float** outputChannelData)
|
||||
{
|
||||
if (AudioIODeviceCallback* cb = callback.exchange(nullptr))
|
||||
if (auto* cb = callback.exchange (nullptr))
|
||||
{
|
||||
cb->audioDeviceIOCallback (inputChannelData, inputChannels, outputChannelData, outputChannels, bufferSize);
|
||||
callback.set (cb);
|
||||
@ -586,19 +610,19 @@ public:
|
||||
int numBuffersToUse);
|
||||
|
||||
//==============================================================================
|
||||
typedef SLresult (*CreateEngineFunc)(SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, const SLInterfaceID*, const SLboolean*);
|
||||
using CreateEngineFunc = SLresult (*) (SLObjectItf*, SLuint32, const SLEngineOption*,
|
||||
SLuint32, const SLInterfaceID*, const SLboolean*);
|
||||
|
||||
//==============================================================================
|
||||
int inputChannels, outputChannels;
|
||||
double sampleRate;
|
||||
int bufferSize, numBuffers;
|
||||
|
||||
bool running, audioProcessingEnabled;
|
||||
bool running = false, audioProcessingEnabled = true;
|
||||
|
||||
SlRef<SLEngineItf_> engine;
|
||||
SlRef<SLOutputMixItf_> outputMix;
|
||||
|
||||
Atomic<AudioIODeviceCallback*> callback;
|
||||
Atomic<AudioIODeviceCallback*> callback { nullptr };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@ -609,7 +633,8 @@ public:
|
||||
int numInputChannels, int numOutputChannels,
|
||||
double samleRateToUse, int bufferSizeToUse,
|
||||
int numBuffersToUse)
|
||||
: OpenSLSession (slLibraryToUse, numInputChannels, numOutputChannels, samleRateToUse, bufferSizeToUse, numBuffersToUse)
|
||||
: OpenSLSession (slLibraryToUse, numInputChannels, numOutputChannels,
|
||||
samleRateToUse, bufferSizeToUse, numBuffersToUse)
|
||||
{
|
||||
jassert (numInputChannels > 0 || numOutputChannels > 0);
|
||||
|
||||
@ -636,7 +661,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
const bool supportsUnderrunCount = (getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT) >= 24);
|
||||
const bool supportsUnderrunCount = (getAndroidSDKVersion() >= 24);
|
||||
getUnderrunCount = supportsUnderrunCount ? getEnv()->GetMethodID (AudioTrack, "getUnderrunCount", "()I") : 0;
|
||||
}
|
||||
}
|
||||
@ -644,8 +669,8 @@ public:
|
||||
|
||||
bool openedOK() const override
|
||||
{
|
||||
return (OpenSLSession::openedOK() && (inputChannels == 0 || recorder != nullptr)
|
||||
&& (outputChannels == 0 || player != nullptr));
|
||||
return OpenSLSession::openedOK() && (inputChannels == 0 || recorder != nullptr)
|
||||
&& (outputChannels == 0 || player != nullptr);
|
||||
}
|
||||
|
||||
void start() override
|
||||
@ -762,11 +787,7 @@ public:
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
OpenSLAudioIODevice (const String& deviceName)
|
||||
: AudioIODevice (deviceName, openSLTypeName),
|
||||
actualBufferSize (0), sampleRate (0), audioBuffersToEnqueue (0),
|
||||
audioProcessingEnabled (true),
|
||||
callback (nullptr)
|
||||
OpenSLAudioIODevice (const String& deviceName) : AudioIODevice (deviceName, openSLTypeName)
|
||||
{
|
||||
// OpenSL has piss-poor support for determining latency, so the only way I can find to
|
||||
// get a number for this is by asking the AudioTrack/AudioRecord classes..
|
||||
@ -813,7 +834,7 @@ public:
|
||||
|
||||
Array<double> getAvailableSampleRates() override
|
||||
{
|
||||
//see https://developer.android.com/ndk/guides/audio/opensl-for-android.html
|
||||
// see https://developer.android.com/ndk/guides/audio/opensl-for-android.html
|
||||
|
||||
static const double rates[] = { 8000.0, 11025.0, 12000.0, 16000.0,
|
||||
22050.0, 24000.0, 32000.0, 44100.0, 48000.0 };
|
||||
@ -821,6 +842,7 @@ public:
|
||||
|
||||
// make sure the native sample rate is pafrt of the list
|
||||
double native = getNativeSampleRate();
|
||||
|
||||
if (native != 0.0 && ! retval.contains (native))
|
||||
retval.add (native);
|
||||
|
||||
@ -850,7 +872,8 @@ public:
|
||||
close();
|
||||
|
||||
lastError.clear();
|
||||
sampleRate = (int) requestedSampleRate;
|
||||
|
||||
sampleRate = (int) (requestedSampleRate > 0 ? requestedSampleRate : getNativeSampleRate());
|
||||
|
||||
auto totalPreferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize;
|
||||
auto nativeBufferSize = getNativeBufferSize();
|
||||
@ -862,11 +885,11 @@ public:
|
||||
|
||||
activeOutputChans = outputChannels;
|
||||
activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false);
|
||||
int numOutputChannels = activeOutputChans.countNumberOfSetBits();
|
||||
auto numOutputChannels = activeOutputChans.countNumberOfSetBits();
|
||||
|
||||
activeInputChans = inputChannels;
|
||||
activeInputChans.setRange (1, activeInputChans.getHighestBit(), false);
|
||||
int numInputChannels = activeInputChans.countNumberOfSetBits();
|
||||
auto numInputChannels = activeInputChans.countNumberOfSetBits();
|
||||
|
||||
if (numInputChannels > 0 && (! RuntimePermissions::isGranted (RuntimePermissions::recordAudio)))
|
||||
{
|
||||
@ -879,7 +902,9 @@ public:
|
||||
session.reset (OpenSLSession::create (slLibrary, numInputChannels, numOutputChannels,
|
||||
sampleRate, actualBufferSize, audioBuffersToEnqueue));
|
||||
if (session != nullptr)
|
||||
{
|
||||
session->setAudioPreprocessingEnabled (audioProcessingEnabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (numInputChannels > 0 && numOutputChannels > 0 && RuntimePermissions::isGranted (RuntimePermissions::recordAudio))
|
||||
@ -945,7 +970,7 @@ public:
|
||||
{
|
||||
if (session != nullptr && callback != newCallback)
|
||||
{
|
||||
AudioIODeviceCallback* oldCallback = callback;
|
||||
auto oldCallback = callback;
|
||||
|
||||
if (newCallback != nullptr)
|
||||
newCallback->audioDeviceAboutToStart (this);
|
||||
@ -1001,12 +1026,12 @@ private:
|
||||
|
||||
//==============================================================================
|
||||
DynamicLibrary slLibrary;
|
||||
int actualBufferSize, sampleRate, audioBuffersToEnqueue;
|
||||
int actualBufferSize = 0, sampleRate = 0, audioBuffersToEnqueue = 0;
|
||||
int inputLatency, outputLatency;
|
||||
bool deviceOpen, audioProcessingEnabled;
|
||||
bool deviceOpen = false, audioProcessingEnabled = true;
|
||||
String lastError;
|
||||
BigInteger activeOutputChans, activeInputChans;
|
||||
AudioIODeviceCallback* callback;
|
||||
AudioIODeviceCallback* callback = nullptr;
|
||||
|
||||
std::unique_ptr<OpenSLSession> session;
|
||||
|
||||
@ -1024,9 +1049,7 @@ private:
|
||||
// "For Android 4.2 (API level 17) and earlier, a buffer count of two or more is required
|
||||
// for lower latency. Beginning with Android 4.3 (API level 18), a buffer count of one
|
||||
// is sufficient for lower latency."
|
||||
|
||||
auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
|
||||
return (sdkVersion >= 18 ? 1 : 2);
|
||||
return (getAndroidSDKVersion() >= 18 ? 1 : 2);
|
||||
}
|
||||
|
||||
// we will not use the low-latency path so we can use the absolute minimum number of buffers
|
||||
@ -1055,23 +1078,6 @@ private:
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static String audioManagerGetProperty (const String& property)
|
||||
{
|
||||
const LocalRef<jstring> jProperty (javaString (property));
|
||||
const LocalRef<jstring> text ((jstring) android.activity.callObjectMethod (JuceAppActivity.audioManagerGetProperty,
|
||||
jProperty.get()));
|
||||
if (text.get() != 0)
|
||||
return juceString (text);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static bool androidHasSystemFeature (const String& property)
|
||||
{
|
||||
const LocalRef<jstring> jProperty (javaString (property));
|
||||
return android.activity.callBooleanMethod (JuceAppActivity.hasSystemFeature, jProperty.get());
|
||||
}
|
||||
|
||||
static double getNativeSampleRate()
|
||||
{
|
||||
return audioManagerGetProperty ("android.media.property.OUTPUT_SAMPLE_RATE").getDoubleValue();
|
||||
@ -1123,7 +1129,7 @@ OpenSLAudioIODevice::OpenSLSession* OpenSLAudioIODevice::OpenSLSession::create (
|
||||
int numBuffersToUse)
|
||||
{
|
||||
std::unique_ptr<OpenSLSession> retval;
|
||||
auto sdkVersion = getEnv()->GetStaticIntField (AndroidBuildVersion, AndroidBuildVersion.SDK_INT);
|
||||
auto sdkVersion = getAndroidSDKVersion();
|
||||
|
||||
// SDK versions 21 and higher should natively support floating point...
|
||||
if (sdkVersion >= 21)
|
||||
@ -1210,12 +1216,12 @@ public:
|
||||
SLObjectItf obj = nullptr;
|
||||
auto err = createEngine (&obj, 0, nullptr, 0, nullptr, nullptr);
|
||||
|
||||
if (err != SL_RESULT_SUCCESS || obj == nullptr)
|
||||
if (err != SL_RESULT_SUCCESS || obj == nullptr || *obj == nullptr)
|
||||
return;
|
||||
|
||||
if ((*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
{
|
||||
(*obj)->Destroy (obj);
|
||||
destroyObject (obj);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1223,7 +1229,7 @@ public:
|
||||
|
||||
if (engine == nullptr)
|
||||
{
|
||||
(*obj)->Destroy (obj);
|
||||
destroyObject (obj);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1232,7 +1238,7 @@ public:
|
||||
|
||||
if (err != SL_RESULT_SUCCESS || obj == nullptr || (*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
{
|
||||
(*obj)->Destroy (obj);
|
||||
destroyObject (obj);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1240,7 +1246,7 @@ public:
|
||||
|
||||
if (outputMix == nullptr)
|
||||
{
|
||||
(*obj)->Destroy (obj);
|
||||
destroyObject (obj);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1264,7 +1270,7 @@ public:
|
||||
|
||||
if ((*obj)->Realize (obj, 0) != SL_RESULT_SUCCESS)
|
||||
{
|
||||
(*obj)->Destroy (obj);
|
||||
destroyObject (obj);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1272,7 +1278,7 @@ public:
|
||||
|
||||
if (player == nullptr)
|
||||
{
|
||||
(*obj)->Destroy (obj);
|
||||
destroyObject (obj);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -235,6 +235,7 @@ private:
|
||||
|
||||
SharedResourcePointer<AudioSessionHolder> sessionHolder;
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (iOSAudioIODeviceType)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSAudioIODeviceType)
|
||||
};
|
||||
|
||||
@ -242,7 +243,7 @@ private:
|
||||
struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
public AsyncUpdater
|
||||
{
|
||||
Pimpl (iOSAudioIODeviceType& ioDeviceType, iOSAudioIODevice& ioDevice)
|
||||
Pimpl (iOSAudioIODeviceType* ioDeviceType, iOSAudioIODevice& ioDevice)
|
||||
: deviceType (ioDeviceType),
|
||||
owner (ioDevice)
|
||||
{
|
||||
@ -255,9 +256,8 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
|
||||
setAudioSessionActive (true);
|
||||
updateHardwareInfo();
|
||||
setAudioSessionActive (false);
|
||||
|
||||
channelData.reconfigure ({}, {});
|
||||
setAudioSessionActive (false);
|
||||
|
||||
sessionHolder->activeDevices.add (this);
|
||||
}
|
||||
@ -278,7 +278,9 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
#endif
|
||||
|
||||
if (category == AVAudioSessionCategoryPlayAndRecord)
|
||||
options |= (AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionAllowBluetooth);
|
||||
options |= (AVAudioSessionCategoryOptionDefaultToSpeaker
|
||||
| AVAudioSessionCategoryOptionAllowBluetooth
|
||||
| AVAudioSessionCategoryOptionAllowBluetoothA2DP);
|
||||
|
||||
JUCE_NSERROR_CHECK ([[AVAudioSession sharedInstance] setCategory: category
|
||||
withOptions: options
|
||||
@ -405,9 +407,9 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
JUCE_IOS_AUDIO_LOG ("Sample rate after detecting available sample rates: " << sampleRate);
|
||||
}
|
||||
|
||||
void updateHardwareInfo()
|
||||
void updateHardwareInfo (bool forceUpdate = false)
|
||||
{
|
||||
if (! hardwareInfoNeedsUpdating.compareAndSetBool (false, true))
|
||||
if (! forceUpdate && ! hardwareInfoNeedsUpdating.compareAndSetBool (false, true))
|
||||
return;
|
||||
|
||||
JUCE_IOS_AUDIO_LOG ("Updating hardware info");
|
||||
@ -415,7 +417,8 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
updateAvailableSampleRates();
|
||||
updateAvailableBufferSizes();
|
||||
|
||||
deviceType.callDeviceChangeListeners();
|
||||
if (deviceType != nullptr)
|
||||
deviceType->callDeviceChangeListeners();
|
||||
}
|
||||
|
||||
void setTargetSampleRateAndBufferSize()
|
||||
@ -451,13 +454,12 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
<< ", targetSampleRate: " << targetSampleRate
|
||||
<< ", targetBufferSize: " << targetBufferSize);
|
||||
|
||||
channelData.reconfigure (requestedInputChannels, requestedOutputChannels);
|
||||
|
||||
setAudioSessionCategory (channelData.areInputChannelsAvailable() ? AVAudioSessionCategoryPlayAndRecord : AVAudioSessionCategoryPlayback);
|
||||
setAudioSessionActive (true);
|
||||
|
||||
setAudioSessionCategory (requestedInputChannels > 0 ? AVAudioSessionCategoryPlayAndRecord
|
||||
: AVAudioSessionCategoryPlayback);
|
||||
channelData.reconfigure (requestedInputChannels, requestedOutputChannels);
|
||||
updateHardwareInfo (true);
|
||||
setTargetSampleRateAndBufferSize();
|
||||
|
||||
fixAudioRouteIfSetToReceiver();
|
||||
|
||||
isRunning = true;
|
||||
@ -486,11 +488,11 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
{
|
||||
isRunning = false;
|
||||
|
||||
if (audioUnit != 0)
|
||||
if (audioUnit != nullptr)
|
||||
{
|
||||
AudioOutputUnitStart (audioUnit);
|
||||
AudioComponentInstanceDispose (audioUnit);
|
||||
audioUnit = 0;
|
||||
audioUnit = nullptr;
|
||||
}
|
||||
|
||||
setAudioSessionActive (false);
|
||||
@ -819,6 +821,9 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
{
|
||||
AudioOutputUnitStop (audioUnit);
|
||||
setAudioSessionActive (false);
|
||||
|
||||
if (callback != nullptr)
|
||||
callback->audioDeviceStopped();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -836,9 +841,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
if (useInput)
|
||||
err = AudioUnitRender (audioUnit, flags, time, 1, numFrames, data);
|
||||
|
||||
const int numChannels = jmax (channelData.inputs->numHardwareChannels,
|
||||
channelData.outputs->numHardwareChannels);
|
||||
const UInt32 totalDataSize = sizeof (short) * (uint32) numChannels * numFrames;
|
||||
const auto channelDataSize = sizeof (float) * numFrames;
|
||||
|
||||
const ScopedTryLock stl (callbackLock);
|
||||
|
||||
@ -847,49 +850,40 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
if ((int) numFrames > channelData.getFloatBufferSize())
|
||||
channelData.setFloatBufferSize ((int) numFrames);
|
||||
|
||||
float** inputData = channelData.audioData.getArrayOfWritePointers();
|
||||
float** outputData = inputData + channelData.inputs->numActiveChannels;
|
||||
float** const inputData = channelData.audioData.getArrayOfWritePointers();
|
||||
float** const outputData = inputData + channelData.inputs->numActiveChannels;
|
||||
|
||||
if (useInput)
|
||||
{
|
||||
const auto* inputShortData = (short*) data->mBuffers[0].mData;
|
||||
|
||||
for (UInt32 i = 0; i < numFrames; ++i)
|
||||
for (int c = 0; c < channelData.inputs->numActiveChannels; ++c)
|
||||
{
|
||||
for (int channel = 0; channel < channelData.inputs->numActiveChannels; ++channel)
|
||||
inputData[channel][i] = *(inputShortData + channelData.inputs->activeChannelIndicies[channel]) * (1.0f / 32768.0f);
|
||||
|
||||
inputShortData += numChannels;
|
||||
auto channelIndex = channelData.inputs->activeChannelIndices[c];
|
||||
memcpy (inputData[c], (float*) data->mBuffers[channelIndex].mData, channelDataSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < channelData.inputs->numActiveChannels; ++i)
|
||||
zeromem (inputData, sizeof (float) * numFrames);
|
||||
for (int c = 0; c < channelData.inputs->numActiveChannels; ++c)
|
||||
zeromem (inputData[c], channelDataSize);
|
||||
}
|
||||
|
||||
callback->audioDeviceIOCallback ((const float**) inputData, channelData.inputs ->numActiveChannels,
|
||||
outputData, channelData.outputs->numActiveChannels,
|
||||
(int) numFrames);
|
||||
|
||||
auto* outputShortData = (short*) data->mBuffers[0].mData;
|
||||
|
||||
zeromem (outputShortData, totalDataSize);
|
||||
|
||||
if (channelData.outputs->numActiveChannels > 0)
|
||||
for (int c = 0; c < channelData.outputs->numActiveChannels; ++c)
|
||||
{
|
||||
for (UInt32 i = 0; i < numFrames; ++i)
|
||||
{
|
||||
for (int channel = 0; channel < channelData.outputs->numActiveChannels; ++channel)
|
||||
*(outputShortData + channelData.outputs->activeChannelIndicies[channel]) = (short) (outputData[channel][i] * 32767.0f);
|
||||
|
||||
outputShortData += numChannels;
|
||||
}
|
||||
auto channelIndex = channelData.outputs->activeChannelIndices[c];
|
||||
memcpy (data->mBuffers[channelIndex].mData, outputData[c], channelDataSize);
|
||||
}
|
||||
|
||||
for (auto c : channelData.outputs->inactiveChannelIndices)
|
||||
zeromem (data->mBuffers[c].mData, channelDataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
zeromem (data->mBuffers[0].mData, totalDataSize);
|
||||
for (uint32 c = 0; c < data->mNumberBuffers; ++c)
|
||||
zeromem (data->mBuffers[c].mData, channelDataSize);
|
||||
}
|
||||
|
||||
return err;
|
||||
@ -927,10 +921,10 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
{
|
||||
JUCE_IOS_AUDIO_LOG ("Creating the audio unit");
|
||||
|
||||
if (audioUnit != 0)
|
||||
if (audioUnit != nullptr)
|
||||
{
|
||||
AudioComponentInstanceDispose (audioUnit);
|
||||
audioUnit = 0;
|
||||
audioUnit = nullptr;
|
||||
}
|
||||
|
||||
AudioComponentDescription desc;
|
||||
@ -940,10 +934,10 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
desc.componentFlags = 0;
|
||||
desc.componentFlagsMask = 0;
|
||||
|
||||
AudioComponent comp = AudioComponentFindNext (0, &desc);
|
||||
AudioComponent comp = AudioComponentFindNext (nullptr, &desc);
|
||||
AudioComponentInstanceNew (comp, &audioUnit);
|
||||
|
||||
if (audioUnit == 0)
|
||||
if (audioUnit == nullptr)
|
||||
return false;
|
||||
|
||||
#if JucePlugin_Enable_IAA
|
||||
@ -1001,11 +995,11 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
zerostruct (format);
|
||||
format.mSampleRate = sampleRate;
|
||||
format.mFormatID = kAudioFormatLinearPCM;
|
||||
format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
|
||||
format.mBitsPerChannel = 8 * sizeof (short);
|
||||
format.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian | kLinearPCMFormatFlagIsPacked;
|
||||
format.mBitsPerChannel = 8 * sizeof (float);
|
||||
format.mFramesPerPacket = 1;
|
||||
format.mChannelsPerFrame = (UInt32) jmax (channelData.inputs->numHardwareChannels, channelData.outputs->numHardwareChannels);
|
||||
format.mBytesPerFrame = format.mBytesPerPacket = format.mChannelsPerFrame * sizeof (short);
|
||||
format.mBytesPerFrame = format.mBytesPerPacket = sizeof (float);
|
||||
|
||||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format));
|
||||
AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format));
|
||||
@ -1082,10 +1076,10 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
|
||||
if (isRunning)
|
||||
{
|
||||
if (audioUnit != 0)
|
||||
if (audioUnit != nullptr)
|
||||
{
|
||||
AudioComponentInstanceDispose (audioUnit);
|
||||
audioUnit = 0;
|
||||
audioUnit = nullptr;
|
||||
|
||||
if (callback != nullptr)
|
||||
callback->audioDeviceStopped();
|
||||
@ -1095,7 +1089,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
|
||||
createAudioUnit();
|
||||
|
||||
if (audioUnit != 0)
|
||||
if (audioUnit != nullptr)
|
||||
{
|
||||
isRunning = true;
|
||||
|
||||
@ -1172,7 +1166,8 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
areChannelsAccessible ((! isInput) || [AVAudioSession sharedInstance].isInputAvailable),
|
||||
activeChannels (limitRequiredChannelsToHardware (numHardwareChannels, requiredChannels)),
|
||||
numActiveChannels (activeChannels.countNumberOfSetBits()),
|
||||
activeChannelIndicies (getActiveChannelIndicies (activeChannels))
|
||||
activeChannelIndices (getActiveChannelIndices (activeChannels)),
|
||||
inactiveChannelIndices (getInactiveChannelIndices (activeChannelIndices, numHardwareChannels))
|
||||
{
|
||||
#if JUCE_IOS_AUDIO_LOGGING
|
||||
{
|
||||
@ -1187,7 +1182,12 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
info << ", Are channels available: " << (areChannelsAccessible ? "yes" : "no")
|
||||
<< ", Active channel indices:";
|
||||
|
||||
for (auto i : activeChannelIndicies)
|
||||
for (auto i : activeChannelIndices)
|
||||
info << " " << i;
|
||||
|
||||
info << ", Inactive channel indices:";
|
||||
|
||||
for (auto i : inactiveChannelIndices)
|
||||
info << " " << i;
|
||||
|
||||
JUCE_IOS_AUDIO_LOG ((isInput ? "Input" : "Output") << " channel configuration: {" << info << "}");
|
||||
@ -1202,7 +1202,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
const BigInteger activeChannels;
|
||||
const int numActiveChannels;
|
||||
|
||||
const Array<int> activeChannelIndicies;
|
||||
const Array<int> activeChannelIndices, inactiveChannelIndices;
|
||||
|
||||
private:
|
||||
static StringArray getHardwareChannelNames (const bool isInput)
|
||||
@ -1234,7 +1234,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
return requiredChannels;
|
||||
}
|
||||
|
||||
static Array<int> getActiveChannelIndicies (const BigInteger activeChannelsToIndex)
|
||||
static Array<int> getActiveChannelIndices (const BigInteger activeChannelsToIndex)
|
||||
{
|
||||
Array<int> result;
|
||||
|
||||
@ -1248,6 +1248,21 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static Array<int> getInactiveChannelIndices (const Array<int>& activeIndices, int numChannels)
|
||||
{
|
||||
Array<int> result;
|
||||
|
||||
auto nextActiveChannel = activeIndices.begin();
|
||||
|
||||
for (int i = 0; i < numChannels; ++i)
|
||||
if (nextActiveChannel != activeIndices.end() && i == *nextActiveChannel)
|
||||
++nextActiveChannel;
|
||||
else
|
||||
result.add (i);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
void reconfigure (const BigInteger requiredInputChannels,
|
||||
@ -1307,7 +1322,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
|
||||
MidiMessageCollector* messageCollector = nullptr;
|
||||
|
||||
iOSAudioIODeviceType& deviceType;
|
||||
WeakReference<iOSAudioIODeviceType> deviceType;
|
||||
iOSAudioIODevice& owner;
|
||||
|
||||
CriticalSection callbackLock;
|
||||
@ -1327,7 +1342,7 @@ struct iOSAudioIODevice::Pimpl : public AudioPlayHead,
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
iOSAudioIODevice::iOSAudioIODevice (iOSAudioIODeviceType& ioDeviceType, const String&, const String&)
|
||||
iOSAudioIODevice::iOSAudioIODevice (iOSAudioIODeviceType* ioDeviceType, const String&, const String&)
|
||||
: AudioIODevice (iOSAudioDeviceName, iOSAudioDeviceName),
|
||||
pimpl (new Pimpl (ioDeviceType, *this))
|
||||
{
|
||||
@ -1401,7 +1416,7 @@ bool iOSAudioIODeviceType::hasSeparateInputsAndOutputs() const { retu
|
||||
|
||||
AudioIODevice* iOSAudioIODeviceType::createDevice (const String& outputDeviceName, const String& inputDeviceName)
|
||||
{
|
||||
return new iOSAudioIODevice (*this, outputDeviceName, inputDeviceName);
|
||||
return new iOSAudioIODevice (this, outputDeviceName, inputDeviceName);
|
||||
}
|
||||
|
||||
void iOSAudioIODeviceType::handleRouteChange (AVAudioSessionRouteChangeReason)
|
||||
|
@ -77,7 +77,7 @@ public:
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
iOSAudioIODevice (iOSAudioIODeviceType&, const String&, const String&);
|
||||
iOSAudioIODevice (iOSAudioIODeviceType*, const String&, const String&);
|
||||
|
||||
//==============================================================================
|
||||
friend class iOSAudioIODeviceType;
|
||||
|
@ -1115,7 +1115,7 @@ private:
|
||||
if (cardNum < 0)
|
||||
break;
|
||||
|
||||
if (JUCE_CHECKED_RESULT (snd_ctl_open (&handle, ("hw:" + String (cardNum)).toUTF8(), SND_CTL_NONBLOCK)) >= 0)
|
||||
if (JUCE_CHECKED_RESULT (snd_ctl_open (&handle, ("hw:" + String (cardNum)).toRawUTF8(), SND_CTL_NONBLOCK)) >= 0)
|
||||
{
|
||||
if (JUCE_CHECKED_RESULT (snd_ctl_card_info (handle, info)) >= 0)
|
||||
{
|
||||
|
@ -23,6 +23,136 @@
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class BelaMidiInput
|
||||
{
|
||||
public:
|
||||
static Array<BelaMidiInput*> midiInputs;
|
||||
|
||||
BelaMidiInput (const String& port, MidiInput* input, MidiInputCallback* callback)
|
||||
: midiInput (input), midiPort (port), midiCallback (callback)
|
||||
{
|
||||
jassert (midiCallback != nullptr);
|
||||
midiInputs.add (this);
|
||||
}
|
||||
|
||||
~BelaMidiInput()
|
||||
{
|
||||
stop();
|
||||
midiInputs.removeAllInstancesOf (this);
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
midi.readFrom (midiPort.toRawUTF8());
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
midi.enableParser (false);
|
||||
}
|
||||
|
||||
void poll()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
auto data = midi.getInput();
|
||||
|
||||
if (data < 0)
|
||||
break;
|
||||
|
||||
auto byte = (uint8) data;
|
||||
concatenator.pushMidiData (&byte, 1, 0.0, midiInput, *midiCallback);
|
||||
}
|
||||
}
|
||||
|
||||
static StringArray getDevices (bool input)
|
||||
{
|
||||
StringArray devices;
|
||||
|
||||
for (auto& card : findAllALSACardIDs())
|
||||
findMidiDevices (devices, input, card);
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
private:
|
||||
static Array<int> findAllALSACardIDs()
|
||||
{
|
||||
Array<int> cards;
|
||||
int card = -1;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
auto status = snd_card_next (&card);
|
||||
|
||||
if (status != 0 || card < 0)
|
||||
break;
|
||||
|
||||
cards.add (card);
|
||||
}
|
||||
|
||||
return cards;
|
||||
}
|
||||
|
||||
// Adds all midi devices to the devices array of the given input/output type on the given card
|
||||
static void findMidiDevices (StringArray& devices, bool input, int cardNum)
|
||||
{
|
||||
snd_ctl_t* ctl = nullptr;
|
||||
auto status = snd_ctl_open (&ctl, ("hw:" + String (cardNum)).toRawUTF8(), 0);
|
||||
|
||||
if (status < 0)
|
||||
return;
|
||||
|
||||
int device = -1;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
status = snd_ctl_rawmidi_next_device (ctl, &device);
|
||||
|
||||
if (status < 0 || device < 0)
|
||||
break;
|
||||
|
||||
snd_rawmidi_info_t* info;
|
||||
snd_rawmidi_info_alloca (&info);
|
||||
|
||||
snd_rawmidi_info_set_device (info, device);
|
||||
snd_rawmidi_info_set_stream (info, input ? SND_RAWMIDI_STREAM_INPUT
|
||||
: SND_RAWMIDI_STREAM_OUTPUT);
|
||||
|
||||
snd_ctl_rawmidi_info (ctl, info);
|
||||
|
||||
auto subCount = snd_rawmidi_info_get_subdevices_count (info);
|
||||
|
||||
for (int sub = 0; sub < subCount; ++sub)
|
||||
{
|
||||
snd_rawmidi_info_set_subdevice (info, sub);
|
||||
|
||||
status = snd_ctl_rawmidi_info (ctl, info);
|
||||
|
||||
if (status == 0)
|
||||
devices.add ("hw:" + String (cardNum) + ","
|
||||
+ String (device) + ","
|
||||
+ String (sub));
|
||||
}
|
||||
}
|
||||
|
||||
snd_ctl_close (ctl);
|
||||
}
|
||||
|
||||
String midiPort;
|
||||
MidiInput* const midiInput;
|
||||
MidiInputCallback* const midiCallback;
|
||||
|
||||
Midi midi;
|
||||
MidiDataConcatenator concatenator { 512 };
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BelaMidiInput)
|
||||
};
|
||||
|
||||
Array<BelaMidiInput*> BelaMidiInput::midiInputs;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class BelaAudioIODevice : public AudioIODevice
|
||||
{
|
||||
@ -33,7 +163,10 @@ public:
|
||||
Bela_defaultSettings (&defaultSettings);
|
||||
}
|
||||
|
||||
~BelaAudioIODevice() {}
|
||||
~BelaAudioIODevice()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StringArray getOutputChannelNames() override { return { "Out #1", "Out #2" }; }
|
||||
@ -223,6 +356,10 @@ private:
|
||||
|
||||
ScopedLock lock (callbackLock);
|
||||
|
||||
// Check for and process and midi
|
||||
for (auto midiInput : BelaMidiInput::midiInputs)
|
||||
midiInput->poll();
|
||||
|
||||
if (callback != nullptr)
|
||||
{
|
||||
jassert (context.audioFrames <= actualBufferSize);
|
||||
@ -370,4 +507,58 @@ AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Bela()
|
||||
return new BelaAudioIODeviceType();
|
||||
}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
// TODO: Add Bela MidiOutput support
|
||||
|
||||
StringArray MidiOutput::getDevices() { return {}; }
|
||||
int MidiOutput::getDefaultDeviceIndex() { return 0; }
|
||||
MidiOutput* MidiOutput::openDevice (int) { return {}; }
|
||||
MidiOutput* MidiOutput::createNewDevice (const String&) { return {}; }
|
||||
MidiOutput::~MidiOutput() {}
|
||||
void MidiOutput::sendMessageNow (const MidiMessage&) {}
|
||||
|
||||
|
||||
//==============================================================================
|
||||
MidiInput::MidiInput (const String& nm) : name (nm) {}
|
||||
|
||||
MidiInput::~MidiInput()
|
||||
{
|
||||
delete static_cast<BelaMidiInput*> (internal);
|
||||
}
|
||||
|
||||
void MidiInput::start() { static_cast<BelaMidiInput*> (internal)->start(); }
|
||||
void MidiInput::stop() { static_cast<BelaMidiInput*> (internal)->stop(); }
|
||||
|
||||
int MidiInput::getDefaultDeviceIndex()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
StringArray MidiInput::getDevices()
|
||||
{
|
||||
return BelaMidiInput::getDevices (true);
|
||||
}
|
||||
|
||||
MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
|
||||
{
|
||||
auto devices = getDevices();
|
||||
|
||||
if (index >= 0 && index < devices.size())
|
||||
{
|
||||
auto deviceName = devices[index];
|
||||
auto result = new MidiInput (deviceName);
|
||||
result->internal = new BelaMidiInput (deviceName, result, callback);
|
||||
return result;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
|
||||
{
|
||||
jassertfalse; // N/A on Bela
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
|
@ -25,11 +25,6 @@ namespace juce
|
||||
|
||||
#if JUCE_ALSA
|
||||
|
||||
// You can define these strings in your app if you want to override the default names:
|
||||
#ifndef JUCE_ALSA_MIDI_NAME
|
||||
#define JUCE_ALSA_MIDI_NAME JUCEApplicationBase::getInstance()->getApplicationName().toUTF8()
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
namespace
|
||||
{
|
||||
@ -38,6 +33,49 @@ namespace
|
||||
class AlsaClient : public ReferenceCountedObject
|
||||
{
|
||||
public:
|
||||
AlsaClient()
|
||||
{
|
||||
jassert (instance == nullptr);
|
||||
|
||||
snd_seq_open (&handle, "default", SND_SEQ_OPEN_DUPLEX, 0);
|
||||
|
||||
if (handle != nullptr)
|
||||
{
|
||||
snd_seq_nonblock (handle, SND_SEQ_NONBLOCK);
|
||||
snd_seq_set_client_name (handle, getAlsaMidiName().toRawUTF8());
|
||||
clientId = snd_seq_client_id (handle);
|
||||
|
||||
// It's good idea to pre-allocate a good number of elements
|
||||
ports.ensureStorageAllocated (32);
|
||||
}
|
||||
}
|
||||
|
||||
~AlsaClient()
|
||||
{
|
||||
jassert (instance != nullptr);
|
||||
instance = nullptr;
|
||||
|
||||
if (handle != nullptr)
|
||||
snd_seq_close (handle);
|
||||
|
||||
jassert (activeCallbacks.get() == 0);
|
||||
|
||||
if (inputThread)
|
||||
inputThread->stopThread (3000);
|
||||
}
|
||||
|
||||
static String getAlsaMidiName()
|
||||
{
|
||||
#ifdef JUCE_ALSA_MIDI_NAME
|
||||
return JUCE_ALSA_MIDI_NAME;
|
||||
#else
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
return app->getApplicationName();
|
||||
|
||||
return "JUCE";
|
||||
#endif
|
||||
}
|
||||
|
||||
using Ptr = ReferenceCountedObjectPtr<AlsaClient>;
|
||||
|
||||
//==============================================================================
|
||||
@ -113,9 +151,9 @@ public:
|
||||
snd_seq_ev_clear (&event);
|
||||
|
||||
auto numBytes = (long) message.getRawDataSize();
|
||||
const uint8* data = message.getRawData();
|
||||
auto* data = message.getRawData();
|
||||
|
||||
auto* seqHandle = client.get();
|
||||
auto seqHandle = client.get();
|
||||
bool success = true;
|
||||
|
||||
while (numBytes > 0)
|
||||
@ -154,12 +192,12 @@ public:
|
||||
|
||||
void createPort (const String& name, bool enableSubscription)
|
||||
{
|
||||
if (auto* seqHandle = client.get())
|
||||
if (auto seqHandle = client.get())
|
||||
{
|
||||
const unsigned int caps =
|
||||
isInput
|
||||
? (SND_SEQ_PORT_CAP_WRITE | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_WRITE : 0))
|
||||
: (SND_SEQ_PORT_CAP_WRITE | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_READ : 0));
|
||||
isInput ? (SND_SEQ_PORT_CAP_WRITE | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_WRITE : 0))
|
||||
: (SND_SEQ_PORT_CAP_READ | (enableSubscription ? SND_SEQ_PORT_CAP_SUBS_READ : 0));
|
||||
|
||||
portId = snd_seq_create_simple_port (seqHandle, name.toUTF8(), caps,
|
||||
SND_SEQ_PORT_TYPE_MIDI_GENERIC |
|
||||
SND_SEQ_PORT_TYPE_APPLICATION);
|
||||
@ -251,38 +289,6 @@ private:
|
||||
|
||||
static AlsaClient* instance;
|
||||
|
||||
//==============================================================================
|
||||
friend class ReferenceCountedObjectPtr<AlsaClient>;
|
||||
friend struct ContainerDeletePolicy<AlsaClient>;
|
||||
|
||||
AlsaClient()
|
||||
{
|
||||
jassert (instance == nullptr);
|
||||
|
||||
snd_seq_open (&handle, "default", SND_SEQ_OPEN_DUPLEX, 0);
|
||||
snd_seq_nonblock (handle, SND_SEQ_NONBLOCK);
|
||||
snd_seq_set_client_name (handle, JUCE_ALSA_MIDI_NAME);
|
||||
clientId = snd_seq_client_id(handle);
|
||||
|
||||
// It's good idea to pre-allocate a good number of elements
|
||||
ports.ensureStorageAllocated (32);
|
||||
}
|
||||
|
||||
~AlsaClient()
|
||||
{
|
||||
jassert (instance != nullptr);
|
||||
|
||||
instance = nullptr;
|
||||
|
||||
if (handle != nullptr)
|
||||
snd_seq_close (handle);
|
||||
|
||||
jassert (activeCallbacks.get() == 0);
|
||||
|
||||
if (inputThread)
|
||||
inputThread->stopThread (3000);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class MidiInputThread : public Thread
|
||||
{
|
||||
@ -295,9 +301,10 @@ private:
|
||||
|
||||
void run() override
|
||||
{
|
||||
auto seqHandle = client.get();
|
||||
|
||||
const int maxEventSize = 16 * 1024;
|
||||
snd_midi_event_t* midiParser;
|
||||
snd_seq_t* seqHandle = client.get();
|
||||
|
||||
if (snd_midi_event_new (maxEventSize, &midiParser) >= 0)
|
||||
{
|
||||
@ -321,8 +328,8 @@ private:
|
||||
if (snd_seq_event_input (seqHandle, &inputEvent) >= 0)
|
||||
{
|
||||
// xxx what about SYSEXes that are too big for the buffer?
|
||||
const long numBytes = snd_midi_event_decode (midiParser, buffer,
|
||||
maxEventSize, inputEvent);
|
||||
auto numBytes = snd_midi_event_decode (midiParser, buffer,
|
||||
maxEventSize, inputEvent);
|
||||
|
||||
snd_midi_event_reset_decode (midiParser);
|
||||
|
||||
@ -360,7 +367,7 @@ static AlsaClient::Port* iterateMidiClient (const AlsaClient::Ptr& client,
|
||||
{
|
||||
AlsaClient::Port* port = nullptr;
|
||||
|
||||
snd_seq_t* seqHandle = client->get();
|
||||
auto seqHandle = client->get();
|
||||
snd_seq_port_info_t* portInfo = nullptr;
|
||||
|
||||
snd_seq_port_info_alloca (&portInfo);
|
||||
@ -406,7 +413,7 @@ static AlsaClient::Port* iterateMidiDevices (bool forInput,
|
||||
AlsaClient::Port* port = nullptr;
|
||||
auto client = AlsaClient::getInstance();
|
||||
|
||||
if (auto* seqHandle = client->get())
|
||||
if (auto seqHandle = client->get())
|
||||
{
|
||||
snd_seq_system_info_t* systemInfo = nullptr;
|
||||
snd_seq_client_info_t* clientInfo = nullptr;
|
||||
@ -503,8 +510,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message)
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
MidiInput::MidiInput (const String& nm)
|
||||
: name (nm), internal (nullptr)
|
||||
MidiInput::MidiInput (const String& nm) : name (nm)
|
||||
{
|
||||
}
|
||||
|
||||
@ -538,8 +544,6 @@ StringArray MidiInput::getDevices()
|
||||
|
||||
MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback)
|
||||
{
|
||||
MidiInput* newDevice = nullptr;
|
||||
|
||||
StringArray devices;
|
||||
auto* port = iterateMidiDevices (true, devices, deviceIndex);
|
||||
|
||||
@ -548,26 +552,22 @@ MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback)
|
||||
|
||||
jassert (port->isValid());
|
||||
|
||||
newDevice = new MidiInput (devices [deviceIndex]);
|
||||
auto newDevice = new MidiInput (devices [deviceIndex]);
|
||||
port->setupInput (newDevice, callback);
|
||||
newDevice->internal = port;
|
||||
|
||||
return newDevice;
|
||||
}
|
||||
|
||||
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback)
|
||||
{
|
||||
MidiInput* newDevice = nullptr;
|
||||
|
||||
auto client = AlsaClient::getInstance();
|
||||
auto* port = client->createPort (deviceName, true, true);
|
||||
|
||||
jassert (port->isValid());
|
||||
|
||||
newDevice = new MidiInput (deviceName);
|
||||
auto newDevice = new MidiInput (deviceName);
|
||||
port->setupInput (newDevice, callback);
|
||||
newDevice->internal = port;
|
||||
|
||||
return newDevice;
|
||||
}
|
||||
|
||||
@ -584,7 +584,7 @@ MidiOutput* MidiOutput::createNewDevice (const String&) { return nul
|
||||
MidiOutput::~MidiOutput() {}
|
||||
void MidiOutput::sendMessageNow (const MidiMessage&) {}
|
||||
|
||||
MidiInput::MidiInput (const String& nm) : name (nm), internal (nullptr) {}
|
||||
MidiInput::MidiInput (const String& nm) : name (nm) {}
|
||||
MidiInput::~MidiInput() {}
|
||||
void MidiInput::start() {}
|
||||
void MidiInput::stop() {}
|
||||
|
@ -181,7 +181,7 @@ public:
|
||||
AudioObjectAddPropertyListener (deviceID, &pa, deviceListenerProc, this);
|
||||
}
|
||||
|
||||
~CoreAudioInternal()
|
||||
~CoreAudioInternal() override
|
||||
{
|
||||
AudioObjectPropertyAddress pa;
|
||||
pa.mSelector = kAudioObjectPropertySelectorWildcard;
|
||||
@ -289,15 +289,17 @@ public:
|
||||
|
||||
if (OK (AudioObjectGetPropertyData (deviceID, &pa, 0, nullptr, &size, ranges)))
|
||||
{
|
||||
static const double possibleRates[] = { 44100.0, 48000.0, 88200.0, 96000.0, 176400.0, 192000.0, 384000.0 };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (possibleRates); ++i)
|
||||
for (auto r : { 8000, 11025, 16000, 22050, 32000,
|
||||
44100, 48000, 88200, 96000, 176400,
|
||||
192000, 352800, 384000, 705600, 768000 })
|
||||
{
|
||||
auto rate = (double) r;
|
||||
|
||||
for (int j = size / (int) sizeof (AudioValueRange); --j >= 0;)
|
||||
{
|
||||
if (possibleRates[i] >= ranges[j].mMinimum - 2 && possibleRates[i] <= ranges[j].mMaximum + 2)
|
||||
if (rate >= ranges[j].mMinimum - 2 && rate <= ranges[j].mMaximum + 2)
|
||||
{
|
||||
newSampleRates.add (possibleRates[i]);
|
||||
newSampleRates.add (rate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -462,6 +464,9 @@ public:
|
||||
inputChannelInfo.swapWith (newInChans);
|
||||
outputChannelInfo.swapWith (newOutChans);
|
||||
|
||||
numInputChans = inputChannelInfo.size();
|
||||
numOutputChans = outputChannelInfo.size();
|
||||
|
||||
allocateTempBuffers();
|
||||
}
|
||||
|
||||
@ -574,7 +579,7 @@ public:
|
||||
|
||||
OSType typeId = types[index];
|
||||
|
||||
OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (typeId), &typeId));
|
||||
OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (typeId), &typeId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -600,7 +605,7 @@ public:
|
||||
pa.mScope = kAudioObjectPropertyScopeGlobal;
|
||||
pa.mElement = kAudioObjectPropertyElementMaster;
|
||||
Float64 sr = newSampleRate;
|
||||
return OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (sr), &sr));
|
||||
return OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (sr), &sr));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
@ -643,7 +648,7 @@ public:
|
||||
pa.mElement = kAudioObjectPropertyElementMaster;
|
||||
UInt32 framesPerBuf = (UInt32) bufferSizeSamples;
|
||||
|
||||
if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, 0, sizeof (framesPerBuf), &framesPerBuf)))
|
||||
if (! OK (AudioObjectSetPropertyData (deviceID, &pa, 0, nullptr, sizeof (framesPerBuf), &framesPerBuf)))
|
||||
{
|
||||
updateDetailsFromDevice();
|
||||
error = "Couldn't change buffer size";
|
||||
@ -686,7 +691,7 @@ public:
|
||||
else
|
||||
{
|
||||
OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID));
|
||||
audioProcID = 0;
|
||||
audioProcID = {};
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -712,7 +717,7 @@ public:
|
||||
{
|
||||
OK (AudioDeviceStop (deviceID, audioIOProc));
|
||||
OK (AudioDeviceDestroyIOProcID (deviceID, audioProcID));
|
||||
audioProcID = 0;
|
||||
audioProcID = {};
|
||||
|
||||
started = false;
|
||||
|
||||
@ -776,7 +781,7 @@ public:
|
||||
|
||||
for (int i = numOutputChans; --i >= 0;)
|
||||
{
|
||||
auto& info = outputChannelInfo.getReference(i);
|
||||
auto& info = outputChannelInfo.getReference (i);
|
||||
auto src = tempOutputBuffers[i];
|
||||
auto dest = ((float*) outOutputData->mBuffers[info.streamNum].mData) + info.dataOffsetSamples;
|
||||
auto stride = info.dataStrideSamples;
|
||||
@ -815,8 +820,8 @@ public:
|
||||
const int oldBufferSize = bufferSize;
|
||||
|
||||
if (! updateDetailsFromDevice())
|
||||
owner.stop();
|
||||
else if (oldBufferSize != bufferSize || oldSampleRate != sampleRate)
|
||||
owner.stopInternal();
|
||||
else if ((oldBufferSize != bufferSize || oldSampleRate != sampleRate) && owner.shouldRestartDevice())
|
||||
owner.restart();
|
||||
}
|
||||
|
||||
@ -831,7 +836,7 @@ public:
|
||||
Array<double> sampleRates;
|
||||
Array<int> bufferSizes;
|
||||
AudioIODeviceCallback* callback = nullptr;
|
||||
AudioDeviceIOProcID audioProcID = 0;
|
||||
AudioDeviceIOProcID audioProcID = {};
|
||||
|
||||
private:
|
||||
CriticalSection callbackLock;
|
||||
@ -882,7 +887,9 @@ private:
|
||||
case kAudioDevicePropertyDeviceHasChanged:
|
||||
case kAudioObjectPropertyOwnedObjects:
|
||||
intern->owner.restart();
|
||||
intern->owner.deviceType.triggerAsyncAudioDeviceListChange();
|
||||
|
||||
if (intern->owner.deviceType != nullptr)
|
||||
intern->owner.deviceType->triggerAsyncAudioDeviceListChange();
|
||||
break;
|
||||
|
||||
case kAudioDevicePropertyBufferSizeRange:
|
||||
@ -941,16 +948,14 @@ class CoreAudioIODevice : public AudioIODevice,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
CoreAudioIODevice (CoreAudioIODeviceType& dt,
|
||||
CoreAudioIODevice (CoreAudioIODeviceType* dt,
|
||||
const String& deviceName,
|
||||
AudioDeviceID inputDeviceId, const int inputIndex_,
|
||||
AudioDeviceID outputDeviceId, const int outputIndex_)
|
||||
: AudioIODevice (deviceName, "CoreAudio"),
|
||||
deviceType (dt),
|
||||
inputIndex (inputIndex_),
|
||||
outputIndex (outputIndex_),
|
||||
isOpen_ (false),
|
||||
isStarted (false)
|
||||
outputIndex (outputIndex_)
|
||||
{
|
||||
CoreAudioInternal* device = nullptr;
|
||||
|
||||
@ -975,7 +980,7 @@ public:
|
||||
AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, internal.get());
|
||||
}
|
||||
|
||||
~CoreAudioIODevice()
|
||||
~CoreAudioIODevice() override
|
||||
{
|
||||
close();
|
||||
|
||||
@ -1077,6 +1082,8 @@ public:
|
||||
|
||||
void stop() override
|
||||
{
|
||||
restartDevice = false;
|
||||
|
||||
if (isStarted)
|
||||
{
|
||||
AudioIODeviceCallback* const lastCallback = internal->callback;
|
||||
@ -1089,6 +1096,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void stopInternal()
|
||||
{
|
||||
stop();
|
||||
restartDevice = true;
|
||||
}
|
||||
|
||||
bool isPlaying() override
|
||||
{
|
||||
if (internal->callback == nullptr)
|
||||
@ -1104,7 +1117,8 @@ public:
|
||||
|
||||
void audioDeviceListChanged()
|
||||
{
|
||||
deviceType.audioDeviceListChanged();
|
||||
if (deviceType != nullptr)
|
||||
deviceType->audioDeviceListChanged();
|
||||
}
|
||||
|
||||
void restart()
|
||||
@ -1123,7 +1137,7 @@ public:
|
||||
if (internal->callback != nullptr)
|
||||
previousCallback = internal->callback;
|
||||
|
||||
stop();
|
||||
stopInternal();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1141,12 +1155,14 @@ public:
|
||||
deviceWrapperRestartCallback = cb;
|
||||
}
|
||||
|
||||
CoreAudioIODeviceType& deviceType;
|
||||
bool shouldRestartDevice() const noexcept { return restartDevice; }
|
||||
|
||||
WeakReference<CoreAudioIODeviceType> deviceType;
|
||||
int inputIndex, outputIndex;
|
||||
|
||||
private:
|
||||
std::unique_ptr<CoreAudioInternal> internal;
|
||||
bool isOpen_, isStarted;
|
||||
bool isOpen_ = false, isStarted = false, restartDevice = true;
|
||||
String lastError;
|
||||
AudioIODeviceCallback* previousCallback = nullptr;
|
||||
std::function<void()> deviceWrapperRestartCallback = nullptr;
|
||||
@ -1159,7 +1175,7 @@ private:
|
||||
{
|
||||
stopTimer();
|
||||
|
||||
stop();
|
||||
stopInternal();
|
||||
|
||||
internal->updateDetailsFromDevice();
|
||||
|
||||
@ -1194,14 +1210,14 @@ class AudioIODeviceCombiner : public AudioIODevice,
|
||||
private Timer
|
||||
{
|
||||
public:
|
||||
AudioIODeviceCombiner (const String& deviceName, CoreAudioIODeviceType& deviceType)
|
||||
AudioIODeviceCombiner (const String& deviceName, CoreAudioIODeviceType* deviceType)
|
||||
: AudioIODevice (deviceName, "CoreAudio"),
|
||||
Thread (deviceName),
|
||||
owner (deviceType)
|
||||
{
|
||||
}
|
||||
|
||||
~AudioIODeviceCombiner()
|
||||
~AudioIODeviceCombiner() override
|
||||
{
|
||||
close();
|
||||
devices.clear();
|
||||
@ -1548,7 +1564,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
CoreAudioIODeviceType& owner;
|
||||
WeakReference<CoreAudioIODeviceType> owner;
|
||||
CriticalSection callbackLock;
|
||||
AudioIODeviceCallback* callback = nullptr;
|
||||
AudioIODeviceCallback* previousCallback = nullptr;
|
||||
@ -1641,7 +1657,7 @@ private:
|
||||
}
|
||||
|
||||
for (auto* d : devices)
|
||||
d->device->stop();
|
||||
d->device->stopInternal();
|
||||
|
||||
if (lastCallback != nullptr)
|
||||
{
|
||||
@ -1770,8 +1786,8 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
if (anySampleRateChanges)
|
||||
owner.audioDeviceListChanged();
|
||||
if (anySampleRateChanges && owner != nullptr)
|
||||
owner->audioDeviceListChanged();
|
||||
|
||||
if (callback != nullptr)
|
||||
callback->audioDeviceAboutToStart (device);
|
||||
@ -1790,7 +1806,7 @@ private:
|
||||
d->setDeviceWrapperRestartCallback ([this] { owner.restartAsync(); });
|
||||
}
|
||||
|
||||
~DeviceWrapper()
|
||||
~DeviceWrapper() override
|
||||
{
|
||||
close();
|
||||
}
|
||||
@ -2002,7 +2018,7 @@ public:
|
||||
AudioObjectAddPropertyListener (kAudioObjectSystemObject, &pa, hardwareListenerProc, this);
|
||||
}
|
||||
|
||||
~CoreAudioIODeviceType()
|
||||
~CoreAudioIODeviceType() override
|
||||
{
|
||||
AudioObjectPropertyAddress pa;
|
||||
pa.mSelector = kAudioHardwarePropertyDevices;
|
||||
@ -2155,20 +2171,20 @@ public:
|
||||
: outputDeviceName;
|
||||
|
||||
if (inputDeviceID == outputDeviceID)
|
||||
return new CoreAudioIODevice (*this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex);
|
||||
return new CoreAudioIODevice (this, combinedName, inputDeviceID, inputIndex, outputDeviceID, outputIndex);
|
||||
|
||||
std::unique_ptr<CoreAudioIODevice> in, out;
|
||||
|
||||
if (inputDeviceID != 0)
|
||||
in.reset (new CoreAudioIODevice (*this, inputDeviceName, inputDeviceID, inputIndex, 0, -1));
|
||||
in.reset (new CoreAudioIODevice (this, inputDeviceName, inputDeviceID, inputIndex, 0, -1));
|
||||
|
||||
if (outputDeviceID != 0)
|
||||
out.reset (new CoreAudioIODevice (*this, outputDeviceName, 0, -1, outputDeviceID, outputIndex));
|
||||
out.reset (new CoreAudioIODevice (this, outputDeviceName, 0, -1, outputDeviceID, outputIndex));
|
||||
|
||||
if (in == nullptr) return out.release();
|
||||
if (out == nullptr) return in.release();
|
||||
|
||||
std::unique_ptr<AudioIODeviceCombiner> combo (new AudioIODeviceCombiner (combinedName, *this));
|
||||
std::unique_ptr<AudioIODeviceCombiner> combo (new AudioIODeviceCombiner (combinedName, this));
|
||||
combo->addDevice (in.release(), true, false);
|
||||
combo->addDevice (out.release(), false, true);
|
||||
return combo.release();
|
||||
@ -2230,6 +2246,7 @@ private:
|
||||
audioDeviceListChanged();
|
||||
}
|
||||
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (CoreAudioIODeviceType)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioIODeviceType)
|
||||
};
|
||||
|
||||
|
@ -218,35 +218,6 @@ namespace CoreMidiHelpers
|
||||
}
|
||||
}
|
||||
|
||||
static StringArray findDevices (bool forInput)
|
||||
{
|
||||
// It seems that OSX can be a bit picky about the thread that's first used to
|
||||
// search for devices. It's safest to use the message thread for calling this.
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
|
||||
StringArray s;
|
||||
enableSimulatorMidiSession();
|
||||
|
||||
auto num = forInput ? MIDIGetNumberOfSources()
|
||||
: MIDIGetNumberOfDestinations();
|
||||
|
||||
for (ItemCount i = 0; i < num; ++i)
|
||||
{
|
||||
String name;
|
||||
|
||||
if (auto dest = forInput ? MIDIGetSource (i)
|
||||
: MIDIGetDestination (i))
|
||||
name = getConnectedEndpointName (dest);
|
||||
|
||||
if (name.isEmpty())
|
||||
name = "<error>";
|
||||
|
||||
s.add (name);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static void globalSystemChangeCallback (const MIDINotification*, void*)
|
||||
{
|
||||
// TODO.. Should pass-on this notification..
|
||||
@ -268,7 +239,7 @@ namespace CoreMidiHelpers
|
||||
{
|
||||
// Since OSX 10.6, the MIDIClientCreate function will only work
|
||||
// correctly when called from the message thread!
|
||||
jassert (MessageManager::getInstance()->isThisTheMessageThread());
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
|
||||
enableSimulatorMidiSession();
|
||||
|
||||
@ -280,6 +251,40 @@ namespace CoreMidiHelpers
|
||||
return globalMidiClient;
|
||||
}
|
||||
|
||||
static StringArray findDevices (bool forInput)
|
||||
{
|
||||
// It seems that OSX can be a bit picky about the thread that's first used to
|
||||
// search for devices. It's safest to use the message thread for calling this.
|
||||
JUCE_ASSERT_MESSAGE_THREAD
|
||||
|
||||
if (getGlobalMidiClient() == 0)
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
StringArray s;
|
||||
enableSimulatorMidiSession();
|
||||
|
||||
auto num = forInput ? MIDIGetNumberOfSources()
|
||||
: MIDIGetNumberOfDestinations();
|
||||
|
||||
for (ItemCount i = 0; i < num; ++i)
|
||||
{
|
||||
String name;
|
||||
|
||||
if (auto dest = forInput ? MIDIGetSource (i) : MIDIGetDestination (i))
|
||||
name = getConnectedEndpointName (dest);
|
||||
|
||||
if (name.isEmpty())
|
||||
name = "<error>";
|
||||
|
||||
s.add (name);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class MidiPortAndEndpoint
|
||||
{
|
||||
@ -344,8 +349,8 @@ namespace CoreMidiHelpers
|
||||
|
||||
for (unsigned int i = 0; i < pktlist->numPackets; ++i)
|
||||
{
|
||||
concatenator.pushMidiData (packet->data, (int) packet->length, time,
|
||||
input, callback);
|
||||
auto len = readUnaligned<decltype (packet->length)> (&(packet->length));
|
||||
concatenator.pushMidiData (packet->data, (int) len, time, input, callback);
|
||||
|
||||
packet = MIDIPacketNext (packet);
|
||||
}
|
||||
@ -375,22 +380,24 @@ MidiOutput* MidiOutput::openDevice (int index)
|
||||
{
|
||||
MidiOutput* mo = nullptr;
|
||||
|
||||
if (isPositiveAndBelow (index, MIDIGetNumberOfDestinations()))
|
||||
if (auto client = CoreMidiHelpers::getGlobalMidiClient())
|
||||
{
|
||||
auto endPoint = MIDIGetDestination ((ItemCount) index);
|
||||
|
||||
CoreMidiHelpers::ScopedCFString pname;
|
||||
|
||||
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname.cfString)))
|
||||
if (isPositiveAndBelow (index, MIDIGetNumberOfDestinations()))
|
||||
{
|
||||
auto client = CoreMidiHelpers::getGlobalMidiClient();
|
||||
MIDIPortRef port;
|
||||
auto deviceName = CoreMidiHelpers::getConnectedEndpointName (endPoint);
|
||||
auto endPoint = MIDIGetDestination ((ItemCount) index);
|
||||
|
||||
if (client != 0 && CHECK_ERROR (MIDIOutputPortCreate (client, pname.cfString, &port)))
|
||||
CoreMidiHelpers::ScopedCFString pname;
|
||||
|
||||
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname.cfString)))
|
||||
{
|
||||
mo = new MidiOutput (deviceName);
|
||||
mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (port, endPoint);
|
||||
MIDIPortRef port;
|
||||
auto deviceName = CoreMidiHelpers::getConnectedEndpointName (endPoint);
|
||||
|
||||
if (CHECK_ERROR (MIDIOutputPortCreate (client, pname.cfString, &port)))
|
||||
{
|
||||
mo = new MidiOutput (deviceName);
|
||||
mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (port, endPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,19 +407,21 @@ MidiOutput* MidiOutput::openDevice (int index)
|
||||
|
||||
MidiOutput* MidiOutput::createNewDevice (const String& deviceName)
|
||||
{
|
||||
MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient();
|
||||
MIDIEndpointRef endPoint;
|
||||
|
||||
CoreMidiHelpers::ScopedCFString name;
|
||||
name.cfString = deviceName.toCFString();
|
||||
|
||||
if (client != 0 && CHECK_ERROR (MIDISourceCreate (client, name.cfString, &endPoint)))
|
||||
if (auto client = CoreMidiHelpers::getGlobalMidiClient())
|
||||
{
|
||||
CoreMidiHelpers::setUniqueIdForMidiPort (endPoint, deviceName, false);
|
||||
MIDIEndpointRef endPoint;
|
||||
|
||||
auto mo = new MidiOutput (deviceName);
|
||||
mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint);
|
||||
return mo;
|
||||
CoreMidiHelpers::ScopedCFString name;
|
||||
name.cfString = deviceName.toCFString();
|
||||
|
||||
if (CHECK_ERROR (MIDISourceCreate (client, name.cfString, &endPoint)))
|
||||
{
|
||||
CoreMidiHelpers::setUniqueIdForMidiPort (endPoint, deviceName, false);
|
||||
|
||||
auto mo = new MidiOutput (deviceName);
|
||||
mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint);
|
||||
return mo;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -495,15 +504,15 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback)
|
||||
using namespace CoreMidiHelpers;
|
||||
MidiInput* newInput = nullptr;
|
||||
|
||||
if (isPositiveAndBelow (index, MIDIGetNumberOfSources()))
|
||||
if (auto client = getGlobalMidiClient())
|
||||
{
|
||||
if (auto endPoint = MIDIGetSource ((ItemCount) index))
|
||||
if (isPositiveAndBelow (index, MIDIGetNumberOfSources()))
|
||||
{
|
||||
ScopedCFString name;
|
||||
|
||||
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name.cfString)))
|
||||
if (auto endPoint = MIDIGetSource ((ItemCount) index))
|
||||
{
|
||||
if (auto client = getGlobalMidiClient())
|
||||
ScopedCFString name;
|
||||
|
||||
if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name.cfString)))
|
||||
{
|
||||
MIDIPortRef port;
|
||||
std::unique_ptr<MidiPortAndCallback> mpc (new MidiPortAndCallback (*callback));
|
||||
|
@ -346,11 +346,11 @@ public:
|
||||
|
||||
if (asioObject != nullptr)
|
||||
{
|
||||
const int possibleSampleRates[] = { 32000, 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
|
||||
|
||||
for (int index = 0; index < numElementsInArray (possibleSampleRates); ++index)
|
||||
if (asioObject->canSampleRate ((double) possibleSampleRates[index]) == 0)
|
||||
newRates.add ((double) possibleSampleRates[index]);
|
||||
for (auto rate : { 8000, 11025, 16000, 22050, 32000,
|
||||
44100, 48000, 88200, 96000, 176400,
|
||||
192000, 352800, 384000, 705600, 768000 })
|
||||
if (asioObject->canSampleRate ((double) rate) == 0)
|
||||
newRates.add ((double) rate);
|
||||
}
|
||||
|
||||
if (newRates.isEmpty())
|
||||
@ -770,7 +770,7 @@ private:
|
||||
if (CharPointer_UTF8::isValidString (text, length))
|
||||
return String::fromUTF8 (text, length);
|
||||
|
||||
WCHAR wideVersion[64] = {};
|
||||
WCHAR wideVersion[512] = {};
|
||||
MultiByteToWideChar (CP_ACP, 0, text, length, wideVersion, numElementsInArray (wideVersion));
|
||||
return wideVersion;
|
||||
}
|
||||
@ -787,9 +787,14 @@ private:
|
||||
|
||||
void reloadChannelNames()
|
||||
{
|
||||
long totalInChannels = 0, totalOutChannels = 0;
|
||||
|
||||
if (asioObject != nullptr
|
||||
&& asioObject->getChannels (&totalNumInputChans, &totalNumOutputChans) == ASE_OK)
|
||||
&& asioObject->getChannels (&totalInChannels, &totalOutChannels) == ASE_OK)
|
||||
{
|
||||
totalNumInputChans = totalInChannels;
|
||||
totalNumOutputChans = totalOutChannels;
|
||||
|
||||
inputChannelNames.clear();
|
||||
outputChannelNames.clear();
|
||||
|
||||
@ -1108,9 +1113,11 @@ private:
|
||||
String getLastDriverError() const
|
||||
{
|
||||
jassert (asioObject != nullptr);
|
||||
|
||||
char buffer[512] = {};
|
||||
asioObject->getErrorMessage (buffer);
|
||||
return String (buffer, sizeof (buffer) - 1);
|
||||
|
||||
return convertASIOString (buffer, sizeof (buffer));
|
||||
}
|
||||
|
||||
String initDriver()
|
||||
@ -1420,7 +1427,12 @@ public:
|
||||
TCHAR name[256] = {};
|
||||
|
||||
while (RegEnumKey (hk, index++, name, numElementsInArray (name)) == ERROR_SUCCESS)
|
||||
{
|
||||
if (isBlacklistedDriver (name))
|
||||
continue;
|
||||
|
||||
addDriverInfo (name, hk);
|
||||
}
|
||||
|
||||
RegCloseKey (hk);
|
||||
}
|
||||
@ -1552,6 +1564,11 @@ private:
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool isBlacklistedDriver (const String& driverName)
|
||||
{
|
||||
return driverName == "ASIO DirectX Full Duplex Driver" || driverName == "ASIO Multimedia Driver";
|
||||
}
|
||||
|
||||
void addDriverInfo (const String& keyName, HKEY hk)
|
||||
{
|
||||
HKEY subKey;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -352,8 +352,8 @@ void copyWavFormat (WAVEFORMATEXTENSIBLE& dest, const WAVEFORMATEX* src) noexcep
|
||||
class WASAPIDeviceBase
|
||||
{
|
||||
public:
|
||||
WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
|
||||
: device (d), useExclusiveMode (exclusiveMode)
|
||||
WASAPIDeviceBase (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode, std::function<void()>&& cb)
|
||||
: device (d), useExclusiveMode (exclusiveMode), reopenCallback (cb)
|
||||
{
|
||||
clientEvent = CreateEvent (nullptr, false, false, nullptr);
|
||||
|
||||
@ -387,21 +387,21 @@ public:
|
||||
// Got a format that is supported by the device so we can ask what sample rates are supported (in whatever format)
|
||||
}
|
||||
|
||||
static const int ratesToTest[] = { 44100, 48000, 88200, 96000, 176400, 192000, 352800, 384000 };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (ratesToTest); ++i)
|
||||
{
|
||||
if (rates.contains (ratesToTest[i]))
|
||||
for (auto rate : { 8000, 11025, 16000, 22050, 32000,
|
||||
44100, 48000, 88200, 96000, 176400,
|
||||
192000, 352800, 384000, 705600, 768000 })
|
||||
{
|
||||
if (rates.contains (rate))
|
||||
continue;
|
||||
|
||||
format.Format.nSamplesPerSec = (DWORD) ratesToTest[i];
|
||||
format.Format.nSamplesPerSec = (DWORD) rate;
|
||||
format.Format.nAvgBytesPerSec = (DWORD) (format.Format.nSamplesPerSec * format.Format.nChannels * format.Format.wBitsPerSample / 8);
|
||||
|
||||
if (SUCCEEDED (tempClient->IsFormatSupported (useExclusiveMode ? AUDCLNT_SHAREMODE_EXCLUSIVE
|
||||
: AUDCLNT_SHAREMODE_SHARED,
|
||||
(WAVEFORMATEX*) &format, 0)))
|
||||
if (! rates.contains (ratesToTest[i]))
|
||||
rates.addUsingDefaultSort (ratesToTest[i]);
|
||||
if (! rates.contains (rate))
|
||||
rates.addUsingDefaultSort (rate);
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,6 +484,7 @@ public:
|
||||
UINT32 actualBufferSize = 0;
|
||||
int bytesPerSample = 0, bytesPerFrame = 0;
|
||||
bool sampleRateHasChanged = false, shouldClose = false;
|
||||
std::function<void()> reopenCallback;
|
||||
|
||||
virtual void updateFormat (bool isFloat) = 0;
|
||||
|
||||
@ -501,6 +502,9 @@ private:
|
||||
|
||||
JUCE_COMRESULT OnStateChanged(AudioSessionState state)
|
||||
{
|
||||
if (state == AudioSessionStateActive)
|
||||
owner.reopenCallback();
|
||||
|
||||
if (state == AudioSessionStateInactive || state == AudioSessionStateExpired)
|
||||
owner.deviceBecameInactive();
|
||||
|
||||
@ -509,7 +513,6 @@ private:
|
||||
|
||||
JUCE_COMRESULT OnSessionDisconnected (AudioSessionDisconnectReason reason)
|
||||
{
|
||||
Logger::writeToLog("OnSessionDisconnected");
|
||||
if (reason == DisconnectReasonFormatChanged)
|
||||
owner.deviceSampleRateChanged();
|
||||
|
||||
@ -685,8 +688,8 @@ private:
|
||||
class WASAPIInputDevice : public WASAPIDeviceBase
|
||||
{
|
||||
public:
|
||||
WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
|
||||
: WASAPIDeviceBase (d, exclusiveMode)
|
||||
WASAPIInputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode, std::function<void()>&& reopenCallback)
|
||||
: WASAPIDeviceBase (d, exclusiveMode, std::move (reopenCallback))
|
||||
{
|
||||
}
|
||||
|
||||
@ -846,8 +849,8 @@ private:
|
||||
class WASAPIOutputDevice : public WASAPIDeviceBase
|
||||
{
|
||||
public:
|
||||
WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode)
|
||||
: WASAPIDeviceBase (d, exclusiveMode)
|
||||
WASAPIOutputDevice (const ComSmartPtr<IMMDevice>& d, bool exclusiveMode, std::function<void()>&& reopenCallback)
|
||||
: WASAPIDeviceBase (d, exclusiveMode, std::move (reopenCallback))
|
||||
{
|
||||
}
|
||||
|
||||
@ -982,6 +985,7 @@ public:
|
||||
|
||||
~WASAPIAudioIODevice()
|
||||
{
|
||||
cancelPendingUpdate();
|
||||
close();
|
||||
}
|
||||
|
||||
@ -1278,7 +1282,7 @@ public:
|
||||
outs.clear();
|
||||
}
|
||||
|
||||
if (outputDevice != nullptr && !deviceBecameInactive)
|
||||
if (outputDevice != nullptr && ! deviceBecameInactive)
|
||||
{
|
||||
// Note that this function is handed the input device so it can check for the event and make sure
|
||||
// the input reservoir is filled up correctly even when bufferSize > device actualBufferSize
|
||||
@ -1358,34 +1362,46 @@ private:
|
||||
|
||||
auto flow = getDataFlow (device);
|
||||
|
||||
auto deviceReopenCallback = [this]
|
||||
{
|
||||
if (deviceBecameInactive)
|
||||
{
|
||||
deviceBecameInactive = false;
|
||||
MessageManager::callAsync ([this] { reopenDevices(); });
|
||||
}
|
||||
};
|
||||
|
||||
if (deviceId == inputDeviceId && flow == eCapture)
|
||||
inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode));
|
||||
inputDevice.reset (new WASAPIInputDevice (device, useExclusiveMode, deviceReopenCallback));
|
||||
else if (deviceId == outputDeviceId && flow == eRender)
|
||||
outputDevice.reset (new WASAPIOutputDevice (device, useExclusiveMode));
|
||||
outputDevice.reset (new WASAPIOutputDevice (device, useExclusiveMode, deviceReopenCallback));
|
||||
}
|
||||
|
||||
return (outputDeviceId.isEmpty() || (outputDevice != nullptr && outputDevice->isOk()))
|
||||
&& (inputDeviceId.isEmpty() || (inputDevice != nullptr && inputDevice->isOk()));
|
||||
}
|
||||
|
||||
void reopenDevices()
|
||||
{
|
||||
outputDevice = nullptr;
|
||||
inputDevice = nullptr;
|
||||
|
||||
initialise();
|
||||
|
||||
open (lastKnownInputChannels, lastKnownOutputChannels,
|
||||
getChangedSampleRate(), currentBufferSizeSamples);
|
||||
|
||||
start (callback);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
stop();
|
||||
|
||||
outputDevice = nullptr;
|
||||
inputDevice = nullptr;
|
||||
|
||||
// sample rate change
|
||||
if (! deviceBecameInactive)
|
||||
{
|
||||
initialise();
|
||||
|
||||
open (lastKnownInputChannels, lastKnownOutputChannels,
|
||||
getChangedSampleRate(), currentBufferSizeSamples);
|
||||
|
||||
start (callback);
|
||||
}
|
||||
reopenDevices();
|
||||
}
|
||||
|
||||
double getChangedSampleRate() const
|
||||
@ -1502,7 +1518,7 @@ private:
|
||||
class ChangeNotificationClient : public ComBaseClassHelper<IMMNotificationClient>
|
||||
{
|
||||
public:
|
||||
ChangeNotificationClient (WASAPIAudioIODeviceType& d)
|
||||
ChangeNotificationClient (WASAPIAudioIODeviceType* d)
|
||||
: ComBaseClassHelper<IMMNotificationClient> (0), device (d) {}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded (LPCWSTR) { return notify(); }
|
||||
@ -1512,9 +1528,15 @@ private:
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged (LPCWSTR, const PROPERTYKEY) { return notify(); }
|
||||
|
||||
private:
|
||||
WASAPIAudioIODeviceType& device;
|
||||
WeakReference<WASAPIAudioIODeviceType> device;
|
||||
|
||||
HRESULT notify() { device.triggerAsyncDeviceChangeCallback(); return S_OK; }
|
||||
HRESULT notify()
|
||||
{
|
||||
if (device != nullptr)
|
||||
device->triggerAsyncDeviceChangeCallback();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChangeNotificationClient)
|
||||
};
|
||||
@ -1555,7 +1577,7 @@ private:
|
||||
if (! check (enumerator.CoCreateInstance (__uuidof (MMDeviceEnumerator))))
|
||||
return;
|
||||
|
||||
notifyClient = new ChangeNotificationClient (*this);
|
||||
notifyClient = new ChangeNotificationClient (this);
|
||||
enumerator->RegisterEndpointNotificationCallback (notifyClient);
|
||||
}
|
||||
|
||||
@ -1644,6 +1666,7 @@ private:
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (WASAPIAudioIODeviceType)
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WASAPIAudioIODeviceType)
|
||||
};
|
||||
|
||||
|
@ -45,7 +45,7 @@ public:
|
||||
Make sure this object isn't still being used by an AudioIODevice before
|
||||
deleting it!
|
||||
*/
|
||||
virtual ~AudioSourcePlayer();
|
||||
~AudioSourcePlayer() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Changes the current audio source to play from.
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
AudioTransportSource();
|
||||
|
||||
/** Destructor. */
|
||||
~AudioTransportSource();
|
||||
~AudioTransportSource() override;
|
||||
|
||||
//==============================================================================
|
||||
/** Sets the reader that is being used as the input source.
|
||||
|
Reference in New Issue
Block a user