mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
3637 lines
166 KiB
C++
3637 lines
166 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE framework.
|
|
Copyright (c) Raw Material Software Limited
|
|
|
|
JUCE is an open source framework subject to commercial or open source
|
|
licensing.
|
|
|
|
By downloading, installing, or using the JUCE framework, or combining the
|
|
JUCE framework with any other source code, object code, content or any other
|
|
copyrightable work, you agree to the terms of the JUCE End User Licence
|
|
Agreement, and all incorporated terms including the JUCE Privacy Policy and
|
|
the JUCE Website Terms of Service, as applicable, which will bind you. If you
|
|
do not agree to the terms of these agreements, we will not license the JUCE
|
|
framework to you, and you must discontinue the installation or download
|
|
process and cease use of the JUCE framework.
|
|
|
|
JUCE End User Licence Agreement: https://juce.com/legal/juce-8-licence/
|
|
JUCE Privacy Policy: https://juce.com/juce-privacy-policy
|
|
JUCE Website Terms of Service: https://juce.com/juce-website-terms-of-service/
|
|
|
|
Or:
|
|
|
|
You may also use this code under the terms of the AGPLv3:
|
|
https://www.gnu.org/licenses/agpl-3.0.en.html
|
|
|
|
THE JUCE FRAMEWORK IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL
|
|
WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING WARRANTY OF
|
|
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
namespace juce
|
|
{
|
|
|
|
constexpr unsigned char javaMidiByteCode[] {
|
|
0x1f, 0x8b, 0x08, 0x08, 0x90, 0xda, 0xee, 0x67, 0x00, 0x03, 0x63, 0x6c,
|
|
0x61, 0x73, 0x73, 0x65, 0x73, 0x2e, 0x64, 0x65, 0x78, 0x00, 0xad, 0x7c,
|
|
0x09, 0x7c, 0x94, 0xd5, 0xd5, 0xf7, 0xb9, 0xcf, 0xcc, 0x64, 0x87, 0x4c,
|
|
0x26, 0x21, 0x09, 0x01, 0x92, 0xc9, 0x24, 0x40, 0x02, 0x64, 0x4f, 0x20,
|
|
0x21, 0x01, 0xb3, 0x22, 0x81, 0x00, 0x31, 0x99, 0x44, 0x21, 0x52, 0x1c,
|
|
0x92, 0x81, 0x0c, 0x4c, 0x66, 0xc6, 0x99, 0x09, 0x8b, 0xb5, 0x15, 0x97,
|
|
0x56, 0xac, 0x4b, 0xdd, 0x5e, 0x97, 0x56, 0x2d, 0x56, 0x45, 0xdc, 0x5a,
|
|
0x5a, 0x97, 0x5a, 0xeb, 0x82, 0xc5, 0x56, 0xdb, 0xda, 0xd6, 0xf6, 0xed,
|
|
0x6b, 0x37, 0xdf, 0x7e, 0xb5, 0xa5, 0xad, 0x76, 0x7b, 0xb5, 0xf5, 0x7d,
|
|
0x5f, 0xfb, 0x75, 0xf1, 0xfb, 0x9f, 0x7b, 0xef, 0x33, 0xf3, 0x64, 0x01,
|
|
0xda, 0xdf, 0xef, 0x8b, 0xfe, 0xe7, 0x9c, 0x7b, 0xee, 0xb9, 0xfb, 0xb9,
|
|
0xe7, 0x9e, 0xfb, 0xcc, 0xc3, 0x8c, 0xf9, 0x0f, 0x64, 0xd4, 0x36, 0xac,
|
|
0xa2, 0x3b, 0x1e, 0xf0, 0x6c, 0xbe, 0xfe, 0xe0, 0xfd, 0x83, 0x8d, 0xef,
|
|
0x45, 0xdf, 0x7b, 0xe8, 0xca, 0xbd, 0xfb, 0x9e, 0xef, 0x2e, 0x7a, 0xb7,
|
|
0xec, 0xce, 0xde, 0xcf, 0xbd, 0x7f, 0x01, 0x51, 0x84, 0x88, 0x0e, 0x0c,
|
|
0x37, 0xba, 0x48, 0xff, 0x55, 0x40, 0xb6, 0x51, 0x28, 0xf9, 0x30, 0x70,
|
|
0x97, 0x83, 0xc8, 0x07, 0xfa, 0x5e, 0x0a, 0x51, 0x1b, 0xe8, 0xa1, 0x4c,
|
|
0xa2, 0x57, 0x40, 0x2f, 0x9a, 0x43, 0x94, 0x03, 0x7a, 0x24, 0x97, 0xa8,
|
|
0xb9, 0x8b, 0xe8, 0xc4, 0x3c, 0xa2, 0xf8, 0x4a, 0xa2, 0x7d, 0xc0, 0x41,
|
|
0xe0, 0x23, 0xc0, 0xe5, 0xc0, 0x55, 0xc0, 0xc3, 0xc0, 0x13, 0xc0, 0xb3,
|
|
0xc0, 0x09, 0xe0, 0x6b, 0xc0, 0xb7, 0x80, 0xef, 0x01, 0xbf, 0x01, 0x96,
|
|
0xae, 0x22, 0x0a, 0x02, 0x87, 0x80, 0x5b, 0x81, 0xa3, 0xc0, 0x43, 0xc0,
|
|
0x63, 0xc0, 0x17, 0x80, 0x27, 0x81, 0x67, 0x80, 0x9f, 0x02, 0x46, 0x33,
|
|
0x51, 0x31, 0x50, 0x07, 0x34, 0x02, 0xab, 0x81, 0xcd, 0x80, 0x0f, 0x98,
|
|
0x04, 0xae, 0x04, 0xee, 0x04, 0xee, 0x02, 0x8e, 0x00, 0x0f, 0x00, 0x8f,
|
|
0x00, 0x5f, 0x00, 0x9e, 0x02, 0x9e, 0x01, 0x5e, 0x00, 0x5e, 0x02, 0xbe,
|
|
0x09, 0xbc, 0x03, 0xcc, 0x6b, 0xc1, 0x18, 0x80, 0x51, 0xe0, 0x1a, 0xe0,
|
|
0x09, 0xe0, 0xc7, 0x80, 0x7d, 0x35, 0xfa, 0x06, 0x6c, 0x00, 0xf6, 0x03,
|
|
0x77, 0x01, 0x5f, 0x02, 0x7e, 0x04, 0xa4, 0xb4, 0x12, 0x2d, 0x07, 0xb6,
|
|
0x00, 0x87, 0x80, 0x2f, 0x03, 0x3f, 0x05, 0x78, 0x92, 0xca, 0x80, 0x73,
|
|
0x00, 0x2f, 0xb0, 0x07, 0xb8, 0x12, 0xb8, 0x1d, 0xf8, 0x36, 0x20, 0xd6,
|
|
0xa0, 0xdf, 0xc0, 0x38, 0xf0, 0x69, 0xe0, 0xbb, 0xc0, 0xdf, 0x81, 0x0d,
|
|
0x6b, 0xa1, 0x03, 0xfc, 0x14, 0xa8, 0x3c, 0x87, 0x28, 0x0c, 0x7c, 0x16,
|
|
0x78, 0x1d, 0xa0, 0x76, 0xa2, 0x16, 0x60, 0x1b, 0xf0, 0x51, 0xe0, 0x1e,
|
|
0xe0, 0x45, 0xe0, 0x37, 0x40, 0x4a, 0x07, 0x91, 0x07, 0x68, 0x01, 0xfa,
|
|
0x80, 0x51, 0xe0, 0x1a, 0xe0, 0x1e, 0xe0, 0x04, 0xf0, 0x03, 0xe0, 0x2d,
|
|
0xc0, 0xd6, 0x49, 0x54, 0x04, 0xb4, 0x00, 0x17, 0x00, 0x61, 0xe0, 0xe3,
|
|
0xc0, 0xa7, 0x80, 0x2f, 0x02, 0x2f, 0x03, 0x6f, 0x00, 0xef, 0x02, 0x29,
|
|
0x58, 0xcf, 0x85, 0x40, 0x35, 0xd0, 0x0e, 0x0c, 0x01, 0xbb, 0x80, 0xfd,
|
|
0xc0, 0x27, 0x80, 0x7b, 0x81, 0xaf, 0x02, 0xbf, 0x05, 0xb2, 0xba, 0x31,
|
|
0x0f, 0xc0, 0x06, 0x60, 0x3b, 0x70, 0x04, 0x78, 0x1d, 0xf8, 0x2d, 0xf0,
|
|
0x0f, 0x20, 0xad, 0x07, 0x76, 0x02, 0x34, 0x03, 0x9b, 0x81, 0xed, 0xc0,
|
|
0x3e, 0xe0, 0x20, 0xf0, 0x11, 0xe0, 0x0a, 0xe0, 0x30, 0x70, 0x1d, 0x70,
|
|
0x2b, 0x70, 0x07, 0x70, 0x37, 0x70, 0x1f, 0xf0, 0x30, 0x70, 0x1c, 0x78,
|
|
0x12, 0x78, 0x06, 0x78, 0x11, 0xf8, 0x06, 0xf0, 0x5d, 0xe0, 0x75, 0xe0,
|
|
0x27, 0xc0, 0x7f, 0x02, 0x6f, 0x02, 0xbf, 0x02, 0xde, 0x06, 0xe6, 0xac,
|
|
0xc3, 0x12, 0x00, 0x3b, 0x81, 0x03, 0xc0, 0xf5, 0xc0, 0x9d, 0xc0, 0x03,
|
|
0xc0, 0x17, 0x80, 0xe7, 0x81, 0x93, 0xc0, 0x6b, 0xc0, 0x1b, 0xc0, 0x29,
|
|
0xe0, 0x77, 0xc0, 0x9f, 0x81, 0x7f, 0x00, 0x45, 0xe7, 0x12, 0xad, 0x00,
|
|
0x3a, 0x01, 0x2f, 0xb0, 0x0b, 0x98, 0x00, 0xf6, 0x03, 0x97, 0x01, 0x1f,
|
|
0x07, 0xae, 0x07, 0x6e, 0x03, 0xee, 0x03, 0x1e, 0x03, 0x9e, 0x01, 0x9e,
|
|
0x03, 0xbe, 0x03, 0xbc, 0x0e, 0xfc, 0x06, 0x78, 0x17, 0xf8, 0x3b, 0x60,
|
|
0xac, 0x27, 0xca, 0x00, 0xf2, 0x80, 0xf9, 0x40, 0x31, 0x50, 0x06, 0x0c,
|
|
0x02, 0xdb, 0x81, 0x43, 0xc0, 0xbd, 0xc0, 0x63, 0xc0, 0xe3, 0xc0, 0xd3,
|
|
0xc0, 0x4b, 0xc0, 0x6b, 0xc0, 0xef, 0x81, 0xf4, 0x5e, 0x22, 0x37, 0xd0,
|
|
0x04, 0xf4, 0x02, 0xa3, 0xc0, 0x25, 0xc0, 0x55, 0xc0, 0xed, 0xc0, 0xe3,
|
|
0xc0, 0xcb, 0xc0, 0xf7, 0x81, 0x9f, 0x01, 0xff, 0x03, 0x64, 0x6f, 0x80,
|
|
0x0d, 0x01, 0xf5, 0xc0, 0x76, 0x60, 0x0c, 0xd8, 0x0b, 0xc4, 0x81, 0xab,
|
|
0x81, 0xcf, 0x02, 0x8f, 0x00, 0x5f, 0x06, 0xbe, 0x0d, 0xbc, 0x01, 0xbc,
|
|
0x0d, 0xfc, 0x19, 0x30, 0x36, 0x12, 0xe5, 0x02, 0xc5, 0x40, 0x2d, 0x70,
|
|
0x2e, 0xd0, 0x0f, 0x8c, 0x02, 0x07, 0x80, 0xab, 0x81, 0x1b, 0x81, 0xdb,
|
|
0x80, 0x23, 0xc0, 0x51, 0xe0, 0x61, 0xe0, 0x38, 0xf0, 0x0c, 0xf0, 0x12,
|
|
0xf0, 0x3d, 0xe0, 0x27, 0xc0, 0x29, 0xe0, 0xf7, 0xc0, 0xda, 0x3e, 0xa2,
|
|
0x9b, 0x80, 0xcf, 0x00, 0xc7, 0x80, 0xa7, 0x80, 0x67, 0x81, 0xef, 0x02,
|
|
0x6f, 0x00, 0xbf, 0x00, 0xde, 0x06, 0x8c, 0x4d, 0xb0, 0x17, 0x60, 0x21,
|
|
0xd0, 0x04, 0x74, 0x03, 0x5e, 0x60, 0x2f, 0x70, 0x29, 0xf0, 0x31, 0xe0,
|
|
0x16, 0xe0, 0x5e, 0xe0, 0x21, 0xe0, 0x79, 0xe0, 0x9b, 0xc0, 0x8f, 0x81,
|
|
0x3f, 0x01, 0x59, 0x9b, 0x51, 0x16, 0x58, 0x0a, 0xd4, 0x03, 0x2d, 0xc0,
|
|
0x66, 0x60, 0x0f, 0x70, 0x25, 0x70, 0x03, 0x70, 0x3b, 0x70, 0x17, 0x70,
|
|
0x14, 0x78, 0x1e, 0xf8, 0x3a, 0xf0, 0x7d, 0xe0, 0x0f, 0xc0, 0x9f, 0x80,
|
|
0xbf, 0x02, 0x8e, 0x2d, 0x44, 0x99, 0xc0, 0x52, 0xa0, 0x09, 0xe8, 0x06,
|
|
0x7a, 0x81, 0x6d, 0xc0, 0x6e, 0x60, 0x3f, 0x70, 0x29, 0x70, 0x25, 0x70,
|
|
0x03, 0x70, 0x37, 0x70, 0x1f, 0xf0, 0x18, 0xf0, 0x24, 0xf0, 0x02, 0xf0,
|
|
0x4d, 0xe0, 0x67, 0xc0, 0x29, 0xe0, 0xaf, 0x40, 0x7e, 0x3f, 0xe6, 0x13,
|
|
0x58, 0x05, 0xb4, 0x03, 0xec, 0x70, 0x17, 0x00, 0x2b, 0x80, 0x2a, 0xa0,
|
|
0x1a, 0xa8, 0x01, 0x6a, 0x81, 0x3a, 0xa0, 0x1e, 0x68, 0x00, 0x1a, 0x81,
|
|
0x26, 0x00, 0x6e, 0x92, 0xe0, 0xfe, 0x08, 0xee, 0x8a, 0xe0, 0x9a, 0x08,
|
|
0x2e, 0x88, 0xb4, 0xbb, 0x21, 0xb8, 0x0f, 0x82, 0xdb, 0x20, 0xed, 0x2a,
|
|
0x08, 0xdb, 0x9d, 0xb0, 0x8d, 0x09, 0xdb, 0x93, 0xb0, 0x05, 0x09, 0xdb,
|
|
0x82, 0x60, 0xda, 0x04, 0x73, 0x24, 0x98, 0x14, 0xc1, 0x44, 0x08, 0x4b,
|
|
0x4d, 0x58, 0x1a, 0xc2, 0xb4, 0x13, 0xa6, 0x8e, 0x30, 0x6c, 0x42, 0x37,
|
|
0x09, 0x5d, 0xa3, 0xf3, 0x80, 0x01, 0x60, 0x10, 0xf0, 0x02, 0x43, 0xfa,
|
|
0x2c, 0x38, 0x1f, 0xc0, 0xf1, 0x40, 0x5b, 0x81, 0x6d, 0xc0, 0x08, 0x70,
|
|
0x21, 0xb0, 0x1d, 0xf8, 0x10, 0xb0, 0x83, 0xcf, 0x05, 0x52, 0xe7, 0xc5,
|
|
0x4e, 0x60, 0x14, 0x18, 0x03, 0xfc, 0xc0, 0x2e, 0x60, 0x37, 0x30, 0x0e,
|
|
0xec, 0x01, 0x82, 0xc0, 0x04, 0x10, 0x22, 0x75, 0xd6, 0x44, 0x81, 0x18,
|
|
0x30, 0x09, 0xec, 0xe3, 0x33, 0x09, 0x38, 0x08, 0x5c, 0x02, 0x7c, 0x18,
|
|
0xb8, 0x14, 0xf8, 0x08, 0xf0, 0x09, 0x3e, 0x93, 0x80, 0xcf, 0xf0, 0xd9,
|
|
0x03, 0xdc, 0x0b, 0x7c, 0x16, 0xb8, 0x0f, 0xb8, 0x9f, 0xd4, 0xbc, 0x9a,
|
|
0x7f, 0x79, 0x9a, 0x1e, 0xc1, 0x24, 0xce, 0xd3, 0xfc, 0x31, 0xf0, 0xf9,
|
|
0x9a, 0x3f, 0x6e, 0x91, 0x3f, 0x0d, 0xde, 0x03, 0x6a, 0xe8, 0x74, 0x85,
|
|
0xe6, 0x4f, 0x68, 0xb9, 0xcd, 0x22, 0x67, 0xfe, 0x95, 0xc6, 0x24, 0x7f,
|
|
0x4c, 0xeb, 0xd8, 0xb5, 0xce, 0x72, 0xcd, 0xbf, 0xa6, 0xe5, 0xa9, 0x96,
|
|
0xb2, 0xe9, 0xc0, 0x1b, 0x5a, 0x9e, 0xa1, 0xe5, 0x65, 0x40, 0x96, 0xee,
|
|
0x27, 0xcb, 0xe7, 0x68, 0x39, 0xf3, 0x73, 0x2d, 0x7c, 0x8e, 0x45, 0xdf,
|
|
0xa5, 0xf5, 0x99, 0xcf, 0xb7, 0x94, 0x9d, 0x6f, 0x69, 0x6b, 0x81, 0xee,
|
|
0x1b, 0xf3, 0x8b, 0xf4, 0x58, 0xca, 0x75, 0xf9, 0x53, 0xe0, 0x17, 0x6b,
|
|
0xfe, 0xf7, 0xe0, 0x97, 0x68, 0xfe, 0x7d, 0xf0, 0x4b, 0x35, 0x6f, 0x6f,
|
|
0x4a, 0xf2, 0x59, 0x4d, 0x8a, 0x2e, 0xd1, 0x6d, 0x55, 0x68, 0xde, 0xac,
|
|
0xbf, 0xc2, 0xc2, 0x57, 0x5a, 0xe6, 0x6d, 0xb9, 0xa5, 0xff, 0x4d, 0x96,
|
|
0xfe, 0x37, 0x5b, 0xfa, 0xbc, 0x5a, 0xcb, 0x97, 0x6a, 0x3e, 0xaf, 0x49,
|
|
0xd5, 0xc3, 0xfc, 0x42, 0xf0, 0xcb, 0x34, 0x5f, 0x6e, 0x91, 0x9b, 0x73,
|
|
0xde, 0x6a, 0x19, 0x6f, 0xab, 0xa5, 0x0f, 0x6d, 0x16, 0x9d, 0x6e, 0x4b,
|
|
0x1f, 0x7a, 0x2c, 0xfa, 0xcc, 0xaf, 0x68, 0x4a, 0xf2, 0xa6, 0x0d, 0x9c,
|
|
0x6b, 0xe9, 0xe7, 0xb9, 0xba, 0x9f, 0x95, 0x9a, 0x6f, 0x6c, 0x52, 0x3a,
|
|
0x1b, 0xb5, 0x0e, 0xdb, 0xe3, 0x66, 0xcd, 0x5f, 0xab, 0x79, 0xd6, 0xbf,
|
|
0x4e, 0xf3, 0x6d, 0xd0, 0xbf, 0x5e, 0xf3, 0xdd, 0xe0, 0x6f, 0xd0, 0x7c,
|
|
0x3f, 0xf8, 0x4f, 0x6a, 0xfe, 0x02, 0xf0, 0x37, 0x6a, 0x7e, 0x1c, 0xfc,
|
|
0x9d, 0x9a, 0x8f, 0x80, 0xbf, 0x59, 0xf3, 0x97, 0x5a, 0x74, 0x0e, 0x5b,
|
|
0x78, 0x9e, 0x67, 0x93, 0xbf, 0xd1, 0x22, 0x67, 0xfb, 0xbc, 0x49, 0xf3,
|
|
0x77, 0x58, 0xea, 0x3c, 0x62, 0xd1, 0x79, 0x14, 0xfc, 0x6d, 0x9a, 0x7f,
|
|
0xc2, 0x22, 0x7f, 0xd6, 0xc2, 0xbf, 0x64, 0xe1, 0x5f, 0xb5, 0xf0, 0x3f,
|
|
0x68, 0x4a, 0xd6, 0xff, 0x06, 0xf8, 0x5b, 0x35, 0x7f, 0x0a, 0xfc, 0xa7,
|
|
0x34, 0xff, 0x0e, 0xf8, 0xdb, 0x35, 0xff, 0x37, 0xf0, 0x77, 0x68, 0x3e,
|
|
0x6d, 0x65, 0xb2, 0xdd, 0xc2, 0x95, 0xc9, 0xbe, 0xb9, 0x57, 0x26, 0xeb,
|
|
0x5f, 0xb1, 0x32, 0x59, 0x7f, 0xa3, 0x45, 0xbe, 0xd0, 0xd2, 0x87, 0x63,
|
|
0x96, 0xb1, 0xb7, 0x59, 0x74, 0xba, 0x2d, 0x75, 0xf6, 0x81, 0xff, 0x37,
|
|
0x73, 0x9e, 0xc1, 0x7f, 0x5a, 0xf3, 0x17, 0x81, 0xbf, 0xc5, 0x9c, 0xf3,
|
|
0x95, 0xca, 0xa7, 0x6c, 0xd1, 0xeb, 0x78, 0xb7, 0xe6, 0x79, 0x1d, 0xef,
|
|
0xd1, 0x3c, 0xaf, 0x8b, 0xc9, 0x2f, 0xb4, 0xf0, 0xa6, 0x8d, 0x0d, 0x58,
|
|
0x6c, 0x6c, 0x50, 0xf3, 0x85, 0xc0, 0xc7, 0x88, 0xf7, 0xde, 0x5c, 0x7a,
|
|
0x44, 0xd2, 0x56, 0x3a, 0x2e, 0x69, 0x17, 0x7d, 0x51, 0xd2, 0x54, 0xaa,
|
|
0x45, 0xac, 0x5d, 0x8c, 0x53, 0xe0, 0x29, 0x62, 0x0a, 0xbf, 0x8d, 0x74,
|
|
0x09, 0x76, 0xfa, 0x3f, 0x88, 0xa9, 0x4a, 0xbb, 0x41, 0x5f, 0xe0, 0xf9,
|
|
0x81, 0x87, 0x7f, 0x89, 0x54, 0xfa, 0xef, 0x92, 0xb6, 0x52, 0xba, 0x50,
|
|
0x74, 0x8e, 0xa6, 0x15, 0x9a, 0x36, 0x0a, 0xa5, 0xbf, 0x5a, 0x97, 0x37,
|
|
0xeb, 0xe9, 0x06, 0x2d, 0x85, 0xc7, 0x78, 0x98, 0x98, 0x22, 0x8e, 0x93,
|
|
0x74, 0x0b, 0x09, 0x29, 0xaf, 0xa4, 0x32, 0x49, 0x97, 0xd1, 0x32, 0xa1,
|
|
0xf2, 0xb9, 0x9c, 0x07, 0xde, 0xe6, 0x61, 0x3d, 0xb6, 0xaf, 0x4a, 0xaa,
|
|
0xf4, 0x3c, 0x3a, 0xbf, 0x1c, 0xe9, 0x97, 0x49, 0xf9, 0x96, 0x57, 0x24,
|
|
0x15, 0xf4, 0x0d, 0x62, 0xff, 0xa2, 0xe4, 0x8b, 0xb5, 0x7c, 0x09, 0x65,
|
|
0xd3, 0x09, 0x49, 0xed, 0xf4, 0x5d, 0x49, 0x2b, 0xa9, 0x5d, 0x28, 0x3f,
|
|
0xc2, 0xf5, 0x2c, 0xd5, 0xf5, 0x2e, 0xd5, 0xf2, 0x0a, 0xe4, 0xb0, 0x7e,
|
|
0x85, 0x96, 0x57, 0x68, 0x79, 0xa5, 0x9e, 0xcf, 0x4a, 0x9c, 0x72, 0x4f,
|
|
0x48, 0x7a, 0x0e, 0xbd, 0x28, 0xe9, 0x06, 0xfa, 0xba, 0xa4, 0xf3, 0x28,
|
|
0x4d, 0x28, 0x79, 0x96, 0x50, 0x7a, 0xa5, 0x9a, 0x7a, 0x34, 0xed, 0x12,
|
|
0xec, 0x57, 0x04, 0xfd, 0x8d, 0x94, 0x8f, 0xfa, 0xaa, 0xa6, 0x4b, 0x85,
|
|
0xa2, 0x3c, 0x4f, 0x7c, 0x2e, 0x5f, 0x4d, 0x8a, 0x1e, 0x96, 0x74, 0x11,
|
|
0x7d, 0x41, 0xd2, 0x4a, 0xca, 0x14, 0x7c, 0x3e, 0x64, 0xd1, 0x8f, 0x89,
|
|
0xa9, 0x83, 0x7e, 0x2a, 0x69, 0x3a, 0xbd, 0x4b, 0x7c, 0x26, 0x64, 0xd2,
|
|
0x57, 0x24, 0xad, 0xa4, 0xef, 0x6b, 0xfa, 0x5b, 0xe2, 0x33, 0xa1, 0x91,
|
|
0xbe, 0xac, 0xe9, 0x49, 0x49, 0x53, 0xe9, 0x67, 0x92, 0x8e, 0x50, 0x35,
|
|
0xea, 0x73, 0x40, 0xce, 0x67, 0x45, 0x0a, 0xb8, 0xef, 0x11, 0x9f, 0x1b,
|
|
0xc3, 0xd4, 0x26, 0x98, 0xae, 0xa1, 0x73, 0x40, 0xd3, 0x74, 0x7e, 0x7a,
|
|
0x82, 0xce, 0xa5, 0x27, 0x25, 0x9d, 0x43, 0x2d, 0xc8, 0xcf, 0xd4, 0xf5,
|
|
0x65, 0xe9, 0xfc, 0x2c, 0x72, 0xca, 0xfc, 0x2c, 0xcc, 0xfb, 0x2a, 0xa1,
|
|
0x68, 0xb3, 0xe0, 0x33, 0x25, 0x87, 0xfe, 0x83, 0x98, 0x56, 0xd0, 0x9f,
|
|
0x41, 0xb3, 0x75, 0xbf, 0xb2, 0xb1, 0xce, 0xbf, 0x91, 0x34, 0x97, 0x16,
|
|
0x09, 0xa6, 0x79, 0xe4, 0x06, 0x75, 0xea, 0xfe, 0x3b, 0x81, 0x3f, 0x68,
|
|
0xfa, 0xdf, 0xa4, 0xce, 0xa3, 0xff, 0xd4, 0xf4, 0xd7, 0x9a, 0xbe, 0xa5,
|
|
0xe9, 0xef, 0x25, 0xdd, 0x41, 0xff, 0x25, 0xe9, 0x3c, 0x7a, 0x47, 0xcb,
|
|
0xb9, 0x9c, 0x4b, 0xb7, 0xe7, 0xd2, 0xf5, 0xb9, 0x10, 0x51, 0xd4, 0xa3,
|
|
0x9d, 0x5c, 0xdd, 0xef, 0x5c, 0x44, 0x31, 0xb9, 0x42, 0xd1, 0x7c, 0xc1,
|
|
0x67, 0xf4, 0x45, 0xf4, 0x7f, 0x88, 0x69, 0x1d, 0xfd, 0x5c, 0xd2, 0x56,
|
|
0x5a, 0x20, 0xe5, 0x9d, 0xb4, 0x50, 0xd2, 0x55, 0xb4, 0x5c, 0xd2, 0x6e,
|
|
0x4d, 0xbb, 0xe8, 0x5c, 0xc1, 0xe7, 0xa2, 0x6a, 0x27, 0x1f, 0xde, 0xfd,
|
|
0x19, 0x4d, 0x4f, 0x92, 0x3a, 0x2f, 0xb9, 0xdd, 0x02, 0xdd, 0xde, 0x7c,
|
|
0xcc, 0xc3, 0x6b, 0x92, 0x2e, 0xa3, 0x3f, 0x11, 0x9f, 0x9b, 0xf9, 0xf4,
|
|
0x1d, 0xde, 0xe7, 0x58, 0xb9, 0xe7, 0xe5, 0x3e, 0x5d, 0x2b, 0xf5, 0x16,
|
|
0x61, 0x85, 0x7f, 0x24, 0xe9, 0x32, 0x3a, 0x25, 0x69, 0x15, 0xbd, 0x2d,
|
|
0xe9, 0x10, 0xe5, 0x08, 0xa6, 0x4d, 0x54, 0x28, 0xe9, 0x4a, 0x9a, 0x2f,
|
|
0xe9, 0x87, 0x68, 0x89, 0xa4, 0x17, 0xd0, 0x1a, 0x49, 0x07, 0x69, 0x9d,
|
|
0xdc, 0xef, 0xe7, 0xc9, 0xfa, 0x8a, 0xf5, 0x38, 0x8b, 0xf5, 0x38, 0x4b,
|
|
0x10, 0xb9, 0xa5, 0xca, 0x7d, 0xad, 0xfa, 0x55, 0x8a, 0xc8, 0x4c, 0x51,
|
|
0x35, 0x0e, 0xa6, 0xdf, 0x92, 0xb4, 0x94, 0xfe, 0x28, 0x29, 0xa2, 0x30,
|
|
0xa1, 0xe4, 0x05, 0x92, 0x6e, 0xa5, 0x22, 0x9d, 0x6e, 0xd5, 0x74, 0xad,
|
|
0xdc, 0xa7, 0x7d, 0xb2, 0x1e, 0x8f, 0xae, 0xc7, 0xa3, 0xeb, 0xf1, 0xe8,
|
|
0x7a, 0x3c, 0xba, 0x1e, 0x8f, 0x2e, 0xef, 0xd1, 0xe5, 0x3d, 0xba, 0x7c,
|
|
0x99, 0xee, 0x4f, 0x99, 0x2e, 0x5f, 0xa6, 0xcb, 0x95, 0xe9, 0x72, 0x65,
|
|
0x5a, 0xbf, 0x4c, 0xeb, 0x97, 0x63, 0x1f, 0xa6, 0x4a, 0x3f, 0xb0, 0x84,
|
|
0x1e, 0x93, 0xfb, 0xbf, 0x57, 0xa6, 0x17, 0xeb, 0xf4, 0x12, 0x9c, 0x9e,
|
|
0x9c, 0x5e, 0x0a, 0xef, 0xab, 0x68, 0x1b, 0xb9, 0xe4, 0xfe, 0xde, 0x22,
|
|
0xd3, 0x15, 0xa8, 0xf7, 0x4b, 0x7a, 0xdf, 0xbf, 0x2e, 0x69, 0x06, 0xfd,
|
|
0x92, 0xd4, 0x39, 0xfd, 0x2b, 0x49, 0x1b, 0xa9, 0x46, 0xee, 0x63, 0xb5,
|
|
0x2e, 0x95, 0xd8, 0x19, 0x0f, 0x48, 0xaa, 0xc6, 0x55, 0x89, 0x19, 0x7d,
|
|
0x55, 0xd2, 0x12, 0xfa, 0xb6, 0xce, 0xff, 0xa1, 0xa4, 0x6a, 0xfd, 0x2a,
|
|
0xb1, 0x12, 0xbf, 0x90, 0x54, 0xd0, 0x7b, 0x92, 0x16, 0x91, 0x43, 0xd6,
|
|
0xb7, 0x80, 0x52, 0x24, 0x5d, 0x48, 0xa9, 0x92, 0xae, 0x93, 0xeb, 0xc3,
|
|
0x7e, 0x24, 0x4f, 0xd2, 0xf5, 0x34, 0x4f, 0x52, 0xb5, 0x5e, 0x95, 0xb0,
|
|
0x98, 0x62, 0x49, 0x0b, 0xa9, 0x44, 0xd2, 0x0b, 0xa9, 0x5c, 0xd2, 0xd5,
|
|
0xb4, 0x58, 0xa8, 0xfe, 0x2c, 0x97, 0xb4, 0x9f, 0xaa, 0x24, 0x1d, 0xa4,
|
|
0x1e, 0xe9, 0x77, 0x9a, 0x65, 0xbf, 0x97, 0x21, 0x92, 0x7f, 0x5f, 0x52,
|
|
0x35, 0xef, 0xcb, 0xf5, 0x3c, 0x2f, 0x47, 0x44, 0xfd, 0x35, 0xed, 0x8f,
|
|
0xfe, 0x4a, 0x7c, 0x47, 0x50, 0xf2, 0x15, 0xba, 0x3f, 0x2b, 0x74, 0xfb,
|
|
0x55, 0x90, 0xa7, 0x90, 0xa2, 0xa9, 0x92, 0x36, 0xd1, 0x51, 0x49, 0xcf,
|
|
0xa5, 0x07, 0x25, 0x2d, 0xa0, 0x7f, 0x97, 0xd4, 0x4d, 0x3f, 0x90, 0x34,
|
|
0x85, 0xde, 0x90, 0xb4, 0x5e, 0xee, 0xa3, 0x2a, 0xdc, 0x3a, 0xde, 0xd4,
|
|
0xfa, 0xff, 0xa3, 0xcb, 0xff, 0x2f, 0xa9, 0xd8, 0xec, 0x03, 0x49, 0xcb,
|
|
0xa8, 0x57, 0x70, 0xbc, 0xa5, 0xea, 0x5f, 0xad, 0xe7, 0x77, 0x35, 0x2c,
|
|
0xe3, 0x27, 0x92, 0x56, 0x52, 0x87, 0xe0, 0xb8, 0x4b, 0xf5, 0xb7, 0x15,
|
|
0xeb, 0x66, 0x08, 0x8e, 0xbd, 0x06, 0xa5, 0x7e, 0x1b, 0x46, 0xf0, 0xa8,
|
|
0xa4, 0x4a, 0x6f, 0x2d, 0x4e, 0x49, 0x96, 0xaf, 0x85, 0x65, 0xf0, 0x7e,
|
|
0x3b, 0x47, 0xd7, 0x7b, 0x0e, 0x6e, 0x19, 0x8a, 0x6e, 0xa7, 0x87, 0xb4,
|
|
0xfc, 0x69, 0x9d, 0x7e, 0x56, 0xd2, 0x15, 0x64, 0x13, 0x2a, 0xbd, 0x42,
|
|
0xd2, 0xc5, 0xb4, 0x52, 0xf0, 0x5d, 0xa7, 0x86, 0x3e, 0x47, 0x7c, 0xd7,
|
|
0x51, 0xf5, 0x74, 0xea, 0x72, 0x9d, 0xd0, 0x7b, 0x4e, 0xd2, 0x25, 0xb2,
|
|
0x9d, 0x4e, 0x58, 0xd8, 0xef, 0x24, 0xad, 0x25, 0xbb, 0x50, 0xe9, 0x4a,
|
|
0xa1, 0xf2, 0xb9, 0xbe, 0x2e, 0x5d, 0xbe, 0x4b, 0xf7, 0xa3, 0x4b, 0xf7,
|
|
0xa3, 0x4b, 0xb7, 0xd7, 0x8d, 0x71, 0xfd, 0x85, 0x98, 0x96, 0x93, 0x53,
|
|
0x70, 0xac, 0xa8, 0xfa, 0xb3, 0x0e, 0x9e, 0xfb, 0xff, 0x12, 0xc7, 0x85,
|
|
0x2a, 0xbd, 0x5e, 0xd7, 0xb3, 0x1e, 0x37, 0xa5, 0x3a, 0xc1, 0xf7, 0x2c,
|
|
0x95, 0xee, 0xd5, 0xf6, 0xdc, 0x87, 0x1b, 0x5d, 0xb6, 0xe0, 0xfb, 0x96,
|
|
0xda, 0x07, 0x1c, 0x93, 0x23, 0x09, 0xeb, 0x54, 0x7f, 0x7c, 0xde, 0x1e,
|
|
0xc6, 0x45, 0xeb, 0x35, 0x04, 0x13, 0xab, 0x87, 0x54, 0x3c, 0x20, 0xa4,
|
|
0x3f, 0x4c, 0xe6, 0x1f, 0x47, 0x7e, 0x8a, 0x0e, 0x30, 0x4a, 0x74, 0xfe,
|
|
0x6a, 0x4b, 0xfe, 0x09, 0xe4, 0x2f, 0xd4, 0xf9, 0xee, 0x59, 0xf2, 0x5f,
|
|
0x43, 0xfe, 0x32, 0x9d, 0xcf, 0xf1, 0xad, 0xb0, 0xb3, 0x1f, 0x4c, 0xe6,
|
|
0xbf, 0x83, 0xfc, 0xf5, 0x83, 0xc9, 0xb4, 0xd0, 0x7a, 0x66, 0x9a, 0x2f,
|
|
0x81, 0xbb, 0x74, 0xbe, 0x67, 0x96, 0x7c, 0x37, 0xf2, 0xef, 0xd2, 0xf9,
|
|
0x1c, 0x0f, 0xe4, 0x38, 0x55, 0xfb, 0x37, 0x36, 0xf2, 0x79, 0xa2, 0xfe,
|
|
0xbe, 0xa9, 0xf3, 0x17, 0x9f, 0x26, 0xff, 0x4d, 0x9d, 0xbf, 0x44, 0xa7,
|
|
0xad, 0xfd, 0x6f, 0x46, 0xfd, 0xff, 0xad, 0xf3, 0x97, 0x6a, 0xb9, 0xc3,
|
|
0x92, 0xdf, 0x8f, 0xfc, 0x34, 0xaf, 0x4a, 0x57, 0x68, 0x79, 0xaf, 0x25,
|
|
0x7f, 0x1c, 0xf9, 0x0b, 0x74, 0xfe, 0x8a, 0x59, 0xe6, 0xe7, 0x10, 0xf2,
|
|
0xdb, 0x75, 0x7e, 0xa5, 0xce, 0x67, 0xbd, 0x3b, 0x1a, 0x55, 0xfe, 0x8d,
|
|
0xc8, 0xf7, 0xea, 0xfc, 0x65, 0x3a, 0xdf, 0x3a, 0x7f, 0x3f, 0x47, 0xfe,
|
|
0xb7, 0x74, 0xfe, 0xf2, 0x59, 0xea, 0x7f, 0x1f, 0xf9, 0x6f, 0xea, 0xfc,
|
|
0xaa, 0x59, 0xf2, 0xd3, 0x70, 0xc1, 0xfe, 0x2b, 0xf2, 0xd7, 0x0c, 0x71,
|
|
0xdc, 0x60, 0xc8, 0x3b, 0xe8, 0x45, 0x75, 0x6a, 0x2d, 0xbd, 0x30, 0xbe,
|
|
0x8b, 0x9d, 0x1c, 0x39, 0x67, 0x51, 0xc8, 0x6d, 0x40, 0x96, 0x65, 0xb4,
|
|
0x18, 0x99, 0x54, 0x64, 0x2c, 0x84, 0x07, 0xfb, 0x26, 0x5d, 0xec, 0x6e,
|
|
0x83, 0xbe, 0xcb, 0xc8, 0x36, 0x94, 0xe6, 0xbf, 0x69, 0xcd, 0x2b, 0xa0,
|
|
0x99, 0x01, 0xa9, 0x59, 0xdf, 0x78, 0x1d, 0xc7, 0x0a, 0x66, 0x7d, 0xa6,
|
|
0xd6, 0x3e, 0xad, 0x95, 0x6c, 0x37, 0x54, 0xa7, 0xd6, 0x64, 0xb6, 0x76,
|
|
0x43, 0x4e, 0x07, 0xc7, 0x3f, 0x46, 0x8e, 0xd4, 0x17, 0x52, 0x7f, 0xb2,
|
|
0x4e, 0xd9, 0xb2, 0x87, 0x77, 0x74, 0xa9, 0x8d, 0x22, 0xee, 0x87, 0x10,
|
|
0x2b, 0xe4, 0xc8, 0xfa, 0x78, 0x9c, 0x97, 0xd4, 0xa9, 0xbb, 0x6d, 0xc4,
|
|
0x39, 0x17, 0x69, 0x0f, 0xf6, 0x57, 0xc4, 0xc9, 0xe7, 0xc9, 0x88, 0xd3,
|
|
0x46, 0x23, 0x38, 0x00, 0xf8, 0xae, 0x6b, 0x87, 0x2e, 0xdf, 0xa9, 0x0f,
|
|
0xd7, 0xa9, 0x73, 0x3a, 0xea, 0x7c, 0x01, 0xe9, 0x2c, 0x5b, 0xd4, 0xf9,
|
|
0x1c, 0x68, 0x06, 0x7c, 0xd2, 0x1c, 0xc8, 0x9e, 0x67, 0x19, 0xec, 0x3f,
|
|
0x93, 0x5c, 0x39, 0xa1, 0xda, 0x2e, 0x44, 0x10, 0x15, 0xef, 0xcc, 0xd5,
|
|
0xbd, 0x56, 0x7f, 0xdc, 0xde, 0x5c, 0x69, 0x1b, 0x36, 0x29, 0xbd, 0xa9,
|
|
0x4e, 0x3d, 0x43, 0x71, 0x39, 0xeb, 0x6d, 0x0e, 0x72, 0xb9, 0x1b, 0x6c,
|
|
0x39, 0x14, 0x72, 0xba, 0xd0, 0x5e, 0x16, 0xea, 0xcc, 0x94, 0xfd, 0x0d,
|
|
0x39, 0xed, 0x7a, 0x8c, 0x5f, 0x41, 0x79, 0x57, 0x7b, 0x83, 0xad, 0x84,
|
|
0x8a, 0x6c, 0x3c, 0xb7, 0x1f, 0x97, 0x73, 0x6b, 0x33, 0x4b, 0xd8, 0x9a,
|
|
0x6d, 0x2e, 0xf2, 0x96, 0xab, 0x12, 0x36, 0x59, 0xe2, 0x59, 0xc8, 0x75,
|
|
0xca, 0x16, 0x72, 0xaf, 0x83, 0x87, 0x9d, 0x0b, 0x6f, 0x6b, 0xc8, 0xe7,
|
|
0x00, 0x77, 0xd7, 0xa9, 0x67, 0x27, 0xde, 0xf6, 0xe4, 0x3c, 0x16, 0x89,
|
|
0x85, 0xf0, 0xfe, 0x69, 0x54, 0xe4, 0x98, 0x2b, 0xeb, 0x3e, 0x0f, 0xfa,
|
|
0x21, 0x0e, 0xac, 0x90, 0xa7, 0xe4, 0x73, 0x12, 0xf2, 0x66, 0xc7, 0x2a,
|
|
0xf2, 0x38, 0xd2, 0x31, 0x5b, 0xd9, 0x18, 0x4f, 0x91, 0x70, 0xa0, 0x96,
|
|
0x56, 0xf4, 0x30, 0x4b, 0x84, 0xdc, 0x1c, 0x5b, 0x79, 0x44, 0x16, 0xf2,
|
|
0x72, 0xb9, 0xcf, 0xae, 0x90, 0x7b, 0x1e, 0xec, 0x28, 0xcb, 0x70, 0xa1,
|
|
0x17, 0xf9, 0xe8, 0x05, 0xb8, 0x32, 0xc5, 0x85, 0x9c, 0x4e, 0x94, 0xce,
|
|
0x72, 0x94, 0x3b, 0xcf, 0x93, 0xf4, 0x62, 0xe7, 0x71, 0x49, 0x43, 0xce,
|
|
0x3c, 0x55, 0x57, 0x7b, 0x16, 0xf5, 0x77, 0xa0, 0x17, 0xee, 0x39, 0xf0,
|
|
0xac, 0x3c, 0x77, 0xa6, 0x1d, 0x7c, 0xa1, 0x4e, 0x3d, 0xab, 0x98, 0x6a,
|
|
0x2f, 0x1f, 0x87, 0x1d, 0xcc, 0x85, 0x8e, 0x4d, 0xce, 0xf5, 0x97, 0xf4,
|
|
0xda, 0x47, 0x9c, 0xbc, 0x2b, 0x46, 0xd0, 0xc7, 0xad, 0x46, 0x0a, 0xf2,
|
|
0x1d, 0x7a, 0x3d, 0x4f, 0xd4, 0xa9, 0xb8, 0x31, 0xec, 0x5e, 0xce, 0x7e,
|
|
0xb6, 0xd6, 0x81, 0x5e, 0x97, 0xd0, 0x40, 0x7d, 0x0a, 0x45, 0x6a, 0x37,
|
|
0x50, 0x87, 0x11, 0x72, 0xdf, 0x80, 0xfc, 0xb9, 0x89, 0x36, 0x5f, 0xae,
|
|
0x53, 0xcf, 0x4a, 0xc2, 0xee, 0x15, 0x18, 0x8f, 0x17, 0xd6, 0x16, 0x72,
|
|
0x7f, 0x52, 0xb6, 0x48, 0x89, 0x36, 0xbf, 0x93, 0x68, 0xf3, 0x6e, 0xd9,
|
|
0x66, 0x2a, 0xda, 0x4c, 0x23, 0xb6, 0x2d, 0x21, 0xf3, 0x7f, 0x58, 0xa7,
|
|
0x9e, 0xc3, 0xa8, 0xfc, 0xb9, 0xda, 0x0a, 0x71, 0x5f, 0xad, 0x53, 0xcf,
|
|
0x6d, 0x86, 0x9c, 0xf3, 0xa4, 0x7d, 0xdb, 0xb4, 0xfe, 0x9b, 0x75, 0xea,
|
|
0x79, 0x90, 0x07, 0x3b, 0x37, 0xe2, 0xe4, 0x9b, 0xfd, 0x40, 0x69, 0x1e,
|
|
0x6d, 0x15, 0x4d, 0xb0, 0x8e, 0x5c, 0xcc, 0xdf, 0x2a, 0x68, 0x65, 0x88,
|
|
0xad, 0x62, 0x25, 0xb9, 0x4a, 0xb7, 0x8a, 0x55, 0x32, 0x9e, 0x4d, 0x83,
|
|
0x87, 0xe7, 0xba, 0xde, 0xd6, 0xf6, 0x1a, 0x71, 0x6e, 0x97, 0xb6, 0xbd,
|
|
0x0c, 0xe3, 0xba, 0x9a, 0x2e, 0x4a, 0x1d, 0x71, 0x16, 0xc0, 0xb6, 0xf3,
|
|
0x69, 0x24, 0x37, 0x97, 0xb6, 0xe6, 0xa1, 0xb6, 0x79, 0x85, 0xd8, 0xa1,
|
|
0x17, 0xba, 0xe6, 0xc9, 0xb9, 0x11, 0xb2, 0x4f, 0x7f, 0xa9, 0x53, 0xbe,
|
|
0xce, 0xdb, 0x9e, 0x8b, 0x38, 0xa8, 0x19, 0x51, 0x77, 0xc8, 0xb9, 0x45,
|
|
0x5a, 0xa6, 0xb7, 0x93, 0xdb, 0x6d, 0x84, 0x56, 0x45, 0x86, 0xdc, 0xe5,
|
|
0xb6, 0x02, 0x69, 0x15, 0xf5, 0x54, 0x61, 0xa4, 0x3a, 0xbd, 0x9d, 0xf9,
|
|
0xd4, 0x6c, 0xa4, 0xc2, 0x7e, 0x79, 0x76, 0x3e, 0x06, 0xad, 0x91, 0x0e,
|
|
0xb4, 0xd4, 0x91, 0x8b, 0xba, 0xd3, 0xa5, 0xed, 0x63, 0x5c, 0xa2, 0x31,
|
|
0xdb, 0x9c, 0x0f, 0x47, 0xbd, 0x7a, 0x16, 0x15, 0x72, 0xf2, 0x13, 0xbf,
|
|
0xb0, 0xf3, 0x1e, 0xbd, 0x47, 0x84, 0x9e, 0x17, 0xeb, 0xbe, 0x49, 0x91,
|
|
0x91, 0xc5, 0x54, 0x99, 0xb9, 0xcf, 0x33, 0xeb, 0xd5, 0x3c, 0x79, 0x61,
|
|
0x57, 0xcd, 0x98, 0x41, 0xaf, 0x3b, 0x57, 0xee, 0xa1, 0xa1, 0xd2, 0x79,
|
|
0xb8, 0x87, 0xa4, 0x62, 0xb6, 0x0a, 0xd0, 0x9f, 0x26, 0xac, 0xa7, 0xcb,
|
|
0x79, 0xa1, 0x7b, 0xde, 0x94, 0xb2, 0xae, 0x33, 0x94, 0x6d, 0x4e, 0x94,
|
|
0x5d, 0xc9, 0x65, 0xc9, 0x2c, 0x6b, 0x58, 0xfa, 0x27, 0xeb, 0x40, 0x39,
|
|
0x73, 0xcd, 0xe6, 0xd7, 0xcf, 0x5c, 0xb3, 0x22, 0xbd, 0x66, 0xf3, 0x31,
|
|
0xd2, 0x8a, 0xe4, 0x9a, 0xe5, 0x98, 0x6b, 0xe6, 0x90, 0x33, 0x03, 0x7f,
|
|
0x5b, 0xaf, 0x9e, 0xbb, 0xf1, 0x9a, 0xb1, 0xfd, 0x2c, 0xa0, 0x11, 0x63,
|
|
0x3e, 0x6d, 0xb5, 0xa1, 0xbc, 0x7d, 0xa1, 0x65, 0x7d, 0xaa, 0xeb, 0xcd,
|
|
0xf5, 0x99, 0x9f, 0x58, 0x9f, 0x0b, 0xf4, 0xfa, 0x70, 0x1b, 0x4b, 0x4f,
|
|
0xbb, 0x3e, 0x0b, 0x66, 0xac, 0x0f, 0xda, 0xe8, 0x98, 0x7f, 0x9a, 0xf5,
|
|
0x59, 0x9d, 0x58, 0x9f, 0xe1, 0x29, 0xeb, 0x93, 0x25, 0x57, 0x43, 0x50,
|
|
0x7b, 0xbd, 0x7a, 0xa6, 0xe9, 0xbd, 0x68, 0x3e, 0xea, 0xcc, 0xa1, 0xd4,
|
|
0x8b, 0xc5, 0x15, 0xe2, 0x16, 0xfb, 0xfd, 0xf1, 0x94, 0x4a, 0xa9, 0x59,
|
|
0x94, 0x8a, 0x1e, 0xa4, 0x35, 0x08, 0xee, 0xc1, 0xd5, 0x48, 0xa7, 0x99,
|
|
0xb6, 0x2d, 0x1a, 0xb3, 0x66, 0x5b, 0xe3, 0xd3, 0xc9, 0x98, 0xdf, 0x58,
|
|
0x6f, 0xee, 0x9d, 0x66, 0x99, 0x93, 0x22, 0x6f, 0xb9, 0x44, 0x5b, 0xea,
|
|
0x95, 0x0f, 0xf7, 0xb6, 0x2f, 0xa2, 0x81, 0x8e, 0x62, 0x1a, 0xec, 0x2c,
|
|
0xa1, 0x12, 0xc7, 0x12, 0x0a, 0x0d, 0x1d, 0x24, 0x67, 0x7d, 0x96, 0x23,
|
|
0xc7, 0x91, 0x9c, 0x5f, 0x6f, 0xbd, 0xf2, 0x1b, 0xe6, 0xfe, 0x5c, 0x84,
|
|
0xfd, 0x59, 0x4c, 0xdb, 0xe0, 0x55, 0xe7, 0x26, 0xfc, 0x3c, 0xd7, 0x27,
|
|
0xcf, 0x22, 0xb7, 0x1b, 0x2b, 0x57, 0x4a, 0x25, 0x36, 0xd4, 0x55, 0x7b,
|
|
0x09, 0x39, 0x6d, 0x59, 0xb6, 0x1c, 0x5b, 0x72, 0xaf, 0x8f, 0xd4, 0x4f,
|
|
0xdd, 0xeb, 0x6e, 0xd4, 0x55, 0x2a, 0xd7, 0x48, 0xb5, 0xe5, 0xd3, 0x6b,
|
|
0x39, 0x22, 0xca, 0x4d, 0x1d, 0xc3, 0x43, 0x23, 0xb6, 0x32, 0xac, 0xe5,
|
|
0x62, 0x8b, 0x9f, 0x9a, 0xd0, 0xf5, 0x8c, 0x88, 0xa5, 0xd0, 0xcb, 0x90,
|
|
0x7a, 0x4b, 0x64, 0x7f, 0xec, 0xd2, 0xb6, 0xf6, 0xd5, 0xab, 0x7b, 0xaa,
|
|
0xcb, 0xd3, 0x50, 0x96, 0x2d, 0xfd, 0x69, 0x3a, 0xfb, 0x1f, 0xe8, 0x84,
|
|
0x9c, 0x69, 0xda, 0xe7, 0x5e, 0x09, 0x0b, 0xb4, 0xda, 0xe5, 0x65, 0xf5,
|
|
0x2a, 0x06, 0xf1, 0xc2, 0xda, 0xbc, 0x88, 0x86, 0x72, 0xcc, 0xb5, 0x45,
|
|
0x9b, 0x57, 0xd5, 0xab, 0x78, 0x7a, 0x21, 0xa2, 0x91, 0x45, 0xb0, 0x9e,
|
|
0x91, 0x8e, 0x4a, 0xac, 0xec, 0xd7, 0xd0, 0x5a, 0x86, 0xe1, 0xed, 0x82,
|
|
0x7e, 0x43, 0x19, 0x35, 0xdb, 0xf2, 0xc8, 0xe4, 0x43, 0x4e, 0xf6, 0x27,
|
|
0xc9, 0x54, 0x8a, 0x4e, 0x8d, 0xd4, 0x95, 0x69, 0xa9, 0x07, 0xfa, 0x73,
|
|
0x12, 0x7c, 0xc8, 0x39, 0x3f, 0xa1, 0xe1, 0x41, 0xc4, 0x98, 0x6d, 0xcc,
|
|
0x11, 0xc5, 0xb4, 0x94, 0xc3, 0x3a, 0xec, 0x09, 0x21, 0x63, 0x20, 0xae,
|
|
0xb1, 0x46, 0xfa, 0x2a, 0xb6, 0xe7, 0xa6, 0xcb, 0x1a, 0x6b, 0x9a, 0x64,
|
|
0xdf, 0xb9, 0xf7, 0xb7, 0x9a, 0xeb, 0x29, 0xfb, 0xce, 0xf5, 0x15, 0x49,
|
|
0x8b, 0x0b, 0x39, 0x39, 0x1a, 0xc9, 0x90, 0xfe, 0x51, 0x8d, 0xc5, 0xa0,
|
|
0x3b, 0xeb, 0x55, 0x4c, 0xa3, 0xc6, 0xe2, 0xed, 0x40, 0x89, 0x1c, 0xf4,
|
|
0x46, 0x24, 0x79, 0x2e, 0x2d, 0xe7, 0xc8, 0xb9, 0x84, 0xf7, 0x9d, 0xe1,
|
|
0x6a, 0x68, 0xa8, 0xc7, 0xf9, 0xe6, 0xbc, 0x58, 0x4a, 0x9b, 0x45, 0x9a,
|
|
0x3c, 0x23, 0x99, 0x2f, 0xc6, 0x8d, 0xb9, 0x98, 0xb8, 0xad, 0x1c, 0x52,
|
|
0x3d, 0x56, 0x3e, 0x99, 0xfb, 0x5c, 0x99, 0xb0, 0xc3, 0x0a, 0xb9, 0x2e,
|
|
0xdc, 0xcf, 0xa3, 0xda, 0x96, 0x46, 0x04, 0x3c, 0xab, 0xf3, 0x25, 0xb9,
|
|
0x6e, 0x95, 0x58, 0xdf, 0x0a, 0xb9, 0x76, 0x6a, 0x9f, 0x1e, 0xaf, 0x57,
|
|
0xb1, 0xb2, 0xee, 0x5f, 0x5d, 0x25, 0xfa, 0x96, 0x0e, 0x2b, 0xac, 0xc0,
|
|
0x2e, 0xe4, 0x1d, 0xf8, 0x51, 0xac, 0x64, 0x31, 0xf4, 0xad, 0xed, 0x65,
|
|
0xcb, 0x76, 0x9c, 0x09, 0xbb, 0x7f, 0x5a, 0xaf, 0xe5, 0xc5, 0xce, 0x49,
|
|
0x39, 0x0f, 0x39, 0x7a, 0x9d, 0xd9, 0x36, 0x9e, 0xd7, 0x73, 0x15, 0x72,
|
|
0x2e, 0x96, 0x63, 0x28, 0xa2, 0xc7, 0x51, 0x6b, 0x8b, 0xe4, 0x4b, 0x84,
|
|
0x4d, 0x3e, 0x03, 0x50, 0x67, 0xbe, 0xa0, 0x97, 0xea, 0xd5, 0x33, 0x65,
|
|
0x1e, 0xaf, 0x43, 0xce, 0x27, 0x7f, 0x4b, 0x91, 0x21, 0x5c, 0x46, 0x33,
|
|
0x34, 0xb3, 0x8d, 0x85, 0x02, 0xb7, 0x50, 0xc1, 0x51, 0x97, 0x5d, 0x46,
|
|
0x35, 0x76, 0xdc, 0x24, 0x11, 0xb7, 0xb5, 0xaf, 0x30, 0x4b, 0xf0, 0x09,
|
|
0x5e, 0xfb, 0x25, 0x72, 0xdb, 0xbc, 0xed, 0xcb, 0x21, 0x2b, 0x90, 0xbd,
|
|
0xf1, 0x18, 0xb0, 0xda, 0xda, 0x71, 0xea, 0xb4, 0xbb, 0x72, 0x43, 0xed,
|
|
0x36, 0x72, 0x94, 0x71, 0xf4, 0xc2, 0x7e, 0x28, 0x51, 0xce, 0xc1, 0xa5,
|
|
0x06, 0xa8, 0xc2, 0xce, 0x76, 0xea, 0xe5, 0x5a, 0x6d, 0x73, 0x1c, 0xc5,
|
|
0x62, 0xa9, 0x43, 0xc5, 0x72, 0x4d, 0x72, 0x9c, 0x9d, 0x18, 0x87, 0x43,
|
|
0xfa, 0x9a, 0xff, 0xa8, 0x57, 0xf7, 0x0c, 0xef, 0x09, 0xac, 0x9e, 0x7b,
|
|
0x21, 0xdd, 0x88, 0xf8, 0xa5, 0xc5, 0x66, 0x27, 0x57, 0x7a, 0x4e, 0xba,
|
|
0x47, 0xc0, 0xf2, 0xbf, 0xa1, 0x56, 0x35, 0x9d, 0xb2, 0xd2, 0xd9, 0x26,
|
|
0xd2, 0x29, 0xc3, 0x3e, 0xf0, 0xb5, 0xc5, 0x94, 0x7a, 0x85, 0xfd, 0xe8,
|
|
0xbe, 0xd4, 0x3e, 0xd4, 0x95, 0x23, 0x38, 0xc6, 0xe2, 0x39, 0xfb, 0x59,
|
|
0x3d, 0xcf, 0x3d, 0xfb, 0x06, 0xae, 0x6b, 0x01, 0x6e, 0x5b, 0xba, 0x2e,
|
|
0xec, 0x64, 0x0f, 0x56, 0xd6, 0xdb, 0x61, 0xb5, 0x90, 0x52, 0xe9, 0x99,
|
|
0x07, 0xba, 0x17, 0x53, 0xc4, 0x3b, 0x44, 0xee, 0xdc, 0x1c, 0x4a, 0xd1,
|
|
0xbb, 0xf4, 0xd7, 0xf5, 0xea, 0xfb, 0xb0, 0x85, 0xf2, 0xa6, 0x1e, 0x71,
|
|
0xef, 0x47, 0xc4, 0x90, 0x65, 0x87, 0xe7, 0xb5, 0xa3, 0x3f, 0x9d, 0x1e,
|
|
0x6a, 0x41, 0x24, 0x5b, 0xb1, 0x20, 0xea, 0x7e, 0x19, 0x33, 0x9c, 0x65,
|
|
0x2f, 0xb1, 0x97, 0x51, 0x8b, 0x1d, 0xf3, 0x87, 0x9e, 0x78, 0x9b, 0xea,
|
|
0xd0, 0xc2, 0x0e, 0xf6, 0xcd, 0xc2, 0x63, 0xac, 0x45, 0xd9, 0xa3, 0x18,
|
|
0x2b, 0xcf, 0xc8, 0x3c, 0x78, 0x9e, 0x1c, 0x3b, 0xeb, 0xcc, 0xc1, 0xe7,
|
|
0x52, 0x3b, 0xaf, 0xfd, 0x32, 0x39, 0x17, 0xcb, 0xe5, 0xf3, 0x36, 0xee,
|
|
0xff, 0xff, 0x6a, 0x1b, 0xf3, 0xe0, 0xe6, 0x12, 0x19, 0x1a, 0x25, 0x67,
|
|
0x57, 0xa4, 0xf6, 0xc3, 0x32, 0x9a, 0xca, 0x31, 0xec, 0xb2, 0x6f, 0xb8,
|
|
0x17, 0x27, 0x74, 0xd0, 0xef, 0x5a, 0x3f, 0xfc, 0x54, 0x52, 0x27, 0x55,
|
|
0xc7, 0x2e, 0x29, 0x0d, 0xea, 0xfb, 0x3b, 0x6f, 0x7f, 0x2d, 0x7a, 0xf3,
|
|
0x98, 0x5c, 0x39, 0x8e, 0x5f, 0x79, 0xfd, 0x9b, 0xc5, 0x12, 0x19, 0xbf,
|
|
0x92, 0xb4, 0x99, 0x25, 0xe4, 0x75, 0xa9, 0x39, 0x31, 0x64, 0x4c, 0xbd,
|
|
0x04, 0x34, 0xc3, 0xe6, 0x6a, 0x6c, 0xe8, 0x7a, 0xfb, 0x03, 0xb6, 0x40,
|
|
0x96, 0x36, 0x1b, 0xbf, 0xfc, 0x80, 0xd7, 0x56, 0x6a, 0xb8, 0xef, 0xa5,
|
|
0x9d, 0xd8, 0x57, 0xcd, 0xc6, 0x8f, 0x3f, 0xc8, 0x11, 0xae, 0x94, 0x9c,
|
|
0x94, 0x14, 0xbd, 0x93, 0x5d, 0x0d, 0xea, 0xbe, 0xa2, 0xe6, 0xcc, 0xdb,
|
|
0xa1, 0x5a, 0xe6, 0x99, 0xe6, 0x96, 0x85, 0x2c, 0x93, 0x2f, 0x5b, 0x86,
|
|
0xcc, 0x28, 0x81, 0x3f, 0xf3, 0x96, 0xa9, 0x96, 0x65, 0xc4, 0x8a, 0xd5,
|
|
0x40, 0x74, 0x6d, 0x6b, 0x18, 0xfc, 0xed, 0x07, 0x98, 0x23, 0xcc, 0x95,
|
|
0x0b, 0x9e, 0x1c, 0x76, 0x43, 0x6c, 0x37, 0x3c, 0xee, 0xc5, 0xb2, 0x95,
|
|
0x25, 0x32, 0x9e, 0xe3, 0x31, 0x16, 0x37, 0x28, 0x5f, 0xe0, 0xad, 0xe5,
|
|
0xc8, 0xf2, 0x29, 0x3d, 0xc6, 0x17, 0xa7, 0x8d, 0x75, 0x81, 0x65, 0xac,
|
|
0xcd, 0xd8, 0x89, 0xd0, 0x75, 0x3f, 0x01, 0xef, 0xc9, 0xe9, 0x0a, 0x94,
|
|
0x0b, 0xca, 0xde, 0xf0, 0xa8, 0x3a, 0x65, 0x0f, 0x4f, 0xf1, 0xa8, 0xec,
|
|
0x39, 0x76, 0xd2, 0xed, 0xc0, 0x03, 0x34, 0x98, 0x3e, 0x87, 0xc7, 0xc5,
|
|
0xbb, 0x08, 0xfb, 0x45, 0xb4, 0x60, 0x07, 0x47, 0xdc, 0x31, 0xb6, 0x0b,
|
|
0xb4, 0x82, 0x78, 0x3e, 0x0f, 0x9e, 0xcf, 0xee, 0xad, 0xc3, 0x2e, 0x70,
|
|
0x3f, 0x4e, 0x1d, 0xbc, 0xcf, 0x3c, 0xcd, 0xc2, 0x2e, 0xfd, 0x61, 0xc4,
|
|
0x1d, 0x97, 0xf6, 0xd3, 0x6c, 0x57, 0x69, 0x8c, 0xcd, 0x9e, 0x6d, 0x57,
|
|
0x56, 0xa0, 0xc6, 0xb6, 0x54, 0xfb, 0x9a, 0x14, 0x79, 0x26, 0x19, 0xb4,
|
|
0xb2, 0x41, 0xdf, 0xb7, 0xda, 0x1b, 0xa4, 0xcf, 0xf6, 0x76, 0x36, 0xc0,
|
|
0x1f, 0xdc, 0xa5, 0xa3, 0xe6, 0xbb, 0xb8, 0xcf, 0xd8, 0x9b, 0x4f, 0xd2,
|
|
0x80, 0x2d, 0xcb, 0x51, 0xe2, 0x68, 0x46, 0xaf, 0x3e, 0x8d, 0xbc, 0x0c,
|
|
0xcc, 0x56, 0x36, 0xe6, 0xac, 0xc2, 0x06, 0xca, 0x1e, 0xc6, 0xc1, 0x3e,
|
|
0x98, 0x63, 0xeb, 0x42, 0xe9, 0x83, 0x31, 0xa2, 0xcb, 0x5a, 0x16, 0x16,
|
|
0xc1, 0xe7, 0xa5, 0xe8, 0xb1, 0x75, 0x34, 0xa8, 0xef, 0x36, 0x23, 0xce,
|
|
0xeb, 0x31, 0xaa, 0x8b, 0xe5, 0xb3, 0xeb, 0x2c, 0x1a, 0x69, 0xaf, 0x86,
|
|
0x75, 0x75, 0x42, 0xca, 0xcf, 0x9a, 0x47, 0xe0, 0x11, 0xac, 0xa9, 0xfa,
|
|
0x29, 0xa9, 0x06, 0x79, 0xd7, 0x8a, 0x38, 0x8f, 0xc9, 0x54, 0xed, 0x94,
|
|
0x54, 0xd5, 0x14, 0xcd, 0x3a, 0x1a, 0xe9, 0x59, 0x8e, 0xb6, 0x33, 0xe0,
|
|
0x73, 0x0a, 0xe5, 0x48, 0x4a, 0x1c, 0xf3, 0x20, 0xab, 0x81, 0x0c, 0x37,
|
|
0x11, 0x07, 0xdf, 0x38, 0x0e, 0xc9, 0xd8, 0xa5, 0x9f, 0xd8, 0x63, 0xdf,
|
|
0x21, 0x75, 0xf8, 0xf6, 0xa5, 0xe8, 0x73, 0x3c, 0x42, 0xcc, 0xb5, 0x47,
|
|
0xae, 0xa8, 0x43, 0xae, 0x60, 0x09, 0x66, 0xfb, 0x52, 0x8c, 0xb0, 0xe2,
|
|
0x5d, 0x8f, 0xd1, 0x86, 0xd8, 0xe7, 0x72, 0xac, 0xc9, 0x7d, 0xf2, 0xa6,
|
|
0x71, 0x44, 0x9f, 0x94, 0xf7, 0x63, 0xad, 0x43, 0xce, 0x07, 0xd8, 0x76,
|
|
0x65, 0x54, 0xe2, 0xa4, 0x8a, 0x9f, 0x46, 0x9c, 0x57, 0x61, 0xb4, 0x7c,
|
|
0x7e, 0x6e, 0x4f, 0xc4, 0x27, 0xab, 0x7c, 0x2a, 0xd6, 0x32, 0xe8, 0x22,
|
|
0xcc, 0xc9, 0x83, 0x72, 0xee, 0xab, 0x66, 0xd9, 0x3b, 0x73, 0x2d, 0xf6,
|
|
0x54, 0x0c, 0x5f, 0x50, 0x83, 0x1e, 0x34, 0xf0, 0xed, 0xef, 0xbf, 0x94,
|
|
0xfe, 0x23, 0x96, 0x1d, 0xc0, 0x1e, 0xf1, 0x8b, 0xa8, 0xd5, 0xdb, 0xa1,
|
|
0x2c, 0x54, 0xed, 0x85, 0x17, 0xa7, 0xed, 0x89, 0x3c, 0xcb, 0x9e, 0x80,
|
|
0x85, 0x76, 0x29, 0x0b, 0x2d, 0x93, 0x69, 0xb6, 0xd0, 0xbd, 0x1c, 0xdb,
|
|
0xbd, 0xcd, 0xe7, 0x76, 0x3d, 0xac, 0xc7, 0xdb, 0x5e, 0x77, 0x3a, 0x6b,
|
|
0x47, 0x5d, 0x45, 0x66, 0xef, 0x50, 0x76, 0x39, 0xea, 0xaa, 0xb3, 0xd4,
|
|
0xb5, 0x16, 0xe5, 0x1e, 0x94, 0x7b, 0xb8, 0xc4, 0xe0, 0x73, 0xfd, 0x43,
|
|
0x5c, 0xef, 0x2f, 0x47, 0x3a, 0xea, 0x50, 0xe7, 0x6c, 0x5e, 0x62, 0xe1,
|
|
0x34, 0x2f, 0x81, 0x28, 0xc1, 0xc8, 0x92, 0x94, 0x23, 0x04, 0x43, 0x73,
|
|
0x6c, 0x49, 0xea, 0xdc, 0x9d, 0xcf, 0x16, 0xfc, 0xa6, 0xaa, 0x8b, 0x67,
|
|
0x81, 0xa3, 0x47, 0xbb, 0xe5, 0xac, 0x63, 0x7b, 0x5e, 0x0e, 0x9d, 0x07,
|
|
0x12, 0x67, 0xeb, 0xfd, 0x42, 0x9d, 0x55, 0x88, 0x51, 0x1a, 0xd4, 0xb3,
|
|
0x18, 0x65, 0xef, 0x5e, 0x98, 0xa5, 0x77, 0x20, 0x55, 0x46, 0x00, 0xf0,
|
|
0x46, 0x06, 0x5b, 0x3c, 0xf7, 0x7b, 0x60, 0x30, 0x4d, 0xee, 0x02, 0xe9,
|
|
0x2f, 0xe0, 0x51, 0x4b, 0x6d, 0x3c, 0x1f, 0x35, 0xe4, 0x1d, 0x4a, 0xc5,
|
|
0x1e, 0x09, 0xa1, 0xe5, 0xfe, 0x3c, 0xb4, 0x67, 0xf1, 0x19, 0xf9, 0x68,
|
|
0x65, 0xbe, 0x8c, 0x3b, 0x6c, 0xea, 0x59, 0x93, 0x5c, 0x63, 0x8f, 0x58,
|
|
0x59, 0xca, 0x77, 0x3b, 0x43, 0xc6, 0x15, 0x37, 0xea, 0x3d, 0xc0, 0x9e,
|
|
0x28, 0xcd, 0x12, 0x77, 0x44, 0xdc, 0x07, 0xb0, 0x67, 0x32, 0x70, 0x17,
|
|
0xb6, 0x61, 0x14, 0xfa, 0xb4, 0x54, 0x3a, 0x86, 0xee, 0x99, 0xcd, 0x1b,
|
|
0xaf, 0x37, 0x7b, 0xe4, 0xe0, 0x7d, 0xef, 0xc5, 0xd9, 0x24, 0x77, 0xb9,
|
|
0xe0, 0xbb, 0xb4, 0xf4, 0xa5, 0x76, 0x57, 0x93, 0x2b, 0xa5, 0xc1, 0x5b,
|
|
0x4e, 0xa6, 0x37, 0x75, 0xe5, 0x35, 0xc3, 0x03, 0x98, 0xfe, 0xd4, 0x3b,
|
|
0xa9, 0x3c, 0xc6, 0x62, 0xec, 0xde, 0x66, 0x07, 0xee, 0xf4, 0x32, 0xfd,
|
|
0x04, 0xd2, 0x59, 0x8e, 0xd4, 0xe1, 0x92, 0x14, 0xbb, 0x96, 0x3c, 0x0d,
|
|
0x49, 0x85, 0xe1, 0xb2, 0x7b, 0xe0, 0x37, 0x23, 0xfd, 0xbb, 0x29, 0x3a,
|
|
0xee, 0x49, 0xab, 0xa0, 0x48, 0xfb, 0x1e, 0x3a, 0xb0, 0xd4, 0x1b, 0x49,
|
|
0xf4, 0xc2, 0x90, 0x27, 0x76, 0x9a, 0x87, 0xd6, 0x63, 0xdf, 0xf1, 0x37,
|
|
0x4a, 0xcd, 0x76, 0x07, 0xe5, 0x1a, 0xaf, 0xa5, 0x56, 0xd8, 0x72, 0x8d,
|
|
0x31, 0xba, 0xdc, 0x13, 0x6a, 0x3f, 0x49, 0x87, 0xea, 0xb9, 0x7f, 0x73,
|
|
0xd2, 0x70, 0x26, 0xa7, 0xa9, 0xef, 0x40, 0xc6, 0xe5, 0xcc, 0x1c, 0xc0,
|
|
0x68, 0x0d, 0x79, 0xca, 0x1c, 0xc5, 0x9c, 0xb4, 0x8b, 0x84, 0x25, 0xdf,
|
|
0x34, 0xf3, 0xd4, 0xf4, 0x5e, 0xa1, 0xda, 0x94, 0x9e, 0x08, 0xfd, 0xaf,
|
|
0x97, 0xf6, 0xf7, 0x01, 0x79, 0xaf, 0xc0, 0xfa, 0xbb, 0x1f, 0xa5, 0x9b,
|
|
0x91, 0x6e, 0x41, 0x8c, 0x61, 0xd1, 0x13, 0x3c, 0x0a, 0x8c, 0xf0, 0x72,
|
|
0xd6, 0x78, 0x98, 0x6e, 0xc2, 0x3a, 0xb8, 0xd1, 0xcb, 0x8d, 0xbc, 0x43,
|
|
0x6e, 0xb6, 0x9e, 0x41, 0x8b, 0x25, 0x2d, 0xb2, 0xcd, 0x41, 0x89, 0xd5,
|
|
0xf2, 0x09, 0xc2, 0x88, 0x6b, 0xce, 0x69, 0x74, 0xd2, 0x2d, 0x3a, 0x59,
|
|
0xa7, 0xd1, 0xa9, 0xb0, 0xe8, 0xcc, 0x3d, 0x8d, 0x4e, 0x93, 0x45, 0x27,
|
|
0x7b, 0x9a, 0x8e, 0xb2, 0xc2, 0xad, 0xae, 0x4c, 0x1a, 0xb8, 0x79, 0x31,
|
|
0x6d, 0x75, 0xe5, 0x4c, 0xcb, 0x5f, 0xa2, 0xf3, 0x5d, 0x88, 0x8e, 0x94,
|
|
0xf7, 0xc3, 0x3d, 0xcb, 0x95, 0x31, 0x25, 0x85, 0xdb, 0xa4, 0x21, 0xca,
|
|
0xbc, 0xb7, 0xaa, 0x72, 0x88, 0x0e, 0xec, 0x21, 0xa7, 0x87, 0x4f, 0x12,
|
|
0x7b, 0x53, 0x17, 0xfa, 0x9d, 0x97, 0x01, 0xef, 0x47, 0x72, 0x5e, 0xbc,
|
|
0xf4, 0x23, 0x9b, 0x4d, 0x54, 0xfc, 0x6e, 0xba, 0x76, 0x99, 0x45, 0xdb,
|
|
0x39, 0x43, 0xdb, 0x7b, 0x8b, 0xf5, 0x2c, 0x2d, 0x97, 0xb4, 0xb4, 0xb1,
|
|
0xa9, 0xb3, 0x98, 0xd6, 0x39, 0x6c, 0xe8, 0x63, 0x09, 0x7b, 0xcf, 0x14,
|
|
0x57, 0x41, 0xc3, 0x3e, 0x07, 0x79, 0xf3, 0x33, 0xa8, 0xc2, 0xe6, 0xcd,
|
|
0x77, 0xa2, 0x44, 0x31, 0xcb, 0x53, 0x43, 0xce, 0x45, 0x3a, 0x72, 0xfb,
|
|
0x3c, 0xed, 0x73, 0xfc, 0xc8, 0x30, 0x44, 0xc5, 0x2f, 0x2f, 0xc2, 0x21,
|
|
0x9b, 0x6b, 0x2b, 0xa5, 0x95, 0x65, 0x98, 0xfb, 0x3b, 0xad, 0x31, 0x98,
|
|
0x9b, 0x63, 0xb0, 0x74, 0x58, 0xe3, 0x5f, 0xb6, 0xce, 0x4f, 0xc7, 0xea,
|
|
0xde, 0xc2, 0x3b, 0x7f, 0x89, 0xd5, 0x4a, 0xd8, 0xd6, 0x55, 0xbc, 0x9d,
|
|
0x9f, 0x88, 0xb7, 0xf9, 0xb4, 0x2c, 0x94, 0x32, 0x8c, 0x20, 0x07, 0xde,
|
|
0x44, 0xc6, 0xe2, 0xa9, 0xc8, 0x63, 0x2f, 0x22, 0x74, 0x1b, 0xf3, 0x51,
|
|
0xb7, 0x8c, 0x90, 0xd3, 0xb1, 0x8b, 0xd3, 0x79, 0x0f, 0x37, 0x08, 0x21,
|
|
0x9f, 0xb5, 0x3b, 0x30, 0x7e, 0xfe, 0x8e, 0x47, 0xc5, 0xda, 0xcf, 0x19,
|
|
0x97, 0x35, 0x7e, 0xc5, 0x78, 0xce, 0x50, 0xfb, 0x98, 0xbd, 0xc8, 0xeb,
|
|
0x0d, 0xea, 0x9d, 0x1d, 0x6f, 0x44, 0xfa, 0x90, 0x8b, 0x1b, 0xe4, 0x4e,
|
|
0x4e, 0xb3, 0xf8, 0x0f, 0xb6, 0x3f, 0x8e, 0xbf, 0xcd, 0xbd, 0x2c, 0x73,
|
|
0x65, 0x74, 0x97, 0x2a, 0x9f, 0x79, 0xa9, 0x7d, 0xeb, 0x35, 0x66, 0xae,
|
|
0xbd, 0x37, 0xa6, 0xbc, 0xae, 0x7a, 0x0e, 0xf8, 0xa2, 0xf5, 0x79, 0x20,
|
|
0xf6, 0xbb, 0xc7, 0x7c, 0x1e, 0x88, 0x48, 0x70, 0x39, 0x0d, 0xf4, 0x34,
|
|
0x99, 0x2d, 0xa7, 0x34, 0xf8, 0xff, 0xf8, 0x81, 0x2c, 0x8b, 0x1d, 0x8d,
|
|
0x08, 0xc0, 0x56, 0x62, 0x53, 0x5e, 0xd8, 0x26, 0x79, 0xe5, 0x85, 0x11,
|
|
0x49, 0x7e, 0xcf, 0x9b, 0x86, 0xf9, 0x48, 0xc3, 0xbc, 0xa4, 0x29, 0xef,
|
|
0x9a, 0xa6, 0xb9, 0x14, 0xe6, 0x0e, 0xf0, 0x7e, 0xf9, 0x1c, 0xb8, 0x62,
|
|
0xcc, 0xeb, 0xcd, 0xe8, 0x8f, 0xb9, 0x77, 0xf1, 0x49, 0x4b, 0xd3, 0x6c,
|
|
0xf2, 0x76, 0x2c, 0xe4, 0xbd, 0x7c, 0x1b, 0x6a, 0x8b, 0x49, 0x3f, 0x67,
|
|
0x93, 0xef, 0x30, 0x29, 0x7f, 0x37, 0x29, 0xdf, 0x6d, 0xb2, 0xeb, 0x67,
|
|
0x81, 0x7f, 0x6e, 0x50, 0x77, 0x7c, 0xaf, 0xbb, 0x46, 0x7a, 0xcd, 0x1a,
|
|
0xaa, 0x75, 0xa9, 0xe7, 0x2d, 0x42, 0xe6, 0xff, 0x05, 0xf9, 0xfb, 0x89,
|
|
0xef, 0x31, 0xa5, 0x74, 0x11, 0xe6, 0xdb, 0xe5, 0x5a, 0xe9, 0xdc, 0x4c,
|
|
0x2e, 0x37, 0xcf, 0x22, 0x9f, 0x10, 0x7a, 0x36, 0x45, 0xb9, 0xdb, 0x8b,
|
|
0xd3, 0x94, 0xd3, 0xc7, 0xf5, 0xc9, 0xf1, 0xc2, 0xb4, 0x13, 0xa4, 0x60,
|
|
0x4a, 0xec, 0xc5, 0x31, 0x8a, 0xf4, 0x1c, 0x03, 0x88, 0x0b, 0xe0, 0xb9,
|
|
0x23, 0xb5, 0xe7, 0xd2, 0xa0, 0x60, 0x1b, 0xae, 0xa7, 0x8a, 0x5f, 0x7b,
|
|
0xfb, 0x67, 0x3b, 0x6d, 0x9b, 0xa6, 0x9e, 0xb6, 0x03, 0x35, 0x34, 0x90,
|
|
0x9b, 0x42, 0x5e, 0x6f, 0x35, 0x85, 0x70, 0x0e, 0xd6, 0xe7, 0xc1, 0xee,
|
|
0x51, 0xce, 0x23, 0x54, 0x6d, 0xe7, 0x19, 0x5c, 0x9b, 0x93, 0x66, 0xaf,
|
|
0x2b, 0x7b, 0x46, 0x5d, 0x7c, 0xbf, 0xa9, 0xc7, 0x67, 0xc5, 0x1f, 0xad,
|
|
0xcf, 0x1a, 0x33, 0x1b, 0x49, 0x3e, 0x79, 0xf2, 0x3a, 0xeb, 0xa4, 0xdf,
|
|
0x75, 0xeb, 0x67, 0x88, 0xec, 0x19, 0x73, 0x1a, 0xd5, 0xfb, 0x66, 0x3a,
|
|
0x0a, 0xc4, 0xcd, 0x07, 0xab, 0x37, 0xa0, 0xbd, 0xa4, 0x25, 0xf6, 0xe3,
|
|
0xb1, 0xab, 0xf4, 0x13, 0x48, 0x73, 0x8b, 0xf6, 0x99, 0xf6, 0x5e, 0xa7,
|
|
0xfc, 0x7a, 0x07, 0xe9, 0xf8, 0x10, 0xa5, 0x38, 0x56, 0x09, 0xc9, 0xb8,
|
|
0x80, 0xcf, 0x7d, 0xd6, 0xa9, 0xa7, 0xa9, 0xd6, 0xab, 0x63, 0x4e, 0x58,
|
|
0x5d, 0x3e, 0x79, 0xbb, 0x13, 0xfe, 0x01, 0xf3, 0x78, 0x19, 0xe5, 0xca,
|
|
0xfd, 0x63, 0xa7, 0x8a, 0xb4, 0x22, 0xfb, 0x42, 0x2a, 0x12, 0x85, 0x32,
|
|
0x9a, 0xca, 0x53, 0xfb, 0x49, 0x46, 0x9b, 0x7c, 0x6f, 0xe8, 0x44, 0xbd,
|
|
0x7d, 0xa4, 0x9e, 0x6b, 0xf0, 0xbb, 0x76, 0x99, 0xd2, 0x46, 0xb6, 0x5e,
|
|
0xd6, 0xd8, 0xbf, 0x35, 0xf1, 0x8c, 0x62, 0xa9, 0x9e, 0x03, 0xf5, 0xec,
|
|
0x65, 0x9b, 0x68, 0x44, 0x0d, 0xfc, 0x04, 0x86, 0x9f, 0x5b, 0x54, 0x37,
|
|
0xaa, 0x3b, 0xee, 0xe0, 0xe5, 0x8d, 0x94, 0x6f, 0xe3, 0x77, 0x05, 0x85,
|
|
0x8d, 0x6f, 0x80, 0x6b, 0x32, 0x33, 0x28, 0xf5, 0x50, 0xea, 0x2d, 0xe2,
|
|
0x7e, 0xf1, 0xa4, 0xfd, 0xeb, 0xfb, 0xd2, 0x0e, 0x93, 0xf9, 0xfc, 0x5b,
|
|
0x3d, 0xa5, 0x68, 0xd6, 0xef, 0x8a, 0x85, 0x9c, 0x9f, 0x91, 0x4f, 0xae,
|
|
0x98, 0x62, 0x1f, 0xe0, 0x3e, 0x5a, 0xef, 0xe4, 0xfb, 0xa8, 0xdc, 0x3b,
|
|
0xed, 0x4d, 0x34, 0x50, 0xd7, 0x44, 0x0d, 0x4e, 0x27, 0xf8, 0x95, 0xe0,
|
|
0x57, 0x82, 0xcf, 0xa0, 0x81, 0xee, 0x55, 0x34, 0x50, 0xbb, 0x8a, 0x1a,
|
|
0x70, 0xb6, 0xba, 0xf2, 0xb2, 0xed, 0xfc, 0x1d, 0x81, 0x5d, 0x5a, 0x39,
|
|
0xd1, 0x39, 0x8d, 0xea, 0x3d, 0xb3, 0x81, 0xda, 0x26, 0x32, 0x6d, 0x94,
|
|
0xcb, 0x99, 0xa7, 0xce, 0x40, 0xfd, 0xaa, 0xc4, 0x1c, 0x96, 0xd7, 0x0e,
|
|
0x91, 0xd3, 0x60, 0x9b, 0x3d, 0x91, 0x78, 0xb6, 0x60, 0xde, 0xbb, 0xbb,
|
|
0x1a, 0xa7, 0x3e, 0xab, 0x35, 0xe5, 0xe7, 0x36, 0x4e, 0x7f, 0x86, 0x3b,
|
|
0xf3, 0x19, 0xff, 0x6c, 0x32, 0xf3, 0x79, 0x63, 0x5f, 0xa3, 0x7a, 0x0f,
|
|
0xe7, 0x22, 0xde, 0x53, 0x38, 0xf1, 0x9a, 0x60, 0x03, 0x17, 0x4b, 0x2d,
|
|
0xbe, 0xb1, 0x15, 0xd1, 0xe7, 0x89, 0xe3, 0x61, 0x15, 0x97, 0xa5, 0x60,
|
|
0x56, 0x32, 0xf5, 0xb7, 0x1a, 0x36, 0x1d, 0x8f, 0x7a, 0xf5, 0x3b, 0x73,
|
|
0xfc, 0xdc, 0x9e, 0xdf, 0xf7, 0x70, 0x09, 0x8e, 0x48, 0x72, 0x44, 0xf2,
|
|
0x59, 0x3d, 0xa7, 0x16, 0xca, 0x77, 0x43, 0x76, 0x8a, 0xd5, 0xd4, 0x22,
|
|
0xd2, 0xb1, 0x23, 0xf8, 0xd6, 0xba, 0x06, 0xf6, 0x12, 0x80, 0xa4, 0x98,
|
|
0x76, 0x1a, 0xab, 0x71, 0xa7, 0x9a, 0x83, 0xb8, 0x63, 0xa9, 0xe1, 0x92,
|
|
0xa5, 0x84, 0xfe, 0xee, 0x8c, 0xff, 0x2b, 0x25, 0xb3, 0x2d, 0x5f, 0xa3,
|
|
0xfa, 0x4e, 0xcf, 0x8c, 0xd2, 0x0d, 0xed, 0x17, 0x5d, 0xe8, 0x8f, 0xd9,
|
|
0x42, 0xab, 0x6e, 0xa1, 0x12, 0x2d, 0x44, 0x64, 0x0b, 0xad, 0xb2, 0x85,
|
|
0xd6, 0x44, 0x0b, 0xea, 0x19, 0x05, 0xb7, 0xb1, 0x28, 0xd1, 0xc6, 0x42,
|
|
0x4a, 0xd5, 0xeb, 0x35, 0xd1, 0xa8, 0xbe, 0x0f, 0x4b, 0xde, 0x14, 0x5c,
|
|
0xa5, 0xe6, 0x5a, 0xb9, 0x5c, 0x89, 0x95, 0x82, 0x77, 0x29, 0x95, 0x27,
|
|
0xc9, 0x71, 0x1d, 0x2f, 0xbf, 0x30, 0x2d, 0x6e, 0xae, 0x9c, 0x12, 0x37,
|
|
0xb3, 0x77, 0x91, 0xb1, 0xa2, 0xfb, 0x76, 0xdc, 0x11, 0x67, 0xf3, 0xd2,
|
|
0xa7, 0x3e, 0x48, 0x7a, 0xe9, 0x1c, 0xf2, 0x38, 0x4a, 0xe4, 0x73, 0xff,
|
|
0x1e, 0xe9, 0x33, 0xfa, 0xa9, 0xe2, 0xf7, 0x39, 0xda, 0x47, 0xf2, 0x9a,
|
|
0x5d, 0xa2, 0xdf, 0xa9, 0x32, 0x7d, 0xe0, 0x4a, 0xac, 0x59, 0xc8, 0x5d,
|
|
0x2d, 0xe3, 0x83, 0x1c, 0xc3, 0xe5, 0x6c, 0xb0, 0x39, 0xe1, 0xc3, 0xba,
|
|
0xd0, 0x6a, 0x95, 0xec, 0x2d, 0xf7, 0x92, 0x69, 0xc4, 0xfd, 0x0c, 0x3f,
|
|
0x7d, 0xb3, 0x79, 0x0c, 0xf5, 0xcd, 0x92, 0x41, 0x39, 0x49, 0xc3, 0xa0,
|
|
0x99, 0xcf, 0x38, 0xaf, 0x3a, 0x8d, 0xcd, 0x1d, 0xd6, 0xf2, 0x8b, 0x9d,
|
|
0x9f, 0x3a, 0x8d, 0xfc, 0x4e, 0x29, 0xbf, 0xc2, 0x10, 0x34, 0xf7, 0xeb,
|
|
0x77, 0xd2, 0x0f, 0x05, 0x7f, 0x77, 0xf5, 0x3d, 0xfe, 0x7c, 0x80, 0x5e,
|
|
0x65, 0xfe, 0xeb, 0xcc, 0x1b, 0xef, 0x51, 0xab, 0x71, 0xfd, 0x65, 0xfb,
|
|
0xe9, 0x71, 0xd6, 0x13, 0x39, 0xd9, 0x74, 0xb7, 0x81, 0xfe, 0x8c, 0xef,
|
|
0x7d, 0xf9, 0x40, 0xf0, 0x36, 0x7a, 0x83, 0x55, 0xf6, 0xf4, 0x05, 0x83,
|
|
0xd7, 0x6d, 0xbb, 0x3d, 0xb0, 0x6d, 0xdb, 0x9a, 0x93, 0xbd, 0xfd, 0xf4,
|
|
0x49, 0x56, 0xa4, 0x07, 0xed, 0xd0, 0x9a, 0xdb, 0x56, 0x45, 0x9f, 0xb7,
|
|
0x23, 0xd9, 0x46, 0x4f, 0x29, 0xf2, 0xb0, 0xd0, 0xe2, 0x4f, 0x72, 0x5c,
|
|
0xff, 0x57, 0x3e, 0x88, 0x3e, 0x8d, 0xd8, 0x78, 0xdb, 0xc6, 0xdb, 0xe8,
|
|
0x75, 0x43, 0x7d, 0x05, 0x3a, 0xb7, 0xed, 0x40, 0x55, 0x55, 0x55, 0x1b,
|
|
0x5d, 0x09, 0xf9, 0xc8, 0xe6, 0xe0, 0x9d, 0x81, 0x35, 0x55, 0xf4, 0x0b,
|
|
0x83, 0x0b, 0xbf, 0x8d, 0xcf, 0x93, 0x7b, 0xe8, 0x4f, 0x8a, 0x5c, 0x6e,
|
|
0x57, 0xe5, 0x9e, 0xb2, 0xd9, 0x55, 0x31, 0x94, 0xa2, 0xb7, 0x2d, 0x85,
|
|
0x4e, 0xd8, 0xb8, 0xd0, 0x0f, 0x75, 0xf6, 0x27, 0x36, 0xd3, 0x41, 0x9e,
|
|
0x38, 0xf9, 0x0d, 0x9d, 0x3a, 0xf0, 0x78, 0x9d, 0xe6, 0xd2, 0x2d, 0xe9,
|
|
0x4a, 0x61, 0x1b, 0x8a, 0xbf, 0x2c, 0x7b, 0x8d, 0xc6, 0x5f, 0x93, 0xcf,
|
|
0x56, 0x36, 0xb4, 0xd1, 0x3d, 0x0e, 0xa8, 0xfc, 0x0e, 0x4d, 0xf5, 0x55,
|
|
0xf5, 0xed, 0x39, 0x70, 0x70, 0x53, 0xf0, 0x23, 0x9b, 0xda, 0xe8, 0x3e,
|
|
0x16, 0x5e, 0x8b, 0x8f, 0x35, 0x7b, 0xae, 0x3e, 0xb8, 0x67, 0x6f, 0xc9,
|
|
0x7a, 0xfa, 0xa5, 0xd4, 0x47, 0x1d, 0xf4, 0x10, 0x8b, 0xaf, 0x6e, 0xa3,
|
|
0x17, 0x78, 0xc0, 0xf4, 0x86, 0xfc, 0xfc, 0x76, 0x0a, 0x3e, 0x37, 0x5e,
|
|
0xb3, 0x26, 0x50, 0xfd, 0x00, 0xec, 0xf3, 0xd8, 0x1a, 0x3a, 0x92, 0x22,
|
|
0xd5, 0x1f, 0xa2, 0xc7, 0x52, 0x0c, 0x49, 0xaf, 0x95, 0x74, 0xcd, 0xc1,
|
|
0x0b, 0xaf, 0xa9, 0x7e, 0xd3, 0x38, 0xb0, 0xa2, 0x9b, 0xee, 0x51, 0x1a,
|
|
0xf4, 0x88, 0xcc, 0xa0, 0x4f, 0xa7, 0xf2, 0xec, 0xbb, 0x9c, 0x81, 0x83,
|
|
0x01, 0xfa, 0x78, 0x3a, 0xf8, 0x35, 0xc2, 0x95, 0x7d, 0xb2, 0x8a, 0x1e,
|
|
0x91, 0xf2, 0x7c, 0xe7, 0xd5, 0xf4, 0x65, 0x6e, 0x63, 0xcd, 0x9e, 0x03,
|
|
0xd5, 0xb7, 0x55, 0x07, 0xaa, 0xe9, 0x03, 0x96, 0x6f, 0x16, 0x85, 0xae,
|
|
0x6a, 0x7a, 0xca, 0xc1, 0xcb, 0xf8, 0x96, 0xad, 0x75, 0xcf, 0x01, 0xfe,
|
|
0x33, 0x6e, 0xbe, 0x74, 0x7f, 0xd5, 0xd1, 0xbe, 0xa0, 0xc8, 0x73, 0x6d,
|
|
0x16, 0xf9, 0x2e, 0xfa, 0x95, 0x83, 0x2b, 0xce, 0xde, 0x33, 0xb2, 0x66,
|
|
0x44, 0xe4, 0x67, 0xff, 0x7b, 0x55, 0x09, 0x3e, 0xd1, 0x50, 0x35, 0xd0,
|
|
0xb7, 0xed, 0xc2, 0xbd, 0x23, 0x46, 0x70, 0x2b, 0x3d, 0x97, 0x2a, 0x5b,
|
|
0x9c, 0x97, 0x5d, 0x7a, 0x84, 0x3e, 0x99, 0x06, 0xfe, 0xbd, 0x35, 0x1b,
|
|
0x37, 0x9d, 0xac, 0xbb, 0xe6, 0xc3, 0x7d, 0x87, 0x6f, 0xbb, 0x74, 0xdb,
|
|
0xb6, 0xdb, 0x3e, 0xa0, 0x67, 0x58, 0xba, 0xe6, 0xe8, 0x4b, 0x87, 0x6f,
|
|
0x1b, 0xd9, 0x86, 0xda, 0x12, 0xff, 0x9f, 0xdc, 0xf8, 0xd2, 0x81, 0xdb,
|
|
0x4f, 0x3e, 0x79, 0xf2, 0xe8, 0xbb, 0x27, 0x7f, 0x19, 0x38, 0x59, 0xdd,
|
|
0x1f, 0x08, 0x3e, 0xb8, 0xb9, 0xad, 0x73, 0x07, 0x7d, 0x57, 0xd6, 0xf8,
|
|
0xfd, 0xd2, 0x35, 0x0f, 0x56, 0x3f, 0x8c, 0xee, 0x1f, 0x16, 0xae, 0x42,
|
|
0xb4, 0xb6, 0xad, 0xe4, 0x7c, 0x63, 0x5b, 0x2b, 0xbd, 0xc5, 0x43, 0xa1,
|
|
0xef, 0x3b, 0x68, 0xc5, 0x1e, 0x51, 0x9e, 0xfd, 0x24, 0xaa, 0x39, 0x76,
|
|
0x14, 0x1f, 0xf4, 0x25, 0x2e, 0x44, 0x7f, 0xe0, 0xdc, 0x4b, 0xd6, 0x1c,
|
|
0x7e, 0xb0, 0x6d, 0xed, 0x85, 0x7b, 0x37, 0xb6, 0xdd, 0x11, 0xac, 0xfa,
|
|
0xf0, 0xe6, 0x4b, 0xe8, 0x45, 0x43, 0xda, 0xd7, 0x2b, 0x86, 0x5a, 0xcc,
|
|
0x77, 0x68, 0x1c, 0xe9, 0xaf, 0x55, 0x23, 0x70, 0x99, 0x4b, 0x3b, 0x80,
|
|
0x7b, 0xd8, 0xde, 0xae, 0x61, 0x73, 0xdd, 0xbb, 0x9d, 0xbe, 0xcc, 0x33,
|
|
0x78, 0xed, 0x25, 0x6b, 0x36, 0x1e, 0xc0, 0xd2, 0x3d, 0xc2, 0x89, 0x07,
|
|
0x15, 0xff, 0xa4, 0xb4, 0x67, 0xb1, 0xd8, 0x29, 0x10, 0x1f, 0x7f, 0x8a,
|
|
0x6d, 0x75, 0xc5, 0xc6, 0x6b, 0x4f, 0xe1, 0x8e, 0x3f, 0x97, 0xb6, 0xab,
|
|
0xaf, 0x76, 0xe5, 0x1e, 0xda, 0xa4, 0x69, 0x9e, 0xa6, 0xa6, 0xbc, 0x95,
|
|
0xd4, 0x77, 0x3b, 0xad, 0xd4, 0x2a, 0xd3, 0x43, 0x53, 0xf6, 0xab, 0x7a,
|
|
0x26, 0xaf, 0x96, 0x75, 0x8b, 0x7c, 0x56, 0xc7, 0xb2, 0x05, 0x94, 0x7c,
|
|
0x5f, 0xdb, 0x66, 0xa9, 0xcb, 0xa6, 0xb9, 0x25, 0x89, 0xfc, 0x25, 0x9a,
|
|
0x6e, 0xd2, 0x74, 0x35, 0x99, 0xdf, 0xbb, 0x1b, 0x92, 0xaa, 0xf4, 0x3c,
|
|
0x9d, 0x9e, 0x97, 0x28, 0x97, 0xa9, 0xeb, 0xda, 0x20, 0xcf, 0x5c, 0xa1,
|
|
0xa1, 0xda, 0xef, 0x4f, 0xb4, 0x67, 0xb6, 0xa9, 0x90, 0x27, 0x9f, 0x6a,
|
|
0xdb, 0x28, 0xf9, 0x3e, 0xb8, 0xf9, 0x8e, 0x07, 0xd3, 0x6c, 0x5d, 0xb7,
|
|
0x39, 0x9e, 0x1c, 0xad, 0x6f, 0xbe, 0xe7, 0x61, 0x48, 0x5a, 0x44, 0xe6,
|
|
0x7b, 0x05, 0x4c, 0xcd, 0xf7, 0x3c, 0xdc, 0x9a, 0x9a, 0xe3, 0xaa, 0xd4,
|
|
0x75, 0x98, 0xef, 0x29, 0xd8, 0x25, 0x9f, 0x2d, 0xa9, 0xe2, 0xd5, 0xdb,
|
|
0xf0, 0x0e, 0xc9, 0xe7, 0x27, 0xfa, 0xa2, 0xca, 0x56, 0xea, 0xb2, 0x95,
|
|
0xb2, 0x3e, 0x96, 0x2f, 0xd7, 0x79, 0xe6, 0x7b, 0x11, 0x1d, 0x9a, 0xf6,
|
|
0xe8, 0x76, 0x7a, 0x70, 0xb2, 0xa4, 0x69, 0x9d, 0x5e, 0xed, 0x13, 0xb7,
|
|
0xe8, 0xb6, 0x2c, 0xb3, 0x51, 0x41, 0x46, 0x45, 0x25, 0x00, 0x95, 0x4a,
|
|
0x2a, 0xaf, 0x6d, 0xe8, 0x6c, 0xae, 0xed, 0x69, 0xea, 0xa8, 0xea, 0xe9,
|
|
0xee, 0x69, 0xae, 0x6a, 0xec, 0x6c, 0x68, 0xa8, 0xea, 0x58, 0xd5, 0x54,
|
|
0x57, 0xb5, 0xb2, 0xab, 0xa7, 0xa1, 0xb1, 0xa7, 0xab, 0xb1, 0x6b, 0x55,
|
|
0x6d, 0x2d, 0xa5, 0xb5, 0x8d, 0x06, 0x03, 0xa1, 0x40, 0x7c, 0x2d, 0xa5,
|
|
0xb4, 0x29, 0x6a, 0xac, 0x6d, 0x25, 0xdb, 0xda, 0xd6, 0x4a, 0xb2, 0xe3,
|
|
0x63, 0x98, 0x3f, 0x21, 0xc8, 0xeb, 0x0c, 0x4e, 0xfa, 0xe3, 0xe1, 0x70,
|
|
0x7c, 0x7c, 0x53, 0x60, 0x2c, 0xb0, 0xc9, 0x17, 0xf2, 0xed, 0xf6, 0x47,
|
|
0x69, 0xed, 0x6c, 0x52, 0xb7, 0x3f, 0x1a, 0x0d, 0x47, 0x57, 0xbb, 0x47,
|
|
0xc3, 0x93, 0xc1, 0x31, 0x77, 0x28, 0x1c, 0x77, 0xef, 0xf6, 0xc7, 0xdd,
|
|
0x09, 0x4d, 0x77, 0x5f, 0x8f, 0x3b, 0x36, 0xea, 0x0b, 0x85, 0x50, 0xbe,
|
|
0xeb, 0x9f, 0x2f, 0x3f, 0xe6, 0xdf, 0xe5, 0x9b, 0x0c, 0x5a, 0xeb, 0xf1,
|
|
0x8d, 0xf9, 0x22, 0x71, 0x54, 0xe2, 0xea, 0x6e, 0x2e, 0x2f, 0x1f, 0x3c,
|
|
0x18, 0x8a, 0x8f, 0xfb, 0xe3, 0x81, 0xd1, 0xae, 0xa0, 0x2f, 0x16, 0x83,
|
|
0xcc, 0xbf, 0x2f, 0x30, 0xea, 0xef, 0xf2, 0x05, 0x83, 0x3b, 0x7d, 0xa3,
|
|
0x7b, 0x7b, 0x27, 0x22, 0x41, 0x72, 0x76, 0x07, 0x62, 0xa3, 0xe1, 0x7d,
|
|
0xfe, 0xa8, 0x7f, 0x4c, 0xe5, 0x92, 0xe8, 0x25, 0xa3, 0x17, 0xf3, 0xd5,
|
|
0xcb, 0xb4, 0x8f, 0x6c, 0xbd, 0x7d, 0x7d, 0x54, 0xd2, 0xbb, 0xc5, 0xdd,
|
|
0x73, 0x60, 0xd4, 0x1f, 0x89, 0x07, 0xc2, 0x21, 0xf7, 0xfe, 0xf1, 0x40,
|
|
0xd0, 0xef, 0x1e, 0x0d, 0x86, 0x63, 0x81, 0xd0, 0x6e, 0x77, 0x24, 0x1c,
|
|
0x8d, 0xd3, 0xe2, 0xde, 0x2d, 0xa7, 0xcb, 0x9f, 0xc0, 0x20, 0xd0, 0x51,
|
|
0x59, 0x77, 0x51, 0x2f, 0x06, 0x18, 0x4f, 0xf4, 0xf6, 0x5c, 0x5f, 0x3c,
|
|
0x6e, 0xf6, 0x86, 0x72, 0x7a, 0x43, 0xbb, 0xc2, 0xe7, 0x07, 0xe2, 0xe3,
|
|
0xde, 0xa8, 0x2f, 0x14, 0x93, 0xb5, 0x8a, 0x0d, 0x64, 0xdf, 0x30, 0xd4,
|
|
0xd5, 0x43, 0xae, 0x0d, 0x93, 0xa3, 0x7e, 0x9e, 0x0d, 0xd5, 0x47, 0xd6,
|
|
0xa4, 0x1c, 0x53, 0xd6, 0x1b, 0x8a, 0x4c, 0xc6, 0xfb, 0x59, 0x3f, 0xa1,
|
|
0xb6, 0x65, 0x32, 0x6e, 0xca, 0xb2, 0x4c, 0x99, 0x4c, 0xe5, 0x99, 0xa9,
|
|
0xc1, 0xc9, 0x08, 0x37, 0x51, 0xbd, 0xc7, 0xb7, 0xcf, 0x47, 0xa2, 0x8f,
|
|
0x8c, 0xbe, 0x5e, 0xb2, 0xf5, 0x61, 0xcc, 0x76, 0x7c, 0x6c, 0xe0, 0xcf,
|
|
0x0d, 0x7d, 0x2c, 0xe0, 0x1c, 0x66, 0x64, 0x36, 0x38, 0x7b, 0x5f, 0xdf,
|
|
0xb6, 0x3e, 0x2a, 0xef, 0xf3, 0x85, 0xc6, 0xa2, 0xe1, 0xc0, 0x58, 0xcd,
|
|
0x4e, 0x73, 0x2c, 0x35, 0x89, 0x51, 0x75, 0xa8, 0x25, 0x68, 0xa5, 0xb2,
|
|
0x33, 0x69, 0xa9, 0x91, 0xb4, 0x52, 0xe9, 0x99, 0x94, 0x78, 0x82, 0x5a,
|
|
0xa9, 0xf2, 0x6c, 0x2a, 0xe6, 0x1c, 0xb6, 0x9e, 0xb9, 0x63, 0xda, 0x94,
|
|
0x66, 0xaf, 0x30, 0xe8, 0x4f, 0x2a, 0xf6, 0xf9, 0x07, 0x95, 0x2d, 0xce,
|
|
0x3e, 0x06, 0xa8, 0x72, 0x7e, 0xb2, 0xd5, 0xd3, 0xd4, 0xc7, 0x4a, 0xeb,
|
|
0x02, 0x41, 0xcc, 0x45, 0x79, 0xe7, 0x64, 0x20, 0x38, 0xc6, 0xf5, 0xcd,
|
|
0x36, 0xdc, 0x29, 0xaa, 0x67, 0x54, 0x19, 0xf0, 0xc7, 0x60, 0xee, 0xad,
|
|
0xb4, 0xfc, 0xf4, 0x2a, 0x83, 0xfe, 0x78, 0x1c, 0x66, 0x17, 0x4b, 0x36,
|
|
0x79, 0x86, 0x21, 0x98, 0xca, 0xad, 0x34, 0x3f, 0xa1, 0x34, 0x1a, 0x0e,
|
|
0xc5, 0xfd, 0xa1, 0x78, 0x4d, 0x17, 0xd3, 0x03, 0x68, 0xac, 0x24, 0x91,
|
|
0x35, 0xe1, 0x1f, 0x0b, 0xf8, 0x6a, 0xd8, 0xa0, 0x6b, 0x92, 0xc6, 0xd8,
|
|
0x4a, 0x2b, 0xce, 0xac, 0xc0, 0xd6, 0x5a, 0xce, 0xd6, 0xc7, 0x8c, 0xb5,
|
|
0x3b, 0xa7, 0xd5, 0x6e, 0x25, 0xcf, 0xe9, 0x94, 0x12, 0xc6, 0xde, 0x4a,
|
|
0x35, 0xa7, 0xd3, 0xd1, 0xeb, 0x5c, 0x3e, 0x75, 0xbb, 0xb7, 0xd2, 0xaa,
|
|
0xb3, 0x15, 0xd8, 0x12, 0x52, 0x45, 0xb6, 0x44, 0xfc, 0x21, 0xff, 0x58,
|
|
0x5f, 0x20, 0x86, 0x89, 0xe0, 0x19, 0x74, 0x9f, 0xa5, 0xe0, 0x19, 0x06,
|
|
0x95, 0xdc, 0x89, 0xd6, 0x85, 0x9d, 0xa6, 0x34, 0xe0, 0x1f, 0xf5, 0x07,
|
|
0xf6, 0x71, 0x3d, 0x45, 0x09, 0x95, 0x70, 0xac, 0x46, 0xae, 0x60, 0xf9,
|
|
0x70, 0xcf, 0xc0, 0x60, 0xef, 0x96, 0xcd, 0xad, 0x94, 0x3b, 0x35, 0x2f,
|
|
0x34, 0x16, 0xc4, 0xdc, 0xe7, 0x59, 0x85, 0xeb, 0x7d, 0x2c, 0x44, 0x35,
|
|
0x05, 0x56, 0x69, 0xbf, 0x2f, 0x3a, 0xea, 0x0f, 0x0e, 0x4d, 0x06, 0xc6,
|
|
0x5a, 0xc9, 0x95, 0xc8, 0x98, 0x8c, 0x07, 0x82, 0x35, 0x7d, 0xe1, 0xdd,
|
|
0xad, 0xd4, 0xd8, 0x37, 0x1a, 0x9e, 0xa8, 0x89, 0x4e, 0xc4, 0x82, 0x35,
|
|
0x7b, 0xe0, 0x1d, 0x6a, 0xa6, 0xb9, 0x88, 0xf2, 0xd9, 0xdc, 0x71, 0x2b,
|
|
0xd5, 0x9f, 0xa5, 0xd4, 0x4c, 0x5f, 0xdb, 0x4a, 0x75, 0x67, 0x29, 0x33,
|
|
0xc3, 0x03, 0x9e, 0xbd, 0x99, 0x99, 0x8e, 0xf1, 0xec, 0xcd, 0xcc, 0x70,
|
|
0x9c, 0xff, 0x7c, 0x33, 0xd6, 0xe5, 0x5c, 0xf1, 0x4f, 0x96, 0x51, 0xda,
|
|
0x1b, 0xcf, 0xa2, 0x9d, 0x1c, 0x84, 0x69, 0x8d, 0xe5, 0x3d, 0x07, 0xe0,
|
|
0x0f, 0x42, 0xbe, 0x60, 0xe2, 0x18, 0xeb, 0xf3, 0x4d, 0xec, 0x1c, 0xf3,
|
|
0xd5, 0xfe, 0xff, 0xac, 0xac, 0xae, 0x95, 0x3a, 0xff, 0xe5, 0xca, 0xa6,
|
|
0x9f, 0x99, 0xad, 0xd4, 0xf7, 0x2f, 0xd7, 0x71, 0xfa, 0x93, 0xb0, 0x95,
|
|
0xba, 0xff, 0xe5, 0xda, 0x92, 0x12, 0xde, 0xbb, 0x5e, 0x5f, 0x6c, 0xef,
|
|
0xd9, 0xcd, 0x60, 0x46, 0x2d, 0x67, 0x37, 0x83, 0xcd, 0xbe, 0x38, 0xf6,
|
|
0xe9, 0xd4, 0x2d, 0x5b, 0x71, 0x96, 0x32, 0xbc, 0xfc, 0xfd, 0xbe, 0xf8,
|
|
0x38, 0xfb, 0x9f, 0x33, 0x6b, 0x26, 0xcc, 0x7e, 0xba, 0xe3, 0x2a, 0x39,
|
|
0x73, 0x41, 0x76, 0x97, 0x63, 0xbe, 0xe0, 0xbe, 0xc0, 0xde, 0x1a, 0x9c,
|
|
0x5a, 0xe1, 0xb8, 0x8f, 0xa3, 0x90, 0x9a, 0x9e, 0x90, 0x8e, 0x40, 0x64,
|
|
0xf0, 0xd3, 0x4a, 0xc5, 0xb3, 0xe8, 0xf4, 0xf2, 0x21, 0xa7, 0xf3, 0x4b,
|
|
0x67, 0xc9, 0xdf, 0xe4, 0x9f, 0xd8, 0xa9, 0x15, 0xfc, 0x31, 0x3e, 0x5d,
|
|
0x67, 0x53, 0x89, 0x8f, 0x87, 0xc7, 0xe0, 0x63, 0x7c, 0x13, 0x7e, 0x58,
|
|
0x18, 0xb4, 0x16, 0xcd, 0xa2, 0x35, 0x18, 0xd8, 0x1d, 0xf2, 0xc5, 0x27,
|
|
0xa3, 0x7e, 0x76, 0x71, 0x33, 0xb3, 0xbd, 0xe3, 0xd1, 0xf0, 0x7e, 0x14,
|
|
0x9d, 0xd7, 0xc7, 0xf1, 0x48, 0x4d, 0x20, 0x5c, 0x63, 0x89, 0xa7, 0x5a,
|
|
0x29, 0x47, 0x89, 0x83, 0xbe, 0xd0, 0xee, 0x1a, 0xdd, 0xdb, 0x79, 0x16,
|
|
0x91, 0x45, 0xb3, 0xdc, 0x22, 0xee, 0x0d, 0x06, 0xfd, 0xbb, 0x7d, 0xc1,
|
|
0x8e, 0xe8, 0xee, 0xc9, 0x09, 0x1c, 0x68, 0x16, 0xad, 0x5c, 0xab, 0x16,
|
|
0x4e, 0xb9, 0xdd, 0xca, 0xc9, 0x27, 0x85, 0x9b, 0x27, 0x83, 0xc1, 0xfe,
|
|
0x70, 0x00, 0x59, 0x51, 0x4b, 0x31, 0x97, 0x45, 0x63, 0xcb, 0xce, 0x3d,
|
|
0xfe, 0xd1, 0xf8, 0x54, 0xd9, 0x60, 0x3c, 0x8a, 0xe9, 0xe6, 0xb3, 0x74,
|
|
0xba, 0x2c, 0x71, 0x16, 0x5b, 0xbb, 0x2d, 0x07, 0xed, 0xdb, 0xc9, 0x5e,
|
|
0x7c, 0x91, 0x45, 0x1c, 0xf5, 0xef, 0xaa, 0x39, 0xdf, 0xef, 0xdb, 0x3b,
|
|
0xe0, 0xdf, 0x85, 0x1d, 0x16, 0x1a, 0x3d, 0x5b, 0x76, 0x9b, 0x59, 0xa9,
|
|
0xf4, 0xe8, 0x1d, 0xd1, 0xa8, 0xef, 0x20, 0x9f, 0x5c, 0xad, 0xb3, 0x8b,
|
|
0xdb, 0xcc, 0x1e, 0x27, 0xc5, 0x98, 0xcd, 0x7c, 0x8b, 0xac, 0x2b, 0x8c,
|
|
0x79, 0x1b, 0x9d, 0x32, 0x51, 0x52, 0xbe, 0xde, 0x17, 0x43, 0x04, 0x15,
|
|
0x99, 0x55, 0xd8, 0x36, 0x43, 0x88, 0xc8, 0x62, 0xa6, 0x26, 0x84, 0x6d,
|
|
0x38, 0xb1, 0x92, 0xc2, 0x5e, 0x4c, 0xb0, 0x2f, 0x1e, 0xc6, 0xc4, 0x38,
|
|
0x2d, 0x52, 0xd5, 0xfd, 0xe9, 0x92, 0xa9, 0x6d, 0xa8, 0xf9, 0x47, 0xd7,
|
|
0xb3, 0x2d, 0x42, 0xd9, 0xe8, 0x34, 0x41, 0x9b, 0x69, 0x3d, 0x52, 0xe0,
|
|
0x0d, 0x4c, 0x58, 0x96, 0x21, 0x29, 0x52, 0x1e, 0xa3, 0xc4, 0x22, 0x46,
|
|
0x18, 0x34, 0x3a, 0x19, 0x8d, 0x72, 0x24, 0xd4, 0x73, 0xc0, 0x3f, 0x3a,
|
|
0x29, 0x7b, 0xb9, 0xe4, 0xcc, 0x0a, 0x83, 0xfe, 0xa8, 0x72, 0x87, 0xee,
|
|
0x33, 0xeb, 0xc5, 0x12, 0x4b, 0x2a, 0x35, 0x76, 0x4d, 0x86, 0xe4, 0x7c,
|
|
0xd7, 0xac, 0xd3, 0xcc, 0x59, 0xb2, 0x31, 0xa4, 0x19, 0xbe, 0x8b, 0x3a,
|
|
0x66, 0x88, 0x66, 0xbf, 0x32, 0x59, 0xef, 0x54, 0xb1, 0x83, 0x88, 0x70,
|
|
0x26, 0xdc, 0x31, 0xd5, 0x6d, 0x6a, 0x9c, 0x59, 0xc5, 0x7e, 0x5f, 0x34,
|
|
0x04, 0xfb, 0x5d, 0xed, 0x1e, 0xdc, 0x1b, 0x88, 0x44, 0xf8, 0x3a, 0xb3,
|
|
0xa9, 0xb7, 0xbb, 0xd7, 0x3d, 0xb4, 0xa9, 0x5f, 0x5f, 0x69, 0xdc, 0xe4,
|
|
0x9a, 0xe9, 0x7b, 0xc9, 0x35, 0xd3, 0x51, 0x52, 0x9a, 0xe9, 0x08, 0x29,
|
|
0x75, 0xb0, 0x7b, 0xe3, 0x8e, 0xde, 0xcd, 0x5e, 0x5a, 0x6c, 0x8d, 0x41,
|
|
0xab, 0xbb, 0x3a, 0xfa, 0xfa, 0x3a, 0x3b, 0xba, 0x36, 0xee, 0xf0, 0x6e,
|
|
0xed, 0xef, 0xd9, 0xb1, 0xa9, 0xc3, 0xdb, 0xb5, 0x7e, 0x47, 0xdf, 0x96,
|
|
0x41, 0x2f, 0xe5, 0x79, 0x07, 0x3a, 0x36, 0x0f, 0xf6, 0x6f, 0x19, 0xf0,
|
|
0xee, 0xe8, 0xdc, 0xea, 0xed, 0x19, 0xf4, 0x0e, 0xf4, 0x74, 0x6c, 0xa2,
|
|
0x39, 0x49, 0x29, 0xba, 0x43, 0x05, 0xa7, 0xf1, 0x9e, 0x24, 0x86, 0xc9,
|
|
0x18, 0xc6, 0xed, 0x65, 0x18, 0x77, 0x19, 0xfb, 0x30, 0xdf, 0xe6, 0x8c,
|
|
0xe1, 0x0d, 0x94, 0x32, 0xbc, 0x41, 0x5e, 0x74, 0x8c, 0x61, 0xdc, 0x6e,
|
|
0x86, 0x71, 0xbb, 0xb1, 0x0f, 0xf3, 0xed, 0xc7, 0x31, 0xac, 0xae, 0x3f,
|
|
0x20, 0x1b, 0x58, 0x2e, 0x3f, 0xfa, 0x58, 0xcc, 0xb9, 0x29, 0x92, 0xc8,
|
|
0x6c, 0x2d, 0x44, 0x41, 0x63, 0x78, 0x1b, 0x89, 0x6d, 0x64, 0x6c, 0x03,
|
|
0x8b, 0xab, 0x91, 0x31, 0xd2, 0x49, 0x9e, 0x91, 0xb3, 0x47, 0xf9, 0x55,
|
|
0x23, 0xff, 0x52, 0xd4, 0x5c, 0xfe, 0x4f, 0xa8, 0x23, 0x1a, 0x1c, 0x99,
|
|
0xcd, 0xb3, 0xe5, 0x8e, 0xcc, 0xe2, 0xb8, 0x32, 0x7c, 0xa3, 0xa3, 0xfe,
|
|
0x58, 0xac, 0xbc, 0x16, 0xd7, 0x7d, 0x93, 0xaf, 0xb3, 0xf0, 0xf5, 0xe0,
|
|
0x33, 0x15, 0xbf, 0x2e, 0xe8, 0xdb, 0x1d, 0x23, 0x9b, 0x6f, 0x6c, 0x0c,
|
|
0x12, 0xd5, 0x09, 0x79, 0x99, 0xcc, 0xf0, 0x45, 0x22, 0xfa, 0x96, 0x40,
|
|
0x29, 0xe0, 0xfd, 0xa1, 0x31, 0x72, 0x80, 0x06, 0x0f, 0x22, 0x19, 0xe3,
|
|
0x3d, 0x4b, 0xe9, 0x89, 0xf1, 0x53, 0x51, 0x82, 0xed, 0xeb, 0x91, 0x87,
|
|
0x97, 0xb2, 0xba, 0xa1, 0xa1, 0xde, 0x6e, 0x72, 0xee, 0x9c, 0x76, 0x47,
|
|
0xa3, 0xbc, 0x9d, 0xd6, 0xa8, 0x53, 0x8d, 0x31, 0x66, 0xd1, 0xdb, 0xa1,
|
|
0x6f, 0xd4, 0xce, 0x9d, 0x7a, 0xbd, 0x63, 0xfd, 0xbe, 0x00, 0xbb, 0x5a,
|
|
0x72, 0xec, 0x64, 0x67, 0x4b, 0x69, 0xa3, 0xa6, 0x01, 0xa4, 0x60, 0xd6,
|
|
0x11, 0x00, 0x93, 0x63, 0x34, 0xe8, 0xf7, 0x45, 0x99, 0x84, 0x63, 0x7e,
|
|
0x4a, 0xc5, 0xbe, 0x0c, 0x61, 0x26, 0x28, 0x53, 0x33, 0x1c, 0x82, 0xa0,
|
|
0x14, 0xc6, 0xe3, 0x0b, 0x84, 0x62, 0x52, 0x2c, 0xb9, 0x8d, 0xfe, 0x83,
|
|
0x24, 0xc6, 0x28, 0x5b, 0x3f, 0x6b, 0xe8, 0x8f, 0x86, 0xe3, 0xe1, 0xd1,
|
|
0x70, 0x90, 0x52, 0x74, 0x0f, 0xb2, 0xc7, 0xa6, 0xd8, 0x5b, 0x8c, 0xd2,
|
|
0x94, 0xa0, 0x77, 0x8c, 0x32, 0xc6, 0x38, 0x50, 0x52, 0xcd, 0xd8, 0xc6,
|
|
0x30, 0x1d, 0x29, 0xfe, 0x8b, 0x27, 0x7d, 0x41, 0xa8, 0xf8, 0xb5, 0x27,
|
|
0x20, 0xdb, 0xae, 0xf2, 0x5a, 0xfe, 0xa8, 0xe3, 0x8f, 0x7a, 0xaa, 0xde,
|
|
0xe5, 0x0b, 0x04, 0xfd, 0x63, 0xee, 0x78, 0xd8, 0x3d, 0x1a, 0xf5, 0xfb,
|
|
0xe2, 0x7e, 0x77, 0x62, 0xc4, 0xe6, 0x86, 0xdb, 0x15, 0x0d, 0x4f, 0xb8,
|
|
0xb1, 0x16, 0x51, 0xac, 0x0c, 0xa5, 0xed, 0x0a, 0x20, 0xa6, 0x0b, 0x5c,
|
|
0xe2, 0xa7, 0x52, 0x70, 0x63, 0xc9, 0xb9, 0x5a, 0x17, 0x8e, 0x5a, 0x2e,
|
|
0xe4, 0x4a, 0xb9, 0x84, 0x55, 0xcc, 0x3d, 0x3a, 0x9b, 0x42, 0x06, 0xd7,
|
|
0xad, 0x4e, 0x2c, 0xb2, 0xef, 0xe6, 0xf9, 0xb0, 0xc1, 0x65, 0x50, 0x06,
|
|
0x3e, 0xf4, 0xa5, 0x5e, 0xf3, 0x4a, 0xbd, 0x88, 0x79, 0x65, 0x0f, 0xd3,
|
|
0xef, 0xd8, 0xb4, 0x20, 0x99, 0x37, 0xd3, 0x57, 0xcd, 0xe3, 0x4c, 0xd8,
|
|
0x49, 0x60, 0x54, 0x1e, 0xff, 0xa6, 0x0d, 0xe5, 0x42, 0x3c, 0xa3, 0x4f,
|
|
0x2e, 0xab, 0x50, 0x3f, 0xa2, 0x29, 0x9c, 0x29, 0x1b, 0x44, 0x24, 0x31,
|
|
0x19, 0x93, 0x35, 0xcf, 0xbc, 0xc6, 0x53, 0x1a, 0xc4, 0xea, 0x19, 0x50,
|
|
0x0e, 0xb8, 0x6e, 0xb5, 0x92, 0xe6, 0x90, 0x8a, 0x92, 0xa2, 0xe9, 0xcf,
|
|
0x30, 0x64, 0xeb, 0xdd, 0xd3, 0x16, 0x3e, 0x5d, 0xca, 0x64, 0x47, 0x32,
|
|
0x12, 0x6c, 0x8c, 0xf2, 0x93, 0x3c, 0xe6, 0x36, 0xf9, 0x10, 0xa7, 0x20,
|
|
0x29, 0xef, 0x8f, 0xf2, 0xa1, 0xcd, 0xd3, 0x3b, 0x34, 0x11, 0x91, 0xa3,
|
|
0x98, 0x1e, 0x49, 0xa3, 0x24, 0xec, 0x26, 0x15, 0x39, 0xd2, 0x1c, 0x2b,
|
|
0xc1, 0xac, 0x9f, 0x9c, 0xe0, 0x8b, 0xbf, 0x6f, 0x8c, 0x43, 0x05, 0xb5,
|
|
0x38, 0xb3, 0xad, 0x9d, 0x03, 0xaa, 0xd8, 0x49, 0x92, 0xa8, 0x1a, 0xd4,
|
|
0x53, 0x22, 0xc9, 0xe8, 0x3b, 0x4b, 0x57, 0x78, 0x32, 0x14, 0xa7, 0xf9,
|
|
0x10, 0xf1, 0x9a, 0x4c, 0xaf, 0x02, 0x63, 0xe0, 0x62, 0x9b, 0x11, 0xc8,
|
|
0x51, 0x16, 0x98, 0x2d, 0x51, 0x3d, 0x72, 0x39, 0x0b, 0xc9, 0x9b, 0x8f,
|
|
0xaa, 0x65, 0x0e, 0x64, 0x9c, 0xda, 0x3c, 0xc9, 0x21, 0x22, 0x65, 0xea,
|
|
0xa4, 0x74, 0xf0, 0x69, 0x3a, 0x11, 0x53, 0x5a, 0xd1, 0x70, 0x04, 0xb1,
|
|
0x7e, 0x00, 0xf5, 0x67, 0x23, 0x39, 0xe0, 0x9f, 0x08, 0xc7, 0xfd, 0x7a,
|
|
0x02, 0x79, 0x2e, 0xb5, 0xc1, 0x39, 0x99, 0x95, 0x07, 0x92, 0xf6, 0x0c,
|
|
0x72, 0x31, 0x13, 0xf3, 0x88, 0x31, 0xeb, 0x32, 0xdc, 0x49, 0xef, 0xc1,
|
|
0x88, 0x9f, 0x72, 0xc6, 0xe5, 0x7d, 0x57, 0x89, 0x31, 0x08, 0xff, 0x18,
|
|
0xe5, 0x5a, 0x45, 0xdc, 0xd2, 0x3e, 0x08, 0xe7, 0x28, 0xa1, 0x3e, 0x89,
|
|
0x28, 0x75, 0xdc, 0x17, 0xdb, 0xcc, 0xf6, 0x66, 0x07, 0x83, 0xce, 0xf2,
|
|
0x67, 0x57, 0x78, 0xcc, 0x8f, 0x74, 0x18, 0x7b, 0xd4, 0x08, 0x8c, 0x91,
|
|
0x3d, 0xc0, 0x73, 0x97, 0x86, 0x00, 0x70, 0xd8, 0x87, 0x59, 0x02, 0x17,
|
|
0xeb, 0x18, 0xe5, 0x13, 0x8d, 0x32, 0x03, 0xb1, 0x2e, 0xb5, 0xa3, 0x51,
|
|
0x71, 0x6a, 0x20, 0xd6, 0x33, 0x11, 0x89, 0x1f, 0x44, 0xbe, 0x0e, 0x64,
|
|
0x28, 0x65, 0xaf, 0xff, 0x20, 0x4e, 0x34, 0xea, 0x0f, 0xca, 0xeb, 0x55,
|
|
0x39, 0x86, 0x1e, 0x9a, 0x72, 0xd7, 0xe4, 0xfb, 0x6d, 0x6f, 0x77, 0x79,
|
|
0x6d, 0x39, 0x42, 0xfa, 0x2a, 0x0e, 0xe9, 0xab, 0x38, 0xa4, 0xaf, 0x3a,
|
|
0xeb, 0xe5, 0x84, 0xce, 0x9b, 0x56, 0x63, 0x72, 0x41, 0x74, 0x95, 0x75,
|
|
0xff, 0x72, 0x95, 0x59, 0x13, 0xbe, 0xbd, 0xc9, 0x67, 0x83, 0xa9, 0x13,
|
|
0x49, 0x71, 0x68, 0x72, 0x97, 0x6f, 0x94, 0xe3, 0xf5, 0x28, 0xd9, 0xf9,
|
|
0xa4, 0xa1, 0x9c, 0x89, 0x19, 0xa5, 0x33, 0x27, 0x2c, 0x9e, 0xd9, 0x1e,
|
|
0x62, 0xb3, 0x71, 0xf0, 0x67, 0x8c, 0x0a, 0x42, 0xfe, 0xfd, 0x83, 0x58,
|
|
0xd3, 0xa0, 0x1f, 0x31, 0x2e, 0x2c, 0xd7, 0x8c, 0x7b, 0xa0, 0xc5, 0xf3,
|
|
0xee, 0x0a, 0x87, 0x3a, 0x7d, 0xf1, 0xd1, 0xf1, 0xe4, 0x13, 0x2d, 0x14,
|
|
0x91, 0x6e, 0x20, 0xa4, 0x02, 0x4f, 0xde, 0xcc, 0xfe, 0x2e, 0xac, 0xda,
|
|
0x6e, 0x3f, 0xcd, 0x09, 0x87, 0xac, 0x0b, 0x3c, 0x37, 0x3c, 0xe5, 0x19,
|
|
0x0d, 0xcd, 0x9b, 0x9a, 0xee, 0xf6, 0x07, 0x7d, 0x07, 0x21, 0xce, 0x36,
|
|
0xc5, 0xa6, 0x0d, 0xa4, 0x86, 0x43, 0xeb, 0x82, 0x93, 0x58, 0xee, 0x2c,
|
|
0x54, 0xcf, 0xc7, 0xb0, 0xf4, 0xb3, 0x66, 0x4a, 0xf5, 0x82, 0x52, 0x90,
|
|
0xe2, 0x43, 0x2c, 0x97, 0xe7, 0x78, 0xba, 0xd3, 0xc9, 0x60, 0xa1, 0xe6,
|
|
0xe7, 0x30, 0x9f, 0x7c, 0xee, 0x5a, 0x70, 0x9a, 0x55, 0xa6, 0xc2, 0xd3,
|
|
0x2d, 0x16, 0xc6, 0x81, 0x1c, 0xcb, 0x63, 0xda, 0x74, 0x4e, 0xab, 0x3d,
|
|
0x23, 0x59, 0x76, 0xd1, 0xd8, 0xd4, 0xe1, 0xfd, 0xec, 0xbe, 0x72, 0x23,
|
|
0x38, 0xdb, 0xa6, 0xf7, 0xa7, 0x68, 0x16, 0xe1, 0x60, 0xdc, 0x1f, 0xf1,
|
|
0xee, 0x0f, 0x53, 0xc1, 0x94, 0xbc, 0xe4, 0xc2, 0x53, 0x7a, 0x44, 0x46,
|
|
0x14, 0x63, 0xfe, 0x03, 0x94, 0x1a, 0x89, 0x86, 0xc7, 0x26, 0xf9, 0x64,
|
|
0x42, 0x1f, 0x28, 0x2d, 0x6a, 0xc6, 0x69, 0xf9, 0x51, 0xff, 0x6e, 0x7e,
|
|
0xf2, 0x15, 0x9d, 0x16, 0x47, 0xa5, 0x44, 0xe5, 0x54, 0x52, 0x86, 0xa2,
|
|
0xb2, 0xd7, 0xf3, 0xa3, 0x38, 0xd0, 0xfc, 0xb1, 0x78, 0x72, 0xe9, 0xfa,
|
|
0xa3, 0x81, 0x70, 0x34, 0x80, 0x2d, 0x61, 0x8b, 0x4e, 0x86, 0x28, 0xd5,
|
|
0x7c, 0xa6, 0x9f, 0x16, 0x1b, 0x1d, 0xf7, 0x8f, 0x4d, 0x06, 0xb1, 0xc9,
|
|
0x62, 0x3c, 0xc3, 0x45, 0xfc, 0xa9, 0x1e, 0x8d, 0x8f, 0xfb, 0xc6, 0xdc,
|
|
0xbd, 0x5b, 0xdc, 0x7e, 0xf3, 0x9e, 0x45, 0x59, 0x88, 0x47, 0x03, 0xbe,
|
|
0xa0, 0x76, 0x2c, 0x73, 0x54, 0x6a, 0x47, 0x48, 0x25, 0x6d, 0x31, 0x6c,
|
|
0xae, 0x6c, 0x7c, 0x98, 0xfd, 0x92, 0x9e, 0x20, 0x13, 0x02, 0x5e, 0xc7,
|
|
0x4d, 0xbc, 0x8d, 0xe7, 0x72, 0x42, 0x07, 0x17, 0x93, 0xd8, 0xcc, 0xb6,
|
|
0x58, 0x74, 0x94, 0x1c, 0xb1, 0xb8, 0x8f, 0xa7, 0x59, 0x12, 0x56, 0x45,
|
|
0xc5, 0x92, 0x8d, 0x87, 0x23, 0x32, 0x69, 0x8f, 0x81, 0x43, 0x3f, 0xcd,
|
|
0x74, 0x4a, 0x7c, 0x3c, 0x80, 0xe8, 0x88, 0xd2, 0xe2, 0x61, 0xed, 0xa0,
|
|
0xd2, 0xe3, 0x09, 0x9f, 0xee, 0x8a, 0xcf, 0x74, 0x4b, 0xf6, 0x38, 0xf7,
|
|
0x64, 0xde, 0x64, 0x68, 0xb6, 0xc5, 0x9a, 0x3f, 0x4d, 0x6c, 0x59, 0x92,
|
|
0xc2, 0xc9, 0xd0, 0x69, 0x66, 0xdc, 0xb1, 0x4f, 0x3a, 0x9f, 0x54, 0x49,
|
|
0xb6, 0xec, 0xa2, 0xcf, 0x88, 0x8f, 0x7e, 0xb4, 0xbb, 0xf9, 0xc3, 0x1e,
|
|
0xce, 0xc4, 0xf4, 0x79, 0x56, 0x7b, 0xb0, 0x8e, 0x9e, 0x15, 0x1e, 0x6c,
|
|
0xfb, 0x48, 0x20, 0x28, 0x8f, 0xd2, 0xaa, 0x09, 0xcc, 0x00, 0x32, 0xa2,
|
|
0x7e, 0xc4, 0x3a, 0x31, 0x3f, 0x32, 0xe1, 0xdd, 0xaa, 0x30, 0xf7, 0x08,
|
|
0x4c, 0x26, 0x27, 0x62, 0x9e, 0xd5, 0xbb, 0x10, 0x7e, 0xf8, 0x57, 0x78,
|
|
0x26, 0x02, 0xa1, 0x2a, 0x5f, 0x24, 0xe0, 0x59, 0x5d, 0xdf, 0xb8, 0xc2,
|
|
0x13, 0x1b, 0xf7, 0x55, 0xd5, 0xa1, 0x90, 0x6f, 0x95, 0x6f, 0xac, 0xae,
|
|
0xd9, 0xb7, 0xaa, 0xb6, 0x71, 0x65, 0xed, 0xce, 0x55, 0x2d, 0x2d, 0x63,
|
|
0xb5, 0x8d, 0xcd, 0xf5, 0xfe, 0xc6, 0x96, 0x55, 0xa3, 0x75, 0xb5, 0x2d,
|
|
0xbe, 0x55, 0x4d, 0x3b, 0x77, 0xad, 0xda, 0xd5, 0x52, 0x37, 0xc6, 0xb5,
|
|
0xc2, 0x50, 0x62, 0x68, 0x0e, 0x85, 0x9a, 0xab, 0xeb, 0x6a, 0xab, 0x5b,
|
|
0xaa, 0x10, 0x9c, 0x78, 0x3e, 0x42, 0x46, 0xbd, 0x78, 0xc5, 0x58, 0x68,
|
|
0x2f, 0x30, 0x0a, 0xe2, 0x05, 0xfe, 0x82, 0x34, 0x33, 0x29, 0x90, 0x6c,
|
|
0x2a, 0xc8, 0x50, 0x49, 0x5b, 0xc1, 0xc5, 0x32, 0xaf, 0x1a, 0x89, 0xc2,
|
|
0x2a, 0xa3, 0xc6, 0x38, 0x26, 0xec, 0xe9, 0xd7, 0x1b, 0x05, 0x59, 0x49,
|
|
0xd6, 0x99, 0x64, 0x8b, 0xcc, 0x42, 0x7b, 0x65, 0xa1, 0x84, 0x7c, 0x91,
|
|
0x64, 0xcb, 0xd3, 0xed, 0xe0, 0xdd, 0x46, 0x03, 0xeb, 0x88, 0xc2, 0x35,
|
|
0x26, 0xd3, 0x98, 0x54, 0x2c, 0x4e, 0xb2, 0x25, 0x46, 0x1d, 0xd8, 0x85,
|
|
0xf6, 0x6e, 0xe7, 0x21, 0xf9, 0xef, 0x58, 0xec, 0x74, 0x03, 0x7a, 0x57,
|
|
0x8c, 0x3f, 0xd5, 0x99, 0x4a, 0xa5, 0x4a, 0x50, 0xcd, 0x51, 0xaa, 0x46,
|
|
0xb9, 0xd3, 0x29, 0xb5, 0x8c, 0x62, 0x5d, 0x8f, 0x81, 0xcc, 0xf9, 0x2a,
|
|
0xd3, 0x26, 0x33, 0x65, 0xb6, 0xad, 0xd8, 0x9a, 0x7f, 0x45, 0xb2, 0xc9,
|
|
0x6b, 0x54, 0xf7, 0x53, 0x0a, 0x6c, 0x05, 0x1f, 0x2d, 0xe8, 0x2f, 0xd8,
|
|
0x54, 0x90, 0x5e, 0xb0, 0xc9, 0x9c, 0x95, 0x30, 0x46, 0x54, 0x9d, 0x9c,
|
|
0xa4, 0x70, 0xc1, 0xce, 0x82, 0x81, 0xa9, 0xc9, 0x9d, 0x66, 0x92, 0x87,
|
|
0xdf, 0x6f, 0x4d, 0xac, 0x37, 0x13, 0x0e, 0x28, 0x0e, 0x15, 0x04, 0x50,
|
|
0x73, 0x66, 0xb2, 0xd5, 0xcb, 0x8c, 0x5a, 0xa9, 0x58, 0x58, 0x51, 0xb8,
|
|
0xa4, 0x70, 0x69, 0x52, 0x7e, 0x65, 0x92, 0xbd, 0xca, 0xec, 0x98, 0x28,
|
|
0x38, 0xa7, 0x20, 0xb7, 0x60, 0x7f, 0x41, 0xab, 0x59, 0xa3, 0x12, 0xd9,
|
|
0xb1, 0x64, 0xbd, 0x10, 0xc9, 0x8a, 0x32, 0x0b, 0xdd, 0x85, 0x25, 0x85,
|
|
0x95, 0x85, 0x8b, 0x0a, 0x8b, 0x0b, 0x57, 0x14, 0x7a, 0x0a, 0x4b, 0x0b,
|
|
0x97, 0x15, 0x96, 0x15, 0x2e, 0x17, 0x36, 0x91, 0x6e, 0x14, 0x09, 0xc3,
|
|
0x30, 0x84, 0xb1, 0xe6, 0xd0, 0x21, 0xfb, 0xe1, 0xaa, 0xb5, 0xe2, 0xc6,
|
|
0x15, 0x42, 0x3c, 0x0d, 0xbc, 0x05, 0x3c, 0x51, 0x25, 0xc4, 0x3b, 0xc0,
|
|
0x55, 0xd5, 0x42, 0x9c, 0x58, 0x2e, 0xc4, 0xa1, 0x1a, 0xd0, 0x5a, 0x82,
|
|
0xb6, 0x23, 0x47, 0xe4, 0x9c, 0x7b, 0xf9, 0x21, 0xfb, 0xa9, 0xda, 0xf5,
|
|
0xe2, 0xaa, 0x3a, 0x68, 0xd6, 0xf1, 0x0f, 0x15, 0xa4, 0x42, 0xba, 0x01,
|
|
0xf5, 0xfc, 0xa8, 0x8e, 0x32, 0x04, 0xa5, 0x8b, 0x99, 0x7f, 0x1b, 0x51,
|
|
0xe6, 0xfd, 0x3a, 0x22, 0xe1, 0xd8, 0x0e, 0xee, 0x89, 0xe6, 0x0f, 0x1d,
|
|
0x4a, 0x23, 0x31, 0x0d, 0x94, 0x22, 0xd2, 0x72, 0xf5, 0x3b, 0x22, 0xfc,
|
|
0x5f, 0x1f, 0xb7, 0x53, 0xbf, 0x49, 0x1c, 0x69, 0x10, 0xf6, 0xc3, 0x8d,
|
|
0x42, 0x1c, 0xab, 0x17, 0xe2, 0x46, 0xe0, 0x18, 0xf8, 0x67, 0x81, 0xd7,
|
|
0x80, 0xeb, 0x9a, 0xc8, 0x2e, 0x52, 0xe7, 0xeb, 0x12, 0x43, 0x28, 0x71,
|
|
0xc7, 0xca, 0x61, 0x71, 0x62, 0xa5, 0xb0, 0x1f, 0x5b, 0x25, 0xc4, 0x1b,
|
|
0x4d, 0x42, 0xbc, 0xc2, 0x00, 0x7f, 0x5d, 0xb3, 0x10, 0x47, 0x9a, 0xc9,
|
|
0x26, 0xc4, 0xa2, 0x9b, 0xdc, 0x42, 0xfe, 0x3f, 0x7a, 0xf9, 0x11, 0xfb,
|
|
0xfb, 0xcd, 0x63, 0x39, 0xaf, 0x36, 0x63, 0x10, 0xc2, 0x2d, 0x65, 0x7e,
|
|
0xc8, 0x5e, 0x6a, 0xd9, 0x95, 0x73, 0x57, 0x0b, 0xff, 0xc2, 0x01, 0xff,
|
|
0x23, 0x2a, 0x71, 0xa3, 0x5b, 0xd0, 0x6e, 0x8c, 0xee, 0x54, 0x0b, 0xab,
|
|
0x2d, 0x31, 0x20, 0x18, 0x47, 0x43, 0xd7, 0xad, 0x0e, 0x88, 0x47, 0x57,
|
|
0xa3, 0x42, 0x47, 0x05, 0x26, 0x06, 0xc2, 0x3d, 0x10, 0xbe, 0xb4, 0x66,
|
|
0xaf, 0xf8, 0xdb, 0x6a, 0xb4, 0xd8, 0x26, 0xc4, 0xcf, 0x41, 0xef, 0x02,
|
|
0x7d, 0x6b, 0x0d, 0xc6, 0x6d, 0x5c, 0x2f, 0x90, 0xfd, 0xec, 0xf6, 0x1b,
|
|
0x84, 0xf8, 0xc1, 0x76, 0xc8, 0xb6, 0x53, 0x7a, 0xfa, 0xdc, 0xe5, 0x86,
|
|
0x70, 0x25, 0xfe, 0xe3, 0x4a, 0x5c, 0x11, 0x1e, 0xc1, 0xb9, 0xe2, 0xb0,
|
|
0xfb, 0xd1, 0xb5, 0x86, 0xf1, 0x4e, 0xa7, 0x30, 0x7e, 0xd0, 0x2d, 0x32,
|
|
0x5e, 0x01, 0x7f, 0xaa, 0x4b, 0x18, 0xc7, 0xd7, 0xd9, 0x8c, 0xe3, 0x1d,
|
|
0xe9, 0xc6, 0xa3, 0xe7, 0x47, 0xed, 0x8f, 0xf6, 0xda, 0xc5, 0xb3, 0x3d,
|
|
0xb6, 0x43, 0xee, 0xbb, 0xda, 0xc5, 0x21, 0xf7, 0x5b, 0xed, 0x86, 0x38,
|
|
0xd2, 0x87, 0xe6, 0x36, 0x09, 0x71, 0x7c, 0x8b, 0x10, 0xa7, 0xbc, 0x98,
|
|
0xa6, 0x4e, 0x21, 0x5e, 0x05, 0xde, 0x5b, 0x8b, 0xc1, 0x9f, 0x6f, 0x88,
|
|
0x47, 0xb7, 0xa2, 0x5b, 0x5b, 0xd1, 0x13, 0xd1, 0x28, 0xae, 0x16, 0x18,
|
|
0xcf, 0xf1, 0x11, 0x71, 0x85, 0xc1, 0xef, 0x23, 0xbc, 0x3a, 0x82, 0x51,
|
|
0x18, 0xea, 0x1f, 0x34, 0x7d, 0x82, 0xbb, 0x79, 0xff, 0xf6, 0x6b, 0xb1,
|
|
0xf8, 0x17, 0x62, 0x02, 0x2f, 0xb4, 0x53, 0x26, 0x35, 0x17, 0x89, 0x22,
|
|
0x91, 0x21, 0x32, 0x6e, 0x14, 0x87, 0x0f, 0xd9, 0xaf, 0xda, 0xc9, 0x1a,
|
|
0x77, 0xec, 0x44, 0x1f, 0x8f, 0xf3, 0xc7, 0x2b, 0x3b, 0x45, 0xfa, 0xb3,
|
|
0x1f, 0x12, 0xe9, 0xc7, 0x76, 0x88, 0xb4, 0xbf, 0x6d, 0x17, 0x69, 0xc7,
|
|
0x7d, 0x22, 0xed, 0xba, 0x8b, 0xc4, 0x27, 0x0c, 0x92, 0x28, 0x70, 0x18,
|
|
0x76, 0xc1, 0x6f, 0xb4, 0xf0, 0x37, 0xda, 0xbf, 0xe8, 0x57, 0xf4, 0x5d,
|
|
0x4d, 0xf9, 0x07, 0xf2, 0xf8, 0x3b, 0xf0, 0x39, 0xa0, 0x79, 0x16, 0xbe,
|
|
0xc4, 0xc2, 0x2f, 0x3b, 0x4f, 0xe9, 0x36, 0x59, 0x64, 0x9d, 0x16, 0x7e,
|
|
0x93, 0xce, 0xdf, 0xaa, 0xe9, 0x4e, 0x4b, 0xde, 0x5e, 0x0b, 0xbf, 0x4f,
|
|
0xe7, 0x5f, 0xae, 0x65, 0x9f, 0x02, 0xbd, 0x57, 0xcb, 0x1e, 0xb5, 0xc8,
|
|
0x9e, 0xd7, 0xb2, 0x6f, 0x58, 0x64, 0x3f, 0xb3, 0xd4, 0xf3, 0xb6, 0xce,
|
|
0x7f, 0x4f, 0xd3, 0xd4, 0x01, 0x45, 0xf3, 0x35, 0x5d, 0xac, 0x69, 0xa3,
|
|
0xa6, 0x1d, 0x9a, 0x6e, 0x1e, 0x50, 0xdf, 0xfd, 0x73, 0x1d, 0x3b, 0xc0,
|
|
0xef, 0x19, 0x48, 0xd6, 0xb9, 0xdf, 0xc2, 0x5f, 0xa1, 0xf5, 0xaf, 0xd7,
|
|
0xf4, 0x88, 0xa6, 0x8f, 0x83, 0x5e, 0x38, 0x94, 0x7c, 0xef, 0x80, 0xf4,
|
|
0xbb, 0x04, 0x5e, 0xc8, 0xda, 0x41, 0xbb, 0x87, 0xd4, 0x7b, 0x06, 0x7d,
|
|
0xa0, 0xe3, 0x43, 0x34, 0xe5, 0x2f, 0x3e, 0x2d, 0x7d, 0x78, 0x28, 0x59,
|
|
0x07, 0xff, 0xf1, 0xfb, 0x94, 0x87, 0x86, 0xd4, 0xbf, 0x2b, 0x67, 0x7a,
|
|
0xeb, 0x34, 0xfd, 0xe3, 0x3a, 0x6d, 0x96, 0x91, 0xbf, 0x7f, 0x36, 0xa4,
|
|
0x7e, 0xab, 0xeb, 0x18, 0xe8, 0xb3, 0x3a, 0xdf, 0x7c, 0x0f, 0xe5, 0x7c,
|
|
0x9d, 0xff, 0xda, 0x34, 0x39, 0xff, 0x6e, 0xe1, 0x2b, 0x90, 0x9d, 0x9a,
|
|
0x26, 0xe7, 0xdf, 0x32, 0x7c, 0x03, 0xb2, 0xf7, 0xa7, 0xc9, 0xf9, 0xf7,
|
|
0x0d, 0xdf, 0x81, 0xcc, 0x3e, 0x3c, 0xb5, 0x3f, 0xeb, 0x87, 0xd5, 0x7b,
|
|
0x9d, 0x66, 0x7f, 0xf8, 0x3d, 0x8b, 0xc2, 0x61, 0xf5, 0x3b, 0x10, 0xcd,
|
|
0xc3, 0xea, 0xb7, 0x1f, 0x6b, 0x87, 0xd5, 0xef, 0x3f, 0xb6, 0x0f, 0xab,
|
|
0x7f, 0x43, 0xe6, 0x1e, 0x56, 0xbf, 0x03, 0x59, 0x31, 0xac, 0x7e, 0x23,
|
|
0x91, 0xfb, 0xc7, 0xff, 0x5e, 0x82, 0xff, 0x89, 0xf0, 0x05, 0xc3, 0x53,
|
|
0xdb, 0xbd, 0x46, 0x8f, 0x6b, 0x6c, 0x5a, 0xbb, 0x87, 0x86, 0xa7, 0xce,
|
|
0x03, 0xff, 0x26, 0x5b, 0x64, 0x58, 0xfd, 0x1e, 0xdb, 0x81, 0xe1, 0xe4,
|
|
0xef, 0x0a, 0x5a, 0xdf, 0xc7, 0x31, 0x7f, 0x03, 0x98, 0xcb, 0x98, 0xbf,
|
|
0x03, 0xcc, 0xeb, 0xe6, 0x23, 0xf5, 0x5b, 0xc0, 0xfc, 0xce, 0x87, 0xf9,
|
|
0x7b, 0xc0, 0xfc, 0x7e, 0x89, 0xf9, 0x9b, 0xc0, 0x3c, 0x3e, 0xf3, 0x77,
|
|
0x81, 0x85, 0x5b, 0x95, 0xe5, 0xdf, 0x06, 0xb6, 0xb9, 0xd5, 0x6f, 0xe6,
|
|
0xf1, 0x6f, 0x59, 0x08, 0xa7, 0xfa, 0x5d, 0x4b, 0xfe, 0xcd, 0x0f, 0xc3,
|
|
0xad, 0xda, 0xe2, 0xdf, 0x0e, 0xb6, 0xbb, 0xd5, 0xbb, 0x96, 0xbc, 0xdf,
|
|
0xf8, 0x65, 0x17, 0xae, 0x87, 0x7f, 0x0b, 0xc5, 0xe1, 0xd6, 0xef, 0xe9,
|
|
0xf0, 0x6f, 0x62, 0x38, 0xd5, 0xef, 0x64, 0xb0, 0xdd, 0xa4, 0xb8, 0xd5,
|
|
0xfb, 0x34, 0xfc, 0x9b, 0x29, 0x6c, 0x04, 0xdc, 0x6f, 0xfe, 0xfd, 0xe2,
|
|
0xff, 0x07, 0xf7, 0x0e, 0xe8, 0x14, 0xf8, 0x58, 0x00, 0x00
|
|
};
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
STATICMETHOD (nanoTime, "nanoTime", "()J")
|
|
|
|
DECLARE_JNI_CLASS (JavaLangSystem, "java/lang/System")
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
METHOD (openMidiInputPortWithID, "openMidiInputPortWithID", "(IIJ)Lcom/rmsl/juce/JuceMidiSupport$JuceMidiPort;") \
|
|
METHOD (openMidiOutputPortWithID, "openMidiOutputPortWithID", "(II)Lcom/rmsl/juce/JuceMidiSupport$JuceMidiPort;")
|
|
|
|
DECLARE_JNI_CLASS_WITH_BYTECODE (MidiDeviceManager, "com/rmsl/juce/JuceMidiSupport$MidiDeviceManager", 24, javaMidiByteCode)
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
FIELD (deviceId, "deviceId", "I") \
|
|
FIELD (portIndex, "portIndex", "I") \
|
|
FIELD (type, "type", "I") \
|
|
|
|
DECLARE_JNI_CLASS_WITH_BYTECODE (JavaPortPath, "com/rmsl/juce/JuceMidiSupport$PortPath", 24, javaMidiByteCode)
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
METHOD (constructor, "<init>", "(J)V") \
|
|
|
|
DECLARE_JNI_CLASS_WITH_BYTECODE (NativeMidiReceiver, "com/rmsl/juce/JuceMidiSupport$NativeMidiReceiver", 24, javaMidiByteCode)
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
METHOD (send, "send", "([BIIJ)V") \
|
|
|
|
DECLARE_JNI_CLASS (JavaMidiReceiver, "android/media/midi/MidiReceiver")
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
FIELD (name, "name", "Ljava/lang/String;") \
|
|
FIELD (manufacturer, "manufacturer", "Ljava/lang/String;") \
|
|
FIELD (product, "product", "Ljava/lang/String;") \
|
|
FIELD (serialNumber, "serialNumber", "Ljava/lang/String;") \
|
|
FIELD (id, "id", "I") \
|
|
FIELD (transport, "transport", "I") \
|
|
FIELD (type, "type", "I") \
|
|
FIELD (dst, "dst", "Ljava/util/ArrayList;") \
|
|
FIELD (src, "src", "Ljava/util/ArrayList;")
|
|
|
|
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiDeviceInfo, "com/rmsl/juce/JuceMidiSupport$JuceMidiDeviceInfo", 24, javaMidiByteCode)
|
|
#undef JNI_CLASS_MEMBERS
|
|
|
|
struct AndroidMidiHelpers
|
|
{
|
|
static constexpr int TRANSPORT_BYTESTREAM = 1;
|
|
static constexpr int TRANSPORT_UMP = 2;
|
|
|
|
static jlong midiClockMillis (JNIEnv* env)
|
|
{
|
|
const auto nanos = env->CallStaticLongMethod (JavaLangSystem, JavaLangSystem.nanoTime);
|
|
return nanos / (jlong) 1e6;
|
|
}
|
|
|
|
/* When dealing in UMP, the system always sends/receives UMP packets in network order, i.e.
|
|
big-endian.
|
|
|
|
This function will ensure the correct byte ordering for a range of bytes that are about
|
|
to cross the API boundary to/from android.
|
|
*/
|
|
static void swapByteOrderOfWordsIfNecessary (Span<std::byte> bytes)
|
|
{
|
|
if ((bytes.size() % 4) != 0)
|
|
{
|
|
// We're expecting to receive 32-bit words, but the system hasn't given us a whole
|
|
// number of words.
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
#if JUCE_LITTLE_ENDIAN
|
|
// Words are in network order (big endian)
|
|
for (auto i = decltype (bytes.size()) {}; i < bytes.size(); i += sizeof (uint32_t))
|
|
std::reverse (bytes.data() + i, bytes.data() + i + sizeof (uint32_t));
|
|
#endif
|
|
}
|
|
|
|
/* The device info is all out-of-band. These are properties of the device that we can fetch
|
|
without needing to open a connection to the device.
|
|
This differs from a ump::Endpoint, which requires talking to the device in order to
|
|
retrieve some of the information.
|
|
We can still use this info to create a stand-in endpoint for a device that's only aware of
|
|
MIDI 1.0.
|
|
*/
|
|
struct StaticDeviceInfo
|
|
{
|
|
String name; ///< PROPERTY_NAME
|
|
String manufacturer; ///< PROPERTY_MANUFACTURER
|
|
String product; ///< PROPERTY_PRODUCT
|
|
String serialNumber; ///< PROPERTY_SERIAL_NUMBER
|
|
std::vector<String> dst; ///< input port names + port count
|
|
std::vector<String> src; ///< output port names + port count
|
|
jint id; ///< device id
|
|
jint transport; ///< transport, bytestream or ump
|
|
jint type; ///< type, virtual/usb/bluetooth
|
|
|
|
/* This returns the name used by old JUCE versions to identify a specific port */
|
|
String getLegacyId (size_t portIndex, ump::IOKind direction) const
|
|
{
|
|
jassert (portIndex < (direction == ump::IOKind::dst ? dst.size() : src.size()));
|
|
return String { ((id * 128) + jmin ((int) portIndex, 127)) * (direction == ump::IOKind::dst ? -1 : 1) };
|
|
}
|
|
|
|
std::array<String, 16> getIds (ump::IOKind dir) const
|
|
{
|
|
auto& names = dir == ump::IOKind::src ? src : dst;
|
|
jassert (names.size() <= 16);
|
|
|
|
std::array<String, 16> result;
|
|
|
|
for (size_t i = 0; i < names.size(); ++i)
|
|
result[i] = getLegacyId (i, dir);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Result does *not* have an EndpointId set */
|
|
ump::Endpoint getEquivalentBytestreamEndpoint() const
|
|
{
|
|
// This isn't a bytestream device!
|
|
jassert (transport == TRANSPORT_BYTESTREAM);
|
|
|
|
size_t numBlocks{};
|
|
std::array<ump::Block, 32> blocks;
|
|
|
|
for (auto [index, portName] : enumerate (dst, uint8_t{}))
|
|
{
|
|
blocks[numBlocks++] = ump::Block{}.withEnabled (true)
|
|
.withDirection (ump::BlockDirection::receiver)
|
|
.withUiHint (ump::BlockUiHint::receiver)
|
|
.withFirstGroup (index)
|
|
.withNumGroups (1)
|
|
.withMIDI1ProxyKind (ump::BlockMIDI1ProxyKind::unrestrictedBandwidth)
|
|
.withName (portName.isNotEmpty() ? portName : "Input Port " + String (index + 1));
|
|
}
|
|
|
|
for (auto [index, portName] : enumerate (src, uint8_t{}))
|
|
{
|
|
blocks[numBlocks++] = ump::Block{}.withEnabled (true)
|
|
.withDirection (ump::BlockDirection::sender)
|
|
.withUiHint (ump::BlockUiHint::sender)
|
|
.withFirstGroup (index)
|
|
.withNumGroups (1)
|
|
.withMIDI1ProxyKind (ump::BlockMIDI1ProxyKind::unrestrictedBandwidth)
|
|
.withName (portName.isNotEmpty() ? portName : "Output Port " + String (index + 1));
|
|
}
|
|
|
|
return ump::Endpoint{}.withName (name)
|
|
.withMidi1Support (true)
|
|
.withStaticBlocks (true)
|
|
.withProductInstanceId (serialNumber)
|
|
.withProtocol (ump::PacketProtocol::MIDI_1_0)
|
|
.withBlocks ({ blocks.data(), numBlocks });
|
|
}
|
|
|
|
ump::StaticDeviceInfo getUmpStaticDeviceInfo() const
|
|
{
|
|
return ump::StaticDeviceInfo{}.withName (name)
|
|
.withManufacturer (manufacturer)
|
|
.withProduct (product)
|
|
.withTransport (transport == TRANSPORT_UMP ? ump::Transport::ump
|
|
: ump::Transport::bytestream)
|
|
.withHasSource (! src.empty())
|
|
.withHasDestination (! dst.empty())
|
|
.withLegacyIdentifiersSrc (getIds (ump::IOKind::src))
|
|
.withLegacyIdentifiersDst (getIds (ump::IOKind::dst));
|
|
}
|
|
};
|
|
|
|
struct PortPath
|
|
{
|
|
int deviceID;
|
|
int portIndex;
|
|
int type;
|
|
};
|
|
|
|
class MidiReceiver
|
|
{
|
|
public:
|
|
MidiReceiver() = default;
|
|
explicit MidiReceiver (GlobalRef g) : port (g) {}
|
|
|
|
void send (jbyteArray byteArray, jint len)
|
|
{
|
|
getEnv()->CallVoidMethod (port, JavaMidiReceiver.send, byteArray, (jint) 0, len, (jlong) 0);
|
|
}
|
|
|
|
bool operator== (const MidiReceiver& other) const
|
|
{
|
|
return port == other.port;
|
|
}
|
|
|
|
bool operator!= (const MidiReceiver& other) const
|
|
{
|
|
return ! operator== (other);
|
|
}
|
|
|
|
bool operator< (const MidiReceiver& other) const
|
|
{
|
|
return port < other.port;
|
|
}
|
|
|
|
GlobalRef get() const { return port; }
|
|
|
|
private:
|
|
GlobalRef port;
|
|
};
|
|
|
|
/** Very low-level RAII wrapper over JUCE's Java MIDI port.
|
|
For simplicity we use the same interface for sources and destinations, so some functions
|
|
won't work depending on the actual type of the backing port.
|
|
This may deal in either bytestream or UMP messages depending on the result of isUMP().
|
|
*/
|
|
class MidiPort
|
|
{
|
|
public:
|
|
MidiPort() = default;
|
|
explicit MidiPort (GlobalRef p) : port (p) {}
|
|
|
|
MidiPort (MidiPort&& other) noexcept
|
|
: port (std::exchange (other.port, {}))
|
|
{
|
|
}
|
|
|
|
MidiPort& operator= (MidiPort&& other) noexcept
|
|
{
|
|
std::swap (other.port, port);
|
|
return *this;
|
|
}
|
|
|
|
~MidiPort() noexcept
|
|
{
|
|
if (port != nullptr)
|
|
getEnv()->CallVoidMethod (port, JuceMidiPort.close);
|
|
}
|
|
|
|
void start()
|
|
{
|
|
jassert (port != nullptr);
|
|
getEnv()->CallVoidMethod (port, JuceMidiPort.start);
|
|
}
|
|
|
|
void stop()
|
|
{
|
|
jassert (port != nullptr);
|
|
getEnv()->CallVoidMethod (port, JuceMidiPort.stop);
|
|
}
|
|
|
|
bool isActive() const
|
|
{
|
|
jassert (port != nullptr);
|
|
return getEnv()->CallBooleanMethod (port, JuceMidiPort.isActive);
|
|
}
|
|
|
|
void sendMidi (jbyteArray byteArray, jint len)
|
|
{
|
|
jassert (port != nullptr);
|
|
getEnv()->CallVoidMethod (port, JavaMidiReceiver.send, byteArray, (jint) 0, len, (jlong) 0);
|
|
}
|
|
|
|
std::optional<PortPath> getPortPath() const
|
|
{
|
|
auto* env = getEnv();
|
|
const LocalRef<jobject> result { env->CallObjectMethod (port, JuceMidiPort.getPortPath) };
|
|
|
|
if (result == nullptr)
|
|
return {};
|
|
|
|
return PortPath
|
|
{
|
|
env->GetIntField (result, JavaPortPath.deviceId),
|
|
env->GetIntField (result, JavaPortPath.portIndex),
|
|
env->GetIntField (result, JavaPortPath.type),
|
|
};
|
|
}
|
|
|
|
bool operator== (std::nullptr_t) const { return port == nullptr; }
|
|
bool operator!= (std::nullptr_t other) const { return ! operator== (other); }
|
|
|
|
bool operator== (const MidiPort& other) const { return port == other.port; }
|
|
bool operator!= (const MidiPort& other) const { return port != other.port; }
|
|
|
|
bool operator< (const MidiPort& other) const
|
|
{
|
|
return port.get() < other.port.get();
|
|
}
|
|
|
|
explicit operator bool() const { return operator!= (nullptr); }
|
|
|
|
GlobalRef get() const { return port; }
|
|
|
|
private:
|
|
GlobalRef port;
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
METHOD (start, "start", "()V") \
|
|
METHOD (stop, "stop", "()V") \
|
|
METHOD (close, "close", "()V") \
|
|
METHOD (isActive, "isActive", "()Z") \
|
|
METHOD (send, "send", "([BIIJ)V") \
|
|
METHOD (getPortPath, "getPortPath", "()Lcom/rmsl/juce/JuceMidiSupport$PortPath;") \
|
|
|
|
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiPort, "com/rmsl/juce/JuceMidiSupport$JuceMidiPort", 24, javaMidiByteCode)
|
|
#undef JNI_CLASS_MEMBERS
|
|
};
|
|
|
|
struct DeviceManagerListener
|
|
{
|
|
virtual ~DeviceManagerListener() = default;
|
|
virtual void deviceAdded (const StaticDeviceInfo&) = 0;
|
|
virtual void deviceRemoved (int) = 0;
|
|
};
|
|
|
|
/* Dispatches device-change events onto the main thread */
|
|
class DeviceListener : private AsyncUpdater
|
|
{
|
|
public:
|
|
~DeviceListener() override
|
|
{
|
|
cancelPendingUpdate();
|
|
}
|
|
|
|
void addListener (DeviceManagerListener& l)
|
|
{
|
|
listeners.add (&l);
|
|
}
|
|
|
|
void removeListener (DeviceManagerListener& l)
|
|
{
|
|
listeners.remove (&l);
|
|
}
|
|
|
|
void deviceAdded (const StaticDeviceInfo& info)
|
|
{
|
|
{
|
|
const std::scoped_lock lock { mutex };
|
|
events.push_back (info);
|
|
}
|
|
|
|
triggerAsyncUpdate();
|
|
}
|
|
|
|
void deviceRemoved (int id)
|
|
{
|
|
{
|
|
const std::scoped_lock lock { mutex };
|
|
events.push_back (id);
|
|
}
|
|
|
|
triggerAsyncUpdate();
|
|
}
|
|
|
|
private:
|
|
void handleAsyncUpdate() override
|
|
{
|
|
const auto copy = std::invoke ([&]
|
|
{
|
|
const std::scoped_lock lock { mutex };
|
|
auto result = events;
|
|
events.clear();
|
|
return result;
|
|
});
|
|
|
|
for (const auto& item : copy)
|
|
{
|
|
if (auto* x = std::get_if<StaticDeviceInfo> (&item))
|
|
listeners.call ([&] (auto& l) { l.deviceAdded (*x); });
|
|
|
|
if (auto* x = std::get_if<int> (&item))
|
|
listeners.call ([&] (auto& l) { l.deviceRemoved (*x); });
|
|
}
|
|
}
|
|
|
|
using Event = std::variant<StaticDeviceInfo, int>;
|
|
std::vector<Event> events;
|
|
std::mutex mutex;
|
|
|
|
ListenerList<DeviceManagerListener> listeners;
|
|
};
|
|
|
|
/** Provides notifications when a MIDI device is added/removed, and allows creating MidiPorts
|
|
referring to known device ports.
|
|
*/
|
|
class DeviceManager
|
|
{
|
|
public:
|
|
static std::shared_ptr<DeviceManager> getSingleton()
|
|
{
|
|
static std::weak_ptr<DeviceManager> weak;
|
|
|
|
if (auto strong = weak.lock())
|
|
return strong;
|
|
|
|
LocalRef<jobject> manager { getEnv()->CallStaticObjectMethod (JuceMidiSupport,
|
|
JuceMidiSupport.getAndroidMidiDeviceManager,
|
|
getAppContext().get()) };
|
|
|
|
if (manager == nullptr)
|
|
return {};
|
|
|
|
std::shared_ptr<DeviceManager> strong (new DeviceManager { GlobalRef { std::move (manager) } });
|
|
weak = strong;
|
|
return strong;
|
|
}
|
|
|
|
MidiPort openMidiInputPortWithID (jint deviceID, jint portIndex, jlong host) const
|
|
{
|
|
return MidiPort { GlobalRef { LocalRef<jobject> (getEnv()->CallObjectMethod (deviceManager,
|
|
MidiDeviceManager.openMidiInputPortWithID,
|
|
deviceID,
|
|
portIndex,
|
|
host)) } };
|
|
}
|
|
|
|
MidiPort openMidiOutputPortWithID (jint deviceID, jint portIndex) const
|
|
{
|
|
return MidiPort { GlobalRef { LocalRef<jobject> (getEnv()->CallObjectMethod (deviceManager,
|
|
MidiDeviceManager.openMidiOutputPortWithID,
|
|
deviceID,
|
|
portIndex)) } };
|
|
}
|
|
|
|
void addListener (DeviceManagerListener& l)
|
|
{
|
|
listener.addListener (l);
|
|
}
|
|
|
|
void removeListener (DeviceManagerListener& l)
|
|
{
|
|
listener.removeListener (l);
|
|
}
|
|
|
|
private:
|
|
explicit DeviceManager (GlobalRef g)
|
|
: deviceManager (g)
|
|
{
|
|
}
|
|
|
|
static std::vector<String> arrayListStringToVector (JNIEnv* env, jobject obj)
|
|
{
|
|
const auto size = env->CallIntMethod (obj, JavaArrayList.size);
|
|
std::vector<String> result;
|
|
result.reserve ((size_t) size);
|
|
|
|
for (jint i = 0; i < size; ++i)
|
|
result.push_back (juceString ((jstring) env->CallObjectMethod (obj, JavaArrayList.get, i)));
|
|
|
|
return result;
|
|
}
|
|
|
|
static void handleDeviceAdded (JNIEnv* env, jclass, jobject obj)
|
|
{
|
|
if (obj == nullptr)
|
|
return;
|
|
|
|
StaticDeviceInfo info;
|
|
info.name = juceString ((jstring) env->GetObjectField (obj, JuceMidiDeviceInfo.name));
|
|
info.manufacturer = juceString ((jstring) env->GetObjectField (obj, JuceMidiDeviceInfo.manufacturer));
|
|
info.serialNumber = juceString ((jstring) env->GetObjectField (obj, JuceMidiDeviceInfo.serialNumber));
|
|
info.product = juceString ((jstring) env->GetObjectField (obj, JuceMidiDeviceInfo.product));
|
|
|
|
info.id = env->GetIntField (obj, JuceMidiDeviceInfo.id);
|
|
info.transport = env->GetIntField (obj, JuceMidiDeviceInfo.transport);
|
|
info.type = env->GetIntField (obj, JuceMidiDeviceInfo.type);
|
|
|
|
info.src = arrayListStringToVector (env, env->GetObjectField (obj, JuceMidiDeviceInfo.src));
|
|
info.dst = arrayListStringToVector (env, env->GetObjectField (obj, JuceMidiDeviceInfo.dst));
|
|
|
|
if (auto strong = getSingleton())
|
|
strong->listener.deviceAdded (info);
|
|
}
|
|
|
|
static void handleDeviceRemoved (JNIEnv*, jclass, jint id)
|
|
{
|
|
if (auto strong = getSingleton())
|
|
strong->listener.deviceRemoved (id);
|
|
}
|
|
|
|
DeviceListener listener;
|
|
GlobalRef deviceManager;
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
CALLBACK (handleDeviceAdded, "handleDeviceAdded", "(Lcom/rmsl/juce/JuceMidiSupport$JuceMidiDeviceInfo;)V") \
|
|
CALLBACK (handleDeviceRemoved, "handleDeviceRemoved", "(I)V" ) \
|
|
STATICMETHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$MidiDeviceManager;") \
|
|
STATICMETHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "(Landroid/content/Context;)Lcom/rmsl/juce/JuceMidiSupport$BluetoothMidiManager;") \
|
|
|
|
DECLARE_JNI_CLASS_WITH_BYTECODE (JuceMidiSupport, "com/rmsl/juce/JuceMidiSupport", 24, javaMidiByteCode)
|
|
#undef JNI_CLASS_MEMBERS
|
|
};
|
|
|
|
/* A source of MIDI messages.
|
|
This converts bytestream to UMP if necessary, but performs no other conversions.
|
|
*/
|
|
class MidiSource
|
|
{
|
|
public:
|
|
MidiSource (ump::Consumer& c, uint8_t portIndex, bool isUmpIn)
|
|
: consumer (c),
|
|
bytestreamDispatcher { portIndex, ump::PacketProtocol::MIDI_1_0, 4096 },
|
|
isUmp (isUmpIn)
|
|
{
|
|
}
|
|
|
|
private:
|
|
void handleInput (JNIEnv* env, jbyteArray byteArray, jint offset, jint len, jlong timestamp)
|
|
{
|
|
jassert (byteArray != nullptr);
|
|
thread_local std::vector<std::byte> buffer (4096);
|
|
|
|
{
|
|
auto* data = env->GetByteArrayElements (byteArray, nullptr);
|
|
auto* bytes = reinterpret_cast<const std::byte*> (data + offset);
|
|
|
|
buffer.resize ((size_t) len);
|
|
std::copy (bytes, bytes + len, buffer.begin());
|
|
|
|
env->ReleaseByteArrayElements (byteArray, data, 0);
|
|
}
|
|
|
|
const auto time = convertNativeTimestamp (timestamp);
|
|
|
|
const auto send = [this] (const ump::View& view, double t)
|
|
{
|
|
const ump::Iterator b { view.data(), view.size() };
|
|
consumer.consume (b, std::next (b), t);
|
|
};
|
|
|
|
if (isUmp)
|
|
{
|
|
swapByteOrderOfWordsIfNecessary (buffer);
|
|
|
|
const auto words = reinterpret_cast<const uint32_t*> (buffer.data());
|
|
const auto numWords = buffer.size() / sizeof (uint32_t);
|
|
|
|
umpDispatcher.dispatch ({ words, numWords }, time, send);
|
|
}
|
|
else
|
|
{
|
|
bytestreamDispatcher.dispatch (buffer, time, send);
|
|
}
|
|
}
|
|
|
|
/* Native timestamps are based on System.nanoTime
|
|
JUCE timestamps use Time::getMillisecondCounter
|
|
*/
|
|
double convertNativeTimestamp (jlong absoluteNanos) const
|
|
{
|
|
const auto millis = std::invoke ([&]
|
|
{
|
|
// A timestamp of 0 means ASAP
|
|
if (absoluteNanos == 0)
|
|
return (double) Time::getMillisecondCounter();
|
|
|
|
const auto elapsedNanos = absoluteNanos - startTimeNative;
|
|
const auto elapsedMillis = elapsedNanos / (jlong) 1e6;
|
|
return (double) (startTimeMillis + elapsedMillis);
|
|
});
|
|
|
|
return millis * 0.001;
|
|
}
|
|
|
|
static void handleReceive (JNIEnv* env,
|
|
MidiSource& myself,
|
|
jbyteArray byteArray,
|
|
jint offset,
|
|
jint len,
|
|
jlong timestamp)
|
|
{
|
|
myself.handleInput (env, byteArray, offset, len, timestamp);
|
|
}
|
|
|
|
ump::Consumer& consumer;
|
|
ump::Dispatcher umpDispatcher;
|
|
ump::BytestreamToUMPDispatcher bytestreamDispatcher;
|
|
|
|
const jlong startTimeNative = midiClockMillis (getEnv());
|
|
const uint32 startTimeMillis = Time::getMillisecondCounter();
|
|
|
|
bool isUmp;
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
CALLBACK (generatedCallback<&MidiSource::handleReceive>, "handleReceive", "(J[BIIJ)V")
|
|
|
|
DECLARE_JNI_CLASS (JuceMidiInputPort, "com/rmsl/juce/JuceMidiSupport$NativeMidiReceiver")
|
|
#undef JNI_CLASS_MEMBERS
|
|
};
|
|
|
|
/* A destination for MIDI messages.
|
|
This converts UMP to bytestream if necessary, but performs no other conversions.
|
|
If the destination is using UMP transport, this won't convert to the target's preferred protocol.
|
|
The connection is exclusive, so this should be shared between all entities that want to
|
|
concurrently send MIDI to this destination.
|
|
*/
|
|
class MidiDestination
|
|
{
|
|
public:
|
|
static std::unique_ptr<MidiDestination> make (MidiReceiver outputReceiver, bool isUmp)
|
|
{
|
|
if (outputReceiver == MidiReceiver{})
|
|
return {};
|
|
|
|
auto result = rawToUniquePtr (new MidiDestination);
|
|
result->outputReceiver = std::move (outputReceiver);
|
|
result->isUMP = isUmp;
|
|
return result;
|
|
}
|
|
|
|
bool send (ump::View v)
|
|
{
|
|
ump::Iterator b { v.data(), v.size() };
|
|
return send (b, std::next (b));
|
|
}
|
|
|
|
bool send (ump::Iterator b, ump::Iterator e)
|
|
{
|
|
if (outputReceiver == MidiReceiver{})
|
|
return false;
|
|
|
|
if (isUMP)
|
|
{
|
|
auto* env = getEnv();
|
|
const auto messageSize = size_t (e->data() - b->data()) * sizeof (uint32_t);
|
|
|
|
LocalRef<jbyteArray> messageContent (env->NewByteArray ((jsize) messageSize));
|
|
|
|
auto* rawBytes = env->GetByteArrayElements (messageContent.get(), nullptr);
|
|
std::memcpy (rawBytes, b->data(), static_cast<size_t> (messageSize));
|
|
|
|
swapByteOrderOfWordsIfNecessary (Span (unalignedPointerCast<std::byte*> (rawBytes), messageSize));
|
|
|
|
env->ReleaseByteArrayElements (messageContent.get(), rawBytes, 0);
|
|
|
|
outputReceiver.send (messageContent.get(), (jint) messageSize);
|
|
}
|
|
else
|
|
{
|
|
for (auto v : makeRange (b, e))
|
|
{
|
|
toBytestream.convert (v, 0.0, [&] (ump::BytesOnGroup bytesView, double)
|
|
{
|
|
auto *env = getEnv();
|
|
const auto messageSize = (jsize) bytesView.bytes.size();
|
|
|
|
LocalRef<jbyteArray> messageContent (env->NewByteArray (messageSize));
|
|
|
|
auto *rawBytes = env->GetByteArrayElements (messageContent.get(), nullptr);
|
|
std::memcpy (rawBytes, bytesView.bytes.data(), static_cast<size_t> (messageSize));
|
|
env->ReleaseByteArrayElements (messageContent.get(), rawBytes, 0);
|
|
|
|
outputReceiver.send (messageContent.get(), messageSize);
|
|
});
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
ump::ToBytestreamConverter toBytestream { 4096 };
|
|
MidiReceiver outputReceiver;
|
|
bool isUMP;
|
|
};
|
|
|
|
class ConverterWithOptionalProtocol
|
|
{
|
|
public:
|
|
void setProtocol (std::optional<ump::PacketProtocol> newProtocol)
|
|
{
|
|
const auto previousProtocol = std::invoke ([&]
|
|
{
|
|
const SpinLock::ScopedLockType lock { mutex };
|
|
return converter.has_value() ? std::make_optional (converter->getProtocol()) : std::nullopt;
|
|
});
|
|
|
|
if (newProtocol == previousProtocol)
|
|
return;
|
|
|
|
const SpinLock::ScopedLockType lock { mutex };
|
|
|
|
if (newProtocol.has_value())
|
|
converter.emplace (*newProtocol);
|
|
else
|
|
converter.reset();
|
|
}
|
|
|
|
template <typename Callback>
|
|
bool convert (ump::Iterator b, ump::Iterator e, Callback&& callback)
|
|
{
|
|
SpinLock::ScopedTryLockType lock { mutex };
|
|
|
|
if (! lock.isLocked())
|
|
{
|
|
// There's contention on the output lock!
|
|
jassertfalse;
|
|
return false;
|
|
}
|
|
|
|
if (! converter.has_value())
|
|
{
|
|
bool allSent = true;
|
|
|
|
// filter out channel voice messages
|
|
for (const auto& v : makeRange (b, e))
|
|
{
|
|
const auto kind = ump::Utils::getMessageType (v[0]);
|
|
|
|
if (kind == ump::Utils::MessageKind::channelVoice1 || kind == ump::Utils::MessageKind::channelVoice2)
|
|
{
|
|
// Dropping this packet because we don't know whether the endpoint is prepared to receive it
|
|
allSent = false;
|
|
continue;
|
|
}
|
|
|
|
const ump::Iterator it { v.data(), v.size() };
|
|
allSent &= callback (it, std::next (it));
|
|
}
|
|
|
|
return allSent;
|
|
}
|
|
|
|
bool allSent = true;
|
|
|
|
converter->convert (b, e, [&] (ump::View v)
|
|
{
|
|
const ump::Iterator it { v.data(), v.size() };
|
|
allSent &= callback (it, std::next (it));
|
|
});
|
|
|
|
return allSent;
|
|
}
|
|
|
|
private:
|
|
SpinLock mutex;
|
|
std::optional<ump::GenericUMPConverter> converter;
|
|
};
|
|
|
|
struct Connection
|
|
{
|
|
virtual ~Connection() = default;
|
|
|
|
virtual ump::EndpointId getEndpointId() const = 0;
|
|
virtual std::optional<ump::Endpoint> getEndpoint() const = 0;
|
|
virtual ump::StaticDeviceInfo getStaticDeviceInfo() const = 0;
|
|
virtual bool send (ump::Iterator, ump::Iterator) = 0;
|
|
|
|
virtual void addConsumer (ump::Consumer&) = 0;
|
|
virtual void removeConsumer (ump::Consumer&) = 0;
|
|
|
|
virtual void disconnected() = 0;
|
|
virtual void addDisconnectionListener (ump::DisconnectionListener&) = 0;
|
|
virtual void removeDisconnectionListener (ump::DisconnectionListener&) = 0;
|
|
};
|
|
|
|
class VirtualPortConnection : public Connection,
|
|
private ump::Consumer
|
|
{
|
|
public:
|
|
static std::unique_ptr<VirtualPortConnection> make (MidiReceiver outputReceiver, bool isUmp)
|
|
{
|
|
auto result = rawToUniquePtr (new VirtualPortConnection);
|
|
|
|
result->src = std::make_unique<MidiSource> (static_cast<ump::Consumer&> (*result), 0, isUmp);
|
|
result->dst = MidiDestination::make (std::move (outputReceiver), isUmp);
|
|
result->isUmp = isUmp;
|
|
|
|
if (result->src == nullptr && result->dst == nullptr)
|
|
return {};
|
|
|
|
return result;
|
|
}
|
|
|
|
ump::EndpointId getEndpointId() const override
|
|
{
|
|
const String v = "VIRTUAL " + String (isUmp ? "UMP" : "BYTESTREAM");
|
|
return ump::EndpointId::makeSrcDst (v, v);
|
|
}
|
|
|
|
std::optional<ump::Endpoint> getEndpoint() const override
|
|
{
|
|
return endpoint;
|
|
}
|
|
|
|
ump::StaticDeviceInfo getStaticDeviceInfo() const override
|
|
{
|
|
return ump::StaticDeviceInfo{}.withTransport (isUmp ? ump::Transport::ump : ump::Transport::bytestream)
|
|
.withHasSource (true)
|
|
.withHasDestination (true);
|
|
}
|
|
|
|
void setEndpoint (std::optional<ump::Endpoint> ep)
|
|
{
|
|
endpoint = std::move (ep);
|
|
converter.setProtocol (endpoint.has_value() ? endpoint->getProtocol() : std::nullopt);
|
|
}
|
|
|
|
jlong getHost() const
|
|
{
|
|
if (src != nullptr)
|
|
return (jlong) src.get();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void addConsumer (ump::Consumer& x) override
|
|
{
|
|
consumers.add (x);
|
|
}
|
|
|
|
void removeConsumer (ump::Consumer& x) override
|
|
{
|
|
consumers.remove (x);
|
|
}
|
|
|
|
void disconnected() override
|
|
{
|
|
disconnectListeners.call ([] (auto& x) { x.disconnected(); });
|
|
}
|
|
|
|
void addDisconnectionListener (ump::DisconnectionListener& x) override
|
|
{
|
|
disconnectListeners.add (&x);
|
|
}
|
|
|
|
void removeDisconnectionListener (ump::DisconnectionListener& x) override
|
|
{
|
|
disconnectListeners.remove (&x);
|
|
}
|
|
|
|
bool send (ump::Iterator b, ump::Iterator e) override
|
|
{
|
|
if (dst != nullptr)
|
|
return converter.convert (b, e, [&] (auto&&... args) { return dst->send (args...); });
|
|
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
void consume (ump::Iterator b, ump::Iterator e, double time) override
|
|
{
|
|
consumers.call ([&] (auto& x) { x.consume (b, e, time); });
|
|
}
|
|
|
|
std::optional<ump::Endpoint> endpoint;
|
|
WaitFreeListeners<Consumer> consumers;
|
|
ConverterWithOptionalProtocol converter;
|
|
ListenerList<ump::DisconnectionListener> disconnectListeners;
|
|
std::unique_ptr<MidiSource> src;
|
|
std::unique_ptr<MidiDestination> dst;
|
|
bool isUmp = false;
|
|
};
|
|
|
|
class VirtualEndpointImplNative : public ump::DisconnectionListener,
|
|
private ump::Consumer
|
|
{
|
|
public:
|
|
static std::unique_ptr<VirtualEndpointImplNative> make (const String& name,
|
|
const ump::DeviceInfo& info,
|
|
const String& instanceId,
|
|
ump::PacketProtocol protocol,
|
|
Span<const ump::Block> blocks,
|
|
ump::BlocksAreStatic blocksAreStatic,
|
|
std::shared_ptr<VirtualPortConnection> connection)
|
|
{
|
|
if (connection == nullptr)
|
|
return {};
|
|
|
|
return rawToUniquePtr (new VirtualEndpointImplNative (name,
|
|
info,
|
|
instanceId,
|
|
protocol,
|
|
blocks,
|
|
blocksAreStatic,
|
|
connection));
|
|
}
|
|
|
|
~VirtualEndpointImplNative() override
|
|
{
|
|
if (port != nullptr)
|
|
{
|
|
port->setEndpoint ({});
|
|
port->removeConsumer (*this);
|
|
}
|
|
}
|
|
|
|
ump::EndpointId getId() const
|
|
{
|
|
return port->getEndpointId();
|
|
}
|
|
|
|
bool setBlock (uint8_t index, const ump::Block& b)
|
|
{
|
|
if (port == nullptr)
|
|
return false;
|
|
|
|
const auto ep = port->getEndpoint();
|
|
|
|
if (! ep.has_value())
|
|
return false;
|
|
|
|
auto endpoint = *ep;
|
|
|
|
if (index >= endpoint.getBlocks().size())
|
|
return false;
|
|
|
|
const auto old = std::exchange (endpoint.getBlocks()[index], b);
|
|
|
|
if (! b.nameMatches (old))
|
|
sendFunctionBlockNameNotification (index);
|
|
|
|
if (! b.infoMatches (old))
|
|
sendFunctionBlockInfoNotification (index);
|
|
|
|
port->setEndpoint (std::move (endpoint));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool setName (const String& name)
|
|
{
|
|
if (port == nullptr)
|
|
return false;
|
|
|
|
// Per the spec, there's a max length of 98 bytes for endpoint names
|
|
if (name.getNumBytesAsUTF8() > 98)
|
|
return false;
|
|
|
|
const auto ep = port->getEndpoint();
|
|
|
|
if (! ep.has_value())
|
|
return false;
|
|
|
|
auto endpoint = *ep;
|
|
|
|
if (name == endpoint.getName())
|
|
return true;
|
|
|
|
port->setEndpoint (endpoint.withName (name));
|
|
sendEndpointNameNotification();
|
|
|
|
return true;
|
|
}
|
|
|
|
void disconnected() override
|
|
{
|
|
port = nullptr;
|
|
}
|
|
|
|
private:
|
|
VirtualEndpointImplNative (const String& name,
|
|
const ump::DeviceInfo& info,
|
|
const String& instanceId,
|
|
ump::PacketProtocol protocol,
|
|
Span<const ump::Block> blocks,
|
|
ump::BlocksAreStatic blocksAreStatic,
|
|
std::shared_ptr<VirtualPortConnection> c)
|
|
: port (std::move (c))
|
|
{
|
|
port->addConsumer (*this);
|
|
port->setEndpoint (makeEndpoint (name, info, instanceId, protocol, blocks, blocksAreStatic));
|
|
|
|
processEndpointDiscovery (ump::View { ump::Factory::makeEndpointDiscovery (1, 1, std::byte { 0x1f }).data() });
|
|
processFunctionBlockDiscovery (ump::View { ump::Factory::makeFunctionBlockDiscovery (0xff, std::byte { 0x03 }).data() });
|
|
}
|
|
|
|
void consume (ump::Iterator b, ump::Iterator e, double) override
|
|
{
|
|
for (auto v : makeRange (b, e))
|
|
processPacket (v);
|
|
}
|
|
|
|
void processPacket (ump::View v)
|
|
{
|
|
const auto kind = ump::Utils::getMessageType (v[0]);
|
|
|
|
if (kind != ump::Utils::MessageKind::stream)
|
|
return;
|
|
|
|
const auto status = ump::Utils::U8<1>::get (v[0]);
|
|
|
|
switch (status)
|
|
{
|
|
case 0x0:
|
|
processEndpointDiscovery (v);
|
|
break;
|
|
|
|
case 0x5:
|
|
processStreamConfigRequest (v);
|
|
break;
|
|
|
|
case 0x10:
|
|
processFunctionBlockDiscovery (v);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void processEndpointDiscovery (ump::View v)
|
|
{
|
|
const auto filterBitmap = ump::Utils::U8<3>::get (v[1]);
|
|
|
|
if ((filterBitmap & 0x01) != 0)
|
|
sendEndpointInfoNotification();
|
|
|
|
if ((filterBitmap & 0x02) != 0)
|
|
sendDeviceIdentityNotification();
|
|
|
|
if ((filterBitmap & 0x04) != 0)
|
|
sendEndpointNameNotification();
|
|
|
|
if ((filterBitmap & 0x08) != 0)
|
|
sendProductInstanceIdNotification();
|
|
|
|
if ((filterBitmap & 0x10) != 0)
|
|
sendStreamConfigurationNotification();
|
|
}
|
|
|
|
void processStreamConfigRequest (ump::View)
|
|
{
|
|
// Currently we don't support changing stream configuration, so just reply with
|
|
// the current configuration.
|
|
sendStreamConfigurationNotification();
|
|
}
|
|
|
|
void processFunctionBlockDiscovery (ump::View v)
|
|
{
|
|
const auto blockNumber = ump::Utils::U8<2>::get (v[0]);
|
|
const auto filterBitmap = ump::Utils::U8<3>::get (v[0]);
|
|
|
|
const auto sendFunctionBlockInfo = [this, filterBitmap] (auto index)
|
|
{
|
|
if ((filterBitmap & 0x01) != 0)
|
|
sendFunctionBlockInfoNotification (index);
|
|
|
|
if ((filterBitmap & 0x02) != 0)
|
|
sendFunctionBlockNameNotification (index);
|
|
};
|
|
|
|
const auto endpoint = port->getEndpoint();
|
|
|
|
if (! endpoint.has_value())
|
|
return;
|
|
|
|
if (blockNumber < endpoint->getBlocks().size())
|
|
{
|
|
sendFunctionBlockInfo (blockNumber);
|
|
}
|
|
else if (blockNumber == 0xff)
|
|
{
|
|
for (uint8_t i = 0; i < endpoint->getBlocks().size(); ++i)
|
|
sendFunctionBlockInfo (i);
|
|
}
|
|
}
|
|
|
|
void sendFunctionBlockNameNotification (uint8_t index)
|
|
{
|
|
const auto endpoint = port->getEndpoint();
|
|
|
|
if (! endpoint.has_value())
|
|
return;
|
|
|
|
if (index >= endpoint->getBlocks().size())
|
|
{
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
ump::Factory::makeFunctionBlockNameNotification (index, endpoint->getBlocks()[index].getName(), [this] (auto v)
|
|
{
|
|
const ump::Iterator iterator { v.data(), v.size() };
|
|
port->send (iterator, std::next (iterator));
|
|
});
|
|
}
|
|
|
|
void sendFunctionBlockInfoNotification (uint8_t index)
|
|
{
|
|
const auto endpoint = port->getEndpoint();
|
|
|
|
if (! endpoint.has_value())
|
|
return;
|
|
|
|
if (index >= endpoint->getBlocks().size())
|
|
{
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
const auto packet = ump::Factory::makeFunctionBlockInfoNotification (index, endpoint->getBlocks()[index].getInfo());
|
|
const ump::Iterator iterator { packet.data(), packet.size() };
|
|
port->send (iterator, std::next (iterator));
|
|
}
|
|
|
|
void sendEndpointNameNotification()
|
|
{
|
|
const auto endpoint = port->getEndpoint();
|
|
|
|
if (! endpoint.has_value())
|
|
return;
|
|
|
|
ump::Factory::makeEndpointNameNotification (endpoint->getName(), [this] (auto v)
|
|
{
|
|
const ump::Iterator iterator { v.data(), v.size() };
|
|
port->send (iterator, std::next (iterator));
|
|
});
|
|
}
|
|
|
|
void sendDeviceIdentityNotification()
|
|
{
|
|
const auto endpoint = port->getEndpoint();
|
|
|
|
if (! endpoint.has_value())
|
|
return;
|
|
|
|
const auto packet = ump::Factory::makeDeviceIdentityNotification (endpoint->getDeviceInfo());
|
|
const ump::Iterator iterator { packet.data(), packet.size() };
|
|
port->send (iterator, std::next (iterator));
|
|
}
|
|
|
|
void sendEndpointInfoNotification()
|
|
{
|
|
const auto ep = port->getEndpoint();
|
|
|
|
if (! ep.has_value())
|
|
return;
|
|
|
|
auto& endpoint = *ep;
|
|
|
|
const auto info = ump::EndpointInfo{}.withMidi1Support (endpoint.hasMidi1Support())
|
|
.withMidi2Support (endpoint.hasMidi2Support())
|
|
.withNumFunctionBlocks ((uint8_t) endpoint.getBlocks().size())
|
|
.withReceiveJRSupport (endpoint.hasReceiveJRSupport())
|
|
.withTransmitJRSupport (endpoint.hasTransmitJRSupport())
|
|
.withStaticFunctionBlocks (endpoint.hasStaticBlocks())
|
|
.withVersion (endpoint.getUMPVersionMajor(), endpoint.getUMPVersionMinor());
|
|
const auto packet = ump::Factory::makeEndpointInfoNotification (info);
|
|
const ump::Iterator iterator { packet.data(), packet.size() };
|
|
port->send (iterator, std::next (iterator));
|
|
}
|
|
|
|
void sendProductInstanceIdNotification()
|
|
{
|
|
const auto endpoint = port->getEndpoint();
|
|
|
|
if (! endpoint.has_value())
|
|
return;
|
|
|
|
ump::Factory::makeProductInstanceIdNotification (endpoint->getProductInstanceId(), [this] (auto v)
|
|
{
|
|
const ump::Iterator iterator { v.data(), v.size() };
|
|
port->send (iterator, std::next (iterator));
|
|
});
|
|
}
|
|
|
|
void sendStreamConfigurationNotification()
|
|
{
|
|
const auto ep = port->getEndpoint();
|
|
|
|
if (! ep.has_value())
|
|
return;
|
|
|
|
auto& endpoint = *ep;
|
|
const auto protocol = endpoint.getProtocol();
|
|
|
|
if (! protocol.has_value())
|
|
{
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
const auto info = ump::StreamConfiguration{}.withProtocol (*protocol)
|
|
.withReceiveTimestamp (endpoint.isReceiveJREnabled())
|
|
.withTransmitTimestamp (endpoint.isTransmitJREnabled());
|
|
const auto packet = ump::Factory::makeStreamConfigurationNotification (info);
|
|
const ump::Iterator iterator { packet.data(), packet.size() };
|
|
port->send (iterator, std::next (iterator));
|
|
}
|
|
|
|
static ump::Endpoint makeEndpoint (const String& name,
|
|
const ump::DeviceInfo& info,
|
|
const String& instanceId,
|
|
ump::PacketProtocol protocol,
|
|
Span<const ump::Block> blocks,
|
|
ump::BlocksAreStatic blocksAreStatic)
|
|
{
|
|
return ump::Endpoint{}.withName (name)
|
|
.withDeviceInfo (info)
|
|
.withProductInstanceId (instanceId)
|
|
.withProtocol (protocol)
|
|
.withMidi1Support (protocol == ump::PacketProtocol::MIDI_1_0)
|
|
.withMidi2Support (protocol == ump::PacketProtocol::MIDI_2_0)
|
|
.withUMPVersion (1, 1)
|
|
.withBlocks (blocks)
|
|
.withStaticBlocks (blocksAreStatic == ump::BlocksAreStatic::yes);
|
|
}
|
|
|
|
std::shared_ptr<VirtualPortConnection> port;
|
|
};
|
|
|
|
class LegacyVirtualPortImplNative : public ump::DisconnectionListener
|
|
{
|
|
public:
|
|
static std::unique_ptr<LegacyVirtualPortImplNative> make (std::shared_ptr<VirtualPortConnection> x)
|
|
{
|
|
if (x == nullptr)
|
|
return {};
|
|
|
|
return rawToUniquePtr (new LegacyVirtualPortImplNative { x });
|
|
}
|
|
|
|
ump::EndpointId getId() const
|
|
{
|
|
return port->getEndpointId();
|
|
}
|
|
|
|
void disconnected() override
|
|
{
|
|
port = nullptr;
|
|
}
|
|
|
|
private:
|
|
explicit LegacyVirtualPortImplNative (std::shared_ptr<VirtualPortConnection> x)
|
|
: port (std::move (x)) {}
|
|
|
|
std::shared_ptr<VirtualPortConnection> port;
|
|
};
|
|
|
|
struct VirtualMidiPortManagerListener
|
|
{
|
|
virtual ~VirtualMidiPortManagerListener() = default;
|
|
virtual void virtualConnectionAdded() = 0;
|
|
virtual void virtualConnectionRemoved (std::shared_ptr<Connection>) = 0;
|
|
};
|
|
|
|
class VirtualMidiPortManager
|
|
{
|
|
public:
|
|
static VirtualMidiPortManager& getSingleton()
|
|
{
|
|
static VirtualMidiPortManager result;
|
|
return result;
|
|
}
|
|
|
|
std::shared_ptr<VirtualEndpointImplNative> createNativeVirtualEndpoint (const String& name,
|
|
const ump::DeviceInfo& info,
|
|
const String& productInstance,
|
|
ump::PacketProtocol protocol,
|
|
Span<const ump::Block> blocks,
|
|
ump::BlocksAreStatic blocksAreStatic)
|
|
{
|
|
if (! isVirtualUmpAvailable())
|
|
return {};
|
|
|
|
if (umpPort == nullptr)
|
|
{
|
|
// The virtual port is not available, maybe the service hasn't started yet
|
|
return {};
|
|
}
|
|
|
|
if (umpConnection.lock() != nullptr)
|
|
{
|
|
// The virtual port was already claimed, perhaps you called createNativeVirtualEndpoint several times
|
|
jassertfalse;
|
|
return {};
|
|
}
|
|
|
|
std::shared_ptr result = VirtualEndpointImplNative::make (name,
|
|
info,
|
|
productInstance,
|
|
protocol,
|
|
blocks,
|
|
blocksAreStatic,
|
|
umpPort);
|
|
|
|
umpConnection = result;
|
|
return result;
|
|
}
|
|
|
|
std::shared_ptr<LegacyVirtualPortImplNative> createLegacyVirtualInput()
|
|
{
|
|
if (! isVirtualBytestreamAvailable())
|
|
return {};
|
|
|
|
if (bytestreamPort == nullptr)
|
|
{
|
|
// The virtual port is not available, maybe the service hasn't started yet
|
|
return {};
|
|
}
|
|
|
|
if (srcConnection.lock() != nullptr)
|
|
{
|
|
// The virtual port was already claimed, perhaps you called createNativeVirtualEndpoint several times
|
|
jassertfalse;
|
|
return {};
|
|
}
|
|
|
|
std::shared_ptr result = LegacyVirtualPortImplNative::make (bytestreamPort);
|
|
srcConnection = result;
|
|
return result;
|
|
}
|
|
|
|
std::shared_ptr<LegacyVirtualPortImplNative> createLegacyVirtualOutput()
|
|
{
|
|
if (! isVirtualBytestreamAvailable())
|
|
return {};
|
|
|
|
if (bytestreamPort == nullptr)
|
|
{
|
|
// The virtual port is not available, maybe the service hasn't started yet
|
|
return {};
|
|
}
|
|
|
|
if (dstConnection.lock() != nullptr)
|
|
{
|
|
// The virtual port was already claimed, perhaps you called createNativeVirtualEndpoint several times
|
|
jassertfalse;
|
|
return {};
|
|
}
|
|
|
|
std::shared_ptr result = LegacyVirtualPortImplNative::make (bytestreamPort);
|
|
dstConnection = result;
|
|
return result;
|
|
}
|
|
|
|
void addListener (VirtualMidiPortManagerListener& x)
|
|
{
|
|
listeners.add (&x);
|
|
}
|
|
|
|
void removeListener (VirtualMidiPortManagerListener& x)
|
|
{
|
|
listeners.remove (&x);
|
|
}
|
|
|
|
bool isBytestreamPortAvailable() const
|
|
{
|
|
return bytestreamPort != nullptr;
|
|
}
|
|
|
|
bool isUmpPortAvailable() const
|
|
{
|
|
return umpPort != nullptr;
|
|
}
|
|
|
|
std::shared_ptr<VirtualPortConnection> getBytestreamConnection() const
|
|
{
|
|
return bytestreamPort;
|
|
}
|
|
|
|
std::shared_ptr<VirtualPortConnection> getUmpConnection() const
|
|
{
|
|
return umpPort;
|
|
}
|
|
|
|
static void setVirtualMidiBytestreamEnabled (bool x)
|
|
{
|
|
if (VirtualMidiServices != nullptr)
|
|
{
|
|
getEnv()->CallStaticVoidMethod (VirtualMidiServices,
|
|
VirtualMidiServices.setVirtualMidiBytestreamEnabled,
|
|
getAppContext().get(),
|
|
(jboolean) x);
|
|
}
|
|
}
|
|
|
|
static void setVirtualMidiUmpEnabled (bool x)
|
|
{
|
|
if (VirtualMidiServices != nullptr)
|
|
{
|
|
getEnv()->CallStaticVoidMethod (VirtualMidiServices,
|
|
VirtualMidiServices.setVirtualMidiUmpEnabled,
|
|
getAppContext().get(),
|
|
(jboolean) x);
|
|
}
|
|
}
|
|
|
|
private:
|
|
VirtualMidiPortManager() = default;
|
|
|
|
static bool isVirtualBytestreamAvailable()
|
|
{
|
|
if (VirtualMidiServices != nullptr)
|
|
{
|
|
return getEnv()->CallStaticBooleanMethod (VirtualMidiServices,
|
|
VirtualMidiServices.isVirtualBytestreamAvailable,
|
|
getAppContext().get());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool isVirtualUmpAvailable()
|
|
{
|
|
if (VirtualMidiServices != nullptr)
|
|
{
|
|
return getEnv()->CallStaticBooleanMethod (VirtualMidiServices,
|
|
VirtualMidiServices.isVirtualUmpAvailable,
|
|
getAppContext().get());
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
jobject addPort (JNIEnv* env, jobject newReceiver, jboolean isUmp)
|
|
{
|
|
std::shared_ptr port = VirtualPortConnection::make (MidiReceiver { GlobalRef { LocalRef { newReceiver } } }, isUmp);
|
|
|
|
if (port == nullptr)
|
|
return {};
|
|
|
|
auto* result = env->NewObject (NativeMidiReceiver, NativeMidiReceiver.constructor, port->getHost());
|
|
MidiReceiver key { GlobalRef { LocalRef { env->NewLocalRef (result) } } };
|
|
(isUmp ? umpPort : bytestreamPort) = port;
|
|
(isUmp ? umpReceiver : bytestreamReceiver) = key;
|
|
|
|
if (! isUmp)
|
|
{
|
|
StaticDeviceInfo info;
|
|
info.dst = { "" };
|
|
info.src = { "" };
|
|
info.transport = TRANSPORT_BYTESTREAM;
|
|
port->setEndpoint (info.getEquivalentBytestreamEndpoint());
|
|
}
|
|
|
|
listeners.call ([&] (auto& x) { x.virtualConnectionAdded(); });
|
|
return result;
|
|
}
|
|
|
|
void removePort (JNIEnv* env, jobject previouslyAdded)
|
|
{
|
|
MidiReceiver key { GlobalRef { LocalRef { env->NewLocalRef (previouslyAdded) } } };
|
|
|
|
const auto connection = std::invoke ([&]() -> std::shared_ptr<Connection>
|
|
{
|
|
if (key == umpReceiver)
|
|
return umpPort;
|
|
|
|
if (key == bytestreamReceiver)
|
|
return bytestreamPort;
|
|
|
|
return {};
|
|
});
|
|
|
|
if (connection == nullptr)
|
|
{
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
listeners.call ([&] (auto& x) { x.virtualConnectionRemoved (connection); });
|
|
|
|
if (key == umpReceiver)
|
|
if (auto strong = umpConnection.lock())
|
|
strong->disconnected();
|
|
|
|
if (key == bytestreamReceiver)
|
|
{
|
|
if (auto strong = srcConnection.lock())
|
|
strong->disconnected();
|
|
|
|
if (auto strong = dstConnection.lock())
|
|
strong->disconnected();
|
|
}
|
|
|
|
for (auto* ptr : { &bytestreamPort, &umpPort })
|
|
if (*ptr == connection)
|
|
ptr->reset();
|
|
}
|
|
|
|
std::shared_ptr<VirtualPortConnection> bytestreamPort, umpPort;
|
|
MidiReceiver bytestreamReceiver, umpReceiver;
|
|
|
|
std::weak_ptr<ump::DisconnectionListener> umpConnection;
|
|
std::weak_ptr<ump::DisconnectionListener> srcConnection;
|
|
std::weak_ptr<ump::DisconnectionListener> dstConnection;
|
|
ListenerList<VirtualMidiPortManagerListener> listeners;
|
|
|
|
static jobject addVirtualPort (JNIEnv* env, jclass, jobject newReceiver, jboolean isUmp)
|
|
{
|
|
return getSingleton().addPort (env, newReceiver, isUmp);
|
|
}
|
|
|
|
static void removeVirtualPort (JNIEnv* env, jclass, jobject previouslyAdded)
|
|
{
|
|
getSingleton().removePort (env, previouslyAdded);
|
|
}
|
|
|
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
|
CALLBACK (addVirtualPort, "addVirtualPort", "(Landroid/media/midi/MidiReceiver;Z)Landroid/media/midi/MidiReceiver;") \
|
|
CALLBACK (removeVirtualPort, "removeVirtualPort", "(Landroid/media/midi/MidiReceiver;)V") \
|
|
STATICMETHOD (isVirtualBytestreamAvailable, "isVirtualBytestreamAvailable", "(Landroid/content/Context;)Z") \
|
|
STATICMETHOD (isVirtualUmpAvailable, "isVirtualUmpAvailable", "(Landroid/content/Context;)Z") \
|
|
STATICMETHOD (setVirtualMidiBytestreamEnabled, "setVirtualMidiBytestreamEnabled", "(Landroid/content/Context;Z)V") \
|
|
STATICMETHOD (setVirtualMidiUmpEnabled, "setVirtualMidiUmpEnabled", "(Landroid/content/Context;Z)V") \
|
|
|
|
DECLARE_OPTIONAL_JNI_CLASS_WITH_BYTECODE (VirtualMidiServices, "com/rmsl/juce/VirtualMidiServices", 24, nullptr, true)
|
|
#undef JNI_CLASS_MEMBERS
|
|
};
|
|
|
|
/* This type holds the bytes of a Universal MIDI Packet in 'string order' */
|
|
struct StreamPacketBytes
|
|
{
|
|
std::array<std::byte, 16> data;
|
|
size_t size = 0;
|
|
|
|
static StreamPacketBytes asPlainBytes (ump::View v)
|
|
{
|
|
StreamPacketBytes result;
|
|
|
|
for (const auto word : v)
|
|
{
|
|
result.data[result.size + 0] = std::byte { ump::Utils::U8<0>::get (word) };
|
|
result.data[result.size + 1] = std::byte { ump::Utils::U8<1>::get (word) };
|
|
result.data[result.size + 2] = std::byte { ump::Utils::U8<2>::get (word) };
|
|
result.data[result.size + 3] = std::byte { ump::Utils::U8<3>::get (word) };
|
|
result.size += 4;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
};
|
|
|
|
/* Endpoint name, product instance ID, and function block name may all be spread across
|
|
several packets, and this type can reconstitute a UTF-8 string from these packets.
|
|
*/
|
|
class StringBuilder
|
|
{
|
|
public:
|
|
StringBuilder()
|
|
{
|
|
buffer.reserve (7 * 16);
|
|
}
|
|
|
|
void add (ump::View v)
|
|
{
|
|
if (ump::Utils::getMessageType (v[0]) != ump::Utils::MessageKind::stream)
|
|
{
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
const auto status = ump::Utils::U8<1>::get (v[0]);
|
|
|
|
if (status != 0x03 && status != 0x04 && status != 0x12)
|
|
{
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
const auto format = (ump::Utils::U8<0>::get (v[0]) >> 2) & 0x3;
|
|
|
|
switch (format)
|
|
{
|
|
case 0:
|
|
{
|
|
beginString();
|
|
append (v);
|
|
endString();
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
beginString();
|
|
append (v);
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
jassert (! buffer.empty());
|
|
append (v);
|
|
break;
|
|
}
|
|
|
|
case 3:
|
|
{
|
|
jassert (! buffer.empty());
|
|
append (v);
|
|
endString();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::optional<String> get() const
|
|
{
|
|
if (buffer.empty() || buffer.back() == std::byte{})
|
|
return String::fromUTF8 (reinterpret_cast<const char*> (buffer.data()), (int) buffer.size());
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
private:
|
|
void beginString()
|
|
{
|
|
buffer.clear();
|
|
}
|
|
|
|
void append (ump::View v)
|
|
{
|
|
const auto status = ump::Utils::U8<1>::get (v[0]);
|
|
const auto offset = status == 0x12 ? 3 : 2;
|
|
const auto packetAsBytes = StreamPacketBytes::asPlainBytes (v);
|
|
|
|
buffer.insert (buffer.end(), packetAsBytes.data.begin() + offset, packetAsBytes.data.end());
|
|
}
|
|
|
|
void endString()
|
|
{
|
|
buffer.push_back ({}); // add null terminator just in case
|
|
}
|
|
|
|
std::vector<std::byte> buffer;
|
|
};
|
|
|
|
struct StreamProcessorQueueDelegate
|
|
{
|
|
virtual ~StreamProcessorQueueDelegate() = default;
|
|
virtual void processStreamPacket (ump::View) = 0;
|
|
};
|
|
|
|
/* Filters out stream messages and dispatches them to the main thread,
|
|
where they are passed to the delegate for further processing.
|
|
*/
|
|
class StreamProcessorQueue final : private Timer
|
|
{
|
|
public:
|
|
explicit StreamProcessorQueue (StreamProcessorQueueDelegate& d)
|
|
: delegate (d)
|
|
{
|
|
startTimer (20);
|
|
}
|
|
|
|
~StreamProcessorQueue() override
|
|
{
|
|
stopTimer();
|
|
}
|
|
|
|
/** To be called from the MIDI input thread */
|
|
void consume (ump::Iterator b, ump::Iterator e)
|
|
{
|
|
for (auto v : makeRange (b, e))
|
|
{
|
|
if (ump::Utils::getMessageType (v[0]) != ump::Utils::MessageKind::stream)
|
|
continue;
|
|
|
|
size_t i = 0;
|
|
fifo.write ((int) v.size()).forEach ([&] (auto index)
|
|
{
|
|
buffer[(size_t) index] = v[i++];
|
|
});
|
|
}
|
|
}
|
|
|
|
private:
|
|
void timerCallback() override
|
|
{
|
|
const auto callback = [this] (ump::View v, double)
|
|
{
|
|
delegate.processStreamPacket (v);
|
|
};
|
|
|
|
const auto scope = fifo.read (fifo.getNumReady());
|
|
dispatcher.dispatch ({ buffer.data() + scope.startIndex1, (size_t) scope.blockSize1 }, 0.0, callback);
|
|
dispatcher.dispatch ({ buffer.data() + scope.startIndex2, (size_t) scope.blockSize2 }, 0.0, callback);
|
|
}
|
|
|
|
static constexpr auto queueSize = 4096;
|
|
StreamProcessorQueueDelegate& delegate;
|
|
std::vector<uint32_t> buffer = std::vector<uint32_t> (queueSize);
|
|
AbstractFifo fifo { queueSize };
|
|
ump::Dispatcher dispatcher;
|
|
};
|
|
|
|
struct EndpointInfoBuilderDelegate
|
|
{
|
|
virtual ~EndpointInfoBuilderDelegate() = default;
|
|
virtual void sendFunctionBlockDiscovery() = 0;
|
|
};
|
|
|
|
/* Parses incoming stream messages to build up a cache of all the information known about
|
|
a connected endpoint.
|
|
Waits until all function block information has been received before notifying all
|
|
EndpointListeners, and then notifies listeners on each subsequent configuration change.
|
|
*/
|
|
class EndpointInfoBuilder : public StreamProcessorQueueDelegate
|
|
{
|
|
public:
|
|
EndpointInfoBuilder (const ump::EndpointId& idToUse, EndpointInfoBuilderDelegate& d)
|
|
: id (idToUse), delegate (d)
|
|
{
|
|
}
|
|
|
|
std::optional<ump::Endpoint> getEndpoint() const
|
|
{
|
|
return endpoint;
|
|
}
|
|
|
|
void processStreamPacket (ump::View v) override
|
|
{
|
|
JUCE_ASSERT_MESSAGE_THREAD
|
|
|
|
const auto status = ump::Utils::U8<1>::get (v[0]);
|
|
|
|
switch (status)
|
|
{
|
|
case 0x1:
|
|
processEndpointInfo (v);
|
|
break;
|
|
|
|
case 0x2:
|
|
processDeviceIdentity (v);
|
|
break;
|
|
|
|
case 0x3:
|
|
processEndpointName (v);
|
|
break;
|
|
|
|
case 0x4:
|
|
processProductInstanceID (v);
|
|
break;
|
|
|
|
case 0x6:
|
|
processStreamConfigNotification (v);
|
|
break;
|
|
|
|
case 0x11:
|
|
processFunctionBlockInfo (v);
|
|
break;
|
|
|
|
case 0x12:
|
|
processFunctionBlockName (v);
|
|
break;
|
|
}
|
|
|
|
sendNotificationIfValid();
|
|
}
|
|
|
|
void addListener (ump::EndpointsListener& l)
|
|
{
|
|
changeListeners.add (&l);
|
|
}
|
|
|
|
void removeListener (ump::EndpointsListener& l)
|
|
{
|
|
changeListeners.remove (&l);
|
|
}
|
|
|
|
private:
|
|
void sendNotificationIfValid()
|
|
{
|
|
const auto numSpecifiedBlocks = endpoint.has_value() ? endpoint->getBlocks().size() : 0;
|
|
const auto requiredBitmap = (uint32_t) (((uint64_t) 1 << numSpecifiedBlocks) - 1);
|
|
|
|
if ((specifiedBlocks & requiredBitmap) != requiredBitmap)
|
|
return;
|
|
|
|
if ((specifiedBlockNames & requiredBitmap) != requiredBitmap)
|
|
return;
|
|
|
|
changeListeners.call ([] (auto& x) { x.endpointsChanged(); });
|
|
}
|
|
|
|
void processEndpointInfo (ump::View v)
|
|
{
|
|
const auto prevNumFunctionBlocks = endpoint.has_value() ? endpoint->getBlocks().size() : 0;
|
|
|
|
endpoint = endpoint.value_or (ump::Endpoint{})
|
|
.withUMPVersion (ump::Utils::U8<2>::get (v[0]), ump::Utils::U8<3>::get (v[0]))
|
|
.withMidi1Support (ump::Utils::U8<2>::get (v[1]) & 0x1)
|
|
.withMidi2Support (ump::Utils::U8<2>::get (v[1]) & 0x2)
|
|
.withTransmitJRSupport (ump::Utils::U8<3>::get (v[1]) & 0x1)
|
|
.withReceiveJRSupport (ump::Utils::U8<3>::get (v[1]) & 0x2)
|
|
.withStaticBlocks (ump::Utils::U8<0>::get (v[1]) & 0x80)
|
|
.withNumBlocks (ump::Utils::U8<0>::get (v[1]) & 0x7f);
|
|
|
|
if (endpoint->getBlocks().size() > prevNumFunctionBlocks)
|
|
delegate.sendFunctionBlockDiscovery();
|
|
}
|
|
|
|
void processDeviceIdentity (ump::View v)
|
|
{
|
|
const auto bytes = StreamPacketBytes::asPlainBytes (v);
|
|
ump::DeviceInfo info;
|
|
std::copy (bytes.data.data() + 0x5, bytes.data.data() + 0x8, info.manufacturer.begin());
|
|
std::copy (bytes.data.data() + 0x8, bytes.data.data() + 0xa, info.family.begin());
|
|
std::copy (bytes.data.data() + 0xa, bytes.data.data() + 0xc, info.modelNumber.begin());
|
|
std::copy (bytes.data.data() + 0xc, bytes.data.data() + 0x10, info.revision.begin());
|
|
endpoint = endpoint.value_or (ump::Endpoint{}).withDeviceInfo (info);
|
|
}
|
|
|
|
void processEndpointName (ump::View v)
|
|
{
|
|
endpointNameBuilder.add (v);
|
|
|
|
if (auto x = endpointNameBuilder.get())
|
|
endpoint = endpoint.value_or (ump::Endpoint{}).withName (*x);
|
|
}
|
|
|
|
void processProductInstanceID (ump::View v)
|
|
{
|
|
productIdBuilder.add (v);
|
|
|
|
if (auto x = productIdBuilder.get())
|
|
endpoint = endpoint.value_or (ump::Endpoint{}).withProductInstanceId (*x);
|
|
}
|
|
|
|
void processStreamConfigNotification (ump::View v)
|
|
{
|
|
const auto packetProtocol = std::invoke ([&]() -> std::optional<ump::PacketProtocol>
|
|
{
|
|
switch (ump::Utils::U8<2>::get (v[0]))
|
|
{
|
|
case 1:
|
|
return ump::PacketProtocol::MIDI_1_0;
|
|
case 2:
|
|
return ump::PacketProtocol::MIDI_2_0;
|
|
}
|
|
|
|
return {};
|
|
});
|
|
|
|
const auto flags = ump::Utils::U8<3>::get (v[0]);
|
|
const auto rxjr = flags & 0x2;
|
|
const auto txjr = flags & 0x1;
|
|
|
|
endpoint = endpoint.value_or (ump::Endpoint{})
|
|
.withProtocol (packetProtocol)
|
|
.withReceiveJREnabled (rxjr)
|
|
.withTransmitJREnabled (txjr);
|
|
}
|
|
|
|
void processFunctionBlockInfo (ump::View v)
|
|
{
|
|
const auto index = (size_t) (ump::Utils::U8<2>::get (v[0]) & 0x7f);
|
|
|
|
if (! endpoint.has_value() || index >= endpoint->getBlocks().size())
|
|
{
|
|
// Got a block notification for a block that doesn't exist!
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
endpoint->getBlocks()[index] = ump::Block{}.withEnabled (ump::Utils::U8<2>::get (v[0]) & 0x80)
|
|
.withUiHint ((ump::BlockUiHint) ((ump::Utils::U8<3>::get (v[0]) >> 4) & 0x3))
|
|
.withMIDI1ProxyKind ((ump::BlockMIDI1ProxyKind) ((ump::Utils::U8<3>::get (v[0]) >> 2) & 0x3))
|
|
.withDirection ((ump::BlockDirection) ((ump::Utils::U8<3>::get (v[0]) >> 0) & 0x3))
|
|
.withFirstGroup (ump::Utils::U8<0>::get (v[1]))
|
|
.withNumGroups (ump::Utils::U8<1>::get (v[1]))
|
|
.withMaxSysex8Streams (ump::Utils::U8<3>::get (v[1]));
|
|
|
|
specifiedBlocks |= (1 << index);
|
|
}
|
|
|
|
void processFunctionBlockName (ump::View v)
|
|
{
|
|
const auto blockIndex = ump::Utils::U8<2>::get (v[0]);
|
|
|
|
if (blockIndex >= functionBlockNameBuilders.size())
|
|
{
|
|
// block with this index cannot exist
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
if (! endpoint.has_value() || blockIndex >= endpoint->getBlocks().size())
|
|
{
|
|
// setting a name for a block that doesn't exist on this endpoint
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
auto& builder = functionBlockNameBuilders[blockIndex];
|
|
|
|
builder.add (v);
|
|
|
|
if (auto x = builder.get())
|
|
{
|
|
auto& block = endpoint->getBlocks()[blockIndex];
|
|
block = block.withName (*x);
|
|
|
|
specifiedBlockNames |= (1 << blockIndex);
|
|
}
|
|
}
|
|
|
|
ump::EndpointId id;
|
|
EndpointInfoBuilderDelegate& delegate;
|
|
|
|
ListenerList<ump::EndpointsListener> changeListeners;
|
|
|
|
std::optional<ump::Endpoint> endpoint;
|
|
|
|
std::array<StringBuilder, 32> functionBlockNameBuilders;
|
|
StringBuilder endpointNameBuilder, productIdBuilder;
|
|
|
|
// Toggle each bit on to indicate that we've received information about the block at that index
|
|
uint32_t specifiedBlocks = 0;
|
|
uint32_t specifiedBlockNames = 0;
|
|
};
|
|
|
|
/* A connection to a UMP device, which is assumed to always have a single input and output port. */
|
|
class UmpConnection : public Connection,
|
|
private ump::EndpointsListener,
|
|
private EndpointInfoBuilderDelegate,
|
|
private ump::Consumer
|
|
{
|
|
public:
|
|
~UmpConnection() override
|
|
{
|
|
for (auto& port : ports)
|
|
port.stop();
|
|
}
|
|
|
|
static std::shared_ptr<UmpConnection> make (const DeviceManager& manager,
|
|
const StaticDeviceInfo& info,
|
|
const ump::EndpointId& e)
|
|
{
|
|
auto result = rawToUniquePtr (new UmpConnection { e });
|
|
|
|
result->src = std::make_unique<MidiSource> (static_cast<ump::Consumer&> (*result), 0, true);
|
|
|
|
if (result->src != nullptr)
|
|
{
|
|
if (auto port = manager.openMidiInputPortWithID (info.id, (jint) 0, (jlong) result->src.get()))
|
|
{
|
|
port.start();
|
|
result->ports.push_back (std::move (port));
|
|
}
|
|
}
|
|
|
|
if (auto port = manager.openMidiOutputPortWithID (info.id, (jint) 0))
|
|
{
|
|
result->dst = MidiDestination::make (MidiReceiver { port.get() }, true);
|
|
result->ports.push_back (std::move (port));
|
|
}
|
|
|
|
if (result->src == nullptr && result->dst == nullptr)
|
|
{
|
|
// Unable to open a connection in either direction to this device!
|
|
jassertfalse;
|
|
return {};
|
|
}
|
|
|
|
result->staticInfo = info;
|
|
result->sendEndpointDiscovery();
|
|
return result;
|
|
}
|
|
|
|
ump::EndpointId getEndpointId() const override
|
|
{
|
|
return endpointId;
|
|
}
|
|
|
|
std::optional<ump::Endpoint> getEndpoint() const override
|
|
{
|
|
return infoBuilder.getEndpoint();
|
|
}
|
|
|
|
ump::StaticDeviceInfo getStaticDeviceInfo() const override
|
|
{
|
|
return staticInfo.getUmpStaticDeviceInfo();
|
|
}
|
|
|
|
bool send (ump::Iterator b, ump::Iterator e) override
|
|
{
|
|
if (dst == nullptr)
|
|
return false;
|
|
|
|
return converter.convert (b, e, [&] (auto&&... args) { return dst->send (args...); });
|
|
}
|
|
|
|
void addConsumer (ump::Consumer& c) override
|
|
{
|
|
consumers.add (c);
|
|
}
|
|
|
|
void removeConsumer (ump::Consumer& c) override
|
|
{
|
|
consumers.remove (c);
|
|
}
|
|
|
|
void disconnected() override
|
|
{
|
|
disconnectListeners.call ([] (auto& x) { x.disconnected(); });
|
|
}
|
|
|
|
void addDisconnectionListener (ump::DisconnectionListener& l) override
|
|
{
|
|
disconnectListeners.add (&l);
|
|
}
|
|
|
|
void removeDisconnectionListener (ump::DisconnectionListener& l) override
|
|
{
|
|
disconnectListeners.remove (&l);
|
|
}
|
|
|
|
void addEndpointListener (ump::EndpointsListener& l)
|
|
{
|
|
infoBuilder.addListener (l);
|
|
}
|
|
|
|
void removeEndpointListener (ump::EndpointsListener& l)
|
|
{
|
|
infoBuilder.removeListener (l);
|
|
}
|
|
|
|
private:
|
|
explicit UmpConnection (const ump::EndpointId& idToUse)
|
|
: endpointId (idToUse)
|
|
{
|
|
infoBuilder.addListener (*this);
|
|
}
|
|
|
|
/* Called when 'our' endpoint is changed. */
|
|
void endpointsChanged() override
|
|
{
|
|
const auto newProtocol = std::invoke ([&]() -> std::optional<ump::PacketProtocol>
|
|
{
|
|
if (const auto& e = infoBuilder.getEndpoint())
|
|
return e->getProtocol();
|
|
|
|
return std::nullopt;
|
|
});
|
|
|
|
converter.setProtocol (newProtocol);
|
|
}
|
|
|
|
void sendEndpointDiscovery()
|
|
{
|
|
// The 'filter bitmap' requests all fields that are defined in the current UMP spec
|
|
if (dst != nullptr)
|
|
dst->send (ump::View { ump::Factory::makeEndpointDiscovery (1, 1, std::byte { 0x1f }).data() });
|
|
}
|
|
|
|
void sendFunctionBlockDiscovery() override
|
|
{
|
|
if (dst != nullptr)
|
|
dst->send (ump::View { ump::Factory::makeFunctionBlockDiscovery (0xff, std::byte { 0x3 }).data() });
|
|
}
|
|
|
|
void consume (ump::Iterator b, ump::Iterator e, double t) override
|
|
{
|
|
// Forward to all interested listeners
|
|
consumers.call ([&] (auto& x) { x.consume (b, e, t); });
|
|
|
|
// Filter out stream messages for processing on the main thread
|
|
queue.consume (b, e);
|
|
}
|
|
|
|
ListenerList<ump::DisconnectionListener> disconnectListeners;
|
|
WaitFreeListeners<ump::Consumer> consumers;
|
|
|
|
StaticDeviceInfo staticInfo;
|
|
ump::EndpointId endpointId;
|
|
EndpointInfoBuilder infoBuilder { endpointId, *this };
|
|
StreamProcessorQueue queue { infoBuilder };
|
|
|
|
std::unique_ptr<MidiSource> src;
|
|
std::unique_ptr<MidiDestination> dst;
|
|
std::vector<MidiPort> ports;
|
|
|
|
ConverterWithOptionalProtocol converter;
|
|
};
|
|
|
|
class BytestreamConnection : public Connection,
|
|
private ump::Consumer
|
|
{
|
|
public:
|
|
~BytestreamConnection() override
|
|
{
|
|
for (auto& port : ports)
|
|
port.stop();
|
|
}
|
|
|
|
static std::shared_ptr<BytestreamConnection> make (const DeviceManager& manager,
|
|
const StaticDeviceInfo& info,
|
|
const ump::EndpointId& e)
|
|
{
|
|
auto result = rawToUniquePtr (new BytestreamConnection);
|
|
|
|
for (size_t i = 0; i < info.src.size(); ++i)
|
|
{
|
|
auto item = std::make_unique<MidiSource> (static_cast<ump::Consumer&> (*result), (int) i, false);
|
|
auto port = manager.openMidiInputPortWithID (info.id, (jint) i, (jlong) item.get());
|
|
|
|
if (port == nullptr)
|
|
continue;
|
|
|
|
result->src.push_back (std::move (item));
|
|
port.start();
|
|
result->ports.push_back (std::move (port));
|
|
}
|
|
|
|
for (size_t i = 0; i < info.dst.size(); ++i)
|
|
{
|
|
if (auto port = manager.openMidiOutputPortWithID (info.id, (int) i))
|
|
{
|
|
if (auto dst = MidiDestination::make (MidiReceiver { port.get() }, false))
|
|
{
|
|
result->dst.push_back (std::move (dst));
|
|
result->ports.push_back (std::move (port));
|
|
}
|
|
}
|
|
}
|
|
|
|
result->endpointId = e;
|
|
result->endpoint = info.getEquivalentBytestreamEndpoint();
|
|
result->staticInfo = info;
|
|
|
|
return result;
|
|
}
|
|
|
|
ump::EndpointId getEndpointId() const override
|
|
{
|
|
return endpointId;
|
|
}
|
|
|
|
std::optional<ump::Endpoint> getEndpoint() const override
|
|
{
|
|
return endpoint;
|
|
}
|
|
|
|
ump::StaticDeviceInfo getStaticDeviceInfo() const override
|
|
{
|
|
return staticInfo.getUmpStaticDeviceInfo();
|
|
}
|
|
|
|
bool send (ump::Iterator b, ump::Iterator e) override
|
|
{
|
|
bool success = true;
|
|
|
|
for (const auto v : makeRange (b, e))
|
|
{
|
|
const auto kind = ump::Utils::getMessageType (v[0]);
|
|
|
|
if (ump::Utils::isGroupless (kind))
|
|
{
|
|
success = false;
|
|
continue;
|
|
}
|
|
|
|
const auto groupIndex = ump::Utils::getGroup (v[0]);
|
|
|
|
if (groupIndex >= dst.size())
|
|
{
|
|
success = false;
|
|
continue;
|
|
}
|
|
|
|
if (auto& x = dst[(size_t) groupIndex])
|
|
x->send (v);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
|
|
void addConsumer (ump::Consumer& c) override
|
|
{
|
|
consumers.add (c);
|
|
}
|
|
|
|
void removeConsumer (ump::Consumer& c) override
|
|
{
|
|
consumers.remove (c);
|
|
}
|
|
|
|
void disconnected() override
|
|
{
|
|
disconnectListeners.call ([] (auto& x) { x.disconnected(); });
|
|
}
|
|
|
|
void addDisconnectionListener (ump::DisconnectionListener& l) override
|
|
{
|
|
disconnectListeners.add (&l);
|
|
}
|
|
|
|
void removeDisconnectionListener (ump::DisconnectionListener& l) override
|
|
{
|
|
disconnectListeners.remove (&l);
|
|
}
|
|
|
|
private:
|
|
void consume (ump::Iterator b, ump::Iterator e, double t) override
|
|
{
|
|
consumers.call ([&] (auto& x) { x.consume (b, e, t); });
|
|
}
|
|
|
|
ListenerList<ump::DisconnectionListener> disconnectListeners;
|
|
WaitFreeListeners<ump::Consumer> consumers;
|
|
|
|
StaticDeviceInfo staticInfo;
|
|
ump::EndpointId endpointId;
|
|
std::optional<ump::Endpoint> endpoint;
|
|
|
|
std::vector<std::unique_ptr<MidiSource>> src;
|
|
std::vector<std::unique_ptr<MidiDestination>> dst;
|
|
std::vector<MidiPort> ports;
|
|
};
|
|
|
|
class Client : private DeviceManagerListener,
|
|
private VirtualMidiPortManagerListener
|
|
{
|
|
public:
|
|
void getEndpoints (std::vector<ump::EndpointId>& buffer) const
|
|
{
|
|
std::transform (infoForEndpoint.begin(),
|
|
infoForEndpoint.end(),
|
|
std::back_inserter (buffer),
|
|
[] (auto& pair) { return pair.first; });
|
|
}
|
|
|
|
std::optional<ump::Endpoint> getEndpoint (const ump::EndpointId& id) const
|
|
{
|
|
if (const auto iter = infoForEndpoint.find (id); iter != infoForEndpoint.end())
|
|
{
|
|
if (iter->second.transport == TRANSPORT_BYTESTREAM)
|
|
return iter->second.getEquivalentBytestreamEndpoint();
|
|
}
|
|
|
|
if (const auto iter = connections.find (id); iter != connections.end())
|
|
{
|
|
if (auto connection = iter->second.lock())
|
|
return connection->getEndpoint();
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
std::optional<ump::StaticDeviceInfo> getStaticDeviceInfo (const ump::EndpointId& id) const
|
|
{
|
|
if (const auto iter = infoForEndpoint.find (id); iter != infoForEndpoint.end())
|
|
return iter->second.getUmpStaticDeviceInfo();
|
|
|
|
if (const auto iter = connections.find (id); iter != connections.end())
|
|
{
|
|
if (auto connection = iter->second.lock())
|
|
return connection->getStaticDeviceInfo();
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
std::shared_ptr<Connection> findOrMakeConnection (const ump::EndpointId& e)
|
|
{
|
|
const auto connection = connections.find (e);
|
|
|
|
if (connection != connections.end())
|
|
if (auto strong = connection->second.lock())
|
|
return strong;
|
|
|
|
const auto info = infoForEndpoint.find (e);
|
|
|
|
// We don't know anything about this endpoint, maybe it got disconnected.
|
|
// In any case, we can't connect to it.
|
|
if (info == infoForEndpoint.end())
|
|
return {};
|
|
|
|
const auto& staticDeviceInfo = info->second;
|
|
|
|
switch (staticDeviceInfo.transport)
|
|
{
|
|
case TRANSPORT_BYTESTREAM:
|
|
{
|
|
const auto result = BytestreamConnection::make (*deviceManager, staticDeviceInfo, e);
|
|
connections[e] = result;
|
|
return result;
|
|
}
|
|
|
|
case TRANSPORT_UMP:
|
|
{
|
|
const auto result = UmpConnection::make (*deviceManager, staticDeviceInfo, e);
|
|
result->addEndpointListener (listener);
|
|
connections[e] = result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Unknown transport!
|
|
jassertfalse;
|
|
return {};
|
|
}
|
|
|
|
static std::unique_ptr<Client> make (ump::EndpointsListener& l)
|
|
{
|
|
auto result = rawToUniquePtr (new Client { l });
|
|
result->deviceManager = DeviceManager::getSingleton();
|
|
|
|
if (result->deviceManager == nullptr)
|
|
return {};
|
|
|
|
result->deviceManager->addListener (*result);
|
|
|
|
return result;
|
|
}
|
|
|
|
~Client() override
|
|
{
|
|
if (deviceManager != nullptr)
|
|
deviceManager->removeListener (*this);
|
|
|
|
VirtualMidiPortManager::getSingleton().removeListener (*this);
|
|
}
|
|
|
|
private:
|
|
explicit Client (ump::EndpointsListener& l)
|
|
: listener (l)
|
|
{
|
|
// It's possible for the VirtualMidiPortManager to be created early on in the app's
|
|
// lifecycle, even before the app's main window component peer is constructed. In the
|
|
// case that the virtual port manager is constructed first, we still need to make a
|
|
// record of these pre-existing ports in order to allow new connections to those ports.
|
|
fetchVirtualConnections();
|
|
VirtualMidiPortManager::getSingleton().addListener (*this);
|
|
}
|
|
|
|
void fetchVirtualConnections()
|
|
{
|
|
auto& manager = VirtualMidiPortManager::getSingleton();
|
|
|
|
for (const auto& [port, transport] : { std::tuple (manager.getBytestreamConnection(), TRANSPORT_BYTESTREAM),
|
|
std::tuple (manager.getUmpConnection(), TRANSPORT_UMP) })
|
|
{
|
|
if (port != nullptr)
|
|
connections.insert_or_assign (port->getEndpointId(), port);
|
|
}
|
|
}
|
|
|
|
void virtualConnectionAdded() override
|
|
{
|
|
fetchVirtualConnections();
|
|
listener.virtualMidiServiceActiveChanged();
|
|
}
|
|
|
|
void virtualConnectionRemoved (std::shared_ptr<Connection> x) override
|
|
{
|
|
listener.virtualMidiServiceActiveChanged();
|
|
const auto iter = connections.find (x->getEndpointId());
|
|
|
|
if (iter != connections.end())
|
|
{
|
|
jassert (iter->second.lock() == x);
|
|
connections.erase (iter);
|
|
}
|
|
}
|
|
|
|
void deviceAdded (const StaticDeviceInfo& info) override
|
|
{
|
|
const auto endpointId = findUniqueId (info);
|
|
endpointIdForDevice.insert_or_assign (info.id, endpointId);
|
|
infoForEndpoint.insert_or_assign (endpointId, info);
|
|
listener.endpointsChanged();
|
|
}
|
|
|
|
void deviceRemoved (int id) override
|
|
{
|
|
const auto iter = endpointIdForDevice.find (id);
|
|
|
|
if (iter == endpointIdForDevice.end())
|
|
{
|
|
// That's weird, a device disconnected, but we had no record of it connecting in the first place!
|
|
jassertfalse;
|
|
return;
|
|
}
|
|
|
|
const auto connection = connections.find (iter->second);
|
|
|
|
if (connection != connections.end())
|
|
{
|
|
if (const auto strong = connection->second.lock())
|
|
strong->disconnected();
|
|
|
|
connections.erase (connection);
|
|
}
|
|
|
|
infoForEndpoint.erase (iter->second);
|
|
endpointIdForDevice.erase (iter);
|
|
listener.endpointsChanged();
|
|
}
|
|
|
|
ump::EndpointId findUniqueId (const StaticDeviceInfo& info) const
|
|
{
|
|
const auto base = std::invoke ([&]
|
|
{
|
|
if (info.serialNumber.isNotEmpty())
|
|
return info.serialNumber;
|
|
|
|
if (info.name.isNotEmpty())
|
|
return info.name;
|
|
|
|
// The device doesn't have any immediately-available useful info,
|
|
// so we're giving up on creating a stable ID
|
|
return String (info.id);
|
|
});
|
|
|
|
for (int suffix = 1;; ++suffix)
|
|
{
|
|
const auto potential = base + (suffix == 1 ? "" : " (" + String (suffix) + ')');
|
|
const auto id = ump::EndpointId::makeSrcDst (potential, potential);
|
|
|
|
if (infoForEndpoint.count (id) == 0)
|
|
return id;
|
|
}
|
|
}
|
|
|
|
ump::EndpointsListener& listener;
|
|
std::map<jint, ump::EndpointId> endpointIdForDevice;
|
|
std::map<ump::EndpointId, StaticDeviceInfo> infoForEndpoint;
|
|
std::map<ump::EndpointId, std::weak_ptr<Connection>> connections;
|
|
std::shared_ptr<DeviceManager> deviceManager;
|
|
};
|
|
|
|
class InputImplNative : public ump::Input::Impl::Native,
|
|
private ump::Consumer
|
|
{
|
|
public:
|
|
~InputImplNative() override
|
|
{
|
|
connection->removeDisconnectionListener (disconnectionListener);
|
|
connection->removeConsumer (*this);
|
|
}
|
|
|
|
ump::EndpointId getEndpointId() const override
|
|
{
|
|
return connection->getEndpointId();
|
|
}
|
|
|
|
ump::PacketProtocol getProtocol() const override
|
|
{
|
|
return converter.getProtocol();
|
|
}
|
|
|
|
static std::unique_ptr<InputImplNative> make (ump::DisconnectionListener& dl,
|
|
std::shared_ptr<Connection> d,
|
|
ump::PacketProtocol p,
|
|
ump::Consumer& c)
|
|
{
|
|
if (d == nullptr)
|
|
return {};
|
|
|
|
return rawToUniquePtr (new InputImplNative (dl, d, p, c));
|
|
}
|
|
|
|
private:
|
|
InputImplNative (ump::DisconnectionListener& dl,
|
|
std::shared_ptr<Connection> d,
|
|
ump::PacketProtocol p,
|
|
ump::Consumer& c)
|
|
: disconnectionListener (dl),
|
|
consumer (c),
|
|
converter (p),
|
|
connection (std::move (d))
|
|
{
|
|
connection->addConsumer (*this);
|
|
connection->addDisconnectionListener (disconnectionListener);
|
|
}
|
|
|
|
void consume (ump::Iterator b, ump::Iterator e, double time) override
|
|
{
|
|
converter.convert (b, e, [this, time] (auto view)
|
|
{
|
|
const ump::Iterator begin { view.data(), view.size() };
|
|
consumer.consume (begin, std::next (begin), time);
|
|
});
|
|
}
|
|
|
|
ump::DisconnectionListener& disconnectionListener;
|
|
ump::Consumer& consumer;
|
|
ump::GenericUMPConverter converter;
|
|
std::shared_ptr<Connection> connection;
|
|
};
|
|
|
|
class OutputImplNative : public ump::Output::Impl::Native
|
|
{
|
|
public:
|
|
~OutputImplNative() override
|
|
{
|
|
connection->removeDisconnectionListener (disconnectionListener);
|
|
}
|
|
|
|
ump::EndpointId getEndpointId() const override
|
|
{
|
|
return connection->getEndpointId();
|
|
}
|
|
|
|
bool send (ump::Iterator b, ump::Iterator e) override
|
|
{
|
|
return connection->send (b, e);
|
|
}
|
|
|
|
static std::unique_ptr<OutputImplNative> make (ump::DisconnectionListener& dl,
|
|
std::shared_ptr<Connection> d)
|
|
{
|
|
if (d == nullptr)
|
|
return {};
|
|
|
|
return rawToUniquePtr (new OutputImplNative (dl, d));
|
|
}
|
|
|
|
private:
|
|
OutputImplNative (ump::DisconnectionListener& dl, std::shared_ptr<Connection> d)
|
|
: disconnectionListener (dl), connection (std::move (d))
|
|
{
|
|
connection->addDisconnectionListener (disconnectionListener);
|
|
}
|
|
|
|
ump::DisconnectionListener& disconnectionListener;
|
|
std::shared_ptr<Connection> connection;
|
|
};
|
|
|
|
class SessionImplNative : public ump::Session::Impl::Native
|
|
{
|
|
public:
|
|
String getName() const override
|
|
{
|
|
return name;
|
|
}
|
|
|
|
std::unique_ptr<ump::Input::Impl::Native> connectInput (ump::DisconnectionListener& d,
|
|
const ump::EndpointId& e,
|
|
ump::PacketProtocol p,
|
|
ump::Consumer& c) override
|
|
{
|
|
return InputImplNative::make (d, client->findOrMakeConnection (e), p, c);
|
|
}
|
|
|
|
std::unique_ptr<ump::Output::Impl::Native> connectOutput (ump::DisconnectionListener& d,
|
|
const ump::EndpointId& e) override
|
|
{
|
|
return OutputImplNative::make (d, client->findOrMakeConnection (e));
|
|
}
|
|
|
|
std::unique_ptr<ump::VirtualEndpoint::Impl::Native> createNativeVirtualEndpoint (const String& endpointName,
|
|
const ump::DeviceInfo& info,
|
|
const String& productInstance,
|
|
ump::PacketProtocol protocol,
|
|
Span<const ump::Block> blocks,
|
|
ump::BlocksAreStatic blocksAreStatic) override
|
|
{
|
|
const auto impl = VirtualMidiPortManager::getSingleton().createNativeVirtualEndpoint (endpointName,
|
|
info,
|
|
productInstance,
|
|
protocol,
|
|
blocks,
|
|
blocksAreStatic);
|
|
|
|
if (impl == nullptr)
|
|
return {};
|
|
|
|
struct Result : public ump::VirtualEndpoint::Impl::Native
|
|
{
|
|
explicit Result (std::shared_ptr<VirtualEndpointImplNative> x) : impl (std::move (x)) {}
|
|
|
|
ump::EndpointId getId() const override
|
|
{
|
|
return impl->getId();
|
|
}
|
|
|
|
bool setBlock (uint8_t index, const juce::universal_midi_packets::Block& block) override
|
|
{
|
|
return impl->setBlock (index, block);
|
|
}
|
|
|
|
bool setName (const juce::String& nameIn) override
|
|
{
|
|
return impl->setName (nameIn);
|
|
}
|
|
|
|
std::shared_ptr<VirtualEndpointImplNative> impl;
|
|
};
|
|
|
|
return rawToUniquePtr (new Result { impl });
|
|
}
|
|
|
|
std::unique_ptr<ump::LegacyVirtualInput::Impl::Native> createLegacyVirtualInput (const String&) override
|
|
{
|
|
const auto impl = VirtualMidiPortManager::getSingleton().createLegacyVirtualInput();
|
|
|
|
if (impl == nullptr)
|
|
return {};
|
|
|
|
struct Result : public ump::LegacyVirtualInput::Impl::Native
|
|
{
|
|
explicit Result (std::shared_ptr<LegacyVirtualPortImplNative> x) : impl (std::move (x)) {}
|
|
|
|
ump::EndpointId getId() const override
|
|
{
|
|
return impl->getId();
|
|
}
|
|
|
|
std::shared_ptr<LegacyVirtualPortImplNative> impl;
|
|
};
|
|
|
|
return rawToUniquePtr (new Result { impl });
|
|
}
|
|
|
|
std::unique_ptr<ump::LegacyVirtualOutput::Impl::Native> createLegacyVirtualOutput (const String&) override
|
|
{
|
|
const auto impl = VirtualMidiPortManager::getSingleton().createLegacyVirtualOutput();
|
|
|
|
if (impl == nullptr)
|
|
return {};
|
|
|
|
struct Result : public ump::LegacyVirtualOutput::Impl::Native
|
|
{
|
|
explicit Result (std::shared_ptr<LegacyVirtualPortImplNative> x) : impl (std::move (x)) {}
|
|
|
|
ump::EndpointId getId() const override
|
|
{
|
|
return impl->getId();
|
|
}
|
|
|
|
std::shared_ptr<LegacyVirtualPortImplNative> impl;
|
|
};
|
|
|
|
return rawToUniquePtr (new Result { impl });
|
|
}
|
|
|
|
static std::unique_ptr<SessionImplNative> make (std::shared_ptr<Client> c, const String& n)
|
|
{
|
|
return rawToUniquePtr (new SessionImplNative { std::move (c), n });
|
|
}
|
|
|
|
private:
|
|
explicit SessionImplNative (std::shared_ptr<Client> c, const String& n)
|
|
: client (std::move (c)), name (n)
|
|
{
|
|
}
|
|
|
|
std::shared_ptr<Client> client;
|
|
String name;
|
|
};
|
|
|
|
class EndpointsImplNative : public ump::Endpoints::Impl::Native
|
|
{
|
|
public:
|
|
ump::Backend getBackend() const override
|
|
{
|
|
return ump::Backend::android;
|
|
}
|
|
|
|
void getEndpoints (std::vector<ump::EndpointId>& buffer) const override
|
|
{
|
|
client->getEndpoints (buffer);
|
|
}
|
|
|
|
std::optional<ump::Endpoint> getEndpoint (const ump::EndpointId& x) const override
|
|
{
|
|
return client->getEndpoint (x);
|
|
}
|
|
|
|
std::optional<ump::StaticDeviceInfo> getStaticDeviceInfo (const ump::EndpointId& x) const override
|
|
{
|
|
return client->getStaticDeviceInfo (x);
|
|
}
|
|
|
|
std::unique_ptr<ump::Session::Impl::Native> makeSession (const String& name) override
|
|
{
|
|
return SessionImplNative::make (client, name);
|
|
}
|
|
|
|
bool isVirtualMidiBytestreamServiceActive() const override
|
|
{
|
|
return VirtualMidiPortManager::getSingleton().isBytestreamPortAvailable();
|
|
}
|
|
|
|
bool isVirtualMidiUmpServiceActive() const override
|
|
{
|
|
return VirtualMidiPortManager::getSingleton().isUmpPortAvailable();
|
|
}
|
|
|
|
void setVirtualMidiBytestreamServiceActive (bool x) override
|
|
{
|
|
VirtualMidiPortManager::setVirtualMidiBytestreamEnabled (x);
|
|
}
|
|
|
|
void setVirtualMidiUmpServiceActive (bool x) override
|
|
{
|
|
VirtualMidiPortManager::setVirtualMidiUmpEnabled (x);
|
|
}
|
|
|
|
static std::unique_ptr<EndpointsImplNative> make (ump::EndpointsListener& l)
|
|
{
|
|
if (auto c = Client::make (l))
|
|
return rawToUniquePtr (new EndpointsImplNative { std::move (c) });
|
|
|
|
return {};
|
|
}
|
|
|
|
private:
|
|
explicit EndpointsImplNative (std::shared_ptr<Client> c)
|
|
: client (std::move (c))
|
|
{
|
|
}
|
|
|
|
std::shared_ptr<Client> client;
|
|
};
|
|
|
|
AndroidMidiHelpers() = delete;
|
|
};
|
|
|
|
auto ump::Endpoints::Impl::Native::make (EndpointsListener& l) -> std::unique_ptr<Native>
|
|
{
|
|
return AndroidMidiHelpers::EndpointsImplNative::make (l);
|
|
}
|
|
|
|
} // namespace juce
|