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

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