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:
Alex Birch
2019-06-22 20:41:38 +01:00
parent d22c2cd4fa
commit 9ee566b251
1140 changed files with 67534 additions and 105952 deletions

View File

@ -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, &currentSetup);
else
initialiseFromXML (*e, true, preferredDeviceName, &currentSetup);
}
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

View File

@ -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,

View File

@ -43,7 +43,7 @@ class JUCE_API AudioIODeviceCallback
{
public:
/** Destructor. */
virtual ~AudioIODeviceCallback() {}
virtual ~AudioIODeviceCallback() = default;
/** Processes a block of incoming and outgoing audio data.

View File

@ -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;

View File

@ -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
//==============================================================================

View File

@ -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).

View File

@ -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:
//==============================================================================

View File

@ -44,7 +44,7 @@ public:
MidiMessageCollector();
/** Destructor. */
~MidiMessageCollector();
~MidiMessageCollector() override;
//==============================================================================
/** Clears any messages from the queue.

View File

@ -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);

View File

@ -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

View File

@ -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)
};

View File

@ -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)

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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)

View File

@ -77,7 +77,7 @@ public:
private:
//==============================================================================
iOSAudioIODevice (iOSAudioIODeviceType&, const String&, const String&);
iOSAudioIODevice (iOSAudioIODeviceType*, const String&, const String&);
//==============================================================================
friend class iOSAudioIODeviceType;

View File

@ -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)
{

View File

@ -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

View File

@ -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() {}

View File

@ -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)
};

View File

@ -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));

View File

@ -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

View File

@ -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)
};

View File

@ -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.

View File

@ -49,7 +49,7 @@ public:
AudioTransportSource();
/** Destructor. */
~AudioTransportSource();
~AudioTransportSource() override;
//==============================================================================
/** Sets the reader that is being used as the input source.