From 7a4f419d53787b29ef30831d8eabf27a79c944b6 Mon Sep 17 00:00:00 2001 From: Georgy Treshchev Date: Thu, 4 Mar 2021 17:06:21 +0100 Subject: [PATCH] Global update: full working with compressed formats, pre-importing audio files and much more. Check the updated documentation. --- README.md | 5 +- RuntimeAudioImporter/Resources/Icon128.png | Bin 16206 -> 16163 bytes .../RuntimeAudioImporter.uplugin | 38 +- .../Private/AdvancedPCMCompressor.cpp | 346 +++++++ .../Private/OpusCompressor.cpp | 202 +++++ .../Private/PreimportedSoundAsset.cpp | 6 + .../Private/RuntimeAudioImporter.cpp | 7 +- .../Private/RuntimeAudioImporterLibrary.cpp | 851 ++++++++++-------- .../Private/VorbisCompressor.cpp | 202 +++++ .../.idea.Public.dir/.idea/contentModel.xml | 8 + .../.idea.Public.dir/.idea/encodings.xml | 4 + .../.idea.Public.dir/.idea/indexLayout.xml | 8 + .../.idea/.idea.Public.dir/.idea/modules.xml | 8 + .../.idea/projectSettingsUpdater.xml | 6 + .../.idea.Public.dir/.idea/riderModule.iml | 7 + .../.idea/.idea.Public.dir/.idea/vcs.xml | 6 + .../.idea.Public.dir/.idea/workspace.xml | 6 + .../Public/AdvancedPCMCompressor.h | 25 + .../Public/OpusCompressor.h | 28 + .../Public/PreimportedSoundAsset.h | 35 + .../Public/RuntimeAudioImporterLibrary.h | 382 +++++--- .../Public/VorbisCompressor.h | 22 + .../RuntimeAudioImporter.Build.cs | 26 +- .../Private/PreimportedSoundFactory.cpp | 121 +++ .../Private/RuntimeAudioImporterEditor.cpp | 17 + .../Public/PreimportedSoundFactory.h | 44 + .../Public/RuntimeAudioImporterEditor.h | 16 + .../RuntimeAudioImporterEditor.Build.cs | 46 + .../Source/ThirdParty/dr_flac.h | 9 +- .../Source/ThirdParty/dr_mp3.h | 13 +- .../Source/ThirdParty/dr_wav.h | 144 ++- image/runtimeaudioimporter.png | Bin 88638 -> 88542 bytes 32 files changed, 2063 insertions(+), 575 deletions(-) create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/AdvancedPCMCompressor.cpp create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/OpusCompressor.cpp create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/PreimportedSoundAsset.cpp create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/VorbisCompressor.cpp create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/contentModel.xml create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/encodings.xml create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/indexLayout.xml create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/modules.xml create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/projectSettingsUpdater.xml create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/riderModule.iml create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/vcs.xml create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/workspace.xml create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/AdvancedPCMCompressor.h create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/OpusCompressor.h create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/PreimportedSoundAsset.h create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/VorbisCompressor.h create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Private/PreimportedSoundFactory.cpp create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Private/RuntimeAudioImporterEditor.cpp create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Public/PreimportedSoundFactory.h create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Public/RuntimeAudioImporterEditor.h create mode 100644 RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/RuntimeAudioImporterEditor.Build.cs diff --git a/README.md b/README.md index 41f2eea..7a992ad 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ ![GitHub](https://img.shields.io/github/license/Respirant/RuntimeAudioImporter) # RuntimeAudioImporter -This plugin will be useful for those who want to be able to import audio files of MP3, WAV and FLAC formats into the game in runtime (for example, if the game involves importing audio files from the player's device memory). It's easy to use in both C++ and Blueprints. +Open-source plugin to import audio files of MP3, WAV and FLAC formats into the game at runtime. It's easy to use in both C++ and Blueprints. +Plugin does not require any static libraries and external dependencies. ![Runtime Audio Importer Unreal Engine Plugin Logo](image/runtimeaudioimporter.png "RuntimeAudioImporter Unreal Engine Plugin Logo") @@ -10,7 +11,7 @@ This plugin will be useful for those who want to be able to import audio files o - Supported for major audio formats: MP3, WAV and FLAC - Automatic detection of audio format (by extension) - System for obtaining transcoding status for error detection -- No any external dependencies +- No any static libraries and external dependencies - Support for all available devices (tested on Windows and Android, but there are no restrictions to work on other devices) ## Documentation diff --git a/RuntimeAudioImporter/Resources/Icon128.png b/RuntimeAudioImporter/Resources/Icon128.png index 85ce4f5562b8e19816934b9b8bc5788710c465a4..9fa3765dd9d097a91508de41c52bf56de796b189 100644 GIT binary patch delta 14797 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 FV1k~S)Kvfg diff --git a/RuntimeAudioImporter/RuntimeAudioImporter.uplugin b/RuntimeAudioImporter/RuntimeAudioImporter.uplugin index 4dfd899..c690f86 100644 --- a/RuntimeAudioImporter/RuntimeAudioImporter.uplugin +++ b/RuntimeAudioImporter/RuntimeAudioImporter.uplugin @@ -1,21 +1,21 @@ { - "FileVersion": 3, - "Version": 1, - "VersionName": "1.0", - "FriendlyName": "RuntimeAudioImporter", - "Description": "Runtime Audio Importer plugin for Unreal Engine 4. Supports mp3, wav and flac formats.", - "Category": "Audio", - "CreatedBy": "Respirant", - "CreatedByURL": "https://github.com/Respirant/RuntimeAudioImporter", - "DocsURL": "https://unreal.blog/runtime-audio-importer", - "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/81fd278e318e4235a028d3fdf46bf036", - "SupportURL": "https://telegram.me/Respirant", - "CanContainContent": false, - "IsBetaVersion": false, - "IsExperimentalVersion": false, - "Installed": true, - "EngineVersion": "4.26.0", - "Modules": [ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "RuntimeAudioImporter", + "Description": "Runtime Audio Importer plugin for Unreal Engine 4. Supports mp3, wav and flac formats.", + "Category": "Audio", + "CreatedBy": "Georgy Treshchev", + "CreatedByURL": "https://github.com/Respirant/RuntimeAudioImporter", + "DocsURL": "https://unreal.blog/runtime-audio-importer", + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/81fd278e318e4235a028d3fdf46bf036", + "SupportURL": "https://t.me/unrealblog", + "CanContainContent": true, + "IsBetaVersion": false, + "IsExperimentalVersion": false, + "Installed": true, + "EngineVersion": "4.26.0", + "Modules": [ { "Name": "RuntimeAudioImporter", "Type": "Runtime", @@ -34,6 +34,10 @@ "TVOS", "Switch" ] + }, + { + "Name": "RuntimeAudioImporterEditor", + "Type": "Editor" } ] } diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/AdvancedPCMCompressor.cpp b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/AdvancedPCMCompressor.cpp new file mode 100644 index 0000000..300ec5f --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/AdvancedPCMCompressor.cpp @@ -0,0 +1,346 @@ +// Georgy Treshchev 2021. + +#include "AdvancedPCMCompressor.h" +#include "Interfaces/IAudioFormat.h" +#include "Serialization/MemoryWriter.h" +#include "ADPCMAudioInfo.h" + + +#define FORMAT_FOURCC(ch0, ch1, ch2, ch3)\ + ((uint32)(uint8)(ch0) | ((uint32)(uint8)(ch1) << 8) |\ + ((uint32)(uint8)(ch2) << 16) | ((uint32)(uint8)(ch3) << 24 )) + + + +namespace BaseDataOperations +{ + template + void GenerateWaveFile(RiffDataChunk (& RiffDataChunks)[N], TArray& CompressedDataStore) + { + uint32 RiffDataSize = sizeof(uint32); + + for (uint32 Scan = 0; Scan < N; ++Scan) + { + const RiffDataChunk& Chunk = RiffDataChunks[Scan]; + RiffDataSize += (sizeof(Chunk.ID) + sizeof(Chunk.DataSize) + Chunk.DataSize); + } + + const uint32 OutputDataSize = RiffDataSize + sizeof(uint32) + sizeof(uint32); + + CompressedDataStore.Empty(OutputDataSize); + FMemoryWriter CompressedData(CompressedDataStore); + + uint32 rID = FORMAT_FOURCC('R', 'I', 'F', 'F'); + CompressedData.Serialize(&rID, sizeof(rID)); + CompressedData.Serialize(&RiffDataSize, sizeof(RiffDataSize)); + + rID = FORMAT_FOURCC('W', 'A', 'V', 'E'); + CompressedData.Serialize(&rID, sizeof(rID)); + + for (uint32 Scan = 0; Scan < N; ++Scan) + { + RiffDataChunk& Chunk = RiffDataChunks[Scan]; + + CompressedData.Serialize(&Chunk.ID, sizeof(Chunk.ID)); + CompressedData.Serialize(&Chunk.DataSize, sizeof(Chunk.DataSize)); + CompressedData.Serialize(Chunk.Data, Chunk.DataSize); + } + } + + template + T SignExtend(const T ValueToExtend) + { + struct + { + T ExtendedValue:B; + } SignExtender; + return SignExtender.ExtendedValue = ValueToExtend; + } + + template + T ReadFromByteStream(const uint8* ByteStream, int32& ReadIndex, bool bLittleEndian = true) + { + T ValueRaw = 0; + + if (bLittleEndian) + { +#if PLATFORM_LITTLE_ENDIAN + for (int32 ByteIndex = 0; ByteIndex < sizeof(T); ++ByteIndex) +#else + for (int32 ByteIndex = sizeof(T) - 1; ByteIndex >= 0; --ByteIndex) +#endif // PLATFORM_LITTLE_ENDIAN + { + ValueRaw |= ByteStream[ReadIndex++] << 8 * ByteIndex; + } + } + else + { +#if PLATFORM_LITTLE_ENDIAN + for (int32 ByteIndex = sizeof(T) - 1; ByteIndex >= 0; --ByteIndex) +#else + for (int32 ByteIndex = 0; ByteIndex < sizeof(T); ++ByteIndex) +#endif // PLATFORM_LITTLE_ENDIAN + { + ValueRaw |= ByteStream[ReadIndex++] << 8 * ByteIndex; + } + } + + return ValueRaw; + } + + template + void WriteToByteStream(T Value, uint8* ByteStream, int32& WriteIndex, bool bLittleEndian = true) + { + if (bLittleEndian) + { +#if PLATFORM_LITTLE_ENDIAN + for (int32 ByteIndex = 0; ByteIndex < sizeof(T); ++ByteIndex) +#else + for (int32 ByteIndex = sizeof(T) - 1; ByteIndex >= 0; --ByteIndex) +#endif // PLATFORM_LITTLE_ENDIAN + { + ByteStream[WriteIndex++] = (Value >> (8 * ByteIndex)) & 0xFF; + } + } + else + { +#if PLATFORM_LITTLE_ENDIAN + for (int32 ByteIndex = sizeof(T) - 1; ByteIndex >= 0; --ByteIndex) +#else + for (int32 ByteIndex = 0; ByteIndex < sizeof(T); ++ByteIndex) +#endif // PLATFORM_LITTLE_ENDIAN + { + ByteStream[WriteIndex++] = (Value >> (8 * ByteIndex)) & 0xFF; + } + } + } + + template + T ReadFromArray(const T* ElementArray, int32& ReadIndex, int32 NumElements, int32 IndexStride = 1) + { + T OutputValue = 0; + + if (ReadIndex >= 0 && ReadIndex < NumElements) + { + OutputValue = ElementArray[ReadIndex]; + ReadIndex += IndexStride; + } + + return OutputValue; + } +} // end namespace + + + +namespace LPCM +{ + void GenerateCompressedData(const TArray& InputPCMData, TArray& CompressedDataStore, + const FSoundQualityInfo& QualityInfo) + { + WaveFormatHeader Format; + Format.nChannels = static_cast(QualityInfo.NumChannels); + Format.nSamplesPerSec = QualityInfo.SampleRate; + Format.nBlockAlign = static_cast(Format.nChannels * sizeof(int16)); + Format.nAvgBytesPerSec = Format.nBlockAlign * QualityInfo.SampleRate; + Format.wBitsPerSample = 16; + Format.wFormatTag = WAVE_FORMAT_LPCM; + + RiffDataChunk RiffDataChunks[2]; + RiffDataChunks[0].ID = FORMAT_FOURCC('f', 'm', 't', ' '); + RiffDataChunks[0].DataSize = sizeof(Format); + RiffDataChunks[0].Data = reinterpret_cast(&Format); + + RiffDataChunks[1].ID = FORMAT_FOURCC('d', 'a', 't', 'a'); + RiffDataChunks[1].DataSize = InputPCMData.Num(); + RiffDataChunks[1].Data = const_cast(InputPCMData.GetData()); + + BaseDataOperations::GenerateWaveFile(RiffDataChunks, CompressedDataStore); + } +} // end namespace LPCM + +namespace ADPCM +{ + template + static void GetAdaptationTable(T (& OutAdaptationTable)[NUM_ADAPTATION_TABLE]) + { + static T AdaptationTable[] = + { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + + FMemory::Memcpy(&OutAdaptationTable, AdaptationTable, sizeof(AdaptationTable)); + } + + struct FAdaptationContext + { + int32 AdaptationTable[NUM_ADAPTATION_TABLE]; + int32 AdaptationCoefficient1[NUM_ADAPTATION_COEFF]; + int32 AdaptationCoefficient2[NUM_ADAPTATION_COEFF]; + + int32 AdaptationDelta; + int32 Coefficient1; + int32 Coefficient2; + int32 Sample1; + int32 Sample2; + + FAdaptationContext() : + AdaptationDelta(0), + Coefficient1(0), + Coefficient2(0), + Sample1(0), + Sample2(0) + { + GetAdaptationTable(AdaptationTable); + GetAdaptationCoefficients(AdaptationCoefficient1, AdaptationCoefficient2); + } + }; + + uint8 EncodeNibble(FAdaptationContext& Context, int16 NextSample) + { + int32 PredictedSample = (Context.Sample1 * Context.Coefficient1 + Context.Sample2 * Context.Coefficient2) / 256; + int32 ErrorDelta = (NextSample - PredictedSample) / Context.AdaptationDelta; + ErrorDelta = FMath::Clamp(ErrorDelta, -8, 7); + + PredictedSample += (Context.AdaptationDelta * ErrorDelta); + PredictedSample = FMath::Clamp(PredictedSample, -32768, 32767); + + int8 SmallDelta = static_cast(ErrorDelta); + const uint8 EncodedNibble = reinterpret_cast(SmallDelta) & 0x0F; + + Context.Sample2 = Context.Sample1; + Context.Sample1 = static_cast(PredictedSample); + Context.AdaptationDelta = (Context.AdaptationDelta * Context.AdaptationTable[EncodedNibble]) / 256; + Context.AdaptationDelta = FMath::Max(Context.AdaptationDelta, 16); + + return EncodedNibble; + } + + int32 EncodeBlock(const int16* InputPCMSamples, int32 SampleStride, int32 NumSamples, int32 BlockSize, + uint8* EncodedADPCMData) + { + FAdaptationContext Context; + int32 ReadIndex = 0; + int32 WriteIndex = 0; + + const uint8 CoefficientIndex = 0; + Context.AdaptationDelta = Context.AdaptationTable[0]; + + Context.Sample2 = BaseDataOperations::ReadFromArray(InputPCMSamples, ReadIndex, NumSamples, SampleStride); + Context.Sample1 = BaseDataOperations::ReadFromArray(InputPCMSamples, ReadIndex, NumSamples, SampleStride); + Context.Coefficient1 = Context.AdaptationCoefficient1[CoefficientIndex]; + Context.Coefficient2 = Context.AdaptationCoefficient2[CoefficientIndex]; + + BaseDataOperations::WriteToByteStream(CoefficientIndex, EncodedADPCMData, WriteIndex); + BaseDataOperations::WriteToByteStream(Context.AdaptationDelta, EncodedADPCMData, WriteIndex); + BaseDataOperations::WriteToByteStream(Context.Sample1, EncodedADPCMData, WriteIndex); + BaseDataOperations::WriteToByteStream(Context.Sample2, EncodedADPCMData, WriteIndex); + + while (WriteIndex < BlockSize) + { + EncodedADPCMData[WriteIndex] = EncodeNibble( + Context, BaseDataOperations::ReadFromArray(InputPCMSamples, ReadIndex, NumSamples, SampleStride)) << 4; + EncodedADPCMData[WriteIndex++] |= EncodeNibble( + Context, BaseDataOperations::ReadFromArray(InputPCMSamples, ReadIndex, NumSamples, SampleStride)); + } + + return WriteIndex; + } + + void GenerateCompressedData(const TArray& InputPCMData, TArray& CompressedDataStore, + const FSoundQualityInfo& QualityInfo) + { + const int32 SourceSampleStride = QualityInfo.NumChannels; + + const int32 SourceNumSamples = QualityInfo.SampleDataSize / 2; + const int32 SourceNumSamplesPerChannel = SourceNumSamples / QualityInfo.NumChannels; + + const int32 CompressedNumSamplesPerByte = 2; + const int32 PreambleSamples = 2; + const int32 BlockSize = 512; + const int32 PreambleSize = 2 * PreambleSamples + 3; + const int32 CompressedSamplesPerBlock = (BlockSize - PreambleSize) * CompressedNumSamplesPerByte + + PreambleSamples; + const int32 NumBlocksPerChannel = (SourceNumSamplesPerChannel + CompressedSamplesPerBlock - 1) / + CompressedSamplesPerBlock; + + const uint32 EncodedADPCMDataSize = NumBlocksPerChannel * BlockSize * QualityInfo.NumChannels; + uint8* EncodedADPCMData = static_cast(FMemory::Malloc(EncodedADPCMDataSize)); + FMemory::Memzero(EncodedADPCMData, EncodedADPCMDataSize); + + const int16* InputPCMSamples = reinterpret_cast(InputPCMData.GetData()); + uint8* EncodedADPCMChannelData = EncodedADPCMData; + + for (uint32 ChannelIndex = 0; ChannelIndex < QualityInfo.NumChannels; ++ChannelIndex) + { + const int16* ChannelPCMSamples = InputPCMSamples + ChannelIndex; + int32 SourceSampleOffset = 0; + int32 DestDataOffset = 0; + + for (int32 BlockIndex = 0; BlockIndex < NumBlocksPerChannel; ++BlockIndex) + { + EncodeBlock(ChannelPCMSamples + SourceSampleOffset, SourceSampleStride, + SourceNumSamples - SourceSampleOffset, BlockSize, EncodedADPCMChannelData + DestDataOffset); + + SourceSampleOffset += CompressedSamplesPerBlock * SourceSampleStride; + DestDataOffset += BlockSize; + } + + EncodedADPCMChannelData += DestDataOffset; + } + + ADPCMFormatHeader Format; + Format.BaseFormat.nChannels = static_cast(QualityInfo.NumChannels); + Format.BaseFormat.nSamplesPerSec = QualityInfo.SampleRate; + Format.BaseFormat.nBlockAlign = static_cast(BlockSize); + Format.BaseFormat.wBitsPerSample = 4; + Format.BaseFormat.wFormatTag = WAVE_FORMAT_ADPCM; + Format.wSamplesPerBlock = static_cast(CompressedSamplesPerBlock); + Format.BaseFormat.nAvgBytesPerSec = ((Format.BaseFormat.nSamplesPerSec / Format.wSamplesPerBlock) * Format. + BaseFormat.nBlockAlign); + Format.wNumCoef = NUM_ADAPTATION_COEFF; + Format.SamplesPerChannel = SourceNumSamplesPerChannel; + Format.BaseFormat.cbSize = sizeof(Format) - sizeof(Format.BaseFormat); + + RiffDataChunk RiffDataChunks[2]; + RiffDataChunks[0].ID = FORMAT_FOURCC('f', 'm', 't', ' '); + RiffDataChunks[0].DataSize = sizeof(Format); + RiffDataChunks[0].Data = reinterpret_cast(&Format); + + RiffDataChunks[1].ID = FORMAT_FOURCC('d', 'a', 't', 'a'); + RiffDataChunks[1].DataSize = EncodedADPCMDataSize; + RiffDataChunks[1].Data = EncodedADPCMData; + + BaseDataOperations::GenerateWaveFile(RiffDataChunks, CompressedDataStore); + FMemory::Free(EncodedADPCMData); + } +} // end namespace ADPCM + + +bool FAdvancedPCMCompressor::GenerateCompressedData(const TArray& SrcBuffer, FSoundQualityInfo& QualityInfo, + TArray& OutBuffer) +{ + if (QualityInfo.Quality == 100) + { + LPCM::GenerateCompressedData(SrcBuffer, OutBuffer, QualityInfo); + } + else + { + ADPCM::GenerateCompressedData(SrcBuffer, OutBuffer, QualityInfo); + } + + return OutBuffer.Num() > 0; +} + +void FAdvancedPCMCompressor::AddNewChunk(TArray>& OutBuffers, int32 ChunkReserveSize) +{ + TArray& NewBuffer = *new(OutBuffers) TArray; + NewBuffer.Empty(ChunkReserveSize); +} + +void FAdvancedPCMCompressor::AddChunkData(TArray>& OutBuffers, const uint8* ChunkData, + int32 ChunkDataSize) +{ + TArray& TargetBuffer = OutBuffers[OutBuffers.Num() - 1]; + TargetBuffer.Append(ChunkData, ChunkDataSize); +} diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/OpusCompressor.cpp b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/OpusCompressor.cpp new file mode 100644 index 0000000..4810b4a --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/OpusCompressor.cpp @@ -0,0 +1,202 @@ +// Georgy Treshchev 2021. + +#include "OpusCompressor.h" +#include "Interfaces/IAudioFormat.h" +#include "Serialization/MemoryWriter.h" +#include "OpusAudioInfo.h" + +#define OUTSIDE_SPEEX + +THIRD_PARTY_INCLUDES_START +#include "opus_multistream.h" +#include "speex_resampler.h" +THIRD_PARTY_INCLUDES_END + + +#define SAMPLE_SIZE ( ( uint32 )sizeof( short ) ) + +bool FOpusCompressor::GenerateCompressedData(const TArray& SrcBuffer, FSoundQualityInfo& QualityInfo, + TArray& CompressedDataStore) +{ + const uint16 OpusSampleRate = GetBestOutputSampleRate(QualityInfo.SampleRate); + const int32 OpusFrameSizeMs = 60; + const int32 OpusFrameSizeSamples = (OpusSampleRate * OpusFrameSizeMs) / 1000; + const uint32 SampleStride = SAMPLE_SIZE * QualityInfo.NumChannels; + const int32 BytesPerFrame = OpusFrameSizeSamples * SampleStride; + + TArray SrcBufferCopy; + if (QualityInfo.SampleRate != OpusSampleRate) + { + if (!ResamplePCM(QualityInfo.NumChannels, SrcBuffer, QualityInfo.SampleRate, SrcBufferCopy, OpusSampleRate)) + { + return false; + } + } + else + { + SrcBufferCopy = SrcBuffer; + } + + const int32 EncSize = opus_encoder_get_size(QualityInfo.NumChannels); + OpusEncoder* Encoder = static_cast(FMemory::Malloc(EncSize)); + const int32 EncError = opus_encoder_init(Encoder, OpusSampleRate, QualityInfo.NumChannels, OPUS_APPLICATION_AUDIO); + if (EncError != OPUS_OK) + { + FMemory::Free(Encoder); + return false; + } + + const int32 BitRate = GetBitRateFromQuality(QualityInfo); + opus_encoder_ctl(Encoder, OPUS_SET_BITRATE(BitRate)); + + CompressedDataStore.Empty(); + FMemoryWriter CompressedData(CompressedDataStore); + int32 SrcBufferOffset = 0; + + int32 FramesToEncode = SrcBufferCopy.Num() / BytesPerFrame; + const uint32 AccurateSampleCount = SrcBufferCopy.Num() / SampleStride; + + if (SrcBufferCopy.Num() % BytesPerFrame != 0) + { + int32 FrameDiff = BytesPerFrame - (SrcBufferCopy.Num() % BytesPerFrame); + SrcBufferCopy.AddZeroed(FrameDiff); + FramesToEncode++; + } + + check(QualityInfo.NumChannels <= MAX_uint8); + check(FramesToEncode <= MAX_uint16); + SerializeHeaderData(CompressedData, OpusSampleRate, AccurateSampleCount, QualityInfo.NumChannels, FramesToEncode); + + TArray TempCompressedData; + TempCompressedData.AddUninitialized(BytesPerFrame); + + while (SrcBufferOffset < SrcBufferCopy.Num()) + { + const int32 CompressedDataLength = opus_encode( + Encoder, reinterpret_cast(SrcBufferCopy.GetData() + SrcBufferOffset), + OpusFrameSizeSamples, TempCompressedData.GetData(), TempCompressedData.Num()); + + if (CompressedDataLength < 0) + { + const char* ErrorStr = opus_strerror(CompressedDataLength); + UE_LOG(LogAudio, Warning, TEXT("Failed to encode: [%d] %s"), CompressedDataLength, ANSI_TO_TCHAR(ErrorStr)); + + FMemory::Free(Encoder); + + CompressedDataStore.Empty(); + return false; + } + else + { + check(CompressedDataLength < MAX_uint16); + SerialiseFrameData(CompressedData, TempCompressedData.GetData(), CompressedDataLength); + SrcBufferOffset += BytesPerFrame; + } + } + + FMemory::Free(Encoder); + + return CompressedDataStore.Num() > 0; +} + +uint16 FOpusCompressor::GetBestOutputSampleRate(int32 SampleRate) +{ + static const uint16 ValidSampleRates[] = + { + 0, + 8000, + 12000, + 16000, + 24000, + 48000, + }; + + for (int32 Index = UE_ARRAY_COUNT(ValidSampleRates) - 2; Index >= 0; Index--) + { + if (SampleRate > ValidSampleRates[Index]) + { + return ValidSampleRates[Index + 1]; + } + } + check(0); + return 0; +} + +bool FOpusCompressor::ResamplePCM(uint32 NumChannels, const TArray& InBuffer, uint32 InSampleRate, + TArray& OutBuffer, uint32 OutSampleRate) +{ + int32 err = 0; + SpeexResamplerState* resampler = speex_resampler_init(NumChannels, InSampleRate, OutSampleRate, + SPEEX_RESAMPLER_QUALITY_DESKTOP, &err); + if (err != RESAMPLER_ERR_SUCCESS) + { + speex_resampler_destroy(resampler); + return false; + } + + const uint32 SampleStride = SAMPLE_SIZE * NumChannels; + const float Duration = (float)InBuffer.Num() / (InSampleRate * SampleStride); + const int32 SafeCopySize = (Duration + 1) * OutSampleRate * SampleStride; + OutBuffer.Empty(SafeCopySize); + OutBuffer.AddUninitialized(SafeCopySize); + uint32 InSamples = InBuffer.Num() / SampleStride; + uint32 OutSamples = OutBuffer.Num() / SampleStride; + + if (NumChannels == 1) + { + err = speex_resampler_process_int(resampler, 0, (const short*)(InBuffer.GetData()), &InSamples, + (short*)(OutBuffer.GetData()), &OutSamples); + } + else + { + err = speex_resampler_process_interleaved_int(resampler, (const short*)(InBuffer.GetData()), &InSamples, + (short*)(OutBuffer.GetData()), &OutSamples); + } + + speex_resampler_destroy(resampler); + if (err != RESAMPLER_ERR_SUCCESS) + { + return false; + } + + const int32 WrittenBytes = (int32)(OutSamples * SampleStride); + if (WrittenBytes < OutBuffer.Num()) + { + OutBuffer.SetNum(WrittenBytes, true); + } + + return true; +} + +int32 FOpusCompressor::GetBitRateFromQuality(FSoundQualityInfo& QualityInfo) +{ + const int32 OriginalBitRate = QualityInfo.SampleRate * QualityInfo.NumChannels * SAMPLE_SIZE * 8; + return static_cast(OriginalBitRate) * FMath::GetMappedRangeValueClamped( + FVector2D(1, 100), FVector2D(0.04, 0.25), QualityInfo.Quality); +} + +void FOpusCompressor::SerializeHeaderData(FMemoryWriter& CompressedData, uint16 SampleRate, uint32 TrueSampleCount, + uint8 NumChannels, uint16 NumFrames) +{ + const char* OpusIdentifier = OPUS_ID_STRING; + CompressedData.Serialize((void*)OpusIdentifier, FCStringAnsi::Strlen(OpusIdentifier) + 1); + CompressedData.Serialize(&SampleRate, sizeof(uint16)); + CompressedData.Serialize(&TrueSampleCount, sizeof(uint32)); + CompressedData.Serialize(&NumChannels, sizeof(uint8)); + CompressedData.Serialize(&NumFrames, sizeof(uint16)); +} + +void FOpusCompressor::SerialiseFrameData(FMemoryWriter& CompressedData, uint8* FrameData, uint16 FrameSize) +{ + CompressedData.Serialize(&FrameSize, sizeof(uint16)); + CompressedData.Serialize(FrameData, FrameSize); +} + +int32 FOpusCompressor::AddDataChunk(TArray>& OutBuffers, const uint8* ChunkData, int32 ChunkSize) +{ + TArray& NewBuffer = *new(OutBuffers) TArray; + NewBuffer.Empty(ChunkSize); + NewBuffer.AddUninitialized(ChunkSize); + FMemory::Memcpy(NewBuffer.GetData(), ChunkData, ChunkSize); + return ChunkSize; +} diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/PreimportedSoundAsset.cpp b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/PreimportedSoundAsset.cpp new file mode 100644 index 0000000..b37f557 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/PreimportedSoundAsset.cpp @@ -0,0 +1,6 @@ +// Georgy Treshchev 2021. + +#include "PreimportedSoundAsset.h" + + + diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/RuntimeAudioImporter.cpp b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/RuntimeAudioImporter.cpp index 6818d63..2a300ba 100644 --- a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/RuntimeAudioImporter.cpp +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/RuntimeAudioImporter.cpp @@ -1,4 +1,4 @@ -// Respirant 2020. +// Georgy Treshchev 2021. #include "RuntimeAudioImporter.h" @@ -6,15 +6,10 @@ void FRuntimeAudioImporterModule::StartupModule() { - // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module - } void FRuntimeAudioImporterModule::ShutdownModule() { - // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, - // we call this function before unloading the module. - } #undef LOCTEXT_NAMESPACE diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/RuntimeAudioImporterLibrary.cpp b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/RuntimeAudioImporterLibrary.cpp index f80330f..fb2e720 100644 --- a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/RuntimeAudioImporterLibrary.cpp +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/RuntimeAudioImporterLibrary.cpp @@ -1,11 +1,16 @@ -// Respirant 2020. +// Georgy Treshchev 2021. #include "RuntimeAudioImporterLibrary.h" -URuntimeAudioImporterLibrary::URuntimeAudioImporterLibrary(const FObjectInitializer& ObjectInitializer) - : Super(ObjectInitializer) -{ -} +#include "AudioDevice.h" +#include "AudioCompressionSettingsUtils.h" +#include "Interfaces/IAudioFormat.h" + +// Compressors for platform-specific audio formats +#include "VorbisCompressor.h" +#include "OpusCompressor.h" +#include "AdvancedPCMCompressor.h" + URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::CreateRuntimeAudioImporter() { @@ -14,14 +19,48 @@ URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::CreateRuntimeAudioIm return Importer; } -URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::ImportAudioFromFile( - const FString& FilePath, TEnumAsByte Format, bool DefineFormatAutomatically) +void URuntimeAudioImporterLibrary::ImportAudioFromFile( + const FString& FilePath, TEnumAsByte Format, const FBuffersDetailsStruct& BuffersDetails) +{ + if (!FPaths::FileExists(FilePath)) + { + // Trying to get a file inside packaged build + IPlatformFile& FileManager = FPlatformFileManager::Get().GetPlatformFile(); + + OnResult_Internal(nullptr, ETranscodingStatus::AudioDoesNotExist); + return; + } + + + if (Format == EAudioFormat::Auto) + { + Format = GetAudioFormat(FilePath); + } + + TArray AudioBuffer; + if (!FFileHelper::LoadFileToArray(AudioBuffer, *FilePath)) + { + // Callback Dispatcher OnResult + OnResult_Internal(nullptr, ETranscodingStatus::LoadFileToArrayError); + return; + } + ImportAudioFromBuffer(AudioBuffer, Format, BuffersDetails); +} + +void URuntimeAudioImporterLibrary::ImportAudioFromPreimportedSound(UPreimportedSoundAsset* PreimportedSoundAssetRef, + const FBuffersDetailsStruct& BuffersDetails) +{ + ImportAudioFromBuffer(PreimportedSoundAssetRef->AudioDataArray, EAudioFormat::Mp3, BuffersDetails); +} + +void URuntimeAudioImporterLibrary::ImportAudioFromBuffer(const TArray& AudioDataArray, + const TEnumAsByte& Format, + const FBuffersDetailsStruct& BuffersDetails) { AsyncTask(ENamedThreads::AnyThread, [=]() { - GetUSoundWaveFromAudioFile_Internal(FilePath, Format, DefineFormatAutomatically); + ImportAudioFromBuffer_Internal(AudioDataArray, Format, BuffersDetails); }); - return this; } bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave) @@ -52,431 +91,540 @@ bool URuntimeAudioImporterLibrary::DestroySoundWave(USoundWave* ReadySoundWave) } } -/** - * Including dr_wav, dr_mp3 and dr_flac libraries - */ -#include "ThirdParty/dr_wav.h" -#include "ThirdParty/dr_mp3.h" -#include "ThirdParty/dr_flac.h" - -/** - * Replacing standard CPP memory functions (malloc, realloc, free) with memory management functions that are maintained by Epic as the engine evolves. - */ -void* unreal_malloc(size_t sz, void* pUserData) +void URuntimeAudioImporterLibrary::ImportAudioFromBuffer_Internal( + const TArray& AudioDataArray, const TEnumAsByte& Format, + const FBuffersDetailsStruct& BuffersDetails) { - return FMemory::Malloc(sz); -} + BuffersDetailsInfo = BuffersDetails; -void* unreal_realloc(void* p, size_t sz, void* pUserData) -{ - return FMemory::Realloc(p, sz); -} -void unreal_free(void* p, void* pUserData) -{ - FMemory::Free(p); -} - -bool URuntimeAudioImporterLibrary::ExportSoundWaveToFile(USoundWave* SoundWaveToExport, FString PathToExport) -{ - uint8* PCMData = SoundWaveToExport->RawPCMData; - size_t PCMDataSize = SoundWaveToExport->RawPCMDataSize; - - drwav_allocation_callbacks allocationCallbacksDecoding; - allocationCallbacksDecoding.pUserData = NULL; - allocationCallbacksDecoding.onMalloc = unreal_malloc; - allocationCallbacksDecoding.onRealloc = unreal_realloc; - allocationCallbacksDecoding.onFree = unreal_free; - - drwav_uint64 framesToWrite = PCMDataSize / sizeof(DR_WAVE_FORMAT_PCM) / SoundWaveToExport->NumChannels; - - uint16 ChannelCount = SoundWaveToExport->NumChannels; - uint32 SampleRate = FGenericPlatformMath::RoundToInt( - (framesToWrite / (float)SoundWaveToExport->GetDuration() * ChannelCount)); - - drwav wavEncode; - - drwav_data_format format; - format.container = drwav_container_riff; - format.format = DR_WAVE_FORMAT_PCM; - format.channels = ChannelCount; - format.sampleRate = SampleRate; - format.bitsPerSample = 16; - -#if PLATFORM_WINDOWS - if (!drwav_init_file_write_w(&wavEncode, (wchar_t*)PathToExport.GetCharArray().GetData(), &format, - &allocationCallbacksDecoding)) -#else - if (!drwav_init_file_write(&wavEncode, TCHAR_TO_UTF8(*PathToExport), &format, - &allocationCallbacksDecoding)) -#endif + if (Format == EAudioFormat::Auto || Format == EAudioFormat::Invalid) { - return false; - } - - drwav_write_pcm_frames(&wavEncode, framesToWrite, PCMData); - drwav_uninit(&wavEncode); - - return true; -} - -URuntimeAudioImporterLibrary* URuntimeAudioImporterLibrary::GetUSoundWaveFromAudioFile_Internal( - const FString& FilePath, TEnumAsByte Format, bool DefineFormatAutomatically) -{ - TEnumAsByte Status; - if (FPaths::FileExists(FilePath) != true) - { - Status = TranscodingStatus::AudioDoesNotExist; - // Callback Dispatcher OnResult - OnResult_Internal(nullptr, Status); + OnResult_Internal(nullptr, ETranscodingStatus::InvalidAudioFormat); + return; } - else + + // Transcoding importer Audio Data to PCM Data + if (!TranscodeAudioDataArrayToPCMData(AudioDataArray.GetData(), AudioDataArray.Num() - 2, Format)) { - int16_t* pSampleData; - uint64 framesToWrite; - uint32 channels; - uint32 sampleRate; - - if (DefineFormatAutomatically) - { - Format = GetAudioFormat(FilePath); - } - - if (Format == AudioFormat::INVALID) - { - Status = TranscodingStatus::InvalidAudioFormat; - - // Callback Dispatcher OnResult - OnResult_Internal(nullptr, Status); - } - else - { - if (TranscodeAudioFileToPCMData(FilePath, Format, Status, framesToWrite, pSampleData, channels, sampleRate) - == false) - { - // Callback Dispatcher OnResult - OnResult_Internal(nullptr, Status); - } - else - { - void* WaveData; - size_t WaveDataSize; - - if (TranscodePCMToWAVData(*&WaveData, *&WaveDataSize, framesToWrite, *&pSampleData, channels, - sampleRate) == false) - { - FMemory::Free(pSampleData); - - // Callback Dispatcher OnResult - OnResult_Internal(nullptr, Status); - } - else - { - USoundWave* ReadyUSoundWave = GetSoundWaveObject((const uint8*)WaveData, WaveDataSize, Status); - // A ready USoundWave static object - - /* Free temporary transcoding data */ - FMemory::Free(WaveData); - FMemory::Free(pSampleData); - /* Free temporary transcoding data */ - - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(100); - - // Callback Dispatcher OnResult - OnResult_Internal(ReadyUSoundWave, Status); - } - } - } + return; } - return this; + + // A ready USoundWave static object + USoundWave* ReadySoundWave = DefineSoundWave(); + + if (ReadySoundWave == nullptr) + { + return; + } + + // Callback Dispatcher OnProgress + OnProgress_Internal(100); + + // Callback Dispatcher OnResult, with the created SoundWave object + OnResult_Internal(ReadySoundWave, ETranscodingStatus::SuccessImporting); } -class USoundWave* URuntimeAudioImporterLibrary::GetSoundWaveObject(const uint8* WaveData, int32 WaveDataSize, - TEnumAsByte& Status) +USoundWave* URuntimeAudioImporterLibrary::DefineSoundWave() { - USoundWave* sw = NewObject(USoundWave::StaticClass()); - if (!sw) + USoundWave* SoundWaveRef = NewObject(USoundWave::StaticClass()); + if (!SoundWaveRef) { - Status = TranscodingStatus::USoundWaveDeclarationError; + OnResult_Internal(nullptr, ETranscodingStatus::SoundWaveDeclarationError); return nullptr; } - FWaveModInfo WaveInfo; - FString ErrorReason; - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(65); + // Callback Dispatcher OnProgress + OnProgress_Internal(40); - if (WaveInfo.ReadWaveInfo(WaveData, WaveDataSize, &ErrorReason)) + // Fill SoundWave basic information (e.g. duration, number of channels, etc) + FillSoundWaveBasicInfo(SoundWaveRef); + + // Callback Dispatcher OnProgress + OnProgress_Internal(45); + + // Whether to fill the compressed buffer or not. If not filled, it is necessary to fill Raw (Wave) data so that the engine will automatically generate compressed data. + if (!BuffersDetailsInfo.CompressionFormat == ECompressionFormat::RawData) { - // Callback Dispatcher OnProgress (not completely accurate implementation) + // Callback Dispatcher OnProgress + OnProgress_Internal(60); + + // Transcode PCM to Raw (Wave) Data + if (!TranscodePCMToWAVData()) + { + FMemory::Free(TranscodingFillInfo.PCMInfo.RawPCMData); + return nullptr; + } + + // free unneeded PCM Data + FMemory::Free(TranscodingFillInfo.PCMInfo.RawPCMData); + + // Callback Dispatcher OnProgress OnProgress_Internal(75); - if (*WaveInfo.pBitsPerSample != 16) - { - Status = TranscodingStatus::UnsupportedBitDepth; - return nullptr; - } - sw->RawData.Lock(LOCK_READ_WRITE); - FMemory::Memcpy(sw->RawData.Realloc(WaveDataSize), WaveData, WaveDataSize); - sw->RawData.Unlock(); + // Fill Raw (Wave) Data + FillRawWaveData(SoundWaveRef); - OnProgress_Internal(85); - - // RawPCMDataSize = WaveDataSize - 44 bytes (the header of the WAV data) = SampleDataSize - sw->RawPCMDataSize = WaveInfo.SampleDataSize; - sw->RawPCMData = (uint8*)FMemory::Malloc(sw->RawPCMDataSize); - FMemory::Memmove(sw->RawPCMData, WaveData, sw->RawPCMDataSize); - - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(90); - - int32 DurationDiv = *WaveInfo.pChannels * *WaveInfo.pBitsPerSample * *WaveInfo.pSamplesPerSec; - if (DurationDiv) - { - sw->Duration = *WaveInfo.pWaveDataSize * 8.0f / DurationDiv; - } - else - { - sw->Duration = 0.0f; - Status = TranscodingStatus::ZeroDurationError; - return nullptr; - } - sw->SetSampleRate(*WaveInfo.pSamplesPerSec); - sw->NumChannels = *WaveInfo.pChannels; - sw->RawPCMDataSize = WaveInfo.SampleDataSize; - sw->SoundGroup = ESoundGroup::SOUNDGROUP_Default; - - // Callback Dispatcher OnProgress (not completely accurate implementation) + // Callback Dispatcher OnProgress OnProgress_Internal(95); + + return SoundWaveRef; } - else + + /* We don't need to fill RawPCMData buffer since it is not used at runtime during audio playback. But in case you want to fill this buffer - uncomment the code below (you should also remove some RawPCMData cleanups in code before it is executed) + sw->RawPCMData = static_cast(FMemory::Malloc(sw->RawPCMDataSize)); + FMemory::Memmove(sw->RawPCMData, TranscodingFillInfo.PCMInfo.RawPCMData, sw->RawPCMDataSize); + */ + + // Set a new sound compression quality + SoundWaveRef->CompressionQuality = BuffersDetailsInfo.SoundCompressionQuality; + + // This variable won't be used anywhere, but it is recommended to set a decompression type to realtime since it will be decompressed at runtime + SoundWaveRef->DecompressionType = EDecompressionType::DTYPE_RealTime; + + // Callback Dispatcher OnProgress + OnProgress_Internal(60); + + // Transcode Raw PCM data to Compressed data + if (!TranscodePCMToCompressedData(SoundWaveRef)) { - if (ErrorReason.Equals(TEXT("Invalid WAVE file."), ESearchCase::CaseSensitive)) - { - Status = TranscodingStatus::InvalidWAVEData; - } - else if (ErrorReason.Equals( - TEXT("Unsupported WAVE file format: actual bit rate does not match the container size."), - ESearchCase::CaseSensitive)) - { - Status = TranscodingStatus::InvalidBitrate; - } - else if (ErrorReason.Equals( - TEXT("Unsupported WAVE file format: subformat identifier not recognized."), ESearchCase::CaseSensitive)) - { - Status = TranscodingStatus::InvalidSubformat; - } - else - { - Status = TranscodingStatus::UnrecognizedReadWaveError; - } return nullptr; } - if (!sw) - { - Status = TranscodingStatus::InvalidUSoundWave; - return nullptr; - } - Status = TranscodingStatus::SuccessTranscoding; - return sw; + // Callback Dispatcher OnProgress + OnProgress_Internal(75); + + SoundWaveRef->SetPrecacheState(ESoundWavePrecacheState::NotStarted); + + // Fill the compressed data + FillCompressedData(SoundWaveRef); + + // Callback Dispatcher OnProgress + OnProgress_Internal(95); + + + return SoundWaveRef; } -bool URuntimeAudioImporterLibrary::TranscodeAudioFileToPCMData(FString FilePath, TEnumAsByte Format, - TEnumAsByte& Status, - uint64& framesToWrite, int16_t*& pSampleData, - uint32& channels, uint32& sampleRate) +void URuntimeAudioImporterLibrary::FillSoundWaveBasicInfo(USoundWave* SoundWaveRef) const { - OnProgress_Internal(5); - switch (Format) + SoundWaveRef->RawPCMDataSize = TranscodingFillInfo.PCMInfo.RawPCMDataSize; + SoundWaveRef->Duration = TranscodingFillInfo.SoundWaveBasicInfo.Duration; + SoundWaveRef->SetSampleRate(TranscodingFillInfo.SoundWaveBasicInfo.SampleRate); + SoundWaveRef->NumChannels = TranscodingFillInfo.SoundWaveBasicInfo.ChannelsNum; + SoundWaveRef->SoundGroup = ESoundGroup::SOUNDGROUP_Default; + + if (SoundWaveRef->NumChannels == 4) { - case AudioFormat::MP3: + SoundWaveRef->bIsAmbisonics = 1; + } +} + +void URuntimeAudioImporterLibrary::FillRawWaveData(USoundWave* SoundWaveRef) const +{ + SoundWaveRef->RawData.Lock(LOCK_READ_WRITE); + FMemory::Memmove(SoundWaveRef->RawData.Realloc(TranscodingFillInfo.WAVInfo.WaveDataSize), + TranscodingFillInfo.WAVInfo.WaveData, TranscodingFillInfo.WAVInfo.WaveDataSize); + SoundWaveRef->RawData.Unlock(); +} + +void URuntimeAudioImporterLibrary::FillCompressedData(USoundWave* SoundWaveRef) +{ + // Get the name of the runtime format that will be used during audio playback + const FName CurrentAudioFormat = GEngine->GetAudioDeviceManager()->GetActiveAudioDevice()-> + GetRuntimeFormat(SoundWaveRef); + + SoundWaveRef->SetPrecacheState(ESoundWavePrecacheState::InProgress); + + // Fill the compressed data + FByteBulkData* CompressedBulkData = &SoundWaveRef->CompressedFormatData.GetFormat( + GetPlatformSpecificFormat(CurrentAudioFormat)); + CompressedBulkData->Lock(LOCK_READ_WRITE); + FMemory::Memmove(CompressedBulkData->Realloc(TranscodingFillInfo.CompressedInfo.CompressedFormatData.Num()), + TranscodingFillInfo.CompressedInfo.CompressedFormatData.GetData(), + TranscodingFillInfo.CompressedInfo.CompressedFormatData.Num()); + CompressedBulkData->Unlock(); + + SoundWaveRef->SetPrecacheState(ESoundWavePrecacheState::Done); +} + +bool URuntimeAudioImporterLibrary::TranscodePCMToCompressedData(USoundWave* SoundWaveToGetData) +{ + // Raw PCM data Array + TArray RawPCMArray = TArray(TranscodingFillInfo.PCMInfo.RawPCMData, + TranscodingFillInfo.PCMInfo.RawPCMDataSize); + + // Sound quality information for compression purposes + FSoundQualityInfo SoundQualityInfo = GenerateSoundQualityInfo(SoundWaveToGetData); + + switch (BuffersDetailsInfo.CompressionFormat) + { + case OggVorbis: { - drmp3_allocation_callbacks allocationCallbacksDecoding; - allocationCallbacksDecoding.pUserData = NULL; - allocationCallbacksDecoding.onMalloc = unreal_malloc; - allocationCallbacksDecoding.onRealloc = unreal_realloc; - allocationCallbacksDecoding.onFree = unreal_free; - - drmp3 mp3; -#if PLATFORM_WINDOWS - if (!drmp3_init_file_w(&mp3, (wchar_t*)FilePath.GetCharArray().GetData(), &allocationCallbacksDecoding)) -#else - if (!drmp3_init_file(&mp3, TCHAR_TO_UTF8(*FilePath), &allocationCallbacksDecoding)) -#endif + if (!FVorbisCompressor::GenerateCompressedData(RawPCMArray, SoundQualityInfo, + TranscodingFillInfo.CompressedInfo.CompressedFormatData)) { - Status = TranscodingStatus::FailedToOpenStream; + // Clearing all data in case of error for performance reasons + TranscodingFillInfo.CompressedInfo.CompressedFormatData.Empty(); + RawPCMArray.Empty(); + FMemory::Free(TranscodingFillInfo.PCMInfo.RawPCMData); + OnResult_Internal(nullptr, ETranscodingStatus::GenerateCompressedDataError); return false; } - else - { - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(10); - - pSampleData = (int16_t*)FMemory::Malloc( - (size_t)drmp3_get_pcm_frame_count(&mp3) * mp3.channels * sizeof(int16_t)); - - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(15); - - framesToWrite = drmp3_read_pcm_frames_s16(&mp3, drmp3_get_pcm_frame_count(&mp3), pSampleData); - - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(35); - - channels = mp3.channels; - sampleRate = mp3.sampleRate; - drmp3_uninit(&mp3); - return true; - } break; } - case AudioFormat::WAV: + + case OggOpus: { - drwav_allocation_callbacks allocationCallbacksDecoding; - allocationCallbacksDecoding.pUserData = NULL; - allocationCallbacksDecoding.onMalloc = unreal_malloc; - allocationCallbacksDecoding.onRealloc = unreal_realloc; - allocationCallbacksDecoding.onFree = unreal_free; - - drwav wav; -#if PLATFORM_WINDOWS - if (!drwav_init_file_w(&wav, (wchar_t*)FilePath.GetCharArray().GetData(), &allocationCallbacksDecoding)) -#else - if (!drwav_init_file(&wav, TCHAR_TO_UTF8(*FilePath), &allocationCallbacksDecoding)) -#endif + if (!FOpusCompressor::GenerateCompressedData(RawPCMArray, SoundQualityInfo, + TranscodingFillInfo.CompressedInfo.CompressedFormatData)) { - Status = TranscodingStatus::FailedToOpenStream; + // Clearing all data in case of error for performance reasons + TranscodingFillInfo.CompressedInfo.CompressedFormatData.Empty(); + RawPCMArray.Empty(); + FMemory::Free(TranscodingFillInfo.PCMInfo.RawPCMData); + OnResult_Internal(nullptr, ETranscodingStatus::GenerateCompressedDataError); return false; } - else - { - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(10); - - pSampleData = (int16_t*) - FMemory::Malloc((size_t)wav.totalPCMFrameCount * wav.channels * sizeof(int16_t)); - - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(15); - - framesToWrite = drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, pSampleData); - - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(35); - - channels = wav.channels; - sampleRate = wav.sampleRate; - drwav_uninit(&wav); - return true; - } break; } - case AudioFormat::FLAC: + case ADPCMLPCM: { - drflac_allocation_callbacks allocationCallbacksDecoding; - allocationCallbacksDecoding.pUserData = NULL; - allocationCallbacksDecoding.onMalloc = unreal_malloc; - allocationCallbacksDecoding.onRealloc = unreal_realloc; - allocationCallbacksDecoding.onFree = unreal_free; - -#if PLATFORM_WINDOWS - drflac* pFlac = drflac_open_file_w((wchar_t*)FilePath.GetCharArray().GetData(), - &allocationCallbacksDecoding); -#else - drflac* pFlac = drflac_open_file(TCHAR_TO_UTF8(*FilePath), - &allocationCallbacksDecoding); -#endif - if (pFlac == NULL) + if (!FAdvancedPCMCompressor::GenerateCompressedData(RawPCMArray, SoundQualityInfo, + TranscodingFillInfo.CompressedInfo.CompressedFormatData) + ) { - Status = TranscodingStatus::FailedToOpenStream; + // Clearing all data in case of error for performance reasons + TranscodingFillInfo.CompressedInfo.CompressedFormatData.Empty(); + RawPCMArray.Empty(); + FMemory::Free(TranscodingFillInfo.PCMInfo.RawPCMData); + OnResult_Internal(nullptr, ETranscodingStatus::GenerateCompressedDataError); return false; } - else - { - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(10); - - pSampleData = (int16_t*)FMemory::Malloc( - (size_t)pFlac->totalPCMFrameCount * pFlac->channels * sizeof(int16_t)); - - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(15); - - framesToWrite = drflac_read_pcm_frames_s16(pFlac, pFlac->totalPCMFrameCount, pSampleData); - - // Callback Dispatcher OnProgress (not completely accurate implementation) - OnProgress_Internal(35); - - channels = pFlac->channels; - sampleRate = pFlac->sampleRate; - drflac_close(pFlac); - return true; - } break; } default: { - Status = TranscodingStatus::InvalidAudioFormat; + // Clearing all data in case of error for performance reasons + TranscodingFillInfo.CompressedInfo.CompressedFormatData.Empty(); + RawPCMArray.Empty(); + FMemory::Free(TranscodingFillInfo.PCMInfo.RawPCMData); + OnResult_Internal(nullptr, ETranscodingStatus::InvalidCompressedFormat); + return false; + } + } + + // When the compressed data is generated, RawPCMData is no longer needed. + RawPCMArray.Empty(); + FMemory::Free(TranscodingFillInfo.PCMInfo.RawPCMData); + + // Callback Dispatcher OnProgress + OnProgress_Internal(85); + + return true; +} + + +/** +* Including dr_wav, dr_mp3 and dr_flac libraries +*/ +#include "ThirdParty/dr_wav.h" +#include "ThirdParty/dr_mp3.h" +#include "ThirdParty/dr_flac.h" + +/** +* Replacing standard CPP memory functions (malloc, realloc, free) with memory management functions that are maintained by Epic as the engine evolves. +*/ +void* Unreal_Malloc(size_t sz, void* pUserData) +{ + return FMemory::Malloc(sz); +} + +void* Unreal_Realloc(void* p, size_t sz, void* pUserData) +{ + return FMemory::Realloc(p, sz); +} + +void Unreal_Free(void* p, void* pUserData) +{ + FMemory::Free(p); +} + +bool URuntimeAudioImporterLibrary::TranscodeAudioDataArrayToPCMData(const uint8* AudioData, uint32 AudioDataSize, + TEnumAsByte Format) +{ + OnProgress_Internal(5); + switch (Format) + { + case EAudioFormat::Mp3: + { + drmp3_allocation_callbacks allocationCallbacksDecoding; + allocationCallbacksDecoding.pUserData = nullptr; + allocationCallbacksDecoding.onMalloc = Unreal_Malloc; + allocationCallbacksDecoding.onRealloc = Unreal_Realloc; + allocationCallbacksDecoding.onFree = Unreal_Free; + + drmp3 mp3; + + if (!drmp3_init_memory(&mp3, AudioData, AudioDataSize, &allocationCallbacksDecoding)) + { + // Callback Dispatcher OnResult + OnResult_Internal(nullptr, ETranscodingStatus::FailedToReadAudioDataArray); + return false; + } + + // Callback Dispatcher OnProgress + OnProgress_Internal(10); + + TranscodingFillInfo.PCMInfo.RawPCMData = static_cast(FMemory::Malloc( + static_cast(drmp3_get_pcm_frame_count(&mp3)) * mp3.channels * sizeof(int16_t))); + + + // Callback Dispatcher OnProgress + OnProgress_Internal(15); + + TranscodingFillInfo.PCMInfo.RawPCMNumOfFrames = drmp3_read_pcm_frames_s16( + &mp3, drmp3_get_pcm_frame_count(&mp3), + reinterpret_cast(TranscodingFillInfo.PCMInfo.RawPCMData)); + + // Callback Dispatcher OnProgress + OnProgress_Internal(35); + + + TranscodingFillInfo.PCMInfo.RawPCMDataSize = static_cast(TranscodingFillInfo.PCMInfo. + RawPCMNumOfFrames * mp3.channels * sizeof(int16_t)); + + TranscodingFillInfo.SoundWaveBasicInfo.Duration = static_cast(drmp3_get_pcm_frame_count(&mp3)) / mp3. + sampleRate; + TranscodingFillInfo.SoundWaveBasicInfo.ChannelsNum = mp3.channels; + TranscodingFillInfo.SoundWaveBasicInfo.SampleRate = mp3.sampleRate; + + + drmp3_uninit(&mp3); + return true; + } + case EAudioFormat::Wav: + { + drwav_allocation_callbacks allocationCallbacksDecoding; + allocationCallbacksDecoding.pUserData = nullptr; + allocationCallbacksDecoding.onMalloc = Unreal_Malloc; + allocationCallbacksDecoding.onRealloc = Unreal_Realloc; + allocationCallbacksDecoding.onFree = Unreal_Free; + + drwav wav; + + if (!drwav_init_memory(&wav, AudioData, AudioDataSize, &allocationCallbacksDecoding)) + + { + // Callback Dispatcher OnResult + OnResult_Internal(nullptr, ETranscodingStatus::FailedToReadAudioDataArray); + return false; + } + + // Callback Dispatcher OnProgress + OnProgress_Internal(10); + + + TranscodingFillInfo.PCMInfo.RawPCMData = static_cast(FMemory::Malloc( + static_cast(wav.totalPCMFrameCount) * wav.channels * sizeof(int16_t))); + + // Callback Dispatcher OnProgress + OnProgress_Internal(15); + + TranscodingFillInfo.PCMInfo.RawPCMNumOfFrames = drwav_read_pcm_frames_s16( + &wav, wav.totalPCMFrameCount, reinterpret_cast(TranscodingFillInfo.PCMInfo.RawPCMData)); + + // Callback Dispatcher OnProgress + OnProgress_Internal(35); + + + TranscodingFillInfo.PCMInfo.RawPCMDataSize = static_cast(TranscodingFillInfo.PCMInfo. + RawPCMNumOfFrames * wav.channels * sizeof(int16_t)); + + TranscodingFillInfo.SoundWaveBasicInfo.Duration = static_cast(wav.totalPCMFrameCount) / wav. + sampleRate; + TranscodingFillInfo.SoundWaveBasicInfo.ChannelsNum = wav.channels; + TranscodingFillInfo.SoundWaveBasicInfo.SampleRate = wav.sampleRate; + + + drwav_uninit(&wav); + return true; + } + case EAudioFormat::Flac: + { + drflac_allocation_callbacks allocationCallbacksDecoding; + allocationCallbacksDecoding.pUserData = nullptr; + allocationCallbacksDecoding.onMalloc = Unreal_Malloc; + allocationCallbacksDecoding.onRealloc = Unreal_Realloc; + allocationCallbacksDecoding.onFree = Unreal_Free; + + + drflac* pFlac = drflac_open_memory(AudioData, AudioDataSize, &allocationCallbacksDecoding); + + if (pFlac == nullptr) + { + // Callback Dispatcher OnResult + OnResult_Internal(nullptr, ETranscodingStatus::FailedToReadAudioDataArray); + return false; + } + + // Callback Dispatcher OnProgress + OnProgress_Internal(10); + + + TranscodingFillInfo.PCMInfo.RawPCMData = static_cast(FMemory::Malloc( + static_cast(pFlac->totalPCMFrameCount) * pFlac->channels * sizeof(int16_t))); + + // Callback Dispatcher OnProgress + OnProgress_Internal(15); + + + TranscodingFillInfo.PCMInfo.RawPCMNumOfFrames = drflac_read_pcm_frames_s16( + pFlac, pFlac->totalPCMFrameCount, reinterpret_cast(TranscodingFillInfo.PCMInfo.RawPCMData)); + + // Callback Dispatcher OnProgress + OnProgress_Internal(35); + + + TranscodingFillInfo.PCMInfo.RawPCMDataSize = static_cast(TranscodingFillInfo.PCMInfo. + RawPCMNumOfFrames * pFlac->channels * sizeof(int16_t)); + + + TranscodingFillInfo.SoundWaveBasicInfo.Duration = static_cast(pFlac->totalPCMFrameCount) / pFlac-> + sampleRate; + TranscodingFillInfo.SoundWaveBasicInfo.ChannelsNum = pFlac->channels; + TranscodingFillInfo.SoundWaveBasicInfo.SampleRate = pFlac->sampleRate; + drflac_close(pFlac); + return true; + } + default: + { + // Callback Dispatcher OnResult + OnResult_Internal(nullptr, ETranscodingStatus::InvalidAudioFormat); return false; - break; } } } -bool URuntimeAudioImporterLibrary::TranscodePCMToWAVData(void*& WaveData, size_t& WaveDataSize, uint64 framesToWrite, - int16_t*& pSampleData, uint32 channels, uint32 sampleRate) +bool URuntimeAudioImporterLibrary::TranscodePCMToWAVData() { drwav wavEncode; drwav_data_format format; format.container = drwav_container_riff; format.format = DR_WAVE_FORMAT_PCM; - format.channels = channels; - format.sampleRate = sampleRate; + format.channels = TranscodingFillInfo.SoundWaveBasicInfo.ChannelsNum; + format.sampleRate = TranscodingFillInfo.SoundWaveBasicInfo.SampleRate; format.bitsPerSample = 16; // Unreal Engine supports only 16-bit rate drwav_allocation_callbacks allocationCallbacks; - allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = unreal_malloc; - allocationCallbacks.onRealloc = unreal_realloc; - allocationCallbacks.onFree = unreal_free; - if (!drwav_init_memory_write(&wavEncode, &WaveData, &WaveDataSize, &format, &allocationCallbacks)) + allocationCallbacks.pUserData = nullptr; + allocationCallbacks.onMalloc = Unreal_Malloc; + allocationCallbacks.onRealloc = Unreal_Realloc; + allocationCallbacks.onFree = Unreal_Free; + + // Initializing PCM data writing to memory + if (!drwav_init_memory_write(&wavEncode, reinterpret_cast(&TranscodingFillInfo.WAVInfo.WaveData), + reinterpret_cast(&TranscodingFillInfo.WAVInfo.WaveDataSize), &format, + &allocationCallbacks)) { - return false; //error + FMemory::Free(TranscodingFillInfo.PCMInfo.RawPCMData); + OnResult_Internal(nullptr, ETranscodingStatus::PCMToWavDataError); + return false; } - // Callback Dispatcher OnProgress (not completely accurate implementation) + + // Callback Dispatcher OnProgress OnProgress_Internal(40); - drwav_write_pcm_frames(&wavEncode, framesToWrite, pSampleData); + // Write Raw PCM Data + drwav_write_pcm_frames(&wavEncode, TranscodingFillInfo.PCMInfo.RawPCMNumOfFrames, + reinterpret_cast(TranscodingFillInfo.PCMInfo.RawPCMData)); - // Callback Dispatcher OnProgress (not completely accurate implementation) + // Callback Dispatcher OnProgress OnProgress_Internal(60); + drwav_uninit(&wavEncode); + return true; } -TEnumAsByte URuntimeAudioImporterLibrary::GetAudioFormat(const FString& filePath) +TEnumAsByte URuntimeAudioImporterLibrary::GetAudioFormat(const FString& FilePath) { - if (FPaths::GetExtension(filePath, false).Equals(TEXT("mp3"), ESearchCase::IgnoreCase)) + if (FPaths::GetExtension(FilePath, false).Equals(TEXT("mp3"), ESearchCase::IgnoreCase)) { - return AudioFormat::MP3; + return EAudioFormat::Mp3; } - else if (FPaths::GetExtension(filePath, false).Equals(TEXT("wav"), ESearchCase::IgnoreCase)) + if (FPaths::GetExtension(FilePath, false).Equals(TEXT("wav"), ESearchCase::IgnoreCase)) { - return AudioFormat::WAV; + return EAudioFormat::Wav; } - else if (FPaths::GetExtension(filePath, false).Equals(TEXT("flac"), ESearchCase::IgnoreCase)) + if (FPaths::GetExtension(FilePath, false).Equals(TEXT("flac"), ESearchCase::IgnoreCase)) { - return AudioFormat::FLAC; + return EAudioFormat::Flac; + } + return EAudioFormat::Invalid; +} + +FSoundQualityInfo URuntimeAudioImporterLibrary::GenerateSoundQualityInfo(USoundWave* SoundWaveToGetData) const +{ + FSoundQualityInfo SoundQualityInfo; + SoundQualityInfo.Duration = SoundWaveToGetData->Duration; + SoundQualityInfo.Quality = SoundWaveToGetData->CompressionQuality; + SoundQualityInfo.NumChannels = SoundWaveToGetData->NumChannels; + + // since we cannot get the Sample Rate from the SoundWave, we will get it from the data of the initial PCM transcoding + SoundQualityInfo.SampleRate = TranscodingFillInfo.SoundWaveBasicInfo.SampleRate; + + SoundQualityInfo.SampleDataSize = SoundWaveToGetData->RawPCMDataSize; + SoundQualityInfo.bStreaming = SoundWaveToGetData->IsStreaming(*FPlatformCompressionUtilities::GetCookOverrides()); + SoundQualityInfo.DebugName = SoundWaveToGetData->GetFullName(); + return SoundQualityInfo; +} + + +FName URuntimeAudioImporterLibrary::GetPlatformSpecificFormat(const FName Format) +{ + const FPlatformAudioCookOverrides* CompressionOverrides = FPlatformCompressionUtilities::GetCookOverrides(); + + // Platforms that require compression overrides get concatenated formats. +#if WITH_EDITOR + FName PlatformSpecificFormat; + if (CompressionOverrides) + { + FString HashedString = *Format.ToString(); + FPlatformAudioCookOverrides::GetHashSuffix(CompressionOverrides, HashedString); + PlatformSpecificFormat = *HashedString; } else { - return AudioFormat::INVALID; + PlatformSpecificFormat = Format; } + +#else + // Cache the concatenated hash: + static FName PlatformSpecificFormat; + static FName CachedFormat; + if (!Format.IsEqual(CachedFormat)) + { + if (CompressionOverrides) + { + FString HashedString = *Format.ToString(); + FPlatformAudioCookOverrides::GetHashSuffix(CompressionOverrides, HashedString); + PlatformSpecificFormat = *HashedString; + } + else + { + PlatformSpecificFormat = Format; + } + + CachedFormat = Format; + } + +#endif // WITH_EDITOR + + return PlatformSpecificFormat; } void URuntimeAudioImporterLibrary::OnProgress_Internal(int32 Percentage) @@ -487,11 +635,12 @@ void URuntimeAudioImporterLibrary::OnProgress_Internal(int32 Percentage) }); } -void URuntimeAudioImporterLibrary::OnResult_Internal(USoundWave* ReadySoundWave, TEnumAsByte Status) +void URuntimeAudioImporterLibrary::OnResult_Internal(USoundWave* ReadySoundWave, + const TEnumAsByte& Status) { AsyncTask(ENamedThreads::GameThread, [=]() { RemoveFromRoot(); - OnResult.Broadcast(ReadySoundWave, Status); + OnResult.Broadcast(this, ReadySoundWave, Status); }); } diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/VorbisCompressor.cpp b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/VorbisCompressor.cpp new file mode 100644 index 0000000..7be4cf6 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Private/VorbisCompressor.cpp @@ -0,0 +1,202 @@ +// Georgy Treshchev 2021. + +#include "VorbisCompressor.h" +#include "Interfaces/IAudioFormat.h" +#include "Serialization/MemoryWriter.h" +#include "VorbisAudioInfo.h" + + +#if PLATFORM_SUPPORTS_VORBIS_CODEC +THIRD_PARTY_INCLUDES_START +#include "vorbis/vorbisenc.h" +#include "vorbis/vorbisfile.h" +THIRD_PARTY_INCLUDES_END +#endif + +#define VORBIS_QUALITY_MODIFIER 0.85 + +#define SAMPLES_TO_READ 1024 +#define SAMPLE_SIZE ( ( uint32 )sizeof( short ) ) + + +bool FVorbisCompressor::GenerateCompressedData(const TArray& SrcBuffer, FSoundQualityInfo& QualityInfo, + TArray& CompressedDataStore) +{ +#if WITH_OGGVORBIS + { + short ReadBuffer[SAMPLES_TO_READ * SAMPLE_SIZE * 2]; + + ogg_stream_state Stream_State; + ogg_page Ogg_Page; + ogg_packet Ogg_Packet; + vorbis_info Vorbis_Info; + vorbis_comment Vorbis_Comment; + vorbis_dsp_state Dsp_State; + vorbis_block Vorbis_Block; + uint32 i; + bool bEndOfBitstream; + + CompressedDataStore.Empty(); + FMemoryWriter CompressedData(CompressedDataStore); + uint32 BufferOffset = 0; + + float CompressionQuality = (float)(QualityInfo.Quality * VORBIS_QUALITY_MODIFIER) / 100.0f; + CompressionQuality = FMath::Clamp(CompressionQuality, -0.1f, 1.0f); + + vorbis_info_init(&Vorbis_Info); + + if (vorbis_encode_init_vbr(&Vorbis_Info, QualityInfo.NumChannels, QualityInfo.SampleRate, CompressionQuality)) + { + return false; + } + + vorbis_comment_init( &Vorbis_Comment ); + vorbis_comment_add_tag( &Vorbis_Comment, "ENCODER", "RuntimeAudioImporter" ); + + vorbis_analysis_init(&Dsp_State, &Vorbis_Info); + vorbis_block_init(&Dsp_State, &Vorbis_Block); + + ogg_stream_init(&Stream_State, 0); + + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&Dsp_State, &Vorbis_Comment, &header, &header_comm, &header_code); + ogg_stream_packetin(&Stream_State, &header); + ogg_stream_packetin(&Stream_State, &header_comm); + ogg_stream_packetin(&Stream_State, &header_code); + + while (true) + { + int result = ogg_stream_flush(&Stream_State, &Ogg_Page); + if (result == 0) + { + break; + } + + CompressedData.Serialize(Ogg_Page.header, Ogg_Page.header_len); + CompressedData.Serialize(Ogg_Page.body, Ogg_Page.body_len); + } + + bEndOfBitstream = false; + while (!bEndOfBitstream) + { + uint32 BytesToRead = FMath::Min(SAMPLES_TO_READ * QualityInfo.NumChannels * SAMPLE_SIZE, + QualityInfo.SampleDataSize - BufferOffset); + FMemory::Memcpy(ReadBuffer, SrcBuffer.GetData() + BufferOffset, BytesToRead); + BufferOffset += BytesToRead; + + if (BytesToRead == 0) + { + vorbis_analysis_wrote(&Dsp_State, 0); + } + else + { + float** buffer = vorbis_analysis_buffer(&Dsp_State, SAMPLES_TO_READ); + + if (QualityInfo.NumChannels == 1) + { + for (i = 0; i < BytesToRead / SAMPLE_SIZE; i++) + { + buffer[0][i] = (ReadBuffer[i]) / 32768.0f; + } + } + else + { + for (i = 0; i < BytesToRead / (SAMPLE_SIZE * 2); i++) + { + buffer[0][i] = (ReadBuffer[i * 2]) / 32768.0f; + buffer[1][i] = (ReadBuffer[i * 2 + 1]) / 32768.0f; + } + } + + vorbis_analysis_wrote(&Dsp_State, i); + } + + while (vorbis_analysis_blockout(&Dsp_State, &Vorbis_Block) == 1) + { + vorbis_analysis(&Vorbis_Block, NULL); + vorbis_bitrate_addblock(&Vorbis_Block); + + while (vorbis_bitrate_flushpacket(&Dsp_State, &Ogg_Packet)) + { + ogg_stream_packetin(&Stream_State, &Ogg_Packet); + + while (!bEndOfBitstream) + { + int result = ogg_stream_pageout(&Stream_State, &Ogg_Page); + if (result == 0) + { + break; + } + CompressedData.Serialize(Ogg_Page.header, Ogg_Page.header_len); + CompressedData.Serialize(Ogg_Page.body, Ogg_Page.body_len); + + if (ogg_page_eos(&Ogg_Page)) + { + bEndOfBitstream = true; + } + } + } + } + } + + ogg_stream_clear(&Stream_State); + vorbis_block_clear(&Vorbis_Block); + vorbis_dsp_clear(&Dsp_State); + vorbis_comment_clear(&Vorbis_Comment); + vorbis_info_clear(&Vorbis_Info); + } + return CompressedDataStore.Num() > 0; +#else + return false; +#endif // WITH_OGGVOBVIS +} + + +TArray FVorbisCompressor::GetChannelOrder(int32 NumChannels) +{ + TArray ChannelOrder; + + switch (NumChannels) + { + case 6: + { + for (int32 i = 0; i < NumChannels; i++) + { + ChannelOrder.Add(VorbisChannelInfo::Order[NumChannels - 1][i]); + } + break; + } + + default: + { + for (int32 i = 0; i < NumChannels; i++) + { + ChannelOrder.Add(i); + } + break; + } + } + + return ChannelOrder; +} + + +int32 FVorbisCompressor::GetMinimumSizeForInitialChunk(FName Format, const TArray& SrcBuffer) +{ + return 6 * 1024; +} + +void FVorbisCompressor::AddNewChunk(TArray>& OutBuffers, int32 ChunkReserveSize) +{ + TArray& NewBuffer = *new(OutBuffers) TArray; + NewBuffer.Empty(ChunkReserveSize); +} + +void FVorbisCompressor::AddChunkData(TArray>& OutBuffers, const uint8* ChunkData, int32 ChunkDataSize) +{ + TArray& TargetBuffer = OutBuffers[OutBuffers.Num() - 1]; + TargetBuffer.Append(ChunkData, ChunkDataSize); +} diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/contentModel.xml b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/contentModel.xml new file mode 100644 index 0000000..c0ae289 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/contentModel.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/encodings.xml b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/encodings.xml new file mode 100644 index 0000000..df87cf9 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/indexLayout.xml b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/indexLayout.xml new file mode 100644 index 0000000..27ba142 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/modules.xml b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/modules.xml new file mode 100644 index 0000000..c526ea5 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/projectSettingsUpdater.xml b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/projectSettingsUpdater.xml new file mode 100644 index 0000000..4bb9f4d --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/projectSettingsUpdater.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/riderModule.iml b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/riderModule.iml new file mode 100644 index 0000000..1a4e0d9 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/riderModule.iml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/vcs.xml b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/vcs.xml new file mode 100644 index 0000000..4fce1d8 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/workspace.xml b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/workspace.xml new file mode 100644 index 0000000..b05ecc5 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/.idea/.idea.Public.dir/.idea/workspace.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/AdvancedPCMCompressor.h b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/AdvancedPCMCompressor.h new file mode 100644 index 0000000..56e79e0 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/AdvancedPCMCompressor.h @@ -0,0 +1,25 @@ +// Georgy Treshchev 2021. + +#pragma once + +#include "CoreMinimal.h" + +struct FSoundQualityInfo; +struct RiffDataChunk + { + uint32 ID; + uint32 DataSize; + uint8* Data; + }; + + +class RUNTIMEAUDIOIMPORTER_API FAdvancedPCMCompressor +{ +public: + static bool GenerateCompressedData(const TArray& SrcBuffer, FSoundQualityInfo& QualityInfo, + TArray& OutBuffer); + + static void AddNewChunk(TArray>& OutBuffers, int32 ChunkReserveSize); + + static void AddChunkData(TArray>& OutBuffers, const uint8* ChunkData, int32 ChunkDataSize); +}; diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/OpusCompressor.h b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/OpusCompressor.h new file mode 100644 index 0000000..8042434 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/OpusCompressor.h @@ -0,0 +1,28 @@ +// Georgy Treshchev 2021. + +#pragma once + +#include "CoreMinimal.h" + +struct FSoundQualityInfo; + +class RUNTIMEAUDIOIMPORTER_API FOpusCompressor +{ +public: + static bool GenerateCompressedData(const TArray& SrcBuffer, FSoundQualityInfo& QualityInfo, + TArray& OutBuffer); + + static uint16 GetBestOutputSampleRate(int32 SampleRate); + + static bool ResamplePCM(uint32 NumChannels, const TArray& InBuffer, uint32 InSampleRate, + TArray& OutBuffer, uint32 OutSampleRate); + + static int32 GetBitRateFromQuality(FSoundQualityInfo& QualityInfo); + + static void SerializeHeaderData(FMemoryWriter& CompressedData, uint16 SampleRate, uint32 TrueSampleCount, + uint8 NumChannels, uint16 NumFrames); + + static void SerialiseFrameData(FMemoryWriter& CompressedData, uint8* FrameData, uint16 FrameSize); + + static int32 AddDataChunk(TArray>& OutBuffers, const uint8* ChunkData, int32 ChunkSize); +}; diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/PreimportedSoundAsset.h b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/PreimportedSoundAsset.h new file mode 100644 index 0000000..d6cc7fd --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/PreimportedSoundAsset.h @@ -0,0 +1,35 @@ +// Georgy Treshchev 2021. + +#pragma once + +#include "CoreMinimal.h" +#include "PreimportedSoundAsset.generated.h" + +/** + * Pre-imported asset which collects MP3 audio data. Used if you want to load the MP3 file into the editor in advance + */ +UCLASS(BlueprintType, Category = "RuntimeAudioImporter") +class RUNTIMEAUDIOIMPORTER_API UPreimportedSoundAsset : public UObject +{ + GENERATED_BODY() +public: + + /** Audio data array. Only MP3 data is intended to be here */ + UPROPERTY() + TArray AudioDataArray; + + /** Information about the basic details of an audio file. Used only for convenience in the editor */ +#if WITH_EDITORONLY_DATA + UPROPERTY(Category = FilePath, VisibleAnywhere, Meta = (DisplayName = "Source file path")) + FString SourceFilePath; + + UPROPERTY(Category = Info, VisibleAnywhere, Meta = (DisplayName = "Sound duration")) + FString SoundDuration; + + UPROPERTY(Category = Info, VisibleAnywhere, Meta = (DisplayName = "Number of channels")) + int32 NumberOfChannels; + + UPROPERTY(Category = Info, VisibleAnywhere, Meta = (DisplayName = "Sample rate")) + int32 SampleRate; +#endif +}; diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/RuntimeAudioImporterLibrary.h b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/RuntimeAudioImporterLibrary.h index 082dca4..5d202ef 100644 --- a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/RuntimeAudioImporterLibrary.h +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/RuntimeAudioImporterLibrary.h @@ -1,190 +1,358 @@ -// Respirant 2020. +// Georgy Treshchev 2021. + #pragma once #include "Sound/SoundWave.h" #include "Async/Async.h" + +#include "PreimportedSoundAsset.h" + #include "RuntimeAudioImporterLibrary.generated.h" -/** - * Possible audio importing results - */ +/** Possible audio importing results */ UENUM(BlueprintType, Category = "RuntimeAudioImporter") -enum TranscodingStatus +enum ETranscodingStatus { - SuccessTranscoding UMETA(DisplayName = "Success"), - FailedToOpenStream UMETA(DisplayName = "Failed to open stream"), - UnsupportedBitDepth UMETA(DisplayName = "Unsupported bit depth (Unreal Engine only supports 16 bit)"), - InvalidWAVEData UMETA(DisplayName = "Invalid WAVE data"), - InvalidBitrate UMETA(DisplayName = "Invalid bitrate (Actual bit rate does not match the container size)"), - InvalidSubformat UMETA(DisplayName = "InvalidSubformat (Subformat identifier not recognized)"), - UnrecognizedReadWaveError UMETA(DisplayName = "Unrecognized ReadWave error"), - USoundWaveDeclarationError UMETA(DisplayName = "USoundWave declaration error"), - InvalidUSoundWave UMETA(DisplayName = "Invalid USoundWave (The USoundWave object contains incorrect data)"), - InvalidAudioFormat UMETA(DisplayName = "Invalid audio format (Can't determine the format of the audio file)"), - AudioDoesNotExist UMETA(DisplayName = "The audio file does not exist"), - ZeroDurationError UMETA(DisplayName = "Transcoded audio has zero length") + /** Success importing */ + SuccessImporting UMETA(DisplayName = "Success Importing"), + + /** Failed to read Audio Data Array */ + FailedToReadAudioDataArray UMETA(DisplayName = "Failed to read Audio Data Array"), + + /** SoundWave declaration error */ + SoundWaveDeclarationError UMETA(DisplayName = "SoundWave declaration error"), + + /** Invalid audio format (Can't determine the format of the audio file) */ + InvalidAudioFormat UMETA(DisplayName = "Invalid audio format"), + + /** The audio file does not exist */ + AudioDoesNotExist UMETA(DisplayName = "Audio does not exist"), + + /** Load file to array error */ + LoadFileToArrayError UMETA(DisplayName = "Load file to array error"), + + /** Transcoding PCM to Wav data error */ + PCMToWavDataError UMETA(DisplayName = "PCM to Wav data error"), + + /** Generate compressed data error */ + GenerateCompressedDataError UMETA(DisplayName = "Generate compressed data error"), + + /** Invalid compressed format error */ + InvalidCompressedFormat UMETA(DisplayName = "Invalid compressed format") }; -/** - * Possible audio formats - */ +/** Possible audio formats (extensions) */ UENUM(BlueprintType, Category = "RuntimeAudioImporter") -enum AudioFormat +enum EAudioFormat { - MP3 UMETA(DisplayName = "mp3"), - WAV UMETA(DisplayName = "wav"), - FLAC UMETA(DisplayName = "flac"), - INVALID UMETA(DisplayName = "invalid (not defined format, internal use only)", Hidden) + /** Determine format automatically */ + Auto UMETA(DisplayName = "Determine format automatically"), + + /** MP3 format */ + Mp3 UMETA(DisplayName = "mp3"), + + /** WAV format */ + Wav UMETA(DisplayName = "wav"), + + /** FLAC format */ + Flac UMETA(DisplayName = "flac"), + + /** Invalid format */ + Invalid UMETA(DisplayName = "invalid (not defined format, CPP use only)", Hidden) }; +/** List of all supported compression formats */ +UENUM(BlueprintType, Category = "RuntimeAudioImporter") +enum ECompressionFormat +{ + /** Raw (Wav) data format. Not recommended because it doesn't work on packaged build */ + RawData UMETA(DisplayName = "Raw Data"), + + /** Ogg Vorbis format. It is used, for example, on Android and Windows */ + OggVorbis UMETA(DisplayName = "Ogg Vorbis"), + + /** ADPCM or LPCM formats. LPCM will be used if the compression quality is 100%, otherwise ADPCM */ + ADPCMLPCM UMETA(DisplayName = "ADPCM/LPCM"), + + /** Ogg Opus format. Mostly not used anywhere, but can be used for personal purposes */ + OggOpus UMETA(DisplayName = "Ogg Opus") +}; + +/** Information about which buffers to fill */ +USTRUCT(BlueprintType) +struct FBuffersDetailsStruct +{ + GENERATED_BODY() + + /** Needed compression format */ + UPROPERTY(BlueprintReadWrite) + TEnumAsByte CompressionFormat = OggVorbis; + + /** + * Sound compression quality. The range of numbers is from 0 to 100, in percent. How much to compress the sound, where 0 is the maximum loss, 100 is the minimum. + * Not used if CompressionFormat is "RawData" + */ + UPROPERTY(BlueprintReadWrite) + int32 SoundCompressionQuality = 100; +}; + +/** Basic SoundWave data. CPP use only. */ +struct FSoundWaveBasicStruct +{ + /** Number of channels */ + int32 ChannelsNum; + + /** Sample rate (samples per second, sampling frequency) */ + uint32 SampleRate; + + /** Sound wave duration, sec */ + float Duration; +}; + +/** Raw PCM Data */ +struct FPCMStruct +{ + /** Raw PCM data pointer */ + uint8* RawPCMData; + + /** Number of PCM frames */ + uint64 RawPCMNumOfFrames; + + /** Raw PCM data size */ + uint32 RawPCMDataSize; +}; + +/** Wav (or Raw) Data */ +struct FWAVStruct +{ + /** Wave data pointer */ + uint8* WaveData; + + /** Wave data size */ + int32 WaveDataSize; +}; + +/** Compressed Data (Ogg Vorbis, ADPCM, etc) */ +struct FCompressedStruct +{ + /** Array, which contains compressed format data */ + TArray CompressedFormatData; +}; + +/** Main, mostly in-memory information (like PCM, Wav, etc) */ +struct FTranscodingFillStruct +{ + /** SoundWave basic info (e.g. duration, number of channels, etc) */ + FSoundWaveBasicStruct SoundWaveBasicInfo; + + /** Raw PCM Data info */ + FPCMStruct PCMInfo; + + /** Wav (or Raw) Data info */ + FWAVStruct WAVInfo; + + /** Compressed Data info (Ogg Vorbis, ADPCM, etc) */ + FCompressedStruct CompressedInfo; +}; + +// Forward declaration of the FSoundQualityInfo structure +struct FSoundQualityInfo; + /** * Declare delegate which will be called during the transcoding process * - * @param Percentage Percentage of download completed (0-100%) - * @note The impementation of dispatcher is not fully right. The data is taken approximately, and will not show exactly the correct percentage of transcoding progress + * @param Percentage Percentage of importing completed (0-100%) */ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnProgress, const int32, Percentage); /** * Declare a delegate that will be called on the transcoding result - * - * @param ReadySoundWave Ready USoundWave Object - * @param Status Reference to TranscodingStatus Enumerator in case an error occurs + * + * @param RuntimeAudioImporterObject Runtime Audio Importer object reference + * @param ReadySoundWave Ready SoundWave object reference + * @param Status TranscodingStatus Enum in case an error occurs */ -DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnResult, const USoundWave*, ReadySoundWave, - const TEnumAsByte < TranscodingStatus >, Status); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FOnResult, class URuntimeAudioImporterLibrary*, + RuntimeAudioImporterObjectRef, + const USoundWave*, SoundWaveRef, + const TEnumAsByte < ETranscodingStatus >&, Status); -UCLASS(BlueprintType, Category = "RuntimeFilesDownloader") +/** + * Runtime Audio Importer object + * Allows you to do various things with audio files in real time, for example, import audio files into a SoundWave engine object + */ +UCLASS(BlueprintType, Category = "RuntimeAudioImporter") class RUNTIMEAUDIOIMPORTER_API URuntimeAudioImporterLibrary : public UObject { - GENERATED_UCLASS_BODY() + GENERATED_BODY() public: - /** - * Bind to know when the transcoding is on progress. - */ + + /** Bind to know when the transcoding is on progress */ UPROPERTY(BlueprintAssignable, Category = "RuntimeAudioImporter") FOnProgress OnProgress; - /** - * Bind to know when the transcoding is complete (even if it fails). - */ + /** Bind to know when the transcoding is complete (even if it fails) */ UPROPERTY(BlueprintAssignable, Category = "RuntimeAudioImporter") FOnResult OnResult; + /** Transcoding fill info. CPP use only */ + FTranscodingFillStruct TranscodingFillInfo = FTranscodingFillStruct(); + + /** Buffers details info. CPP use only */ + FBuffersDetailsStruct BuffersDetailsInfo = FBuffersDetailsStruct(); + /** - * Instantiates a RuntimeAudioImporter object. + * Instantiates a RuntimeAudioImporter object * - * @return The RuntimeAudioImporter object. Bind to it's OnProgress and OnResult events to know when it is in the process of transcoding and has been transcoded. + * @return The RuntimeAudioImporter object. Bind to it's OnProgress and OnResult events to know when it is in the process of transcoding and imported + * @note You must place the returned reference to the RuntimeAudioImporterLibrary in a separate variable so that the UObject is not removed during garbage collection */ UFUNCTION(BlueprintCallable, meta = (Keywords = "Create, Audio, Runtime, MP3, FLAC, WAV"), Category = "RuntimeAudioImporter") static URuntimeAudioImporterLibrary* CreateRuntimeAudioImporter(); /** - * Transcode audio file to USoundWave static object + * Transcode audio file to SoundWave object * - * @param FilePath Path to the audio file to import - * @param Format Audio file format (extension) - * @param DefineFormatAutomatically Whether to define format (extension) automatically or not - * @return Returns itself. + * @param FilePath Path to the audio file to import + * @param Format Audio file format (extension) + * @param BuffersDetails Information about which buffers to fill */ UFUNCTION(BlueprintCallable, meta = (Keywords = "Importer, Transcoder, Converter, Runtime, MP3, FLAC, WAV"), Category = "RuntimeAudioImporter") - URuntimeAudioImporterLibrary* ImportAudioFromFile(const FString& FilePath, TEnumAsByte Format, - bool DefineFormatAutomatically); + void ImportAudioFromFile(const FString& FilePath, TEnumAsByte Format, + const FBuffersDetailsStruct& BuffersDetails); /** - * Destroy USoundWave static object. After the Soundwave is no longer needed, you need to call this function to free memory + * Import audio file from the preimported sound asset * - * @param ReadySoundWave SoundWave Object need to be destroyed - * @return Whether the descruction of USoundWave was successful or not + * @param PreimportedSoundAssetRef PreimportedSoundAsset object reference. Should contains "BaseAudioDataArray" buffer + * @param BuffersDetails Information about which buffers to fill + */ + UFUNCTION(BlueprintCallable, meta = (Keywords = "Importer, Transcoder, Converter, Runtime, MP3"), + Category = "RuntimeAudioImporter") + void ImportAudioFromPreimportedSound(UPreimportedSoundAsset* PreimportedSoundAssetRef, + const FBuffersDetailsStruct& BuffersDetails); + + + /** + * Transcode audio file to USoundWave static object + * + * @param AudioDataArray Array of Audio byte data + * @param Format Audio file format (extension) + * @param BuffersDetails Information about which buffers to fill + */ + UFUNCTION(BlueprintCallable, meta = (Keywords = "Importer, Transcoder, Converter, Runtime, MP3, FLAC, WAV"), + Category = "RuntimeAudioImporter") + void ImportAudioFromBuffer(const TArray& AudioDataArray, const TEnumAsByte& Format, + const FBuffersDetailsStruct& BuffersDetails); + + /** + * Destroy SoundWave object. After the SoundWave is no longer needed, you need to call this function to free memory + * + * @param ReadySoundWave SoundWave object reference need to be destroyed + * @return Whether the destruction of the SoundWave was successful or not */ UFUNCTION(BlueprintCallable, meta = (Keywords = "MP3, FLAC, WAV, Destroy"), Category = "RuntimeAudioImporter") static bool DestroySoundWave(USoundWave* ReadySoundWave); - /** - * Export (save) SoundWave object to .WAV audio file to device memory. So far beta and it is not recommended to use due to the fact that the engine can clear the sound data itself - * - * @param SoundWaveToExport SoundWave object need to be exported - * @param PathToExport Path where to save audio file - * @return Whether the save was successfull or not - * @warning It is very important that USoundWave should not used by the engine so that it can be read from physical memory. - */ - UFUNCTION(BlueprintCallable, meta = (Keywords = "Exporter, Export, Save, Runtime, WAV"), Category = - "RuntimeAudioImporter") - static bool ExportSoundWaveToFile(USoundWave* SoundWaveToExport, FString PathToExport); - private: /** * Internal main audio transcoding function * - * @param FilePath Path to the audio file to import - * @param Format Audio file format (extension) - * @param DefineFormatAutomatically Whether to define format (extension) automatically or not - * @return Returns itself + * @param AudioDataArray Array of Audio byte data + * @param Format Audio file format (extension) + * @param BuffersDetails Information about which buffers to fill */ - URuntimeAudioImporterLibrary* GetUSoundWaveFromAudioFile_Internal(const FString& FilePath, - TEnumAsByte Format, - bool DefineFormatAutomatically); + void ImportAudioFromBuffer_Internal(const TArray& AudioDataArray, + const TEnumAsByte& Format, + const FBuffersDetailsStruct& BuffersDetails); /** - * Get USoundWave static object reference from 16-bit WAV data + * Define SoundWave object reference * - * @param WaveData Reference to memory location of 16-bit WAV byte data - * @param WaveDataSize Memory size allocated for 16-bit WAV byte data - * @param Status Reference to TranscodingStatus Enumerator in case an error occurs - * @return Returns a ready USoundWave object + * @return Returns a ready USoundWave object */ - class USoundWave* GetSoundWaveObject(const uint8* WaveData, int32 WaveDataSize, - TEnumAsByte& Status); + USoundWave* DefineSoundWave(); /** - * Transcode Audio from File to PCM Data + * Fill SoundWave basic information (e.g. duration, number of channels, etc) * - * @param FilePath Path to the audio file from which to receive PCM data - * @param Format Format of the audio file (e.g. mp3. flac, etc) - * @param Status Reference to TranscodingStatus Enumerator in case an error occurs - * @param framesToWrite PCM frames ready to be written - * @param pSampleData Ready PCM sample data for which memory has been allocated - * @param channels Quantity of channels (e.g. 1, 2, etc) - * @param sampleRate Sample rate of the audio - * @return Whether the transcoding was successful or not + * @param SoundWaveRef SoundWave object reference */ - bool TranscodeAudioFileToPCMData(FString FilePath, TEnumAsByte Format, - TEnumAsByte& Status, uint64& framesToWrite, - int16_t* & pSampleData, uint32& channels, uint32& sampleRate); + void FillSoundWaveBasicInfo(USoundWave* SoundWaveRef) const; + + /** + * Fill SoundWave Raw (Wave) data buffer + * + * @param SoundWaveRef SoundWave object reference + */ + void FillRawWaveData(USoundWave* SoundWaveRef) const; + + /** + * Fill SoundWave compressed data buffer + * + * @param SoundWaveRef SoundWave object reference + */ + void FillCompressedData(USoundWave* SoundWaveRef); /** * Transcode Audio from PCM to 16-bit WAV data * - * @param WaveData Reference to memory location of 16-bit WAV byte data - * @param WaveDataSize Memory size allocated for 16-bit WAV byte data - * @param framesToWrite PCM frames which will be written for WAV data - * @param pSampleData Ready WAV sample data for which memory has been allocated - * @param channels Quantity of channels (e.g. 1, 2, etc) - * @param sampleRate Sample rate of the audio - * @return Whether the transcoding was successful or not + * @return Whether the transcoding was successful or not */ - bool TranscodePCMToWAVData(void*& WaveData, size_t& WaveDataSize, uint64 framesToWrite, int16_t* & pSampleData, - uint32 channels, uint32 sampleRate); + bool TranscodePCMToCompressedData(USoundWave* SoundWaveToGetData); /** - * @param FilePath File path, where to find the format (by extension) - * @return Returns the found audio format (e.g. mp3. flac, etc) by AudioFormat Enumerator + * Transcode Audio from Audio Data to PCM Data + * + * @param AudioData Pointer to memory location of Audio byte data + * @param AudioDataSize Memory size allocated for Audio byte data + * @param Format Format of the audio file (e.g. mp3. flac, etc) + * @return Whether the transcoding was successful or not */ - static TEnumAsByte GetAudioFormat(const FString& FilePath); + bool TranscodeAudioDataArrayToPCMData(const uint8* AudioData, uint32 AudioDataSize, + TEnumAsByte Format); + + /** + * Transcode Audio from PCM to 16-bit WAV data + * + * @return Whether the transcoding was successful or not + */ + bool TranscodePCMToWAVData(); + + /** + * Get Audio Format by extension + * + * @param FilePath File path, where to find the format (by extension) + * @return Returns the found audio format (e.g. mp3. flac, etc) by EAudioFormat Enum + */ + UFUNCTION(BlueprintCallable, Category = "RuntimeAudioImporter") + static TEnumAsByte GetAudioFormat(const FString& FilePath); + + /** + * Generate FSoundQualityInfo Enum based on SoundWave and already defined Sample Rate + */ + FSoundQualityInfo GenerateSoundQualityInfo(USoundWave* SoundWaveToGetData) const; + + /** + * Get the platform-specific format name based on the base format to find the compressed buffer to fill + * + * @param Format Base format name (it can be "OGG", "LPCM", etc) + */ + static FName GetPlatformSpecificFormat(const FName Format); /** * Audio transcoding progress callback - * @param Percentage Percentage of download completion (0-100%) - * @note The impementation of dispatcher is not fully right. The data is taken approximately, and will not show exactly the correct percentage of transcoding progress + * @param Percentage Percentage of importing completion (0-100%) */ void OnProgress_Internal(int32 Percentage); /** * Audio transcoding finished callback - * @param ReadySoundWave A ready USoundWave object - * @param Status Reference to TranscodingStatus Enumerator + * + * @param ReadySoundWave A ready SoundWave object + * @param Status TranscodingStatus Enum in case an error occurs */ - void OnResult_Internal(USoundWave* ReadySoundWave, TEnumAsByte Status); + void OnResult_Internal(USoundWave* ReadySoundWave, const TEnumAsByte& Status); }; diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/VorbisCompressor.h b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/VorbisCompressor.h new file mode 100644 index 0000000..bb5b238 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/Public/VorbisCompressor.h @@ -0,0 +1,22 @@ +// Georgy Treshchev 2021. + +#pragma once + +#include "CoreMinimal.h" + +struct FSoundQualityInfo; + +class RUNTIMEAUDIOIMPORTER_API FVorbisCompressor +{ +public: + static bool GenerateCompressedData(const TArray& SrcBuffer, FSoundQualityInfo& QualityInfo, + TArray& OutBuffer); + + static int32 GetMinimumSizeForInitialChunk(FName Format, const TArray& SrcBuffer); + + static TArray GetChannelOrder(int32 NumChannels); + + static void AddNewChunk(TArray>& OutBuffers, int32 ChunkReserveSize); + + static void AddChunkData(TArray>& OutBuffers, const uint8* ChunkData, int32 ChunkDataSize); +}; diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporter/RuntimeAudioImporter.Build.cs b/RuntimeAudioImporter/Source/RuntimeAudioImporter/RuntimeAudioImporter.Build.cs index 3e24567..898f634 100644 --- a/RuntimeAudioImporter/Source/RuntimeAudioImporter/RuntimeAudioImporter.Build.cs +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporter/RuntimeAudioImporter.Build.cs @@ -1,4 +1,4 @@ -// Respirant 2020. +// Georgy Treshchev 2021. using System; using System.IO; using UnrealBuildTool; @@ -9,7 +9,9 @@ public class RuntimeAudioImporter : ModuleRules { PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; - PublicDefinitions.AddRange( + //PublicDefinitions.Add("WITH_OGGVORBIS=0"); + + PublicDefinitions.AddRange( new string[] { "DR_WAV_IMPLEMENTATION=1", "DR_MP3_IMPLEMENTATION=1", @@ -21,8 +23,7 @@ public class RuntimeAudioImporter : ModuleRules PublicDependencyModuleNames.AddRange( new string[] { - "Core", - // ... add other public dependencies that you statically link with here ... + "Core" } ); @@ -34,16 +35,21 @@ public class RuntimeAudioImporter : ModuleRules "Engine", "Slate", "SlateCore", - // ... add private dependencies that you statically link with here ... + "AudioPlatformConfiguration" } ); - DynamicallyLoadedModuleNames.AddRange( - new string[] - { - // ... add any modules that your module loads dynamically here ... - } + + + + AddEngineThirdPartyPrivateStaticDependencies(Target, + "UEOgg", + "Vorbis", + "libOpus" ); + + + PublicDefinitions.Add("WITH_OGGVORBIS"); } } diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Private/PreimportedSoundFactory.cpp b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Private/PreimportedSoundFactory.cpp new file mode 100644 index 0000000..7b5ff66 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Private/PreimportedSoundFactory.cpp @@ -0,0 +1,121 @@ +// Georgy Treshchev 2021. + +#include "PreimportedSoundFactory.h" + +#include "PreimportedSoundAsset.h" //maybe #include "RuntimeAudioImporter/Public/PreimportedSoundAsset.h" +#include "Misc/FileHelper.h" + +/** Defining custom logging */ +DEFINE_LOG_CATEGORY(LogPreimportedSoundFactory) + +#include "ThirdParty/dr_mp3.h" + +/** +* Replacing standard CPP memory functions (malloc, realloc, free) with memory management functions that are maintained by Epic as the engine evolves. +*/ +void* Unreal_Malloc(size_t sz, void* pUserData) +{ + return FMemory::Malloc(sz); +} + +void* Unreal_Realloc(void* p, size_t sz, void* pUserData) +{ + return FMemory::Realloc(p, sz); +} + +void Unreal_Free(void* p, void* pUserData) +{ + FMemory::Free(p); +} + +UPreimportedSoundFactory::UPreimportedSoundFactory() +{ + Formats.Add(TEXT("mp3;MP3 Preimported Audio Format")); + Formats.Add(TEXT("imp;IMP (the same as MP3) Preimported Audio Format")); + SupportedClass = UPreimportedSoundAsset::StaticClass(); + bCreateNew = false; // turned off for import + bEditAfterNew = false; // turned off for import + bEditorImport = true; + bText = false; +} + + +bool UPreimportedSoundFactory::FactoryCanImport(const FString& Filename) +{ + return FPaths::GetExtension(Filename).Equals(TEXT("mp3")) || FPaths::GetExtension(Filename).Equals(TEXT("imp")); +} + +UObject* UPreimportedSoundFactory::FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, + EObjectFlags Flags, const FString& Filename, const TCHAR* Parms, + FFeedbackContext* Warn, bool& bOutOperationCanceled) +{ + UPreimportedSoundAsset* PreimportedSoundAsset = nullptr; + + TArray AudioDataArray; + + if (FFileHelper::LoadFileToArray(AudioDataArray, *Filename)) + { + drmp3_allocation_callbacks allocationCallbacksDecoding; + allocationCallbacksDecoding.pUserData = nullptr; + allocationCallbacksDecoding.onMalloc = Unreal_Malloc; + allocationCallbacksDecoding.onRealloc = Unreal_Realloc; + allocationCallbacksDecoding.onFree = Unreal_Free; + + drmp3 mp3; + + if (!drmp3_init_memory(&mp3, AudioDataArray.GetData(), AudioDataArray.Num() - 2, &allocationCallbacksDecoding)) + { + UE_LOG(LogPreimportedSoundFactory, Log, TEXT("The file you are trying to import is not an MP3 file.")); + return nullptr; + } + + PreimportedSoundAsset = NewObject(InParent, InClass, InName, Flags); + PreimportedSoundAsset->AudioDataArray = AudioDataArray; + PreimportedSoundAsset->SourceFilePath = Filename; + + PreimportedSoundAsset->SoundDuration = ConvertSecToFormattedDuration( + static_cast(drmp3_get_pcm_frame_count(&mp3)) / mp3.sampleRate); + PreimportedSoundAsset->NumberOfChannels = mp3.channels; + PreimportedSoundAsset->SampleRate = mp3.sampleRate; + + drmp3_uninit(&mp3); + } + else + { + UE_LOG(LogPreimportedSoundFactory, Log, TEXT("Unable to read the audio file. Check file permissions.")); + return nullptr; + } + + bOutOperationCanceled = false; + + return PreimportedSoundAsset; +} + +FString UPreimportedSoundFactory::ConvertSecToFormattedDuration(int32 Seconds) +{ + FString FinalString; + + const int32 NewHours = Seconds / 3600; + if (NewHours > 0) + { + FinalString += ((NewHours < 10) ? TEXT("0") + FString::FromInt(NewHours) : FString::FromInt(NewHours)) + + TEXT(":"); + } + + Seconds = Seconds % 3600; + + const int32 NewMinutes = Seconds / 60; + if (NewMinutes > 0) + { + FinalString += ((NewMinutes < 10) ? TEXT("0") + FString::FromInt(NewMinutes) : FString::FromInt(NewMinutes)) + + TEXT(":"); + } + + const int32 NewSeconds = Seconds % 60; + if (NewSeconds > 0) + { + FinalString += (NewSeconds < 10) ? TEXT("0") + FString::FromInt(NewSeconds) : FString::FromInt(NewSeconds); + } + + return FinalString; +} diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Private/RuntimeAudioImporterEditor.cpp b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Private/RuntimeAudioImporterEditor.cpp new file mode 100644 index 0000000..bc33277 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Private/RuntimeAudioImporterEditor.cpp @@ -0,0 +1,17 @@ +// Georgy Treshchev 2021. + +#include "RuntimeAudioImporterEditor.h" + +#define LOCTEXT_NAMESPACE "FRuntimeAudioImporterEditorModule" + +void FRuntimeAudioImporterEditorModule::StartupModule() +{ +} + +void FRuntimeAudioImporterEditorModule::ShutdownModule() +{ +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FRuntimeAudioImporterEditorModule, RuntimeAudioImporterEditor) diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Public/PreimportedSoundFactory.h b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Public/PreimportedSoundFactory.h new file mode 100644 index 0000000..d98681f --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Public/PreimportedSoundFactory.h @@ -0,0 +1,44 @@ +// Georgy Treshchev 2021. + +#pragma once + +#include "CoreMinimal.h" +#include "Factories/Factory.h" +#include "PreimportedSoundFactory.generated.h" + +/** Declaring custom logging */ +DECLARE_LOG_CATEGORY_EXTERN(LogPreimportedSoundFactory, Error, All); + +/** + * Factory for pre-importing audio files. Only MP3 is available for pre-import because it takes up the least amount of memory. + */ +UCLASS() +class RUNTIMEAUDIOIMPORTEREDITOR_API UPreimportedSoundFactory : public UFactory +{ + GENERATED_BODY() + +public: + + UPreimportedSoundFactory(); + + /** + * Check whether the audio file can be imported. + * + * @param Filename The name of the importing audio file + * @note Only MP3 is available for pre-import because it takes up the least amount of memory. + */ + virtual bool FactoryCanImport(const FString& Filename) override; + + virtual UObject* FactoryCreateFile(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, + const FString& Filename, const TCHAR* Parms, FFeedbackContext* Warn, + bool& bOutOperationCanceled) override; + +private: + + /** + * Convert sound duration from seconds to formatted string (hh:mm:ss) + * + * @param Seconds Audio duration in seconds + */ + static FString ConvertSecToFormattedDuration(int32 Seconds); +}; diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Public/RuntimeAudioImporterEditor.h b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Public/RuntimeAudioImporterEditor.h new file mode 100644 index 0000000..86b7ff3 --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/Public/RuntimeAudioImporterEditor.h @@ -0,0 +1,16 @@ +// Respirant 2020. + +#pragma once + +#include "Modules/ModuleManager.h" + +class FRuntimeAudioImporterEditorModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; + + +}; diff --git a/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/RuntimeAudioImporterEditor.Build.cs b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/RuntimeAudioImporterEditor.Build.cs new file mode 100644 index 0000000..0be5f0f --- /dev/null +++ b/RuntimeAudioImporter/Source/RuntimeAudioImporterEditor/RuntimeAudioImporterEditor.Build.cs @@ -0,0 +1,46 @@ +// Georgy Treshchev 2021. + +using UnrealBuildTool; + +public class RuntimeAudioImporterEditor : ModuleRules +{ + public RuntimeAudioImporterEditor(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + + + + PublicDefinitions.AddRange( + new string[] { + "DR_MP3_IMPLEMENTATION=1" + } + ); + + + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "RuntimeAudioImporter" + // ... add other public dependencies that you statically link with here ... + } + ); + + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "UnrealEd" + } + ); + + + + } +} diff --git a/RuntimeAudioImporter/Source/ThirdParty/dr_flac.h b/RuntimeAudioImporter/Source/ThirdParty/dr_flac.h index 854cbfe..2dcddb5 100644 --- a/RuntimeAudioImporter/Source/ThirdParty/dr_flac.h +++ b/RuntimeAudioImporter/Source/ThirdParty/dr_flac.h @@ -1,6 +1,6 @@ /* FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_flac - v0.12.27 - 2021-01-31 +dr_flac - v0.12.28 - 2021-02-21 David Reid - mackron@gmail.com @@ -179,7 +179,7 @@ reports metadata to the application through the use of a callback, and every met The main opening APIs (`drflac_open()`, etc.) will fail if the header is not present. The presents a problem in certain scenarios such as broadcast style streams or internet radio where the header may not be present because the user has started playback mid-stream. To handle this, use the relaxed APIs: - + `drflac_open_relaxed()` `drflac_open_with_metadata_relaxed()` @@ -232,7 +232,7 @@ extern "C" { #define DRFLAC_VERSION_MAJOR 0 #define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 27 +#define DRFLAC_VERSION_REVISION 28 #define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) #include /* For size_t. */ @@ -11806,6 +11806,9 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat /* REVISION HISTORY ================ +v0.12.28 - 2021-02-21 + - Fix a warning due to referencing _MSC_VER when it is undefined. + v0.12.27 - 2021-01-31 - Fix a static analysis warning. diff --git a/RuntimeAudioImporter/Source/ThirdParty/dr_mp3.h b/RuntimeAudioImporter/Source/ThirdParty/dr_mp3.h index 110a57a..e28f318 100644 --- a/RuntimeAudioImporter/Source/ThirdParty/dr_mp3.h +++ b/RuntimeAudioImporter/Source/ThirdParty/dr_mp3.h @@ -1,6 +1,6 @@ /* MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_mp3 - v0.6.26 - 2021-01-31 +dr_mp3 - v0.6.27 - 2021-02-21 David Reid - mackron@gmail.com @@ -95,7 +95,7 @@ extern "C" { #define DRMP3_VERSION_MAJOR 0 #define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 26 +#define DRMP3_VERSION_REVISION 27 #define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) #include /* For size_t. */ @@ -3519,7 +3519,7 @@ DRMP3_API void drmp3_uninit(drmp3* pMP3) if (pMP3 == NULL) { return; } - + #ifndef DR_MP3_NO_STDIO if (pMP3->onRead == drmp3__on_read_stdio) { FILE* pFile = (FILE*)pMP3->pUserData; @@ -3907,7 +3907,7 @@ DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint /* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later. */ currentPCMFrame = pMP3->currentPCMFrame; - + if (!drmp3_seek_to_start_of_stream(pMP3)) { return DRMP3_FALSE; } @@ -4005,7 +4005,7 @@ DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pS /* We'll need to seek back to the current sample after calculating the seekpoints so we need to go ahead and grab the current location at the top. */ currentPCMFrame = pMP3->currentPCMFrame; - + /* We never do more than the total number of MP3 frames and we limit it to 32-bits. */ if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { return DRMP3_FALSE; @@ -4450,6 +4450,9 @@ counts rather than sample counts. /* REVISION HISTORY ================ +v0.6.27 - 2021-02-21 + - Fix a warning due to referencing _MSC_VER when it is undefined. + v0.6.26 - 2021-01-31 - Bring up to date with minimp3. diff --git a/RuntimeAudioImporter/Source/ThirdParty/dr_wav.h b/RuntimeAudioImporter/Source/ThirdParty/dr_wav.h index e38c232..be1438b 100644 --- a/RuntimeAudioImporter/Source/ThirdParty/dr_wav.h +++ b/RuntimeAudioImporter/Source/ThirdParty/dr_wav.h @@ -1,6 +1,6 @@ /* WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file. -dr_wav - v0.12.18 - 2021-01-31 +dr_wav - v0.12.19 - 2021-02-21 David Reid - mackron@gmail.com @@ -30,7 +30,7 @@ callback which will give you information about the data format. To determine the Introduction ============ This is a single file library. To use it, do something like the following in one .c file. - + ```c #define DR_WAV_IMPLEMENTATION #include "dr_wav.h" @@ -144,7 +144,7 @@ extern "C" { #define DRWAV_VERSION_MAJOR 0 #define DRWAV_VERSION_MINOR 12 -#define DRWAV_VERSION_REVISION 18 +#define DRWAV_VERSION_REVISION 19 #define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) #include /* For size_t. */ @@ -424,7 +424,7 @@ Use the `container` argument to discriminate the fields in `pChunkHeader->id`. I use `id.fourcc`, otherwise you should use `id.guid`. The `pFMT` parameter can be used to determine the data format of the wave file. Use `drwav_fmt_get_format()` to get the sample format, which will be one of the -`DR_WAVE_FORMAT_*` identifiers. +`DR_WAVE_FORMAT_*` identifiers. The read pointer will be sitting on the first byte after the chunk's header. You must not attempt to read beyond the boundary of the chunk. */ @@ -534,8 +534,8 @@ typedef struct /* The size in bytes of the data chunk. */ drwav_uint64 dataChunkDataSize; - - /* The position in the stream of the first byte of the data chunk. This is used for seeking. */ + + /* The position in the stream of the first data byte of the data chunk. This is used for seeking. */ drwav_uint64 dataChunkDataPos; /* The number of bytes remaining in the data chunk. */ @@ -565,7 +565,7 @@ typedef struct { drwav_uint64 iCurrentPCMFrame; /* The index of the next PCM frame that will be read by drwav_read_*(). This is used with "totalPCMFrameCount" to ensure we don't read excess samples at the end of the last block. */ } compressed; - + /* Microsoft ADPCM specific data. */ struct { @@ -1932,7 +1932,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on We need to enumerate over each chunk for two reasons: 1) The "data" chunk may not be the next one 2) We may want to report each chunk back to the client - + In order to correctly report each chunk back to the client we will need to keep looping until the end of the file. */ foundDataChunk = DRWAV_FALSE; @@ -1964,7 +1964,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on } } } - + if (!foundDataChunk) { pWav->dataChunkDataPos = cursor; @@ -2106,7 +2106,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on } cursor = pWav->dataChunkDataPos; } - + /* At this point we should be sitting on the first byte of the raw audio data. */ @@ -2378,7 +2378,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_d runningPos += drwav__write(pWav, "WAVE", 4); } - + /* "ds64" chunk (RF64 only). */ if (pFormat->container == drwav_container_rf64) { drwav_uint32 initialds64ChunkSize = 28; /* 28 = [Size of RIFF (8 bytes)] + [Size of DATA (8 bytes)] + [Sample Count (8 bytes)] + [Table Length (4 bytes)]. Table length always set to 0. */ @@ -2411,8 +2411,6 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_d runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); - pWav->dataChunkDataPos = runningPos; - /* "data" chunk. */ if (pFormat->container == drwav_container_riff) { drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; @@ -2427,19 +2425,13 @@ DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_d runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); /* Always set to 0xFFFFFFFF for RF64. The true size of the data chunk is specified in the ds64 chunk. */ } - /* - The runningPos variable is incremented in the section above but is left unused which is causing some static analysis tools to detect it - as a dead store. I'm leaving this as-is for safety just in case I want to expand this function later to include other tags and want to - keep track of the running position for whatever reason. The line below should silence the static analysis tools. - */ - (void)runningPos; - /* Set some properties for the client's convenience. */ pWav->container = pFormat->container; pWav->channels = (drwav_uint16)pFormat->channels; pWav->sampleRate = pFormat->sampleRate; pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; pWav->translatedFormatTag = (drwav_uint16)pFormat->format; + pWav->dataChunkDataPos = runningPos; return DRWAV_TRUE; } @@ -3238,7 +3230,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, dr return DRWAV_FALSE; /* Trying to seek too far forward. */ } } - + return DRWAV_TRUE; } @@ -3307,7 +3299,7 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; /* Trying to seek too far forward. */ } } - + return DRWAV_TRUE; } @@ -3399,7 +3391,7 @@ DRWAV_API drwav_result drwav_uninit(drwav* pWav) } else { paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); } - + if (paddingSize > 0) { drwav_uint64 paddingData = 0; drwav__write(pWav, &paddingData, paddingSize); /* Byte order does not matter for this. */ @@ -3417,8 +3409,8 @@ DRWAV_API drwav_result drwav_uninit(drwav* pWav) drwav__write_u32ne_to_le(pWav, riffChunkSize); } - /* the "data" chunk size. */ - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) { + /* The "data" chunk size. */ + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) { drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); drwav__write_u32ne_to_le(pWav, dataChunkSize); } @@ -3430,7 +3422,7 @@ DRWAV_API drwav_result drwav_uninit(drwav* pWav) } /* The "data" chunk size. */ - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) { + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) { drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); drwav__write_u64ne_to_le(pWav, dataChunkSize); } @@ -3609,7 +3601,7 @@ DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) DRWAV_ASSERT(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */ } } - + pWav->bytesRemaining = pWav->dataChunkDataSize; return DRWAV_TRUE; } @@ -3643,7 +3635,7 @@ DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetF */ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { /* TODO: This can be optimized. */ - + /* If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards, we first need to seek back to the start and then just do the same thing as a forward seek. @@ -3791,7 +3783,7 @@ DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 frame pRunningData = (const drwav_uint8*)pData; bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; - + while (bytesToWrite > 0) { drwav_uint8 temp[4096]; drwav_uint32 sampleCount; @@ -3921,9 +3913,9 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav if (pWav->msadpcm.bytesRemainingInBlock == 0) { continue; } else { - static drwav_int32 adaptationTable[] = { - 230, 230, 230, 230, 307, 409, 512, 614, - 768, 614, 512, 409, 307, 230, 230, 230 + static drwav_int32 adaptationTable[] = { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 }; static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; @@ -4030,15 +4022,15 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin }; static drwav_int32 stepTable[89] = { - 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, - 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, - 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, + 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, + 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, - 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, + 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, - 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, - 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 + 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; DRWAV_ASSERT(pWav != NULL); @@ -4180,40 +4172,40 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin #ifndef DR_WAV_NO_CONVERSION_API static unsigned short g_drwavAlawTable[256] = { - 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, - 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, - 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, - 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, - 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, - 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, - 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, - 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, - 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, - 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, - 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, - 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, - 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, - 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, - 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, + 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, + 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, + 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, + 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, + 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, + 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, + 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, + 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, + 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, + 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, + 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, + 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, + 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, + 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, + 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 }; static unsigned short g_drwavMulawTable[256] = { - 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, - 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, - 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, - 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, - 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, - 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, - 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, - 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, - 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, - 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, - 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, - 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, - 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, - 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, - 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, + 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, + 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, + 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, + 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, + 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, + 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, + 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, + 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, + 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, + 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, + 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, + 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, + 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, + 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, + 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 }; @@ -4306,14 +4298,14 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uin if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } totalFramesRead = 0; - + while (framesToRead > 0) { drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); if (framesRead == 0) { @@ -4346,7 +4338,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_ui } totalFramesRead = 0; - + while (framesToRead > 0) { drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); if (framesRead == 0) { @@ -4379,7 +4371,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_ui } totalFramesRead = 0; - + while (framesToRead > 0) { drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData); if (framesRead == 0) { @@ -4730,7 +4722,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_ui if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; @@ -5064,7 +5056,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uin if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - + bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; @@ -6022,6 +6014,10 @@ two different ways to initialize a drwav object. /* REVISION HISTORY ================ +v0.12.19 - 2021-02-21 + - Fix a warning due to referencing _MSC_VER when it is undefined. + - Minor improvements to the management of some internal state concerning the data chunk cursor. + v0.12.18 - 2021-01-31 - Clean up some static analysis warnings. diff --git a/image/runtimeaudioimporter.png b/image/runtimeaudioimporter.png index 92b9fa90aa2627ced0a21d55ef8a055702d51ea1..a7d83a66a6ebef57ff3be9b613db8203b081da22 100644 GIT binary patch literal 88542 zcmeFZcT`i`_Aie8s(?pDx_}hvHS|zaYUmy5hY~RK5?W|BKCzQc1f+!~ zARr(mKtQ^L_BI4O=YGe%-|zjE@y7e(W#Aaud(S!7te-X4+AF@)P*WheLVbmZh=>HD zD62(8bOB66bk61<7lAtmgJIu^h|Y+>WMnk%TRB-k8MG80FtG89iHWik5wS(bMK&q5 zXpjXr(a3$w(EG>KcV|{j4f}!jVGyGbhKB_*$RdbLoW%{t;sE>~5stTwzxkbIRd|1Y z^|J4|k9p5zo)cdPo!Cg35TornlVvfK5~LZl{;ZHW0=DF)!D~tuh1R^?nHmE zx_qFVSoz1}A=SEN!B4vZ0`%ER=a~Miqk-OeaiymTlXsE-{Fn4J+dr;7&Qlu^`a}I* zUcxSo#ue8?BBl0C-#^SA54%ayhl#x-b~QVra@)l2u_2PQ-(W?jBOhvjS-yLpIr36s zL>H)rKKP~5&4FG`iq9+O{^X~(7r9-dS6SGw8E_zZeP|u(n5OWLdt8zf2KT)(cmi}O z!7+;#tN8iX<*Ez=*T^ME1Bl*zQr}1tQe)G7$m&J0RawBxH*HDgT14hxR)g3 zv0QQB`_XM)MuuY&xSb>;A)pUcH5g=^T%ioYJbc`ie0(4V5iuTqVL>4Q5iSM+J^?;n ze&A1tn_om+P*fZwz;NUNcrxIkLZkDbvXE@Bsk>N<9xrLKET#^yclwg8`^KY?^ zZYOR69LDQq?##>2!*^uTF`@G#C%BW_Bd32u{`>JiF{J#ePT&t@3oBUtu;^6R`+6^w}0hn>(kbg<-rsM4l<<)|^Ik~%9LghSw5t&Y{ zaf53?|MKU5zz&fAW`@J8{|)calYepm)C=}+NROWUCUq8G1Q3gM`HeK>tMSWCgSK{vSzyhg@97$==CT)yWFb1H{Pi z*L?^8Y_3k$Fni#^e+4*31}jT(YbRF+b0CZ`2Xh-Jud}1gv9_wJ;t)qSxVfVx6e26h z2$;$PgIS3~g)A+hf+8Z^VtgQeZYaN?B{yJ`6}KRtDAdY=-wJ9j3XFc7M&hzgmhML> zcEnF8Q7b1)K*nFX@mmX;^IHjta*K$F0L~B=wB{BQv$o>q6A=;>u;RC}HWwoJ4$wy3 z6$YS$x&7az9%*F-NE9#^fSOxagSh!Eg)O=HL~E1jrcpC@00!4~a(A@SH}`=4Ju{A<9|`;WbKd_!l7@`xzmakD|H zMCJHEgrV(W|5k~AH5wsT6=6rCd!HDu4R!g;4aX=aZf;4)4$C8qfLbv!5N^Z%MUDJt z*+}5`d;|sZ@V@~L0-2kWHQdwO6?)$WNZ!*r*7$F*>2G2DXZ_uPB=~Q&hgygLPPVk- z2AP{fxkdQ}1-V5*AZu=r7*yC?)KUPbc_6+2o%X`wAdonp$SIQjztsLjyyYWvM;j>6 z5bz!+)=^#o3I6}k9xG96sD&VaG1dU4aGMJs*<)@l$_)a*+R9qQ3Th1nVBlZc!>_|9 zb_C->eE<9Gv2=3ufV%!I!<@}sff9xT)h@|s?ds&fVD9W}53@W*FkTNwtCM!&cS$k8 zof!Ut@_(BLR!~>i-!kFvqx@EZ;s3|+{>Qwt<`WWu3W1>9!h8av+=2pv7Tgws{1)6+ z!WLpeB31$d0wSXS2gT`swC6vYDgR5}{kx9fKdK`E0UrLpw7q+Hs2}{;xppKP@YuXA=je{ojqiDVG1IAbG^^^#670|2-}Jtr0(+B!KSz z=c9fJ#ydgTH8~~BMuj7@xH0GI1w)wtwb>&M}{9;_)GcaO~ z=;K*M?fYaJ??WDD@=H;_ynEMMOVwMee}k@^zn6^jj8T}7Ccj=``eQBBwts_tqW6I) z>9x04JVlJ8R)*}%-jSp(k4YCj#o%TL=5AJj+aipKkm9We3|f_;f{vK zS;AMvf5fJo5ZpKmWhH!l{QUot{vYpwr!yWzB3=@75KKbThI>PkeV&@A3%q-hNxsWD zvzHwi@=Z0EM}rquH@R9^#5UTr*P=^bT$=T1HU;dtun%3qF1cAR>>Ntu+*B8bK6m))f&u@CsEQT114CU2SGficN;SKtPwlT`2qvP<2 z?AG}ZKR?qnwby+2({{d8j#OF(uen|QAzG#9zcHC1@@GeU_}I4Nx#PTg%go-<#pHYE zhDT%LQUkgnGdh>C_r<7pPoPSl&bx%F>g=jnKS(IEBo8d1E{~9kJygh=B|+wWe5|(m z{sTY12FB|OI~TkcMoZGx*4KZO*O$$_O}U=JOm+qPu5o{)&NIzpWjuu!HuB;+Q+A4T z^UH~m8aD+E%nGw^4K1*>aP(=t1L)E1w|ut<@7$?sTCy29l4MAgok0ca9{d2g8Jm(a z;~@dha272btMjnKmR;NE0eyw?GH=`&o8gB5gO)(6%ToWNZQvZE-=d` zwV3OM=fc7Q^i@!C{ggn8??RiS%>$92hz1TGc0CtJ>0R~I+{3-y3k~=+I{_89CD^o@ zk0vIEWxr$Q6+x2dMMw?xZmuS4SA;C%g-H#?zU&l8Zz8I!Ou)SjO<}tXrBFszIgF;h zj)|$hhnf4PdbABiMD*A)Iu>dpNG&Nz9nK-btDmEuIPXFG*1j3lC%F664N93U^>EAI z*4lcjgk_9{y>sRfVZ8arN1ES~%eua~GNg`8OmwulPb@ke#!fAUKoPg}D2Gzej8-`` zO-=^Wdn;*~RhOSRW=!`(RxZUd&rtlRxqXJenU6TdtxNEN*4g}$qH1#KL#>+DGPS0| zaUTlhq$v+6!dMmDc^ztzt@zXF{5AO@pDR?UiDhqztrA0`z3W+8ELlk)S62R}zMH8nc-wp?YNsZBY_2!*Vr z^GS;8>iSByAu5r1em>i1`>}d-R~hw1H$7ebjEbt_+ZX~mr?a=KeP<&%Cng!MP`AB& zd1<(z)y5z-YfD^1Z!!hs)5XiY57DySwkCM%T{Ii`$zA)=c!yCWwVutjfx!CRZ5*cv zan|tuwXSLJ{bspqhss)Mj$x-Z0MkGbAF&LLh~rjul{%m})Ij^lP&r6UEju+q@dyA3 zD<9f7Fy)v<~ z?WN)^IqMR>981b=9LsOBmF&YZcDIq75Ss{^3s-wC_6h2m=vu}n=zm4V-uUS`Qd*E- zAmsYvvlF-R#!ne!85sADpfKbGphr%n-4GS>$0lM1+*Jd9E&kTI_uH|@F)!4_*p3PB z-N;-zKk}ZBxX7@w{f)zDJgPtTu}ao@qc16tYHR!3TuiD zDScW<74wdt6#8Ps__o(=wt}DCiIQ_e$frmx@y+i9LoSB_BQ{U3vPnvsRQbW5U^l%h zr=HzPg?5Zb3OXu7RyidrGa{G1VAj7&q>AtWZjffV6Qfyj9AzuXbyQa&?qtOIQ3kX#vS`|IJNy#C@>8+s>C1 z@Ws+wN7rCxXVGkVAimS++O1U65_I7gIQsk^Qrsza?x>(9jtWYtwoIFpP?}NcKn^l( z;6@d^cf3R7Gdrc00rhRR=_cq8oD=54MaB!mM?7S0DR&Z+ZfNMq*X~J`Fv~t4wZWXuW|;HqcHvWT5~Cys7+ z1ryyc@IgEzMX37iNc2wcAB8DCY z`Mbu@-ZqUmLgcwk)2*JTf#)1+X4dtU4F@jp(jRNXz@zCg^;Q=5#zWt!H+95e#IQyW zTp;|&Xj2?lGM2HRSYw+NcV%=lB;OGELo>uT$jo28R;$6FeU&2DX5`*2^tL|a6T_|S z4LyHDrwfHT`eypwsd7o8tAhKxHFI&U&Xt?-UVf$!%(&!keVxa0FUB*$5J*c-FeoV{ zj;U#aHL{f{F=)>;&2`Pywj5OcfUbmhY12S6ZOAT>PCaR6^_QQ2fCM#{F@j(e(bQ!qt2nc*LPg#blV8QQYD3c; zv`F%2yl{WfAf6ignCU{&(qe)Yo$-9DrLa`fYA|ku!YyU`DC0$9cH7^T~ao+gGCeRoAf363@kmf2k}G5nf)LB?(X3^ne~`HFDzpE z)>2Rg8AL%SmoBn(^}^nj^Lt9Gmws&SS{az`aFxWwN1G<5q?n|{#6WYw#V2cfv#UvKUmbcQ&3=}2KZ>}Mk{p*&jxfzBzi5oksA2U~ z4gwlC$@-uC4TlG=RPrs*#UOKdM~%^xXhh~@u6>SquKmh53$>{s{_A$rVM_Ph9J#$4 ztr4d*{%6mDQI$QMo&DH%_qH>ZP)ic+yLD!R({{`K+F+kUvJ{UD2PRzd3 zXGzCl!Zi7UtAr)3yV?WmvtqI+x@C8=CS(}?@E{175d^k_Va3_iL;7wy~z6xQ(i8 z_wghnjqY{r9h_t@W&0BY1mCcT^(uynW%lOg+u7=xnigyLDbhtM#8*!$i;-O+p#wYa zX*=r{~f)dzG9Nc zx@DYqb!bmzD7;>EI;0y{ww{!o*XXyK>og%1?69(Q9n<@$Vi*?=wJ}jmA&pIdZ7SGp z9PF;xb;LUWE#ullx`FcUVodX@7e`b2AKX_Jx40jsEb(~0!*oV4{B4U?1y z?(SoFQv&9`W$CizhQ}ueb7OK2$JEVsepcs)3Wxm&t46LX%9*nNsnj&_Gq?EHwuWTc z!cfRarx|Ig;+}s~wn`$Zd)%wR)wTTAWd&d+(9!8%w}nW4>mqCu^q5T5k3Ue*4I-VN za~e-CFiR}h=CBp~%DQu7zWkk*j7uGe+2n9AVI%yo3$qpC?%29O4k$mUn^tRVnxu$$SZ!gkaW?x$slj;7}fZJpDzzSLMMhCoQ#WV6)Q)hYjy6MU)$tj*E4Y_ zQc^-}do9=Gj71PS&K_+J zCiqG*6u%IVV;6u^tDc5mE=lkwEOD6W0i$nLGU2hv?)hLEI&HJ2QJBThpJ^N4Q?I!% za?a&v`lpS$5VEW1GGLp^&xVruV0!6XbE)c>4~_4ouzxY_bfQsC=|Ru&WVE4?5^gH; zk+&>94S5xQh+SDY=u21-$qL9kcoKvGnNnrze2;FdTk1eK4PA9?JrTj5xfJH63C=XD zcW)RkjxDSGHL}TJL>*&}y=r-o4gfXm-r%E7nggMIm-gV={JF#ta^wRS@pjNaY(5jl z!Kq8_>#Y%jtQ)tXHpxc6db>A<9C-8I(vUOfhbXbm8l%3&rsiMP=OioARefv7f|Y@f z!qgY^vl@BRhWYRyI^YyVA5;k6;$vogpmyxN$D*>sRIP)zz09P|TnZ5IOOa}svbB5|(%Mztq>i7)2X9QMhJK4USvHUYrfCDFO*HtF z3kNfsiTIO+lj}*BrR3T&4T$IcndA@Nk@PD%jN=IXFfk<;&)gT~zjJU)T%)ezG>Y>? z?Omdq--34G-4Xh_Kit7>lM9Rk?W@~8L+S^0%i=pcw-D$e7vT?}4&;Fe`G5@l!3P`x zkBN?Xj}S1xP|R$Ht~y4L*FKo6N=Dr2J4vliyHC<bJ{CV4g8H>n%9&O z7Q2>)-QP~OSotH%KoRw|JJ%Z?PJY-hPI0&_w>TqOHA}jie;~ekOSOY8-Qf zO=crh&q68CuQ8mu7^H*xZ)=Ovm%?ryj@2vrFp`AMG9z#-T;V1a!PX?24P&MP^O zEFWI^_?4~uG3wCXUjsSq5$jP}D7&jdD3(VwP#fCZa2@OnE_4b}+%+ibszXz(w-Pp4 z>o?Vh*Ts5TC24sRtNut_j2{2CTVJJL>d~ufTCDaaFYvA3$SF4FTU_y8SC|BL5(SC`k@r+!}NoT4)!E#kqhX+gE{Q zhNyWb)t=ZG2y?DEQ^JYoiJ46aeLN1^E1TT73E z`#m{kvm6rYzT0D@!F}5?WsnXLmum~zyiso2bX=+ueajJmJ1>W**?Z( zcc=Wc8qj;ZueeXMsdq#P>lR1hnA<`$3Ff&xF9QXMMHH#V(YFhHQ@f!|I}lW$8#`z= zL4WwlUkFc%_Cc*V8rHLo#a0b*4?P(%8FPC;V66a>fVd!cqC_&o9RfD9v3t-f4#CW{ zH0DXG8W+ACZm8tva(AD@CR9|Ka$LjVra%Nu)3b7WEgs4}YT6pqZ=dssqJ)Aq2jugG zIGjTK#U@GovL*(AoUi4nFC61TpOEV>GC>I0Nve>3_JJ?` z8+&$jR_`-^z90BbFzgTTD^h_VxfC8_J&6d~q_n!xI8JbP`t z^ltc{MgW-2iIuml*~ggVVrl7fKSd|vQquZhWt7nh@w8%w_0uc0Hlrey{FywQ;^-e- zCmWs?CIawBE}Ubp(Kpg6z|479c|Jz4gs;1aU*a`#>IO!J<_sxn8jO*0e_fLqYg%!+ zfa0lvH{{XfeKheQ7)%7GJRW*3?_F9^QHB7^f|gvXu(VCfK{l~>_+!20!;EWyq5d_ZX8u^pP%%mMkcohWojcASV%||4I1}~rKt!K#E(jvDkyK93Y|aE(2rwFS^hdV2Pfmx zu0$i^mM4CxN}6s338=C+yl(%_VYF~dR8x%o!atRD7)`QUCTe#qqBoCP|Dh*soy@u$ zb%a8^1j1pvrPi*a(~t@oQo92Zol^7z$$u7<81u0p3|{fF4D^YlP0WBjXp~*Nb#mP; zAK*oVKhAI7-TWM%wvalu-Oic!IZ%0bkvaVH>z;j90z%(=%?7qKUitE#I`wW*N!(c% z2`3w6p!?PJXy^YSqE(qar8?HIs*EUhZ3yA=lyYH0hDcy~_F_y#fm6FjpOG7_n+C3* zRdMJHsu&$|pxY_6qfHt1+QDZWMvR0A@2mus^+|2Ky`$_(ELq2$TT+sgrJp}k@Bw|~ zMEg8JNnN13Ggcx1ZiET53r5z1s)cKyXWx#M0&W7Sq4{tb^uNLLhhT(*+sm2Nm#NrCE;6TzO#K#xLt zzEDtgSx8;CY7{auxVM+w1%ziLxVxKuG8VRA<3s(Z8tvAU>AnY;2LM_P|bU#jW&;9|`Bv*8KRnISC6Zn~Dsg@UK-WtxPiU zrXwzCjXS+?NrTnKH523Kxh(F;?5&0e#7II1@WLcE#l=E#xh*M4L@my|I)=6rCZ5#i zLMCYspX*I15s8@JU{4R}ks2%CQpPU_l{`{-$Px0s`!k1867s$8N$XY0bFqL$uvJh( z-18ktzRNc?*SA@7bN0pGC4B3qDc7j;3=OvO{eMmx)VggmUQl5dl9#6GV8V#8m}%&x z2aldif;}OWMF~C0>?{xSO2acG4Xx*e2+o< z9$tNutWlBuhe96q+zjy#A$kCH3u2l}xR~5w+J{elrAkOPT z_ohVjRm|H@>?H?TwjDmclt?^lo*q-KPFKN zvQ>=WmdkPglFDd}CeKiRHFf!ybCrMNKJQwh7K579w5>ht-o0AeAD-VLT)%O3xCNmC z$x@G{G4y_fY(CEx*!NB{!}|ngjs+|S4Gd*53cnPlc2U&PxYgxst&jQWQ0}(b`MJW7!GS((XU3uc#k8D=LZ&JpJusOA~4lp*~clF_gwR z78|cCk?$uoO2&t=0(R#Tvy0;Bu{NA@3#_jLxJMno^3vhL70X@S+^1iq)!jz-iMqlU zkp-1rqtcPSFHW78ELw!_`7FTYx6@xIC|qt{+*VA+helUoObVQO^UFjkGb|G6#(shP zEN2oC6;lo~MysB~h%9QO_G;8hxF6RY9y2En`{0eHl1}ug^}K2Y?1BTJhNfl8Z6|0F z3ED)zT?l=_C#`R8Tv|bdTiU4FVXxF92%RZDMvzb8A|?KriVc>5KhUNi zZ7`?~-wTn75R8pBX%qJf^?bd6=G{z0zg9a4AJweA5J>3S6|I21k6HZ902DoOqdZNm z`TI<-YHnt~{2|dX@yRpZs=$6qEy*dd4nX`#kjoGDEp4OojN7Jp!_ofBNEF=V{i89d z9kceI(7M|>qncjF-N9p7+2O>;_1WqZ5;#*or=Pb*bS5_4ygt3Iyn`Ld)ZXapJ4|ur zEb%;lDqau}-#)jxIJg=xbS-Ei7k8aqb~5h;>ee0XEHV#|r8?A8Q_2{PHdZ)I+sja! zv+J+o8%2e4CsN_B^O<#7Tqdc?&?I-Ttj|h%aXHd?#AXqr(H0j^#~_PMfqc)8p1@47 zN~BwqRDhN|l9u)Tond$%wMH2;NnFXAkJdL5vS(}($9?BL#-rUjo6Wih0k5};t!GFE zM>N*HOxJKThbtx=h|gR$F~F-*34DtnD?jC^&stJpeCDc-q3=Q%NmCt&>3&TM^HP!3 z^^ob^8J3z`Sg~>Fe~w%qiy)&o<&gk*1YnK8Vs;?~@pGe9kO!>K`QT-xSG1)_VU<)5 z@4mxcO6~qeKt5kX$!O`+l@ohj=mYlHYOP#_cW($?iP$qk`%uf@|Fnz7fm^g1P)^tC zjki-8)XIZ>)v_mDz7nNGgax<#*<4eSKy!(>)a#H-%_P+| zfzum3iHC#LtWvqDROh}$kZ+%ATc5G?{NikK?r63|ad1upsyjA0>kYL$;?vp;?oM6C ztD*?S@++U_5Beyu)iw?Jz^_+4!G;eqP6}idc#RsItl?Gb?>bwKu)^EnC+_R#1y`FN zbTmYNrNSA|!PqR$(P2R>Hg>&<>I+?1r-d-7gwK|Sj;+dyr|@aoLpsUE@?1d$EAx&jdta>Ym`)71O2=IJ5-odclv)Q*f5ns?T(5~!h`|QF z{rp`<^E$;%iiIyzVyg7EToQ8;hJP-3veO+y&SU0wwTevrSq31$B%QYpphe&}Iwg+& z#>yVjXy{BwWuw{}B@(KL&9+KRS#KjN19tWD-BGzrz`O)1yEu<}N+sT~9Y_sK_<7@D zYN9vALry^FgE%0$%?>Zf<&+#W6W-_Eg&WanRI=M@%G&m9D%6E4SLAu12M3gVvb56M z*7uMqSv!^^YAx;9@C9Zx4d&==SUUqrr?_sGk4o>0J(qvRBc=_eK6cdwceRRvSBjZxisp5u zNbZiG9RmATyfCqjP)=42R4VLTKd7iF1ay&Iy7J*p9?P2do@0|v#p6?BINH@{Df;+y zUk&O9!u*6J0`@c`gK<%r9pVfT z__KP{ayC!@jPp45`yX~};4`$K6FmDv`RIz`^okRBqkP_qa?D5S^v>0zJ3m(8ha;9J zBfN6}-u#H4VwUqwDC=W2xf8(n1{`=u?>li4kqB^fWd6npTY4RE7-nAJ^a^}*rFD8$ zcSPxWdIjveot-B?y(&1O6h6J;1g~z+uXXXYw zw7uZqFhVl5me#l^zk2@K>mSFpM z@AC8~esqiQoxcEaQ{d*?s{4a;mK%7BG|_SllaF2=o#fNWS?-p4eRO@|^Vq$R6ox#iwLDu(NQW5$JyA zhA8@%-BsvXe0xYOzE&4`SJ*M<1U_DdTFCf{|3BPLC!E+=HxK`v zk9nahqgB7c7)!{a$x6r6&co@Nm|J4)W!i-*w)aa6a&+v8^&$9R>MJcWuUbF{{5FKi zFio+&<139EGddA-SrSE@7W=$`_r?oL#f;pI1F1YN-(PQVnpea8I_AU@urHcHUA(X+ zc>mey$I7e6nR|~|X}PQ~{?kU(-L37mDhK{|H&?qB`D3xy7E4Kx-EO}uFF*9+IR#)< zmi^=ucTM&M*%?aWMOj}Ev4Uo+h6!rMPabeDq*mo6D0nV&W<8mf!s6J)k11pOnZW0= zH^$H8x$5d*<0*$%hq(wRm=e z_#aNKopZ3S(y6Ba=4DxPo8kH|?lxiOKJ3@^X3e(c?`}z!)X3I_uR9Jh0`}}=l8tWk zi*`=eTB8{~=IRw%n+2Ksn5cq&$fD2bVzLb(LYJJ}k&H-tNDy^n(jHQa#VM(u)udg~ z$;;oSe#nus^4-^J-&D6mR6uS9U+e~Cawl$xSNkhz4aFO(@W44Bx15yob!8?8=ps-$uwR#vL!Q3SRBW_I9HcJZ{pZUn zlrI+5+p5=md)>|{T!92?9)cof2dOS3vZJ#FWAjpUNvqMN5)tyKZBNB6VTchkPFtqU zAb`&%2=%x+V|c@hw5r@)%Ot${IxK}5sqSnM6ty*rXRCvjQc3Q%Y}%tYq6HO4 z^f?9;PwA{xE$I)1D;=Xq9y5=J#eFysQ%pPrp>Jrwj3vW->HZw(wi*eE) zy>e#$E(=q;RoqfppM88=OI7mf4M%=HN4a93HSrAPHZp;IlUEvDOZ_WXH8&UkOeHJX z*t!Baj3bM_7$&K^=zpLW+}%{}`pjAyd~e3QZK$icvfR{1nYL9^PBm~#Z@F*F54`ay zNn~>#d^q^JBQ2&3RndnwA12At0zVt#0;l2G1{?vpjXrfYuJj^C@Nv$!;x!tL|&N+h%AI;kYgc2a{lJ4QPMQ6q0qLYG8D zF`m|b2d}!!=q0PZson-E^QAduyHW?{UFMl2V{uKhx}P0}uDj)iK11SuZg_`$)A(Xf z&s=S(XWI1`%qB85Mu**RYvi@gL4hZ{7e?WM=w*jYDD9$|{w}C+28fC_5wHPO!TZg}Yn7 z_n{g0UcHAty_vxy1gdkFrGYEY6n{YBkCN^>Y!J`}fjM2veN7`BrKU&m1Ft6X2IFb3 zbn~3x-My)-rLZ$}eFb9A9QAZB70bKR3WY6Hu*jjDQr%<9`pRnA%$KlnX1Qry`$fyI z-`q2x%UhuAc%Y0aMk75SLndNo6Ac?QDOBaE2@#D36V$6h4j~?kF!1S8?1d0uj)iMm zpg2>OA3CL*sc4K9*72TX2XmObiRDqDsOzFbB>GFPrYVVlrJDQZqJ@{^DwHtujrf6_ z0L-oHqZ`-fQey+!ew-Qkb0KN4H0^3@BZJP^i@J zz0$j)PIgntdT~%wdhmcWB0`WDc6b*egppit%n6GP<7pcr2adrF#gE?ws|_WdHu0rT zfqmEHzC-G8;tHt;y+_4~9E1ok@1S|R@Wvu6VI~QryU4C?`s{|=l((rWSsY^fd5tK- zo6=k6jLQ!tB^zh2)jI85+bpT{gvkttkkV)M=1F$xqr;^h5z_~oBUS6N) z*5oGX(vApYIqv=*)5r>!ER-wcI_%w7Z6Vk%*x0MJU(Yk$5q^s|^GOpr_#}cQXE%Sv z;#M+DVdr#`#oo%aR!T^j@b7`~L!A0mP665@M%;NSNg((5vcB?Bzv4HyG4$j6NdKm- zYrv5xi6Am}ltmg`ZcBe7uH2uwWT2F{NXIEi?Mn(ef%IF6SBkfL|5?k4HSaDc@*pFq zSp&lwIHzygq+$;KVfxNmkUX{N)mudya{8)w`j(U4*xU=B&Lm6`rtzb9-TdX3CUTpc z{Ee^Q-hazQ5n)v5e$rB~Q&LUz5)*OzcQ(po4uB zhSc@Hxw89C!i3Lps~7xq#3^PQI8GwIG`%11}952LM&*I>J)tMhqXH%P*bMZSWFpVTsuHgDm8sPH8|NkNQAmt%QNq;Vs9-Q}+93LH34{rXc+rqSqFM%YKvhF|ZMYrUESaR}4y zjVHIo)g2z<(}s%UCSnC7wQQ$5E69FsXuSIpI~0@um{m7VFMAk0sK%Z8u6&ruGXdlWcX$}t|dgRBUY=PB;g~k5hJWY(Eqd740P60 zv^Aum#Qm38X~apdnGP(OYX4ODs5iEs)>K{F{pHCJ@Jjy5FiGG@DhFhdnF@IplVI&% zJTS-WYn{z>_siVMLb)R>G{!QNZrB?Izj!Fx3D_TyFls`kg(c=4+_Xx3wbm z*;}2`qU0lj)Mat{%1!}XMZXeIL(I1zO^pj4RPyUt9!6yawlU*h*JA~yZHn?dvQFBH zvB0vv3oI-!JZ@?tx4w4fMoGYyVkeup{%97-)te6A*z%0EWT^$M3-6LTc{{c%3TBo_ z@7sBI{(6mmCqN@#>S%H&PYKC>%WAfAcPE-@_UqCWsmKbCriv*SSc^2Xt$pVR6c;wHWV~czzq=dW^$NS@B|fglGqIoFi2pR4-_sjP$HI65KRt0be7Owl zLu6@TQ5BPUBAaW$%J5I0Uv@6ft)u4m{6(<5*S=vi;PNP^lzlNM8d~YKWFll&={%jq zzZ*?51V$WO&>rE?73`$cYq1mT*I?FDqK+z>bE?rl6R@k0Tl{Ssku&QM$D}(nMAxS+ zqgI(}bm%#@I<7jRH^C?Js7$e6cjVdobk2f`OV93$s9jBeHrt)Ha(sFNSdqVfSoU>V z6!VxE!h$I|*bLU&PSNnnBSVr!1LZG*>nmD5{D${IiwngD@qc!q&C6HG*Jw#j4zK<+ z^a#Si-Xo;{EmyV4+l-Y&qI9fv+^kM!g%&tP%-{IAyMxno;HQ%+W@*iS%_&2z${lt(#p)v6RN&H;PA@gek@ZA&p2C~c?m-CI^${W+|@dI7r~CbeE$ zw-(I4mVC6->t7ja65bchmd+Q$4jb+@B#hX&I&HBF}i9R@X*s~ue3b`eQ>7D!- zq(?g9HNhwyZ1LQ|;6mei_YOU~dhJefcUA4gqS0aA5GND=zUHQ(R0_XqnRTjbm31=w z7w${1&3ylTR(kDTgPS}1WK`mZ7f2G1O;2_iP`M13qW}mo91-%+1M)k z<*hLO$se*w1`><(^Q|YZD7aF9c?i`ru?JDk|^ebB9@i+;$~-P6RNP{ zi?yd1?A^=S{ZDE06&aRB5@9&m&0npa9Digblz3M9Bplx3U78r+&Ju6Y%(F?2b&ZHk zj7Au@Qfmp^YvRS{;CX5^rAK~JY${`vM{YVipPNZAlUm6Lro)$^+K_i^Zrqrcvb;9F zHU}Knh9#84sb*V`4pmK^gXV-TFkwEAV?sncv8y5nXuGc3vf+KMNVE~}fnht5Y!BNxNazPkJoJ9TID;mq?^~^p*EdV~ zsemgq$_n`cwr{-~NM^Ec$Js8T z8sacP`@yM%> zft^Vc@=~-Z78!yzlh_Hi6#wK#vpj$qvEA-$41S1>I1Izt%ykE6R(su zIJU;e7sbUlvNY_LZ7jQCFs3YBe%G0glJ>jx@#c2hOEIwJz5e*Z>lkSY>2#7hNBzU*Mx4K++`hkLDb{9zN`xMw zpb6$Oia=!qY7-{Z_Zs-MmOi!iRc#ybR*eZrzglK(06{oHYjd z?|H=~B-jCw2t<-!c=p}^K+K654;~;CL~Trt9A&cPz=2jW#g%L&PsweO{a*`*cH3?f zU-8dP*-9c`>(+D1%+&60a#mF0nGCGc6D4WomJkCYaX?u5w!~rDI#(3Sh}pX(wvgq~ zIND^+f!Idxfo*u7_Yh}iTK>kpBrf$1Csbvgo-X|ux=>i10k%zz6MZAIkEm#R8;{}& zXbjmuS5al{r`c|VuBQeVVFbI?qMomeL5y4MJ9C~Da_c$Klwy`4)%83 z9gjgRiWXU~_L3isQ8y*dudmHCsT)<(i7cus$E|S(eN}td(UIDFZdZ8WqhCQ%P0?V- zCEBFEYM0< zWf2;l>E%M#a)TXAKF)5m!DpiII&}g_!=1Y@etA>Lr-Qi&8EaHXXCNs~+vbrOB7Xhj zKyXFqN7u<=PJxF=L64<}LjpvlY%DBtbE!VXT3TAnSmd94zeEz4&fN1{V8u#6%%V4Z zAnHm8WefsxiCI_4^{vbFr*N8T$F~iPSOp~kU}eorK)ir`wdhAz!wqi|AP>s()be^9 z>MRT!5r^|vd#5yTfu-7Z`b^sfZ6mU7f)yvt;D9edjbSM6c4;FRidt%H83*zsmNS+l z9&nMTHnghhQDgUm#*eLy(Pku+Zvfa8}?zlooyT#cOec*L-CPwj25u z8b>*$20^0~xd}P1n^`Eq9JeSG=?CjeQg*j1cD?6F8Vp6y-%!_5qvLs^$4z(|b{jXA z15m1=%tcXQF2L{al$-ojYDoK+F`tqo%YL;>C>7pHm1H`{0y%f_&3C~bWOMTa)if!S zAag|w$@@QlUa1D1xyMS$f-OLr>bKM;9LB+4qYRL)#$PTF-BZ5H`BL6XfQ>Kc>(N^4 z$~xLcc+UWRf!mCF_gj!pWrZH~(sFE|ONR&QDI8{G8#ivaA7Py)TyE06zQeV@+}AYp zwJ`?X+85BX5E%~=rv5un2!er<*fCW);Gh7gny6P{6e9rxY6_x#O1qFGRxG1X80I|i zKS!vzg70}AttWvQ7J@)5j!{QR8p6hjm#!so z@7#ON&S&ra9C13Fa3Yra5~NAaFnieA$k59A2JEn+z$yn04$OU2oRdvw{;-{L}uFS5*BC6 zdy-IwZMXjHx?k$5BQP~NX>v=kNloI>9Yt}ERwc>@QiS#tYE{PFpan5Bj#snN)9Nw7JS{IJD5Y%GhAs9#Sv2bF7w~plR)|n+r-VZwX`_jz-h65Vjq9lXecUhSSmGYFLIF8>{tq4GqSz$U8nfk-^A{G3pk$2e zyMNw?Hxoeu8Ner6Cd0C9rR!u_o+KYvW%%sm4aYE5=g-BAB<%Iy_p(IV2vt@`rW1fT zPqgH7CQ*dyRus{eK_-;?zSDl^IWpysz18)}c4CG3=_^7`49kGOEf3&}O8WXc&Ufd+ zkB%JO1oi4Sl3@w_ms{n>YZ+nr4Go@knU}PBlUelle4XvGW8~u;{W4{>Cs&c(+ShJV zXe{GuKMx*cy?{VUzm*Y$N5i1Er+-$XlVgiu*KE60hoe%K$Mr5YqY7g;YG0ouchKr% z!7nb{k#4KWHXRV$+Kv2z;T(ljRuKi7M+3F1DFq!=9`7nEE936^{b}w?6}cVK^EQH* z`*RizM;=i^XRUYk0~gokk%Sk^D_-vP=~W(}p5|-ZavYcZ^ce#)U+B~u+%meabOIKR z*R@95mDJBu8AfF5=0lhqZ@*0uR&CZ_C2it1l$12QR+}|ABwqG7?`hq)yNq;(P)7X^ z=BTS9Ufn$;6L0w4AgMjYwY5~df5Uz$t3hcDYMEcZop7VDlEOTrlZ$iw86AVfSvDeX zQOdLXky(ofB&f5=O4Rh+?qGV)^Ca{z&RfU8)v#=4Vn3CY55BX}SLdvVodZ}F#} zZbNEtg5jm$r!!?u&D`~&oE~;;Xx&p1Z2fs)6T4dD)i1E1K2cm(ogGwDDnAi))SHud z)i5#ZEj>!$U$mwEJ;%WhM5jKYQ&?G>rV`i>N5gd^0cALIUS?`^+*s4$nGi#VV~Vlc zHX4&!Hhz7ov#`p>Y7EuPc@?XHXI0ZtOT4@HqTi%m<8n=?4k_bMncnx~@72NuJ*k@o ziE+1@%k%zi*#mBL@1_PkyTJc!*KQG-tXn zo>pU5fO#K2KU^)b7a;ci>#$dLY#`HZ zT_?2l`l747vCeCeYGlG*m=`pbrEP6t$I`|l;$pq^BAl;0qqieI*1Osn7c&TzZqgy^ zud{egE3p}7BSTGon@Abd(=L0EODeYWw?==%U71#8YBV@*sMvNJfo?2ExL;&CmMn2| zejr7gkWYT*9K-lL^yEuc^1MQ53`}}GHkt~fL;xR@toKc>&i3>3^AR9>;iMD7$G)Ef z2P$*sKa4I)0L;a0)KTN}>XN*N>-jOgs6BZ0b8Acdoa4mienPa99?Ph{hnc`ZW3coj ze&km86Q!)oZn3cVNaeqD8#VwETId3V9gPLJ&wToT^Z6 z+NZ3ooj}|7@V=jq1K(rc*DLRsyc7{5a@8?X=?S$~QQpl?&9eVK9*}y3@1aCA0t~)8 z^z%u@nm`fUU>{k5n+_BKtI`auYgn%;IyRK^H1pFRH`ta3Q%$JJJ~Y~Um)oi*}Lx`P-C7Qr1zI5z4ELev$}v9T7tKR7~MQZeuQ ziB0#S)2w)DBW}5ozbb_b%j0YiqZhvfg219y%XXIy+4W|6BBfcgpG!34I~TGOs8D`F zt3xIEbNB z(U9NYCJll7#(`=%k8X$Ds8Cg_K=gVRqZmhFrLCm7sj^^j(RV7ca;M;>2b9-s zsO_$>wDgT5?6l{?KQj#3{}>M?1}OdmaHp=-Lnwmqzr?@}dCSvSp2u2fDKh<{qlgQ< zmQ$CuvkU^uIp-8t(1-Fj07R~U^M>#L77ky_ zCKj0LV)Cy=^OKyj;zo^@M?JKRg>W_dsuhN|D~k2VqDPVXPC9eXA)fg1P}Y?|=(+(HJg zw^k^v_uGwU8AJorF2p`13O53l!5(}IS{?wO+#wh7ZU8o^!g+i09aI6h@pV+Hxd2j4 zy&Y|+1`l$tjaxvCvhqI<3Vj6!1Z6B1mbeO#Qp5i)g#$P?=I*QOUTW@E3SV_VU$JQL z&#k+@L>20;cLH(#yR;QM`DN~hxI7EZU@MxKH==+*{`E(t5wZ}bK#T(5t3!oa<y64kr-IX@wop_&TN3WS#u=b3{*-Mnf@JpDXNmn z*gcAe@g#SAS1n~MqX(<}3B}~lwx$%6L$1+LSwyZ=My+@(g8+fMb|r}Y;0@_z;=ei5+WV-*h#SKfI{RX3?KDp)H7B5j8#e&RXO}t7COnpB+~ko zwun3mUom!>&*E~zEqLdy-4(DoY001e(0rwTvjk6NthP|aPqG^ZrU;dm z#D-Gl3K-k%|Ll*P=QtkCLqA%8ULU1$TvuFF{9!yP_r>?E0&O=C-!C5$vQ2w``~-TQ zXJ{DZp%;30ZoJpReKp=oEkE$@48<2wjh-q&cUM*0^8{@BD_v-@>5H{=VG&Z*Toq0g zTqJe$!cc0!=|>KHU)MJ%?!Cvc|C7+QvI4P=KoYXHL8iH1A!oO;rU}Yb>P6o1lB7Bv zZSpffS&zI1p_yZ-GxyKD7SJftE~3h|{~pz66$V+o?|wXcV^ z=8%-y;9rXM>FG_G;W5lKhyT1#TwGiZDW_>CmmIW?l?3}f5{SIs3!cV9XOp6nfD9WG zG+mQ|^Q!Xrd*cM3k-b=!d3*PKv;Tdt@=I3MQ4j-=@6Lb=;Z9(yo#UsE`mrtxrn}MB zHc}?)%v{-!JikO?DQ7oo7j^o4sBd`rh<;+uu#DxSj#$yK(^R;2b7f18h#I9^U1Oy; zx$8LgOaEf%mp*?{e9tD@U!tCE6bOYh*_-==ie)0{WNFvC0L0@CVvQbT|GhMcfRHWE zqQdqf3h9q7*v!Mcm&??3A|>pyIZc)N7|-^35ocp!-yCIk8^lgmk2(ixs>ceNK+z{9 zoN54Jk3OQAaidT|(WhGlh3|ZC7SVjyFF(>le=6d5d$B+v=V6>8O85xZ(_Cyc94 zhOFK7u@iv!_P+ukS4>O{3^1;V(Fvhe8W>^kcwmJovcocy+659fTYASHY8>IZCL(8A zSJzi)J4(i9J@2v8w2kGhc9n;E+b$dv=9jg&-oxpqAtta5JGhBu@GgMpN zKh{miG+a=ld3ysdXMD*xfHO9mwxHiQT(3awb28YCbFKxPg(jWDb(Nb(JqEXLBOTx0Hi_B3b&^x#ZA3yI zHQ&dhD$LJkWYH=Uk4yoiiVpF0mhLV)d|*uLb()UF#@A7` z9c|9VI90X$nk(gD8#{O=Tdmm2tg3A1qD(V7A%^wmaZzXxqfV3@gIp$tNkay~2@G5@^O2L>4Ft$ z3llahfP|igN@g^Em;IJ=Ld`zs@l)|yCDNM#7BoeQ27LFyQdfR)@hCtMztgdxuo78D z2R@?kU_rTE@$(7bh%3Zi#Uga-d;t($-{r$+jViZDw4BVa$5n|Kl0KWTE@UHvKd^iZ>B^J&u$k&Ag2IZ z2No5@F&5b$cjOBqZt$l<3^e5CB=f9qiPEsw0pX)hV%Lt-{rIy%9}FN2gaEqB_1uWM z^}|DM=POM$b+v-3s*~W`^Io&m2(FoJPjKI?oE%HQJ9KdF8R_^nvmW>!aNjgk#BFM} z+8ZqSTt-fIq}Ks%NNe_37787V(a_H>HC9lwmMa=9)E07Vfe}zg95JBS8mIqnvoEha zunb~%fD`aGl#gUDg7F z_R%0+IW;w!z~Ndqcw}Tc0MCkgC_?a1GG(+<5{=)c`#+IHBqZdMa%T4|?wj9!LQf&S zIed$Jm?7omFrOYk9T~k1$RK^|QG&j|h}ZY=ATv{mwH;Lk8u$!3CiFg3FT4|GNiBE;0}ZM0-74nrsdg-a#OJ(xZU- z@!Qzu$R1)o5?d`gCdYH{&w(_c5Ib;{Z~8|^$q(NShCo~ZWT6F0AW@k{fN_+tj_d)r z{I?-!(tU9sXN#?s{e%0vBHdbqj*fwb@l+^aXMCdjabF@#`XU`A<2xMKVnaC!8is~H zX+ar67^=&N!uJ0@0MvXzTS*jInfC$B*<`Jk+55{_Ye4)LNsD5s>jSiG8j&|I8QMnD zAdoT5SQO^X$iyTYnJs(q{XfD(0Z0nD@E@GTkUi)urIdJv51*{0mcnU$edG!l*qD1R zwg8Qz$kV*PjhPS#Z&qgJ+Oz{a=AT)mMQ(qj1&;v%CKq?{_+shsa_M#GyP;vAiPwOZ zadLqEtJy!6M>8S7Sob~hMNJ$LWG=g~kh$*%hgddD2H_O>8C9mB$mp8LbGLG&781!$ zy{T46nX=b)QMXA*5&Ri;!($fho+b}{zrGmR2tSv`f0_V@d zE9a&!TU|W0Y{Yn)loFMF8@%qacB54blR5lxF_GOEP<3tgPDHL>UdYkMRKL!@sZ^a1 zzg{4t{caC?#nLbuqnzcBmdoBGF1bGF&_ml|m7-+NU5rOqBw!!r#jPdqViU4q`E~jH zSys27>e}yA&=v$`JkY8uWG^5`w51F=DB}?(huqJ(a5R;-z=A^t zMqZlv@>tE(aU&)d)ka3zQJkfXfU^`2+P#ya3h~u^W!g&;6C2Aj<0XlJ5HCU?{ApRr zoUKtw&8#_Ghu%LT8!)O@JrbM61?Zt{NLGva$2Y;WULwAl;YqWNpK!;4V1{OzTV7J= z)|l@ABFY;n#NH0{J|vJ1z!vJw-q*Bwnuur6uf_N8Kq_P#w<#L?z`(7$gL1Cr&b4`2 zQSE6#)+(J}>`a(qWD~V0Ghk`dHYD-i*tFbinU|IulK7V30O&APcA8IT+9FMz6y8Ea z3jx#U|D!W#E-Gk{1-WdK7uTOfC`S-$GLWtyK-wrFHbMHT^{0E-U?_tJV3Yh0DmihqBGyv ztN_~)7Vow$Wi3F@rk4k;Q&iS@VVad++q3(^v=oy+eCw?EVMQA|V6(Dd31i6db9fi< zk45wZY+ln#m}lG)$`(I5eb#qXrUa0@N;Bai+9g}l5m%^@77ZjiS(KbYir+4cd8v|_ zvgei`k5!jz$)CK~M;<|#43-sS)6?I%4htmz+J8`)s#b7p;$Cf1D8hg0g};6IxhpyZItPj;gItxvTgF_&sB^nue~C9t*=37vT5drn78?S8P<>b=A{{n>Gvp3i3T3WRqKb9@$ahp)D z;u&D9?o)W&Hl8-spx7RtuTS(tu^=7oRo2zd0YvwX?RKy^)KoM3VFZC%J+T3eZjgAiLGzcUpqbgpcVq}831D)c#d;NJyQXNqhdJwbl@ZwH=t0Uyc0z4jX*^c5` zO{281usAyCVXLP^Nk1eM3&pVvTp`@$C@BdT?fkNC*TrLAO3H?}G~C=Ysbd>gEklc> z?yWduw>pUBWasw~^ML9ZcUjx!>#8XZ)IH{YfJ8&iwNXDSE0as-{l%4Vvzt%v1xq4Gvwl0AN8F3Ph6x&oP%P>z{mBJ@MO~Uc#Wj5@A~Z5S zKJSg+eb{ebw1dk;gCdp4o5R%GwIKQwMF|NB-fYx!`}P~wQbGc`?|r>@v6jItWz8$6 z;#66=k}eS@VNLokUy6Sy_x|_iWe_%8hIXR zPUTB7Ty1Nh=Q{sY2AsoHVLkrKkbgN^<>)s$UT#&@XqzJr3eey%Ztr}3hLc}arC!o> zVeviVwML=lPmmepyX?*|GBc~vgg02`fB$lA}UdM<9jRddW)zkGw zZcDGz>+3&zY8nRkC+7|NP#NfcPnlqTUXcndnULyBiW0En=M6JVRNBAn=2)sdZ!Dlv z)xRLRoEF0`lYMT^`-IgM71Lzd8dXncJZyP$_^0XObkm=T`BPM}fNllgDB?O62LS?* z;FYrYda}W-oX2)%nv%nq2&9Q=&)v^ivfSPk6&VgTyDo6z9OJlF773>(|}Fk}+jd$vTAcomV)3IoGj! zc^jpYEZq2)x1Dypj{l~ z#Kp6C+j$`b0p@)$ze*5MY|I;5DGC0Fk1Gx#AQQGBe}O#;&;ZbUW0wR1%;!rhD;5L4 zKVb$uW7#2)erSHY!F@A(b>T9qq9CMGZ9Cf(PCLb(Df+u)s}MUYC%e;<`^wj$p>6e# zo1ljtDhQRmnC75q&wPqd#cr=~C=eS-r2}(gz5z?76yQdTJq%go2tSYab zjZje`+`C*pbVvQIWY(ez17l4?>>nzODbs-&`2=)knqj><(wyGcW@X>i@q9VX@@Q?) zMhZq;QoT(*+^AmyG-wJ32A}}lI3iQ3%I)AJE)(smZpxnQrA+R6FLxVvUV5~FcoL1U zlc^?|lLWfRoLU8PomVXJ^!<^DvU&ln9CCiEK*?Z=pUb;!8Z=qUU->}BMMIZ1IjOyg zbcx$Qy38D2s9eb_!Jh#w@7_FSERnL&^R}_^(w6j}2hA3(>_Sz>9)j5&1P|`Mja`1I zC2TOfdrBU=Tq>ANPt!FteGUeUtj?Qx2ST$yJSBw}nHxM=#&3D7?D(+$ z>mQau`5&R!z8)_H25kmS6biOZZ<)$#ci^#0Q6Ihc-@J`Z2JWOk>97K}_+mtLtn;Ai zCd()x`wA_Uw#%NvxF?&%yTjRW&nE&6iYBH!^d~WP?4z=ZiisALo0stUwYOHRM(lb7 zeD!;#nW^dr6SEOhQ`48+baZWHh0Lk5)LosuxqZ?KhuPWL{hmt^9Yk!pHG4ymGh3gl z(SE;abU#KkxE(Ios2!N}#I7F=DcFuZfS334%YtlG2{by5VW2~3{)i_qbXavLaZ}5u!SUwU=63b0lDe~cH$W7n(BMy2nsyZR>Wtq$~`c)4ELsX`+_b1uY9cygP z7go6q4`5}?vQCvwm?HsC9!h(vQrNoz$m^lSA}9wz;im8fx_}Q@l|JVwAv7FWKeBIx)6afc zn=NOr`?xRHL_e^D+Xfs7syfOjH;rW&V6y_u`%yixbzSE4x z7Ugd+S|c0eEXTYHamaZANxFQKuR-L8g!V6Pae6mn@Apm}XeC0Ql7v^>`Q*FOmzV>( za{=*+-1X(@m@gXo(&=TT^%M__K~uxXF2nKhvHR72htqx=akbkPu+E!+x_zKOSwMjE z6$C^0hu(=$?T4a}5WE3g^!wNYPGfq+#1g9@D+6da zAp0}h+$boCHL1LD>Gc8O&Nf+yK4@hD3PSB555ONA)~=E7^v6T!S#*qYhxL>g5L^e? zo9q{PYckeu>SKz+}{lk;`pGc)m zL38dPutpyeN^hJ0_V&ID0k~6JhnKKHVqD(X$jJKnYO3wndB5=G7Va`S(9>+2~+XPtK73=gP+LS^avkWhK0ovLkea$>QT+q%2=JCWpgvH75Wg3IO!H1lc( zvv-4?gU<b?VxKqH??HQ(@`%e$?y!0GM{? zVks>jW~rmAhhOWeD6kCA}^Hs%I!FD%}% zXVUrAT9u@tR=Kt-m#WrSv-Q5lAZnl`DH#K8k8R(?(*ZUq8}k9w=>rKTCMN23>NY`R z7j<<8MHKMBjb{trw%g+)Qlbs~3*7U_yqHY8+r4pkz}gbkS8W z6#efP5V7gGAf3AV`kVy1?X}821Uc_aPZWE>1$GGVhL&CTip^RH@MQC^}r%t5B=0 zT={gqwzfG9_^!gh@TvWl>u;yL=yS>7fqTTdbI!r?<#zLLz2>nD%pIDir*%U}INnlw zpBa9?WY%xc1ly&S*zqr>N64}{(Y}DtbX^~Q5l%=-W`r{CdmP8%w`psTBYI4@S?M>u zV-3ysq1W8BBlJ+R@P#1|<{g15O|05zZ4#@!SC&Fk(J z6b8}SN~FTg-ffKV`J@{42%LAq9zTo`)-all361vr~9I|HU~OoUa1iAlRc~*F}p5&@Fb__x#aF?gMwhqk|6%{!a@ot0;Gv=8G12?_3~>^dE%fJfjCBmng zyy|(Z#?yd>tgbUqSC2(WB?u#UFKeu)v`dX75|;tI=x36Uy=5v1I*H4E{7BB%7nUuF z9x*q2x!|0%_|aWH0keUs(KSu`vPB7SVdcF|&0?6yn$un=3f5pB56X4-Mgfk@P!?81% z5y0AXW|WmpB_yR;m>}R%d>@C*am^dm?hfW9P$eri@bg5t55UbF7ejcfYG=PZB+v$j zOTi|}Osr#*LH8E$k+vH6SdgGKg;w`BjBVDOx3AXc%a5jmytrRv7(SgiE!g0)oOoef z61FSyqhbkI)wKY0VNzFipAp<^d!4Bvwzl+C9M)kCKpXVYL;%4}Z8LN&&@+wml?PQ+ zFVkqD6W1$>jwGnDU^$Z%&mT~^Bv zE0>KBQH%B(fBQx&R_d1jXBeh|7Ntu`7Rx%73iz_5_l1M8ceX~TKEEPBGMNYDZrCeU zf2ONgkQ|c$INf1pcFQQgn1hvb`4^z0{M_ok(bX6;sZH(7FjC6L!gQ<%)G+%gQbW6t z7*`c0^_gfS=rf-{#5hHLh>p=UI|i8fR}j$y>7qJln(!1(W$pnb+09rT!F^Xj5S>~T zTqc3+{FRR8Kw4g$zI&eDDg~$MyMi#Ev-tpPDr10SZ1WRCXcJUksf13(Zg6nFq-(ml zeGWBSqVT#W9=ohmI=lHcp?HLMPN;Cric5HNEsTCZErBRG#6P=z_B8b5b!Gf+Oe7a8 zZu5$~<3c^7X}E2NU;CWkU62sTI_KUmfZUgB9PzHtqf`BWm`c-N0b`r0D$2kk6lCs6^e?Kj2TF zsF{TVL%@A*ZtfyhlHp-g1h67Z?R6BfvAH=lZF{yiUnD$}@~#nlr~@@bTAlL{aol0p zcm#YF6%sh;xs0!@Q5SJz)Z& z(Xr3Z7i@(e(cp_`=nfC!-`<=yPgO=Dv0z zoumxglBuF8-ktRdhYBgsUzBBxkw5qT=jFr#h1cSu-bGioy0`jnI>l}nd8TK{s$yuO z#_m9}&Z5Jz8Er|9!d3wQ>H~J*ow8$Y$Od09yTul|o-^b?hQYpMuI?>a&6fFr48cTy zI`*Zo{rszgqoi{}v2Jap3fw{IO*R$jaToP|vtne9%B+6QDdUvOJt8AwaHPO5120iE zESpIDPW}y!R$SSccUeA*_W)>(OF8DYYOi*4ys;9tDZcze*u?XiF7LzNQpsJ_{wh4R z>&31}!!B5Mn$t#ijQzF9hv540uZ|$U1wL^M0BBs^S7$t@lt8x=vG74nv#c-u6&QE~ zCtvg1&54$W*-n&Y^)aunuLHzlY^D);iNyl>`>KcCaq4%g22KAl7`$#ezi3IIOm{jC+-QqIO)-ElInvR_+rPgTI<$a3f{ZoRmUoXzFaNW3g`EtVbS~A98jzH@xWUXd zD?16LSnmp0n7V4E7oCa+Nu)Pl&Dt;P?5AEC26%mXGbixYKx^H0DmIJmCli!vA5L#F zZ033OD>3*Uh8p<*!Lv10Ms99ypBIECKGQweRO5Ol z4pFNiP3W3PU84MWZN{G2jb*8G-+8y>M`NPan;BcO?ut3tkuG)R%es24T4p9 zPW$oF`^TC%VrLwKqquaFA&=N7ItH0a8W1bq09+^IGFMEzbZSpZ1RX<`ha@0Wx86aA zbp3TsFj#KLj?&PzfenAiOvKEcu20zV=fHMEB%m%9e@3#zVL|;<;2+EgYaLlhZjxe7r8( zlOrn}?jY}60X6+t4g1}rL)!J-Rp>=o#CxOV!pa+-;s+ z@dn5FMZ>CYns-imYvU6W3qSx)L=EO&ToOAvyX;+@+YB@Lv0b>kvoB2V9 zNf$sH!?v|}mhawOfM6hF-><@Rnnpt-^cEBfy|}!?V?)SuEj>)@W6@LJe@egmflVuO(&t7oo6YXX zFZ6jwMYQ|kY*zYHBsDH%pb)hGZ$zue!-pJuHW^EG3e_Lda9nwtDmnFmu8Gj)hDA~FMHWO13a zsmYO2InDKN*y2VUEkjX1Z@U*Cup{DSWRQHeQfH%=c~^An+-O%2$?e?vQl~pFH%BQ%$TV|pk9t7T4+`>?^b{je9!&Mz z7`Pps{dbqPX7lUtVhfzxrlgP?>J1rngtD1+D?Wcf`;0MgoZF0~Iy$&U2zu zKFPM*%B~O=nTVsL%w5?8*1ULGdwwJ>bT<5`I1}#DdcGi^ZNgI@>F!c86u-fdkj&P< zwn7U_S@F@3wl-!YgapAYObtKqjvm2mA}V(J4Rg=$g?M(9xpMIn#+9Ngw+8S0)b1)B zxeBWc{cuiZW1m*U2Quqy0NcS@zC0}2QPI?V*~-3DQTABEgH6m2iD%VRF$ixY_Mzk2b{ zDG2x|!2DRs^Cp+d3L`GxC8gPSUE|2vj7Bl-IyIvQKrrNSYqMhk++3TzU-R7 zz_n@dj?;L$o5n<6Ini-;0TOiZj(3X1rIbQ0j@;6%|h zW1Gg~QB~+zO|lqb7yMrx^<*_e;INA#m)`|peH(Lk-lyo@yi z7lKUdG|L;`9Z5)VSCb55wWyZ`-ZMqGM7Xe6!j5*E5|NMRONvQ%(ay z=c1+A=AugnG-*bFg@N;VOE9spbdYQU0`((hKS#_`7uv@1J=3Sw(q;Y7c-3cxF1JWlC!hfNgM8_(=Y|J`!MsCV_ z*kT=5pCUYLr>I$26rG_DKCu(fB!&wmEn9`Xo;irNwqC!w2~U1mmFe(7mcQ)EU6{(7 zR+xX$dLnWwPB;}_-$1e3d@D?WD<#dwZ`g9T5ID-!2aCk@ci_!mju!Bjsx!*>zDJNM zV%^CvbS~uA`ieBPxj=_BQz!bRE`d58Ba8F2)74|#>cm!k41A@E%eZOBD`!WZBSy47 zXL>3jwIb=oG#~PqPq)22pXKS&W>$VbryDuTkM~l28|0uD1QC4%vA(0KEjn;~qqq*q zJ)eQ#f7NHtPD|`F)htqPzFa$Y|J&Yv(V!+Hr zX5%DbFEUghSiQ`4qpPWTrYg-w^^~d~`er`2w`Zsexp-=s7=h;O>$v5jR5lT zSljazH}96yBYcVayoc^r@NtT@`V@nq3JXtuZerNv-bwY3IEsMu!HKxk)p92?tE5=+ zQL_8b{gW{tpB8>ypU`5zH8m{38!B1-b5)<{n=O;oL;Eb9-NMo`YFf(TqNdN(Z(_?Q zI$XQxgj`+bYZgSx`SHE{+}zH)b8>KNfRNRI(d3rLuHFDBodK&2hHivO5#ObJr#L;> zkP(?mPephdHcMwy2J1tAa*!>xo40DC7Py{+duH>*t;^o9$k_Yi?w@-?V`ABJi;CR$ zYRP&Gg+G#fj{C%&j-}XvR;s=Z`ywv>MCdcO*R*LlbSGov;$~61fBz@0Sz!9G3dH|+ z22{!0TNtFSRD}l3!kH?Q3d#KUAetcehBIul0mQoQ+qZA}xD#!JrYEKHi7$G6Y~44h zu=nr`o~6J49{u9YOkJhQn*E2INLB6JqrDq^^amV}yWZae1Md^?J!f>{UyqRfc!waS z`_wYHq*8F1N;27ra#m+b1r~R!n2y zV^PNY2p?bRXx@N9TwEMKv-znBFP~vnv*K4ndZjhTdnqn=#?7}tXcLEAd_Zt{4f{lX$bJ>hWW~6!pF002#in|IQ+{CmOMj^b0$046q zPhw|ezq}U5Cp7iY+#KJ?y0l4b4U&;{-K2T;&Dld+PQRL({lZ{Ocs#U1miIbyZqZ2R z&5Ky8fWx&HqEjBE}{SU>;YUwF?uxn-Zy)Znv zdl0Uv8w|Eow6F;JR$E^12Z>0xz$g5?{5Y<(dHcreSf^M6}15?9# zL6KdLj^c3|$P7JNyYWx$YQv=|gBw&A7gF^}N;Mz{jEaVbmBbeClgsdv!7HI0o&PZO^G%11#A<@i=f=W*OZJU2$iwe8Z?IeCpKU~j zhtt%CO)v{xOUe^GuScG;d0q>p1TLZWXOCpQHq+x0v>yW%PkC*B2jY1z#YjnMaPw)s z>krD1@bxbNQ>_9ak%8vF_>r)-b@}!CX+PBwFbU}0VGd1pr{|a)Pha4_)qu<#ie+Kk zEvo*I6nMX2Y3W0k7eBvy$@vPyix>AxOK(zjC~yO2`*%4wI3k@inIrJY9!dD6r>EoA zjO|Tt)OvyGx|+@6`nOH~t3d?xd!RPpq0@R;+0xRoi?Z*cED(M+Iob+CU8&l7+>kg+ zi`MDcw=547J@Q(oyu`)*zW4Vy_(^`sxxWjKnP>Z}LunU#h7sBt-07oq1&aoan5bOx0B#-@lc(%XTbO%0`A+%@Z;%X4v zIz?Uj^Mn1jy%XM#{<%>h+ku@}bS;f{<#u1o2JtV9G;YpuW*q?8N*uzCBb*Vj z@-;{QZSXiSu=9IO&FjpZ9NC>Q1Y+UzBr}t?%i+hn7tP3VKknp|z&k3csvqv!@iyE| zK@}DepH?%THr9ORPhxG*8=JJeOss6I{;K@FHHa<2J#5B@sn?0b3fJlc!FQ9)e`{L> zY>o%A=WtM`a^A9>acEEc4z9F#(SLh)wc1YZ3AIbS0&Edxf?(1u7qL!8gvtB4FiV_npvg;XE`YkJ4 z+qmg~z&oh5x<5w+)~cj-CE?e-6mT;65s_;Xe&456;~a~^d@Bct>szRXcvy7VTXA{H#bgc1L~+%wm9 z?BgDLzuX;j1_timPFAdqml{KSmwVz)8`eVr)r1C6o2jSuHE)yB0|R98A7wm#f6Ngg zK*7w4kC#;VS)cAKNuCP#MGn?!Qcb2SrT7y!Z_i>}k!D4_xCgJFB-LIhm|5-*9Xi=D z>UI#AS?npS-W`{~_pA%XMrryZ$Qe~ktBFO19}PdUtFoH@6~0C+V_=X(Z`h1jaG08a zIQ}D5?B<|O>*?3)ydq#ghxW9qw>Nq^r*`60VD>W&Xt7c|puiRgdRqXp<*Z-7_Jkpu zbNH^I8%# z>7_oVwzo^BR@1$B@l05FW_RDYUhkvhOZ;O4B_$i3X8MU$x=>HuPKDb{n93D;-OvCLz8A1N=ylmq2M%H8d`f^qib3uX3MA;N#%n z2;0U@;in+SgIZAVLmpUAG>GLKM~efP^^TKrt<#;U55i~#FGBO+4l1Tk;cXn^ZaJiW zT7L#Ha{i`YpFO5S`KD36e(3VFj;C`h0x_S38w060U$dfS3~}DgNStcb#ZdAY*4#$3 z#v}9#>rnAyX@Ij;3KtXi^6REQ_^z@Q^G$0)k)P2HL^c&1igb|t*Spjk^XEHV*IJW6c z`@KmDaoW~1(r50VODTPI+9t_${(#}_@-BA~`|$%oA00!{oG^ACRsEXram)Pcl5Vp1 zxbx%?@jq2XSp**LT#%^|i+4y^L=;}y_r--eU zlzrIbh1lIa+pNa17N4h`@QF<9&@I()i}_aRl#c{=ZE?Pf|G6GDZ|~eAmJ}B^tbG6H zj@Fwt;81`rfJx`+9MERByZy06o)37mmo*@0#0I=ecr8LL4)&)ffV|7N?Hl14s33^( zw;Td!^U+<g&rbGnd=3q6~j|XC{%!Pg~DdlH(@?pS~GI=Ie z(0&pt-;gVcvGA%KC+akAVOG+FMlPeBxR25`i~q{@OH6|GkFwi4s9%N_wXDifx)%A{ zb`Jhtc>HM-Ph)QEJpRWcVn3B+^{cYpQ|2Va(i-tzVNL(lgK|5$B-{=G<};_-Hy6KY z%Im=Q75!&}`^B5|12QOy1c^WM&lFlq92DfI9KIhDY&PdstD^y(7{K^?Li-*WnZm+q8cRMj*NOGJSBf`62kq_pV|$-$g@_P+^cIOeL=8gTKIs5Fr&)#dTvle=l z#l*qMc^g0v514#>d{Qqcv>m8vXhb0X6rH}`cB$?@>im+#)+LB>tZ zO;>srkZ-YXB9r;@Vb9s&&LnUWwydDA#`_Sa9mz*`K5de^c}0a|in5=j)-QEM5^zZ6 zItw$r0Pgz01kkGvgMvas+d-s~?55K82ZJ%$#q5vSpO?Mf9vi)9UkhIc=oA9|<$(MG zKWoxhu6*Io&Ym$nvi#0tPfXaIy5~J4nE0Nw`qj#A{kTx2=C%(Mp2M0S)P@&W%VrTW zlUsS^xl7Dr+62b~#yf)+SzYLSDY0O|+>{0#$!odo&)332ThPi|-^^oA&Xs{@;aVNo zq}6=(>(?(jQJ2?iq4``^=ql6H3ymycBBy#|&kiB`;oohh!&$kYbag$0{=bQ(IW=?Q zKzZZ#{qS`|^N=543`wMyN?bqT_e^d#%LJA+8!8`!FQ;$Oy0~Imo#xs&`N%$3E2)+D zJa@}9xokdmv~FP^N^{sh@0u~zdr}>hEn+|r>fRUD^ErHL+kooIy>zq8^;?E0$_3Ic zSl3oa<#uEgdG^N3r{u*LN;0cP9twqQSmkATHH_&~koor~v-Sf5@5@)Oz{x{Ut>M`$ zNR4$r`G7VOB49tvG!T-Nojp3T9JF#_*%Nb1R@Ur58WeD@U*`Aq4EXvuhTZJU$LxYi zF9e|_dv*t?Rgp7G;&yI39G5s7d!v7;Hwp<6NeD9RIPLZhBrO)y<6}cR$5x5wGm$TZ z6I%){o{V??F<$d;s3Pm1)Lzd>x|Lm_g8w&as{pSx>b#l=Nz0q|R2 zJPnUKNM^W9)!@#~&XF@N4_#a;zJZKgvc_-EY`_b^8nEf<=~FM5IQ3r6Sb`aK>omI;zFYk5O z$EtRXP0H?~cjNJt{JPJdcNdOtGU(loZxHsgQcs4i;Y=14kr%3?Ac8wD9%?e0(6TyH z$016vaRmdHl=681B%jmJtl1$&D&oaj&}Ly~8s9CenO$E{2Ven!#?PNWBaAD_H(93< zL1hhUh26lj0kJZgF=xDl!5`gKrY(P}=9(;f@Sm(V|LSA3-Syj<7VKykB$85BSGP4y zi0cLkN=Y+_-Ifryp4^>7ysS_T*=4o+G}XXI-&h%7|8oKu4FyM@E+Y zzIb!xNC!{?G&Mh9#!*depG@W{#=d;><~h(1I&F3ZSd2#R&D1rC7*hHOux`~cekX6hWNhTqC0fPi@W)-5az4Gr;tgFB#oY0DgTI3zYW>jr>op>C(N zjVCsu*#KC2Z#xkFE(biAI+*=rY=0^lY%Oi-Uo-Z((Zc)E?#mF!5vg>o2gm*&ahsdP zaUP>qJ)^!PMI2kzvn(2e5Zu+)ff%Q9<{qm$L)X8lhkd?ZRht6`P>G4V#QFuT zWcas3h1oC5T%ETG)~!?X=axg%Bje)or*4`DhhLXfO=DcZN}O)=xII<*G`8|-!xwVN z{ulI-0JE-wu@|6wY-+lD=v!&P<4k-pQ)_QKsRaLW?Y(jLnU6NcWCIRt?whwuubIx? zU=m00KK*z!pM&GK91x_!uLd93whXxKpQ-I)S(xw&@?^{n+$$)K4-boWxv@v$H(O% zydj>SK$XYVG`Z?zLRag#mdtV5m@S1VKtoVNIsr0_RA+(;OFFQOp@UBh z#@nboO>W1I2+}g?o)%{|VSntzGJi^-#1CpE(c_qw&6gw;rjXuF^ZQC+>xrFq4#gkYbYhkCXTeZW`r9x6 zwEd0X`mG{`^eyRA`8%o!k5tyzr!fmT)~_(GCkFmaEt87+&6_u`#oXRjgmnB~CFy8L zW|pEI7#K)hm+)MRtJsE)c0z}*OFIDVHSOi)w|quT=Z~M>5OW_wA*8~H1Jzy|4G4G} zZ(DGOm>mq%rwxh6$>j2JnNl=c^QB#KZYA$X!?#Xyc}(%3WCE3ymA@~R{w9I88*5V& zPDl70s1)-!cK{e(T4v_Og8gW|(uZMv6O;SSa_kI7MUUQKgGF_=o;SpJx;sa3JwQ+} zKuZKQ_uk==vh&IoJ)ASi*;9n4$ZvC?g%2g-BjT>p8Zu(v7G^+3AjdR*Cx4VBth(uW z?gC6iMO8JWey=$9+?*VUmfQfZ4&D>gIPcRYR5wng*8vS_}g-Kz06hV2Yz%}of((p|mzcv!w-EKICV-{94c?Uf9#{i-2;k6E zOz2n6Y=4)>%gTvAO_0pS5i9B|n8EbsmSuU=A>E9j+BUy?3hx>zLH%s+cHu#*C+k*U z_4yVK=5v8ym!HAby!-$sf`FNv`u7i zfF@&Ws-D+I0#MA$dRC}5TeI?uQ$p!OcaHserjvZG)!z(hon6GjbidZ$s>sG5sq3ST zOnNIql{O#5aYX=h+DbjztOw_YG$JYrw{S28Xb0SD`Uc#!wt>M;_iC6$%>&D5$$BxrhOy9F;UPEEyplfC%5Cy|<(YEx}^{v*>wGC5A+ z8oE5)Ax0nci(Y*ds zP;EjXrLbqSd)&S{lvi@fvTLbq=E+<9+%Q=~lH&dcO61zT-}onZjKF$a74~Ik&*uYx zfqsyn2n3zD6srGn9?akcnf*hsy%1&~V_;wa@=DwR^8g|(=)Ljy33|HDQ8x_qNm46= zCeDx1=GE>T)Q;CvKBLL=^KO4Wut6RXUiX2g-C0~^OpXl}^NS>nQanWLh#L$qxP<1? z_!79-1$XYSvmew19NN((KD-YQc$+$Z03%L8SNGM<&FaD7w1jAW>WS~VAlw?*onZ_sDIJ9yNhua=FfGdgW<9MM zPc`P1_XMc%Wy-@R)HO87%mRMUNbh&zeCX*`5dx60uRwx#svR_2#6!-#<9OKF+2dui zvn1Vv9~Z*#{}iJi zxXw31fdfLDhqdikPdNDy@*MDkn$lFL{cuhf0GKP7kU>+zNN)0xZk(laQ6hJ0xJ|K- zZNWD_Q^P*%&!IWXTVnC@vqHks1mv7Wv7lD`>AR z7BD3(z)!$DfjR~pNYsuMntlXcj{4@!+nk&PLj&<^B541n?8{m=5sO zaIMG9kbJ`Kf93$Z94*=`sImcdNI5UResiTdF72J+>6MpWwa%|(y?oh`($;>YpzKE~ zC872t&>;jp!q28byY)t`{vO|k7Ub89_6^}`&h=73uC)p5#>{OER4HM$cG>7%GEwE% zJhyaMir>Eq)|4_afbCPJknq89At@cN!G!{@S)k3h?72zIQZe7;{z|W*-vGpK1FDt* zgAzKzdkjAZQBhj@4j#Sn@ekMN!sD7|I9@qB!)xFAtQt^pOVIt!?>09*5$>Yz7%Rq;ta&U0#(J=MvOf#KkY%mm| z5}|*@MW4-s%FS1#)J^q_`-ktoq-D+`MkZt4x-?fB&9&PmC^|FTtVH*z+B> z9OIkt=f4ES7cIN~>WkhprA%Q5{>-Jj{`A($Y)}ZO)Gx+{0CRzA!>=zd`}dDmoRx#} z$j}?1{-+~PNc=|50Gv+~N1<&fJWRs8*qa`%{XP2HGs519gLlYf>oiXm;|qFpZFy(i z^xk}?&2{T`_(-^HN!uU!!Wan6-?0O=+x{KqdSG5gm;YHDaa>VmftwsF1aiN_$81R* zIxctJ()lbq@7=%8M6^r>Nsw;H757jWK-IEfP>0`Nn&mhk;(t+&eJaJ9R{Z17ToTN? zTV^}0D!c(ae!LaF;`cYBXV!LTu?Zh8Q+46rgXUMOXZOOkLEAk}&&Ws|TQ*`C_|F&e zoZ3;di&otsGXsXM7e$j%n#JmQ@$7SpqI-#cCyX;MV;yR~V<%jjDqK6CXCs(ICSn)} zK*VrtEsmcFrVDGj3v*4z-vk%ad zVbTm<*Ap4eb|E%=2v__58CX7;MXtx-c}T(YXvtj)ugR~2(npQU%mX*EOAUzIr|-Tb#7ngQSzNep-Wqr1i_+GmNGYQ@O`<+3Qs{?=Mzxq zC`8}L=rzu==@FqMfCtcgL>)d|I?rgxrt=;{6w%;%OA^@2skxcgKkWQbwf`{oG)A>Z zepaP)1``4=wf+CEG(qEC6`lzuJ6VoAU_`>Yj}#wCO(UI+?Q@~-BlNYZi{9YU&DOfw z6=b}mhG^X{SOxU`l2vc*mR7u1F7d6!za)rD89!=tcyC@`IsCITX-YGin;q;3{+Udi zN0yeBqOC7Bq(R7=ju-}!e`ZdDRMN3?X56mZw4dM7Je%HLveJLW>h{&9OXZmDJv&`p z0v72f(YJSLbGGVM#eU%bDXUmH*wxhTUA;}?UtC4A{z%ltWvlZmPN74M1a*b61gNkc zlBcgk;@=*A<1%d_usR!QE34c|slD-@QA(Ryr+gAD8}K~0R-xXD8%>vh6Y@`Ei^W?r zR~9MHse}Z3Zc#e?_0Mp(sfh+ljSxVE$lleIHql}3i)VjYIT;?LX`%xXlr4~KEA%)< z4z5~n)DXsT!zj))4NC~kXlXdFoo01SjQ|O-)c5Nr?)b>-)cbl)z%ti{TTH9o%6r>J z>DVz$peOr~ju`u;h-9nM^>mV1v_}ksieZ%P);XZp{riJn5R>v!-rTq)WWLG$Pe@Pz zLwOgtU&?R0*AW!Jk8us-7)wFGCIjfM^8dS&W#qNNv*!~6;KplmKeUfh#4rfA4>mxK zmX8(ruL)^KZ4pZ@MgBMO>LX3TXP$0iGTkT;LgUz>*Wu#&5?~R(Cgzu42W5m`zxr8w zHoaqx&O;-Hl@sFQ7s)q2JqO=D|7Yg~-Z5$uTv9n1bqFd`{~O;o4?dwMj`u*tu#ILc zfeO6sG0y)k4rHZYld{UM0~}{Jr8l2>J5vPPQS0V=P+SpH4D`5ufKaRJ$P8TEEwF4} zL4px1^6Mb(-oCXFeEZ!^Fy`#U*#DUt6a@w!+8Mh~=^Y9v0&4ikCEI`U0vx=5Z^#}E z&@*=-)7RICCbWm}_ zsA!Fmcy?Uv%|LLQ60u~ISLyo6*qF{W)aSnZI;dA-csERj{+R@P?!+-XF3a#u!iR4@ zTFvK4iYGq3)eIs$oLV?SE&A5vWaC^gwWOfnimrW7FwRJ+w<&lDZzW(QOu$MWeA`_i zmIQdvKX}OMn4{I}QM@J0)iZprsGzavP}oFStH)nu(e0bMIA z(2ZJ*7%m2Vsw-OXU2XWr<^J9*%i@E$I0dK2Njp2Ah)y4i=Z9-TPzKF}VB!yooRP!I zG7Uh%B^HDUz)Y-09TI?@d>9$9OZ-N<^i!_Kd)7gB#(#6opwZKY@6CqADE9avBjrJ8 z=Hj1?g83?aHM+P_cOb^siTyXcxLviI@?e-`P1es;0|F!#!~R~@;37e)eMIXmYpSPr zq;u`t@yd98tLNsJaH>aLk&_pP8ugPG;PSh}i>HYph^sbNuVcRXogdq`^FIqV_Gl8Y z+2C+NIFEEE9;ahhd6J$ei-EOrXZFXLs+k!}D4?bC=nvii>**?g0z7vraRM{HVZ-us z%hJpSkvXJ)%SZjD{PU7-tA3Ex`*U&rIQR%kT)<7?9ZQ0?q`OyOxBlE!LB*{SG-2kF zf$(Z=X_KKV&wb|C<|LyQf*ao{ZjmEsDHL01rL~O+oghT1Ex?RZu)#<_0@Li4->>J3 z_eU3W^mA#d!X02|S8o`Ywrea@ous)deOuk%Lw!DuBz+-Ik5w=87;)%2zURT!Funhm zo57OwfNOH0^6q_cy`IRUX!gK7m`=|^2Uc8ANWC?qp2ST!9*00OD>Kr~WW^0{ zvnl$d+3NJT2xs?LBtnXMwe31=z*mkL3rX1W!#}C(x&zU8Zb`<+clKH+nU|C zjMUDHSJ<{+-}WdP4?H^rP>^yhG?x+ZGSoSp8rQc5^$AGplezewUAwR9Ns5J^SSC%o zwq3~|&6>yS&!~fRziJap!zooUCiM`jm}`~Q$Mt$oibGob z{@MXwKL=J~rtgn-2F7~>PmwU2DK$9GjsN>K7fSb7ljImWUAe#)Z`M%WRrf0Hod4kM z@%6F=A1({fgE^f&(I{69SNkJ1`z(B1eYvTiUN_t+j%!xnx)kuwaA1d(I`x5>%3Ih} zXc>=Y=0yTjO#M-~&Ki=LeXl1ex-|>s)NW`K@85&z|I>x{y=e2CzK;mjJ zR5VEhUT2_muo8V>rs*#jOkkee0#@Gc&Jeb^%X9MUt;F?*48UG40Dw1^x#YeVe;{`W z#Pp%Go-s4raPQW0B30}3ID;7HfJ871`{Gy} znpMkj(^zalvbI|uCNx7d?xUWMgNAT%03US28^YH{m}r1gJc(~a;l5m|(}c5vx!??IW>$EX~e8LUu@J8`dLM`!dqHi6MjiqQaW;WmG1gCl_ifB~C;eN3|dn1EF z#i>`e)I!&5wOy8!C;M|Eak)M?y(~SKxapAkS(yr5mrD8my`Md!R>urUZzSjCi`B@) zB@rG<{Okb(GHt^|5C&H?nh-KY?tO)}nl3M6bHM4yoi(06O;_6`*CDyuX71I>zM<`u z)J#}XkFDo~W%WtB?s5s4sa*4g!#9y4@i~^uT{e#$iWgS{tbye1ZNV~r;$;yi14S2S z*hLoS%GQw#v7wsZwDXpCY$ZZ!gX!}3X^QaQrxeU5~x{YMj)iQPM7c0WWDV)a8%e5)Lr#dKQ6 zr@ie{2um@Z%3ERLfk6zGcQ~)wJ)0L%d|}-8?US06pMJR$4|~C)qJo_@gqVDs(5UK# zq8C*=&w^Mb1>+vJ^Fvtk>X`Zr{63fKv?zP^y(@ot_`5?<$R?`dJM!sO2alr0KbaSp zSQSdf%p=8MD|DD1>`*MX0G*FCBXqotVv#IG>)FY>i%q~Y2;Ii`MkKvLULPURrn!6F3^Im!>Bk%>hf8IXoSfYsiz51*|1VbNr#pLDx*77XXb496+T5zG@lHK)Gg!u# zYcu5TUoa)%>?!U*o|61$8c;mM^<(n+i5JwrBD& zz7%*s{_D^!)vu-yXDV_JJH&`H{lB&BAYeT`bHho5;^tEN(`LlS5c2^^&Cl5wk=Me0 zmFRoGpGc!R?8^RFJ$NYp3W;_;vOWz&x?`0YvE^BOY&|rUB!3$6v@`KJE|!B^%L4@( zKDNV;%C}0+PYY;;%&$s5m&d_8%Yu$72l0hNuQL8P4+jY>E61@x$Qef#x31(MjTNx- zV;+?1V5;Fk(?X4YnxcjEe>2UVj}X||DUP~x+7W^8AW_Hc{)LYCb^49tmx6{7163j{ zyctz}Fkd!4F)G2HgSDIaT8O}nIvy-1w8W6zsf4L7o2(dfbBeVsa@fNuD0garTB&n- z=c)s&L^3q{=J_0soc4|GG~35FRLT5}n+AV8yHl7wm|=}s`WaSTQxgRa zoi;Zi%w8ESS-OCntcRUf-WZULbvs8E@+V({Kn8_hDO^jMSxGSHYL;F}}_B{!oR`N~UPCE~+dmZyR4oB1_IuC4uE%~N3 zT_fo>N*>Z!2sZV|rY~c3azihP9cp9oeA(S7g(#$USy24W4rPe6iK;#A08UBYuzP!* zjdwPR@nqB6D$l-a$)~LM@LGc*|1(O#ot>N7-qmxZ$#^L2ibTZb z@6adxPIu@1IbKHv-9hz)+6r47q@7`*3s__r^M8Jk;HB+!^h4uARWcYG>A5XB>TW2Y zwtc!6!sj>L&2>j0^bInU)2e?)?)#6lB^7#)UzEqOzkI!8)}f0#vOloRl2CA7{ML^( ziY@@XL;hOo1&i*qE37Z^6gDY?YZS3PebasFDUO*e;(w6^86n%T8Q!+iSbseh$y&ULH^InImwytdZ#fbLch zsjx&3Kf$XE%uhHuESGHKppU>z<;&v|uOgUtGfq3kt9b`DPeEp7^L9Ky!K{Eoi@R%q zbYOlHqq+A1`-%Eqrbzy%wm$jXr^B(EqkZgl7@QDebv?+-G)&cy6=YVlI@1sJF!r^? z>0oowdSD?!YG32c1D5wx_Fq~Ijvc3mT_WQPB>(5Z&!a5u<({)s6cBg|nmQ|(HR1-H zId0;7Pt9YNC5aepGbo<)avhrY7$F}ZYo(J%@VQ}rY)%-cqa|EnCRfxYwXy2CJ$5m> z-C`jOrqi=)?Q-y&C=JNivw+7_9z)qvD31kmo89s^A!2%}gpJ|JT+ND($VFy<(coX? z@D}~4_hnCbP-;hE8uEBXUt#ve{nWlvF9XISDe4Yp%cRIdA(>Ew@f7BNLpp)eGaO2( z1Y^Y6_`dx1%K}jy>y`1;`>bDRMTkYM>6`QWlHpMv)yIytfscx5!+TZH z3XfI8T0YGl))?l7b0Y>xA})+~T`_;9hG1J2jw4#HZ-vNYlEmQ9V2N^5D*4?meMfz&cQR#dZz1aen|j|pLNt>2bnP??8*yTA%22wn)c;zT&33@_ zfVeK(a-jpCJ@TKT3|Q*;bDix5l_Q~p}9O+=V>jBBp%N%@RdPpqP;rtJ4T($@DNIrsC5G_kM z2&UD~qY3eGiYHf!7uFYX7B`T~R?he9Pw4a)O#KU3ewOu!7UaI-ef7FgFs=<=uEcR@abFFsn1OA?JZMfL#V)O^FEb*NiKy|QnX0w z<4c+|6z;?8*Mw;FXA>pgsDY5pE%wx~UecDWd2c#vcF=E;b~^4XG^!S2Kib8J=;)iC zRc&B@coyXC^DFPgQ1C$$9S3e~!<)*G*_uipkB zhJuHQ3dY99HR_qvFLKN4>VzxI+8CVZ-K_2nxQKqQ4!h*}YUkB4eBF5j9Kr|U|I`|x z+D3vqq}bZHR#u4DMxCp;kJ6cI-b&yP{jt5e@#s-3xBcC(%;wk-6LJvG=$oMh7iNzh zF_}EkwGc#he8Ar*5gZ|7ycgo4EaXBHgmYd>p@*0R;&t8{mJQg7wDZIZHPV}rrkagO zm5NqbK|^DG%&ZolZ(LC+JgWHnUB{C`=s5JXXa|kEc!^F+@drP;@FbGTjp+s&)um>w z^UgYG+`UG|qH4MQz`u3%X7tGI4>%r*A}Y^tK42gCo|gW=6M(r^?iX=RyR58 zp^^Q~?1OGiNOQmqloy)7kSv%6_Z0oirRf3Nlr7%a`U0s&QtMsa)n7yIQy=AD{^DiV ziQtNNaBzSrD&m{~U{SqsqH|RfKIo)?{{IysyU10TLEFUS1?X*dIi;gZ?i&HMkjI9G z*C4XhEa$4OYgho#MI}@~C~nczc&%$b$`OZpcwkj{Q9iuJ#`*7~00`?kjHh&i<)|I} zU}ORU!>ePNkH5v2X`{;~=q78jk+pjtTlLi+t?#}JPa*OMNx@I+vSr$}Rgdav!`I%3 zeJ)!Ha8mBnEj8ssRTgglHDL2_VW9lUm*LmeliNLm)ysi(?vE0;QVVI; z4!BOq2rZE6qj?fCq?LYMDPX0Tc}!4()!l zq1|Z0S2)-O^q1T*^Aa{THp`&5-UBEb?2_UijVq_yg2Xa-U;TRc78^)+(L$FD|H-~| z4A!pnMLOfyDHur{>M+licI^{ZN~&H;Yl=BxAN5$TY;F0Z2-t=%UGlCc6?iSt{$2md zoY1)P%l!$K4-)*D?7Oja(kRRgqVj)5%>b=rjd`Xk*(h;$Dl&HRc2@x zXWy{DSe>>wl!vcu2--_pAakV}5Jsv|2;e=v2dItKqcL>=5=r`8GhkP12@u!*0DkHY z)NA6(fyaU}in?@`SGUIlDAPUDB=cBu=7Z*m@G)?rn#a|jfWpCl3Sjw!oi#!#Aou)) zeQpHd?$qJ$)?=4JYQ7T0eSS8+QU_@khRNCGFI`sN{MNlu%+q+0GZQzMjjPN_A@_i4 zc3yDoSj;@4&C{>?r<9KIQ%ELZC!T{<_=~3 zmORaYmp-!SuH%i&Vc45TcU@-dh=5SeT>y_s<6Hj#4hQ=VjyUhbEVQhoorxQ& zftLPMwRy6L^Ay|$Mc9}rJpJL*U&d!EKRuh+tL}n;+yyt$Sw4-7w;$^FKH3qkUn^<1P;T96!C)LFUDLHuXri`O0`%f#*j zGTOLxf-r>#Y`qZPPyH4WK1Lv$c-=IT#6Xfcbm`+)ha;LGC5#8DC;!~~Lp~r{e79!j zF996%`IG=R`q%k(^>;u3iJ!8p2j}#x=YGFDU5Xub(5uUUf!g|aaG*z**itZ63YP1W zC-mSX%5=W(AHeY=-4+5||5oLoN`x^;`HYuqSB4{yz5=OR#*fDOzqYdQK~?C4@VMLl z_T50uC{d<1A)S{Lt`ml}YGiB|XS>z63NhcOOWQiE*r!T$dh}k?%B1bun%dIg}QxRNN zT(EinxY1X{BL;TuW4$ID#b!L)dRCj3M&Hco7u2YEb2K?YA3qL2UZdXiG^GKSR_n>E zvp6`Ldb*nVKER@FS9kP*7A1X3fwY+0A`?`;0^lzIY%84)0C2&Fosf|lFNF(RF!1!q zA)BzyoGbQtRbr!G<1i>E)EjFx2J#Ef>k_l>&f{ z_np7%xDaud8uMu)Z8F@k8;w#MRLlrjDwQXJ4He&e4|;0Sb9ZSDI0_+&g0Kq-oJXHp zQv5GH2fRv)_+up%5#wC=!g%J?;f?-&*6%cVMrs0>W2BE6ODJD)Bu{>AA}G@eBPkzX z{_U$}>E~1Zgdvly9KZjq9~L9xvy0242{!_ACd80k4{yUf<;Q2=!AkGxXAL85X}dvk z{t(sNEZKT>zTLR{aZUhGD+&jjD*hK8)%&tWZq)(A2}tFXoY~lkGYdT==QDS&Opm+f zy?dQowQiJwu7Iir55(fBF2NMTnY3yO*lyZL8w$*?@9yb~T1}pv3m8m@;Tel)n#nD$ z?-uUTzL21hsI>&l^rRh9c5QD0nHU(O-yfO z;tAx+Z2irdx&2Z$ti6KoyVWJ$G(Qr-jkvdCAT%;sZ%{an{ch+f_HK!@4fEdo-|&`! zWaX0;&wxj^4Om0wRq2M0&Ix-TwTmr3RLTcsIt)U;5O4O|WZvP+ucPu!ub@J{>u%O)UNCJ0;ttBAb4Sj$2Z8KbDh z#mR2`i?m}~FsK4=-r4gr&Rt8AT?w#qP9Ag<3qB^(4IA)!|_2rLRF*Yu9bH7NLbOWUR4`@FGY4ON(iG z?1gP#V(gLTpjT^Gpb>bzU^LlZ*S(b^4$5wMllW^Htg*81R4E?gI3sJnL;EYT{X^wU zb|E%Z%=ZWp?|0Qk2p~@yLX;P~GJK~sT@Wa*-2Oww%1vTcAJ}({){0BIn2&D5@*I{= z%4u5K7P*WPfyM6~jR1nuiIL$@h%3vFCKuS3qkALCxqREyRs-bI<=eO1iKWP}fpK+$ zUY2S}J{Rr+z@@(dPycpNcI%|mC(Oc_+eY*k5IEU?wgWSY&OCf@i|=0N z)}Q+}+ZU(m-!Gf!jfA9FmF-`2k5giiX_ur=Be`4~ez%5r=d<9=es*&axTn2$dh#l? z{UJLsZw&GlMec?fiOvYND@V|ZlWT_b-QD3md7jan&jEo1Y>j9fS2-UFzU;r8-;ShM zBh}Xb6461JINM1buA0?xUkpKT{XXB!WliplUP|?GSx<3$d@!SwhQw0SGP(4?h>y%d zN`zq-Gos!*r>q;kRCnhg3vredt_L0XC}&|uCW=f5p(M%!%~^vs9FR1~@np<-4{5#{ z_MHj;n$7CXFUuVnawrb|+v- zP(RVF%g@IKCPZ@K1FXi-xJND}^<}t6 zTOV_pqu|o&-Blzy{l#yPWwKNUD}%gqVw2n(6mAatE>M~SX+FW*RT?!!PSZYZa!d>& zy+$HO^bEc4%17=NXUjl(Id0qaT_O}0F>i6PR5X2-ii?~GX8l6%Z;KC&jHlP&@bL0}D$WgMfa=aF zY4>LEKJ54$q<1m-PZdMA_?!pGbPf6#|*r&EGwg zQA^B9JMa#~HX9y1x)^1}Gp=9icXVvQ<2d>3@0H~suo@pOe>WcSaN8_w%*r!4cxSqU z=2z{rjiu*|Urv`^L=6!Z^pd?a>oK zfTp0I%f{w>rdVOD`HHm44hHIs3^sgbt#Kf`TFG)=lQDQVF*UXN zN=ugLOA~$;IDfzz@N(${t)j*XQ58Ulzp#)_CeYic>YCp}M;cROk`Tw0LtN+7DimVV zQCvZ=TKqi*HMG)u=;LbHau>SHa{N0dcW$BJ?#1Fehshef`(Ga&v}lDf_?SBQwn|iKw7l6dBAsiRE0M2H30ch^> zyk7T`+dmlH2gG${m1=qTyR@5qHnAcbxCl4Whxo{MaTp(|ki?`NvxZv!g4-l@P6I%K zhPIgVNMdI#5D8C7w6rITb5GuxaKMVCa**?)NNQFeAsyH~l-Mo)hyz(&4Zqu%OxXxM(b6oWEsuPs37-2OQmYa!!NqSasg4yRHB z(MINQX2l!xnNVtSb4CT}$FsA`%I>kkd%&mXLH+TN02RNf`v#u+;K2iIVyQMqH@AKO zdhq~qiYS6ypk4%cTre{;vyS0f$ML&$4GjdP+SA+Z+>a%<^kH!eCafW@CtlmwfzF47#J9+GQ4CsG&BT~|2}|-QwT)L1f6uc zVCGmk;P4a|;C&iMHPY+%-e+hsIcy$Da_?>);Xcxc|K#3ga1e|n=L`+GC0uO?_hfR~ z+?S#W*4DYNc3y)ioIjSmX@3{%N63X~=C%)|5`bRcVrzz&Rs{8;ddOdwAq9|k zSb)Y70NLT4JHNyDuIn~xYTYkyDGGqIF(I(0PZ>dZ0Rd_bxV!=^5}tQ3t%sVupOk)5 zeFhn$pvP+O%SoVx5dk#jqSof&aJ8V|*WM#fW}kJ{+B^M8f?s&)5{xi^$f@cAUd(-c zM6Wu0tR-Dz+KRZQhf|vZ>C&(qD%YT;5#M%j$OLKH4mt5T;zDoVi4S&pOwGCJ#F)%bH>m1JN%gz@@RM!nyw-i}j4%Oq#r<>%McFro6k>Ax6L#6loxi zUbO6X-txKCtS$-$vR1?x$WAyjCvc1ecM5@Yl!dep(a+U|4@nw1Ubkc94y6Lnf{d0D zM=VQLC@kCgMvvc%r|q$T>5spg15jw;3oOex^$7zu#EyzNpb8`XXTQgP=Y78bfcp)g z#xsFB3P8P6l^a&~_1ut~-}6x`*2+!NFE>zf86kogl+(2M4F+L$5`eC4*ALI=oj+il zQW-c5z$m@ODa6uhx_qK9)W^j2iC?{JWV4a7J?-PueK@Of9TYENVxylZ#6{=MvH!tD zX4T#+>q~yIu4BpPY5g@qQIyRYL{MSab_z41(rqs%{$_I53_gskWD@4$f(GY%?Z)~Z zHOa0IR8rWquiPPo1uWAs~uYA)yfP?LK?{0qR`1Wxlz?KVID#$VB`*{2V(cT$<4|EVCC!NUDnBS(p zdgZYca=H>uz(RO#{2C~1cgmd3M*;vJbyHJ#^1k^;7y4&SKQdlc$I_F6&9#EH^>uIr zYLG9Je{!(v=xA2_9VPhowsaGHmRs%IGcU9LE3u0%?GTEmc-o9?f25Uo<9CkwckfVT z)069=NZ`QMsY0t>tHRu3kH@uxAdNdPz_opjUZUOiQG7O*I6^vzpj$=>W|E9>HlpD)AX7}b zVd+*z;^cU;um@pEgX5&B6?OvvTM`h(Mdj>`6{=&g0;!Q?a1=bjkMp-hn#lOcazkrC zyOft}CPnw>`7gM%Sp_x0PSXtWs8HLCW?U%n8L<~6ardU_och(Rm=4l)YpG!DP zp~WWEuT%m^r4-@RSf-??WIQ&=N4rjME4O7%PYFx@Ih@h&QZ$S=N+vM(&_!d`?h^!8 z!xbZsiTewCK1_YoD}~e!Eaa#Zf|fcPs2!j{!`V-;oJfEHVr|{YI&6T$G=$&y=)g=M zD$0Aqex7w-&lmET2&R)2q%kq9Zs5H$OZQtf*s(O`$8SkkUn&+nMXg|T8R+hjeQ5%^ zCD^idAk`a_!h`)QG+@*oEaBa}PuBESI^e-u|5c(F3?g6FWdSYE|7cYCTH&&0lIA`j ze}ZEx$Y#%ySmlypVq#FpWT07eArwg$&!$trR`^i+gnDD*)TNDY88*2TrwdGhn!Yj6 zf{+NCBA2kP0h=@Na?=6haKpkY2FJ zG%r~ipccLWjRLJCnlfBxL4-ihX;%S;5QUYh#M0WlBv z(*-}}qw^e{|DyhJ&{iARumi3@gf2AzYj1mtha_QkSQcmp3Isunmll#HgShvPP}^(= zbv!q6d%yB3%K*~JTAp>mOgPC)w|y+RkO7ps*6|Uzzzg7Q(X^p6BX|5d5Y&8UhadWre=Ahg~YVbh$dMX(^cWXJ9pBmgGK2yl}|) zFCH9+cmzi5^rOq(8Fk!#|Dy_h=4o~pvXq>O+50P4@Z^M%8Z6)64!-T*&XelBt!7h* zpRA{PS`Itp4&r%B=DcoM5AICT+yg}iC#$Zf&8(A6rEHAhG;EoBep@OP5uCOtTmB8pLVgoXpyrb~vPd0YhpvNDc zCl^H6%XK&aoAGR;4x&n>%of_G@T5P1tWaP#jIqiW_Sg?!uRsmYVGg3pVIkzwy${m3 zTmjSOpMNAq65o;zHc>rR{YKrUaq;W^KgUWr+HHd!oOh{E+@Gd$Y8 zS<(TX9X*=mlfvgUODL_Dw){md}kBJg7PGUsmso%IbKwCu6(q zV}#r@Bew^;Y_&Pw>Br-gAKA22e=!{En|6q4(#1cGDLR+2D^n&sANNq)H?zHMR{3nx z3$Z;oCeF;lnh}5DC3NTX_|H|F;Y^(VI!@5xZ1#1scu9&!X{Lsk(vC}6a$?^$4C)_$ z20xPy?8&$vpZ@J=R*)@4xFrn4R5QMi{U#?O!UAUW@`~O#oqGFJ@b5F*S34*#eVzLT zcANv}X9?(%>T~)(eHOjBJPfzb6WMggi{Hn-w4aIpk@uroAak6W@&ZFryJ>6_$Ns5ixTAg1w4jxe2KFK;*? zqP*GoXlnc*u5+?!r^k*yaZM*!GeenyYN~!=9Th{cLBqY%-8(G3Wt7vy7-BAej(D38 z#}D5%?2B8)V6$^`+B&NtxJu^tO`G+8p~lBSM@8_Osbtnl*RbF_hqxix0vM*X=JK}c z>Lj4oz}g#sJtyI`f2*I92)*J<#!bNe_VZ4`vo51Q-t#XJSzZ1N$dZ1oelq!mi1+@Y zoHg_oIHSl-BCe4#4`Ap|EDg0z*X=A0Ti^TVIpewtR4`{Jf?~-R;;hz5-3AMnPTkHn zqN!#iPi;tbM^kChr*+qG+_DhU>e`ET%;0Ie)LM&xecf8fFN6BMYAV8D4P7@D`k#ND zjSQFW1|(ruGB9$Iegc)2kn=N~`ELo%cI(#YG$_}|!_eDW#DMC3>rOnvfS}ix%*4|e z@;z@CE2k5$KU#*Ojg;U;f*(N&H}8m4`4?9STAl=zF_n9R7>DYt5Y8wYzoBq5Az7@rQ9ske!7y0e$sUq@ zJ^a|cKC(yhp3|K>ci2Qf{_U9lLe>pP31r&T7a=kbI=N73u=%4(^THd?S4waPyOh(Au!r!dNpupyM*fY4 zw7=@!?Fi3ym2}`g`p1=zC73WL!2D}Gx<^8h*1#^eXpeZ;j?ya8mP0##LXkLxi_OSd zoshj#10Ry4Gk@J%W(66{jRehR_B9yi&P)6*FqHnY-!9)zIh@R`TqWh*yDZCtmG>Qo zW=S$TZ>BLxoP*u?$+x|y``bg^U+=gezyA%*>x#9s|9?b%bzD@@*7neXgp|@Cf*?qD zi2TAx1#y+@<4 zbyi3O<%=&OcQ4;)ubLEJ;yZ4%hm>Zi`f80rbn%DXuAizDCG?8Q_fq2Aymi5vOEDos ziz9rYf+kBa9B_;-ny`Df<*&0I=~JA^XR+}ihO((Zv-tRmMy;sD`w{RNlY0(m&y0;} z06WBbc6!XJh{xZ1U}hUbva+_8#AdJ;YVwGY>hHHtjy_ddmltjpuSo!oNyoMEDqP~& zn5w9AQslxEK6*F&-DkxHZX5oO-S4XG%!MlkhqkPH|36Fu`45Iqwb39wAq_H$%uPZsFcxCcq13|=z*rB#IC<_skZ zRaziTI_mA&C62rCp(B+$yUbTC30{K4&c0G(e}MUST?#XYDD1l|#E$ZrpWyp+OoKM0 z!V=4-B)n0gIyEbT%`GUE0WaFzgDaKKu&$&87hA^lK7(1UbziXaC2;1sC=edgBNh;s zfayaS9nfpL>Q|XNvKRSOz3iyY_3-jwO4!2I0NG#$R#t3GAzYk+8ki68H+iQeN)=!! zFt1z9QpjS!6XR>j1;SgMi@1>Tj$X}ULxGxed z1uZ#K#aL-1&?v;PC=#v1OHcF5tuk!9W%hZ(hSt!-SJAB=qg%0{)e!I&;ibj7>TIV> zr1h_%laS28d+G107|_Iw8|n8o6G9LnXAe)02&gqN$Eg^+`MI5`?&)9DG+QO&mri>> z=M(4KAIaeK?hti*`d)}Qvhf^kc!bR70J7bDizeLhWSh)$`xHWNV0tI?7$dKxrDYM0 z77X%zKy&bfbtAU}G#Oxx4{w2?i{h$1XeS1kM=I>v?MhszDbQ|K)LAz6i0kk6q`OXj(MXhE--q8vH$Vio z_WPX^F&BwdWaUICMY6EM(rUZvQ15sq6!?cZSAoHiCo)p7Z~UB-P?+q-l_rIN+%C18!Hs; z@6OTg(XB-D#DpnFcbgkya8>Ba}eI$;QhkB`}?AcX}#PTrfwuZLt z?%jY8xXN&&?yBa{+_`D>41uy%el;Tqs?RRE_2(v}N8mQlq!h##61ruF5>o329c}bC zE(Dv8O84Z>RxF?Anz}uS^NFJZH%B%WuhZM~SdJw;!@^Ajs)O-y3@3yXr+{cQ7f$9? zwLu0M`TKmshD#Bi^Ss04yBzU7kKmJSIs;_j#}+H&-4-r`M^q9=v)@wN5R&yYf#-hy z8I0Jrw>?%GE|;{jsVxH7SkeR|b9gq&Mcd||ZL`7DGw3MVIrGg=qiHneG;Z=JS%fPB zqy|8LSAm(|w?RnR0|>XU$|h!tR=#}sf@*+xS$EwYzCGVuZ$>*uNhf`P6o zMy(mJg;s>gmMd>_0_~T0L^Tt@YfwHo^us?T&Ii^cS8EZ;rC!9+TblEAKJ=Eoq9-fR zGgmNeURt9O4g7p5btU2jwlAKHbwm=_(sTUpz`;yuIYash3{Y=O4-vtPX#Q-PXtk)+uZ^7~l9S+s zsI?R{~#iIc!MAZ-9E9`r4cDv1F!DFmkq?sd*J0kMWy|W%XO`W@$BmMyjsWc z;IOd6FpqMm-Y8(QMVJrw4)=D;z>xcQA?&=NjnAMC^Xex3yxFCR{sF}LW*dl#_JG^E z4u_M?3&)wDh2Fns`6>XDM6PTtqfY9(8N8K+9etQM5Dkbm@k|I@Px)KPabu z4gIkZTyTX2Xv#zb5G+{t$N z*Vczo+WZ8NG5rqF%96HGhdOUP8{9prZL5Ty@Tht-ushY8gJh}-{1X5Sn0%%BJ||x9 z_j>d>Oc*)(ESZ1a9tm=YdmSsHF;kT_l(}(KKbYUHnSz`goqOHjCtBbB`UO(7;tDM7 z-&Ru_b+B*Tn+QI%xWbm*b7sdx+w&E!;A42Ptw*k8HjE={;9&97UuCJW^#aijZ_#wL z+wBV0%J;@A27^Nqa$Lw0g%D!&^T=E(%s4UD`yPjqIy_5}YSD(@4HN*;4%6eum-9tA zIXNi14us6G*2Kf+LAT0>&!49^Rygk8_1ZgaZ94`WHCN=hMD&t0q%3ONM+abq{tl_q ze)7skC!%?pgfDUZ^3&(HAi}75^Fw3W;Y1=MlR<=P-$2GcH+wZ=a?+Bjpt?jkMiEvxtKv;3H3K z!~pc<&;6iE^D035S*cNT`?^9VoN#=IbNq@RgTI+f@0-SwU-0LiCMU48nSZ5A=%b!5`iBMK&LFlmXSYYuS=||h7!T9 z+occgdH6x&qVh%CT}tgfz}Cx!j8p{sghKK&dszMcjeJi7>QSPwoLd5-aQqLubX2j` zd|X^riyPVOhlhuk0$O@xasY&0D|O;bz57MTWmn$>sCp!w@X5*Y4|`S_rB)x|2ffsq zC>qI}_VdO9v>@2n!n(>5C7D=I12PKKWIU>KQ!zWpdPLU&TQ69CdtsrmNiwbfM+ReQjG#ynWLzBmfZbTc(HjIOQa1$~Pc5HNEa)zs7^3C|nb zbnPS%I==7$fy&lJlc0C!$1wpP5iV`7BKHnjw-(VrCMu$jzO*AO$ z*v&sbmKr7!s_~@fQutE6)OY9}{n*>o?oB2!zcV36yh4~fpQ)#C;N&)8WuLm^ps?1+kG;X?l ztxeBrN4-VedvrIXszdZ-6!EChHbt;cSpkQ*vGR+1ggA^qzsM~sp3par@LExQ&wFYJ& z<##U}WO1~zqB>(W|?Wu=OXrb%@J&Xc1D zoLWORWEt2FQz8cK&Q!9p5H?~#g7mOH4L*V|KlM48OhS8+ zu@CTyynjD@W+x2_a;S{zk(p-4`$EzQc!%o zFy7<3yEARKujKfjMM6mA_?aRHm%e^2l->kjv=89)0^NL#*%|Kr$xjmFtZ4{wWHqkh4Izscc z?tOo={=*Uz#@{i?!?KDeBq6S5m{(U6_*!{P;91U%l(pldq z1wl8+48Ja}Oa5V)DE=!*KZJ&=Q)?C@sYvA#t)ohg!KSRGW%zw>gkdzuZHh)dSFFO0 z=l??iiQmo-25pHtIyy^$ZCaXoVAJk!wJ-5XIOF?gsvd@?9za*l^?cs~P&c1#`hZZU z3Xwo3K?Q_qH{l|HPi7vRI3_!tAnJ7dae*DtZBA=;J`GNLCObx#?WFOhjLZR_Yk~C- ze6W^=f-#4Cp8*a4EW{+$0Ej{m-t&skN1C@hzHiG%(ibUWh3B2We=8)+%9H)cjFq@? zumJ*{p^Xp_{{h73%wFdtHdS*mGZf~`rOArlU+q4C)pO}T;WLSs&uVg4v-q#hJtBdn ziHL4=e?nFh0y(PGUs%oagy7GlKFqw4$il+MK*T7grG@6TyKSKW&?y%zN7#0Eb-mp& z42xs9V*srG0@%=MGou163$RNT?VQ9T&%SO2NZfzLd|~44f+|B=zm=H}M+7E#AYx>r z>7w*Q z{=zw)t{G@2m?kTpElj(Az5P7UV8Qmiz0Pq(dA_MdQT^cYd>ao1Gr~E^C-&nvBkuKQ zC5wTPm-ZUr84%9OCqLbZ)VpszL;=Ya>-kr_>N0fd&~p3J>9sch@#6N8 zJS4FF@b5Gb(4xq1Dg0k|lbdag>kEk@31%kD^!u{L2_*}W>70HMJb6+Unq3oT} zFTfl8mDKs&`|EQjK<+ne-+A`S9-u4hLs|6S5MY!SBd?Qu7%9di|I<@FkSJVNu_j0% z1AU?SgL1`@2rPm269DYn$3EhalZ zk?0m?2u~$X?yCB!A9Nn*qjAyVMHN!ZMRp%_ta6%3>Yhd80?^RD)0!Fc74RuD4#Oy$Wf-83nlmXg?MR2F*EDCkgTNp{E)o?k3|IGMR-8`*h@5h>Rt!jy||zgaYI_Axw6*s zTM~@Ck$?R~>r3T09~T`Eci3iSiHB2YpVX`q{6hZv6q=s(poq~iq&aODAwGX zx<5OQNIYh}CztQWKto{syoe#P=%DMz@Y8QE#)-V=-Y9hBSUrD^4zS%TPm{cn-pxS^ zZimUIfI}*;K+aD|8@jr>+H3DBC##u<(XqDrM6=>~Gha1vG=^$171n^#3H_3gEw(s@6sq!fv=+Zm=rkXihNupFf*O{;nWRjffcj`0{1CBZ9fuzf4z<326v zsU^KHWT5$zJICbD@7H@^A5$E{8#cZh-?qzKQiZU=3U`?6ZnGtmuLE1B{b)16TUcz6 zvG`u-eYY$sf6i(z8Y5-obWOOAyJhNf=|dk+^A7vf)b!sb5?f@<6+Ll>;^1o{TIr=$ zYOgq@2hUc&0rD>pao_|P2LuGPcMbxpvbweg{4$ej-iSG{8a}r-XEJ^nfI1G#DGGy; z>RzSN({WAYm34?-T!s7Lh=uo`TO~x;Y0vH0CT=2a&YAD$-EA&1w7Ex%z+=m{Q^j6; zj^61DoSg8dKY`_xU<_S|RQgAIF`TW83TKEh#W7atUD5Tyoj@TXo^pVgTv)MUc5|=c z**Ue`)}APM6JxD`NBd~QY*C!mt?Ncb>U&tvXSggbA!DEPUz^_zmxY=Kv60Bx*S9**|*Y@nb2((@)QEiH&v{Sa8r z`xh4%KpggN-vyuo;kJBxO#f4sOg9b9*mRgfwM@QDRy!?ZyNWcN(Wpx18y$cWUz9s< zc3q}@47wpU1MoxY%1u?OiA#+y0cHMk;*M6YjSmnZ6VYZTo@u5%mW~^~R6{ND{UM{`$?Lhv@rRU&zANlBXFZFo zTF{PqpD!=#Cy z>CuG%6@hchL3egmY9ym+(gw@TW+m+=h=aGuzk#f%#~rJ4;nl}T71 z(L)h?t!|orrNiJPgC*@i@oM8Mc>r?%o~M{7#f7ilsZe*sCG}c=!^UD~YCG5= z#>>=H=u6AagE&-frKwReVGme-X@urnW!eWxW2@`x>PSycLaN|$fk_`egn%X&oSPQF zod*Vy%pY+^va<2}1SJ;43@X}{C0gFZn}Z5fji}UJmMZ#$yYBM9D5-!|+4Ka{(W1^2 z2_U{y#?HtfELuD{L(Ywds0xfeY6Q7mi?Gw`+NIu!?QkvIlluT0U;dFkQoDs7e8xu2 zT!gFanTj0@u)$&@Zqz?o+RaVC11)^0CgTUX*L|&Cy{p2Q01(BVZokor{eJ;=P_{X_ zbU$@e;|2R93u;H_o5Htw;a9&F|0=o=!j7G&f*1(mnl2C&TobB6_VxkKa!kShLYsKp zW5Y`am*_*SA%JxD^ptEIL9`tt>fF25<1i$o0c=de^);#t;)hj-sy(3)pHdz}U*CDh z{cmRc(SwT=U?80skZ(8KoX&p8n7{Sb3wuKd5q4k42mMx|Zk&nVfbK#%5EFXs?mAhc zq^8yl2))418L3>^#HA>iM~;A`yqi zQ?eIUxtYL|m^YXryhZ)%cYD|&EfhXLmQYXa5BVwsR)rZ91f7 z6H!Y*P8~Re1@^ihk66Y+TQ#O!U{`5dOU}q<2zrjk&Q2hq9(zcYgT%2?}>*fEeJ#s z=&zS>K8bmAj&Chh6g|F!E+4;rE2;tLT8q>&@Se*t!GShHD2ZPJm@QlBj=TFDB<0UC zPOq(>8^>ySzWogXsOx>(^UyBmFZPD62)hf_YmmHI;EF5Ha`+SznS6!@kNM)Y8WdN#3 z%r$W{=#7AaclZa;lmob6FFgsR?}0Rjk!nBQKZsZ4q3D;M>&6lck>;V5l-lrM>e}O< z?5_yWurlVs0~okvKec{T2SVGL+=g6rg4ZAoKml;lC3y>$)Z7;4X64|pX&n5lj;+3n z-T1ID9Wt8rEOf$(#G%itdH1{^vMuXIfbDP`-Ywf^?0rpQ`sD7w=m(HEP`GD*RUdjZ zVygt)nU&Ib4kn7ci&x@gTsr}BE>wWf2$j$K4u(-`05zXD`04+8$^M??+;jrqgo|~9 zhbm4O$OUT*${nt*uUjs6MFmO*0iyZdeySFSGN5sgP13Q(N!94bF`C=(=n9qhY>S8K z_ftJ5`34G;T^KmOtW{0U3V_G@`iT7em7?x&=(Jx9LHpB^AD%s2Ck|=bSzko=JHFQj z&3m8RKf5#8CTK~?3f2WcY>ccSV*mzzNDxsra5y-hb;8sf$l-pxO!(P?Wr?njwWu#u zs^A9S?dHQ+F)LX@1WP{wKpFQJ#5X;J53cH94Z983%-p#qX?k&UW$1L&fPv{PVXYX0);B*0{L&HP)HvF;z$; zcKSTTc@t?ZtwbY;{YLZ^6RJHE<=J#M5H-M+#f*_cATfMN*IseEgmNI9lg{wl8UTkY zdoCFC77XF5V?e||eTnx`-4kj8y=;SzqJljxp;>(K7)_!>k+Dg+r zlQl2j1Z-xnjkPPpyFYd>CQAd6u}-CS(kVaVJx`qURY{TH`g)YHXr5}R4l>#}nMqR{ z8p^IggEGDqyFKn_RzLOYPe3bkx^q34x9QP!Q4`m6?noc^0b$JE*h%1~v(74_<-4s4 zQ^8dmn(GA*V$ZkoN~CgQ=%I;|g;LdGLAz!yVFAh{CUSS0pFawoO|F!QkB!Bjw%B;a z6LjqZDqSM14$hNsxD~K{4S)lV;m-4WrFY|_qw%`By3BjuKU7L3@c>=xD4u=`{(k=+ zNj^SO(63JPMpu?Uc>D0$TG0?4X(FGxVp}(9+eQ*_Y(?c)oII70iqVNwNh11&96y$A ze7Lj%4iwS3=m*zb=TwF6lRrqjiQ_*2ZT&Hs_&(hz%E4DsPcNg2=;TLQdZ3H5<_b5f12;8b9i{;Qd?;gh5b5#P{S5N@vy5g+ovy zeFFm{u-;4Hux}PBwMx*b(3E7u`mu-9cl(s|N(sSOW8}t~r&}0UT}n7OD2JBe-}l*q zzlhiC-psat_gH@WoO~n{>#pnd`5S;k88Nkj@r0_DLp&hm5RY<0J1uPhv!am-qCqvo z{#{|XLI3W5PYH;lZc9RPf(|qICu|ixncADxV@G<4LMARqM~Bp z);3202ztS|7HW1Eg*7ug%Ja7QjdW`?M(LXZ4*oDO0{F5AbjSznf5$;3Q8l(Bxy1%O zwb|LvHc29F%|&sS7=ouEK;W*LO^g!o^069`gIzOKOJJ!hXlBKp*1LBR=rzG^el-n_aK`nWd#=`_@Ci94na34rb4LA_{rI zW-xC3ImjuI5F%)LvJC;}AvY6H??%5dO^;*x5Y+bi!oGO>FG!2Qv;;e+I?& zk|+K~M6%*lVC~aGEFtDzT2Eg;?p}5kGs^i~;g=T_;OWXP;DfRi+{D{txTx-Id}b!P)&IrC8+e= zu#_d2ff-HK(CYwezadp9`L~bB?3yv7s8fRW$%6Y@EEq?kUZCZg*HG$4Cnvf$3k+4P zfYZ;)MYQPP4Jwa@Mn;`p5-zfuU0^;9sHnsN#lv4zPD-+5a%~|BQ@;9oB!`xstU|M7 zPr*aK@Xb3moJ&a=cRvzR8YjWU4yp$|7eQ$j+~Sx^zC%tKKuPry-mX{7n3$_oQth^; zlwWvinDknMm5U2CUkm7Db6t&y%+1bNsH8jU@SJPe7JhAzHR}YRX{F8(@wxG1^VZ)qv<06(9grpaKsd z(5vJ#f0U#Up1+hPoS_8+Q~OVxRmuqy@D(>3FXHy~NkqWPjF_Jh20L&<*WGfK`&cZ> zD)lNE6FDR#Xo&!GefH5?qkk>DE_j<8L1n;AaEKul?GEQ1? z(%D!{E@dGByPhlU0Bpo9WNxjo;uMO+>E4+oFO9rp6ceLfM4r~?1--Qi2oR_9T_py5 z4O;&`@w9e>;ezmcmKij}4@HVUuUbOGU(=9GjO8)>7T z)6f9)@Y|kwo@Nt-nW>8fyyL{5xGfm*39wh!HoAYOiFm)UbURed9TUM0I}XAv&^HS* z@hcjBgKmqd!C5@PqNEg`<>VriY3C#;3dAPjUJ1>|23~ntEx%R#)b5G~4ktd~y~xZA zU#nk4gBX1CpxkXfqk9I~ZM>q(FD$b1K-yj-xPl1pJk#?M)GIgGbT`ASc464qSgj!(ds-xf+sDFf}G ztoW6x@jun&3(w}Rl`S0lf|$3W`0-DI-yOW^Yv*Dw`-JciU8)vic&^j*7>`)`UbbFY zMGX&bk&(qUO$QZ(SqU!phYwg0aGus1D=r&1hTkj&KPT6$F419|0|vc!Xs8Fw3`S2* z>J=#CO@W!7YGA*m$9$q9ixV<&T@dIT z167KLjvT(GC25QU=0sxp3k^PfQcv%@r%zbQ83zD=OcRx!BXW(w7hTNI@y9K@pn!lw zb)eYAHj?KXWl>!3K!)IHngg=&+qY8*NIRBBsYeBwAvmf%XxGE;MhKg%oIfsMO`0a- zR6LfLbkOTJ+l%e=&E9+Azt*&xm=qfu8&RbU_&SKe_MniKk(<63t@mBX@H_jhQfS%$ z^{A!-WMBR}ckH%AV8#H8IR1Kdo%!KviOVMy+Bbq_v`LT2m)4D3o;&Z}DL~}jbKvJ+ zzHYob)qV@zS_NVkqRtbErKlQG)dycQDS!rKunM4UURT_{hO#ohil%~Gme7Hv*ljDLG}A=0s3Iv9yaQ!NnvgM zn&A;2M%~8X59AF_&A}1Z@Z?MykJs-hAxJFF{vL!8dWQmQiwk1eB=zR=0P7Xd2IHq9 ze;W0J8B9C?Cf+U(Z=i(Rl9Q94#Rk6@laiu$U;k~XtbnWaR7!NZ7 z^#9t8piw+B)b@?Nc#u#N9KTxqZ3VNGes1OqLDpuVcd#Bmf;bVHEfxjPl#;D$K-k+HqB zA$vh*#4X3E`orT>Z+DEm#E-WesuGYthk_0RAuP$2370m&1mg$q`8hlIotf}gyaW*u z87>Ja=^Iqx0t{CamzMqj_#cY9olqy4P4n*Ayf)E2fd#xt`itfz8h@2m-Ya#|V{n2*iP$w3n|T7ohZ z6%<$iWef^l>pSxCG*Q2Q|GuXK;zWrSG;^}x+rJCk;_@*AV+xidfADl(?kE8*G}&>M z`6{bUD?ftKD_Avbl>keL>K-U1;6B@NYkZ&nDH1gE6*%PF;jvku*`BSZCnrytYPYEZ zb>qIivxPwT<(bs^{sRb79o)A%a{7q0Xe6?BFOZ0{?q!MJwIR$tWt17;@4rk|($e48 zoPb`5t&59-jSb81*L>xA?Eh-dZyD|k+wu?F!~Q=T6i8iHHrW1ng0YsfX40EyX=pq^ z?F>-x)R=87^%~`dC{Gz4W-TX%sz9XvE(nV1MO4|u{{XD`#ns&VKyx9)BH1*?iBBGg zA;OOe+wT}DBeg>kv17kec|`}`ff;tfL^pXW4>A#5!TN_=}* zTGfthSUOlnyElr`=)nq2^=m)3svY#`oH^Sf6V=K>C%|!0WRFbv{$N4Dqm3EZTf?X{ zY!X5fhC^=WIf2NSgm;D&KSuI;JZLjA9@jOJQpK%DdG1x+l6BlEaA2|n_S6i$!TytV zr`c`vQsw!cs8{l(w?or(N|+aP7>g)wo^f$srhd*!BI;3HfR zmW$^X6fjCWq77x}&~S;YC@=nM$8R{b?PbfK#_TXzIg1U%x;ztIz?CA`RWDz_T(Wql%*+K2{2NHnTI0aT z_}jxC`Uu+%DinWvSR7bNVhHqP4(K?v2s__dV51245xAuRce3`gOxLH(oWhhw2-8sd zukVprv+2-_$8AldC+{ifi8L{F?8!^ZN{1IloV7e9riD#7`Rl1j_2+7qIUqwAOFvvc ztkFWOnxu#`)O5rzn6_asypJdrJSCEEAH}hjr2L(s!Yo({fNo$re`@~$>P>kC;{15f zPZHs-)iQsbWm08D1_6+rL|1ns^v{H?fUTo#Y*|&=1OBu!e_u}zFH0aFd)u~qbK(HW zf7`CKWd}^rB4qb?PVek$v9rM{f)@)M2hFZ~-#%Vbb$CO*`Z%{gR*RUgJKQ%8x^Jn| zDnAP1)ORYeN*(SOFQ+8fiQru)TMY~7OJXKfW@HSgYx`bjB0NNN1tvUc7rYREx8KU= zHF^)scp5=e@U=Zxf8vMz5rYJ+Hx4ueirL?uK~tUp9}vkH4;EGx!UqZX(`dqe$48Kh zv}ZlOJ6A`L3!Iu^qhzWCD|dyJ=@u22vB`D_cOJqs<}w)UCw(YGHkfQB4<4J{F+unY z25eN+f5R}KTiCh-0h=fTX<2F<(9?Yv&60&_%=xYqY*g+GCP$7E@Qicl*>JI3AUjwc z*v%Rs{OWBzu^?FR8$m&}2%@6cj#E%e1AR2JoVOqzZIqib)FNIV&BX)hoRkh5`OIwG z>$;z#oE=x=g_WM-bs)T}BtjD>UBJWbV2AL6jdT)Bt}-Q1aIXtxMlCB7DOgs_UAvtk z{{=6b*Ca}nv|;eagq^Idg4;0g@5ht{@MTz3r|Z@X571`qo8>I}llcvq#GQIS1>^!3 zIa+1IGoz&rPIgh6${q+uJAo}daC(Sby{}i5&MZ)wHlJT?aRqYdR(2yt1|)Rg3quZm z-0=jGiSYd*e{X;4Jz08|)P0A?1UkIn~a8uCX>|+c$aRBk9 zaD7wFfA2q)E%4-ozy?%7=J3q}hh_yrgLS9-g!x(MacnkL0DJz6$>L5kR=|i&(MKha zN%1i{tnTQIt(v6XE0!xf5jaM}OJGddEgYgA{eC4c5O>Te8x}M6=HxIPDC%vx8KU`^ zXymbcnJ3K9YxV%AYgW<+I`0kH#tljdn+3K}JhBA9~_jpkter8%aV{PV1!KXYWs!}cB9T>`_HA=*;u4u0 z4R=}%O1^ydHxMVYJXe(EzXNXbQz}h_Z(of;I#q&*VvUABee0$jeXtPN80cM@s4yyU zjGr)S=<3pn0mM2o_m1=>E`G;aw_FtauED`1AX@K1J12rT5-q3$%PRzVdI&#Lo z=(g#F$x`mmcv9N?=>pRl&fju=SSo}KpaI5x!HBzhns591%y6S38FkYQ-%&_EtCfGJ zl7anFlt6w9)|)V4VVq8wM0!l|q&*}9t78!o0QPMYW{md^g|$(peKGnst)k*){k8=4=jHFl5uk4 z02uZCQv|7|by-El4swr^8NrZ?+`R1cXl=+}N`UO*xDo|_M#fW?z%BDlV`hJ7GJqDD z(oWZ^zKhIiSibS5B{nr!2rW!N=<3Lp0!hgpjruDwV4|d8!{TAMa|7JiFX;Mbs2|xg zpNNO@As|StN4rkg5F7*JAeZ$}?fgt|GiVSnJB*?_WE8lkxyr`Dv4i9S2a1~{mF(@- z_!=Y#e!@e!8!qpS{^>#KT_B7XT=$>@8)AkoywJ+?wn}vsquKtRB6wl{Zf|=SROm(M zm4&ck(|Mt%!Vl7K28~>ta9kZ+%V@}g02Vc7D|=y;@ck8xO-sei?g^7Z4Iash{b6sTkVWGVI~I%52`dbiKi$N8)$fK4?+O zgM$~9BY5*)5Q7|(`aUDjr1d1~0avbs&;t}%5s;oGt9Td52pm36)9n>3(es|BfiPry z`|__K-tI`&3S`wYFcXV`^cin31*GQB(?p;yX^CzA&L3A4BN!X<04@1@E8P_?gj|VS z0R=QE-or$Dog`T3g8pcg zpL}%!y|`;A~ zUA=4C%dKvM#>H=2o|BuXen|{{2{$8xpcoB-sbkbu}??LHLn?$jcn=E^! zZut`Lb74bJQ>uWfD;x}iqa^#Zi`1SN(-OZM1B<#3bhm&Jgmj2P7B#=20zAOp7X$n} zzF`J-YH6u14CaXrcz`P>j$8Ts)lK4J5>Y;own2f9aI0q>1Q%xzJwNBNo2uLASCuCH zWaBc+09oF!k!rS|RTY~5iq=65o>R z9E*xcBA-VRL`V7!+xkmA#gq>I=}1mU&*?=-EvSP9IXI}{rV{%@Vsa1jAuD|M?>5;27!s0hxq2miJk`^eYfv(L$sS=3dV=k$XbV!x&GF^J#f8eW6#>n6Mdl_b0&- zXddWOcuR{wj`OkeR5cpNOL~9(>IBpC?*YATt0|H0`;XELx%+P#jjIiw+S=KDFHla^ zFg1;zoSfYKqK?6ez8`Oi^&fc&ROr_VVq5?D%9xSm4kSh?qRQ+qAncti)@5UQ+?Ctf zNweKSDq3+GBH#dDlREk>6^PUEIXm440`OOSoc;F9S;th)oMjv-NGN~wHz~}D`@U6e zc&dJWLtlb!MwPidy>4!4Ar4D~l9dilP8s@OR0mYGT|X+x{;7G;!3nt~6nyk=&!X?p zn97dJ_!*YMH#fnYKm~{VM0cE7B*Yk!J!1R*69o=TiTvx6osKn6@r|~lNuycF9L-l! zg|<|*d@m^Dj3erZ3a5P`f4UiM(SFTLvi=*OqEbK|uOKt-MLHx1&0I409V8Q@8snJ+ zMdUpK9l{3sPO+sR6|T2eJjlEIZ~n=%knsR{>*9#=bQ)X!1$rtTKibnhiR{5&)UQ4Z zxm3~xE9L1u65&Ckeaz?Rg%ir(QRh!P^zdvh5FUO)A8@V>ff5dWel!LSRTaefU{3F( z%q-`qm-ro9lVQJ8{X;|_i@&q!6B9WvcSc`!x z#>)_6EXNpK%jQa~qj6vJO)8?>ZC)ce#`qZ)7CzqAP}!YOl;E|`K`v{>(^||s(+tWs zUw+Tb>v{%3RY+co_Nff142|b+T=`}O(Or2=l+@ciMjp784ieaOr{F?RUycd2q^z3y z`eoDZui5dv1N4_a4!lawx>?=$JPY+zs0nm83PRYFSZfgwc$N;d==tg8o;&XSeZS#L zfAf>erCodUtA)C7_}5jpE2XBKGlR3cT(;9iQncE+ZRs9{NB21rXNNT|N1)vwaQj{~5ID-ajx_<(4i*6a2gr3k5vlbb9$K_yB;(|YHkEL}Hpo_}}MEZVPTj&uL% zr>c4HtfB{|K8htCZV^2cR>_0;6HO%oZ_cWqNj`tSM}&Rmdu@v}z1#G9DDBV0X!&?Y1<#%I%GROKZ{^`AL6VZu{$uAwe>hoH`rF)?Y^im5DZhH6&aB9!-Y?A%)xxUidv z0)cAWyTU@;`Ohk%`{GrfUNF(Xk9aE)lgvp`&qXr^i?lbnQU3V8ul=NA^bbyL z8`PP`_utKSbg+trWEfP#18CiUk6h00?P?dkUJ4AM>CxmpnPD4dxbquG32SRw!(cIA z^?2nK6=yjf2z0Z4R$|LgV^30JPf=p~@S?w=a?9SnnTmzq+1b&NO^|*!Hc(<`13F z-4${gO>`eh;UKeAnuy^^3mgzo+G+FOWTF3ciCh+DL_^&~@S%nm`i-lJ{+{FtKZvb1 z+v(5gJ-ydbRG6`K{RqQ(3O7FbD!8rf9t%5E)LYzychAkFXZo+dRJG0AU)NUmJ#)$O zUEjRREWyqAhizjck#OpC(2(P1sz=A5O}tIERi$kST_D59a&zCit_|w`-_3b;0X7Fs zI6luR!T-zYj5n9Wu@n(vdLvU`!AN1Ka?2<)e|nKmLLrpr^=#d9=90WTnfB(h`lFk^ zB+Nm&#n5PC zVw%}1mZa~k)@HJykbB8ZewEiWo`e;+D4p2vx%y=4DbH7tcZNY3;RstL8!jIw@ia^VapmO{*~5LOAVnQtm{zlRqyr#d8i`0mv# z&%pdH!swF;IzMT(b~jY6&$)(?j1{E@9bW*7y{Jc^e+7I|i1({yqq7B4HzRhheKQhQ zeKP?Euz=GgKds++j^Xw7-KLG*#jUjh&U{+lu zd09iW$s%>ph&)IDl3pw+YDGli$0rf=k2Zz;HZP`XVXJPpPd4)xtja1Io;yJ=wN;`n z?S(IGRd0UVC`Ih`ULqf?^kV#bjPUqc_Xi_4t4W4lzc>yrCDmOcav3ysVkr-$ZtUzR zUTv%^&`(5X-ISL*gue^Dev~lYrOf#uT+{0ta`Dz~!Ubw{Dp2$lW;c4TioD#+>=efI zgq7^;?1LWX+m!zNleV*r9MBPBe{q;{*z)%E?@yjvDf%V{UkIwCm_DM;6b@5h>6rl{ z@XYSS6aF)?NXM;yo_`~p+WN9G-{k3L;Q#DL=d8SrSjcKGI<3`74)0#9Uu!4xs(OzG-%9#^cWiyCYD{uGvji;b1A4cYwWYce#2^W^_{jWaq_z%xvXg=%gc|`P|&yxfm@E%($;_?M|x9wi**-RQtQ)|rD z1{k&=hRvPU9z9dP645Ll%ddIc8e>r`_J>#xk_m60BCgC5{`(ol_~6Ln4Sa&lP8Lz* zGo{pHXuku20sV*ZBE>n^tDXLNw_mp1QzhfO%?_t9_(wvik0&>0|BDQC2w$;iyaU-0 zcpt2tteN6w!sLzI8Znqm7X8bwE;eA-xiRwW&)d^g%p{ z)W48cfzgA-c{0j_68f4pHZ(84@u7|_5H=tEqy9SZ_CWaX-xCM-1LIKoenmLZLEdxh z2>3WXMTJv3qtnkr#efoSVS*q|QH^T|wU z(x$)@e+5d9zHIGri@9&NLhIcHoO#;K8T_|PP@TtlJNVt8|Cz+z$TdlQ^D@_IUaT@b zCx5fKYU#(C3t0s#jr}fD+2B~euweDgoO`0Zx>M8h;^$97uz-6h9&&p%gV1XkcCO~9 z!1p#15TrHRen|Jo3QTM2au@yQREkBDGa}#3e^qp7yVR{{JyP>LVH0ajqw*DiCB|W= zcPbW5oQ`W>_VDyLQY)*bG2Nc82<$kRCGq(lMq2kc7`ln5k3R5Ez@iqi?`z!XzmLaL zu=SH-plw&QVEy-#o7I(C>mka(WO64%v_ zjlDA-2VVRl?~C052FEPb!*H)=0&ZG1d9{}SoP%csZQ*9BsiA0q`sA4ur2^f*N8mnW z72|0V6@#tEi>*5Q2IUgSFh5q&u;1c;g5RInEgDZ#1<$HlDX#W|9Xh~4L_#7U&_C~) z`Z(w?iPuO;S4T~m)B9t7jDG@5;xJOrsU272DazuW6@IZMiCX9^W6B`aX8O;i@W7=? zLv_o>TipugthH+Ox+!*iI{{&DW#-cc1>Sf=}X_EHk+#v6##IwHEGd=r$GEdHzj4U7C6GUriAni4icN7v| zI|f>HYH(#i+oBtd1ET!?Uwhvb*5uZ$%PPte6$_v!V510vR6{Q!QbI3*P^2gz5Snx$ zGz*{@z=XEwy^|m%^rE1YAVeXA4kArLmELovM*qDp_PN^6xjVV=3Fga}ImaC39q$-( zehHg3#Dc^8EcLJw;Hsm3yK0pvCLcPKSnGPdA6xU9w9osaPe@3wZ;`l=`wkt`IJD=3 zc&%^GG9)A80!^ z?v4I?b|W@fM9IUYI-}xCpivLfI{fdSQ&PELdp-CMu)rSN=EF#VCZ~kB!2QV{@@#Hv zJ6Cifa;BO;pc@x}BY%fL0_LUB49SD$kL z)AX9(&WHy~Wo0Yt97L>fm8I{J|D(ATZlH+{zpY(%Al)8CP}t3vrhY$FP^7UciwEJ( zy|@-#_2?O0cJ>YFyClM5VLTQm($c zzC(w~fzFi<$;jqS?*{+r*s}Y)&@yu2LlC02IxUS32JNDz;I~lR2*BOO| z8}$0@dd;sdgvl2Nuw8`hb^9>N2o{-vv-d zM6O86Sr_KldIHjLxKY-6;?Y9|l-?VMXmR<~7fz?!iwgASqjd@ZhEeD6f;HvUc1)$? zm_jm`LoqFl@VZ*y@vmbTm*pz=4bt?tUJ11VuMM?J!_M^T2yry>&F_sz{0@$6yfSV7 zmquA_*D(daIgR_)>$)xrd-!!DRLu% zbDY0%iuFIXv#e#Kixe|&{gU<(y^a}`mKw+JT0k8n`0K7PrNc|BVYgRnhbQbMl^s;8q~iazT!~xzb{KaG81r$n0!mNsYv~z+`Ph7+ih#w(QHF z%J9}$dGHv@8o)O$D17}AKh88KUSa?KAcMS^cRp$!(H$XiL&R2nPW8N9K#&r90_{r! zz|d5js-(^cOS_>#7j)SKOek3gxtngXMAl&#c&hKqnrj zqGqAwR*oZ+cY!seWHb3iHTdmJBdDF057bHUNuUdq>k1~){z&px74q@*pT8JxsXfk(P%-Uzt$w&6xkb3#N z81Ft$$xci>v&&P5i@WD?3OYU~7iU=u>y-cgQbg}ul8yck5G8T{&YUXTCP3lEudHPN z0^$ZtsyDC5nup{Yn#v`9HSTRpRFn z1m9UcAL@ZVGc=O(I$9Sdz~~@tjY9RV-8x#H^DPD+zUr$bH!qM-TdSTsJ`J3|godBv zaw&`D_S4$bKOGffo2G)oo7jqz0qq+NqhQmg_=RlDmh8XXn zS#C`xa77a|6MEK)q5X#Lt2SEl2cD2*Bu?|1-uDIX7yP=DWK3rRya(UVR-5Xp!Y{v7 z19V}UM6w^tRCY%=YADnS|MGlR{XmT#OGZ+N@@8Jk$h1s|V`Xd#zx<{Mc4u)pCVv>W zKBWC=6g$)3pzYz>;AJ*Ar}?PxsR+~`SH$YQ>nR% zk-6S5Z)fYN7!&s;0@#keY6~qH7q`O31*}FBn%4E568zSPQmS2fDMr%%)*#n-@mq3U z-#>P{0fgzMI*_4QlFY8{U^c_>>3$lMSijea(!W{(d${J`-QCiL`d2F0fW~nXmPpk&&hzM1ji)UsdEHw zw+FnuUv@t~=km@AhLZZTKFrV_l8*nK2v?-p+zJFB89mVd^5bYC}Yv?UQyF5Ki#;ICD?*&^aNa zvI2)rt7U!PN5uVGIR0`xueeHwr>S{KV_{C(Zp`NX!Sb|ILn~|fE~N;+^NxNp@+!1Y zVO33Q@9TMuu;uRE!@S=hvXI_(*7!c^;(4$q5V?d#ab8dPyc>3@XJ~J!=Y6EqGtoLmGu4wShZ-pE1|L%i40gR$vmy8L@uKT( zg{{_6eKAo%^`6`3f#Plb_9K+LgHlIp407{#%F_%zN=nIWt|X7N?dNAVHvUR=ZP9%> z^D=QQ>vBhmxbxK0rSQCatp0fzILTHhweq%Sf@m8+&HEkG z`y$GiWNeS_`D<8bzjMlbUP#_Ap%y}ygM#K(uwVvJ|CvFXm`rKJB1M0jz^{M0T{<$H zO(M?n@kP$zzMQYUI-Oi+F?e@>%kK9tIoU2+>-)X?vuk&=N+XQ=@5aI6kj~H11gmE( zJZBC?988?T@3vJH?#0HIeaJ*j^4>T%-A9Ungv4tXO@4@9Css8D3>%l#m_%z!Dn`q1 zNl6VWl)JU3E<}8*p<&qe~hNH5zL~86Bpv4P4K}Wt#wM>u-^M4~b2gG-2lRnUCV0 z!()0`xeq#8lKMp$hKHEFy*fa`jmMjMv~eVEztm|@ko{OA^02S6!oC&WWGcO12ZDj* z?_jVGthg=nBpYd``S`Gl`6)nDQVWY5zb1p0-H;`h0c-(2G9@Yd3RhK`Urq#?) z)-fq`p~Z(0uA~ZLP^0H|KTyr8|Hu*O@Y~m1x!Qfnm-lbCdLd6W3CtD$XA+U%)AS3( z-jO61{pKx@AM2C1h9}!;IrZ(qV^*JXE&U28j0(VL`Tv`HpgRlFZAFCilB8D6N^{Qy z73A*m)-Pye?VFRf*IQX8VM{uJbv4Dtrp(xZXJ0)GWtFKRoklmjepjtjnPz$DPp6pcCD3LT1@`CL5gT-+9xW z?V`HEuJ^AboK79^P3!O9tcsLV56jGO^R}WxE1y~jmyyMQ&@Pr0N0D}VUa6DF>-ACB z`j(o4ltrTq#Q^6F84hZDRR)0PFKN-m{NR;dhJVm&df1gTUc4)}soPd&yNW>M@WO@_ zs0A6=Isb^P)YwQ-6}^n~^{Ebodt+P7n+|I@+hLU?6-&hVgL z$o$#*SKH@gHWyv@R#cCUJV;Ir-R{yTA>Q-0v$*C7cnDfzQ7lcbr8y+0g^K=f)@G$^ z|EX2X5orOQDs+F^=rvDtfRALAjKVE(9RK7=_b8lw|KoFoklf^xGkf-r*ODL`j7UDAQKh8;=%-`C#Z-?*Z2e54Jik1B7b|$w4#Uup%{@5K;g25^)IX+}l`iD7 zN2}Q{bxCw4NtUi>m4G8Kx0DBeXUYXt?S9@9hVOQ^F{>Aj{j|2 zC+PWQ+GP6sVq60>3QWo?b}NTT8wCEckf$~%RQ9_i_sR94FT5hYWjqa&9Etv*PF2~Y zd{IQBD=BZV-@$8V}Ye|HKD$BW*ydNp>@7clrBrd$y z9=yUj@|{d@@m;#!iu9Ygbt>xO~x&MlRKbk2Hf~b$K~DEiQ2NIp4)Z6|4aRf zs&t%B%9!eIIbuH*ZQRFgqRIsktb2}9%y6k^03IP`hTHCbfGPG1jqa1qWslGqV z^3z4=Y#TEzd|AB*WnjbZq+11^-;RTyXHq>!!$au1fV>eJ{|Kn%uZ%PfO!#bl^?i%(5QKW%I7yYhq}7X#)rlNHz*72RtdCRy5y0y6ఱL}48GQ0MRP4;MZ*Dsa{cq5*K7zta#8=TblgJt+Z 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 Pp~O&S?Yp=;R!{#AvnI^V literal 88638 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