From bbccd4a815fca56318ae477519b20889a3e911ed Mon Sep 17 00:00:00 2001 From: Asmin Studios Date: Sat, 24 Oct 2026 15:18:03 +0200 Subject: [PATCH] first commit --- .gitea/workflows/build-plugin.yml | 177 ++ AsminVoiceChat-1.0.0.jar | Bin 0 -> 932497 bytes README.md | 43 + plugin.yml | 28 + .../maxhenkel/voicechat/BuildConstants.java | 24 + .../de/maxhenkel/voicechat/BukkitVersion.java | 77 + .../voicechat/FallbackTranslations.java | 246 +++ .../de/maxhenkel/voicechat/PaperVersion.java | 59 + .../de/maxhenkel/voicechat/Version.java | 76 + .../de/maxhenkel/voicechat/Voicechat.java | 167 ++ .../voicechat/api/BukkitVoicechatService.java | 11 + .../voicechat/api/ClientVoicechatSocket.java | 20 + .../de/maxhenkel/voicechat/api/Entity.java | 16 + .../voicechat/api/ForgeVoicechatPlugin.java | 12 + .../de/maxhenkel/voicechat/api/Group.java | 47 + .../de/maxhenkel/voicechat/api/Player.java | 12 + .../de/maxhenkel/voicechat/api/Position.java | 13 + .../maxhenkel/voicechat/api/RawUdpPacket.java | 15 + .../maxhenkel/voicechat/api/ServerLevel.java | 9 + .../maxhenkel/voicechat/api/ServerPlayer.java | 13 + .../maxhenkel/voicechat/api/VoicechatApi.java | 52 + .../voicechat/api/VoicechatClientApi.java | 78 + .../voicechat/api/VoicechatConnection.java | 33 + .../voicechat/api/VoicechatPlugin.java | 18 + .../voicechat/api/VoicechatServerApi.java | 125 ++ .../voicechat/api/VoicechatSocket.java | 22 + .../voicechat/api/VolumeCategory.java | 44 + .../voicechat/api/audio/AudioConverter.java | 19 + .../api/audiochannel/AudioChannel.java | 33 + .../api/audiochannel/AudioPlayer.java | 19 + .../api/audiochannel/ClientAudioChannel.java | 22 + .../ClientEntityAudioChannel.java | 21 + .../ClientLocationalAudioChannel.java | 19 + .../ClientStaticAudioChannel.java | 11 + .../api/audiochannel/EntityAudioChannel.java | 23 + .../audiochannel/LocationalAudioChannel.java | 19 + .../api/audiochannel/StaticAudioChannel.java | 21 + .../api/audiolistener/AudioListener.java | 11 + .../audiolistener/PlayerAudioListener.java | 26 + .../api/audiosender/AudioSender.java | 19 + .../voicechat/api/config/ConfigAccessor.java | 49 + .../api/config/VolumeConfigAccessor.java | 13 + .../voicechat/api/events/ClientEvent.java | 13 + .../api/events/ClientReceiveSoundEvent.java | 42 + .../api/events/ClientSoundEvent.java | 16 + .../ClientVoicechatConnectionEvent.java | 12 + .../ClientVoicechatInitializationEvent.java | 20 + .../api/events/CreateGroupEvent.java | 23 + .../api/events/CreateOpenALContextEvent.java | 14 + .../api/events/DestroyOpenALContextEvent.java | 14 + .../api/events/EntitySoundPacketEvent.java | 12 + .../maxhenkel/voicechat/api/events/Event.java | 13 + .../api/events/EventRegistration.java | 16 + .../voicechat/api/events/GroupEvent.java | 22 + .../voicechat/api/events/JoinGroupEvent.java | 18 + .../voicechat/api/events/LeaveGroupEvent.java | 23 + .../events/LocationalSoundPacketEvent.java | 12 + .../api/events/MergeClientSoundEvent.java | 12 + .../api/events/MicrophoneMuteEvent.java | 12 + .../api/events/MicrophonePacketEvent.java | 12 + .../api/events/NameTagIconRenderEvent.java | 13 + .../api/events/OpenALSoundEvent.java | 35 + .../voicechat/api/events/PacketEvent.java | 24 + .../api/events/PlayerConnectedEvent.java | 13 + .../api/events/PlayerDisconnectedEvent.java | 13 + .../api/events/PlayerStateChangedEvent.java | 25 + .../events/RegisterVolumeCategoryEvent.java | 11 + .../api/events/RemoveGroupEvent.java | 24 + .../voicechat/api/events/ServerEvent.java | 13 + .../api/events/SoundPacketEvent.java | 18 + .../api/events/StaticSoundPacketEvent.java | 12 + .../events/UnregisterVolumeCategoryEvent.java | 11 + .../api/events/VoiceDistanceEvent.java | 20 + .../voicechat/api/events/VoiceHostEvent.java | 17 + .../api/events/VoicechatDisableEvent.java | 12 + .../events/VoicechatServerStartedEvent.java | 11 + .../events/VoicechatServerStartingEvent.java | 20 + .../events/VoicechatServerStoppedEvent.java | 11 + .../api/events/VolumeCategoryEvent.java | 13 + .../voicechat/api/mp3/Mp3Decoder.java | 16 + .../voicechat/api/mp3/Mp3Encoder.java | 13 + .../voicechat/api/opus/OpusDecoder.java | 22 + .../voicechat/api/opus/OpusEncoder.java | 15 + .../voicechat/api/opus/OpusEncoderMode.java | 12 + .../api/packets/ConvertablePacket.java | 28 + .../api/packets/EntitySoundPacket.java | 26 + .../api/packets/LocationalSoundPacket.java | 22 + .../api/packets/MicrophonePacket.java | 18 + .../voicechat/api/packets/Packet.java | 8 + .../voicechat/api/packets/SoundPacket.java | 42 + .../api/packets/StaticSoundPacket.java | 15 + .../voicechat/command/VoiceChatCommands.java | 308 +++ .../commodore/AbstractCommodore.java | 120 ++ .../BrigadierUnsupportedException.java | 12 + .../voicechat/commodore/Commodore.java | 48 + .../commodore/CommodoreProvider.java | 68 + .../voicechat/commodore/PaperCommodore.java | 114 ++ .../commodore/ReflectionCommodore.java | 186 ++ .../voicechat/commodore/ReflectionUtil.java | 68 + .../commodore/file/AbstractIterator.java | 81 + .../commodore/file/ArgumentTypeParser.java | 18 + .../file/BrigadierArgumentTypeParser.java | 225 +++ .../commodore/file/CommodoreFileReader.java | 88 + .../voicechat/commodore/file/Lexer.java | 78 + .../commodore/file/ParseException.java | 20 + .../voicechat/commodore/file/Parser.java | 85 + .../voicechat/commodore/file/Token.java | 30 + .../voicechat/commodore/file/TokenStream.java | 22 + .../compatibility/BaseCompatibility.java | 211 ++ .../BukkitCompatibilityManager.java | 173 ++ .../compatibility/Compatibility.java | 82 + .../compatibility/Compatibility1_12.java | 83 + .../compatibility/Compatibility1_16.java | 83 + .../compatibility/Compatibility1_17.java | 78 + .../compatibility/Compatibility1_18.java | 79 + .../compatibility/Compatibility1_19.java | 84 + .../compatibility/Compatibility1_19_1.java | 74 + .../compatibility/Compatibility1_20.java | 72 + .../compatibility/Compatibility1_20_2.java | 67 + .../compatibility/Compatibility1_20_3.java | 91 + .../compatibility/Compatibility1_21_5.java | 73 + .../compatibility/Compatibility1_8.java | 133 ++ .../CompatibilityReflectionException.java | 16 + .../compatibility/FallbackCompatibility.java | 78 + .../JsonMessageBaseCompatibility.java | 30 + .../compatibility/PlayerHideEvent.java | 28 + .../compatibility/PlayerShowEvent.java | 28 + .../compatibility/ReflectionUtils.java | 215 ++ .../compatibility/SpigotCompatibility.java | 74 + .../voicechat/concentus/Analysis.java | 397 ++++ .../voicechat/concentus/AnalysisInfo.java | 39 + .../voicechat/concentus/ApplySineWindow.java | 46 + .../maxhenkel/voicechat/concentus/Arrays.java | 86 + .../voicechat/concentus/Autocorrelation.java | 186 ++ .../voicechat/concentus/BWExpander.java | 30 + .../maxhenkel/voicechat/concentus/Bands.java | 986 +++++++++ .../voicechat/concentus/BoxedValueByte.java | 13 + .../voicechat/concentus/BoxedValueInt.java | 13 + .../voicechat/concentus/BoxedValueShort.java | 13 + .../voicechat/concentus/BurgModified.java | 209 ++ .../de/maxhenkel/voicechat/concentus/CNG.java | 116 ++ .../voicechat/concentus/CNGState.java | 28 + .../maxhenkel/voicechat/concentus/CWRS.java | 116 ++ .../voicechat/concentus/CeltCommon.java | 843 ++++++++ .../voicechat/concentus/CeltConstants.java | 36 + .../voicechat/concentus/CeltDecoder.java | 534 +++++ .../voicechat/concentus/CeltEncoder.java | 888 +++++++++ .../voicechat/concentus/CeltLPC.java | 99 + .../voicechat/concentus/CeltMode.java | 58 + .../voicechat/concentus/CeltPitchXCorr.java | 110 + .../voicechat/concentus/CeltTables.java | 80 + .../voicechat/concentus/ChannelLayout.java | 24 + .../voicechat/concentus/CodeSigns.java | 65 + .../voicechat/concentus/CodecHelpers.java | 370 ++++ .../voicechat/concentus/CorrelateMatrix.java | 94 + .../voicechat/concentus/DecControlState.java | 26 + .../voicechat/concentus/DecodeAPI.java | 245 +++ .../voicechat/concentus/DecodeCore.java | 145 ++ .../voicechat/concentus/DecodeIndices.java | 68 + .../voicechat/concentus/DecodeParameters.java | 66 + .../voicechat/concentus/DecodePitch.java | 37 + .../voicechat/concentus/DecodePulses.java | 62 + .../voicechat/concentus/DecoderAPIFlag.java | 14 + .../voicechat/concentus/Downmix.java | 36 + .../voicechat/concentus/EncControlState.java | 105 + .../voicechat/concentus/EncodeAPI.java | 352 ++++ .../voicechat/concentus/EncodeIndices.java | 104 + .../voicechat/concentus/EncodePulses.java | 132 ++ .../voicechat/concentus/EntropyCoder.java | 451 +++++ .../voicechat/concentus/FFTState.java | 18 + .../voicechat/concentus/Filters.java | 274 +++ .../voicechat/concentus/FindLPC.java | 80 + .../voicechat/concentus/FindLTP.java | 153 ++ .../voicechat/concentus/FindPitchLags.java | 72 + .../voicechat/concentus/FindPredCoefs.java | 83 + .../voicechat/concentus/GainQuantization.java | 61 + .../voicechat/concentus/HPVariableCutoff.java | 29 + .../voicechat/concentus/Inlines.java | 1263 ++++++++++++ .../de/maxhenkel/voicechat/concentus/K2A.java | 40 + .../voicechat/concentus/Kernels.java | 267 +++ .../voicechat/concentus/KissFFT.java | 296 +++ .../concentus/LPCInversePredGain.java | 74 + .../concentus/LTPAnalysisFilter.java | 41 + .../voicechat/concentus/LTPScaleControl.java | 25 + .../voicechat/concentus/Laplace.java | 85 + .../voicechat/concentus/LinearAlgebra.java | 106 + .../maxhenkel/voicechat/concentus/MDCT.java | 150 ++ .../voicechat/concentus/MDCTLookup.java | 17 + .../voicechat/concentus/MLPState.java | 14 + .../concentus/MultiLayerPerceptron.java | 61 + .../maxhenkel/voicechat/concentus/NLSF.java | 625 ++++++ .../voicechat/concentus/NLSFCodebook.java | 36 + .../concentus/NoiseShapeAnalysis.java | 251 +++ .../voicechat/concentus/OpusApplication.java | 13 + .../voicechat/concentus/OpusBandwidth.java | 16 + .../concentus/OpusBandwidthHelpers.java | 72 + .../voicechat/concentus/OpusConstants.java | 17 + .../voicechat/concentus/OpusDecoder.java | 499 +++++ .../voicechat/concentus/OpusEncoder.java | 1062 ++++++++++ .../voicechat/concentus/OpusError.java | 16 + .../voicechat/concentus/OpusException.java | 31 + .../voicechat/concentus/OpusFramesize.java | 18 + .../concentus/OpusFramesizeHelpers.java | 42 + .../voicechat/concentus/OpusMSDecoder.java | 235 +++ .../voicechat/concentus/OpusMSEncoder.java | 735 +++++++ .../voicechat/concentus/OpusMode.java | 14 + .../voicechat/concentus/OpusMultistream.java | 60 + .../voicechat/concentus/OpusPacketInfo.java | 282 +++ .../voicechat/concentus/OpusRepacketizer.java | 261 +++ .../voicechat/concentus/OpusSignal.java | 13 + .../voicechat/concentus/OpusTables.java | 40 + .../de/maxhenkel/voicechat/concentus/PLC.java | 240 +++ .../voicechat/concentus/PLCStruct.java | 42 + .../maxhenkel/voicechat/concentus/Pitch.java | 267 +++ .../concentus/PitchAnalysisCore.java | 458 +++++ .../voicechat/concentus/ProcessGains.java | 61 + .../voicechat/concentus/PulseCache.java | 22 + .../voicechat/concentus/QuantizeBands.java | 303 +++ .../voicechat/concentus/QuantizeLTPGains.java | 64 + .../maxhenkel/voicechat/concentus/Rate.java | 308 +++ .../concentus/RegularizeCorrelations.java | 20 + .../voicechat/concentus/Resampler.java | 375 ++++ .../voicechat/concentus/ResidualEnergy.java | 87 + .../maxhenkel/voicechat/concentus/Schur.java | 96 + .../voicechat/concentus/ShellCoder.java | 91 + .../voicechat/concentus/SideInfoIndices.java | 52 + .../voicechat/concentus/Sigmoid.java | 31 + .../concentus/SilkChannelDecoder.java | 197 ++ .../concentus/SilkChannelEncoder.java | 671 +++++++ .../voicechat/concentus/SilkConstants.java | 157 ++ .../voicechat/concentus/SilkDecoder.java | 32 + .../concentus/SilkDecoderControl.java | 27 + .../voicechat/concentus/SilkEncoder.java | 52 + .../concentus/SilkEncoderControl.java | 61 + .../voicechat/concentus/SilkError.java | 27 + .../voicechat/concentus/SilkLPState.java | 42 + .../voicechat/concentus/SilkNSQState.java | 715 +++++++ .../concentus/SilkPrefilterState.java | 32 + .../concentus/SilkResamplerState.java | 58 + .../voicechat/concentus/SilkShapeState.java | 22 + .../voicechat/concentus/SilkTables.java | 211 ++ .../voicechat/concentus/SilkVADState.java | 36 + .../maxhenkel/voicechat/concentus/Sort.java | 87 + .../maxhenkel/voicechat/concentus/Spread.java | 15 + .../maxhenkel/voicechat/concentus/Stereo.java | 283 +++ .../concentus/StereoDecodeState.java | 22 + .../concentus/StereoEncodeState.java | 38 + .../voicechat/concentus/StereoWidthState.java | 24 + .../voicechat/concentus/SumSqrShift.java | 81 + .../voicechat/concentus/TOCStruct.java | 22 + .../concentus/TonalityAnalysisState.java | 92 + .../voicechat/concentus/TuningParameters.java | 56 + .../de/maxhenkel/voicechat/concentus/VQ.java | 230 +++ .../voicechat/concentus/VQ_WMat_EC.java | 60 + .../concentus/VoiceActivityDetection.java | 152 ++ .../voicechat/concentus/VorbisLayout.java | 18 + .../voicechat/config/ServerConfig.java | 59 + .../voicechat/config/Translations.java | 31 + .../configbuilder/CommentedProperties.java | 454 +++++ .../CommentedPropertyConfig.java | 144 ++ .../voicechat/configbuilder/Config.java | 11 + .../configbuilder/ConfigBuilder.java | 167 ++ .../configbuilder/ConfigBuilderImpl.java | 211 ++ .../configbuilder/MigratableConfig.java | 21 + .../configbuilder/MigratableConfigImpl.java | 58 + .../custom/AbstractValueList.java | 162 ++ .../custom/AbstractValueMap.java | 120 ++ .../configbuilder/custom/IntegerList.java | 27 + .../configbuilder/custom/StringList.java | 27 + .../configbuilder/custom/StringMap.java | 36 + .../IntegerListValueSerializer.java | 42 + .../serializer/StringListValueSerializer.java | 32 + .../serializer/StringMapValueSerializer.java | 55 + .../custom/serializer/UUIDSerializer.java | 23 + .../entry/AbstractConfigEntry.java | 134 ++ .../entry/AbstractRangedConfigEntry.java | 49 + .../entry/BooleanConfigEntry.java | 17 + .../configbuilder/entry/ConfigEntry.java | 29 + .../entry/DoubleConfigEntry.java | 40 + .../configbuilder/entry/EnumConfigEntry.java | 17 + .../configbuilder/entry/FloatConfigEntry.java | 40 + .../entry/GenericConfigEntry.java | 17 + .../entry/IntegerConfigEntry.java | 40 + .../configbuilder/entry/LongConfigEntry.java | 40 + .../entry/RangedConfigEntry.java | 20 + .../entry/StringConfigEntry.java | 17 + .../entry/serializer/BooleanSerializer.java | 28 + .../entry/serializer/DoubleSerializer.java | 33 + .../entry/serializer/EnumSerializer.java | 37 + .../entry/serializer/FloatSerializer.java | 33 + .../entry/serializer/IntegerSerializer.java | 33 + .../entry/serializer/LongSerializer.java | 33 + .../entry/serializer/StringSerializer.java | 28 + .../entry/serializer/ValueSerializable.java | 17 + .../entry/serializer/ValueSerializer.java | 18 + .../voicechat/debug/CooldownTimer.java | 22 + .../voicechat/debug/PingHandler.java | 46 + .../VoicechatUncaughtExceptionHandler.java | 15 + .../commodore/CommodoreCommands.java | 44 + .../GroupNameSuggestionProvider.java | 39 + .../MissingArgumentTypeException.java | 12 + .../placeholderapi/VoicechatExpansion.java | 64 + .../viaversion/ViaVersionCompatibility.java | 51 + .../voicechat/logging/JavaLoggingLogger.java | 94 + .../maxhenkel/voicechat/logging/LogLevel.java | 15 + .../voicechat/logging/VoicechatLogger.java | 37 + .../voicechat/net/AddCategoryPacket.java | 44 + .../voicechat/net/AddGroupPacket.java | 44 + .../voicechat/net/CreateGroupPacket.java | 80 + .../voicechat/net/JoinGroupPacket.java | 70 + .../voicechat/net/JoinedGroupPacket.java | 63 + .../voicechat/net/LeaveGroupPacket.java | 38 + .../maxhenkel/voicechat/net/NetManager.java | 162 ++ .../de/maxhenkel/voicechat/net/Packet.java | 23 + .../voicechat/net/PacketRateLimiter.java | 66 + .../voicechat/net/PlayerStatePacket.java | 44 + .../voicechat/net/PlayerStatesPacket.java | 54 + .../voicechat/net/RemoveCategoryPacket.java | 43 + .../voicechat/net/RemoveGroupPacket.java | 44 + .../net/RemovePlayerStatePacket.java | 44 + .../voicechat/net/RequestSecretPacket.java | 52 + .../maxhenkel/voicechat/net/SecretPacket.java | 122 ++ .../voicechat/net/UpdateStatePacket.java | 52 + .../de/maxhenkel/voicechat/org/json/CDL.java | 196 ++ .../maxhenkel/voicechat/org/json/Cookie.java | 121 ++ .../voicechat/org/json/CookieList.java | 45 + .../de/maxhenkel/voicechat/org/json/HTTP.java | 69 + .../voicechat/org/json/HTTPTokener.java | 39 + .../voicechat/org/json/JSONArray.java | 855 ++++++++ .../voicechat/org/json/JSONException.java | 22 + .../maxhenkel/voicechat/org/json/JSONML.java | 332 +++ .../org/json/JSONMLParserConfiguration.java | 35 + .../voicechat/org/json/JSONObject.java | 1771 +++++++++++++++++ .../org/json/JSONParserConfiguration.java | 65 + .../voicechat/org/json/JSONPointer.java | 158 ++ .../org/json/JSONPointerException.java | 20 + .../org/json/JSONPropertyIgnore.java | 17 + .../voicechat/org/json/JSONPropertyName.java | 18 + .../voicechat/org/json/JSONString.java | 9 + .../voicechat/org/json/JSONStringer.java | 19 + .../voicechat/org/json/JSONTokener.java | 401 ++++ .../voicechat/org/json/JSONWriter.java | 204 ++ .../org/json/ParserConfiguration.java | 46 + .../voicechat/org/json/Property.java | 36 + .../org/json/StringBuilderWriter.java | 81 + .../de/maxhenkel/voicechat/org/json/XML.java | 623 ++++++ .../org/json/XMLParserConfiguration.java | 175 ++ .../voicechat/org/json/XMLTokener.java | 278 +++ .../org/json/XMLXsiTypeConverter.java | 9 + .../permission/PermissionManager.java | 19 + .../voicechat/plugins/CategoryManager.java | 31 + .../voicechat/plugins/EventBuilder.java | 57 + .../voicechat/plugins/PluginManager.java | 308 +++ .../impl/BukkitVoicechatServiceImpl.java | 24 + .../voicechat/plugins/impl/EntityImpl.java | 57 + .../voicechat/plugins/impl/GroupImpl.java | 184 ++ .../voicechat/plugins/impl/PlayerImpl.java | 30 + .../voicechat/plugins/impl/PositionImpl.java | 59 + .../plugins/impl/RawUdpPacketImpl.java | 37 + .../plugins/impl/ServerLevelImpl.java | 41 + .../plugins/impl/ServerPlayerImpl.java | 31 + .../plugins/impl/VoicechatApiImpl.java | 114 ++ .../plugins/impl/VoicechatConnectionImpl.java | 147 ++ .../plugins/impl/VoicechatServerApiImpl.java | 336 ++++ .../plugins/impl/VoicechatSocketBase.java | 29 + .../plugins/impl/VoicechatSocketImpl.java | 138 ++ .../plugins/impl/VolumeCategoryImpl.java | 205 ++ .../impl/audio/AudioConverterImpl.java | 41 + .../impl/audiochannel/AudioChannelImpl.java | 59 + .../impl/audiochannel/AudioPlayerImpl.java | 102 + .../impl/audiochannel/AudioSupplier.java | 31 + .../audiochannel/EntityAudioChannelImpl.java | 88 + .../LocationalAudioChannelImpl.java | 81 + .../audiochannel/StaticAudioChannelImpl.java | 113 ++ .../PlayerAudioListenerImpl.java | 79 + .../impl/audiosender/AudioSenderImpl.java | 99 + .../impl/config/ConfigAccessorImpl.java | 32 + .../impl/events/CreateGroupEventImpl.java | 22 + .../events/EntitySoundPacketEventImpl.java | 22 + .../plugins/impl/events/EventImpl.java | 31 + .../plugins/impl/events/GroupEventImpl.java | 38 + .../impl/events/JoinGroupEventImpl.java | 18 + .../impl/events/LeaveGroupEventImpl.java | 22 + .../LocationalSoundPacketEventImpl.java | 22 + .../events/MicrophonePacketEventImpl.java | 18 + .../plugins/impl/events/PacketEventImpl.java | 47 + .../impl/events/PlayerConnectedEventImpl.java | 25 + .../events/PlayerDisconnectedEventImpl.java | 24 + .../events/PlayerStateChangedEventImpl.java | 59 + .../RegisterVolumeCategoryEventImpl.java | 17 + .../impl/events/RemoveGroupEventImpl.java | 22 + .../plugins/impl/events/ServerEventImpl.java | 19 + .../impl/events/SoundPacketEventImpl.java | 30 + .../events/StaticSoundPacketEventImpl.java | 22 + .../UnregisterVolumeCategoryEventImpl.java | 17 + .../impl/events/VoiceDistanceEventImpl.java | 54 + .../impl/events/VoiceHostEventImpl.java | 41 + .../VoicechatServerStartedEventImpl.java | 17 + .../VoicechatServerStartingEventImpl.java | 36 + .../VoicechatServerStoppedEventImpl.java | 17 + .../impl/events/VolumeCategoryEventImpl.java | 29 + .../impl/opus/JavaOpusDecoderImpl.java | 116 ++ .../impl/opus/JavaOpusEncoderImpl.java | 82 + .../plugins/impl/opus/OpusManager.java | 46 + .../impl/packets/ConvertablePacketImpl.java | 192 ++ .../impl/packets/EntitySoundPacketImpl.java | 102 + .../packets/LocationalSoundPacketImpl.java | 88 + .../impl/packets/MicrophonePacketImpl.java | 84 + .../plugins/impl/packets/SoundPacketImpl.java | 166 ++ .../impl/packets/StaticSoundPacketImpl.java | 39 + .../voicechat/util/FriendlyByteBuf.java | 866 ++++++++ .../de/maxhenkel/voicechat/util/Key.java | 43 + .../voicechat/voice/common/AudioUtils.java | 65 + .../voice/common/AuthenticateAckPacket.java | 20 + .../voice/common/AuthenticatePacket.java | 46 + .../voicechat/voice/common/ClientGroup.java | 77 + .../common/ConnectionCheckAckPacket.java | 20 + .../voice/common/ConnectionCheckPacket.java | 20 + .../voice/common/GroupSoundPacket.java | 53 + .../voice/common/KeepAlivePacket.java | 20 + .../voice/common/LocationSoundPacket.java | 74 + .../voicechat/voice/common/MicPacket.java | 61 + .../voice/common/NetworkMessage.java | 175 ++ .../voicechat/voice/common/Packet.java | 17 + .../voicechat/voice/common/PingPacket.java | 45 + .../voice/common/PlayerSoundPacket.java | 77 + .../voicechat/voice/common/PlayerState.java | 100 + .../voicechat/voice/common/Secret.java | 140 ++ .../voicechat/voice/common/SoundPacket.java | 64 + .../voicechat/voice/common/Utils.java | 15 + .../voice/server/ClientConnection.java | 42 + .../voicechat/voice/server/Group.java | 88 + .../voicechat/voice/server/PingManager.java | 100 + .../voice/server/PlayerStateManager.java | 155 ++ .../voicechat/voice/server/Server.java | 502 +++++ .../voice/server/ServerCategoryManager.java | 54 + .../voice/server/ServerGroupManager.java | 185 ++ .../voice/server/ServerPlayerManager.java | 107 + .../voice/server/ServerVoiceEvents.java | 167 ++ source/decompiled/summary.txt | 2 + .../voicechat/voice/common/Secret.java | 137 ++ .../voice/server/ServerVoiceEvents.java | 154 ++ source/resources/CONCENTUS_LICENSE | 37 + source/resources/META-INF/MANIFEST.MF | 6 + .../configbuilder/pom.properties | 3 + .../configbuilder/pom.xml | 81 + .../maven/me.lucko/commodore/pom.properties | 5 + .../META-INF/maven/me.lucko/commodore/pom.xml | 162 ++ .../org.concentus/Concentus/pom.properties | 3 + .../maven/org.concentus/Concentus/pom.xml | 63 + .../maven/org.json/json/pom.properties | 3 + .../META-INF/maven/org.json/json/pom.xml | 263 +++ source/resources/plugin.yml | 28 + tests/ProtocolCryptoSelfTest.java | 71 + 454 files changed, 45050 insertions(+) create mode 100644 .gitea/workflows/build-plugin.yml create mode 100644 AsminVoiceChat-1.0.0.jar create mode 100644 README.md create mode 100644 plugin.yml create mode 100644 source/decompiled/de/maxhenkel/voicechat/BuildConstants.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/BukkitVersion.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/FallbackTranslations.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/PaperVersion.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/Version.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/Voicechat.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/BukkitVoicechatService.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/ClientVoicechatSocket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/Entity.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/ForgeVoicechatPlugin.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/Group.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/Player.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/Position.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/RawUdpPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/ServerLevel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/ServerPlayer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/VoicechatApi.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/VoicechatClientApi.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/VoicechatConnection.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/VoicechatPlugin.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/VoicechatServerApi.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/VoicechatSocket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/VolumeCategory.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audio/AudioConverter.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiochannel/AudioChannel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiochannel/AudioPlayer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientAudioChannel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientEntityAudioChannel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientLocationalAudioChannel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientStaticAudioChannel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiochannel/EntityAudioChannel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiochannel/LocationalAudioChannel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiochannel/StaticAudioChannel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiolistener/AudioListener.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiolistener/PlayerAudioListener.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/audiosender/AudioSender.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/config/ConfigAccessor.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/config/VolumeConfigAccessor.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/ClientEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/ClientReceiveSoundEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/ClientSoundEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/ClientVoicechatConnectionEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/ClientVoicechatInitializationEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/CreateGroupEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/CreateOpenALContextEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/DestroyOpenALContextEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/EntitySoundPacketEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/Event.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/EventRegistration.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/GroupEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/JoinGroupEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/LeaveGroupEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/LocationalSoundPacketEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/MergeClientSoundEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/MicrophoneMuteEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/MicrophonePacketEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/NameTagIconRenderEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/OpenALSoundEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/PacketEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/PlayerConnectedEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/PlayerDisconnectedEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/PlayerStateChangedEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/RegisterVolumeCategoryEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/RemoveGroupEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/ServerEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/SoundPacketEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/StaticSoundPacketEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/UnregisterVolumeCategoryEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/VoiceDistanceEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/VoiceHostEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatDisableEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStartedEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStartingEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStoppedEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/events/VolumeCategoryEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/mp3/Mp3Decoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/mp3/Mp3Encoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/opus/OpusDecoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/opus/OpusEncoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/opus/OpusEncoderMode.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/packets/ConvertablePacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/packets/EntitySoundPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/packets/LocationalSoundPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/packets/MicrophonePacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/packets/Packet.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/packets/SoundPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/api/packets/StaticSoundPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/command/VoiceChatCommands.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/AbstractCommodore.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/BrigadierUnsupportedException.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/Commodore.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/CommodoreProvider.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/PaperCommodore.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/ReflectionCommodore.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/ReflectionUtil.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/file/AbstractIterator.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/file/ArgumentTypeParser.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/file/BrigadierArgumentTypeParser.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/file/CommodoreFileReader.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/file/Lexer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/file/ParseException.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/file/Parser.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/file/Token.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/commodore/file/TokenStream.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/BaseCompatibility.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/BukkitCompatibilityManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_12.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_16.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_17.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_18.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_19.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_19_1.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20_2.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20_3.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_21_5.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_8.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/CompatibilityReflectionException.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/FallbackCompatibility.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/JsonMessageBaseCompatibility.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/PlayerHideEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/PlayerShowEvent.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/ReflectionUtils.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/compatibility/SpigotCompatibility.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Analysis.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/AnalysisInfo.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/ApplySineWindow.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Arrays.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Autocorrelation.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/BWExpander.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Bands.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueByte.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueInt.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueShort.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/BurgModified.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CNG.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CNGState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CWRS.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CeltCommon.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CeltConstants.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CeltDecoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CeltEncoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CeltLPC.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CeltMode.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CeltPitchXCorr.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CeltTables.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/ChannelLayout.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CodeSigns.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CodecHelpers.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/CorrelateMatrix.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/DecControlState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/DecodeAPI.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/DecodeCore.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/DecodeIndices.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/DecodeParameters.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/DecodePitch.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/DecodePulses.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/DecoderAPIFlag.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Downmix.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/EncControlState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/EncodeAPI.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/EncodeIndices.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/EncodePulses.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/EntropyCoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/FFTState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Filters.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/FindLPC.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/FindLTP.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/FindPitchLags.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/FindPredCoefs.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/GainQuantization.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/HPVariableCutoff.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Inlines.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/K2A.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Kernels.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/KissFFT.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/LPCInversePredGain.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/LTPAnalysisFilter.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/LTPScaleControl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Laplace.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/LinearAlgebra.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/MDCT.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/MDCTLookup.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/MLPState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/MultiLayerPerceptron.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/NLSF.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/NLSFCodebook.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/NoiseShapeAnalysis.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusApplication.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusBandwidth.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusBandwidthHelpers.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusConstants.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusDecoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusEncoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusError.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusException.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusFramesize.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusFramesizeHelpers.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusMSDecoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusMSEncoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusMode.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusMultistream.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusPacketInfo.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusRepacketizer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusSignal.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/OpusTables.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/PLC.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/PLCStruct.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Pitch.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/PitchAnalysisCore.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/ProcessGains.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/PulseCache.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/QuantizeBands.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/QuantizeLTPGains.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Rate.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/RegularizeCorrelations.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Resampler.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/ResidualEnergy.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Schur.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/ShellCoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SideInfoIndices.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Sigmoid.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkChannelDecoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkChannelEncoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkConstants.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkDecoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkDecoderControl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkEncoder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkEncoderControl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkError.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkLPState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkNSQState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkPrefilterState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkResamplerState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkShapeState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkTables.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SilkVADState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Sort.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Spread.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/Stereo.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/StereoDecodeState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/StereoEncodeState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/StereoWidthState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/SumSqrShift.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/TOCStruct.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/TonalityAnalysisState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/TuningParameters.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/VQ.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/VQ_WMat_EC.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/VoiceActivityDetection.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/concentus/VorbisLayout.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/config/ServerConfig.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/config/Translations.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/CommentedProperties.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/CommentedPropertyConfig.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/Config.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/ConfigBuilder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/ConfigBuilderImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/MigratableConfig.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/MigratableConfigImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/AbstractValueList.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/AbstractValueMap.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/IntegerList.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/StringList.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/StringMap.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/IntegerListValueSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/StringListValueSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/StringMapValueSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/UUIDSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/AbstractConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/AbstractRangedConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/BooleanConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/ConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/DoubleConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/EnumConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/FloatConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/GenericConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/IntegerConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/LongConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/RangedConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/StringConfigEntry.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/BooleanSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/DoubleSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/EnumSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/FloatSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/IntegerSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/LongSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/StringSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/ValueSerializable.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/ValueSerializer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/debug/CooldownTimer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/debug/PingHandler.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/debug/VoicechatUncaughtExceptionHandler.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/integration/commodore/CommodoreCommands.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/integration/commodore/GroupNameSuggestionProvider.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/integration/commodore/MissingArgumentTypeException.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/integration/placeholderapi/VoicechatExpansion.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/integration/viaversion/ViaVersionCompatibility.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/logging/JavaLoggingLogger.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/logging/LogLevel.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/logging/VoicechatLogger.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/AddCategoryPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/AddGroupPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/CreateGroupPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/JoinGroupPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/JoinedGroupPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/LeaveGroupPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/NetManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/Packet.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/PacketRateLimiter.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/PlayerStatePacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/PlayerStatesPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/RemoveCategoryPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/RemoveGroupPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/RemovePlayerStatePacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/RequestSecretPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/SecretPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/net/UpdateStatePacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/CDL.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/Cookie.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/CookieList.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/HTTP.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/HTTPTokener.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONArray.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONException.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONML.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONMLParserConfiguration.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONObject.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONParserConfiguration.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONPointer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONPointerException.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONPropertyIgnore.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONPropertyName.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONString.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONStringer.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONTokener.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/JSONWriter.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/ParserConfiguration.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/Property.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/StringBuilderWriter.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/XML.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/XMLParserConfiguration.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/XMLTokener.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/org/json/XMLXsiTypeConverter.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/permission/PermissionManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/CategoryManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/EventBuilder.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/PluginManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/BukkitVoicechatServiceImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/EntityImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/GroupImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/PlayerImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/PositionImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/RawUdpPacketImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/ServerLevelImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/ServerPlayerImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatApiImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatConnectionImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatServerApiImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatSocketBase.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatSocketImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/VolumeCategoryImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/audio/AudioConverterImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioChannelImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioPlayerImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioSupplier.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/EntityAudioChannelImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/LocationalAudioChannelImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/StaticAudioChannelImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiolistener/PlayerAudioListenerImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiosender/AudioSenderImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/config/ConfigAccessorImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/CreateGroupEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/EntitySoundPacketEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/EventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/GroupEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/JoinGroupEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/LeaveGroupEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/LocationalSoundPacketEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/MicrophonePacketEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PacketEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerConnectedEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerDisconnectedEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerStateChangedEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/RegisterVolumeCategoryEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/RemoveGroupEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/ServerEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/SoundPacketEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/StaticSoundPacketEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/UnregisterVolumeCategoryEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoiceDistanceEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoiceHostEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStartedEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStartingEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStoppedEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VolumeCategoryEventImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/JavaOpusDecoderImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/JavaOpusEncoderImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/OpusManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/ConvertablePacketImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/EntitySoundPacketImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/LocationalSoundPacketImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/MicrophonePacketImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/SoundPacketImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/StaticSoundPacketImpl.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/util/FriendlyByteBuf.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/util/Key.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/AudioUtils.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/AuthenticateAckPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/AuthenticatePacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/ClientGroup.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/ConnectionCheckAckPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/ConnectionCheckPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/GroupSoundPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/KeepAlivePacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/LocationSoundPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/MicPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/NetworkMessage.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/Packet.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/PingPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/PlayerSoundPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/PlayerState.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/Secret.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/SoundPacket.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/common/Utils.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/server/ClientConnection.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/server/Group.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/server/PingManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/server/PlayerStateManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/server/Server.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/server/ServerCategoryManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/server/ServerGroupManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/server/ServerPlayerManager.java create mode 100644 source/decompiled/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java create mode 100644 source/decompiled/summary.txt create mode 100644 source/patch/de/maxhenkel/voicechat/voice/common/Secret.java create mode 100644 source/patch/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java create mode 100644 source/resources/CONCENTUS_LICENSE create mode 100644 source/resources/META-INF/MANIFEST.MF create mode 100644 source/resources/META-INF/maven/de.maxhenkel.configbuilder/configbuilder/pom.properties create mode 100644 source/resources/META-INF/maven/de.maxhenkel.configbuilder/configbuilder/pom.xml create mode 100644 source/resources/META-INF/maven/me.lucko/commodore/pom.properties create mode 100644 source/resources/META-INF/maven/me.lucko/commodore/pom.xml create mode 100644 source/resources/META-INF/maven/org.concentus/Concentus/pom.properties create mode 100644 source/resources/META-INF/maven/org.concentus/Concentus/pom.xml create mode 100644 source/resources/META-INF/maven/org.json/json/pom.properties create mode 100644 source/resources/META-INF/maven/org.json/json/pom.xml create mode 100644 source/resources/plugin.yml create mode 100644 tests/ProtocolCryptoSelfTest.java diff --git a/.gitea/workflows/build-plugin.yml b/.gitea/workflows/build-plugin.yml new file mode 100644 index 0000000..b35b691 --- /dev/null +++ b/.gitea/workflows/build-plugin.yml @@ -0,0 +1,177 @@ +name: Build AsminVoicechat +run-name: Build AsminVoicechat + +on: + push: + branches: + - main + - master + workflow_dispatch: + +concurrency: + group: asmin-voicechat-release + cancel-in-progress: false + +jobs: + prepare-release: + runs-on: docker-builder + outputs: + version: ${{ steps.version.outputs.version }} + tag: ${{ steps.version.outputs.tag }} + release_id: ${{ steps.release.outputs.release_id }} + steps: + - name: Checkout repository and tags + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Calculate next version + id: version + shell: bash + run: | + latest="$(git tag --list 'v[0-9]*' --sort=-version:refname | head -n 1)" + if [[ -z "$latest" ]]; then + version="1.0.0" + else + current="${latest#v}" + IFS='.' read -r major minor patch <<< "$current" + if (( patch >= 9 )); then + version="$major.$((minor + 1)).0" + else + version="$major.$minor.$((patch + 1))" + fi + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "tag=v$version" >> "$GITHUB_OUTPUT" + + - name: Create Gitea release + id: release + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + GITEA_SERVER_URL: ${{ gitea.server_url }} + GITEA_REPOSITORY: ${{ gitea.repository }} + VERSION: ${{ steps.version.outputs.version }} + TAG: ${{ steps.version.outputs.tag }} + shell: bash + run: | + set -euo pipefail + if [[ -z "$GITEA_TOKEN" ]]; then + echo "Add a repository write token as the GITEA_TOKEN secret." + exit 1 + fi + api="$GITEA_SERVER_URL/api/v1/repos/$GITEA_REPOSITORY" + release="$(curl --fail-with-body -sS -X POST "$api/releases" \ + -H "Authorization: token $GITEA_TOKEN" \ + -H 'Content-Type: application/json' \ + -d "{\"tag_name\":\"$TAG\",\"name\":\"AsminVoiceChat $VERSION\",\"body\":\"Dual-protocol Simple Voice Chat server release.\",\"draft\":false,\"prerelease\":false}")" + release_id="$(python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])' <<< "$release")" + echo "release_id=$release_id" >> "$GITHUB_OUTPUT" + + build-and-test: + needs: prepare-release + runs-on: docker-builder + strategy: + fail-fast: false + matrix: + java: + - '16' + - '17' + - '18' + - '19' + - '20' + - '21' + - '22' + - '23' + - '24' + - '25' + - '26' + - '27-ea' + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Java ${{ matrix.java }} + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: ${{ matrix.java }} + + - name: Download 2.6.25 base and compile dependencies + shell: bash + run: | + set -euo pipefail + mkdir -p build/deps build/classes build/test-classes + hangar="https://hangarcdn.papermc.io/plugins/henkelmax/SimpleVoiceChat/versions/bukkit-2.6.25/PAPER/voicechat-bukkit-2.6.25.jar" + curl --fail --location --silent --show-error "$hangar" -o build/deps/voicechat-bukkit-2.6.25.jar + + repo="https://repo.papermc.io/repository/maven-public" + curl --fail --location --silent --show-error "$repo/io/papermc/paper/paper-api/1.17.1-R0.1-SNAPSHOT/paper-api-1.17.1-R0.1-20220414.034903-311.jar" -o build/deps/paper-api.jar + curl --fail --location --silent --show-error "$repo/net/md-5/bungeecord-chat/1.16-R0.4/bungeecord-chat-1.16-R0.4.jar" -o build/deps/bungeecord-chat.jar + curl --fail --location --silent --show-error "$repo/net/kyori/adventure-api/4.9.3/adventure-api-4.9.3.jar" -o build/deps/adventure-api.jar + curl --fail --location --silent --show-error "$repo/net/kyori/adventure-key/4.9.3/adventure-key-4.9.3.jar" -o build/deps/adventure-key.jar + curl --fail --location --silent --show-error "$repo/net/kyori/examination-api/1.3.0/examination-api-1.3.0.jar" -o build/deps/examination-api.jar + curl --fail --location --silent --show-error "$repo/net/kyori/examination-string/1.3.0/examination-string-1.3.0.jar" -o build/deps/examination-string.jar + curl --fail --location --silent --show-error "$repo/org/jetbrains/annotations/22.0.0/annotations-22.0.0.jar" -o build/deps/annotations.jar + curl --fail --location --silent --show-error "$repo/io/netty/netty-buffer/4.1.63.Final/netty-buffer-4.1.63.Final.jar" -o build/deps/netty-buffer.jar + curl --fail --location --silent --show-error "$repo/io/netty/netty-common/4.1.63.Final/netty-common-4.1.63.Final.jar" -o build/deps/netty-common.jar + + - name: Compile Java 8-compatible patch classes + env: + VERSION: ${{ needs.prepare-release.outputs.version }} + shell: bash + run: | + set -euo pipefail + cp="build/deps/voicechat-bukkit-2.6.25.jar:$(printf '%s:' build/deps/*.jar)" + javac --release 8 -cp "$cp" -d build/classes \ + source/patch/de/maxhenkel/voicechat/voice/common/Secret.java \ + source/patch/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java + + sed "s/^version:.*/version: '$VERSION'/" plugin.yml > build/plugin.yml + python3 - <<'PY' + from pathlib import Path + from zipfile import ZIP_DEFLATED, ZipFile + + base = Path("build/deps/voicechat-bukkit-2.6.25.jar") + target = Path("build/AsminVoiceChat-${{ needs.prepare-release.outputs.version }}.jar") + replacements = { + "de/maxhenkel/voicechat/voice/common/Secret.class": Path("build/classes/de/maxhenkel/voicechat/voice/common/Secret.class"), + "de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.class": Path("build/classes/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.class"), + "plugin.yml": Path("build/plugin.yml"), + } + with ZipFile(base) as source, ZipFile(target, "w", ZIP_DEFLATED) as output: + for item in source.infolist(): + replacement = replacements.get(item.filename) + output.writestr(item, replacement.read_bytes() if replacement else source.read(item.filename)) + print(f"Built {target}") + PY + + - name: Run CBC/GCM interoperability test + shell: bash + run: | + set -euo pipefail + cp="build/AsminVoiceChat-${{ needs.prepare-release.outputs.version }}.jar:build/deps/netty-buffer.jar:build/deps/netty-common.jar" + javac --release 8 -cp "$cp" -d build/test-classes tests/ProtocolCryptoSelfTest.java + java -cp "build/test-classes:$cp" ProtocolCryptoSelfTest + + - name: Upload JVM-specific build to release + env: + GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} + GITEA_SERVER_URL: ${{ gitea.server_url }} + GITEA_REPOSITORY: ${{ gitea.repository }} + RELEASE_ID: ${{ needs.prepare-release.outputs.release_id }} + VERSION: ${{ needs.prepare-release.outputs.version }} + JAVA_VERSION: ${{ matrix.java }} + shell: bash + run: | + set -euo pipefail + api="$GITEA_SERVER_URL/api/v1/repos/$GITEA_REPOSITORY" + file="build/AsminVoiceChat-${VERSION}.jar" + asset="AsminVoiceChat-${VERSION}-Java${JAVA_VERSION}.jar" + curl --fail-with-body -sS -X POST "$api/releases/$RELEASE_ID/assets?name=$asset" \ + -H "Authorization: token $GITEA_TOKEN" \ + -F "attachment=@$file;filename=$asset" + if [[ "$JAVA_VERSION" == "21" ]]; then + curl --fail-with-body -sS -X POST "$api/releases/$RELEASE_ID/assets?name=AsminVoiceChat-${VERSION}.jar" \ + -H "Authorization: token $GITEA_TOKEN" \ + -F "attachment=@$file;filename=AsminVoiceChat-${VERSION}.jar" + fi diff --git a/AsminVoiceChat-1.0.0.jar b/AsminVoiceChat-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..50ad3564de33cd9985d9c89a4d84ea8111c76381 GIT binary patch literal 932497 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

3Zs_h!Kov zM;!q64H3XFt4PI1SS_`;1sDUjVIETj4vA^UomiL2(x|4{bgOVQV7!&FGO(7`d`Jxyd3OwNm$8v#r(}Dvw#_ex zr}WhaoYCj5)vBy!z5IEsdx?Zo+Q{?HS+!|q{WxwUK(qZF2nL`8`;_zJ26pn(B{cgi znAig`->NQxV2}V9?4-K@B$&2kZWO{h)oq++Km6)XM^wVt#;-|ke;#BKXC8Lw`Fl{-HS9Q>jFZa&;PP2(mY^AQ5Exy--D=5kgh&JK>24)$jMWsF}Q zKO)m31nT3U+o+tpRp3u$az-MED?05!CC~;VPylUoOWa!|7zlp7AK2$ z8%EToq_SpwICgw6Ib1MSc0t#g5qRsuL)G>5r;* zo0#36oRPDeY4NCd<;^5A?{;DUt>;x#Q?8*CVbOKYDpg-?CxPZYa)`Z z-7`8>-Du!o2xBwx1jDw!)U-rR70}!6vBhc#!M^W)Yi&X>&Z#2SvoUHiE)%est#EZN zt`K2}R3kMAQAyBfHqg^=Rg>%c^c6{j_zP_NE!k%qtIuq+p@@z1p>cY!btF))*s+LG>`D{@8iD3^)P;N}XexLY;$An7>2$G&Hy94f5tv zpy@rKe+|n262SI^HTl1iw162qn!f8fEEFXa3_Aq3sH<=ea)71rG>QmkyTP!gM8k@C ztKB7H+cmB)q#QY={v((`0(0#fn6@oF<$2%v_|WLZ>%>6DzqRD_f2ETGP$>wgfB?e- zu{8FVSJf7aX=c#!T3n*a3MamNX~}9~u{GjAg5A-xTrGKf!>#^NU%GY=sw_qVsP_~Y zH=bZ3ix$Cxd4P0v)fMW#C;P0?9o0p;Nc#?M!kMtNG|QW{@X368l!YqAM&?1x`^9Y` z5z}B!ulq*n52&)>MY>6`P0r4^H59ITR@zIA5VQ)r@8}q$DXpfr;^3uhsHv`Bay12VB@b+rR;0bc;+&5jz1Qhw6i!6J&GVuI3P9N)E(0*sDcG}IH6Kz z(owD8vz&`Au&^o)606p)&a643X+Ov7c`vqxhwoeAl+my9Bz)_b>bZD>Xf)J%T5H?1 zTdsNkw$D+)*`0H?#D4VcLYuRIToLSTDu^F%)aCtjDKf{`~u|vto|KTKZYni#PB=d@GQPA_Y~HK!?Y{OwAbWj65yP>}^0oKkg1z zCT1YjA6G_4TQ>_UdlyD4&}0bXKN#-c2S-5VO?xvFS2I)bzvq_apsEt6)7%%pnQEG5 zH65^Af`EPg3l<5($w(D2DPn|@!}78)RN%$qg-#j&d!qzO+;Iwd5?O6n)`TVjOMK&; zVEkq6=Ih1l_5$wZFDh{MV*&kiXt}LBsh+SWkVu~RjAPuASbQ}N(27T}!5(XQhz-## zry?aJG>wsMgRZaJG!1@?bAD+%Q}N<{8;h>aH;ALOE@aE#yI|pd`hL$Sw;Vj-S_%re zM&Ve-CC4+m`Rj`Qwe7yle3ylsryESS?3te#;9)HQZ#@FaAtg#xGp1|?`DQ3?KYLWE z1gM@sK^Y}y|E-;^-PqOwsDDd*&xOJ>;GGv=wQ?r@a7x%CBi zojnG;jKs8tC4{VeMN>S2K7qyW5@5F$n*0d6&Hm^TEN4mY!gDjkjNxG5u?6SVup+vQ zy@te8LupvjV(F0)&HEty*u`5Y8y$mJ(GToLBjYPkj$!<#x{~k0&2M86o>d@UCHud@ zQ_RW*RJi`HA&nEZK{rDAkuA-!WS$O6C(GAwlkzt`u8isQ%eqBSa^%hpI7M%k#?EV& z5bq6W>R^dp!QZLl)}Z5p!Y7;CSwW&!EyGE*}*b2Y-_H@ zX6d|bVBfbLEGtvj0;dkPqO@{0AC>TUaXh%OKg&c3^~6m5?^1nDgSn;~oOdb@_So|W;b z@N3l(cY4)a3#X%5lC-z|(w2F}W4gr)(u~=y1()u=f)7M+nFwPJgz>;JfPR3Zha3r) zTsZw~9G!>fHTGt0O=6L)qUhll&%NN^(}*pwD_uYF*uUWT3;%%Oe9WesK&lcaTD{M4T2v*+UzS1mqu*vSr zB93TyLt?O;Nt<@Y#2F+zxFbZ8XT+Nemx_uyUy<9?x2Z?~leh|U__oV`MrTf4?zLYs z>c`hRKkiN(x_CU~lx1=98CYcDIn#&jTOW{B9sl^gP~YN}IBiO}4_q|1{kHHqi*_g_ z!xZChEo_l2X)OrObRWQ`bnptqUPGmX@AHnP=Z2UuhX1xG0Dgtt@gn4A6Ng_s9Ee}1 zdecmrnx&6jp)tVJAK*@A+t{<|MLS^nLj}5sVVsF>k*XJ=aAD=v`@Ewv?TWB-KGFs2 zCe5C5-4-DH8!yiKxSW%Iy!HzY1LoOXdm)n!wR)#pjBYEjS4VqeW9^Y;=2y+OA)TPq zu68CAdkq8en&;ZRVU!X#XJ=j0q0L`?W)-mCOqzZ;lC;+9v^G}jETMhXFi1L2(cQI+ z@bJ_atXF08{0#KX(L`)z@aYWjJQN?ocPgY-W}()huO-cR(&+zg`n#b@m#bnLA)oeV z+oAAwl!>v+*k(%~-UZya%^%8?4cDIgS`%M29E18t?Q@(q##Nl2P4~8B*PyCPGr^y2 z4JAJO4jUTy!YC3Ig%@zx##iSn=Z;hBXD=Q8jnP&5TuwU}Cn7Cq9Epx4$57Ud8|xZt zK52?65adnKFk4xQUKGa`?onZql2ZOF_1 z-xXB;BSK*vN^zaGE-k-upo5izgz1eED??KSX!b7CueQq*kJaA9q)K0w-l8`-k->^% z`-2H445s%+EvjGHo%1HUARXwR|6BL#VaZad=gQIVm<<5)!t-LAC4q;Ae;(jd^izJ2X7xQn%|kMXSL&g@ij6I7 zJ*(G;CxCTj^_mG$jZL|^+H>7?q?!gm)$_4xWMFxbK+ES_T+{4kLzTIk<8MxX_$uxC zkeNoyaal}KCZcwtD{%;>!!-+5=^+^%r+~QyZ-@+;_W!WyL+%eaCdiicXto&?v0b+&e!idQ}>?9Ip3|BTjyU_|LCf&XX{#fugziZfTfW+ z(=z08c70|*64II5y5bs`JI6o7Z&-Mr8py1J8ue=(rDamGr}XS2mP2jbompP%EmDqx z&3Wx@o92P$JKVo!!)VwahWsEKm<9Qw@PFouDkhE~5!63?FEd77792DRnQfGB#AmXl z-|mXPkJ=&wM+OgWVbszch11Cy)z2d%RIVlw?jtNj*B4E}AfiKf!3*@b^xT}PzDLO! z_!0z3&y^$=m!M>6*_@xX%I7}c#eama=xKx1p1Wn2ngdKz0L_i({AdB{p^V#5-vp`- zLQ-|1s$B(s3*so}!V6}IvhNBpel6CWn-P{w&c#D0mY)s>Y+LM*>6b7u1DZ7Tr^oj@ zSOo=UO~rUJ`^%?ae!wjPno9$979gLhwk(d0^fSEqDr{r3eHgEn&*bPET% zNl?Xwdlt}rD=*7IRqsUrY_>*Qp{tunytdWU$GnFe=rE&2DU$uKs__M=TwpjN-}gu9 zuh{VIdd(5J*1ogreGS$XUN-Ut$Dyvd4SN05Z|YmSm>_^_L+JAdA9Y|FzQzf9lmyVU z-+xF-|KFjppQ?W|7XsWX#%pO@Hd;njua>VQ(hFF>Y2gx;7+`Y~Oh=!c*(r>;>&JZp zo-XRUd(E;jF?(TrzYED2QxO*yz%h7Fd#9v&x=m(yPMRFvXM>2$j=p%xf*3A?{TPuX zrE~l8sYy?rEQck0_r4v4Xo;lDAze~2*&FYrT?nT9X+G0Qyy(r6ZJRAOxRtgTvQ=s? z22Qh#elLSZeD0y8LmQjud7rNpExuzA>6vY- zuu}%M=^&xm`~tLFaVc|-Y76(s7{W2?Gs<5fHTaGzJH3HSec=TPsPJHJEx0;@#qOHF zJ$);leziHCHtDWaqs=zyk~1`ktJl#W^XzmOuf3OCw^P?fh-WuT&?xBoOn@%}(NOS} zzoDu0#MD&Q023Keji#=eoPhA_+a{8a8JGy2MJ&f7Eaa>Hz zDox}n>29@^#ZM)QpM3?RMNJZ+DWQg)c7XOMsDjO?ibzG+5&Ium#pz-`>x#&2_98*w16X6jGy>yAsJk&5w|50g+63*)_A#QBfI5 z$H_a!ENiYu&yQa>*3+=EQ0c5|{i1Nuz<)CAQA7V^*m3#$p3-M|60el7EDJo;wNmSh zp8HZN#@N}R3>N6`17D0V`3o1Zex^(lm4wDtHOM`D!kNP`x6O#$Ha!>HZ6g{AFyBV& zR9!f2@%~m$48W}k!UjiO{Qw&PJxMMM)5HHa!`}YBy(9Agbpr=t*iHVyu(xdc@?C~X zFy!K+F@YHNc3F+PO*AGWJOm#%D0Q@5d=SIVtoOT)?l0VS7lhlsLH=v1p>byx`>X5= z0!20D|18x2Y#sm8!cQlEhUShd54tW=m%?8}L}Rw(*p4fC|KzP!NQ8J?rsfxz7fozr zX+Q~Bae99SyIIin+wUjnj5^@gT2mDZr9vv5h{lg)!=+I~&fAlQdse<>j@)ozp`mO` z0n*zisZm!GghL`|CtYK=6bji)vcLG`z;j(q$>Q$=CS#k$6)6utc6)wmC`H(WN505Y zRg_CQbq>Y(D9Q8Q+pixGAR|O%m3y2jg%W08FsIE4-!Oe;s1GHUm{zC%J<-dO&EQvc zvU6Zt_lLkccwelH^cE>^Zxb1};O?rE_43r+4<8W>2vG|nVEUk^`}@Exsn2ot@oLOJ zm?V77q(e^cd0WEN=AbPf;^KFzT+FC{{v+ulO-ScxfL@^(M8HV?XD^}l7kC9iGn@@< zjZFT?{e=m-pxL{>c=6Xfr7kT}e`t}=R+1ML1TqDdq2vT~-Lw-dbSd!Zz zcCf0euMTM332+@hpKrujZd-A*SpewaE4535mNUDc^63FG51TVia^VAu4-m{1WY;?7{a!z-ulbbkc%Il`uCQUr8>bk{ zq`)VrtDMJOdX(W?ZebAVLsw=@ywQ>AafwCY85ArstH*3$*7qG!HXuA>I=^Xk>NehO zh)RSczoem4jjyVBW*+)kcDo_#P#TZ>EI80BXs3G!;i7!xLzOvr7aV0Y(1D@EaM~dH zl*H_XI_rXUhmW3hfUx8!Ig0laTX-770Uw z5T7n^#1&e^vwH5-OcuAi{8z#6l1^O%1|Gt2V7?gc#y$Ue+3LbjP~c9G09&etAtb^M zG;7^qHF(`2A4F1XkT5!FFF~Jt&mbA{1ZFq+;H{DJV5HGdc=@oLZ=~vV4+GrSI3AUP zR8xNjUdMItFaZGs2E(pYnrd8^KbMO`Db~GR_kvN_=%&V#Ct^3} zmU0Nrlhh{L%ZJ(%nEog@m@(&Pzgcp^A&AD({Me|hwZU>*Ayl?#dGlQ)gCb>{RvpyE z_W|3pYpP%O&c>0BE&B{p5(cfaXA*>soa-B$wX+7+y8@g^TxFFE^%JcVp8Ks7pB@n& zk_5b0J`M~&K8M!+s(EByM+$dZ$(NEIi9&`55WW5(bOqQBCTzr$q&nl!!47~iD|ql` zCsa`aPV70$G*a?ReOuE?u}f3xCUR|dzFFLYZzw!ze9bwK9IB#DSLiT;?wdRGjjSVO z$oxs`PDn@$fE$##N761|c|1oTh1u~c4t#dh1pBBdE9=dWe`s#a zP^9#4Xj)}8-STYV`fdmfil556U(=hif~8U+2cWn17co&U-C`0;Nt!VhGXJP<+zX6< z5`c_L1r#0sLule}7}5sZD*`R~^*DDyC?-af8h_DJBeshq{v}^sCVhRn>{6DRfjA#vmQQ-VANQQ} zeGa!js)s%B?wKL9`Z6?T#2nKqh8*)~BfDGQ1jUVHtCA8V&fiWiH-zsGfVjAL{5A=afW2S4XV&)% zCYZOUQKea={en%DX~C_8V`MYccsVJ$XJJ=??zXEaIW$xSN7Yqswn|mIwlpIrsi?B& z!SXxs%E(B577M=e?rS8S6_%w-)fXJSiVs9D5tIBk7+91;QP_MogSl>1>|!vhRCKJl zSW(~9oxfnz3;(p_$Erfu5W;-EN1|F?R$+FJM+cvcEL(8 zir|+=K;|2#xso9$MNiZ_ihgTL{1T{pXP*?{RdD4aJ#uFkz%WU1=ERTVlJFy=ubpyi z6MV3h0H|QP@%jgzm9d|QP58g$ymt2X|AX`X{A)ZQa7sRHXmQ^1777xlGNj(YWI_Im zl7G4s&PCDjV1$Ct2(e(oM|FP$;+|l1Y5Z{5JpXdfDFLr7mTJ2x<`2j<3xQyBpU3?I zxpwR)$rA3tB;6_`@aVH&g%8PQ1|!4Ra{I&)n0Uw7cO0}Fzi9+fDho1;9t)L{fJIi!A|gNrXjXDp7EbPz@9FfZ+Aj-O$0L=12HTZMny8Yw*GjE z2lY!*FVIs+f}q8J_ekge*uK{{T^+- zC!Q9fzfs<|R%EhUlY%{2n;)8nqJ&YZ*b(Q>=DX5g^JyKvhmMri@V#=)mdjn#=f$56Hr+aLB4=9Z zTyM|2yMF=Rsj_Dutt1I|hjt3or{cM;+eOEv-^WerUU}r|z5FqSgTi)dvH-%SHW2^) z3rcqOE>6rc2CfE*pbz3EMs}c;)c>+Ek$7pR0#Yg#8t%GlB>ZSkLMnvVilm#)gUX)6 z0xJXoNgyX`l!nf>YP?9G0jjXPUlSvHU^kB<>!s}w_38DG?OB2?p+ zSoQ{np}e2~Ty~O^E|RZjcwTqPx_)`^>7H_Wf7{5;hLC&++%}#WOZe$2o8&p{$VQ)~ zTLUj!lwHbm8+X3Q=GwYz+#%DMl**w#aV*p6+DqpyQJ%)APC!VyTx(sGf;a3en|d5* zQoel(2}Q2`z)A1gubWPv8Ixm%@t!~1nA5+uj2&^c-T&aJ`iTf50Ouq;{L1vJ zAalsVgyUeXsoWNNNCk=4tgFp?0|`gMPvj|hf3g00ho)NcpwDay8R7{a`1b~mV#=BP z4gn0@leQL^Ig&Y#vaa&>eE09$yi_4fYCr1|o0k}Rk9+|~gPu_3yKA7Uz0?W;C2|hJ zHSfND(m*85t2^8uXBddq_GvBz<;Hv-TQ`+WN-GA zI~b)<4!AxYyq29O4XwIXF#|f=5B-fIIZ=tDeq&qs)k>Vyzv~^J;RHC%B{S(}pb@OY zI}g(*S~oX1Far~1)La-QX;NumcxX~4+M4XrqdHC7DaGQhWblS`*Yw%&(^$*uMo@&< zn5xgYF%>`m)KlR{DDMJ<+e^h*|Cqe3j2pNM5!PqTY~$>tIU*VZ5drM@;Z6xgteZR3 z+8Esu0({hvxp|FB%y2#y%L_f^a^>HJ1jFvK)I5IvxL%b`Qvli9x`QX1LCrdkLLaNot$Npy zP~$YE$W{1namV;Dudh8a>x7s6 zYfd85NVFQ^H^Tsl9YL5L{u&EqnqBzR4wjBS$h!y)*D_LTPEA(w3*!iCw{4h_IqX0z zIkUxz)vMOl)RAY?ok>9btQV0rI*--T8Psr_=@_I}`L`;{{xp)L`;!HKP6K{yJ5*sY zRH_iFV5*$a$EvqIiVuXOl4Sl^Il|vbrW5sw**D%(bdTrjkBr*pPHzMu!PlYM;K0^q zmPHaM_>YWn_*-Fckzj6SqF=I0Sf&<__sLw{T2ugaU_>67qJfXEW zxNLew{QiF4THL|8#LH1PIT9{(wKv(HJ2idiKYOGS1q3$Skhf)zA7t}j?34P#3f$Bu zlavbwd^P@TW*02u6B9xwV#ba3bYy_?dJ|7JxOQd|2L_Ph22*ynPjaGrrT-ZRxtBpr zGKcdVk^?q=OfC187BI%qJb4oyI~5x7sasJ7o64VU&Gz%zj6;mLe9f5|~g~W-bvy3JIY?0uB8O+;vl2HovRCBfFV0*UNYsQUTPq-?k?jLov>$=svco5K}H9)iIyt;ZYJ2mJjvHezGL`CR^jT|-^(Q5w~Tdy1oXv1N9lUE zl6ekr)1%Hmw=Hqtp6^J9$MO_={CT{#8$@OIiNr(HN@=ZJ+7>Hy_#v!X+xUViO-?*- z3vI~(jhWYFekoOo245HI&cRtBW}iYg!|*#hqC=8c1`rn`V?WNT+CH5@g6i=*{rUkq zG3y|6>Y?D$LyFs@0Q;w1fv&Gf%pTdk_CKmqfL4bOLFq5?+A`2q1GIH2U+@mOAj0$_ z*)xX7MM9!i3KhiGi>p1JB&=>tPH#T$%J9_b>w$RnI$DC(GMXKNJ!UWAR1#6ZEH37r z6qJbQ?i;uBIhfWjx9I^!@ti$tu`>gc=A61{?u!P#=2iSp>To6XHc00dD0924K?Dv+ z!6M?~t*5GR$z@ajM6;i`a$D zSO;R9KW=^hD2p741oWstpxYcsS4i~V1m6B7X-e4sx1_n@gfWUW=*t*cSo^6iwwD2V z4VJm0@OLn5yCy6&OnQnqi7&${r4>Jgg{+O|gjBxyXUHqNeOiDA(1chtk&)WK>YlN5 zY%4*gu&ppo0=(2ZFS%>O36v`pwrpR|iGAOo9|Dx@i_73SA9sZpvT3`2efmXL=17t+ z+HZI6d0Vr+fbTDJ$V2x?@ zt4Es@K<3flu3L?z(Rp@%@!v`Fs-@L`@~d#g4l>T)@x;RO5Ye^Y>!$DYYLiUv-dpH2 zm$s)@XTa{}7kvvQ$r=}Oz&+4`4`+o5z9C^1w8y;=-$N$PJc{ebW8+J6mSQc)9|xy5 zFKO$?LP9gr`YHjVN25XaDuh%+!{hnwrJR|rESH!1airH&X=xJfuxs3p(gBLw={;R# zHNO?JegfgMD3&)`nW4A(VmXT1B|^n|lLD>1Zn2l5J(KE8QN<^fFSM}z@(c4leAOk_ zf`=HH_ujn+Dv=Uf>d&*xl5EN#{dRak9(g)R`RcCj*()#dBN$p+b}F87I5U8xnHp!h z2)mV&WK2wCP?byRC77PG#%?hFa~4Tsbsi^HkfT1_Ha5nFMC;{Z( zZTYRQQf<42{pId!K&vB_DFo?LP-3l_jd#!sR>+JzAoCq zQ4vc`sV`V#I&b_OQ(Hi&1K^FWX{KjluBE*fzQ>qdUpH^oU5&FSH&S$CB957=bX7n` zCWAL`6-;_IYj_RE9p~uM(NX|J@=E)Xh~dtPUT*7{%Cht8zV@)77NpUs##+z^i+RhM zc~If9T)u2xz4Fu8eC;xa;E9SgnL=W##FinAVS z#MBl)EZ4HP$f|O&zjVi{c^`nH55Wrl75^hHYt9d2T&@^1JRpJQP|KJH{^8~5cc(MY47+jkiH-+SCkDn>O{I)=V3JI;Ll5{5o1&g0()RVG+yF2jg<^WFDPJ zKzoS&?VLe@Q=4k+Pg~`f%3DMmbHJyr0Td&@`ovnrE)mK}Zj(>1UNiYwGN?w{5G>d> z{pwQ6$~9l#oGR_wdR<^Q&r@6tX2ovR%i|tv?$mDi!s0t?u6aO_Z z)!U}}W5DCX0P&Y=U*Qj4Ah*8fSS`!|49`7t8I}tur)9?X@t|*f!3Kq`%_E_+7EUar-Gm zUE+3Z`rNJQPt1f|p0E9jw%x7w zT^jcqpp#$qxM0_B*)Z_hMn* zb_^xjH|T!_C>dJc5nMnPv<#Xf{dX6J|IX$A#ftuELkWs1Yib}Hn&RTZ_WvL)^*F~t zVQWR;PeMuoC3A`&PiHhtbT*xw$npRf*qSO_h6xW9erEo{eZK~ubx3mcO{iEQzVE3T?d`w#k-wc zgHBg`FzoBJCH--Eo>e1$8Kb$e{G1m~%7P=~!4sO)<~6}uT-S!9y6Gl>OyC*GW}uEt z!`rSk`XKj{iWQdt^&p_P9|0a|qrHfA2nM{8JUuI)tFRjKn0U3W)beghhGjm1x$KI% znDXOBIM^e!xP7ymVekElo*Z2>ewmpz&@(Y{H)z|k@Ts9GzFJMzlvJOaUeUFQ&J+No zTAGt{lC8-oeKmhx^wIp58Z?%nY^*@bHe1i`mp9Q$1X+72ml^#?As?m)^*NfVAO)>J z>pbXOhFa%?3XrQFpZ5ZGXcIl#x6M|~Iil*xL*%eB;iq9`mj(98;BBVzLHeyBZFi+p zv$nk=kWHP#a2V&GK=H!4ei6{1iJ&>>)$Q?_5=Se(q^X^X-oNWR=gXncl7@-= zS7T3=+-tmXKSGb*uek@VUdK<`v%E_F;-n2g0zE0otC-w#%4yHz&2=|=Gc@Xt$mThAO{=3Ezaif7sa)RV7I4QJQjoO z7K1$f?kD`gyA>nI7eysZn62Z?3}U(+E`kNV;D>?api3NS>04OsdFW^N68|*Q z9I?e8OG;8RZ=7F*?{&4(Z@{)bu%S=HKEtNl)*c~pRjC`Tldd7`ocZYD`~t4`faM${ zIy`w~V)mjG<k$TUGz|BVq~PsZ$xmuJnQP$k&t%5)|HW^O&Xd2o zl%2%w3$2VK?Ow9*T39pJbgqa5l~QJK>&wB1Q!twmhj4K?Cd+iGThGe5+S9o*=(zDN=JsrZ*D^mK*@8-1Q`{uruINmwx|(oF)z zm>;FSM&PE3S!UwB=15&xmfvOdW3*-;65JCd4kIKZhnHCE?nS;_8Dab7?wg}GH~drf z%#SrXm1+_vl_1J)7F$4N9ij=R((*BID9Wzep($Jrn3-o5_yqv}`#Kz>OfRoX;K+Z2 zP*~kFyFA1*c6mY1FY7+PjL|9TB=rj} z=b~>r-q=qO%xEEG>VW|DB&G_iDmKK1vmQvA_IL^{E>0fKy8Ek46DfC7)oaWXA>O4X zAH|~ThqpAjz73d)G@S#!!9 ztKE%vm@%=o^xb0FmTcqiZu6OgcTtYIo$Yz9KKL2Ej9B<+Q4eis)zsHEg*M3*WwV}X z)vR#pCbvFv*irKH+@_g6SriMC>3LPDwNhR0vk?ngxu`J0mP&!M!O!>U5FQ<|xw#^) zO4cIJ*H3I@`kCioGTXP{2mu}T=(uBYBC=K8CfKlMVh|0k}iHb~y~v$#JvF)|E-CDDgS+l?!S=G`f^aEy4?PFw2?J z#g^+<byT~G~`nvUC9Z(h5 z7{=R943doUOeeph!(S;zlKGfnZ&y_gool0R+%f|bQ)eULFFzqi2|<5vk?d* zHKnHeIWrSa`x@u>&^f)Tse9*p5?`K3Wv{b>x=3$|;Q1Ta!DZ~{j5FX2rW!U+@mV_2 z@&FE8(Haf!g3}*elw0!MuBr$WzpT)Dlljni#jZxY$y_+LOZ?m>-=tv zYcm|wqpin zW)ph1Qe!(er=5+%g|c?5H3Bwg&;;w47V*4Y5e`Fur6)O7P=4@;7x1r&awqXO3h z*l8>!a5!#dEnnvyc%r(*5Sl}0D(nh{>$}Rqpz|&lV2L3#A;Ip9lVY~~aI=Lu5cLhg z4zD1KYQbhCzj^=uz-lN=Hh0ZEB(R&hrG~}<_7JAl6{UpCI8~8<)XWw3s*w-#Er#uyxV>V;oOZBTglR(`*rZ}^&EA9+b>9}3=D zqDUzU=9sb7_l&1P>CMn%^JZ&q8=CrUN-!N$hVQc@L@+qGgns#1O}pzBfW7UR_UPC7 zB1J9akdV#$v)W3v0|4a5z(YTkqk6_Gp8q;TW)!WpH639rq&tBgj-6c@z7)q>JMx@f zXC(HZo+e0jm{6L8kNQJf$9?ZcOgKZ1_{SR3= z*rCioXi%6Oga7cs;D0kr{+9{re+iC_i8d-2>bODG@i<%qG-z~Pxu0d$P|Gdo8yRIw zzkkjBQW7dhIIMuuK3r3`GT?1+<8~9>z|QBon)+1B{#K#@WI#g~Eh3M|j$8M<>&gOt zx*zqpzRdQ8P^mDHj^U6xkYXnp5sF=_JCmu_)7x;*t59NL*>hLmp_js@UV<5AF^~D_ z5RtK+TRDdGXszO*J;rFqO(?6G&c@KZUUK0svSQJ&?kKCJO@}y!AaAGrn7eMjpvAz* zt#vVAb3nUQTlRCL-fjPJ#KGnO0lHHD*keq$3F=WsM-!9J_VMlYK)a}FUp$IbcStzr zllG$)U#d|GTCY+)7F2lFLPMgrY>;w{J5TljW?;pd))eayRBx!TKxBehY5Nx=v86O2^V>oKC?*89Bt$^xu@DZTTSa+L*WQBs z4-}~6yM2=-fXlI(7+%?+S(0U-!rb_;*ssn8W@7MBy5`~{8p}$7!hxDYkK!mGz!-1_ zC0Aue12?I@#);F>a-pyui zUx^myE@t+u*e_%NImJppB0vK7;<6JDsx-PjwpJmB(MmCL*ihn@@#uLrTRnwtjulaE zxE%98xoOJ112*&EX|wb{+&du1Hn{Zc9jY`KcQ)||`> z;C=2|MNkTMs5FH)4MPD&f0sJufkQk;e@=Z0AY4_bfQ1gD=T}So!RG6JIR~>cnDhGr zwOU%AOSR;siX&PheQdR@IO}s%^%mY&2LB z-EV;pgi=>fIn~xFRD+01q)Z=X<$hDz=gU6AqY~^{q1PTnQ|vZJ&L$?~7H-|8Zc_Zz zvF2;5udpfWK6mamvuil-21A!4$K}Iy>VT;iPh0b55ql5E8~Owrf7z=lYX|_=4VVe^8P3nF`>72yGO;3+HI*TybiAA+0`c`Q# zfxwqg+R_t_+e41%b4bx`4?W6*9lxvtbdXdk{^nnXrF&vXu)06$m1W$1L1{ajy;YBh z7*Emoa7*STHk zyj8F!J4+}|!)or5+M7xy)uy3wlS@X~PbHyAdHPCMo~DY(D2Q$%lcHKHy%GPKTqA?8 zR4gKV)R@#%O9Yd!*TbReihwp0>pk9tz+-Zj$8?}%rJ8Za0ngZ~XNPP{h6}DUsFF}3 zX1>+Sv_nw3YEEF~)3JcAs@TBHx7^L@;-18K*6utY9Y<|4Jb?h@{(+Ci_)AmB%;9dJ2NvEZf8^6&a`vu zBSJJX8P(Dlk1E4{k-cx8Yh2bLW-_r((XW6vd2J7Riz}K8OBhKMjfEXagF}icBfIp? zW?aq&7F7hfW=_Sk^<}=#UGPh)1k0CIwagpv=)Kk6R}K~M%|bOyo^)ghCn9qt9N~>4 zTf|=u!Ti{Zm43uW4z7>B3@NbIww5VXNot;*2Ky0K#ES8%+zDZ>V;MPj;DWB%hxV_t zDjtf>^aZ-DL#c1ZfA8;`0Y34UGw|p_=eJ;@+JZ?8Y&yU;gQ19ge;Y%-LQ&dA-XH&! zVbQa((WCf+7$WecZ8Dk`Xth^uzo`F3tJ?XYVs__RCiprLd8=dFu&}?8aL6siYg6pq z4aM6lS@6>orW=ZsXYYOIWUw^7BlDI^w*Hk_BHq^kGAPr>#wnt``j^k4+7tCWN>9Z9 z>M}n|NN$ioz%DPszrP*v|A__t^GHO}xgrSV4bF~NmSZ!|U7_(_kx@5?SUagS`e?=W z`J-MCW}-}jOha~r6I3&2(*e%ntNL28RCa6S!xL!;8>w#}> z73bA;gIMTB`%lt?9oae7^W+p=~8La8mp;kAY z$q8TUfP=Q}{0|(93ffRu5#k{1p{+)TajJ>zyfrC4xjJsY-7(f`ExbZ%&7-v_PKUkk z$Gz!gyTRGB9%|`bRkgqB(ILO~XO)2NVH)t6fQu6u0t1Lms zN*d&ZX#%t*4ZU)N=1lf7`DI)tY1`4eqs^`la_W~l8cLO7&-_Xk0(|M@WNrB6vE18j zH7Ztx!!%J1Mi-Zt*7N$Ab`Ynch1w?1wYCUzuR6I`*R{yrSmyUqG3tG{BjcRvHI`_8G6l9nYTsi!qL;$QJ;#n&$pk*A?5gNK@Dxye|wsdZ}2kgPF4}U zN`sizcj7?{o!-y;Wf>B&DcH&Idyj5e0;(W5BE{o8=zL2GEVz!|1xYs; zo(zbNMaX1X69k0|Zv9;hJ1CZpN@aFRq{ueI954tU-BGE)UT6T%V zOPYF_#94xE z*;4k(J9)FG+|@}IdWK~S4={a1SqXZkX z*Ge6?TS`kyV|$$$k%M zLR_6E&256^qwG~fm@7^b@C2?>M$NpK<_}a^jvoBZO;ma8HBdg;&JMxlQnfEq_Cr$# z=4KA?XpJoUsr<5bQ^{K6#Rk&zA@a#HR2DcPIJzf~VO~A@C#S&^Ea9XzSl$jWwOj2r zO1C$NDZ(Z5aZy?AUH89fm&;e3A1KHBDaf1WCrm$e84b7qCS9o^_?5(%*~&`tOLsua zaIdQbQ~X_*?yWq;?qY4@5Xl zJbtBC(c{AD22HOFTic^;TX^-R_|4QE6cb_X%cLPM3`5t%Fx0u8lu@4Mz!S|*vzt6O z+rj$q$;D@;N`o!S{w=f%XGA;cl?^WB-)^^`GiD!B-ywYUiG@)hJ?HwoPF4_21F|*> z9tl5};L{i<*4WMerc>*%nrf3-tBR@J@N^xWnM(Xj3lHHLO~$AS% z;;!^8Dr=sLL+e?|%Bkn7TS>{7v9U>|-Q5|`6>VK+<3Yv95moxNDn@ti%nBsRQ;V^- z#kwZ3sKCcNg*-+{dF9Tmr*T=ArD(mnk(toh{_)=SP?1pFPAfzad#o@5-}Oe*;pT^t z!YLN^1lp8JA=uKL_W^>-sTA~7;B`BkJPRGcsyT^2i+lo|E-E;n?G3)#Cga0yNL4Nx z5xDPB8$NDr9KF;P;}HF7ydBF>MU{zVa>>%tAtmy$)i`>k&}^EQ>@z*O+7;6IxEC7N zP^JEZW9VYGA5HX!a2|UF9P$+B_-$~>7O9WPNBeBR7Exk>^@8Nf?|b6X_9>Sha^kYP zFxEGA0h(;Enj|7O#BCwC9Jn;g)#Rmzn6WHbP>6^*D>AN`d>SqkO`?vOby6i2q=>E2 zd`}jJBpIE_DMU?NX7yE0F~683RZ2=U)j{HZK=m;*+_hE3A#?I;6+uQ<0XOu+%r*1xYbrAyrYqaac~HU8M^%j>}!(CiJX9 z$}lMR7XdUsF_z*Q&tN2r`kZ&%3VeG5`r2MT-aZd2evMqfWDPqk6LILhoSpbnfZCV(pplacGEc^ws>`+m)(F-8=nNz zTl3&^=cau-)S{E8G&%*6GL?m#!Jd9ahdl%#K_=JyBtrD$zS&xBbi&d(-5~BBG)QvI zI3O6Pj1~vLI-_aM`$QW5GjVe*xM=~t!RY(~n>(KlN4_WpTkgEO(xSWc)45t;uJwg( zN|!ZTe??4H*;kckzw;zO!UlZ3y8NN2uPFd3Fzvi;j8;vVB86M?Ypu<8b?WfSIS2X}Ic zymOVFE8iJvyQ;l@ zc=naQ+Zr8v$ns+yGzODxVAV?biY9n0rG|QC%$HaG1u^tRJ;La-P&Q4J_^C>)vXn?(KE=-W z8z8gS*^b%s75l^;klFiW2l#x2z~Jg$-ajnXwe+HlLm=b_gy!@uOrFYQ60Ge7A1@9g zcB;ftxTPi9Q@H6GmuV2Ik+RR;|D%-el@VMO1Qizfpu$4^KQ!3?k&O7i6c@=hD)VZ8 z8L%G`1Dd8%X<_A*>u8>cmG==HI6SsQSYAn^BB`gYa@}a#sFszGw2;VF6FaOzro}Db z@A3j^Tn2zCer^c3=(X;yVGU=hI~XelH|Fppu16%(zrDq#0dW9;=)fBb=3l!a1~lBY zaMPgKE0J=RHnfP)X%|CgM1ptT21IDQcvm^ny_Tx5yjKVah(5)SUhwLzhy@(%53D?JMTd@TCT}8)r*-kmp*jh3@U8X<GOw|n88-J4#doGjY~h!Tj=1(a@*ey6dr{M@%sDI|$VhAY zFd$m1%KZ>DSYmH+*ixq)&W_5|h}?7oNHAMdGCNo8Tf41K16t(gBwSLs3&o#xfYn)@@;$zS+!Pa^l)L*S-wkBa)H&$+-qugsKZgf@PPhCzKq zW1<{7r2~iig^I*X4w{$V8i~HqoWs69k1os2P*Q4xXa*rj{xc7?v-pXv zJXBR$MWPu15EK6NyJQp>CqQi}6hK??M&sN`BMY7Gz_=!1aMd{KL%Ra{Tf=%q&9)J6 zNZy(}orlO*d^;F9$XzU{yJ6F8<^1XpRjtW9SeFa@!Xgj~r$rr96Z=Dp&Ga_rD$+V2 zru{VN7s)+c8U2EiI}Qh;Eu5Gg-8_84v=|<)=6NG|Svu*U($*u7SPnu;w)+ z#zp;F3jm|6O@2YGxr>@A$6f*yaDV`i|4RW`t~XYi2t$U~91B!_)F2Vx{~_%igDiWW zE#dC6ZQE9tZQHhOcGhSAfhJxNz#EBPt$yrC=_~jV{^(crg&g48YjuO4`gF(R?Alr@J*^O0UDanGy2z1ECu6GAZDHCY-0_Ov}#}K&=Gy!bceh7f571cdo_GPZhoc{*5=*MxXFk zXF8d?{&uTWDneGokc=xe-eKyXAYBC(Hfi*mweaE-jvRvwA&G9hq9iW6p6x+RjF?A3 z7q$Ug+ZY@mNsid1V7s`8iIWmw(yj>59@fALzNSH$S(s1|HW+!B1WJd{I6>rZ-MMr& zxN!HcKE>sr_ljB=Sfi%R{9sPlx2mPAp6|ocC#%b8LR-E?*TPHeSTLcT`8qO+(u%FK z(}2dQLwi=HSKlve>b0=)C$#d%v+}=_^6vgyq=y}@m-3vz%NB3KM5ySdK%Sqt5@gI3 znhvPAt3}qGHl$^Gp&`jO(vhB$*Gy613|$#{dRT{_N0rceLu-4bI&;qM?-WHkp6Azi zNB?~rslA1PAd`d~_?zLS#va{0mrN0*jP2~MQXrYl7D+*><>Md2W#BvfGUsoMLHgaW z{J*tu`5)bv|1@R)dj}@T>KkYLTMdY5O)X;UsGGf6EW(j)rW;JQA7NyhO;o zt7tr-simMZ-*G}`t1h|ADA;tR%9cx4pR=gmDuuSA6wpia+>FZEZPs??yRfENVN;Vb zS{+C6=8v2W*$^CQU(I?-?5;~Lz!YOU9ZtAUZ*-cJ97Fm&H$DP*zjBi|(a$|fk#1R$ z4x6#%d)s0-&k;M%vj&-=6Il+=^Fxti$3X0oS z)LIbaPQHt|J_QdrO{=cFJo!=56xGmVuOJwXeWoC_L&ubCs!C;+-)^otCr_gjuESRB z3ZD&{(yTiG?a1tC(kXZ6INaqT6i-euO6F^%p&!zvxm$Gv*se4~1&nCZk$cPPDWRv2 z#R3VejX}P)>s2jV4usctIaLG3$*q=IJn#%gax49u%XZ@7bPv{U|^9#gZ zvbGvu+;kcT<^&+KpoxF}WBSc2)~cE?0`AM&bM zE7!E~j)BTOPzmx>w?q%Fu4({*$C?EC`+LS!83!|S@>qwU)=6_*mcI4hE)H@zn z#Rypos%%C$Ct-8wq*Mjao#7}Aen#i@eE?wIok|jYh(K<5C#Q1*H;19@Mi z@1|Zfz$Dt8X^>JDT2(c8&($2d>gMmLc@net<}NhjS=`JtW;N*e6Uro6wl6l}o1=J>%)!Svr?=_={-?K6E$nJzYf6$EubA)|~}Y zD5uFL{0c!4_au~lG`$#y>>7x0Q+_nTZ|Vh|PWx$&j&r{QIfM;;RaADOLN!zV1rDmN z)!^j8h-Zjs+jwh8Is=23W=>gTCSbsnCFc%UY)MFBmeOFCxr{8b21+gCPY>u#6kpOMFo70qZ*>5PSZaK@dNeu|}be&?6@7;Zujl6hb)TmnV zzI5e^Y!uKmW?*{gaAKG2khhi(QYt!HHi3`zxRv)!=pvH<4Zsi7t=J0xhqi;R2)LH*1zYL`cLs}q)`{hwyj^DlI(knR^)40Lzz;FE#xZK-7lEENzXYjvWd_sm2Zv%pwGMrc+%3ZdMrA;V?N;xG^M%B0=R0wdY+z}e@k{Fv) zIZe+pdvn`u5CKimH*bSALNZsPk$g~UPZ5SUM~6w~t#dTI*0SMPAi9Ow5CKGjslHqw z?e}Y#2qNtKXa=zAvo%@0JD;$He%nTspQ*fu(_8)!F_2cEF*Bpk&h}Zd?QQV)NdWaB zkuO{}i@(|@!kixt?PC_Y0^?nepXBOt9Iq`l?`+I|tNJOsYh!7GF>?=la^A|Qe$BR> zVs;##WQatj>2kGTygN?0x4TN+tqsy2->Ziw`vLx^bRV6D9=t~|;oR_H7}J!iQX{`k zv`+m1e{RK)X<#8&wht_;!=b9p#vq{%fBh5`uS5GZGieQ$2g~QQD=6x_ZO~tz;l##2 zc)Q1*!mg{+zkE3^!3Q7lZwr{hpTaTm#lSu%_CsN89IY%8Pc9DNyhrzyOmj?Bh*(?t z-o-L$=v;hk0uTIt(uU}k{BZ`3M66~|RiGhWV-b61ydvzh%iovONx{EPWE$gWAphf* z*ddkKxv>oE6}VtoR7?$XLaS6dfxc66_%-$1wjyhD?~txH-6vw3?hw{t`cB$&*kXhG z9>bS*Blj*6`}GgONPBrOcChceh3&i6mH98;E&mInkQy;QGAh3dD21EflZ;L|IC+fDm z9!>|{9B8mEPUr2-`q=*0xdi6a3I<|^%0vl6^Cs~YJ#O`XOgWrsE` z360RCn>q$;{vFOLuJLS6>~u0al%$f3 zvJJ#b{J4jzzVg2`Znxy((5O}rYsl>6nW&^1;Z5{SHejLe5;-|E*0A6(cNB*VSH6$S zcn%mbh=K6Do9)mjSFLVeF_b;5R?i`YAQerSJMA|j6Z#9fF>pxff6y4fVQ@=C4*JHr z6`bXeD|Z-qF!a4!uI8eE-c^5b z*i>UCosfmcbjzt-o`*VkTgQ+WXA zb%ToORuMBd!a-LG=cO~9yDXdkN;QZrIo-OgTY>3hL**mMC__O^4|#~xZ~^M8l>Ab! z&s&#eOYf-Zve~H5F|%HEanM$tvf9|^(ieA%t#Kp;naLq6B`AjHlZ`FIhml+ zywJdhajSX}pM9HAd05n`*zguW~JyWiU>81xs-fhF;uQ>EHb?i z7u#39J$_e)fWa$x%+3e|QH@AeD9gYvj!WQ-LsY%T0EwHJC3201Aiw~e1_2l+a>sto zAo!_oeNSn4KaU*c@>u4spZyaRe2Jrc_r^t6(ZM*oPVYB?Q!gWoel!9c8R)A$hhThS z68i-3p}#-Y&tI+K?StyuXaQ^8X{#L~(*P!r#@bv6%X4>`G({Ip2`nW~FhR)){lefB zC_?UhZnRUkBXY%C+)KU(@;BeT^^Hxn+}lt{98 zmrtm6R3}f#G#2E-QEtC~ToKJrZKyd!{sv=0x%z!r)m9vHdQEE3ZMn^S>K*b}6hC$S z!e$ZDx*lfx&_%u!ER0WSf6dXS^Uz<1NK2Z;>+q}N9_!r^kQr7SYChy#x`L$MIHe&tkJZby~e{@>70?zC=cQqeU{(XaV1M$@N-c&9ZOy$qK$WU6UW3k@4z&2kto=x1y{N#3*zsO# z#3SU6rMDyaPigW))?EO0mA*75<_0USR!h#<4apGetCsd#WkrYSVOy(2u$O3SvysgA z;ztcjqT5_bw<(I{II*(>-KYoHs04#0n$E@~hHiv*Y}~F2Y92J;z@33QaLO&zj!t(g zgFXBOqNJ@*pU#V6jp$UwU7md{N^?$jMme4~l9?pRhsxuRgG%c(%m>t8vZ6T{F-(KT zO6TALdxo4eXQ;M$MGKPw=ZfYNh#Ci-5m%wK*Phd*;8rg01_9w}hCg|BP=OO7g_8-L zY0^+WUv~@!8lti-)u@g|D*diQcxF+Z^{0lC$5U@sG3f(o7p;sNG3l-xJBE{i>Nd+n zos*4*&mr~%x^}Qw4FG=NQ_oFnZRU+}ds!ph*5x3N4d-Li7$-zD_4!up1L$&Jq={kn zqYPDrV}&MD?rAC2rhS&~WkcrDHDi|UdG!<-Wm}XiMPH?mO*09J97H-crHcpVGrSNW z4+3pVx`qJm&Sg4ivkZvI;%F9bt3&GzT2vT=yagX`296{&u#AGv^r~szU1QjtV2QcM zr{!M{q+f^wdYow{V zwmefk;KySMP`R?}J}sR=xNEa1sP`!ej>i>D;=pSH9t_sAe*U8uRNwW|{TuG(B0%bMuP)P1`K;)G~5ew=+q&LAET8 zu{smV$1krTTOk5!26BxJia14=2212m$KiS9jnvK{T!z_&fcD-JU|TjqWBQ#dnjEl@ zjg~ohm1#6zi^B4`ygp+N4K#F26ci_Ptf03DL58qP*n!BYP8bBbPACi!MzR!d{Ixax z3Dj5zbzTh1&d=2q#1RI2pJKKbC-x!Yec#g8ks^kt`hkk@AQJ7qIZ3#gRlTn_!)v${ zx`}g8F^A2!`$Dnj{K6~j!?@J4h29vu&_wUp+U;XE{pqgrR?w;51i!HAcR2O)%~&L&-mf{P>#T>?Lp|1gXh`x!9T$okf<+#9eg`-i_!h$;n*NR8$l~rW=-089 zc=eqk!m%Q(ygTx=8bbHzQZrDtGJiP1w7gJW4Y7Bioh_&&Ri2VSUVI4umf$nn){T(~ z(!*_wU*H@xb|Kn(*4dNMRp_<3V|J=Zw%TGFhBNDBTST%!Ipx+=N9R%3)lrA{lvMcD z9q`u0n{XEr=9Qo5D`e`g*818$E7U9B-A6ycSK?}$;$3D;EwlH2SY5s2wx-DoKhV(| zG%O_;MjfS?Jr&!%_c(mQ_vMBJLcvqDpSc<3k|J-yx0uXw%gOIS>9HP4?it>L+8+9s zMw>`EpV?e;6)|2XKMtqU%;@bL!np41)R9Axd4C=>XHRePKwRA+Z(VUC+AWsBUea3I z^W_bgA{yiRWLqM{AM6;+to>2zpL)ai-ce8e~-ZQ zox?nA8TVM)x&e3jp#R6^#XyRL_)T*A4e?*6-28v2pZ`hCkZfm%ErKxUx3}EA%yAm` zTMAoKT$8|c6)))rt*lHUTDh`C8AP=51L$g&+T_e#b{n6{izQ$1bU0YYT!xgif^tF$>nys%Nsw<_6`lEl274vDvmwn2m`PD7iqyqCctgbkwK@Uqr31u|bL&xl$ zgq5r#$}x^!_o+!ENYVujU88PBeZ$8hcdT}DbTE$gU$aw73?zqkPBtz@QGEjp*B zdyeG=F|yu0MnIQh~+ttE?xPG3L?% zN+0_5LA0S#fK>%SJqmghy8&F<`Q`xg(upU_U-P1&%W69@l0eLOvS?6UQ^~lT+6$TE7O(@xAy7jz6Q|}( zq5O7qGQodArLYHOP=gv*FvNo|#dp79%+=3fQhPzEQ%8kDqyF3XbNz6%XzXD({g!u< z-7p(dR_iaD$(~o3d92T0O_#%}79QX9`bCaOrN%`fsU!_hOHyo0ydCB@+i9F)*Vyvl zj_a(`f}_^h3}9JS@XiICZ%-KgKOEa#K6K|!H>pA?qUh?iFdKo_z#$E1EYIE$7c1j` z=HH`Z2Lr8$`-%)amAnnQsvS0bdkR6>nt-etBM<9KXNv()dUd+h@38+d0wH--L&d*U zC~&`{@V`o?_}@n0zmq8vM*dA-frQAMA2A<7@-bP~VDw%X zzHiHswDlVJog!XVyA;E|RApm#L+hx|$HT`H%(61M4#SB;1!D;96!!w`w7a4|=W)Z= znES#-<#5o~*9tJtDsbLWck9V1wQ)uP|2?5}!28>+kDoDZEGB4E!9iqh@6<&Bk$-K( z@6&O>K#21NMWB>^)2+{*6ucUSftopEyR&V!ZYGg5l)DqGNPKGkXO-no_jq8ltgZ6e z62P1JHfc*Sobvw7i1I8$*_`F8%TA{@6d3Tibsc=`l0=@v%L=;o+VXY2QPzz_b!1SQ z7*aE<$u{7n$W94!AcrndHnm)X;|?J!NTfBua8 zy+;P7z6ZNVvPOgv^ALBy0HC*Fc(#|6X0LX1{~wk`wc05VVOT$Y(0niQF#hLQ-^JO& znqJh=!o=3t+C$L8*+kIA^xv_)Hl#a>$fI8k*@!e5Dv}^ObNp|L#2^HIgcSRbF@1It z93U!ZQ_hiLW3owUcJ3Qj9@S1e4aBQAEP0Sl`cBoQH&+vpjR#E^7m*jgn)VWA%JyR5 z&ktTB8I=#;hs_GLD-|`DH^5b_HSq8Fb8>nH`!>QzA+~*jdpMB_Vo##F;`X|(GKpA~ zrD}=erVAMejy=J6c|)LpF}-dEbndt_F#1sLZHQ!nu67lzjeIfHE*HBcJ z;lxR=sf(s)O?;tD7{BB2=bG{sF=PpKK5scK`QrKs*oUo4nn&u=n!yOy^whc=Q#@e~ zx5>fi9~hMFg+KxNR?BF#NHAx0r~E9X;N>N!_gl-23qQMP5hFy`89@F#)vZcaaaM|C zRA3|3$rrXY^Ps?;GSVvti~%n|3@mP&R0xKr)Vm+McSU7{I$`__CD=E&vGF7clRv0Q zU9lWJLj|5k7@){!PdcoS-IxJ3LU>L3wz`h~b7;z=nqodfWTMHAqf-PPOk%__-^&gn zM`X#581dnSyy*T1hb%5qTKL$4yz)AF1jo`s&`BvJR_Q_+Zcd*wfLw*TxO@>dl<@vc zKVu0`B1qF^Y#1*se0m}+yIz<@lGEJ)O!^u?lXelpfpZ8zgxCNHwyy_;J}oA?w_HSS z$iER5D&;`3gaU08&u2?wz%eF+obNT@rQsh&^;N?st@8OyPw%uvquI45fnDm*kefeS z)x8()4C+@rC1xSj1;UdtW8mhUbRTci^^*ca%1!M<^c3*-pL0^(g zPP1+b>G-CV@$<%$hBMDP9xtNE6P|#%(J^eAYMj{Wq^@0DUN0Nk#y$(%5N9`zAA?{& zJScy`AB`4m33&k)k``&+u#a-^ie2zA;AJi~THsi%N6oAuF${}(WKJzyT&3jF;_MYq zT_~T;a|eWd2A14`J$;`pse@=A-&MFv2gUTx&3s0*R0DI680 z(wW%7`tC%po>E%yRPWjIREiCgIe{a9l_~8RpZ+1uM{&Yj?==8k4~xT%2Bj={loXN7 zfEOJ9<2my{cIwG2*tAtGRxIAXtqRwopgCm`TNz!7H~E;6!BbXa55Sj{vD=v8+A$Gi;U&E;HEWZzD!U>?!^qPlFyY4(nhW zh@93Ogo&iAGo~O)pobE^SSNQuWAoi+CYC1dfgOuFSIwq{$o)Yx4(`-qs3wI6XyqVeWjSU5XH8G#BkErERtte%M-TubVx z69*nb*6yJlA87G{q9&i=odUSL`De&bIT0fU6nOJGf*Z{Y-$4U%RrO+vk z^L&&Unb^N;MxN~%dWv&uhFlFnY}_!P6FXK+aj8V0Ht&rPDt==-T+euYHed+-0ZsGw zz@hj%|I1o6061Eajis{dgcj z02a;}>+ATqVfOY?x%hQ4d40Aa9Lvd~{VH&wi~X|WoiwQ?57nv}u2|xd*Gmgy+)`T{ z=+dSk-72tlt|21JDGRb8vVqz(Be`-Sn=_(f=`N1PzHW$(S@*xTsYw|UJd*6MrDE;7 zXzxE_KFZ?H?X1Bt;I`T0P8}yW3yuh4z<2sY8K?7!Rii1L71|)z#m>5iAz}!g`qQp^?!aKb; zCsGhbqj>!_jokdlpIVDMf9f+v4Dejd1}2&BG^uYC+R1 zh~SRYV;iCtWG@%;@RzE^fZEem>1;!09R&S3aE^8nAvRl4orQhX6Ev>CV(uu*S|OXs z1Q21-9kr7rS<2)CBRS~)Nbbr-B8xe2XOuhboj4d`4jm|Gnk`^;VRL|$@`@EQ^i?=n zpAK7+SwRmW;8}^AaKk6Po-y$vFu*ujlfp>M)+H6-tSP?6Pn6OPGR8m}i95ljzMd7E z73+;SpjNXTD@At9>-}P7K6Q^FpDr)?qc{R0mp>Y>%o!|MQ07dPJg9i$Och-+o35N8 zT+~qJ3@6yba#bB!$kk$B`P~-tfDp3LsUyrIMWUkb{+*ZaOgQ918A&^1q)^_Gwnyqt zDJZZ$Xr!W>-WNZ>pX$^Z&Kh~swlyrO>85OqVq%EiOcdqK7 zE zvYllK{U2?gNrp;FBjqcNR=E3bG`uIh9oZD~#_WuH(b+G1(PbFN5B6@cF|SQxYPKuL zw7xY*-bKN@qh*QB$s_%aoSW`TdU$U29IF=jrTNHk?nIO!FxvY)JxLhgB3RWjL>H{AFd zaIwcUV6j8D;bxFeaN2XPmlhVVqLJA%hsg-{!-aA*yC|`cr{GMTXj$bDW7AA0SjsWQ ziZHWq2Tu`hGxF;!H1h_A<4r6GC9{^~AJ)}b@`mff>AK7mjm$ z+9wI9**hn4uwTv}&?*)VG(4o+IahSW6pZoJ?m&x?dctwSid_H>$8z{FP`!OF>53|7 zOR&0HJaGRqGmdVyn-s#fcCI;DT>ueh#15}gRZ>nwwb8o*wo?;s?v)p8$>!MXzd^DA zUnD$&0d2P{184dP~Wdf1?jQwZ{) z?jDUPE#mU)toU!|7&e_POKjr!w`Ur=@CE7LcnAZQ+7rV%R=6N|DrvZfED5J- zSAGyVdsK*HRv68NOm7*eh5jzQmIi6T)I_L-^oO%y2>Y#scq@qe1~LXW`}p&Z*}ARU zZ~4uGXb>QH=T)?Og$pQzTkJU{NBu-_)SbcxzjQ)T?hj)q?f{3J&B7sb=lGL* zeOSRA8lD*>*vSP$&59fZ3A4N&=vH8^jev5`6|h525Idnp9<*r-G{gu}dVEO)j30RV zKb+xY=uLn0LjS%$1SatM=3xONUu;Im{60K*^E61XO0GNnq|s4UA`wNnofY0K+8j7RXUsmtOPC9r;v{iD)V_ zVRVd!e8Y$#qag;{!8;5naQ=u4NQ1o;gd(8EeGu0Wtl}GWDV9bnY|T2-D-@3yYj9CK zw&u0iAqfCq4E<5BOv)?qCHAG~2omXZc|}e4(UERu477GF)f(KRJL{l#S_UHrg$*)x zZ!ZfsbuULZ^$y=DY*IJP1UL}*Xeby<0%$TpI_Usj229C6Xkh)y7(eG|!%t92gk-^p zr8ye((+J7;1xdXEcp&Kr9q?+m2%sH)!3gs|qIj*Mc+s`~wMLs8Um`civG)(oC-CZ% z9uDsR193XnnYYZO!sr#kBcFd{ZZt7Mw+_^RrbZ(0-T@htkZoX_!Hl9g`CWu& za5E~63Xld{$#p}$Dkq`j2o7px2AM=kLT!-8beK#Xp_1N9iQ8Z_pc@1CdfWE{QG^6< z2M#bGq!dc<4JY`<8Sooa(s+`96na@3dDHy1Hn?^vW^UJF157)+!4$45kh?jzkQf*n zcpzmc>3Y!0_`K}q8=aujhSy&ChLQd8X}&S?KvJbImjv99VjGBa-cV+Tk*>0!*ZiRW zfdjl!3&TzmI+G^=%K~750B}$5n41IPX6(=-)Z>`rN$buyPOzlo4S9;OcpFsGE=j;vk5AvwJ$CjSdqX`ZHk_MZYYxr#X7R7 z$t%{4G+)W6BllSm?E|)%hK&hiL58V5gtz{zmkD{Q0yjmzC= zJ=D4qX=po?z8hO>X#b@2NV4O-@+-C_vd5m$Yvgb2FCXTXd_{!HL`Yd#r&i^{f&@aE z*+{4q3Q>wDc?vl|k(;D+$y$UX2a9Gt#te7rHK|0aAld;=FvVaLaTkgK{isSk1^RM? zBTr?^kRG*^V;!w8@Va^IS4C8hpnuQGBucWxG6k}B1;fDDIV$75jcP!$aGQu}d!K63 zcOm5Ueo;?p*F3OQ0obD5yQ&@F%S-s;)%f(Xxj{^(=B9}KiUX>-9St@9iRW-VIzgq7 zP*1UDBeGJ7a$um5RDvVC0<|Vx-rq|KRH7#Tl29vh=&5U;jM+8Y><@;X93>rnKUv-t zux0|P*Q|04iTcTi`PEMQX@UAFf%(-;`^k}9!%Mu!C%^4RL3ZqszO57r>8U|F8p$+w z)STb>{SwslZ9TaB&T376zeJt`E-i&k<()6OJ-_;ao&rD*>7HNHKu-;zhk6Im`Ub!3 z8h`lNTI;=p>V1f5(<*YS4Dcm1h`zk*M%h2eXA)otuC@%SR|XhFPtY>Z8>+pbddq~s z)}!`$&|^CJ2?yj0zC~Ty9bX3LocD_8t0?~=R(#7P|B+w#S|hD;z~7`L@xYgE83eq5 zcWNy4kSWc|$&A|dXet*>p%m?uH>#j$GNPP_Tk4psY@2S`4vea%=wVXck6Q|rr2N9P zpbcJ5M5^*fx~bQ{ydwc4iu^;nRKDgR|ACU?Ltr9-i&|63kq0;yZ`m$07=$0S|gN#VN*3JV7wJa=U5G!goMG4p2`!9 zmiBHdfC8(N43?lmB^xHz*((K{onIA0DgCA__NbSBQya4I4eUzioj9j;5DhuEVVrk9 zkXJe=a!=fozp8&?f>AMpYl3n7I}^vj2<&M}?hwMgd=Z0u$NScs^{sy_cKCO?cIBjh z_N}hjYEUitZ+boBmBakB`(eTCP+T6X(A6jvTPVBMpHw#lsZVQ#T=_jy zb%cxmcze}YrPO?-*l4AkYx;vZ>zQKuLmOQKomvAPaYOgK$>Q$}l|&7_GbXFV3|5MU z?gf*@?{cXQ3mTwLD`nU){@P7UP0M3wqtZ|vr@?o^WRAK&AnLAoxh>t{4ANKzISzW;FM3Ixv?@2>OJ$LF=&sZeDR#?Dt&U_*M& z>7{Q>B_PqA?XXX9t?;R=WOt*2 z0q($wr0bKqP%~6PqEAr4JU4HlUrwEU+=m(nr_v>BfUn~b%>LpjU;i3=PH%qZQhr`De#g;z z-=KQWX7cT({p3ac#KY`aM*GTx^6QD>8$|1o2=)^n1h>A4Expb(@s6bRrN*gSl4mK%OXwz-+wb$WdC(rn%;YktOk&gc2` zIB^fjHi`5D)oV)qB0Z4~$u_g<(Oh8tG5W-Ao!$R3jV)QeWKuqcQ@W|D*hQ(Nb74QP z9C@{(m$ApiwBQM!tE0H4rQ-RJL8vJmv#9W z-=XVfe3`PN%r#fKLb?K-4)5RrUFl3g+Z)F^QW9NT-U!9;!O_#gzNt}__r%KHEgT>~ zO{^&%rHGmk#f(7@|8(S1h0aU{0E(q&pH2rq5@-5nJcIK0YzE8aXI~e zPf52Ot>@U_RMmyIqsn~j@~9MLwT!?xnbaZ+d?QdMh%c#`Fp zNXcUd{0mZL`gPI=4z((Ji&HVDy(DL}N(a=X#~`7lk}E%5q2Ofek1w@9}j9qi7 zC>uVRRO~_v&y~nb*!T}`T{tM;<|Wc`DdymoW51u0nz+VE`_&35K9(giajBD4tpDrj zVd_aJ2Z;$jcD{Hi>l=a0`jAY`u9Mi?QPb(La-6Qa9 zNjnsDRO$&f3K{`beXlT^hy(Wk*_gH%=VftNG7SS`An5*^z|zc{fq%bxw&97)_fb@| z=ZFs}^=$^#n)VYTW|R6upSWZ&0*xaIlWfwtP+a5eM1jRNrWJ=b!ZYuIH7DtA9_Ok? zFY99XDwRu;EtGcI<5LTApGF({1)_APsw!o%vTG^h_wVo-u&jzn!JNFPR~0MMNFAv} zcTH!_n^oynaMrP`uu6oY-@|H$luyt+~zgBIm zMp;UiTGnmv{#YjQHeQjasy4>@oVK zm1(?iw5(LRzT-fSBw`L^gwcJuzL`CC^Zg+#sA zlSagub|8Y=bH<7|qCjMYAkOvBn(8Ph&oa-q^2yEL#~xKLVAp@OqXJC0|{C9=h!Lf9`Mdtb;++YTVazn2ya0dM zJ;%ku5ok5364Vm6QJ2s=!5zK&dA>?*I*76x@KjI%wps1n`u-A3w3l;ySd3lJjxE@C zbI*)h`jRIcD8T5=;|IYsE$aGN6POLI=oX*NsqMa-U@9u18y~m4e~;GkQYdz?;nv@_ zgg?XRfbl)qnku55Nt7dry&V;s6uDf<2H_Fs2AXlXF+I032-mtEg>&ddw`?8S{pJsO zKAc83=d1Wt$lZ!F78Q9lo~_PUwkq?fE9kMcFGkQz!TdY*=bOFiJ7W8@qOX#Mj`F1? zKpW)}nXy&8<>gmlyMqthnP72ILFf>&cQ;GXP{7)(!UjyuwrDoIQ)0CXSXc39->)u5 z{NsLX(NQKkNCJV)cVYg`alUN=(@VtapRjjkKpa;PH&pcYomTY2ypcsqM(LpAhVg_V zDIABbKT$%M$}0_Xw5^G6MlndmZ%D0(GifW?WnWos`zvr94xj4!r7JnHmFQvQqKPt- z4J5{2E&pl`MW8l>BY{5H<^(MLj(i`9=%)=@ zTgNmhBXW>k^NMk*k2?q#JXfd*jxO6hmvC?>cXy)sMC1Bwj(a*cn3;00gdMAuej)eR z1hF$1@4(J|J+%9ebNT(&n+%2Tcxe9i=wbQKW5V^{jIimA>}+i8Z0QADj4kX`zK6d~ z{|b@h%X1P5!Rz2wH;)yNb{ z>Hsf+jdW>K&-5{Em%(2-C(@<~+6Z!If1cWgWxgUG@rT*xW)ojbp)z^jaHIcXZKprE>D ziX-;L@{{p-RqQj>h0HZ7E$o~NRUzRZr?ykk{>aoEcU2zUrb@|mr#(_?-MU{F?K+N? z?k5uORS;kbD!gR(SGs8l@^p5;VEgXm?IyPQUE-O>LokUUJKsppK3d zMA>OUHcSnv9I|fR5t8|^NGCN2O&6i9Y$w*P3>h3Kj^s&@chEa+?de9@kAl1Rgauno zt1?{|s;f_34wM&#lSnB0dMrHv_J(ElhT(LZVUGTd2u!#`w3#_YagO9qH>OD~trS6Q zSUHc1uYi$PDFwQ_W&{>1*1K>7acP1b%)GaTwU|}@{TC#3z9>NaiFPOt5kH;{N$d;?$Egvie^}e{ILv}&K5#_VZ zTkKCf3L3$Jf>A_AjR(6Oav;HY_)oD<%|uA>JuKsyk4njz`y2&Pmd`h}Ae={U@ z-2p2H2`yN;oqjNMcPI)?Q@o@|-+s!?asiDzs&E>B5)@O+f1NBT_*Dl!yzM_-*5Z)F zQXw&b#+jiY5|7IHRB`I$D9>iK4N>P)z^2$7-e_Xq!xJym)_^?9s%VehRG-|WVx)6H zsVhI8MH&l_5YjpV1fcC5vVjryF-G)-E<^-@AnOt7UQ7TwX23x4H&^P#Me}EHd#O!+ zCx3tThaTD39z_598RAg$>z$VG&FdC4duzI-{~FHhwhVVrH{?huk&EO~))(~ICE0+p zu}2^Z?|~yf)4?;XP)#l7*GAirkA4ze$}@yP76Sg=5B#xJDNl$ygofY;Gw)S*&Nggq zuRddBlH9D^^{&GnoMZSb-@&qx&)%Z3K4m}q(6WwPu>F@!J|!;cmK|Bd`JQYThCu>k zm^jntAJ#B|NG4So#Q@;ELLNH1NQMn9C;K%8UB(TK~{@g)$Y^7t=vq3lJ zjnh3PLUQ#9>#&?}4KSQj6lAPN19&;Metm88;!{eRvF%^A8@Q{UrI&Q&IXS+rZyaXx z_nAA>s#oA^p#|+jJ@17>ZVau0@K;QdA(+*-h-wr!pM8peb*|&eSZPeRCGDjoOPw8%x<1aUNXKve=@K{L{T*?{BWsp_KF6Ha|B14y%JK>K}@@KE{ zc8@`~kCVLyBJPZ{wxzt%&MXW5Uf&j8k7T3!%uA8|3^D7vmSmhv-6c_%9&;f3LV_PApI})vnwaF0K#1m>CCk}Ahc@~f!~&II4>|stzgS&z@bnsi~go`hJk0lmqjUl z=t!o!9$-&)IGyGm!gPREnto1bD?MMFgJrHP{%TW+%Q2HhGujV}1`QfVOd!_e$yhH= zB6N*pXnHK5XQ2}fbzT>tj?J(ba19~SokYEDi60#`r(1uGnsdl ztkueXpew~D_bJopC(wsTE#&>DJ@Y~rIFK!l3j#->viU!2xK~$W%Q85PmUjys<0hFd zY#=YWd;BBe4>j2uM>Rz}^k7OyDw$T^6t&7pt-5dUOSrfnF4>X9$6QWjI+gnkck}h7 zdbN->VAs|RcYk%xApRS1?-V3?+iee4mtEDxF59+k+qUiMLRS{LY}>YN+qP}eV8kUbmeWw(5@%fCg2 zFlUK~4QbnD5eP8UE8S4ilj)>eD2rKjK1@aH5mcLcj%aBm8s?RnT}!R2!s(Iw8X}kO zqu`6SM0{}VSjLhd*#i;qp*#MYi8-Wroy*f!)o{4IUlQ^DsdBi&j5`c`V3TFj_(!46iN~u~`YQ zRqd04WbUKRf&Lz%kO78n*0bQ$W5o{DoJ6er5x2gVM9pK)8d-vEz=GA~bP{ZM7vO-j zXyRC|Wa=-a&4H@wf~lI2<`sl57N}*NQeAoDk^4OD3`Ys4bIySGUhK^l+2Rx9 zi~E59uuCXy;=8Uj#DI#tB*F+b)SsE-k?3L`hQ1 z+#|Zt!yF1fI~uKiK#)G(GFx2OB+Ys)(3s7bP5%RE@>#&ys|O|{$8QE>NUif)h-0!t z0#AIdhJ`8Rx8v1E)*auG@1ym%2Uru|A1N>Nfxmi0*~rAi#~zmL^s^>?x>8$O9+qf4 z0_KCYG*_+NcyiQYkjOK^r`Q7M`BJ8>hF)#9t5f5SuYpFW6T7WDEEUc(CCb=bJbI1N zYi%k{rz(C^lu(|Il+qh@qbCpuwQNkYHcgWXo}fS0ijx7I>+lKfYIWxSwDJW zT>5kvwVw)>ZJ}MTHvPA%c`JzS?0(`tiil* zgnxtOpFw3v>;lVd+^d4sxU<$Y!Qa{{k`2Uf2e)$_v75|QCkTUic*>GZcnAn*9xi31 z7ERtN(B5_ql4Oi&|I}U~^RsN}G+nV>Ema|xGI2X@T3~k`Qb)HYTUw&HHT@DC1-4VD zS2CvUMYnOj!!vcZzK`rz$!%BWPN*xsPfcc%me~}eTT)e-+HenlN!a9U8;1(YQ$*4$ z0F#hpz_&2spoXYopmpIO*tq&B;JrSlvCAajK5>6I7IX&v+VrZvAYfyfyWdiE!*7=I z7qFRe%LWlj1(`B+Cc1(ueMxI{$6n_~rho`~m=R0Co?2(^RHLiGVD%J{P>YEyJ3~%~ z#Uk}-YXjcyGKQ)>jN`~{0@}keH}XeZGaE2! z34?Uw&x-{J_R2Tz(CLYL&e5c};k&)B8ct#ME@lUwaMDDX!i(Ll#Wu}oK%H!x>vSP* zFz;k)z0Fkca$PEJ+>bcS@T@SWRlgORs%)Q>AOq&HS~Hbt$UCuY2Hb#@WGkSjzgCq2 z`0s9;04G9l@&fKw0gkEky!gUdUJ1b7w+}^&KH&47E zG){K-K8u^uHeX_ghNl7ysp3gqMzKjzjJj%xkN(sWpJmq)Um*gj*w+j*M9J}b_O0sm z8YM=g=u3gw{6V$+!1V59Anet+o}I<)`IKi)w^thU&1-?Sr?Iwnx$4o> z+{Va-+fNXm1+Y2_3R8b8go2p@L2WfxJAV$7{>Jben!RT&G|Y96=DB2}(w$1A9ZUws zaE3nOy7VJXrgeE^@U$OIX^yHhv8bAd7GaA|w^0@GBq^^U`r9g}bggl4Xi9KszQo7A z39oV2IuahIEUQ|)NwLqUfj!V~qN8K1u zk-Td*b^pzCEW6d_>F2d{Z-*3V9+qMae*ZUlC>?)}>(Fh}$og8vjM?QCsLjGQg( zY=z8CjI91wpQ`npHTf$j%g$+;w2-w?GpfOTNz@tUR7Efw?w-;z2MMu?G)pY@M!j78!8RT7o@Hc*dU=QMd_o^R|y?k5drOAMEtW2JbTVmgjbcyQCMyMw&ae^hhMu zOohxpUb1oj&_xzG^O?i&fr2P(IFn8P-^nZb~0lCMuG{#noIJ zmdrvnhJ=>lQfn;uU7z@B_rLQ`G_6YK&V+=jWJg4r30}-|!6Zl+(s-Td`37E)JZCJ7 zZrH#)%#nxac%K(EH9N)Ko215?EvrK|`7y>e{uD%Z55(s$a0&DRp}qvN81j=z!A#>P zLa!On;h!xe2f`_rZhJ(2+e2-rauRI?D>wVR`0iBaR(KlgDz5Ib@jFJ+sRxIDYQaFJ z5D|+pBUoG`QVC(B7?f5VZI$RnDQd7wHf(cCE(Olp+^{oo8<4&6e*9aRl#!h${D0*# z--&bovrPV_S^a<7}-G+au%QW=oHwK$c`7PXY~LlW4RSr|OX zvW;)PKt+QH>;C;qGvw@brytJiD<@x0;gNl*)1O$rwuqD+?)iZYop>ip7VPsQS}d^j z)tgZ~sIbbrYu^=ONct&#tWB6Jk6UQ8bE7Eo6B`-*VO^&-6J+P(s~Pa(r&(2>(w`@k zP%Mv?F&^+5X}-1Yp-DRteJRQ#YTW{y2zv3mltwYjxu=OSm#i@Pf?*bJ?V zxW)!avrPd2T6pBR#UOWptEK^mM?EbEnb7VBU;0rKArI6V&v-G)mhN^m&o_bL3Zp*p zp4L)$y|x0!T32D`SccCt^&b_2^`8||uRFWXS^!(46;RTE=bhugAnCG0-TcE$y?Wn2C=NTh8KNFvyUUC>;T3BB-f2CS?F~5%D>=0&&cFb|&mMccX z6$@^OA!#E~$wNPZt$;3gy%uLJ>!)-<=u|m+;>ztsYfEjdJ(=&dM@99rMyZzUg7zcN zTFK!Y2^c});SgAlaw|%K82t+=|D}iR*$Xc-6jDFZy#ux6>^A!c;M@w+Lj5k2CPUJ@ ztomR~&`JpLE?-4KAMB&#UI6%;urdGAmq(;qq8^HQ2n@x4u?s1Jl`ZQRSK>rH5y3zU z?}92@Q5|qq^J?2vR-XKoexM^zC0J-b>6zI;0$GGMYX!z~+};C53oVE`)&%O;04Qy< zrXNk9&C5MgZfFiG#kYwjpgOi1t)BQ6v*?liffP!M=Mdo#*UMZ_~0;=uz9Xmt0oz`m)nSZJobwp3%mJB-e zI&(3dT_%-HtgQKFpGxuiJ^C?GU^9jHPbDQ6T8zz8WNmOe_eFgDZ4Uvke;b;d}X=#$2yM{w}-J0?NT(SZ8|4pk>uVI)sMc- z4Mm!c60a;xwMD8*hkq1rO6xCbBYrI6Z9U0zC9s-6dHXbG%c2pza@P*-2d+br) z0S2g|#c@$USwCEIg?|vmO+>&2$%?rmZh`#36>2K{iwm0x#3ZatYsJ0LaX<2Bdd33W zrSX#$d0ruingC~mST117&&a@)t_S^k!@@%O7+dz*q084aPn8zHQ;2;bQvC~ zLdM0LC&cDfVps|fneQ28|E6~Om#xolkCle3J3?`eT z6C$Usw7_Tcm`drG=4sD(0jJ8vcjHNi=Im3Gu(rNg1O7bH$Bs05`l{TL#93mb#2BRohglBY zV1Nxav=o91<9~6f7j3coGAVI>V!y42_29s1edU}4Io+s%togM&4k!5H!aUupx%A!j zFdv^i`=7pj-_MCw-u|Y19Cu^TkQLojXIH08mp?5MGH}Zw1my||g?~ajuO{<)#cPmb zoA)YTD$WwII6p<6das+{7?6q! zIr`70$IupB5P4Y)F?+UEpc5F)4ag~^yHGm9B|9c;SsxkEX&Z(gf@-evz5CN$=wdA1 zAo0(_cvpfIbQi{k36<6>?i0+cz@ag#XykITz%U8{N3MskKZH7lVHKp3O~6?fJhCwWQxpSO=)z zt|`OX<8xRR5X*=ds+?lpDWHv<g` zX0vlu981`3xGkC92cyOezDF;L2Bjyr*j<)WA~|PjcPTH#q!Rw|B6x}0_aFSp@T0Q( z;GbUt3>}GT)n=TKWsi>DHWLw?Jsu6S@L?ISkmI3RVc1>NV7d9~zm!qV(eCq^?kJ?e zzpV(Y=?!a=vLGF|08&qC&^jk-?Ge4A#4r!2ei)KPxvglhg-mXYIe)@6&yW7#Yv{Uo z>3PPX_V9mG8fy5`?#i{wL zs;+2&>9YN>0YlSGeQEo9pXaP%we@YueNUE#Qw5kbBY;msX|6sYYzCYFAy0}1WihUg zr2`-i;64z7Enr=eeJiVxekdJQe$riKS*}F60~N-71j=Q38_{|{cB|;^tq}ywP{24u zLwlW%U&1J*yDiMwkY9;PO{-|FqGEnu0GO>ezG0q%3|A>qy-hC#sFNF4mex~*nWdop z(rhsosV3t|Y5A>>+A4)M#Xv#FwSoY&>v6BvJWr-Br8W99qDq*`IxB(9jM$vglORWbYT!^SenFh3XH=oo z(D8Un87r(r4__v8HR;$lv%ek-WDBI>CM{a zku|`+jw+f_g%rya-MOsN)f`o|9(>{jh3A~&7MHWs&)xH{S6@-gus%`(?d(gFu-yN!3t?t0 zz*_Dgw-3El0J|1-(}c@$fq6DK*)8oOxYtTKev!EmUtOho#WiV-=*7%p57W`3+ue1K zt5^19$~-niB1C%k~t?cP{clFKf zQTodErDmg_)_06cVOY?J0(Xn&-9{uuVCp6cza}4Y59j{dBA#kC!YCCfGDvQ8Mp`SA zjpIRrcdG?TN_WGxNis4wDwL}}(u>~sF~*~+v2lg)8O>)h)49l(fd zjV0<&1*dDh#yxSWM1 zuBGc-&VguHA_u$`qhTtn|FW-r|8+Mg+e@*_7!FaPSUp>smHpmiii#p!dBj3MXGxI3hh&6osBIJC#lQ8|m1LE5s_g{!`yw(4e6g=en&2``q zyItew0m1vg)D1MsCCam*XhLBCdZeKg5{>|yxN=+83NNyZ5+>4&nG_!&zNo8ohjgdf z0O=sk37DseZ=Un*`fQTL_x19DY9CgG3<@aYG{Af%+*5{EWa1XP_15=4(jf6JN4b2HO?Kk@+RfNb<{O6i=&GAu&qx!RQ3K-}To= za2Ha4{6iDin4O_fok~rH9e3NJG`$SxD2u;a3+-?h_{QydYRkcDtwKo#>Lj;@c?M)` zWj*Do&74aGlK}f2y%r_;sD}SjGIIZXL~b4c%CvUv)O>Rr1Pzo3^PHW)q6_V~M_kkP_%FzzMVb^9}sLZ}P zi3=(Clo5&&j(QQarTDh!AOlSrD!WpOqh49D`4L5MHE#<&P5=i@sB&^~g$_1TdXIaY ziC|J&0Ki}6{z6n8;6cnUon6B^cI>moef&V~F8DC`MrX<6|-5Qw9=qmM$2=;3U1f>%39+^Kk?TOP&Y^P8$EyDEP`^$?e`YVj`~O67`was%l$ zqqL8vBG@7kLymt5oUbKmZ)l7c3HrLZ7s)s?Y#9{FNLE}mNziuwG7l6nq-GdloJ$_B z$sI6l4e0oD^pZ}8*xx)Tba^7*^fJqKonfmeex&k~(-?k%biqKuE*3`>o-URvXYHKT zK{Z7`!s7Ctgkxh)+Kpw1O`|}ZpQ=I|rM$1A$MNqQl8bIR&Vx?2PdZQ(^EN}9=f|nu zzoG!iC#To|JP=ScF%S^@KSTjp6K6L&M=KcBM!c{O5oK`CstfoJ@Zy-2gvGP5YaB+NDv znu6g_wJ5+jR;%?gtyjO}ob~2dZJ?X8-VP_siM6sF_-6R-Haniuou(eSjy8*`Ivxs^ z{=km}UT-g4pFBfBrYV_vjkB{@YLbmxdvPa8q>d=`(ee$%wbaz4qi&h4k~sw;f0)*o z=gSaO(-@hYUjXR7r|KyELjJPR(TKxXBglm`^0unIGoGTS#wa_~)2%=dpdU|*VxO3}*tiUG33!qJF$h8-a ztOpn`p7~`ft|+I7F*U{>FxigFi73m{#ByV%`Vlb65DIQl9W+<#-F`LZl;dJuE}EmR z9_G-lq*!~Y5g2KfoC`k%*tjN%3YHh?82QVlNA#+sq_1Z1{Nz)`#9)BA5mMz%8cU_f zIWg`TKAKQ&Vj?IHg?AfzJ*;$FAr*)qLkwC*)lRbz;@aV+9x4{X4 z;a;Rqmm26$hlpqI6R=&ZfK49nX!JcHq z_nFOQ`zGl9`pIHJHk}`;U?0jj?3GS{a;es`Xe_0DksWDg-L=J>G5e>?z!ToKc4B5~ z107T`(J?sI`weN$drgYF`Onx6-+V5$TE&6WUPQ1^ z%tqOe8!$reffCJ8X*AMXXjc>z%4;5y`f>3JNsDbCg4(fR;|b;dNfGT{j^kX(L4@P< z9iHCY+j#+|42so2C<6x>pa-lM;a~`lszHQP?*6v`5hlg4=aLAlmDmnIEePhBWS%#KHX4w~7;I4c-BFdd`jffv4k!uQ8pgN%wunf1KTigUk0C(lB4 zT`Yf3KqW}<72VyH{(!~ASlZ|m3^vP_p z#R^OIhg)wgqeg$`aGGojp27oBf71)Z!usc4I=d5B?Q%5qiSObI`lrhDdAe0_gbQK7 z+`vcMq}zN-w;lObG2kJK^Q)jV!BkTgXXe??;6&PR!xlyCEy64Ks#IzE3;F_vLEe{C zLsivaB8}6|LE!pOs?q+A#L>c_K|r|KLEzG%)|g+TsU1e9{rjM&J-HODjL#A|LATX} zG|{8AAXl3rTnwmyA-JSm$tyH5iw&|-HTUnOd6;!2ky9SVI^x3^XH^~i&WKG7pn=I* zu}XcZv^H&TT^fK{KZDJD2v24DtwU+u276oDC|KNO>G3;3Tn? zS%q10g|o*?d>Oj~mf_S|6{-(zrj?Unr?M6DG^+2qC$^w6FNkqqyrz7`M1y0^kHTeO z8+50nSpg^fYD`0wB25M|$9ck}A4$b{Oq;6cVd#iuVf}Z3Fb~<~80REZXS|cm0mdcT ze)6=gnhgr(t?Y<=6Q7CO47A}vZJjs#8O^#mKK#@?e$LLI8%p1?J|j_a}^W)33dXdx>7Oo zw&&=1tBAG)hiUTsiIhz7xQyOY41jlwpQ2;^R=t>0N$N#qvv7Q3t%eC8pt^jJrs30^ zahIgkV98ZWw<4;=C=+6E;KLu zNi$Kyepfl8;$2p}4jf_PPgXUv6bzvn5Uz%3-Ro>#<^mgqa3a^9+O7hg#hfNtDyyS|(M3?(5Sca^hnzWIsD@&T zax<=(GHVVOdaK>ULz+bxKMRM`FS68r!F8kI8jOET%=Bd6>3&W) z1c(2}i}nUJ)8XggI{C(=p*~`SP(6J=G<;nO*YP%a2JBp42CO~$-{vF**vPp3pNia} zBTV7!;2W|)5fCOF^?NyBTCC#ObQ$+!qALMWVo+nIE4%%Bb)W@(Awy;dqAQ`pGTjZ& zoB2X4mDLEE%u@1Pn>5p;;zKiz9> zK8T-OUbBeSG2{!=?PWA-a&PUH^<3@E@UfHgjmlgj=f>(OaFY)|aCMJvCWGwz)HHIA zeT${9q}AL5u-dC`pNa7vJ0s7?D*8?=6zUE+C~z%vP-8Q&2(wJiaG)h^t;Rj|RIXeXWy z#AYa&Ri9mx_TwbUo0+}`b44x~AHksjJK748gS?QwsQq*!LB%mTdvEW+{mDGt%ikn* z{T+=5lVQfa{d8%~xa~aplEdeNbj$I_Q~RqLr2Y|ytlV8a{Y0Ih-Wu~1lpLa6+TmUA zS^M%9=Y&-6tHmTtoYx6dVtLjf3(M0sG2h#}6ME;R2d?%Uotxw5NBNEcS%U2zgXc3p zs^C(!Cb;o5!<;NcbxEmaCYN$b9955k%Fr8l7&C9%$!~Yh&GUJ0#vAnx@85 z@{d4?%k+J<$CBXthA!7Dl&(s{S@cH_(wB)o1HCumE+_Szemt+1oH@M5z>m21k)3K^ z@ZCxQ5ME(qJX2IW0~)-7h^U#Kp_M_h(iG?>RjS^K16b$G$YBL;<{FKl0vx)s=Ea+? zD0s{RSybf6BD9~;ZTq+k#1w`3KV57tDIYkR(A+3_3ZL)ptWVYCc?a}t^d3-Ts;)1H zCVYQqc72V$sz}nYH2I|Z-e!ki+r_>AogLE3n?h#3rO})$ zf0Vi5zk4e5=o{Plej|6o9~LU~#IxQ0yYvd0T(e)z+s=LyWZ{X#1Q3+s$0%ZMnk}6> z1q+(tC&C0mV5TqO!S4dPyKKkAS?!O=1yvXJtF1-kBMzhk+W=u`Wz6FB8UKTOf>&3c z50_)7UrAM22a2G5xX%y_@bALCU6a~mY7;Pk5qcO8FLf}w7wZob_Cc)2U1$gOqH?2)!s`FJbNK)FgecqkO>@m z4t+bD^X}LaffbN165DMX4MRd`;i?(n zL$Xm&F_>)$5ZW5HzVDlJ$wp;^KFz_oZa#0nMYhX>wxJXRcGxZ57? z#UY3`ax`iQiqnR_|HD{*8d4-42=TERK@g)GhY=r06Yd_(BnKED-HHUe-^nYI68^`0 zV|-q6`t=!mdi&bWUa`3z!Rt zM@3sUH>mfZa4|S%!I}Fdmih&v9d6DQ>;bZvCMU#~OlW5%&o4m{)j`-V8Td zT<@jc`MgZ_MIozq8R!Xi`H;?kqo=WaGwx{rPn=on}*y8o}no9wV}UI zUV}rp!5g}mwUg}=9eA#a6fa`cO=5oJ$V7yV@NpSTvsKuhRYY0y9@fe~Q`{P{^gMTkkSuoX&E#5kA=3L^ zFlV~yEFe2g<{(jxSMB>{V>m?7gfi- zu|AzpEtrWSn1Mp^LGeTJ^#g)BfLi8TJrT}~k-hE}#;o;Med0h!vJ0b!Q=Vmw6+;q5 zUEhy!$L(9Y14Z%FijAvIJwG+goBiD-wDpX`nc*tBE6p{RH^x*DcX$K`6{F*`Mzx>X#6wI^xqxya7>b&hwaFK2H0c z-C0|!Te9D>Sg+>b#CCBGf&qy+#Z`$M4jr_s^mH-Qr~;Lq%fiIOz}xQ79-Ub(gKE>H zF9XrJhZclf#{g@nkSd+Z09Ldq+&humLH+RsY2jJBACD&~LWy`6+OS{@VFOHb6c4gC zA?Y_<9KNQ~+6KDgZu7E1n&AwEIW{{Xp*P*A@EaNYLZ-cMlbnb2ZJAEIL$E~D#7nHT zW!KS@Bl<*ASO0l-l7d#?H+M~pUJ3hmzEMfANs{$a!a#_2d>`PGjbd1xJ|yj_=qrc^ORB9Pm}F_j6BENB{ck?2wY4MM5l_JHUg$Xam83r8D!K||R zmZ+0SleSO>^W0?cFTOWdm$)D+tzE0$=`yuU!U>ce>ZslA-2~o0p4g_Wn|i94PK0JY z8yk+^P^*FW)HbM(92MAAKujc$GZr-hfERR*L(FvlGB%qrP66;W6MHxjoQ1s1Z-3%d zjTU<>f|{nbQvIdtKq{1e)uWLGt0ha5P>@Tu+rFuTt47HBdUYhEEJF8DKU#Ps7kF%GWW~6nVsL47( z`XeF|=3Z*z=hBLI^G|mp5nyj?<@W!%G%jtbVyYc-_Y=Rg)h0+i53QWO%{y#Hs7LT1yPcGM3X|3!t-JQzQPkXm9?}Ip45I_sRTk= zaeOd?h%vB>VmY)**sF7;7)J939g{^2sDLN$1SDpZOK6=1aR5_7!rERZBM}6#^T(0} zctH0f`Z6I8$b)Gd&Ur@qA}(IUN|9uJdoCq#pgNa>-%u)X{wj7;tN>IN|?0@b!lvA$9h=Da#sBcCy#%$35QA~Ewk8M(bir1Tv6LY z*4M{7-8QfPsd(r`H?VOW0>cbr3_?gm=q#jcqUV#nRFZ&0R~G{)(iv_oimVu41HW#H5}9g3->| z@U5XmMMvAropcSRWK&Xq&G3aDgYC4vu7zkjyZ?1Vb^RsmA8OR{p@asWY}T_I+eaue zF#co0Fm>B=W6r0bwXPodwBRskS6rtpp#Z%}t!)K)_7LRcGcAx;TVdE4 zHy+3Op;6$S$OeoH7ug(yuKcyjuS=B0G%T!E-QYVmigNb#gUsBm@vz#Cc0SP)L>HFU zCmh}#zurja0#-Fo*Snnno6p^AdDYcTM=S@K!7JvE>9P}*s+|h82h=SDLAHC~uAF3M zhAhf2ZS6^%$K-5V)*L=RupY8Wt!Cu8M{z6<3Vp>Ukj`qw zj^I(nVl0Q9!{|`No?L4NCNsy523iaQ1=8oW{z^-tnUa_$fni>XZ>NLqGP8Q~ zhw`>e{NZi!9e0X1I`km)k=@RIrivE5p`Z_l6#^MB>u$P_0VchjQg}(xDStnw^?y-n z3*WCB(W(7n=8Wq9%SW9>W3MawjL`oX&Tw+n>ULHrR6S1tR1j^z%+UUV~s<>|nm*;pa@#8`(gI zxzH%Au`MQ~pv411wJ8OvR@QnpXj7h@0H!Ao_!^Y_Om1B%-{N_&>H#7YJIX;}AQWA`V z8oWHc4Z&aC&GQXVqjc6ZvJnkY3q?|sQV-wIt-ayyWOs287t-foA8p2RCW(}#68gV~z*y9^ z3p45iu12UbrK5kysvQ{`aOF8r*{iVCPt1X&i{$(35jdp zF?QbznM1MMvE08_)(L4ttRWLfuwoo7{0Txx0u-vl{jrtSB=os?69ZisW{1@gk!E2! z@|W^u&NgTBlj^M={Oa|8Cs31lp9p@40)Q$uPq9)_!LlA9Wtk<8P7;gra3N-CWPub z3s?*P3_wvUHtZ7$v>bp=DabY14<+EROIGbf^E@;+&ovzh#lx#8y`BKr?D)NDFsgE> zjX%0ZJX_@H7==x$HbSKi?g2-o(4iLSTQyr{J2Z9hGP@pg_n-+lb$~}?0E(3Ny~e4R zOe+-28L4E<{LPGMn5bmv+Q2G1jR)nT$y8+Ipj-on_7F-XNlvCujH~$5N{}Hr!9T^| z+nS499`85=?piu~;>_j%8xOFk7OG<$Wrg?)wcKVXWKw~;d^*C+xttsKkgmMk%G*?< z9#oaisP4Uc4h5N_(ia6oQ%qjEd(o5Md8V%Wk~O*Y<(k&HoGc_`3}9huMWCIv%%nK6 zJfX(h1UVg{?=YNt`x<_BrP`G2#r~|PUvBDEr4?#N&6MN51?hG96Z!p~vPT}ei1n0n zDGfP}Ba|rx?fl^-_YN7LWMF>f)T6ZqBR{yPSqSa_whiv-%0e|hk}%Z>bN|HRj2p(r zU?e4T8Ys`2%V*Y|=ji*RKh_#2wQpKvqA?wtpjxV46P`-}rh~wE+zs-l1JfT68DT0u z0f%9dhfkxS>UteVS_thSafFTGr}a>Rk1NZfz8hZtz)HOm;QeAe`m=q<|ChwHcEy+Aa;bI!|#=He1sZ>ja7!H)*UHPwIr9sB=lzN6U?yF|ERsUN_Z|sHNTK4$w>UI3ncs|Gd z9@3-}lpI7BPeklupq=IQmkVc8m629Gx<^4C^U~<`D)}ta^jYgW3Z8gN7DYy?L@Yj+&tISX#bTAR0W39LDA?L~XV1=i&+7iK z2~54L*^|SosnrNzIWDL}5hH_+3$U@1u1h+1ljrh)?;ob{dumQ`<5hi6QEadtRpL0P zN^CuZK#7NEP6eV9*xZ%@W&1?=~5+ai&ni~enn|A@fM&t$+ zDQa8S;k|_P`x5bWdGbX@pKO93k`2eYFb@B1d1wyXfN!g|j`id31ha)XrpB1YhMcIaua1+H~EkFuY*%zc$hYKCj>@Tzv_o2a~N9a3l z58ob2FB%#AM4o7z|6SS-rZ{$AuogFVkMP)>fbS*|XJvu)Ea9~v}IeYP0q7fKh_oG#hod=k)vQ^3L0dmX&1z* znjNuncJTYw)qsN2F$$zg=n6V!QkV`anx-y|IK)Y{kA-g#^XonboNk`XJW5IJ^F_1*`zN_o_AC4W)(X{GTy?=Ca-y3WGYLx3lnzay z6x%dXw=&u^)1Z=Zl7)DQ;u_qkeZzNYV?KV5 zg&7uRr_QA4A3z`S8IyIuz$0LnU{hn6A0J;w_L&>LU%#Ias)Z}FfO;TR!x0S(o)bLt zW=Rgv=IG${EY^^FsUpwZ%LM6T)}v;{m<4^qrTPP$1&YQ__r=?WTH}3`9IS2dD6Xs% z8>tQ)2V{<@T$-vY&W$dh6yC&JDrs#{0(NNhDlRz+?l(6F^TKlw27fUg+sm|p>9A-K zF^FOymjiC&5!r`RHpUo8lTpB}Uf)_>Z9dv{dFNFGSSK#r!!1k$c(puQ)k`{2MVlt; z#(t{|(U=l?5$Ai30rms@8bY=1;?FM16m=ke{1nYJzN1^J zZ9CKJT7FqXU}Xaq^eVdal-0Q6VmYeLulqlYy;F2$VVv!oO2w&EY}>Yz9ox2TJC*Dt zRk3Z`W@X2=ZQH5f=G^W(&gs4nr{C7&8f%R2Tl0_ko7FluiASpI=}ddPd{PyQ=C58| zs>)&HS;)z#8<1!Eg!>#@Q_O9Qnl?#67@^I0ElMSN-`UyAmdhJ!%A>V7Iq0v3%V3|H z8J?fafq&#T*OufC-y=Zk?jEca*gM)5UCnB0Nlhgc_#qMKO)c)5S_OYhJ*Fom>NRG4_M>gSsR%}8Xk zOkoDrK?o;`G?Q?$TIppbsk*?nL%*EesmQrNL00_|l8JZyeo;8#HxfoVt6oP_9Qc z7dL4hMug7gzr>~=r>zkBQy9O8qrXkWaBFDZq90D}YTH?M+aErsu6#d)X)H^pDoDS< zhj=CYN<8#kLE5nS9cpeK@HSG~bkwk3yf{8Io^GA@G@s55rTGxhvzsSq{@;g(ID(mo z!R`8M^V{h7%V<~b8BWKE7X|w3jp6h0>laLspIa%N0pxd^j+UB@Wg_~0)ZPnNN;>{B z!7H9M`i_|8E;PWqYBzO%_-KTQ(sVb^gQ&7a zp;J?i3emN*>}h^EhO;YJ6S7)9GXr$XEHJ5H%EXAq6NO(=H`mnCl3o=C8g#m`$2#}= zUQtjWg1;iA#L7JZh-R?>o{k*-n1sFi(@#*oh0*hLb-lTK_m8LE6-lC|s!YQq$c7?< z?tp6wa_92X3riPe_B%d2yiL;M)|MV?8|W4!bz5b>9ThPiGnuTe3@5L9qYY;)7f;jC%eWV-kJUg8x_Fxr>>zyO}fNf3SZ3 zH=p1?D#EHHEtfUbub;zE2F}2W9VaZ1Bc!<*kAy0Zj^P(s2zF41?A&XSLb$Kf{BXR4 z^6aAs^LN%lIz(SE0kl5d73L}@9`a!Q!U~tqF)i1JBZHpLZ?=wWWFm`18e|H>seZe7 zyvQ6EK*9#mb4vbe7uUidAApP(=JIq+hp&Q zyy`P`;pg~UD#G=sP`{i}8Gk@D9Pv)6-5<}iw1ljfmwP38c2{k&H4eJQVVgttS+$U& z4}pvWbaGLA7+n$7>K9_zWDm8NsL-iZyvT#Qj-2w_L76^iVl*^^d=dlwC>dN!~Rr^n-+_8#_>wzNjY zCy=L?N*)Gch*^FHuy+&FZg0t`Uf%qkX)-_Web@O6K0vAx3Ju>0fOw`U zWH3rKdx+HEA^Nm=jYeOS+E5uj5_fI!DG_B8OhRGJ)6An;?tH;?FIm4VC(|0_YRUIf zHWo+%#wNLzq*ine?~ylqib88a&eF@!N6vn`-WT3`i zkl;bXH%}ME(jd2!B+&8J&&v2C=}|?gr4S=#IIESWGoG9TsW$C>(D5mXy$Oa-4PP_vGgwITl~oeWY%{vb>l32h@K(Pw!@v@L^v*XtS@W z=KuBD{m=9Ce^Rs5bX3;Vkvz|%&^rq0ZS`3D(ixCvuX zb+gx
5%Dj*zV?W<__-FIH$wT$)};ae~RBsqa+TG@=YIYJ$s?8SR{4&;cH)@sXm z?YVm-49IAoW+F)D;dXq0>=0AWr|;P=+3%kRaucfv=oo@ zYrAItPY)MA&iW1?>4G+VjInynXved@e(-oLiZs&oHzE}KL?_)jqhaTiB@G$8XU!o> zylJ?MgWcK1)9`S}pqm^yRi{(8A&z95g>7zg>}Md%OV|{Zl2TZU^kq!{JZY@q!_{vPZU)cf z+|eSR_AR@x{Qp1(h?H@;I-A~O5Q7bEdO_x1G5hC`uDz=J^=&GfP{dYV2T*-JoPG_r zz7w{I5?JD3kh9^vO7u`9p*&t zXu9e*G)9KB%p1Fa*fe}I{rW-ri5E!aCXwl*yAl@eC<@l{_cuwBK?5x?<`03+2$vMq^!WSaZClVxh` z>&v!bVNUqHN}NBKTmwpft3j1Mjo9#5IjN z$i@;f#|nDu7|cEkAM~B?eB1c=h$DXuUytt;hQJPb>jH*h+X?#mDQh1Kh+5TYt>24$o|Wo z*(_{$K{gWZ%MgzMK{qN^CXL@_r0$gx_5ZR zL%Z|4R!KQUru4uKIN}idu2%v)PMrv;cekG2A7+paREq58AwIF+FcUm;ho)*aCs+Ik z%u_ug7oHeQE4o`)-!iS70g z=t#K5-Uk@HY-bs{Z0vvV!tV-T&GV2LD+P8@^S;oWZDvg7&Ka4p%h2Zj6_lcPDkdhJ z7Qk`;%p~o$^VS2q0^h32ySYhxqug9w!MfengZ{l5l&au#&o{M5SPFz<+2R+)Zn0^Qt=lUU9G#^2QYr)N=+?y0*)qMd5PPN-7epB2PsbbtOCG$2M49_006MTOltHl2$_51QD$0AE zjLvzX$~>r&Di;Kvst?IvR&|5MUq&IguAg|hn~?HzKi7pc2Dw^}O(GN4DTJe0BVCet z*Wo{MSJ|L*uBl(K;Tpq&vegNHdTV=-9a|kJ34DKD1wZK^+z~*G{Tsq7ij*6GI<`tw zP>C?OsPHYng*E_97z>fHp=UUDWI^e?)2++Tp>yzNS!)Xq(m8cQ? zJ^BL*(mT~Gm75}Ui`cQAycXtHLXjsgB6SO%Lcs$dV{plz!zd%af!AU^?I+XJ>Bx3RBOe8n)>jL~w@c*jerZG9I*1k$+ zsju?+{|fc{PYw6q?>kv57gsZTGw1)f_mUKt<@!lxY($PobHrV!F( zNbft9nea#at{!!sxR8k3N-%nsv-Jbxk_8LnzMsDS-&)003<0exwK(@f$p=wYI9VsH zYp2g8s)mB~8aAZw1sEF*J(91zz+q6gkhrD0_xz~!$-SQgWS!$HQ^X$czx3uuV!@!Rq9;R-A zudV*_M6z-A>B#28aZu=7+>U8jsH)I>VhFi_SMEMO#{b@CRD*3p$^2?DzM}mP+Km7I zPxl{Rt10!xN7oE}NYH{MjW=z5+R9QJprtICFfwZ@JvJ*})tEQkn9z)pzDlxtu|KiT z!TJ=<%1MmuB%-S*3XwvS?^cmZC1%O0ill*t{O!qa>l55h)Nhk_j!sTFL-kI?`RZS` z%jOmLk8-}XC z=mD?CihTQ!b7kWweBNySbT%LUhW@7l2_Tgw__q{$F%rLFlx7z8n3TumdVo+7A8})^ zyRAckBC^fNS4N8EZAd6T?_8R5*1V%Y4PH%y&8}008rFH8GqQE*tUWzP*@0~yjR_{- zL8-5>W@0AWtjPh2+I-!-VcH1yJUoI+>$JzN6b!kCzRN_iO~|wEhxxkes{3q$v=nES z`ip4l0pMOcN%`^jsl#xaeEZI!4oT(|Bot;kTP@(2%M3}yp5u(iAKYE5jQ9XH$?CxE z3BIW=nY#O8bHh(9KRv>VP19;t^lrWQ=r1jVoyA{t-h5#3GTka`$`789raLmz0{`ggUh4!IO;XWHRcUm%sER!& zBeG=O-(8|(LG$ruo8)ZSGI9)iw^DpgQ5GA38&?UTG6z3X5AA{}4VJ9=;lt5Pk+lf&al6rIs;n_l`K3~*S*QktYLqrO;uFdS2+uZR1 zEf7v$UrWpAg>7QC{IIIN{*QL7YbC!mk*=9dxW!Akq!ZTXhGR<^WiL(Cb1 zzLE3y8nomIBOMroWo(6i-3;#55iWZX*EDp&?-bOpAqTAc?d!7QMSL2Z4Y5b3p75CC zhd02&tG|I#>B7B|Ny#Ce=c+sni+kU8Dy8uK+}Sy>6X>Fmn)#Dj_&6EqvY<*kIm@U!WOO+JHer7~ECLp=amr~OGnFzO*uxs_tJ(>-4uK*GGx5S>a6Ylo|`29bKy)bY<^c+2`0KQEiK^M)6oi4XU%B zIGpI5*uU_0i96G^TftuF8M#yCI65KSG`f}NUkZXexX0~0g8&ddC(k5~T8RU_TBdcC z?XTCN=+|Q#-s`3f5j<(O43lhMUGl$B6YCTF3Byar1odvc{1=5z$UApb?~b=Wa0vF&`;PDL`2tFZPdW{l$EJCzn2SlTsT2CrZ;(53|IGKO=v1CI z%|#y|93|CSymp!PjkIZLhI3K?CzpLM&FK9AwPP0=ujQt^)Fh=;O1$xttQT?hIllTi>27McV4UeY;lyikKOWE1-PZ`Fn45 z>(ZgoBon0s9M!Ts=L#yAd|>-NT~ZEG`TdH2L3(W3W)BKrIfF>XHT<`^H9(VxAK#=6 zmAcJ{!{t*%x{cCkc&GM0|b1<}XLEMuC1 z#L(g?mts%syK5r0>}ehtG7PZ=m%wULm5t3g*@N0LUJ?cKYo@i9&BW~KNhfM4ooIoe`D)Kb zp=wdm&CmK!Tdxc|u0D1$RSGbfbqfo1`IEM#Kt%`sR=if=Ir{yFL z)8;1M9*-Yd8I|@I!NR2E^}K8XU$>R)1;>ldXcbTv>be?cW2*E!D?A)d3Y|9BboNEi z@jjluWySh%4R@^zPsi+;=leIxsw?&lR2IiwR2IfiD6^>$4caw2BVOQZ*fpdWNm zA`1#)qLh0neVX)h40lP+8qVhyOl?Q^G`tc*Aa##xz&z7+Brriu`({RWRoOixA-MIL zthWdhn&=YZ996$6#)+Vxmm%UGJSLR+VJk6MKx$0)^K6ww3BadQa?Ouxyx{B#`eivh z_d6U?cb}Yb-qB|l$5V~@Zi)Ef!jP;ZBi#MiqQ(>{F~Cz?d4;*1hJevyHGGA(Apww2 zody>me-#CzDJe-d+6vbvR5|mfDp9(81Cj_+F6%Yvn7(CKH}AKbQ>EL`Z@+VHb+$=@ zT#>dtAeLnVx5CP?ib4c|<w7&6D$yg?o*#jk7UI9b75M~%vm0+t{P+@Wss2Yf$ z*2oaKb_vW1&y?YH;uWy>3aOA>oXl)sjmt}Po0;tA8*6~3TasSI&ZDX5HV;Yu&}ks3 z%8OorvXMR2qODZ0h>xpV?+5OQ*)F+sMs(xnQCRs<>K=V@niz>I?XBopopwdH@=9+_ zvqrpeLWE`?fmH&LEXDa%cMjOLNF*|^cnS*g#T(8!ee;fCBWKHGl?uximAa$6`=)zV zOHF&(Sy5Y@_jC1B}<5{*G%f`3->*TI<^;=C{!+*+v2P@udvFJI9uE4Bw zVoIB+qI2M6f{Iak>sn!mdLD(15352jeTjA$dfo*EzAN0XvP{Zzt-xuWe9by^=9S5x zdG2&yI@O8x>;(^jp?9X9+8V#woKg?!DgE5IWx8`1wr_woul!Tf;c90O^f)kHG&@qb zM`6M7P?8|Ui;CcsQ(Me=!G1RGAfIE^eSf@pC=*lO8J&K<_Yf4<*d=T;i*c$m+V;l_ zdq_^)rZwj~6trCX334+%Vvu55?U1unUXj_yN(tugNypSaJmzC|Zs{CH5AK&E)iil4 z0Y7cRQSMD6H=R=XRsA|V&gMo20`??s#Ps_L<;v0JgcFx`eJHtTX0rJdmhD#m!=qk& znT1iu-+O?r%`dlyb9WzKPBuzfFrO4lAG23WaoGcdH~_B2R4R@Ix1X z#+Y4c%q=OwZvHe@!$!ZZMVISckwg+ekLAUm03F~LTo;D;^AQh&gVDTN^>&7d_vKqc z%k?6Feb6;Bq^Y5z&XHHU5AlX0n2^JZ_X_FsunI}NOw~yx`s~O_T~n~4>9J6Sn)j01 zic`C}kxqi7X#!{z>kwC8k_FLn~)OACI#jPq$itDvSqjq170G6Kmdm?abS2e6i=MjmI@pQ; zn2doS3r(+EfKg&biliMTMR4G8^p?T9+YuB!Rp#|cA!(ItDxoiW*{si9D@E&7f`GT~ zT5|I#Ym?Jq(UouIRUywJd0xLl8q}$=0xH*E>K&i>R*w*IO5~}JK2(>zOQ+?M0!Cje zH??D5cV7a`Z`Bqi0a|Nk6NN4+#2)}n4cjp(w~9{u&!lOtn%)Y-)oSUd)6@i4dd8Hx zkUu(a?{yB@7Gr8}?@GkW3G&OJQU9SPXqN;N_d_Fg3HXYCb56AEwq|$_8$HaAzrECb zjsK?KVzWnp7n!TkqqcC(S}V{#z~wz8dn9S8k{-}&(#d778lTbEFfZu> z8u7y`rzD&R$>ty0&ZA8w$}L-`(466s3~;q%g=={wu7rDPF%3P-YgK%8mG{oJ1H@nP zq$;2=V43@l;}f!MS!P1Mqqmv<$PnFDQ-!RUM!{o}jvv%<%i#I=68D^mN~ zU4QWk*EZ=ND>8h@$J>4$U+^$L@P}~lFnpIAHpQ~4kk%9m_qvb`JkyxLdEHud(EJ4L z8zoLL841Vk6$jl>O30rwZr&BKfl;#jyGl)$Ii~gj%y(73lLmIJ|(Hrzht8{A@0%>e02q`r=&L$q|$?%L=z}H6-&c z!$*y3K28OEm>KYGty%c0mO0(xfi#ANz!$ifeXnyIK`Pz7EYPN*$u-Uua(1zb-h1jb zxSJ2{nnB?QoC3Rgv%#LoHv!3ogA4$)P1$PcYYhUm3CooBdowZS92$j_ggSnKf5@vU{k3 zRMh+RyBje74#gP)&E5G%XgGGK793`9-mg3-Vk{&~goPJ=FQ0$tj zExtS=b!n35mw5=|Kow$7skSPC-oF_i)72f*w0=aHds~?tj%&-pWp%ypvHV+7YzFqE zKw8IVmuD)nynp;ILgS^d`k1eWXDzr-3^Dx?3f`Ur>Tdtbxf0Zs6qUqSM&$njr_jWy zJ}LU(f=_eVqux7wkTJLPQaPG1h0u(N`j6b8^w88d{V%T8NXBxs7^lfS%F#Dl zdFRl%$b`SdBU;6&L=xd1tYucK3g{cQ$$|1A7p^C@w4iJ%2J#}c=A-*h2sGYsy!AA($ox&w~_igqIEv3SRIAc>$7H`^HdrRJRgTYho5O=NFYL7VmrlGf8WQK`y4`UCb>H>&&w2Okmg?U?yOkTg;0W6!9-SZQXjZkPk~lS3awl{YK-8$vU?DX(~0bQ2OnP z`po@n9)VKl-Z7u+Jt5>MW-^-3K+Dzgiv9ZbH+ zimH~*W=5v}*^$-q(#2iM`uq?d3dH&Wbxve3{PPc_0B{d?78B!{2wS=|B$z6YnH7{&Ro z)NX=P7bCLpBXJNe*tKIAm8nYfOzxM$kR)!BBaZ%RtsDeTQ-aPL-ZdEe16)nhLE51} zQ(zG(ZCe+A@Mk9}F?k=n^dX_MqDuE}s1)DR?Tl4Si;-?+sE|^EZu*8{X)fM_(S)VE zKrDwVNhu}?G@EM(PnB@gd4o_=s3^}LgqatiiXNlh!x)T5a59TUwj}JeyQeKX!%ux&vne%Xye4llV|<@@sP3S0D^ab?TO9Un-U)FD zIg`%__&TkdA`!biJx7zrcLoB=A!oo4aD!GVmKGL}e~vTsuo~N|%xgAOjxrZRliT*H zBZ|(Wf;XUHd_|$)omj;E`bpl~*ccb@NXl4p(Q1VP1w<_Vk&HAWlu9Ud#-oD3FjLvT z$Cv&yU_t;kYWbj7{+#0ZmWxFRjN}m|ysu-8*Q15d($;QeuF)f%9BpP^fZTkjbmk34 z+7i?aSdY}X{XT={Op*>8@_j|D)6Bb|%Zi-9%sa3w)St=lWO-sM7)5(V{Wr$C^fDVK zJ_!gyA`%-27a%7u)$+JGKq9UP@^{6^`>qFk(XvjkW@7c(0D_CL^Bj1lr%74HJL1|u z?y}0d4*MWQK6bg+uT@Qt*4bOg_EO2Np!@7g3BY4*K~4g5p{f% zvA*Fl3J%y(OH@dUAiHD;g3798Q5fGZM6pZ>dz47P$P8zK;grSQ{+%poX`LH_Es`!U zJ+4g6Jt+DEMVfnA)4TE-m0&cZYK$KG6PU|d6d?@zw4Dnr;)!b{oKxAabr1HWsbm5j zUyKOZ+JY7VtE?-3FMeRnUzHx^e3-C3{0{aJMEYcq+z3Rn=7Yz8f@#1@+|%Fx8wGE*+MriJ3YZdsA{*YD>5Rgb@4KitXVLnGwJ3< zui!R61ohzfWgN@Cs4__KrkNQi0b?(J3?t=P!+y^Q5oXj6@961_UX^hDZLn z7|8LtzUH-=i%0!^M+6AMYG{%0-D7STFoxCzNn9%lBm_)sky4DX7e@IP<4B8gXhMky zBoCY;;5D>NVK-zPm@0yXZeV{-<&dG>r41Ye%Wn1bSYhUk=ZsAZ1)&fO_4qJFDxii_ zTA|!P?0A1BNv4&=;nRZ>JWs7lpm={8Va&X&$7t=e2v`vKM+j2wa1FNJ?LlyM@X`;d zq}Um=GCWCg{hIP*3b&}A+5BSoxu`eFqi~Fqoklr@l!+G=f%PhLS zgHVXXMwls`RHg>y%bu&5Lx6A;y5!)cxq>l0(fxkajs|#z-b!gn1Y`1Zg`~hlo30Ys zrWs$d%5JAzn`Qs4xt>!x+wAICgLQjxMqV;z7MobP3TsSl(s);R!`y1{i&Dgw&k#P5 z8QO#PbC%zb@RMqw@C&=7{mV&VCqnNa>Kjq(?%piso3X7rFA%kz1Kwnx@kQ-N(eL$$ zxW@O5sdZ}}YawQd)$0Ac=tX?a8%!`R-6R#hubMIPn9Y-vzcR#Az}$$z?;eWv#xT3Wn%nJR&`&mwOWrr1Hmn0=3{byxm_ zIO;@ho!o*C5L5BfHn6YjNec;|z&pV;|8t*C0DoT$4TVO^O0fgZa67C^GTswJhq7`p zao@Y1p%==Kka#=G@e_*fGFSAcGe(?Hb>*5)>zZFcG--_$tL-pU2G>ixBqYIaRnMlw zbd4}cF`%?u6BTN#Q3|wCnc(NW4G%d@u@^ynDX^9&=KF{zPjP9PP{vCp6YmyZThR}# zC?9u}uVTJ)`L!Z8{kB!pMoX8u(x2)b|0E@Hqb{*K{Pw9ML05w)#w4A1RGSqG zOM$uK7+m^XF+LhE3WiyfAgr-qJ7fJw2>fNb&^N8LUbUBToxA5 zd1w8>gmh6_nRR<~%IL!j?vUxc+PvlF;yZ_+VTgT;(@P-^XTa;*^qmfZH!Fg!qI-31ah zn=TTWV*+6k*8Z%LYl44V?Quv~Vsw+BI(yC(RX4765LW@p^vv83uJvtrP6X+4N80^Fo29I@E_30kfhAu1@@#Ro zeM)&-hN{lLZ)wbTm1u4+9nWm$0P+pox9*_o%HDr$_bf#P&)OCp5{-mQWppi7l)kkK z2B!(7U)bMZVO2u&*kKeB@t-~|)lAj=r7&U$tjtrY20UJF>shn8K~6Qf=UCx8P`Mj% zc;i)iJ_Iv7wzvf;Q>4CHdfue!&l=36IQ=LLIkA`-{fQ{!9Pjg=7)6X(66a2pMf49l z#ANNTTMn%h)Nw=a@GWf|XVY9Q`BggQgmRWHW`+`FII- zHkvQeHpF(|-;FmLq)RP+a!|G?IXbc3Z~T%iNMpeXZ;)jXkMO+$5!i(Ws}nnH$Z zI~Tdb&#cGPqm9)rU@K1qqMHg@cciKlHsK7hMZt1}iL-n8_#&M7!lX0%S@>dbe3O5I z#R}>2E}DE7@&I>?JNaQmiV@t3TJ7Ab1O~&n7@d1s>)7rN5iy8_HxSKyzM`bY8+33^ z1t7H~9MXZIoM8=b>3)wJQZ#lNZBkp;yUS9`y`n*e2+{|HF>)pxEVt zb?{JK|8J2mst0=TI;JmW^S~`#7BSi=W{EpI!2IpWFNQDl#q6ra4~MY%CExdXD|_hL zC^Dmll3Dkv9xY(>3}+x9(nR#HRODhu*X5jN|Jh-ILd@<;pJ&OylP|+onfDBB_OB(9 ztn8r1JqUTXjJ@^kmAz-$!3>ZrW7-T|x zGLun38IG$*Phzccgbl!68%fGBDk{MK5$c ziM>zL@($u>0Tt@+9e6w9xe>QTK8=tetKjc_n9Txz0QW@USR5jvDc5Km1<;`{c%ZJs zyKj8f0x_{Kg`|M*0beYPpLZ+dd`7eQWZx|%ASX>S?)Z{_ONNQs3dEowix@A6k%|6@ zz(RLAm1BGRR&<1n<|NG(;}Ow#A44S=!-8CrY~TZHMij^RoJGRHxL=V9aSrB)L!$rJ z*Y_K|BgWY^N%J)p z^6D?S_iv$?cp-^lvA+FcRI*vjvOxVJfOjz8Jq0=}i4*`99o|iq1pZK;hf-Rdl?a{Zx=-wb_m~MrS=@v9-e{Vn?C~U@Qg6o(?H-o;7uW<99v%_ zeFrDL;FJj-PQi?-*_IiP%vBhZc_VsYWy#$(*fnY5_E2e`4L8)%%BJ_TTBn_3jomaL zP<{=><%SB{bM>h#Mr(@cFdP zna1gLS04*B)9lNZMF~1LM%=zK*AzWfZ=!drDhkqGvzDQy zs+U9gn#ET&TVE9GRjz7ZbgwSF_T7H2|J$UKqKKXT`1kKkFhGDX_35(fXuV;>Ws?6% zAp3JE!2rBgNL%h2>V8z-P9n_Sc19#=4J6!ic{MG2R>o2+(Q+~~N^wIP4D+&Nxz;!u zYu>0)ef9fPTPRf2QHS*KS{`unmzk9V%fG6kt53JCuSR0ra9Ohm`xlbL30Wj=c}lJ6 z^ZbM+F2cVDO*&yU4&o96R3r27>fAc|PO%!>>4|8^zv&Dd1p;`Rv*>YoU&afs(n#H) zIUEJYe|@2O|F{pB0s38m7c9Yfqfqp_q-;+PTIrANMk0DuHU3<>i@>0EqeZeWM#CL) zcza~}&y5~2#6lip2=3y41?||RY;QU$|N64Bj0)eNOnr4zJASfB$0}mgwf3! z&F*$9C~S6S&z>I3Jz|`&2O<-BVnIo`s19<*jWMyPCpC@buP#mB2z&8oxK?F4?isR} zNTYlLmajd;3fhg-)x$s^^uh&tk+;Z;KTMWqS(%&U*Sd+3v}lVllwBl(#C?LzEiZJx zvWX%Jny8To^N3Jp-A`vIR9GL#^2z%|*kEQQ6xRxNs)u2iwE*2*2(uEycQ(bF1ksnW zBzI9@T5b9ret11Q+ag&0AL)rM5L60~l8 zk(ZT?*Ol8-0_YL7T)6Ocz}n+=$Kg^E$qU~H^rXba65o~dMH|tb}2%TjkW(K~ulgRY+yHeEzU9&Bz`goUY9R@81RRlNG%s%MUA9>hV zLY&`da#%8MM$!=?s+lRTkW3CBXU=BX_>X~*j?F&G#;FX~rZ$?1X5vUKf)M)**13b> zzQ&j|WeJL{0xQC4#n6)2wB@YWj0-qJUWENt96sg*8P=g30=>t*ZVq@s8W#iTT?mt^ zx2eCa+DL{{%ww{$_tl^tCMPb&Kx~$PVgiHIkAZcW#yja}Jfy^3Dx9)W(Ac2Nycu)n zbDGte-Z&^V*}tNb%NU@_$n0i{msaTrH#SRT>@2t+i4|BO;6HJcluY@>)FPX+WdTy+ zt`EW{5d_Ca-Db8bwOOQ@LB>Bj6BM}tHFPGJdG)3clk%ytm>uZ4VTh%vp(nnlxS{%o z7B_n=_R5Z|N5)X*&Z;dxP!|j(81^_I33p4)jW{G8kU=j;=YwXxs=XI@IJ>=v`Z#6#<$%@zcLA*oY?-s zjD_ZM<+ivNwFTxi%=|;=`)e`*OcmItvVVQ(n{$>|r#1SwD}w(e3Iv0#q&0V`9> zoGfm%2oBJEqqmuOgX&m;RkG${ij{$eVg%HyW=IsUCxQ<>ptUHq9nEka>@b)R zYKB=J0?Pg7Gjq3T7&=6Vd~Z4qrtsx@jT?w)$L?s9@c4!W>~^cZ5ia|e&eN=9sID|N z%^cYI5|U_pNXqHCsXZMbmTanxSdxE;V&$-?F;wj~TedbV zuM)OhTY#2(;gBfKKnAddsKxU+PVE0+3g%L!96VZzYxaVA#aE^f$Oee^0${{O5;-QY zOFg%w6+Q>D)Yz8GP+D=1Nl9Xt#87(694=-7%t`gv99K>#^3xp#a>{7QpW`%h0Fsc z3diZ}2UC_GWQ~>#w2~GhlwL zky(6`6rQ(GQG{h#kh-?~@U2Tru`{3=Ph4c$0TKc5K=d=LsFVzfIQHHyLvNw5X;Hhe z5I{9mi!qn$+V>-!_ri9)WR}O;d9J82o5{XZiN%g=oX{GvSb{gW4y)x?)5wr0fm{mm zZN_4?rvJ!c6YV1VJUjbBl@*z+0wq-lQF3T+$t*=XUxxNE96&w{L&ojTfxkP1E~dte zIVbzv08u`f-NN*KT& z?q}e5Z8)@u{Z1@#(AW>1EE;Ss5N_WK6)9Rs2kAtu`8c^O4Yqb)5w2Rea%7Dp z>y(thso=97y4j|j<&ih*Yy;`c6Rjm3Pd zwkO-0IP_@NaOvw(7lk(HtHK?(nXdRX%$dT0R-TJ)*r8cw_=SjQV6N}eas8`J3&6fS zA;&9~C?Q$v+DkG+Ym-b3JQ5trih*@fg)8mGVAZ>b zdN!5&DlpyU$As0ITaGkg9zUFYaZGmAI1`p;F)vKV6wwy43afHUn^ci_dql@;2keWx zm%s$`XAMe=Qc=uIm#>>YA9_Sb(6^wu@^@4kiQfsqt96EN)0tWs9`?jXQK?cv6(SLT zUL&M)`Xk^rq!6{#Ejazt?f0T`kat{7A1#J7ehm*X^XG4i+-v!V8kWi57g9U))4V=4 zZWwq$AK&SE84xA@%%DyCiIUm7oc`Os^d2i^i`YApY0%u!j;)h)8Jo>R`>ymJ$UcJH zjj#g)V@4WFOx)4~Wy&M0$%Lm58C2|;f?O*ZSGHD;?1HLRwlsU201QW#k}qb|wzOtf z**j>Q9jj1#Me)*J4=ANMNvq`akei*EtRJR{hmO8Oc>IO_*GaJZ}PgtGI2R@Be zXjoJmd7fy6cpr27AofL(h+{2H6)gfqi2vm(7dcGdt^ezaEk#AZBA(;)a_Zf;a^@P9 z!8l1hJ$KKM-pY6AI3a)inP=$OBfCX9jIUj(v}II&52x=sH+JtA)Q5y1*bmvcBLO$? z63P~q0v;b$3{*R6orKC7E5FC-nw+>mz{DrrA8;j&H3t`k{8S2f*>dg@spIY3P!9k6 z@q+O5-C=gBb$pXVvA^7cg1zB*9fq{E?bzAv<_8eQC4@A z>WRa>N63ft=l?_5Sq0V6McbMrxVr?{gy0)@hv4q+?(Xgz*Wm8%?z(X&5L|-0yK_0G z?#usps=E52U%Gl#^^!5?_y$KtM&r?{KC}>H+PehhrmmpfT>B>p*8gK`X`?_%RW;F^ z!h6$?F}`Nk`aM#X+T5fTwnP2ouLsp6aG^*c`S7wVig8qCazcP|g5j5`tf1@M3Kr{0 zN{wDa42}^~fkWM?H95100h1eYwz}{`##)6sK^4-lVfzwx8I72E?A5H2q}TkGz4#(x z+8)MV)e{j)c@rpi1%m?hyrRfr$n85#Wr7*m)9G~DTdeKjlKMZ^hzEA33>d?=>is-; z4~5v4X)>E;OmLRk*64YZPh%AW3Nn=>Niub~bmTMuWVlYiJ9B}rI&1xpm8b)F< zG?Nn9VH7WQtWQ{={d=V2n+qiJ6l;+%;cHHma~8->pyeKi_Eh5Q2wRM}OQkAwYtG6? zHP%WYo~UuOQAUA$BE-lUE7y?u5;mVbiT;w5O0ydt0k35-I&B#}kJtQK4aB3liCR$c zGW{7k7O(_VEvb!MwiI-=5_AKd$4~5t0aQMMVz>)DP-=};r@yy;VL{gpFowuW0%ew- zF9xl~JFx$|2MQGMDi}IFH_7srn{4PUSI354-1c_r7yi^Z-czdyY2iE}8q2D)SNmIo z5&izbrL^;PqR>kLp&DgKT6alz0}D0;uIWHEQznI*y?lHbi|1WWLO!r=ML%lM;VW>J zrg`MlQn1=wcD_oZVt$We_ZEQ@wD_K*ym5)XF9V#@z{gSHHcf)Y7P z(GimWoX5o}D-2C11_dF_v^ZAE(m*$%9#>TTfB2cz)dh$mjRjth(xq_CLNmVLXX81k zm_~dBo{Txw+d%2(AEy{x&kH4AEy)Jk8ZM$^#_;AD`lJpbXnnfNEL$sUj5;`!9 zPesRNIPd%fm7a=H%e9AZB8M6CCG{OxMz$dskN9<6_n53PeSr#b_ZR}4&_{>W~+6lC8P>Fu|@ z7o_{n44)2?ZSH@eW z)ea6wqfARWGuanscaWyjY|$3yB}rwg8H|t9Ip!qQFZg*VJ)Q{_B^6Z^P<1<>6`jqex9fCr`@@B()zTWG$R%DJWozSy)CE^@r1g001-jdn_`@Osfr?wc6S7w zK5|wM`zB?NB$Is3EeH-I#Q|>qz?U2|?w0hrULiN|{ux$OjYd&ZBvnxOHJC5~9_v0v zximL`2q$W%$cEOGTxrIh+VSQ_hOexkh>wUHC0?G`dv9n4s!^)l@aDcbbMr5^r z>DI}xOHb|4Sh%tQi#|_zqoIPtQo3OGcS1dR1gM&M+6BcJt)wxkVsR5iC<6o2`-s)b zUuudN=kQPw(X|>AN;F=-%B0FkrNy;Pq;&`6K1-lSJiY~2rWOE$ggMfa)KvKG?I=_+EeXT5wmjUx3gzJt-M5NVQK|6A~uo3uN5iz`sY zjL55rG5h%EyMP6{$YPQ}3~dh=0h^Jja-=%WtYIoVGzM$ zSV>o`z?TmQlwV&ya%Ep-dxfvK;SvTfFfl2HnosCAx=>>c~vpX6hq_9s}L>unf2<&n=eYoEWNVes{9WLqe zk>`k*&g>=G*l{O~`6dP-J}Uy@*e7cQNJSL&7t%!5#~6Z0g@M^OLd4eR!On+_WI&Os z#Z^&z^O1J+~4Q7ILAePn!;nxy)$wzmU_LV3^P8&cW8<4d-FlP4wd1ATBzW3rv zWj^wB|Jab-REYW(V9!8x+p-CQ+$6MPkC{)o%WZjr82jFun=|$~3bcwEN|6PfWjGXM zmkTwdgL;?qo)-?F&f&VUN}Opa%E+A}pVTQapH*Ug4?p^DvBN%;bjX=66Ps`Rl3M_t zAxn?wS|aMMVTY=_cO^l3-@X~|&D#v5J-HpVmpCQB@J03z(Y=u%%Eof&AA&o*$}*dH zluSNf2%}?<;eVAeOcgt@q{adQ$bf!|OG-e$0YuHV3qiBWez4{_h8SCb z={MY=KLVzHq}cejg@hT#wk_GDGW~d9(hSzJ4*o}?x@6%Md^evm7>*TBB74nejN{$( z4$*u~o;Q0BP#o~*&|QhOv?W8@a~P9|B5=ZV2~JFjuaUoXw-pIy0hXZv(M6pgKK)dT zZ=Pe>5JFgN1z4d9F49j*Jdt;Mihw*|E$la_)#Plrs`K&sj({IqZFdDD12G6z^RDWTx6E5@BrvTG%DqKDc|lG1c|D>7(}X zru=_|Opx-6I#YmTEGhiIZ!yu$qx3Bi-DRD)wOhX#+2aiD*iL|XKMhdgxUt&lQU`E` zlGcX)=;>lNP>cxXKU060eoMtovcpIak-IDzd?QyGC~ONnhS>xM$zRH+6OGEr{v)H9bmX!fW!M)L)LiV@X^!+;f=4+%3@OeZ6&lb+d5zJ>qihc_96 zD#s5^fi-4c^pYeVu)L0C{#P_PA~7~(5+X4MXt6=kawuCl!B-`I{5yDj7rx;oA*2^# zZscBKLP<+f1e%ARk{HY3EQxSBsI0T!@%Q+*P+yNXc3RsjQ`i_-RC!>Ae@7HJo6uVz zzA!e$zS6E^QWiFI=qO>AOmT<+vkUS40quKX4;;2theaqD+sKFh`xue*f?QDEp6X6~piTA=sVMmM*-p=cE0FMJq^1 zD@sD!_q`m9wJYRLCr-!nORij`L=Lv;vgE~_2zPBs_~pn~ytH4~(;u*_@vW!TIx=q< zyFdP#!^e_ntZTTgghv)-_{d2G+0H{lgq`=;fd`>w7_FrLC0E@>ESA(aA+sz_Hcz-% zXzBr?Z=R(Uq8*lrRt!zW=<9qg$WlRqnlVMyfN`FzjS?)(B|02zLL+hAAUremr18`I zK(tlZo;=_Rzq2`|8;y<<0zefx*AFLis~{((k@@`iFM@+t2d$(=f!(JI7)ebhOMgG> z2B#t$+mPb>Ybg}@{T8@Ix~@UyMF7wk*&2S?;<0~ri3x7G9XuL-aUVxmvlotJ$Ofhi z-x1jH?>B|M6#`eaboU)RjOCgsD&l;e>N94ZHlgdP-d}O z6RHPF4uL$<8NsPFzu2n#5uZ8NQE<)&D4(I;(3jPtqXk=;aP$>#V`pj(1&E);4i}w! zP~S+J53?Rg-$M5L12i5X-jaQXQ;2#_tjmIYR7l}-scBu52aaN2Hn-3c->pUDpRF=C zbmCngssWyU5~?UGW8=Uf(?LAi(R?6<%IIDfO{9Hd+m7^lXaYg%o<^^=awgW9keLTP zzL?;poYD=S_K`zY;eNTboa>WWUGm9NLY8(`gcxUmOlucla3OuHDfvu+r+ipvgnanu zjKuK1?9DW1Zn(H55#cr5Z`oS#>(LU;vFQfTj5tG^%qw#`uQN930Y3+Kw1`Ah?fTDw z%ILbg*?GM|U47Yup>rdImZO)b8nz=joXxks6?D5hqs2{y1bnBuB zMEBVd4b?*dwE9t@*|dLq69CM<%=pJ4I@;Rh0Y8*`JBW#}#S zYUw4QPFk%>JCi|$vk6$WY`J+JUDJHJ8Dv@?eaghKRbWvGbs>;!d1h{K#~LaAM*fBv zZ_iK^_t})w>V=v}$}o229AnK(3MQV! zCyk1n2ueyQtC|D(SaS7*+QS_&?!2;cs+l_fG8OBhD)V_T6N^M5x#OhV^5V`#(bgmJ zZXG5HUuskD9rVIAJyCt1F~8p8ImajQS2n1N(>VyiM$&)Y;^-ZM)_>$f`EA2*y)U6! zZYqFl2ZwT=BPWloLgrQ`>t$x)v%|+my+c1D-{oGXatTBD5JOWfyVWh&DY76ts;WsJ zx?vbp8~sSDc&GK$nBMLIu8UiR{4RmAdD@bDxVM~`Ghd<#rVBCwGNysOuZ4*F4IRrx zKQQ%y9lzJVL2u(sST_*`MUH>>8n09*_A=&HT^q|0t~9T9gwxxRd``EPYYu~~ajzGV zr`_#A@v2Avy+c*5cuJRjaE`Xlvk3(3Y3zC?a;#EAZ=CGufT_Zj!8_N+UTb@Rk}%R$ z#r?WCFNhRr`kq*LtMFLfU{PE^r+m$NxYN4ru{z|4_B9NW)rDPP$~Ki#Eb3JJOSoq8sTbm* ziCE>tvL#mXjJm;UUn2ZC;!+(jv?Vsn{K(Yd9H!ci&;B|3b~mgmRx$MRn?vyg&v%}f z^367LYYcckgh9R_sY4Z(St|*lE{HZkUu|eB>oQJzxD!)e9%9q3Qwgte^rpFY4JxMP z6j&3vdNa;%^h~$)b{LdXx}2jiyuYOfDQ-&T_=AHsgxTH@MQ*WqZrO@wrWq4_)01DO zB`s~RU-(}7dfyOV3`8{z-^@LHa2sDg1HowPkK8Ep|Md&`^jZpNBQ|R~%)edM|>tKA8sQ_gV*I@%~&=hD$9IHY=1qs!FT!mP(!} z-y@E{BT`7Gi>_tFQ@&%;FqE@iPyr)KlvE?#`{*~T`$UKN;+Qx{-qd{we-Yl-ceQ(TyQ z|891n85ynA|6%r{Hhjb&VbT{iw^A32?UG25+3-4fK#2IU7?=U4y0WPUdEMbl_7 zt#Rl*G;0jP`{dUq9$vsdvyU-|PO~5O5~7=P=u8m*SW#QO8SJ~Ieug6jnMupRB$MJV z1*`}8caR%7bCc3$F=|3OUs}%~9NwQ}O(e3Hp>!JA0 zKtn@P3;u+Z_TnsW$kIkiiEOm8aM4BMZK@tVSai!`(WQPU3|i)U2mfpN{-wEPDlGcS zD)^r7gNH*TlaHh5C!L3LHl`%ToGh;`RXjP=MHdm#-dJ^5!#|B8y7Re0L+*oS?{pRR zo|W(YEf>hTd#SXdvJIl11YNVzfvk_E%Et}^MVv5?e9hJJ>8mKK5&>tb@7O8(H@si{ z@-2t+8qt#|GzH2*~G&z-ngUD?6~9YZx)?cj55Fnvn(bV zrQX)ZmI$;VJ#OHVhyC2Evth9K<1S7lrc@vzjP{f@;|5X)!7ecoz#^&i#!SNHCzR`~ zt$1Tn^B>jsf%2tb>$dr!W{Oq5X+Tp>wgg|-5a5mfzvLdo=W@i}&p<{qR0s(E|C8%M z=rga#%+Ar{zn1KjBR71r&yWkXX4Yn&aKd>R>yomhs-zTmbl9S3NekfG-zsaEaoqUX z*}7ZJY~*^vKT&p4zs*2 zZr1kNn4gFJ{=MVdr}Gbn%^1#D&WbAIPEuEx>*>mG=OayAhxwA^f6 z0O?4&%u3}5^o$CequpD#Q#Yz+QUfcu+@rkk7{pTv_QPuJg05O|N>NAE>(|TE({b)8 zhGCdjX%w>vc3S&<$R83gJ>MMr(ktlbxB`kgd*PC+ZmPQoLV%_Evvj~bie+3X{6xZ< zxMSlmgCMj7j3~C87;;|pm21mtP1N|&v$~uqYa3BZdp6O`1o9I1l-iQqKxZKtuSMJt z7pa%(3devQuQFNL8$b|@^u~CQ;Iyo$UjE?PVSDz{8b_+SZwD-n zp(h8mu8X$m$E3bdQMY1C5d@fv5q7Lq6bQs!7zj*GCb!Sa4C*|fc;fGcIWKSGlw#9HZGteO%#<0v!iwZ)WJCMbk|!%FZk zM{RQARDlPD7x#A-AS*@#b80-de%8)*tCe93X_k0w>@tl9GG3$%pRNVQiJu>{a9G)^ z)VglfwLMzJcY;}IN4*KQpt6=qKsO!x4N93>g`|zeZ=JK#M8ir6^V^xg=_RLX^ldzT zvyKMWN@(fFmkZNfiS~aVEM=r#2IC1ioMKKO$B&?rv?f%YIE$`<^aLv= zNQhJLXI4UVJnkjOX!)!fu%E%%Al~KPDO%a3B2T;p!20-(hfb-DBHH~zVryP$_fa_x zhY0J5awex*)*TOjtqU^{+{XYm8Q0}+B(JEhs1uAZpa}l6if_90*G4Wf&i-s$dxLMm zn3aH=N_yiR^L#^+3NEyHn~k(kAg5i1>LLbr{CVC{miPS7_WpvSw4j|r@WH5?NI9@x zCtbcTFsH`(rhxXv;mxFwdQ9%t_pNNy&7&$Kvc80AnuoFu75fRj4fRY0>k^UuTN931#c*@DtV)!h)agEWyL*rXg zH3F@WpXom!3@C$OOxK)Hcds-~$D}qm4`02aQ!5o$V~==?^&C%Q?+yGgrrj$~sDAs^Ra|6LquWF0dL&>Ancov>A+O zw#`L`jF?}Mw6Zxf#nG~>Gq z@>)%Y1S?irj?tt7GEzD0U82>+Cjfyd3nrl5YYnUR4reI7{8x1;EKF2DT{bSop177M z&)-s)$#Ui@}Q7MMa>bFh1$X_)U()c=gFV9uBe)ywe6ZQ4H{nmvlicsa1zg6=5f?Hz? zd4vT`4-$RWhSE~&#|!QA8J23Sv^9DH$|!^6@eD1vjABWePh^#bbU=7Um?Yu(kq0VR zbR+AV#WYPX?O5cXLYZ7%n}GsPMg90wZ1NSufx@5=l?fpk;&Qa_T%xf8LVcqkZgLui z?)>^ZzuFKLnqDx3Y+u>HQulIExUlx3G*k(OWyf2QN@R)4k%bL#YTzgT)UQ-^29Umv zXZ@UbQ4|Qza*EHCd01m$39CY_%Eos2uXam&U3XEDuL=eu^P!{YFn$^>jWLa0V!;AWZW&A?6kiLZqYpMsE@~7MP z%~bP<7gCuM!yC4f4TsI@J{(D@0vGbIgTrg&tR*AtGi(m18&-sUC6B!j4oM zk|oc?NekY(mK9V7MV3e*SEMm*&*nqTD{wy}1CchruDc4q+7nAYw(oKoj?J(P04c~+ zW63YjcF;|26c=fD>1(q6GCf=%sM0jPF_E)ZRck4@5i@cQZ%XhMUm&ttB^ObMAP)VZ znk|q}jtz+nq!SE3QcYMWs}P5G(uu-kbbwWbp9Rg9Y5YbGI1SWhuQ;2Dr+pr@SdTjF zno1VR`?GZsx_H1;%I@1gv@&!`pnZBefbDF-_-dhRcrX==}bJif&!!W^C8lfDpt$p0)*|5 z4OlTFT%=&JXdG_!>zV}So*5dH8^(K~sVGL)c1MJlh+-Se!*Yyai(3*_5)#QVK|c`C zO93kF2%E(E_FVBr5{1CR3ZIy|WAC|;2>XgoK8pd{;mZ9hjnjsMY1(_Das1VAL%~2}#Ma%v$l>63In*1`!LZac1{Aadjw)b*<hupcn{`;?4^O32^D<=&u>OHtM0R~b)zuk~&s-0Er(@YK$@3zntfEOU z9nKv*y%vU5ULp7~X%}N*n--Di#qskG59T)LVoY3DQIEA$DMCytqr$@$xYx;wWXs&O zGB35=Lodg=g!jUZ(1ID)(UFQTt3`&tu8$(HVYO9UfDRg_RR@j=ILO8_#1n(j$Fbf8s-Wg!^^k2L^(rys7(*lfh0{PJ~gsHh8aIpM_ynJ8b z{D_Ok+*k2trI`&Zev%8x9Jy;}!uAWbNb6iVPoNH?J*DyDzv@6Xq}8)9=VRQ2E;!A+ z8DM=3^t?X8yhq|DEDDcFvRbMU6o1ZqoAirw2eBnF zb10!^lUch9|KbR}EUGN5c0pIV0cP_8IgT{V)J>qMzqzNWkYx&jL>0Lm{S`Q3DGz59 zV*-h3t{XybBD;7z2AU{CbzAGJKnP)Y->Px(8>j9|7fQ8OuxSm+;@9Ej$i8Ozd>c4v z+sEC33utK4J(_f+3I;_08Bb2qJvjT}%Z%&rf+<}tFB z$fzHMW2Lw&MMHxv*PdvQ{F0sJ1|Dbb06W3`!zQr}x94aubdTuwm{weXZUv1?)^1FH zucW?rgi`P#2)Q8YBCDk6)KpXJ%t+;HT^P>_tsT2rmWQwZZSQ8O#7&Z)E_}0r+!$8aPS%TC z5mw(C)urKawpjU;Pi2R@D-$Fw4&4%=SYNd?><-tG3?_U1gELokb7z~Rldq%d;ov}= zFtA2^i0BMgfoW^7(nT#cnGPK;3tC4!Zc?560G6H zPSNFM1O%%?JS^$m6ewX(MR#%>a@7h}j>|?kJpMfj&W~&FZ!;!i0nx{o8u0Ir-?tniO)=4dws(FhxgM1qsD4%ClqXJhms?JU+ZGzPG z&k$UWBXx^1nLXZ4HTQR)`m}~5-fFf|nZ0kXAj-TB*qrG2F}bl54LLGUDdlB)kjhV^tmb((=`T{)oZFGxgBwTxvMl{&)fy{bCO~XUG`il ztZw$(u9@D1>&pkhPlO{vuO8(+qf+27Tn={lq0YU|PuKQrXueX&FZt?bedih5$ zzn{7`v^+obd(Cxe??=TTpKr7|0oj>R|NEUHrS}_di|;qM)y2W)OxfOQ-0E0>%C87^ z1}mAJ?M7W9l_#4<=-Q}%b^p*uCn!5|{|vtVI8xbrcq}$Q?5Q!NeSl5%6L(i)3r!G{ zneHY(tfGH<^M6zAF8f&exa;EScx@HS42J&+H#dvOTy%YqiJm*;X4AR5s+`nQn-Qpo zUQWEBQXG|7l#x897s-x_|Ag=G$LK@T0Gx~TO<~D!KzdS=!Ue395q7-o1`|@oUc|k6 zxBJ~;?+3DWco??be0<2`Ibw`OUE@fzXw~==pOMQ-W0Y*yyg1&OLzjk>WmbC;MT#*! zqoS||U(tEBu4TfF+kuVZiHwrm!cdd4 z@0&j*R*w|K6CVT)d4$;&2~3Mx1;FVa<9Q5GjCn*%uV-Ex)tL|-ubka7_L z(!z}7S8@=_UMR9)#8D%1Q_fpJkXW@|H<8M;kuQoYi7prqk7fgDx_i@?=B!T=$F+b` z-81_I_B*oSU3Yo;fr4&UUoh{|CRl0-w{q&&_%9sA)+aLvKr4BogoiBXt3GgO2U5B> zoC9tSO_;Ns@taF3)gE<=iTy5yoEB;V6TKJ=V(TyKK{hQ@XoVkjE6m^19!M0j0+Ra3 z@U-jjl^%A|XHR&9yBi(pzh>T0-xxOGKlbbZA(t~;PLd~xv?i3Z*R8`Upd z)y!&NAo%8$S?N>z%$^~x`k3>qZKFZPXb}(-eeM&3kjmSn%EVzm${A526@suqwa^qs z__BTT>i$;tK)`^7J7kGeYq|AZgZa*7Ru|g)H!*2n{jxFdMp0dOL-uk zclET%z+HLVSfqUnr?Ke&|TaNQZIDCpE=#|-}Z5UQwrp5{c%q@ z?D~FNgZgWL_wU0#wXmUMK&R*_`1Tm!Ewtodq;$!!q0UbqbOGpnK-sS9BI=rhF@mT@ zaVQTgAkt4x>_XIqFf&4)>XXcjbWsmvqnSLeLmxy&?+vLG#V3dkBif;e0hk4#!`P`n z^Pi!}67s0ig(ojZ))#g@;`!3GbVoiAeCB0X^!5<-Ol_jzz1}U;@aANeO@~YQvC$EM z`|UY%J;_SPk+=WN_aVNU;qbgbI`<)>o#qWkyQuViqbcPBk|WRjW2w=cFi}_ZNi8}V zY5w`V zCvVJB^KmAfG78`<>r6%RuycfY*~z-9%YGU@D#(e&z;+MuK)!w1W3lht-FXti>=n9Fo?KXltdR zNT#ljSL4Xm3zSflL>sqzNXDTHLEwXUfRp$hh&Od+5XUj#}X3@@vx)(90Hq=?@qSX8Z(Pn8gXz+|0+fVV%# zS*%`t`Q!EVORTUyP@AYPP%S_zuUsDE!yfYyV$Lb9?W?YKI8-aVgLZU=1JXITsTF6@ z#Kw~uQ+N2J$mPO`;d)y(9I+1KtTTYUFPH=9!E6_p;pd|0&+BI7dM;{!?QrWh@lvcb zlVLJ*k3BYYTyWW*)40=wv%X2ZL95^PFIdM=_0cD!W3QnrBgb)h>m6@8L4%z#;!2xu{2vK{tb3sACW$KC;CM!zV;!pwklIP2CkU4EPL zflmZ54a%IIaL9L@xtj(4WOMa(V$vNwig*C;r=3yuj3@JHVt?`h-7oE?xUP=SFDMBf z5nGFPrZAg+m@ftny?KA`Z5}Q);MJZ3dr!%5o?G~0e+>dAdJ8z;`I*Pvi!s`O#- zt>agT%V6q2-q&hzGyb@2uvrDO_8%5GKGn(bOR;IwNj*~FmU}QO5x8bEo1n!HUc~)- zkvsHG9ZEtS(cC3L@lG&-)$|lR`>F)2Na{$-qmhM;il;yKG}o@&V>5~4NSU3k!P7hR z#VD1HY+7T6Mz_*7PN+D{iAc=LW1xy``&7}jd(7%1sbxLJmRxWxnXdyL!%r@+=RY28 zL|Izy6+&iNLS~8#{RwV)EU8-&<%oXG2oYxVJm=jbH((W4I;YgN?0)0;0ohlXcz1Ym z?$$Yj`Y{7VMXb{;uY3tYkh=ols=_fWWyG8GUD9|1eE`YUB`oJ4)kIJQ#gELZ4AGC9 zGSnf+zLu6Ppl9b|Z?@-K>Q{D5Oz;IYDB>sP3p^wC4g(NFR%B%>Q}0pH^Cs*aL>hxWQogQz0YQ#jN#u)sBL-gP5tqDT9R>5#oc(wdah`> zNLepKJGmdT7_*rhM(DHQO0a{d8iBVPUKd>t0Kp$QX}np^s*G0@523CPlNoBh7wmSX zw?{DN9{jZu`CySZAn&d=e{QEhl3Xn2TICorhrnYp9neqs>B#nGDaB1Rsb}~qGxM5O zSlv6CdAsl?CPnJvtV7h~TV!b*@UhKV&%%BNY1UHML!iB%7J6(hVH-TAQEBrx1QaT_ z?9}xeoGW>u(Dy6PFJNu<$%uIMTz*pyknQIKGxhoCL#K$u};U z=Jz!3K9@Iax+GtF7zp)GSWas8o;8wLn1oWvTo}5-CD$Oolym4eWavoz)UF%J7gFah zNO9&6@_CyAU~zy(x0Rt=PdyDj_95N0u;g7uL5{s9RbvobRc{WOV-d7Y>g}``78awfIqd8wqnXanz!+7+9zzEMl@J+4x>q1U1c4y;z2lr z;<}KO{nFS#0BZnS4l7^QuScF=D~QAR#yMMIc+R~Tl^qQ(8imt>_^9jz7+jj`W+HJh zT_P-3A(Q8JE~MPd5x@JywFs&SfhQT;4>mxi=W;n!q6;R&@{T;yP1)H?v{zA?ctP;( z?kRt8!>o_ln!LAg=F+}KOq+f2p1&~7%AHk`HO;sz|A-CpkP~m<<{!*EreQvMZrt$* z?ghmya7P|t3R*&|p6TX#Pmiv4#-|*~?5GIx)QBqUs&g@m5p!u%!rwKJQxCk=Le%Ss z6S&?4n7-}L4gXSS_6r7KF*~lDKLhcXI;kcOC&741Y|YQz%;hMM*mg3Gk+$Bfwz=LE zl#k%+a`L~r$!69uSFV$jZm3Y2zOL?#@t5HC zOnCAN^2*X?Evk}9n5Q_lH-o%|EKMDJd{dR2tdDk1pTyWBL2qq!Ry{^qzOn}-6+~;k zDx){doU$F&^}py_*ZdYr6{eA>J#>(k0VM=Ic8#-tG`Y&HT;a!P`6P7MN2ucAq$

}9_z|8 zBb&X3Dd#bN3B-I@P$Qk4bhA({DTh}NwTrXdY$JN~PvJKD6N~nw#KgjG=~Do<^z9)TV_s`?u;MwvhtHgXO8@e#)x&Sb!EJepuIR}vj>Fr}rDu8-U6&c4xune`8QU-&Gti#g zhI*U?z9!d+TB1i~85|JK#{8lawASMSt|pQ7O4Qr?Q<;n@arL&eeceY<2WPm#B z^k_EV4z*0RD?Q_1qMdj|=^Su~!c=?!rpoDZd0;OsgB~PV@nLs7a8!8o3EDW&L+-`? zkGuY#g{+-Lj(r&zAyG{w)=+ocpwM};>>W0}*gPgO3r*H#9e&geh0_|Q>>UlVDs%8* zsdKlbH)`b-MgCsgau~L566i{tlw6k80b4=J)#KkQ-_MDxVE9S@tcm+8a@YwsUBwr|G2Dr1qTM}hoR*4upAA5d+E@qvU9#$6zE|H*t7m;tXY5gXsIJ$HKikBmzSClkm z5o$22nFiLE4@g(SXeAliZBCJERA4Pb(Ahb*ieimRA1_8=1eW4HjG?8k`EoxFiMbM9 zuzu@Z*s9;hH6Kpz?^~1WJPm5jUfbfF##?;xqiyxvsjn3E%XCM(9S%InN^gNL(Mbxt z(l%XZN^JwaOJ6yT6`%G8pg6j45 zK_FkPWDQ@dq|!BlUHBJ(1|L!#FQ?mk@8-ex!)y^JvA$Gb;d1Ve^g=(ST^MC3NV1No zkFg#A{hs{rQyrSLK7GkeME9V@twU!4I z%2$G|9Tjh$2d5E_Jy7F2=7?{|>MAzLc8YDgBV*CB-tcK&zg<*!Vp?t8clLxFRX3f! zKz><_FC&l33t2DK>j>Ij`o68U?}i~8T?AbBCp?+Me}pmr4SSpH#907bIO-{Qm`-!& z#^>vOuUDc`MPI6UvQsRl&ZS&a*NLh{ankh-#n{2O-6#oS=bY5mIlzPTNEO7uBGE4T zXg&7BO?BL;ZV@)wz%0LjWjj2@uX?)gqK;l5`s_(et9puRJ42yW5^HdIzO!xpU7tL# zi^4NM!!Qa>;~NGI>T$qL#3&rJt=OThoItIKRc33glUfmw&A6p|9Tx=_$iP zzhC2c;m3CZwwwAE{HuLlk7vVItUI3rPtGgXz}hn>{<7g{)!k5Bhh$UewPBCUoNxWF ztKGl;2=o24-`Gz1_?w)C4S(+42DGAh0U8TR{dbhAWN2q@YVr?aT};td)>Z|zy}1Fa z0z%S6A&^$d8Ucl=`$bJF256^;y^YMI{gLwAqjnsJI%$cC2v3GS531%O<}PB}KEJG= zco$ZW2LxI<&^%@)(oM2I*1W48K5Dv_4<7mbUm#kC%>%{hY}>5NmA$ps>hzx7l-b!; zE!cdoLpJSPY)gv1NP8RBT34}h8Rll}w%@L`H{n!y*zlkbJ$bdg*S;uqDWw#2Hb02& z7ukg#N1*EQeD_v^P_vaZnc# zYSyGeu*6#rD*=zgs2hE!y{^3VmK`Th)Gwju#+%K<(0d6YOA_rC-E}WR+M6(**14eF z_uKWPpJEVf4p;O~f37LWsV~1=A2J};mW2ddHjRHo++*cHQrSWKz021|O|O=fE}eG` ztMGfv@OQ)%;Kb0w`n~D9*S5wVqM#PQF{PUZ&w^$%yqCOO0f{DBzn+9VByR&pjYlNS zAojxyM{&PcOeozJ6CSStFWolTdgkkJV`5MMPLYEJVYSI~U?;}X1%)O>Am7N&w+TZE-q*FRyz9$(2y^$rfvc)D_539;;kz_|hs zQnv8Eu68MtN;KcY3bt4Eo10Z4(qR-}<9{|&ie0#9F6pz&g?N6b#$2o3sx5f0xjz;Z zkJEb4)?!qHRdqg3U#rf%A#BxY;(hBp|K>6yOkVD%%!x}~JL!#>g#+&1^5KXEsa#zq z(8jgUD{wWNu??T`^5gPk;VU2%Ndo0yB80qGTp$n*F@QCYHjV}A6+CCv|Hn8fRgebh zBx9{gyX&hQUoyc=LIad#NTW7-FTL>=4)=>w)` zC>dasZ8Ykj{UG$9>gh8l`7;)!2zE`JVW1=VsBu32q+5=Rzj<*F zOVEWS=TynhMw1VcR;Qg;VVJtcO2T}jFsW^p2fDbZl}x^$Is=Tj3@1#iO_wCa%^b%; z0q=m67+EHNj=?@n>@2htkP>Z2-%q#UmS#yW zOyc*cCl*&$r_}n<56>72+*&HCFyr3t%JY5tImfx%@8b#3Iu5_9r_Ig#&|VS5d&v!z zchk768owXX8*|zSIb?5renMC;d~AsIq6N{k@?c>yM%?G-ix34>Z)e;qYh3u@v@4#A z*EO8OO!hS{*+WDZ9R~6@uYC zWKU)zc`I=|bl-RI-y30*p-`CpX+^XR4Yf2M8(pJGBA79Y8cYjI+6vc6CJaewyVdF1 zkx*X9U)%Z&oZ}`OMK}0C8do3yB!;L4TXaMy(Trh|PT~w2nA22~9GK&zcC`C(r$dM? zjoBuW!Diry(<2mUEfBb&BIU&U&cgpUT7WhYOEmpcxLltC z67QFg+g|}N-LAf?E76XE0P=jhfwYw?on+=DBg;f5qWb$5ou|)!NlbJAqL7fft;vb3 zEKcX6t=EUQ8^8|Aac~8`HOaZ{&vzQ{HC6}PW~h{fi=WpWa_*o^0fFteDaZU8lN;tC zbvR*$SpiF9amtZqcLA>}=z^QBCKzODNtqC#Qd%lK5x77rr~>5IgkO|;DM@hH?cD{- z-2QhJsK5{98p{kXX^JGgl*_h#raba9Ycr&J1Zg4Fg=Jk?xdmAk;%E{j@k%EpT3-?w zmWKq-u){CY3AnUN;~`(Y?ZeUJYDBd%$5t|!^N=8nkJf_|`fpQSmj)7pjsB=8VhX-q z)_MlXQQ}nZYdfJZ=b3;f&K!Az3~PykzUC_{ZRO>E>ZAG&7XgzO%$y(F9WV*eU4+!E zsJXxmV~FX^R1>iVwyh2g_2Y$6k$}u4))D2{nKZJ2M+pXb_QZ}q5q80 zMeJSwKmUgRt3+N*8h64~1!)Wm)-@uGGBq3(z~n9zevop@2u-LCt!PLRax~Xb(T$u* zaHYsIXGlhi(PzTj_pu15jumPsRC>bWe<$P4J+e4>Vg~P^E(_F*@@kj=c>CD&y+6<4 ze}kK-r%$t{>L77%PI}eg{M}rVS>8tWrQ@_Mvwic#vZTmIyDgx{dbv}tUs0OeiQ--Z zx_5cJxiFJS>y;F22sADET>rQY+T=5PVD&wr)|VFdFYxK0&jzlod{p!C%>0P{xop?* zA|$_1mFO|%+Dq^ONUq!W8ysW6evF~hU4}Ku!7sw8~u)DH}+arg}J zs5gfNAw1a)tTq67>;@hfH_>ol;#!qW_UDD?6RytMJ*9953W;~L&!1O%ZdTiIjKv|L zNgcLML^7RUx7kfQmSexfB1Pd(Hnx)2HVdCD%Jx1hfmRabe>laQs|cw%9b;8YwDf$K z;`h!JZ~aKqqZ!;-;J7l^8j6@pC~~5krUTu~u*J7gYO#E$v%5(P*g}QlkL8Q&O&Np= zbSYDV6{NpYj6p05P2v1-OtbXKfu37;#M@+G>R0hFPq~%(UBxX<3Qw=6Q$IP$c*r7X zHEK=OJ5ukqC{;7B@UFh=uT(m2EmcDAi{~dkb<;n!Nf9LACfuTneeXk-rghyGa8=s= zjYK1{`=V6fIr6pP8YUtP5ijEKTPJW^#l5K@jZ|>$A}r*mQ8?*iz%`Z;G^I+hxgDDF zpbL|7qu+szY3#D!rv#tRaEaizQ4T3gp_-6Oubx@P6FV4`4)}(Zj(QY^mBg5`b^+3z zf@2!?%!aLKtrOt8TNtf<;24N9$)ATHP%1wG>MOx8D=h{mGZNSRQKw>pkzFE#Ki9%^ zK17{f%lefP8E+VediR8)JL*T~tHUqId|imfD&6Z5THO%AD&?Y@k1IRnHKURD{J^Zf zW_pck8&f`lB7kL5C6`IQ@%!!9P@-W1#?<~3E=SD8Hh5G*{dXx|$S6KxCZC*}OTef` zZbu1JeH&(3w`||9DYfG0foXm4l<1$3Eb_NS&y#=P|9w;}fbt#l6R0_B4r{1q zXgwjYY)p!eew4;$#gr&UgtarP6P_D);~!bh*N+4_Jz(;!3^p!}^UC&aF101T$AqlF z-)<@g9)`8H?Uw;J+-qE1;TJPy5J6jNC^dtA$jEx?R=5wkuDM_JAvO;riZq=o?3(-_ zZ_PRgG!I3^PpCvofr!ea6cYr>?;IGkXw>A4imFfbUR-tBhBYNY67G6y4*KgBY0R4q zjcdzGl-r$)OO{mrxwt((kBjkw--xXPt6!HTHj+D*Q273!zRX8wm;W%Mn|-_d$a;LzX~2v2Mz^2lPYn4V_c-0jQ$EwMyK?ka?>D%RZ`?0JjbWeJc zA5A}1nSV5mG!-^axZ1@@SCZMqA0yQAj7>6f8__zLsZoB)gJrqN;*4Z`bQ*ge5khlsIk6nA{Xl(BS2TVWzIcOn>G(So)$^IdzAToDULXc zbJSOFiNpjWnB|1vAZ#Hk9?< zF^TWWE${CA1g2M?Ue|`AEy}w$1?iCNi)}eyP3M;&&X{u+{nnT|3=Irx%?;8&uX6V$ z2auI5S9i@HQb4PQPw>}=@nQjSH?4>D2t;M$Ke2}HBn$%MZd*ii9E{NfDXx*7LIX@~~Kxgl<1?lgSK)a&<~`@D9YqHHMIE+Xzsu z_943&*tfoQTWIPy5~4j=$?UbI6pi1KKky~)1xO7QVo<|8NqmqX&g=k@-GZ4gH#QFG4;feXuI{^hMpIOg@&Kj%y7Z*Q&aW`&=!-WJe=Ku`1qxbZWgrl z;*rJJfX&fe>#1D2WzQJ9x^cX9+uQmYe0w81Z1haW{%ft1h&INOG%nPK6^Yd(e!%^c zpD2drry{MlTOCeSxl|U@OT?`5%ia6yPYF->Kd}-%!C(zjp)rMWfBM$XBa>h1AWgXo zC!wkAK>#pKc9c6zalj`Kz#m896A^W!+6AK?p*$z1o^eoDT^;6#1Xue^8rRSJ$(-#g zU+|w^TqidPN2GRD{3MV2Q>^qB#aGmVH99OQKu zTf}W(@F*TpCO$cLUGS+!Zf3!k$z8R=7p+1r^$MCFEg%w!{P~*#IMDuL_73&$F#kF> z*Z2rZ^V>lazW=aN6gRa4NxzN%3G@Wnzjy>RPhrRv)e;mygZLxyiQjC0tQRL}tXMHo zuHDd;yQ>kinB~Ats*-=ue^*FW=VXh(bV4Rlh!7GC_WB6uyyd)=75jKxW4|3hv4II} zYMfWS=H}X%4{%1p)_=uA=k`OpN^@(l4@_x}K``caXm%;Bx?xv7QANQK4!(rHwzO5p zqohzUT=!hxSoFJ4vpzQPo4puRd%i(FgP(S;s8;C0n#AHXd^b6cO$-g`3kAsPp!-z< ztF|Xg*B1>dwXba&YixP2oZ64mn@g$`1Dmp`7ae#eV08C&-?zfO->tXjkG z-uW~2{<1O0>A%}(8X`50!W>mbgHeNuw*ss#f~Orw#ltBJ(O;zjf8FxX0ePPD@`3tM zL+CL5GI?Hg$*_YxnP^t2o?wRO39M96ul=9B`X;DK&_M)3D3b%zbRQGpX($MvE5ZQbG5_gEgv! z+DIIQXCSE&Nk7W3crb|E`uCmPf{+cNcbcLHh9v&ankfNP^*_PMB9f@;z*NH6RMO-) zX(k-tC95=#Mhll*G3M4_*jpv(n=smBf!sfDp(`9*2bjh;N}b}h587Z&(g<1V>o=r7 z`t1KbuFQap`4J%cxCYV3e;8N)vtaPgxKh!z#}!56UAFeN-6yxaAa8|e0U#~8CDPeG zyJV0T>W8xaKsiXgWp1XJYV2rO^kR4N2+;4d|5OHpju>wK4vFFq_9GXsCL60}O`}oi z(a`sJtL+%*Y&Puw=kDD8lbunfL&czMrt4#yPaoagPh{Dx4RheC%K^}W-N&%0)!ON_ zEzC^Tt8;SGRka$P0sOYm6{}mgv-J+>kEN3_JuNV}{g^Ms#%dBT(#GBRTHk zlC<&{3YDyVp2Bv2;Bx913rnt?9o6^ zWDiSav%TfUg*RKZ+K0#+Nz)oH z2FDISrC1D`SYN&Y5E(95eI7CON$D+l;t5~yTP$;Gfd-JJ#}V8*G3Ez8@AOrgGA&0g z0bs=jsv@oQ29#!;Vv+RSwXzu7h)B2ZFCpD~0Pk^Nx^jt{u166Dc;x1xiDB}uA@|i5 z(nrtq6cg7^bEw~HU=QEFIj2wZURA>*bO}?!+2fR(y5Vix-g@`#r3u3Uz+({tW6)T6 zKK&w5lK4E5N~4Ymdn1$|M|;w<^in2WbF?^PQI55rI=?hvpM^7oQACiNoE< zKA40kiw7~lf!gq> z@Ce)%^b`UU*$IxsXVQ{w|Cn3(a^AQ&mDQ~LE!=pxGW*cQM>Hieuv05=rQITlNDVNm zLN24RnCCI%>eQUw{p~u3;PdGl(P!+G*U02>Dp5Am;$_}iFZ>ooaM`N-z{R*K?0VUx z*k6bKhI1t&00i~_unv*2w=@4I&XeqwaaBPJL=+z>187LZY*llCa=2iw+L0ePB607Eh2M8f zTzp&;b^4k!H>b?{!M9H+Zdguapt!pod@kL8B@^Us3uJczBqWOIjJfB1>o^}bc&9yq z$G}Yf_xA^O10db5nCv?I;@0Ap@8z0zxANkmF8Z44_PXO)a?1Xai-9!*-0w@a?TVVT zKUEgCM19NFC7(2^S^EW4`{@oBT`#c~`qp@xN^I0UUFVA$HBjvM5KSV3sjIV7F?lzi!^+(_Bt(@O2;J?eAA z;?V25ABb{1vpi3)|MgTf!@d)zpSE{LzS^98{G11ax81O;U0p$4O%L$zLi5YgdJ{VY zR)ycbF^J&yN122(QIq!RmQc}ViOu}zP(e}3o(>-TFt3Pv zo~GN)#Qf&gmwoIK+^5h~P)rqt$e}>wr+*D={H4XpOB{<=QX!oED}z@>bDT8?aNdIL z+nd>ZS?iht(hv;Go9i)TRM)j9-7+1Sf9@T0f0rvpD!4U6;N}YQ5Nw6-h$hU0NvjOg zUcs8LPr~-cyk~G8UdiSmhwe#7=3CZ$_4Q&0#7*CXRf=^d$P{AhT+6+_LFb^0m1*l5 z*-1U51A_o&wC~Ic+$jJTn=cJ^3TSSG&TN?DmpA zH6+dU#ap40yzv+&e`SdBJYhUqf@}}vkw?FUhSb&H+JdUvZaR}_ObKR`wBV6MdzE1P z%mYYqG*eT$qZGHJKGgPaed&9jYu@1op24ul5lpam;r8JgAZ=XFu#7i$jz(^9jPe0` z6lVE6$1=7tcb$TLBKFLdtp}|?NvTkEO;92`ipPnDG>t01FwNO~GOxPQaW~_3z9;A6 zHWHg*GlKj%cJrIW%%AxoJ~T-(7WXO}UoG|3%>9w>3mZH1+1RJjKgxMF)x?nHGJROg zyxu>3Zioo}OdWnXBB)dF8QI%4vJ^;_oHxx^_BHv&zMB}G+p)|J%+u9g?EV4EvnsvaGT9_-Jp z05Bs4F&V5%$&N=K#@fn~p+$B`yS`4`E74StsUz-VoAX`SO9k=gb_*u3M!z!U-LWtV z1v%XG%s>BPi!5b0bkmdPh^MUWvr4@wl(caDLrx-Q zaME=e8DPn?;G}QNree(uhf->m!eE8G55|c1wT(GR!7T!jK11UKW2`~f>f5jMe*TUW zth>Ol@;a$qp?y+$^qb<}bEnKEujE^ZW)mvKzsw;O7{|Bp`sfhrs*0OIyA4RFItTW8CJZEjonjGRS`52v#DkxJ7YaC zlK6ux2>Nvrof6+YG+tHj9#yi+vrsPiydu{SLkWs

mn%!JU;{q*kqf4?DRlNwTtds2Ztk0X1`RA(A^?1{AdYMx;n|~eVoEYq) zOwTU*x2IxelU#}}Zn_1)wSA?!66`fi}BFD<%z9FH~3d9f-k_)T671jVhJ{*_E zO?5ndc_ShDXIs-r5q_2wf3_I;?k}q;rx4UQnsf(RyCYsNWjAc=9EE8-f+Mw|{qQ^R zmU9A@*IRE;$tGkEz`iYRDf<9$_5fEDffOFjh=VqWg%Tzm6Otp^x=}LH6nbX}ij-=l zAI8!;#*rqpy5^B+EdhNKJyHGNcH*)n0apY5FS!36Fd>l(1u|ftKKX)*INARUm;cvd z@Bd8x{wdVlv_}y{8=TgKfpxJy4O5lANoA9q|h{|L3Y;CLbthF9OX-ozX4F{S&m@3 z0RL2^am)-1Gs7a};je~6*AaY%&68o^eQJKBsiD_^MJmi<5zMdeJY!iYA5q)IZ+bWlVv)rl}damDu=!!Fmw4Kb@h*L7bbV^H`4%Esk>A7h1BmQQ$04$$?P z`Q1{<#-Arv6*=O%L)U#y3@)PT#;6}DGBhxT;~}db85a_ZwJ}JHtK?wrMDNOJh!f(1 z_#)aj0bD-{e$G(n#J|_t;GX+&=A#ofN`4o-B|f;g9B?Zd%Py+lNLqtXlP@p9CL+$n z|BHiY6QtnWWe7RY2n*nVCWo$A!xjK5B!GlQ{JcVl^xfm;>oOl0<1b1pu)P(?KD90& z;$fk95OPFLkim(6(RAyLp^Qz)Uh+14yhe?k5jr^cnKoR!=0P#xbqon6gJAXkmZ(W_ z1nm;>fihMeu_~?a@GbULtg-m8jOa>aEgZfG$k;K3GG(gvfkDYAA(-^G|A1Wxf$f1UU=vg*#6aF1FwP_HS2$ry<<~nn7VJ20G_t{_C*)*U0LhQEMxYrVf%9 zo2P89vK#n*j$EULKwnv^d)Oo+7a`0bB-It(8*b=Fj+vT@Th$DFQ)QJ+vM_pS;~z_7 z;a;VOO&)l%obo(st9kY0{P27K1!(QhuWc{tYF^u(-NnW8@Q%M}>M1}0Ib*1sbu6;= zro`~#k>6<76c`tP-Ic_29hR=cxHJ$rAm?@c*1Du(P}ps5nf0oAE3Er%`y`9*0(BDk zWe4cI?H{B~_Nt^}_~KkqxE&=>Xk%7+*xl@5sHwQRwIExvjm z@(+5oY|h}P?buO=Gedx1Bz&CVd=Bv zT}7XVY}U~c->TM@85v(|Iy9zJeoQ(yWp0+dpM?IfJeQqksuEy$AdnWPWSev=O|YvB zabh_e>egcmFvAJs35;YkrTGjK?%M|K{uxp5`=37Vp?9#aSEiD2$9#olzj=UTgUu0% z5o`#Yxk~&8dqn(rJgGsft3@N8PCO@h!1gTpI%CThO3@)oCg5I8LO;R@jJhK&ElYig zrJtBb8oJB3rX0g>4gTSU5k7Bm&p~->6v|H~40*8=4FYeJ_00ot?92zYSt^QJr62tV z+RoK59L)L;3g-< zmcYqHqYP<7lhHnbC@K4nfMWHjEODyC+(`lsU;f~xFt3L?d9CqGc=7nXw44inc%rIQ zXkz^2!u7Y7b&c;IkRIy~U}G0|cOCEW_n&@V^e&Y=?-|7Q@{VH)yA}doL^tk!!VSf` zihNSPLj;wtPlmq|BR0)gFPd(Rv61~YEVkCYb_t&BPQ*J-qoXg2TTX3bRC8SQO6~S+ zyRxm;5yQ@!F2eORGL9IOkut7D3S=ZpWn#Rg&jBeBpq}xfLEm zL-E5}2V0*$H591XYV}Mn9$@Xa>ovD|RQkf7dFSGa{5OfY1a0zRh9~Yqhk#_J;{NKj zWsh{&zP&<`^v96ffUT#7}}! zQF#NjYopPqGGM<=G^`Bn>wk|ynX-Y1_u{&6Rq3pHEMqx$f2+M54&XB%TpZo2cthQB zp}FZ`$a#8Q^?Z9ZOakX!M1T~ePuEFe_T>CFvS5TiV~hS`PvX+TuP zoi(&0of{PJdF5eSqnh2X=$6Ic7QHqcpn+Cfr?G=y%hf)qsW!{cZTv`H>&)34N5eqO zMLVJLh(u!Qe%7O4`DbdefWj&GQ%P;BL;!qlFb}=6As?5Y1qlgj$2 zFQ?DI!$fN0EmwL?e5PwR{*5y}QrGjuDx$=gs?AsDJ4!@h{g69jA~feZ;+9rkw z!p}MzBliW*{8YYIphQgc$UGZzlTuOB9_=+8$+lZ9j5=JU6PM?_XUTABm50DrTU7WB z*zE>+5yc(LE!1#w#NvA82NT1GZrD9@z-vvZ;=gg$rLpZ*PvqkdGDh5qQP5 zmcMsjynB}%dTQqF60Rz2_9+*jzAYb<7*-2Bw2>|#s&LJKHZ*0ai`RFU36?bt6n`OJ zy5w}B9BFUfaN+9gmdkdLhhwCC`zWc6QNS@Nb8y_yEPY$gmD^M!x6Bx1&(ZF?TzV3f zk6VAaUTBk*ro=0kXl2YBHnm&2dFuS0UE8%$CYT7Th626V#OMTNw4V;icE-ko53MYp zfJZlBo1u||a2}#tlP!xu4r64Gu}ecJfRx+$60@rRDYoX0l=2R~Rppic z_`pbe(Ag+j0jzkP!pEtvWLW-ih3r)&y=0)Jq?Ewt2+wV)Xj;KE1;-h|KM2K5V7V>8 z+^{YFO?!_eSlm+(Gy9F1-T75KTsdi-qVg-ImA=ETkojQb`U&bw#P~_ql(s|?f*np# z+om@PSo=2Z0U+Lw%b!O4!E9}*{a44eV^*(pj~|gL*c6I~k+Uf&cu>Xu!`{y^4E~3C zo5@+d&mKO{5J=dHvYXLL zTd0R=@|8-twx(`8q-4ScZ3ELZKzZ7xp>{KZGIKV{y}F@HQdVNt(<*3Ogg+Evy9i3t z%xWqhJ(n-{OZ-2e?CL0aG?urD*H&kCrSUx0@j(7%!h^tVKu)Gxg?XSWo+npSfx4c$ zxERDyQ%t8A)_HSG##?wK^zImJQ&p!xi0!rGdC2emm$90gWtEwE{nJROpJlFZcDj^^ zFKtnP@t(nXG76PYoGvT65Fg{FXBW$dt@@d#LHw{8SE3dRA=GVj=EdhGl>MX#lXRq^ zJ@|R_p=H+`!U@mUp{aZr2-u*9o?EF%y_)%>-X!NRKHQvD+Zj9;&D5^va zRbaa#BYfs<3In^hpF9RIlxt8-5j1~L9ALf?a-zKgDHU@ye(n}Cy45sPhm@hCp1lR| zHc2<%ojy@(D96`bvvSNvC4bz;uSsps%Yzm-zddA}W~k++zAVq7!^r+Mh>C1fuyON> zuer_?UutndO^Ow7t+b(aS{im>=Inog)+yoFtl1Ki^)xz0?AhBvL zZ-(*B-3UhQ5-t*sDY4(T#CcsfGYlva?*X?mN(R)uIV3@NGn!Mo8X8t6A7&h}4it1J z4o3XrAq(#a36%9syhrX(RyCx*#{7f`(oF-_T1IYB8YYTHGC6+a*DAaU9O9`%)G^MYGXp!Uncq&u zte{sr~&jCIB_m|H)+pjD}1*xnRljpK~!Eni{2hT@b^vhdlw;zIyuQsBVknhHXxzvgeMJOMc zpuI)`-1{~ zCF@jlL1Gd4Rg>B<1*wj!{BS_TKB;oLZX^O+8?ECMX+=}@sQeK7CBgaxi z+v@2~MsApgHewY7)wShsXw^C@qx(45cY?_f7!V&L2)|EZ4ZCkV@<(vgpGVC6XhA%( z_MxmQO?ls-(#mQ{tMYCuF=g{WEb04a@|a~;40N?TL)`kUy;_@~Fy+@WiBfsD)(I)} zSZ(74*xxC*i=}tKWN;x*-lz@B6^~-E5G#n-j=qI{6LnZ;Th#LgMc#(YrbJ00^vlwP z6%PNbQxob9|9pfykDFn^tn@Ek#xKRVr_sAV?n9)8?~Rh-mFQHsM+xB{Qhu;Xd!Qlv z1dD0|6of9CKxQl8&|qlGdMvN7J2$RCaMAA1sa7AySvocq9&(j8l%M7&mHBuM6B8+% z_7Z%@bc%nrn3rDr(sN%_O6TNF(4rrGedmI>_+IUZH{It%^4c;`0{wUwNYD9ds^;|i z8m^3%SyBm%TI}B0fi(vRo5Zta6pXw;j&DrFyWeEx*Dw7^3{m&ti{a%*Sq~VD={wt) zGIV#+u&jEH;QkP$mQA3%8+4E!kNGm+v(M{4ncCwteI+%u>%-ISi~*r_rTurQ!9!YS z6cPv`Zy>*=|GwP+SBLW-Ac^1n%SUx^deg_|+v*yYESp+Ylh)nm>fEOU1?VDCA`wQi zoqZD)a%GB!+Ew_)9w}rk1*Dv}0ui%@fz~ntv>daeDc&BI21#L1T^b6EaQ7!9w~7kkvZ@s5OvRf4Ln+VC zu)!r+c9dke5?iE&#R2k^*%XZ_8%iwXsK6VULK?}WAw!-Sk{`UN1tQG+AgAmboT8NdvozJCGxJhCwDaQfxb zm&ZMF!k|}yh4RPq_$(JztoSD?=+GtBEs7+!!i}`;A*rs8zzV{PA>X2K^>-+}Q})hi z(wY@9>}8`ZW32eTa`x_gr-(^XH_lviWB(9zN0w0D2*XvceYBPjk+JH8xc()hBW`3U zN}tf1GdOp?y=ov2-L4Fl2a;x?LiEzc-!hnL_%lmxP(%H%9_fFd!Tg(*&R^Hu{g2;a zoUAk$Nba)DVww2c6kAXPjjj;!#BMDb8W|%XxfC#4%)M{f>DGnH;!gT%N%!*ktrVJs zS;1odiu1U0TC?+-pvL}FOr03Vb?wRcF@2`^k`Yqb_n_)V;_!(em^YPEgyF#G=rH14 z4aq7!4h-8eWjnv=x#f#M=n&vniM%4fVAJ;g&-OGTo7k4Qv=ONaViLLkhqHGKt|fZ6 zev=(*M?1D{+qP}ncCupIw(acLwrzXIxH<26t8ShDmwW0~_3T>JU%IQF)pL$Hd(83k zofS!`Wj+*8 z#2ZDr>!HN>KnR}jK&ZcV2@G2uTfMGI6?juYc)jo>35tSc4zvSZ&+dF_H@P!(Kr8H| znI(3_!+%#tYzCTNE()aWG$`(M}9 zp)91&ij;~G<&u{fm#`80zx6YZS*?|{1`>)kpo~<|&nPDVjj7}8EGOZbWy%ja19}K= z4jOAwcuA(Yy9Q|g+<653dED8n=m6}$c7tZcJ{y3aux|3MSk8ZkTwHzsM<{3>NSitR zk6vN?X;eQ=MnqYFMnYDU&c?vi#Foy^(Tvv8$1b}H>!;`>S0*T@XAUH(DaWLxq-pP@>%ks?|7W#hE-VyV ze-^gHDS&{8|2H>wx3Rv`w6WW0LH*j+>qB`qA|96Is!AuVl47D3ng@5_dZd!Uk&-4? z#HSe9uKVVVTgC%ZdGy0n&1Tglr?;1on6UHrg<0_iuREk!ay z;32{{@B1w-Lx3-t6q141eKnMMg}OoX5yjJ;c{Joc^_282rC29pm^}?5a#AXkxiKSu(jVh#b|x!h;sQl#0Ssn{2S|KwD?+5l z*W)+P&!@Wsn*kaS!%z+ZG(}$eS+d`5ch{Vkk~2+gsexeR2u(z-kpr`qWk87Uq3jA2 z_zK5P^5V1~x5@n-dzw@swgM(av*HujNDZ!UtEj>zIjQ~UkE85ju9(EaB$smhKlQ{H z&IK5B8CWnPkcF8lT3R82MJ7Bnv*`fb1PLfnVyh1xIff~iG&tbPCmjeUl8|MRi{N3( zk*rZ5uXW7VXjOJCGFk~-xNGT6O7>Ui^A#cUWCdj&9+EJg-TsXHj!kj+CAHfCN780?jxPIoPHO*0Ynmq}Hy!Gle+O=+vZ@D!dcA`D) zAln2STw6;^yE8-ET6GE6-=tuz%Xx?Lhp`{4NY%QF!0n;jN(Rfz*~KQzXTAFYRTQl` z+>0e7iyl{P`=#9>RE4X*yPoK9*&j!c$UOhD)BsDie7VM6moW#RnG(@oEF;J|;QRJs zbt?pbU|6!(Ic_%KeCi?yETYwO9&RyRnG|o{R?H@;U(_@~G?mB&rsGE!Veha)&x3DV z+p>co60^jyqzKbM^F>_DONsh*obZDIaiD^c&D(XnQX8Sz&M9h>yB3F0JeAJqq?5Ol z7p4pbHdt4{FxQSPaRY3SjhK47ea;Qm>)oB2`ZFKe_?#DiZI_%@C-(rmPyxkm&;nzc z|Gn*jCNtY}u0$}Ok~RUZ2ez^9sK3EV)K^;`fR;}WeK!6SU#<+%Bgtt*2I2_4a_u+2f(*A^$k7?g9(Del;aX!l7mPX`B%ySyp~Rx&ud4wTxU|n zJ*#^3_$qs-2y*B5zLN__N-Dvn5&#l~ zBSX8yA731V@{P)%&HSOi>PEvt+@T_9mJy%yK#*D@jpXSK zB9C13V>=+0hqG(6#|>pq2f}y}amm(KIOQ^MmK>afdn*18LC*r!3pgOYFU995p}V#*_6 zpRy5pu&JjE?uMX09msR4B?K;CO@Huf@{#_v)3+MImb@uk_(&~!FNw`W)Ksv((W&p` zYfg@sO1qZ)mqLC;>!~%J-vhCeE#(DK&Wx)O_3VPRGx3l49#}Z4{=Hb_RA_$~d`oV0 zIfPGil_=(*gSZRePA9d)t;^%x35;@rk6G&q_OX3ASvb*If7c>s=GIL6>iHNbp~z)@wDc1*Zjidw(a0}mSAG3xLap8ShK+_Bs(2u3r{rF zKL7+zdH5Apn)KtzCv$d$%Lf*AI{x35EK~{OUNce+LJb6v>thid083j;k0_f?{tHt|}L}0YK#%#4U9;#}%6m z()CqJWCF`}d;~R%bVlFTPr)$$j4M&H6Z}=Wfw_be{#S>OBdE&$*c%hqU;!KYDb#rw-R$*kRwWFQH7YI{lLm)mCc-ZO_)gC?Dbdw3@;4&-@*j}Ee(ap zg&|SqpQkG(VM^J8?5V(NIe1r*&S=)nQ$rZ7tuLo?$e{M3+1u+~s*V$yb$ESVRxxGPFPZLzfc87IV2k&k&6Z^&``u8iJ)+ zOP^H>FiG%@g-a`~KK*!%OEPK+padg0IrEc6mO=^pyUMNB`I%?c+8HWBKj-2wjg7jBsP#}CYkX}_4ll8E_p{nRID_;UXjdP7 zK)$+1s|0f~k2`g`#_g1?dPvnf9?R?>S~r!E&AR$B$J=)*fH9HA6|t03bbZ9Pgy9Ul zv_@h7$xT#8s&gxqE-i61cg%T+z5cVU_~-eTP^aIEGUZvxX8Z!1&h6_a^fdYR2fxX^ zkbo7m%ekPd6nfo4Ehn9&MkV`?;Ak@P|5&eL)9f%n1fze>|}XJv5;gBX55& zWI>acS_*zX9F3^+0;|8kro|>oO(sT7un$cK7FgT8si`wXIbWLM$_FQFr4J}_QycM4 zXb7_mRpkY4s7u08X^7xdT9o9-Fxx{+y=aWUul6Fc-Z8g6P}5)=F^#PCL$zR@RS$^b zIF*}%sYXE6C7`I6?ef|d4(neNH&-zt`6Vn>@z*g*;!uenIPnYFC(81`IS`f+PUTHq zJEdmlTM+iu7|MXT1Z#9c@gkSa89Sq(R`t#`GT(4cbxd`CFj^%fq*8eo}KiaDXh%j+inkBd4l1?ICU7=|LOE|UcQ)R+UYI)(4%2FK|I>m2G3Gudy=#xP)- zc#`5dGrOqi8xZBF7uCz2pZaM+;~$L{t>oj7G+5q#Ty9xhAT=PG zf|zX*LgsTRl8~hPeYvZfuHXp`Y2-!Ut;0(Y?M%X8Dr4QUi1K1+?UlX)BO9liQp>ra zhCrlQcw~XbUZLroH=JP>&ET8`;<5aJ4*!?mB4_D}Srz7?>RQ@}g>S}^0aasoU%_n- z9cyA!1X}Ux+n{+$(dK!TrzKS*%k1B(5x3^mE&FCL9mB|he8D001AEeIoz5`Xx8XI; zh(^g1S~HxMvA3gEI%j)ZwX}sC;qy?D-mryPz-0U6l`At>b*e+;rZeJ&$b3(~D`I7Q zxmP-TS9FllA%YQZ>D~SBNcU8vX~i+Gsw1Cub}_aSs5{{I*Eq44Fht&C^j!Nuc#g5s zu3@vTv6HO*KW4AUcz0$&0i6iUGd7_PWj57(16K)xW zuV(J1tebkJEOI4KrK%3_X1~bPCMxm2zi2Eu7>iRDGjUDHldTo~d|9zB=b675k>=y$ z7khHa(4tOE4I&P#K!rs3upZLF*8)xJ(vFnYsClLoZkDzqM)+x?v`FI`rHz{-BKs$q zV#uD~DIbGZsGW(pt$r7IK14Xoy7i@u z*9^LNo6={(vlzcQbI|q}4tx}2agub!V6hZM{0Z`eqdybSv_5`E^Bg{S6|$@}CXGbh?CKK+Lm*f9%o2|ZsUa=V z8raRFva@8C<47obk;>14dg4ymmyDZ*JD57Hfk}=VW~m^bi7L%cJj~4E6_#nUjJChH z=cch1V~>(hmXa>QMcCdMCAwzK3$!>hsyR~@b1~F6P%s;+nP?`*6;n2rTPI<9WNMU< zWXb84oR~uJ-LjZeOsD@`=rS|(wMaP&(J-+j&RbHf_GnTxsz6Dtu$snT>7|WR$}#&l z^fHPiC%;iwQX>5t#gP!rA~Y#cAR4-NI%R1(Ze%=4|0CgJu}$-_4Z@@33FPWDe=iW< zMFw{`&=RbOhbMz2VMf%jZ^S{`24Q4r%Am|D3T>34#oY$SQ(pXM7>B$Lop=0CpgRi! z5D@kMM(*0#Sy}wwrRC%l8++v=lo3BNuV<18!4wi0YAI(0-2@+kBz#R&MFM>|(treh zVw62oCz%xMr{S$m^r`|y%?qt&a+N$3VJq))l=7{-Ce`Yp&O9yKoo!ncQM3Iv5T!6|cZB{4U9(=d{Da-> z!UbrjLH1(B4B{Xv))JMQ4~1z(ikZ#lZBlz^rs%AJRRGqAWSdc0Dl&AyUZ z*0Ib`^cXuXk^Bj%h5zwuEL`3(Wcs7=If1GTk3MPoJY{G2fTmYsKL9*(cUtn}rXSHc z*a!qFZE801X)&J~{DT9jmP${SwMgU=V=LPVF|kFF31=yk|Q=#&pTgJ;o}DPE|ZoZeGfkR$+DrNL(tyE&tAe+p9Zu1up-iwspS z9a&w{K1Eeo?6MKEYUHs7YCS9tMf;LjLaKf%18 z{K8d^jOK#zB>ip&nMkd^xOae|iNLHD+;_aLvmiHt)wE!`_;Enx5hr;Y=f$-W`n>TH z6^Nyn`~x;h54Ce&tDr3?-@)Ys9ntSYYLRjJXJOfiwCH`ArzGG|$vKVQkiJudzBa_B zl4y&Z_5LW`cWd8CL%Y!|*6u8TMeh`1bA2CQAeIi7Z>TdbRBQQ2Z6XmH-l<>wlTT$T zIfHKd24=I7LW6TaoO3AihK{c#0j`v0kWO=~BU&RdBbPyzSXe6ZD(eWH+qFw4u(p}W z;v`Bj(L{ZIub`j1?wo3`;zVlnc%B|+tI_W4*2hQ)9m8@!euY-^)sNn3GQsUFmB!;R z3%)Rk?zuemH*mS$=AG0)c7o)(DE7UrCjo?;US;<{vU1f5`i@Ge6c#Exg-Tv)9v528 zewdlO>`sNE?JfHADuiSb7wdr4^D2aJcYwP9@}-R4DPm&k8BA`S(Xqqi7|^ct#F}~~ zfhTtxguy*+Kww|KGo=*RDL2!KA(Rq}H_sGA#Qph-W}^LD2U`Vr1Td~UfSEbSrzk9Y zZ$zMtWLq@Cd>>!MQL~kxEccS8kwG*A6+YqlO4@@xQ|bE`f0PrR4*Iv#?AtwrbJt_4 zy9UZ@L@G#AMJN{m`_J|Tn56j&GFKcrL0?jRch(n&;h zmASHC^qWqhB3wZzYlwKY*O66e#-^&*nB+ao0e)pI+iGyg(6%@S1@eLZ1)9pC;%l~- zva|Bp5vQ`OcLG5`bw4kIy0Tr@2hR~ikH+l@&-`gxZYF+{qklcciTOGzrp`ASb^hZb z!kSXiCnZx0B=bOIJ%;fCTgwf+C3^PjLS5ghYjk>Aakpk#UEfXofh^LRb7D%QLaLi^ z>uHB8#u2{=>@DJwrhSH9ebBNw5jL8nTZ`oPWsY@+So$S5wCD}S?-n>FyKxDdVk1cw zTr3~PIt~Lm+?XNciio|rcO&Fcn?WAQpy;qnW2jWoGA-QOSVpsit!Ps(Q7Anm8=}=A z?L0X6E6`P0fb4l&GI)o0LnNtfX(G>M0annTcLw%R9BxTGF>$9rSURR&3+ZixIQY>A zDAe!$8iyjifxatmEQlL>84s}Bl=!Dxx8HX-0LYsK10&pBqwa|y)_I3+v0%XtLmvqt z#mXe6ej%E9N}D;t*uO)Gfr0NdIfyY064fy`x?bPz1LFKHg^y1 z$R`eP2bj1x;kl*b;Dw$@X0nvn$awqE*0zyDGmaIba-2p12JbzCoi7t5XOK=sf1#B-9ppLz-+)n8Q0ky*oF9*h?Aw00pp~`DHh(~Y?MA7Jlm;2wa=IV2=z?epZE%yjkY zdBpeJA$=d3d<{^2q^cDMt_)3BK;4E+9z_YeE7cUU%a($|eMG6Sd&F)Xp(t7yK@Ph2)9W@5==OuFMZ>B?I{LsfHZi`m>OlH;Kkl^~2Le(B! ztRcy4=ZKg?A8v0yHKVH?6@sq?U*z=ne|Vl|8;sM`e#{)KKk%&o`iA)bxsvyPq|N^e zzfDfoQv1PTh2&<#rXvV~fuhtmYYrj&nlr3M;wRoOCn&?Gcw{Her6-;oG#0GC!mIb| z?W}1*6R=WKtM%IATc{~^^#O5|@4!4UHGR6g@p<-rHu&ciE4a^*kY}$uU@5}^ z%!Xa9Zl@tc{hNXcW#l@ksc;lIqLD*UR1i!`||(VnSnFzbvN3ji11B%rs5oy~u^vOAou(o&A8NOOkP zc|T$;CE@;)A2|?Mf3BKD_bQlV8)Nyya&uhlV7sscIPYYlD{LR}TzP7r+DLmRA`7-1 zmsxI8QsukptvwsO`70sHI%?88%LgJ#Jr@^d5tI;}A_pF2kZo>3*r%FQs@tiok$)05 zJ%Mx!@>(b+<}CH8S4y6`Cn`g#e3B8=?X%A3cx6Z|2M0`QDRYtlc-#^6&lUgBJD$SsJ8dvv#-#$ZtX5ZB~3!EGq~&0 z6NfDb&J)|;ZU?NJI;PH!je+rhQ^ziC{B3GE9H*Rb%MldeEmh0k*2%jS{RqZ?<(}C* zwIei)n;iWC7`;zzs=_HMbRSabgw(RM@kuM7vY8Rn!7?+ecJ?KKchZfTryZ@BZ07V! zRh`mDuD6pjS8Eh!Y-5T1lQ*xgOq4Zv`?8s+fo#ABt%wwS_Pd7`U8f=6;~#caZfWT# zqikF@9K$1Lvr9g%im2Xcjf&64^Plx;J$8Sp$Y`v5GL+D9z+P#5G^~cMX39yXlhrs< zgJ^BFRSBDHYnIg2X_SgU2Bn`>LzwIN6X@QH5C|l52S>;U0&K|-0J292l3n&cEkULc z4zTmibHN87fM-}7;r!w~v_t1z8f4-;W@AtD5MA;14kA(t=N!SE%QeTUe)vN6Z2VH< zUg3)~m$Jz?hdoAenLfhp`KbIR= z2ct#QN8f5>5apOar=WF8cc@|Lvzb3^lMIl!cEZWT>gGPN*r!IbADt79V$crxK;rD? z?x2uN^6*E<=MO%|%?I3g=a&3uIzfV4+UC;FzDN%kARx;BMJFY$tStZE-y43qCpYAk zpV^ldrWV>nIFG(zX+8@xx)xf}@~nR{W&{uf<;LJj7;C=K`(Of%6~NT^QX7f@4M~sy zP4i(^?QNMA{=T4U>8YK{&f}cz)J(h~p@t~|j&-`iBZKGt^8M?3=Jna_$Fpo7hq8Bu zk*r;p;#k363q2;n#aCV8l#aWGbp~A1!bG0*6N!aYrK+*)A-=Th8!Y-6>Mcb4P zFnH;rsBrWm3vQ_X^%%H-T$oN2EGtXVo)KAH+j^+;0FnCsmKF! zf*BdRyIcEsz-N^ZULKVaJl#HmBPa8@Wgr;WF#KKTz$2(j_YadHs}*BKb{$7Q!^!C%{V=~T20jA3RVnEz$-A@LJp zAI9nK(E&;nRvf8h32kP|@I!--5luK;85~&ETCh=yw7lR`lHwRxv&+tn@jrwS*W=^yLCmhv2(~zQW9jL^G zC_$Vp&3ORP5h<#dV{3vh^T{|8f_M+m)e-=BEBHvB)e7em`x~T1Yg+p4py}3~WH)`G z$Gu@%m1!!U83&?oA>#b7UGQv`;|lcY`hCKxAq$<8Zs)hGd6OiGN){s0#(i~aVg&tA zQ;oT(yJh7kpZQA!YZl5A{F-ek<$Cg(_DSGTh2u7*bGKnCSG8d&uC1A@22+JMzBupX z@2U4;k7n))?R=N$N8B^fuq|N)^KJV-dBmU(HI!O4I;gHTw>&y4rG!Zi89&UP zH27Qy^*_HjCNLDGYyl*)Y!2~9Q_nM;9x-pY z27B6o*7hoi?-+g9XfsBvo%DaqH}|?>tlKNK<6MEYwM?U-h@_8KsYe;Jb>=9P8SD?1 z8n+xuf+O&@V&<|NA|uTa*TPd%vq@;UGl!9uWexPj<3`KM0%w+wGHeFP4{tp+*0?U! zXRiX4%HQy{e0VVkxXn~Y7A}-}@|S&~z>ql1Nc=CLw}}0*r)o%hN_l7k{voH7Q+^N9 zydeEA{y6BDhfes4O#2MD5Q0s!5poc52k6W|t=DrYIwoqzxL0eZlzf;`YOiDR`!G)` z;pyR&d|CvUOkT(;IoVO^Vvi0o(nFsrF_s*FzN!>|`M<^wV8Kpxh67hBSWh$6a*w zLJCsdFpi`1x;lhA{Car>(Z|2U9|TQo9(%(bs3oHcy@{Wyaim3NiBvQdzXu4_ zm#t|HRTPjK(PXPQCNj*JCz7hHZ0>n4a-X%E799JN6&dAN28m5(EFyU1*4$uyLkw`puL9{gj1%z!NP;9**YzADSHw-Sg_!W%N&YpS)waP^caqym#7_-f8`P zzhvt|cuPqELhI@K>WKL#;tFCC$j%);@k1KZIMkq>pFzS%@km!2d%z^oJt4HSPiJc% z*V^1g^4n+hJBIe#D~wVno#LJ7P8nUvN+0UdZ@oV>Ugvl+TTpl<-NN_8rr2I zZM)t(lG5FO#wCm!z}3YkpRo_h90egq{5oZ5-t+sPCN#)?GWzr%Q)tVNie>m;4i3t8 zR{s@z_J7BSgsK0^Ivet9jk=;oEteOjwygGU8e*h;Nv4FsT0|EnBDdT(OzWPFXH+T; z>f;k(!(_8{2@*x#dKYVZu(LOtlE(Vz2i?Bj^qTQ_&U)_tc)5j2I}ZFwwmi;gR#3XG zsK}J#9#M6jf|j1qW58qKS?OwNbGMq)QmMH|9{mluf@V8yhu8sFw1ZBJts+Z7=s9hZ zr6+LfYSd*N+Pwo#Q4&^YUpHv|58G%dcIc`(Ut5|D!m&i8i z-#%7it7A3w*?9;acMYGp8n@;ca}ezh76aqCU8j^7K7*S&3T|66CvT;MJ)!z8*$6Mr zDjF3YWhWRl5}*uR6|8EfhW$(>HAktf0Bm!Ns61R)O^oX`rCd;YThFwdyV2}GIh3w8 zBB(!|!&T1p4X4DH=u^92bXV7~y9_ZH{w(i@0Q3+HpwkFMVZ+eKw&y-s&*eZ2e#M!> zgJc2!ZYrf;#VQ|^m=D6gEKF4|BQeV6S`>S`={e~2c1RRLW4amRwey)Na-=(*E*jQT zoYb~-Q(RL+AL`yDHOgfgpqrD*{Ii|Jn3AOTi~4@JF2wLF)V5qIukT4?*?F&PN+9NGuHfVbc;!kaG-UkjaJrT9CF6K%S%@ zQ;~Jbq)1^O5SkW!{WCYpFivcmlypwKG{7TkNO;P9_^0%2A3KlWQj8ssr2FJQ5=D0B zx&OM5|HLy{0RhqdFaGC}N^-ISj*bTZmyq<=8_HdIY3=)l$LVBy%(wv=pE$rkCN14R zxmXebDl>$n%sLQD$Po%8F`b>sKuNf|>AGckJ)|YVT;?4Lvs|`d6?&^_+48gE^RnB! zy1B)21MON@h3IFk2hz8%x#+IWUJL_+RNu`XT}$&SNcv5P;T?~7_&P~ z<7u3Uo72sMX}P4;3W~f?SpRgUbuc&jSX`;v!aQWyr-KNxM^}gqEVbhImb3vGk9SYI)fZ1F3?^$*@7tbvObh*52K!^ds zaqtkJ-<8KfLV5=`!gZ)Pnfg&5`aCV&vtNvl*ag!Y5E$0YR8L<8L4*qm8wUrFkkOq` z?t=0$>))6|k#j!?*QegwLX@<=Th>uH>30@1@Kz&6+-zq@jU0xAxv)X)rbL7hS;q=3 zVVJPQhh|4CPnx}0}Or9B7y}{k)k5hyB0NI@%mjR9W03|t*aWnyS z)b4xL_lugE9~l)9yRU^+(8TAOy2JyD)-betb#Z82zfFoPsw_7H{#23LNHGHsPYne_07+C6)Wu?waOUeFJRWVtbgO@QT}o!GLCeT&X2)tbX98w*6Ujd#t!|> zEYyi-wWHr#BC^>&HMJw3UrUYyKkhdHC}=@Aemw9o7Xt$2yEu>}0f7d;Z1CHP#_XVm zA)DfEsXFNt5Kx|m;8$mM2|~@?AL()+Pp zZ=|i`^BdQKtPqW;q8iamF-8MxI=|5(Y>k1&dCTT-bI52U= zXPNV`N47m~$1|epT#u%_B#Ig(bB4>sC%>Du-3r=3pc<@h%g$S5!$S2W8@cXG>uhN$ zu}`b>1QOb?ckQ-^=N;?5r4XzqF;((gltwp6wW#n-ro>CUkr}rWJJD(B>bkWs5_Q>W$ z+J|^0?S4z0W8fNtw28{4)^^JhctT0nh~N0hjFv;jE)>t29mKEPkKeZOZ!pk22}W)p zlhu9J4(Nk5pE`Omt7s1_&`Dz9UL_I=Wx{Ej&GS~LOW7aojwi4}ZvzE(gy)5KMU`$p zIZ7rM5rp}}e|HlbcEknkD^cFE%EC5Q*yycaUmFi!&F*&BrE724>IG3i!)GmH2)D~Z z|BK;f&`_rXhL6NN6SiObg(Kfmj2hD-wRkJRyoxBTZd|9om_KAscoxT74ude z$^8Pd?Qmf85&!qb2m=OA8*`iJ&-gffFn4Vln#Zl#$58b1om!wJzT=)B)F72L%={#V zfq?`&rZhS>4M7{*!whq<6gPW-A44!1lVFf`@!k$-Yta-SA+?DF_g6etsYD&voirLn z2tVA}p?eI&rZ_q4uhDaJ%l#yQg#7k-AzDiN!thN{7QzwjLjvgIjc+M zUOLQy@{2#OVK67>fTvWQ(DSUgY$FeI#BNs$RmLoY8GCYmdBiaNz!G3z+LNV%gk5W( zZ>zI9kS^0wP$PyNGUloa!d^T?(#d3CkPfn$ZeB|Whr51U(NK)O-o*1wg(!a$(zvql z3GCqk@bv)%?thWbSz%UFinMv=fcC1iB(V$~jY*Ahh-AUIh%Iq?Wo_LKn_k<1-hc`_ zEf3ubWY6PFJEb3DiV2&3K%0?0756Gy_~3{xiCo@t5q=zYwmp>vOcdheb^5DDZ>Piq zV{G6TXYOu742{}}%Y`XCOk*-XiD;~Ep-jTUa z2;4Sr$NFmQ$-a^c7mDY2=@hNXG(xk)Csb??NpPFXa1NW3#zM5!{aG>q84Sj0MH93R;l(^o)DcO0Te1sz-`nUP>O_ z%Ib!DauP%n5mBkp+}-3e>{7dpwq0_$6Qq+_ac)y)=6uLcuhIq7$~ESt3IgO_vFZ2f z{ZQY)oV^pcg!b~}vQv@@yo8C4uQ6<*|D+s4)33$aJx>Xwr}H-4bM1zR9#KTU@zO15 zf<>cUF%{-Cb~Yy+F6nuu7jo&Xr(pv@R<$`5ccjJG-+Wj$@)`uamn+E5EQXUOtHu`y zyi9XCh`Ih9FH&fm-0J%0kYur4MwtBD7cDeT*C!&!Ufu}WD^d4C%N;77(0ao(sM_Kl z*pn*A7Z;3OJLZ-nha1Y=zmxX@z}%43n?qExg|hqx8#%+$e(+ADr*i90kHkxS%{<$% z#gy71VCQtDA(htTWhaUTy}4(4=tBV>!Pg4R&6$5hcxVUftMG8dhgIB1;GUE9lGM@L z;m&djG?@H1TlommQm_Ksa25U>L15V2FiB79bsGA}dEgU-fUfI$!nh$mH2rp5oaMPw zgvV5ti(vez-z`_K5PvrLQ4ueedK}fD@|lqMQVWU9^r6)~&Gdcph&BANliu7A9=IS# zNn<9u!?g5hH+O?^xB)6=rWQC+4mUtmN;|0q3_C)+ozKSrb%T_}I8s7a?Ku56RAeoI zDJu>h1i@|^Prr{E6Zg#`%QB=@|LhiZvYDxkaFs*)b#g|NcZE%Q3}M`fd{;>5jV#p@ zPyLk)1UR4A3a&44_CPx^cwI1D2bVKA6uq~{gdD4cV|u3U1Kun)a1%ig0kzmaJDWh4 z87OT}vmj}NzS^EMRcb(oVG?J_`FGTSrh`C;aEM)p%-RJX;EHylWvj6h0~wbsLm1J( z>$e>q?rk0VdbS$*x7a9-18>gsB$vliP`NktxNtj`NkO1|mDs+Hcdr$8ftPS`S3e>V zl%<)Ppza>?QI9kRYH%cYmn#Np+C->JC?oYOi0-rP?JlmufMEI2mSD~XtVcS*u*2s4 zcp?u^i`8u+Pvt0vqFkItm!)BXmh`cV_rk2OLCu661hOy=vW^^iQZ@LnwqSs9U)ebX zmvNo{t4J>mX=F$~>%Ss$CfD>-M$uUIk-SqPK>C1x{B}kd24HPKz-|YwjNva)ht6}@ zn)ibuDX--0etwL6nimnY;GIhONeA!@aASwYAa)-~RGJhy{ck&Cvy2V)>0MRmK&2LC z_rX_hjs@ZjcVFZkyP@dDfDiR4ry%YVcr0$ic=gG=6xjNpkuG2)G$#fGnOOHh#Z7JU zj3LJE9giW;%!-zk@9*bf=Oerby6e-!FlQ(|3229MESa_mA+-gT4=0z7(7j1Ck%7&M zE~4dz(`Cf0c>PdZvao6ecw5FgU4b+P^tLFYMi++eq{TrMjL>MRz;K$3wvgw>zHD5? zqLD4b!qr1mwTG>CCkQ%8Jp(|&0vvs`HkL7*2Y{B9$>M4g1l3}JD4e8E)uT$yd^@fN z`fB;r-7d7*NrH|{dbBSgC@4=d!LXdFUf$XBK{9heL}^pQirJxB?6hrx#*k2!Jn^EK zfSClzQ>>KReas~GCRikObsF|+mq~F<{>{9PHP!4IsZ&E^#bj%9gBH}pp>8Ca(jmy2 zd}Gd9&{@p+FHJZ7`W#{t?JVQk{(y-@i-#@;S7?Z~(L`llY`9k1xDV(Krib>vjPPHi zhU&`MHZ6G4u6tAWm=zM^%5ZcBd55 zYl&tsGwmF!eI-yEDf;b#8)^5!QU~4qHnmML`L_B=qU#Yx+!yQo7h>9{Fs>n;s;y5@ zGr|tSRSEJ5{4gul*q6W;!ih`ZK+~2;#HKXd8)&lqW#CzT*d$Y?8G%p5{*Hj>60@b0 z{waEizy_jWYn#bL;^|+W-ANln-II!M&;bR2^8m{#IO&WKVmip%z!9j_Lz@Zq&K&Hy zi3}gcLKDgoJJUN%xQ&dL3I~M-ZCxkW+1dF^M7hW!EpC#x`_gr99dz8sNHh1TMnHdM zSALsyJEW)1K1bk$7Ty_jXsN7Icr9`SW0VZ?2xPz34bsl&`4Hx<=+LquN%zsP>zz%Y z{w^H?w(ir`&3&@QhAh>^&H3W%Iq^><4EcQreZD>lUMXpZ;buUce9K)5HN1#3QGM;P zhPO1H4P3|yxDmmZ&F_xtJQT7&)smoEt<{h}zCJP2n$;zXYE2R^{8T_c=de&OR3Nw@ zN9%mhU?mVJbUn(T%nSYx&Wy`w|#|sik^AbsOkk4refpdzC zsRp3__2>?JX2H^eXf)KQ#f2iidRl@cp2kQ}`gzIn^E_>ltgS8xZ zo{Zpe`=b^6nc%`Tka*3-)p@IjF(H%ol++`Ck~4m<>^WA28YcE+cILrJ<*Ojb18v?& z8Lk@*C$HN~mfwX*0Q~}b2&)7V_71u9cu4D`$H4~Sh3YE@_5lWe)Qc$nW5r$Hc2g~;7%#al%p*U_X1GVQm zcR(vSz?~co*=^etMIn= zW`MTF7;M9{=3st^2rw(rqQ5?ifZQkArtFJaHZfbxSFUCg^LkAx_<5A`ez_dU&f_+}MyFM?sXwn0E3~1kxC@)ycyLbsE zxdLTF_nMJM%`Z*Ng1&~wIIPDjrt~`$L!TIluNRZ2(`$we-(ci&Zy%C)9Sg>({<4Ms z7O0ra{7Bph0|>y-h}y*R{Lqr477Koq$+ru>-ULG5-1i=ldG90wddv$e(Iqp|1~!MI z?VE8AO6YT;5K~2_py7&P3sIU06#j--*&^~~=(Lo5;uXEd6-{#-Lj_LIzx9d56(8a# za&$;pbeTkaD8j#2V+mVg?uD$OczgCw2aqi|i`qmpo!OZNS)ib0L>XlI%rhb!4jbe5 znn!Q>a>)k7Q^b_ujh=WCxY}EBKatKRGwdaK}7AFCMjk^VFQCrklk%Y^unZ>yY<$R)t7RHV@<@8)Q>p#@z)L&z&IoE!pVlWXZZk91d5OB7ocCv zi)pk$si%LjPiF}X{qY8YY3$!&yQ4{L@l9+&k=}4q#qOI3AlBWAjSo0*uf{St;)BH= zu#)&tW6O#W61z>TTH%xGQT?rw`c26H`7-r@dKWA&YghnIwj|GqI=du)hPgN|DqVz6 zxZ_{$QC0WSP7cra9lewB;f)v(Md$*~_j8L*tl$76cdl?g9z^l| zE#HqBWSXTTKNx0lNvPxx3c_thiYNXo;bQO;5zOrb;@ALa`CpVS`C%=E39k7mu0}uM zy}{M>p_~ z|Do)iV|)qTHt#cYX3p5QZQHhO+x8isF~4Kmwr$(CZM^f_-8Y+k{@G`f>ZH4quB19u zo$Bkp@6R>E0CM7yA+%j{*yKbbIbG<$tScJrL^27C`N8Z@n%X+~Pnmn1>RIH&mJr** zH&R@v5I;>?Ng8m$^jgNCBKUb=bQE4jMGi0S6zeF<8O(s0fhkiTJcTD%xV)RoCNIg? zhweE)#2+=tA4PH&7gDk-Kp~TJ>Pjf!4C`a8zh_$b@BJk8AJ?|%hL-&4^UcgUc_E)Z z^rwURz~m@lRooHmRet^cW~zk!iwN z5JsmGArGuUOU_AURgQ^9uezqShbSq+pu7k~xG=?-NGr`MFx*pjd~J|ge6a+ z#`nL*rnE65gPRoee{1HEqpMi*2UA_E$puePmQOE5$6pFTqy12Hw!6vR|YY+#q`c_}pqqj^AFI@*F=G$6t`WGyEBvGm$Whs@%V+Hj| zML%%N$DCLLRWgh6&Ms$nY_!4)@vN+{c6o}cA#;ahnSPZd@iJ$;1=*#@*pwM+52 z$5n2>tv}-Rzbq{H8`eP<3~<+xU&<1k`*V}hgyJR0l*idrX5tgoFdumTs9*<>!fIuW zv==0jp{j#JNeA%*T>`|ggxTg~NY6Z$H1r$uv!6&#mny>buyJ;<=?90R9vqNVi$_Dm z>SpEMJ+**4i`pPBGMLRA?|6{^CGVncH?upevyVP9_6T|1#vdH8ek=|6`nSeV-l)Xn ztS2}>BWXl_EDVWPx&Pyul9A_{Fq)9mV<9=+;*bQ~0cSkQDN|_6n~CNl!q~0n&2rg? z@#TqsK#=V5Xk}G3z^iI%E6?e^F@tE6=Rpj}ia%hqZHJ5g;lmx3<8vW9g<5TUUB;h1 zVQSbOijOx9vw}CvX(kBcdsK2;8Y(9bBnibz%ZCq!g$u_X6w*J9sH>q$qUPX#_{CeJ zY>ckuU)>gfD?Qgc%=GG@440P&cJdd3(%??XcHeAnAEv{NorsCo9F`9f2MC#?Xf=hR zxdxy$kvOmGsm`cm&TtRzL&2pbZu)jQF5yWn#TPv!@bGibg#I(*te7ragHI}#BPn;L z7F#4biQwc|KFJq$exn1Zl8q{dqY9lk2d8LwIwvzt2u52qD-KXOgS{?)0-isDfT7KG zgiBbNNp>o})$*hqUnj?iy$pl6oK;W^HVdP(A}5!r!PYF~wBau}oJsDu!2`1SV0Ky* zDOtg!b&lK^67RgwlWuw(@hT@@Ns~Oj;OWykXT#0Kk5EUqY})6@IDWxAV(+2_b;i&x2@KTz3I7WU{g&wwBu|;d2Es! z*2Dz zoS_NvC7_{%9H0ekdKlV3NJqstW+*~|9t6OfJO5obXIwMKOiv|ap_-^5510+C7yJec zj-@-74u&g;n0K885Pv?{qf#Y+863*I-C|e~UK_wdS;1zPk}8_^6;24oF=CO7c?f|A z@Qs&+`O0?J)7}6 z_z)xasA8$z!f1W3Y*oTnbeeq5wynAxbv= zI!7YR#B_R{Rt!9aCyn;;Q&WuTx>SQWzW(@0=rt*9NfjpOdh4w`s~>qjm-v%Fi}M*) zk)}(en@!-7<+FxTic>QOOj8K)`p9807*n_Y^y8QEA%QX_7=xzA$WPe~4WYb<*DUF4 zS&jA`&YRH^Z{vpAW>f&tiv-*PkvoUPbzvzwXsz`AJDtJSJ$kbo-C7Hu_5PMB-t4FjvX}k!!CACsWIS>CQW%ic1|}Yc!`D9 z^~|=lsU_m=qa$NdJ z>f_US?IOcM*Nh7CU})nsWRF@nZr(`9;+!?-`BgIk=pG>o#JiIs5>n4aCd2_sY^{Z? z-oVRv7zQIr#`@&Q?v<>6A+xb*t*Nh1&)`)nnE7qOsM9UTP}f`Arbz|)y=?XCxAixL z;~YDtHTy*-ECu%99MZ)jQ%umMI*bBT@jz7?V(SWblFNJ9`6>nU1nax~RK3BJjK*KB zHZfMWQmoPlnXq=B#e@|;$K7gd;EzzJop#6F<(l$?zHN4P@7DW~AiY>BJi2!|+7GKx z{_knWopzZ^yFgO4Xv0gkO1OeI9bpGWcIbp=mVEZ-+__f=tUd+Q|IvPUTtz)Z-(yCQ z!SRU;Fxtpv#*Guig$DS9Z3=rEcH;~ts0jaQe-Iq0+j$DCe>u9xJj0S9)k_VnD7dQn z8QhF}cw~9PqX04KD}%)w4shyKWApt5*83|2@XoiE4p(se5J+6bdX(+9ny3Tz|8rcR zAOZ@Ae{|Xf%7_ti;Psc7B15pp?d6!NHjKWfm~bJj9a)VjoI0*qdu7o+^~Tl89JcX1 zYY<+r2rE(gQv%%&pI=$wo~og1uce6B=9GBDsC@iv<4;#iW07M@EE{gvP=ZOA#g@zI z$Nekof)y*db5b-i&b@3hrE4JabdWLYo}mrh3hwkzQ}d3)m+#*q+IkJq$RfVfhUw;c z+M#3qcQWN;u-`LT^Y?_L|H4QsWln|3NUNWp!zd&eSjMjx=RU$@Uk)}w0?V0tZr^-c$FI=(%O0$Z9OXJOih;IP(SM{v0`PKuwvtih9#X!WW(=0b7D6i2TEQs(D2VB z6HAlz8`|zB(C|MN6IX3HF=0K1oT9$j2YhXl7_QnrhVcvDxo|?U?=7uh4RMte(hDk` zkbP6%b}JOiGGYWTd&yffow4t3#j{)Z1=|M&c#6AA3#OsVb5ZQIt|Z_y`;5CNLX|#$ zWg(H0LCHgws0k`n3;KJ)gfB*wC_kh8J3*TP?s= zC}}wW)+k!D!BBu+2Dv@hoq31Cp=#k}7Yw^P=!_b5jYf|!8T3>I2U`|8=#V}z(n&y(Zbp#~fm*wKA^R~hl&uqZUn~Cza!YX~$^~hxQXx3*X7!n;h2f2RSk^C`7SJba8mGOjIJ%14`1m2Xv^%wI7+%JYY zvG{|z6=1da?1ryZie&?d?5B}lR96ix*gJ4Vd)UmrHz?N_@xxQ=gg7da2eiBm2 z_AlbK;?Ou-lHHooSWaWpU^{S7GDVs!S2<3CiViPdUQSGUidf>EUD~rMaU~u^bwaZ$ z-(Jk_ulxeeU({Q(3=2ceMiLSCWcKT8P|>_JM-I0k%o zPh9hL=LW8SzhepS`eNEf`6|M z%BX{Zf}RlLJokp3x3CCpP(OA}&kgem;oXJN{B3NMyD;RdITgPV^DinS%N=3o8@F>I zTUdCCh^apBE#Q#VI#nmIl`)hnG<;BT!tdUHC7b-S3pq~3{7(gQ>#j&EqXA03j3rmF z9UideJ+XkQuF!(GU*L7KyF?uks(}huPmODe!F`jkI?w-cnr>kRIC9v=y((qVDTQ;R zV|&+D1Ia?jCUD+A>tWh+3Q7vO&Kcn(qkNiY%G1)Jo3~hJp zprF8&jzW9Gni!skdz;U6Hp1C!PzL}8{?MTzdA~~ zH0H2)8sVsb;#+*q>)ccJI}hxU<6dm-80&tk?)i{Q&ai{@ipO7KkC^$@ zxFCdr{8s}yWg~l81Bj`mj(UlWSbY{Oq4e@4Zaz zG5CDdZeQhxkt;~t{VAU%er4N9WGzs<#%tt(8@&S;KjY^_qhVO>6dGn!%z2i)Av zAkLL7$>KRjCjsrXldRH#wmj7UL>9x{ZJ2clC0SiJrk#j}&{gIvJ2y9RiL(?URbuRD+*E zf}pZtY5RaOTm76>TMya6Az>_YH~q;FsbBX=_GLq>aN)djXleASW#Je^GxHyy44c6Q_|y843D}6ay-EC-b{I=5X{%te0dj8Ba>wC7;kS7TrDb zYt@zQtDf#AH>$G%vqwr4OKA{MN(z!F5>o%8jo-%?9j|sLyp#Bv*lL@mu`J&H=yX{= z4-BMXSX2Yc#zM?b%Kwg|lww189mMQ*TG+erO#Js}p89PbyryuVd)N|Iko39Ww@cya z)^cF1cBSsbSiz-~WZ9=M9I|MJI^jg#nWkpHLI&5iYgOLCp3MD*Vb&nHD?Xuy8<1Fk z^U-#Nz85IRL?lnjjYMM=lQ~|z@1d~T-%G)TzQlc;zx|g?UCiL**{UBfDC3XOB-4L0 zgHgoY$i&{+!p`=8qtiD%v|Xb6uey;W0P z(#24jc|pR`@ZI;h5Z{YsjHqQgie))OT6ic+xnGuBky2p;UJ_4MQ9SQr(Mo$AI3rNk^7 zIJmAmf!nh{JX@EUpNZP}v?TPrKLmw8r2jpN!haH2W&Rg8 zY%XR(^=iLIs~!(Vy_9836}KVJwaNk5@_bJ-#x29fhT)4deP@yyGz{yOl`OZraJvNL?iWw)n&j1Z*(8kt%~VN}T7r9%)7ouSzCMnjfEvZd z$$iBdYC!m9m5nY_x9@?8RdCrYe#M1FZQF%AoS2>}?5txBV_$Uvi0 zBCOmg#D034TJ!sNaU`l)g7*}3u6sH@kKm#31!@3;%|e-RnNqB&fK3bp3Q|jSY;%8I zTiS&(_iPd>q*j{}1(gAtx&>F3xt4*;>SaI$M{sOaL-q-KK)DJGe2e0ZGpSON(28k7 zeJQNM1V@0MM@8eqoXaE76w2LPJCbXKnF%F88p7r9k%(n^syZ8XC@e%tS7E(NL?h zuy!sf+L$xToJwYZvvSUz&_=6vY3`tDuI|@4h+akcxMTyQN)6vO zLcWDIOSXBqtwMDeMYR;i3aUoshFWi_X02cm(C}ka9GQ^nLTb}+J~9CMV*?=ZgBQi` zB-(P3ptebOwzL@}2AN(`ABRn#)zJ@c6}(xhRB!Ut_}j2kUv(1gQt)F0yOU>{VCx!E zqOV3vk&!^!{%8`>sz~3~W@B(#0*lMnFT>lTLQv$?Nbf<P+-RAd*U|6@{CYFf(*1=Xph@1SXsP+)Hx;=>wq_D zJn>_xGix%GvAm*mFAW~B1@OgLpM^K7lhU2&S~GOde-EM1MXM=9TTDc{dKqY{2F4}T!DQp^B)Fyj~EbplWe<(G5WFP}O ztV<4Be~K_YCe~Xyo;Q+e5X3Bt{Jf@jUj`6AFX~PG$CV=Y(kY~v7w2kEe_YUpOg(S~ z5`xe1XwzQvdTXUSoPCpcp)Uv!mj_vGNI~+{3a`84#}avZSM~PCWHZGZ5~u4(Njnrm zqc&Y|(jU!;)ub(24UlMl%mQUvH;yZfsx);V=J{+BP)xgO31PM{jHD{$cC1R($lKJ| zUwS^8q&^&?PFZ!_Iyf^^{WnP5$gMBhO%rSH^koUwN`o-2AyZfm>Q%>%r45Hbpx6l>8Is$v(uh|m;^ zSV9=(D#>la(br?{e+up*)Du|DoR~r4q0irqh92#*)?yYaqiZi5QLA~CMQ!oyZ(5p} zPfytPCJP1(*F{j~V{B#b24cO}9Sip_oAYD^`N*Er&Fi^bC41NPlc3wQ=f}Eruo*b#iwUuFhU0wBM8Rpf*osSbDa0+z8{!^&kP&-n zPuo!_W5wAmN|TqMixa0ev_ztcVQ6PWbgn35M}*w2Sbjczls6$RSRQQTWZ$tm2JXMS zd30zjpU|rNsS#om>%j1QO2F%y{1H-qzdI4Q;KMq+1EFRkcE~e-aDXu(->RClv?p_e z)zdxKXaC0NDu_BAFKU#38TZ^u-c+5F%58e^=NgFs(MflDW$<$E!AYQs$^NjGpz9L9 z(I-9_@Le&DE2iwv<>IiKsnr{uDOqO3hTvm9STmO(R#!Q=wLUFY-d@MM#p7xv+*|HC zUzHo&dCljfHw_uO;X8}di^0X{i^Hrp<`Y@_j@c6MB}V>g!@XWt_xNb!gGtr!xM)1a zVS8j;c6lIP7I}@6_{1(By(TIU1A7?38!aELjy&KZ@VWH&C*#L0V2qIXq7MknhRl!v zFcI@f?WYj>esU`F`;ReRD^qajjHzAD+l^ZoI+m>*e8{7Ir=LGe^0#?Kih-C#gYb2_B-F}xZV-ByN6JDM3=v?;2pqu|KoFnslGE{ zXi*K&CQDc)QfkpPwGW)w6)iQa&KRbt`{y$^VS|{j)>N-I=Pl}btMQ88Jl?oLz~-2| zF}8V!!As}n#NRc-<4C6$ALy+C>y*ts@p%V>8@ul@ds`XnrKv3gsVwulgjlYGd0VCZC95s|y1e`)Y^`5zk<49k zvv2;4e!9%)LppQk_$>58F!O-pcNUwpuZi>_+mN%bRmLdP5Vfz}#BPizhac; zhc)#s%08hk6YY^peq2SZK%zscdj_xvUc%~l6!vlU?X4B+4b2f!ELqJfE7=VuXY}^2 z!(G%p#Sw%o{b-B3z0Gf5w*shr{#2GG=tK>p@&BCM=oFnWT3I`cQ-?TUZ102kdW$lB z_i+b+L`8XLlR+q0YLCFVYd<3odGZcIOc(@njKIArROPFOFH@X~h*4}I^Yq2=IQ~lw zxu22YV_#`t_wk3eQxAWEAkzoAkaMajzvcWC*oC(ao1N~C0@ghIE9)|-BX3rK1%(0h zy|8kFVzkm>3GIG46L%wbyhAt9n?76XK!+D%1^Xm^RMPUz3wfn}RWsa!Gl?^OV2^eb zP~s59VE+sqp#%Gj++g;q0c+y`Yctb2^virvd`2tO|0shC_~F<{WCH!B$0A1$*G-Un zoNg#&8uW_!Wq2qPe)vK7RrR!g_zCs9}ZKOL<7jzU>eipP}>8&t#jKbo$ z%9aSibiQ(B`NYkdP#(ypFb}bBZu0m8Ir)h!nLGxBF>y61I)wSf4g;!42WTP30Y4v| z<;_ojwe8CET+JvG|2P`XE1pdFmP428$YGgYvJsy}cQ1GKH&UClf^F1PXbBaAg9f1>~a=Ldr#E)DlBWm7qaOPM0&AhB8IY z1?npK`qk2@Jxi8QtOadb#rqQ#lZR4ATzLi9a>?kD=KC7XMTLD(f9qx_$B56wu)e=G{=ECfs~-W4 zA~iw>@`zU^;TabIUonzD(Nn7YE=LowGa!@1I4LveR5 zmv9en>|jGv@2SZAd%OSmaQ^icQzQ;=r)pYdb2@gaz^@Av}4#^ zZs2^TpoVY{XDWWsvvW)bPd2{Q)90<{4PNYS^WM`jGrr6E`mdkkpH|y34J8Lio+k^> zi3JRuL$gZXi41SvQS6UB55T~98ZJp7FsziLa0+wH*$1n!zRMRy`EQu728&+mEd{v9 zKUegpx_LRQ2=X+@C92_xgVKvb){9f#D96~1nfEI3QYvUTXOLC$6t3mWYE%Ofvq@yO=`NLM9UwN!v6g8aI=ZB*TXy&7JtG>kT|28EEPr=g z)sT-sX&>45jWc-<7)0(_@Sf2R<=<}^pn@2e_oMHU!?9yCk;fj*w-6~-jU#S#{};QI zPXtGA;2#N!?#H==@4qQS$wE#up;#F@7OAH;hv-xc%^VFfz+!tTJol8yNYkppMo&bAuGQ`hI^Oa=4S*m<)>F2 z(;ZA)_n*VZ)xU~v66x!S7I=KRNh_NBl($z!MU*Yu%GYF7Hf>gt-trnWsSlQ&iwQC+ zY&sdrWW?NH)*%DI;l4?($sdqJzehUT%e&_xh@@K0Qj=frR0-}SsXC~wQJf0vRJFG> z$0`BYNaFXudEwhsmm5tgQ$~@F?#!-Cqv5qi50{!G4_zu-vL#pd$VBgxrf2M&{E2ahkJ8&cLEV*jtJpxMW!k6RHS}bhwyI2P zQ&CZCb`u%QB#O-D9=~HmwrO6dShg8P;cER1FKM1W6-gnP2|`UZnX`Kx@dWk^WK83;=lXcnMw= zD35l2F02C}(|WCFfE=_BpuOl)g`%s%##tL2^fT}gZ_8!YCEu(JQ0+OO?j-OMj>$tU zclADV=?eh=S(6w(B5a|ZM7Y)=Q*QfB`3{EcCKd)wS0CDToGUmsgOIq*5vRlxyp|yx za^0Bl_Vitn9NUK@S2@=7H^&j=(Qa>tf+3kZ0v*ax2OMrDICL0lB>zA!yxSH^3C#UM z#Ghz`w}-hJLBd(l)*VsdfhY_JIu9>MM1%7M>Hn1ZwpPITN`IEs-OuqKN2325QS3i5 zpPZqkiIFog!~Ya{qavjs`{bF^Or|!rtP0kSnb24R!saxHTrC{Mqe;p=>%jSg zS`yhP8kkefeg47{yt&+y{>(!^*cqb8rXl;rxj7EkAak3l|6=v6W-u|j{v&T+{(M_l{@(^J>tb#F{|nqQ zcJuu3A=$N?12Rg8f?7G=LUHj83sD20pacby;vxh*Acs9+GHsF>ZLBM@i+Uhvu=av` zJ#oMJAah-yg@b>u&R#?H1g(+}i%k|7!TayQ2xO4q4*U!1ol+I! zpTF2AAwD0%l}FL;3CKf4*z$mc=ucsD7gX$e8svxdQbgxO^?^su^ z_c$WQJkg&(HLdBRE8|Z}sR`bh61EdH+H=Cm>f;#|^>Go`2U8W&c3Vk&X5()*=ZtEO zcLK6&jgv9QGsduuX67iWElK$?u@h+;!;gN|Mn|8^oYKyL=@_nXB_>o9dvy|{iRH9f zsZ(?RMGc2r;1G<^{PoLh=D!Et``^pte@>K+P6+QGo&(>P&lHE-RVEi30WmSWApV7c zXJQI;ujWTntYkEy}pgNk`%UX)d+UO>V zRaFV}L@!H5KK_4M>>CdA(jeeUvU^d5o0pfQ)B$@`g26*&T$$$CbJ*wD%{fGyjq}3) z%In%+5QJN;Sx!=i;f zQ3aOdH23wfhnQL*f_gbXroswYt$3H#QwE-oma?VE&>o5>3RpBwXDgvx8wR-M!;C}2 z$ORVZ92HWnh4dkF^T2=T8Q_eosie{+baMiJs9fxz#f$^<;HGA7o*H6O88vw$2HaWF` zDFaq&!Wme>ehHTeI$8XnwY}3ro!iSWV>ij^I1kt)Lku`mj@<{hu*@)&Abl}!F;87D z?xkIQ0F)z(!AC}02Ki#)KG&SfdYjT|Thwinu-K+a*uzm^HvkgN0E8^z;DU_=8=2O=Ch-!NdA}(ZRUH0=gWZyi zRMXJ;-bC6^+agX{Ql$$`x{*$8l5+=@N;Adk=By@~6PLJcnUu4$Yx}3>iX{3#UoB&8 zWj#+9>CzSsnex&!RoOyQZXibzvHEGjxLaRr0Lfs&AF$-Sh(@n)`PDj(t-8mJm#N^< zs*hdmTfc#QLQ2=DKwE3GB0W*`E`2ex4iX%@%7{f7>kM_Y^~7km#lf?tAvetx4So35 zd?p7_19QSgYp;&;x+-B#-tP8#o>@ex;pwPrXZynOoacJ>M-L}kx4ce(%8m+a)e#5Z zh##Q69Ls1oFo?gRo@r}V(wu$DQt4O2Xs^o)eE%oC|B(6%xL$F`=(R&rtOl=<-GI!* zN;*VcJ=f(9s+&S9K)!I$LO|@iPUd*X&60fFD2x^?+n#LFCTM5rWeSuFvD7(S-`edR zfUOQw0ZPC&>YAGQUk3q#VC85u`@O6bawEJsu7M-MtL{QA&r;S1(sf&y43Z8+ z^NmK|1L5_-%X*x&-Oo@-HdE2;V(cQ^3TQTjVt*ntV9yNaN4vfOYv@+1XFE>T`4j!P zf+k&w!emP%?Hd+v!4Hd5?bRkt-Y&IOxl6gJQN~@ASRJCBuOY1?TIgy}bIYMCbf2t9o@rI2bp51hGFWA5 zJOxB?ywD01A|?;xbOf0Rkq|JbEMixT^*1t`-C1HQS^X(P;Y42cFMHb6^ zBjm8~bx4z6`)vJ5`H3neHBeGKmn-RuR#83?s7T4QHrnUFZoWW&;kq8%dangXMrXLPIP_ zP9l-xZlK%~L*t&7Tes3lDp<+~+EI#hChh9lJUn-6N;)uS*wP6`BPvkmu|lAmvR4b+ z*8(JVJq?m|^c$&Eow^rJ@1`6Lr~AjfrGOE4PG2yr9k*C@Kr3FSayZiU095@fVu$VT zJ70<1NPhvPn=Fm=z`9(|)LFQlWlVEH@yT#HHm2wzD@@@oD5`F+5NQ(Om<*zX}WW~yXjEyGKP$yn}t!^NX2_lm^=ilF$UtEwt@u{E1BtBSTt z8CaS8Q&yQmC2#cneo->n2X+ zr9;9LG06I!#WR(`B?OH1U)p=L)IQ%dY|Fe)uN0Ozi>mXW-s@+)bvw_n;)H#kI_-dUkm{8PxQd_=c zeA*0a1*TqcmnO2gK@RTHkpd@k44UTJh^hV6&D1Hk%W2=`-rs&j6$L8Pf6H}fO>k!S z2amirmgZii2V6jyQ0(>@kgSV(%G8sy>dKggICXxayB31@mn>RXMjcyc9xGKASM5x% zj{M-(4QFw#p z+WmsxqOwo`k-NGTQB^iFtmHge$y(<^h{DRztEkIc72k^dI$vLeFv=OT`$sBLF(8}h zcWc9@0ggFtzd6qzga(!MnnXLeC}AUW(?afZI}M&b&h!hN zl0%7SoY3-YsJmfp4Buj2jJZ_{OCxJ9u+~D3?)8k=si^NUY0|p8n3iX~5S|f{AKIoM zcu^Z$QOB*yA|0h+I~&J?!y5>nFP1;4)(^j;5z(Bt`qx%@?gn&cPaYTeP~qmN^G5FU z%&YWNJNT4{%LRd@nNOXFTf1aTVT!$`PdDIvr9M^9ijX>^HK$hYd_n~(&6H*G3~a`R z4%lTefHWZWVxffn;n(IcxL>q9;`PbakgtrVnOVnF-ZYY=`tFH31{H8<*FRcT&@N$e znt{FoFbGj8uZz2J2$ewxf{{yX_bNe7hN)`(kQc)O-bbE>XdM5Ym?;Xot0cIPZ?^K2BvIJbtR9NqN~J7|)& zxJjO^k#aX09aTy036LwWK$*Je{sQfAKG?j$D&2tg#Tv?`JP`Iy`81z+fvgUebz9;q zcREbPG!r*5P|4|MY}NY=oeHskvDg0x_c9}QQp`P-{fJZi93;_gKYaT0Sn{W6icoyA zx>_krj{ed43l857D2B;r!7J=%h_6+CvhQ#g0#-aNbm$j(h+fghrERP5K)O!rAOYp1~X5l0UV z>gO^}1J(;?m(DKluxxHQlp4tCO-+stYu8jtF-C$xVk(uDZ-K zWIp~#3W}NmA~jE<2#$pqNN__iiFJUqa{|NxK|g0*>RV; zVNLl=GVx|%ow!~EMDNo@TXkyV3IZT6e3-p~KbR3-Nf{}?^Y6zZ0*m24nve(t>ogLs zHEM=A6nND^ioT2lNffktEsstanw6#0tbf_6Lb4ULq2(kO7g$rUBe54E1F&ni~_f9l>nS8XB7&N47ElN@((Rpu-ic+rX zB&!Yfly&(qWAj}h8!^VTQU~+ufqc~{O0i07)MmNchmwN>eT1`N`|#$ z2Mis$WauJXg2onctd*Aw6I^(=&uME0KtUX4jit1O!HE^IBO7v8D!|dAYGHTQUj`|L zE1V2^(|Z)6_TZi&_A~!&(@Q@en%CZM8&WZp*XW-=j7fv6^*8eqn;@7uyb^-kFMDfjy&s9HRB^%&jkbV z5+;;Uf7qJ5RDt4-@RHsNU)sX8i)frawLCYC8v`8}G~t}$Ap=M@Sj=jz05*o=a(#cN zH*6tr4IWl}RN3EeR=|M<>4r04zwo`|@w2~Uj6A{MR0?keB6J6wq9hPRwU9-)WD|} z3w8^c%}EZAh#7aibE-=bIkH7zW6a!lBkCO5yczW`kATR$*gnqugAQNKB^B==xGyIg z(tD{h50aO9;pLOrUfzP>v_dsCE7CI8p6o5gAMfPzF)MU8rbsVq&r~O7kEElq$^DfhMt6L5cO`GY@({FvJ>g+z2XpFD9HrPpTl`mv3?0KJ@Ve0@ z)x1uSe%-tiGceu@+(Y-ClI2&yYqYyIwjsG(>$L}Jv%9F)#-1N|ptt#|gflnSmQP!$ zUgEITu>p_SyzFfhuLa;sX~ZiZ9o|9RJAvUtHFxXjy(vDksym<8-aO})E~8-vf$kp~ z#*`F@ra1XzR}#*}%RaO!-^M_iv%$A1?pchrr_e_V{r-xOiyk?!;0J#V5~Z(dO5(^9 zmbh!1tb8qyV~$h@weRTl_gTvVZJnm4Otib`N1A`fnWc)Hpqz5~QIUd*fh3pSoZocP z8D4a`%KOBEApKtR9*gL`TrfdqhsDL<43C@9c>Cl@i|Cj{u>IiqUUlU9w`0MZF?8;e zTf{+oSzsYx4=?hFZ&N($JPm!suLZ@?_&iK%9!{Hzx+hz~)eDG6K zgK*2V`4FeCbT|q8)7$UY9t{MRpd*RzH}4MzMMhM8j{WL@T@+WC(!x;=HJ5W*)Go@c z0O>SyRnXdEopE6F@0g*f%~&EyP<-@@3Dv$u)1>oTdzIdzTyE*s!H7WJL{gz6{qCGw za-Of;BAslX6suEZ2T|z1@l<$#E`$$vC&y^j(*dEXDMRs^Pwr@(&>?+@l1vH5kW*0w zK_anY8)v@_(ZUkkM_<8XbOnG2&U%8@z7HjAM4}RQ zzN0`EsoBb%vIl4HSV$PQ2kG!XUs5cH{7%Ev%iE{S`~7r8oLkjr6+8v8sRKiMf@#MA z!{^RbA4kF83oXb3VAmrY&{nhrGbr0h8@O%<-e@Plw2bC*MSJ&a2^6kmIIp}qKqS+% zaK&xw^Fg_vJ{q+0+H&XsGecrE*Jf*1id3;1lBCEsp!N%HO4C8T8NFI;scPzkaT`p^ zX`)0^P%bE+hxq-R1>qD`Z|-4vg&)vo6JB$8Z=6+&tzn7wc-fh|racZ{>lO`T2A-Wo zjLRTTkWzHVOs5Y57Z?uBj7Wr!zaY{0m*bF({`k0Uwq~!pC1SG4?k0 zF!j_@4%KSwG*(NKMHq3sc!w#PAa&kq>kXUizCf48JRP?zF;z4Uhh~kCo?J=8b|?m( zD{LuoSLcpqCivRv0ezc)C#toDZ%0^yGM-fFcra{k6niwT1)l57c>P*2uOSblV$Jf^ zasb`lA>-E4mS6@_tb16zk;N;6-axUsh`8)TsO6mU!^iQdce)^v=JN>QL=QI*0Dr|0 zYsv{>c{Ah481euTb+042CINYI95$BPlB?NlT%-x3u%Ssw3(|-3Yh&X;Sj@1Nv6dWY zSB(Au&N8{tBZj8yW54E5x>}@qz5CfEhoL5$oa4O4e$>(6CB8p&0=TzO#jf12y7wr< z4r{Acn%p_J29Ipe&uc}~|B|p-n z5{9AjIIZHtRF!x~!){&1(b(@O%HYO??kvP?XAEFp`GR(uE=KANlf-2;8mR3S-C5JO zrHG4f=8%sYM-#Va6i$gFdf6CwiL;Zp_u3G)x`bNvk1~da_NTrz$Z};(tJ3-#v&)Z| zf3TmziE7uvo@TgR+a$`wimkRuZi7nNoTPe)$xKmQ%fs&+7&ec;|J(>w&jqOcp%IP# zV6b%Gdxi@S%z(xg|3D| zrj&h|F-&B0@RBjtx;?+Om_C>waxOO(Oj^O=nrh_BxPS1gnoFT)jh2IBW$`!goyIcS z`vz(h5WJ(#Qi7n-p#wo%yS6N?4;w6)Mrg;?$ZOk+*jIH`dHDU+6VlrM2ZBI-zjNy) zFiiOmTmF-yttXfx_ueaR?*Co>i*fn4x6R9YmJrmKu~X{EH))hD|0&-((Y>(Q4&4ao zc?`&r@3X*$Ft|yFqiW6sj;SvnJU(r$Jc)?aB zs)Da3^Hn7m`);(=rV$Ak9RPPZcEi`Bk7#@4woK|3d>!#uvCPgrcRwY)lv<8oUm+Mm^bUI zay5l9o=Q-gH*em|&fTaGO4TV`&zsnmXpeP;@H2K8D)i3Nm2Mm7gA?(TqfY0dH=)iT z7|3og<=I7MLgp5j+&{?Vvw^4LA{M;l|CN9cDK}UADrZI52Xm7 zV46BStI1KPxv0*!X{(ygCB7*)vh5A24~kyYyGUq!y`$czc!RoHbb<$hcspIyxjKiG zYjIRFx+S}bU8NLv{PN=(EHu1OKf^WUBrJbA#fS8XqalLRzgX( zR?ExdnqKvEp^vZ0*QY+&RGW*Z4(1ihK+s!i3)6iop+*bX!ep1XhczF1@mRHTbTRyTDT03BxX`OQdwQZLH4SFzco{CtGTV ztrENvh>Zy@eo6D$7P0hnf?B^|#WF{|Ul)p~4+O4qb8z68_OndP5AsW`95d4Dr%&*q z7_GQstSg@G+BqkcEg_u9vQ*cajC<&{wqvGqxP{Ulo~g-}`UruNT2O4)a%GDhV{7w!^cW~@|VXQ@OPYihyxiWEioKWKG6~A8OgeGxz z$+*W+cjGNv-K*{+B#$3e`+vP>Pd&gx{2XD?yWEb}zmf34cL(eL>DaP5u#bglsxRg& z!oJq$d)&^f_OfU`s4Eq<lH>!KyiI$49~DJe%8dmCB`zFo{rC^vU5-N=V*~RN~rZxBYa|wI{{ZE*X98pe*%D6l|QGx z!9;wXQ0+lO9F$>j5JLSVyREVRIa#Lq8D~^t=@xC%oKoQ9Z;o}Xj9=Ov?`ZKuTzL?h z-Itmi^$Yc~t$wb4S)?29Ayd8Lcaw6|tJrL-U#s8v#X56Rxx+{LDOpaX3}$q~&R^G*=E;+A4@ln<+Cxu-V>;auIOMn3=i8NIO7ZAy0Wp7X{!OZ~g} zE{0=>(tlZ$-Xs|5q|W~q?#g%1Q9HeF+X0fcj6_NBzQ|+c>yqPYjO-S zgiTuwNoaZxrJw}U%>!}KbDkAt8C-2&SI>L2CQY`CVD@wXH(|$>t!(`oA%du@w~UfL z+lmL=NS}8yf-%U()kYaXa1Z4KS=U%+8AE)xLc1kKORSqi%auhrLYTiewmmjC9_z@S zbCS);)#c^k>RR1IT-~-fcXv95#`k>ns~Ow>aX*ZkxIC^i^`?7-ftus4mSu^FBU}Cefl-O&8qQ zJ_o*eOfrq}ys=3Sd)Ppoxj8a#)2vIP=_fSkK2WMS0o0Q2jmqF^!;NU2QDqxq|~!eYASvCYdEH#$d zRBJ4EM59iM7%SY#<4s7NUNKe~b^Oq3g42@h*dFi7mRQd^b{J;BdFErk*5Dne#yWyb z@n*ggjO(m%j0=nl8K;W~qn9S@I+6{st~OSuS)%G?Q|BjGPhUcqyfBt%kGHxTB$&?W z1fT7d?5az}({*}RP2oE+LQprm8l==4dc4&eynE9%oDF^LhrEky?BT7XtF#&%!6Qxt zg(aU3(oX*KhI~3GhRdge*ejpb`!w`+SEEyV55}bgCmR!Sx8$CFvRiU}f~Sl$ci-(8 zlZ-H5T}CL$>Ict}Z$BAF{#b|<<9}bD%Q1EuT;gS1;TWeHr}5R5IXPS9S}pHCsqJF} zVQ>QN1ykEMksRX###M~j)j9>dyo(Q2N^Fli#s^&vT}udgN0zz5Vr}zvwz)N~mo$Ry zo>t&~F}t#>_jUiLgLCsZh31}npw9T*1o5a}a28^+t~u7p^WsKt+Z`8%^EkcfkY!oC z^;|wojvraZXUo|wUGZ2t-l`dSj3avoobB51V=`z6rbk)AoWcz+18ex=R`{v~O%~oH zjOjmYcd~H{?=t5x-%c>JcCd`ydS8F{HWvvu__J(Nw&*9wx4iV^4FfGf?|ydm8OsSw zu}+SMJY?#|96&xpJM>jMjT)V&02W=p$w?sBAIcKz(esk<(i)^O%U7q*5c z)z5CcaPoz%kxATsL43hQ7epE^nyniw;~>H0IDUdx@?#v4ROC2q$9UX$!d5>wp48Q6 zCv#G`>rxs|*$5g>TgC+zE+tr-W2w#alC5#eIBd~Dw=AL~ZW-U;t<}kNV$)8`_$GVQ z`g%@#yJdWvk0pyG(w6Z;!D%^~!YR{ukq_ETx&Gw%V^~7@F6Q@)@7u;p#t&>G!@Kwf zb$7U>9qc*j^XLjHpYsY7bCu7=kBy($veft~A-Iw^wWYf`l&*MmeDrssBkl!-7%1-& z;i926o_ppsmaI(2TDG#u?tZuZ2$?QeD0pLMXZ9^;QAdYYXvb@Yy;7e?voO&f$7l#B z01Cr63meRgEXAEkouXj@BvH=AH<|}2N3=$Mn3GrD}@Ab zFBHCjLHJ@G+%gv~uEsb-Jb3$IzyL5Q9DU*drpj5h(I@UjZ8%yxJrb>*p23U^8rSZD z(WtJKwCCun4}(yOTGXKd()bmC8mObNpiBt;-*f`jnrNUmVQ7p-!US*t0ra92kD(S{ zLmduc40=(ICp3m(C|IaN6J~1sCwaK?)ma#;@t=fQms(9bY*%70CqSM$>4nl zusBBvc6fm25yUrjA64#?K4cr`VhPB2Me~<1M?*fxgZw!t&G~5f0GcBioVOdM+5;tL zMKV}ZcEx95N6OF7;KC=vk!bA!Z0I@qU+&u_$}q^I7zC*)GS9?H;A}dAGSZk0M*zT2 zpzu=+#?LVvzrbkx3K6{GQv-HcdB1`-rNC)wyG2iH*vZlzLunFiylRDs862%e#8e2a$T1*+BD z+yoofVHQ4xR#^BdO7Uq_A;{eEIs6iC_c;8L$6>zG9L7>yiyl-#VKuJAZrlL_5-;FR zv?72YzJbr+E|{?S>|u}p6%cwJT_4V14Kp6V6-+rTL#&TnSs3TZz=nus{Q7YD-CCqs z#IL*?6>hT~F2C0mgm4BA=)ZirLh`@sH6gWf)r1tV`EYAb@qHZu{rrZ=VO;DYzc%_5_V!>{xcnjY zIMg~B%^Ez-|rFlk5LD7;lo zU+I4Im6r9q9<72N{|AE4DRz6Ldtm65?NR)O(1VNk>i2|0a2kzj z<59ducoroG@#l5ruOGl)pX9c`6B+~6foEYiWbj50##aY=@n2ryy*GOCc6DHc+R)HT z^e8EbwR?_!mI5I|Q# z(fi@h2N0&KF`2HxOu81$bR9bBL%5tijBDv5xQT8+4}BDO(TzArH{%JqMYCfT0=Nm2 zwGs-T6X%jarZxhe^jY+z&!Q*E!b~kFPikgK%`6rj_sxL!W~1eVhN7QfF#R;ImvX2l?O!x!`r1tKJGbn(V2w|3bk5(4!5^ zXfK_bp)-1E*3yOy&DE-if3&6tSMb#m0_&l}*ivmA!mbKhnxPe}e4bFPGt6+KC1(UH zgO%pJIITKR8Jyl|S6kI~hSoHOszcAB>LA3r188mxadUNu&8q59qjhbN_8fh_+Nusz zn$sIJ=nSpryL*maol{g7B5)K{p4jX%X}(E~CM{&anFhO0t1Cog@@dp~82z;e0-V_{(u+OeBSzzp+Q-9sBZR`*bhSbdb?Bh*4{wN>GR z6sI;QUzpvNO;C9Pq4+13iYQ9gx2tiNs6IMPoC_tEpiDFmpeQ$ECM!xx;0#aJr02}6 zjq(B&$zaCPhG#J*9Em@N2bw%p6&(7dSThnzf?I_ml|NWl4j_Jkl&I zAk9#JpxHt0!FZP*L&A|PMcnq1NCjO=)Jxq@=260iJc2PM)X-qg<`dUJi4VaRA4aLT z9%IBuFiG5iMsXwNi<|RCw9p^XLVrXH{SnRVH=-bIr^`G!`nG48wM^)xD|f@5&`VeK zz^tHa_D$%eYn!8GlQVSvU6>y&i)ZNiJJA#^Tc9tdM=R)t4Bgy=@~Dhv=vLL5p-(pN zIr>_(fQk7l#9<|sJI7FaWAHe`!G}7k8{Q6 zuv+XvOnfmH#nmONa+9KzzWb$E>@(q0NqHV{DE##$J3xOsdx-S#6b)bU&C4Ai9B6D+h@*f zk2ymS(JU#@^>i=Y2dVMi=Ly;La3q?cJuG4MQ6JAQFrFFO>!O;$N5jz!W@v$G*yk%@ z3v>iiO?pUUTF&sqcVUVb5fVSZRPm!c@tN*pIGw&s^;&$UYg`3PMCf7qN}eU>KD7_z z-P@lpTApQcKZuWF^7wGj4|a(8*i)qBWkZ#Wb^gx(DO{>$tP_tI-$UK**O zKSvuhqJ&C}K&>A_bRdqP(u8`SiH6)nl;aSP<540fpi)jktqfzNjQCiOgiQwhMf1Df z$G_gke@qd6zfONW0j&S=kc;(y9%8KDJV~q@5s*zNkuy;#XQNi0jgfNBd&2rHI^tn{ zpNIAMe5@_TdY_B((S1uJ6@omJ1p*k1;b=fPs`Y0c>gD}WUyguWffBhA<#IKK%QdKx zYkkyf@=zb|qdwk8eN=zcg%I-i1Q&t58ha7i%h;EkB*8Z!AlpzPH=|N+MXhYdNV)C3 z5L}dsK_1p=Pt#>*o;Crj=pow5Gw;$qGf(SirfW_|%PSC&yHFypM7jJxo{BoglqJLT__mdpf;| zp&GO4>_f^Yq2wV1NG z&pZK1{Rsl{rznv>L%ICLyO7jIy3ymtY#r6%AkiZn?GtAS>@Cc`$v@;7pn1U|uLBaZ zvQv;Yd12%4`6g#cIHEFQ&e}aV6n#oqJ*W&vqKAZ)5p&fB_r{*+Q(}1b#_)`oXU8&P z;o3d8Irj)Ssv;~@0?kTdgHmWy26n0du2LpGt}OJZAoi*dUQ{J{ zto)u&>F@ch{I=`k6tMCVpOt5O**<4K9?=ZQhz)&ssfOqA((!w6vb>}?e&Gq}sa{Uj zW*wjdz&KAp-&J@twd0*Dhg!`HW$T1Z0?W8g~cFX zmPmLI)_XbcQ9cB%x!FJW>3u-77pU#hoZ41xaMWg$sx5i_jPm*k@-`2T_Ah$zm)7vJ zDCId*Av*QU;J`oz=_Qe@;~dF4&XKI+9Pu6Jh>T?Y<0X5Jz83Kv=lmOy9I_#(K}Rq) z=QmGA2vC#$K>pC|6E0^(Age0F{3 z!Vu>v%nCiwRh~wyh&&6E_MpVmj9mgbyi5pdQC26rjQT`gUlsn;s_>`Q19YFCit>{~ zQObyJ7iT8&&9!FR`iBr`f z%ur9^4D~b`)iatzD_~(Fr1$`RMQ6Vn=wY1+o&iCIxC#o5`s!+JjLq~(InyWQOwa8H zW{PWkx1UMx!sQAO_jwKgxKD8az||?+LE?kH{%!K~uNjWWjQCLBiJYIfF@01_e;NIt zxYh&o2!!r7;s)II#NW=&PvzJwVY(mKryTTRm8hdDNKJmm1N1 zE;UL$YV?X*_GyCfMWc!`$HpOwhW<1ed`CU!nm= zTtlzuUIJb(7XNfhe<4)8;`TmdH>TtZp?+Npp$16|NPNmmug&vZn7i1Ta0wpS7uHsk z6+T2S^Yu$T7<7bS=Naqm9; zHG}j4ao@i1Q)2jT3=2OctlbzKeo8!`!H7NiuZdv?#1{)*TN+^=e@v4m%i}??oltp6 zs613tdTy4>+B`FwU>h^x7_(4e%*HTd4n`Ss5i#cDOk)A&84Iz%I0uW2ML6GBidJJe zt}<5OW5ycXVXVcS#`!*H?(`&qHS}FR8SnDQ$X8#|vxCH5@!-Jx|88IYZ$`jKoB;oS z*oXh$rtf%cyUT#!69AIxQ z-5l)|k9m?2Js|Gm*ALQ5e0^|Vb7UU`w&Nyz8V$G^pVFT$>*|JWX zopz;{biP$nYB9jGY)|T#kvxE}UjuiP^^cF~_(H%ZzKV)wmYh zjgR39;}%?F+=`oxPvFzWZMfa|q)+K9FhYDyF9f%HoaG6!+vn_VO>@>ukJGCjKhN?E zqF^{OL_CoZPk*`3T)QuCt_6KY27MLxxH#m2+U}{iA*{Z>&e_dpVbp)QT5*W5&=D*( z(VKGwjr}l)ARcZN8}Wa=p_V<@4+^{56Af7@kb=Sqo+ct z9uX_t7wE4(gIrY@3`Z)&cQfL95A_+&tH)u`3*to&RLUF9;M{P=J=DOV&|{9}?+6(G zfNlH}j`6R&vE-#A{jm%d*NE@yv4s4w{6PG$uWzTv(kp(F5qXP;9!@|WhdVzIKRph2 z{?RLba!l?7$`J@uzzz(76R0`??);(;cU~5r-ca%?Q~aTni)(b9kD14u--_QAwRZlP z5r6K>nt8{uc77{f^I%=(jixehG|U?j=oNq5cgz8;fEiecP+%2G0&DUHROxqE=?|z< zTq9V?ab;C@K(C8I{dq$f@wdLC*?1h@ye_g+FzpFimAB^U_^{0hVos#z=SPL>C7f;2 z*4%m+=tLlJY2Hw({NAekp;U=$#NV};mAQ#u5dYL`(V@K2cPP7#Jrq74exRXnc-d|^ z1To-H@|X6&M-T|yaQvZQY~fJEzr+xKD6WP!!;xS{{P)2=L;3V^xbiRY?>reZax&(v zH+2uPTR7~&LKz!WC}W?471)E4z~?b2@TC)wu{R-L<*hewi6j1`aYti^Bf(yAG$Tb{ z-t-=4(!9mBWyhX0!+OQhV^5lAVFsQ)ib~pV`i|zO$6?Pi z;@2LauXxr#xTwR!8LbA+Xf=2lEzi>#iSd}w-_r@bufWp@yoNyFPbdrg8Rdc3Q62az zMh5D1?WI$N@ z27J!Cv#z=iEzIa1m`s5p6K|f0UAc*84o1K%&*N;h-(j^so@#N8w*CUB@Ft$1pkGM2 z(u>y&4o42k(7FTC>6PVCuh21Xj+>)VW{$}V-t@1}Us zIoiP0YzO2dUC4GgSFtvp_g~a`wQ0)4&rGb;b&Ya_DM#>5fEoYs>Kb*OiWxz`oQe{2 zI)<31V6u5CW|*g8wmAb!%+s;fJOvk-O?u!LB8b@-rYRf3WK5D_8PS_9F7}CWu}_SP z-HK0rSJ%%Xfb(^RMWF=e$tYT3UBqJ*z_}qVwuq(V8 z(Y9(8k&i=VD&%<7nTh1DB7plS)vb|SsnN@oStP!>M z=}fI|>O3Udmp1guG+RH9%I$=G;f%bDZ}AW&`WqkYv%>#;%AN@nEsz4B(A*I=LcR&_VZs?~mZ zYes(31+J8S=Jws3&(pcQY9&>MKIuNho2mGb_q!QAR&`_qn7ARHV=lGvi)t0 zgNa=u2OZ_Cn<-BvGG)6i#oPr{Q03Iz?kMx4u*@6bm^WdFc{6IwTQSPK4Po<>m}Y(o zXP7;hW$wlb^A22O-ib@i&tR8%4?beviyO`RakKda+-rUj_nUk1p!pykGatfX^UL^? z`LM4}hae<-bb^_4Jv=QHcKTPq>w41izt`mu7A{!$o!qS*mN5QD-XZUV#Ld{?y)g^> zywY=<&$#!bfOp5}i68cs50C1*b%~n1E9~h>L+*`afj%MUf^}T3D{$gpv6EobZkxF$hq79YCbYAS4TujT1T)~D^q*r$CYczSiHgz@0 z_sSK6aZPLu>8E+G=*O25{|ANHScTQ{Mg6&6Hlq_C#I^E8{rO?p>`C+-I2KEEiAh7> zLCBQ9Bsz*QFim+XXTNBuZZc^o$doV6H)ZpitcHz}m8N`gt|^;y`o(f!StYQoGL&0` zG0dt&tyP6_)=*5aYB1ZX#Zqe|R$HU6)*6fT)@WR4)nKDF5gk@NI;{vUwW7Gnir`vn zGOn|x;KSBbTyIVD4TYsJ<*#G}CT61+O;nEnu7i-T$X`R^dVRG@n+G@GDNobfAYauP zv|4P_n^N3lBwM{MjEQ%_Og+d$Mmn~GhM^&!}Kk)YVU$st2XQvdzb3}5I_?y zz$NlU{n;v;aXCJT+vSToh|Qn;{SMizzu)1D!#tE)6f6{nBWN&b=-fi?A3+WAk|TXN z(emL7#CFsI)?8TDJlNJklw0q^5bIo2TT4)9HDio*9-6F`SZuAr3TrjiT5EBkbrCMH z)*)tHgjVYUY_iTnyA{I@Ya=eV;<&=vgd40lZnie#&p{S})&G5sr-LL2y4E-h;q?dMtyF=T`Uzh@)__8!9Nbiy`)K9*xGk@b{OzXMK1%Uv z`KN+xKexP=->XNNKXUJ4rAL`_yab*dj?{BF-|aci4YwZ1>$!h`g|9x%34_AXiDf=~ zm-2QvGL8e%{o!Yh51&Kcec;dT4}SzlRr|w#_W1DG2{Vf@U864L<%2KK>2;OV)6@kL9gi zWaZ4rVa$jg<{cpVXoRu(8<+Vggh<{=2=1O1-iEa=4`T%}HIOqng2P}1t5Fslj;X;B zI6XKr579GyM9=gQJ+nWeG{#4CgGbK6k;5pB9%iBGi+}_T%6VVGsj!06FerFR@pw=! z@6*3u`FFnX|H`*DoOh0emg-xJ7i>QcTJQL1wRq<-1YNY6y6%yUaX(X1ulX&A{4v{gMuGJZSYo12!0$B zgE?hC5lWS+K^obJE=c2GNU17QgI#1*(6f)&^yUTe_c>&pNQE*oYDi?CoW=OgFd}7R zGO9}Nf37s{MW*(wa3%Z1vugGE7I$g)!2Q2nlZVX-R$&02*w4|&8#JGQ+qCn*iFRO$ zE?nT{A6OhIyD+1wBDEP+wFgT_l+C;w!GgBNNQD}fQA74%YNSF9*H;rF6>5aO8WE{b zBlT5zq(ar{D=SjltH$XK*^AsE}pD{oo_; zO*N4#(6h=xJ_nnBB*jxod%YEAaZOp>Js4Fp7+2i`yJj%%z6XPA2G`*pL~90*#=RIH zu27RQYDi75iZ+KU)MRP$KE>9Inih__j0o3cR8{UgjsVnQt%g; z9DErwgTKW5;44@ZeAO4}$*5A(y>hz6dR+W(u^yLSZ7H7v@$V89uRm9(ihg?J^<&qc z^Us}l!0z+($i&D~ytmSjd%1WyatMNJnAHpcw=r@+ow>JJ?)54x$Dsj1)auWX7^^?C z9<>Y(@$2VYI=lrlcm%=VI~Wl>ijg*9oGmckmOdTE=hcu*^bww@8g=AV@K4EjL6;d? zYXKLBGpb1!SvN<$Gd7k)vqx+!ZP3SM9O6w84eq%b29JqDVVTAN$EQDsz@DSeRU6Oh z;c?3WHCvB$&(VifF4l4ej3XTOf8{6|VP?bsub7Ms)J5%H51JC~^qBydkGoE#4b>N-gO@T~vQmM-HhakE&&aX!ar_H|~u@Gh%}# zsitQ(&|#DfsvaB61*`1QFzqoYv&UknJq~sDcuck@pwXU)bL@ItY)`@_JB-bCG|w*B z;tzR?CJirsTQq3|EdC81Z>azB@|+vOz2dWc0+m~<#bS=poIBF~?5OZl>O4Mi=a4!t z@~B!ZaMlPvRuv}MD{gO&=#Qn*!!S8nD)&YkGV1*P5v}0}meLVSq_gQwKChtCq?b*4 znL=KWNvDVG*(kN=pv+!~@%A~GYA-^gy$o~g&2zo{u!k%&;^d?V3@j)cUHm{cZ$z=G`PkK%FX1;wH{Q5d83EAcsEMiD|YDR zUbmi&Z@!UHG56*h8MU#e4+8^GH8h-ZcB6ehYV38Wwb$be`vNrD7v%-2W~vsAts{E< z5PiEBqMsbr_3z?7j1KDpcyS+whIIwJxDO>2D$Zg1{z1C{WWCIf+uLmzYHvn^-Imw$ zWO`UH7(unkSDioh1b0-# zrfljx2-cLHc|ZScxEsMRCzYGSZ0{_^3as>%VHqr^q@?5s29q)0M1#A$&S0Ct-T~X* z3CF$?RrdQa+`bCq?5k04e-P8{Yca#V5i{+Zu+;t-R@%2>DPG93KhBGl-weeB` zhu-%0biU=8YC8wOcWzi$8wSHQy{dg5|Lx>r!|(xdusNDhsj^Y`Ar#H1G}q7QhS_dI zH`fM4_Z?ITfBTQREThi^yS_s0^n49`hRfUc46p`#ub_ zAHXR4a~N-b0bzSTPO-m&nf4=?Z)ebKAHZt+QLMEO`jh_@j8j+WdQM=c$K(J~E+MjO zrb@u(Uv%!sd%oodhf?P z7f9G(({TiYpH`~@)}A21oCOi&{ZMnoTWHSH6kkWFeHcURXHjQAhq?CiSZRL~>+Emg z68qb@)cy`WVZZ2WiXoV+c4?Do9U8r>OK_^XQX5LZZ63jZ+dRp!`IoaiGA;MWbfqpC zJ|OOE)lVbadgZb%TjxK-cETWwxO(EcT= z?O&nJ{x!zfuVSqIJJj31#|--qINyE^>+L_`0{hSYLL5Z3x=!oYdQ7L=#djca0mi5g zsSiV9EY_>*)rYk>Ug=Z!N{_lh{0sl~H?g?;j%vf+`EJ?LVRv(ox`EyFoHR(#h&&zl zCI;JYVTAp5{$^42QBDKod&25Qb(7cM7d&pB7C8(%l2L9t&^v~tSSH;7pI4q$3KgQa5j1Hly@{HfT^9-=6mn1!Dn(^z5JrW{&=4AgQ$o4g zk)asjAHjK=zhs|=S>o?{|8AZ7I9H+-#5yiTHk-80vvh-DnNhdxTat5feGRLfT+i6l zbPA0}AT$AHXriw#&AjAx*2yLQu`!QM+dVo>@OQSA1va0>j_ruDvR<|OfU=f`8){@m zea0)F$#behi%=3;jGEB7d8xDs{!k~VyL4)(1l8)Z|6gU-10Gd%<$rJH-gz_g?wuqv z`NJd={)r^c%LI)WYKkII70^U&T*FXpsrUy0MbTC*5vyDNNV~OATTE2eU4?N2#u(h0 zfC34Xf39|S-L9>=wYFMYp!MQJ@7#Oy@+P$3*A>6--Mshio%`-R_uO;NJ?D4S z4qgLC3Hn87E0i|O^k$0Lt$BKYJvmCScb57lpDQznfAS+ZX^k^ucc$57LuqRuID2$E zD}C*ROr&lYC-Ir7eUTS$Wz{qcu-xeS5BlS+OnoX5wwn)#4^f;9&NLr(<*9Ijre&XX z&+*5E#k`hd%JZTKN>KS$nD{OB6#Ww0V$}cl6p6qZ>v{C+?OUlh*QbZ9rQP^)dWc=x zjgIsXI=gW>`Q>+GZh9!Vz8kZ&v;OC`>;6>j$e&3MVS6_wlbqB|5+g+cjG;II<=t<+ zrDFnml}wQyV%sRFc93mrVYK3sER`v+UKP8YXU}#)sBpsq+d?rz_Q` zE7hkf)u$`frz_R3Mn=`AzqO7v6*BfI7F%QDV<>{3?VM$!^8_Z?=w#Ah0h7;_LNv5& z%p_S#(aaK51J1*UI$el4kKj({3Eb^$#W$TC);LdLqtk;&oo(3SJdN#6FZ!Klu*Z28 z2c3TW*x7;Co#*hr^N;wz$>U?EfRoNnE8y(1g!6(`;q0-hoV`}HGiarpArE?W4Fua0 zJPL+>2nF8Ea1d>5FB=5I>zK}l$W_3qLXhoaKLWE-2(W$HH_#e8Ow-m_P1CAmFcGb> zK1@VfUHZ3_xN6$O;lLFq*`%Mz36dOr0-MlIlkX)r8d`-+~&zc@4t(DJq?xq1Jf=r#o+=!TCAPao)l-=NCBN`6cE$zry9t zJNT0GF0OTcjoY2~@D1lTSm*o}4?6GTG3N-LbdI9O`5ksSzsHNtf8r(Q7~XI`z;B%Y z!pF`Ztbj8Dj@W}zLy9k<)Q~4ErlysMjj7Foh>fWoiHMD{f1|D0a}m^S?DN_dUd0x`nTpF+enbf6xeHf(`4}{)Q%j99^`gQ^GSXWWI>hQ{yiD2 z)kfw7A~x71HZAuMf;N*L)EgB$M{L9*fT-Y*B8Zq^s1hM$gzd?{Q3RDt6hI6SFW{_k zcpMB&sf8+3>Gz5Zg)g&L%=fJ_n&Jd^fc>nspS{tLXK&}(uZhmJ`7hF-2*NMD2Tq`x zIy<5pi!yK#M_eROEvhhHBz+s_1g}>Uut{n194Ypm3RE8H)fh@-XL@zB=~a2~yj0EK zqa8@d2qL(WXQ9|*>2L-q~Mmek_Ej$4C=%xauMNTjVA zq+6T0wTTY+7(#vqMNj(C$f<)0augRJC}tueW}!rU0Ttpx)QgKSRa}ho#2m~Pb8)el zhgQ*w4$+3Mh|928T!CA}7jdg-#|m+!cidl(Dat4p!c?55tgU3AyP17}>%b7fjSBXJ zV+EpYA1hHSsMBM@otg=&l3AnZ^sP}6eAF+n*$DY@+9?B~nVj3e)k($L+SI~k?oEcC z1Z5>%NF_5hb_MIA325-9B1$7&!3WrfN!y#Xu$v73xml~=Njn)HU`JUq%YF8!ua(*X zrifC{Pc=e+M5(2qh{xLykrPPU=sb>6Z3=8d0uVQViyJXnEQTv?!dYS|&JoM-d2tJ7 zisfh(x8f>s8?F{Buu$BN8^qUeoA^5J5O?BEaTivKyK$dbi8Z3r3wCcsotlU+CZkPF z4q1=adV{{!8}zl_ps&?~u1b39IPPbEQ0EH66QNpTvtbu8kl(WR%~IWOTtu7F+5laX z8ekuFKmER;Uuuu1DCMgLo$Bdngz~weF<9&SPdywM$C>0wJ7aMVw zc*xrc;uxp+9>qg6c}u#<=pj_1k5eFho&i}y`Hg_^sApt0HX_DK@aQALj{s)9$HKDoY6za z*!7vKZT$pls{yfBsq-*Ba<#A(a$QW(UD}4=sniLwAqSx~SqZmWlPZ+N8O5gBGt5IS~r(?M{*28G8 zVQkDD#a>JmFJi9Pk1vS>SS|KrjW~>l#Y=cu9QJA8uPBP9|6bvh#&5u20jYac zd<dlO8pl4^VzM4{=fZgEqQD zR#g|m?x0oG=MM34T_|-2`M8~YB9XVcH;K;H`Q*O1&*hpUC=$^TcIaezenm2r z4>Y&f>7ut&a~2&`%sx(!jC-MW-fUes ztlX>Q1hNjLvL0v1GcZM_(JWoekPWz4X3!=ZF<(x>LRpU+WD^$4vvHH0iU;I5STCDB zh5ceA)yfZ}4ROAR*CT@Y2=nVui71+p=GQAp?F>xgH}D%NmK5_%K8%|%UWLd6u%1fh zh`Pt4nI1j89uHSF`C|Q6(ez$`042yYCaODh?g?w2anx=o@Fi^-eIQA}0B&EVpD*2l zXeJQ|Z$*q$XA{w!>F3MT1MBYL2O1e&1;SQpca6>W)>s58@Rdeh4zD%C4=t8+x_hmYx?(?trCD^u% z`iNSsre9AVcUKD|)qZIv$qKx)FCNpcxLUoRM6awO!&I=Tg(n$nY6+3GAVBBxnF7C` zer$C&s_8eok?@^cXJ|E&hB>n7S0h2EPPSE)yM$;v=2bFxF` z3K{yA;)#t)*uxbb`9~DLXoQW_Bg?8HmRSLZrM8g zG5oz$N`h*~_m9Hj2oG89dz&(eV2T~u8VKowpcRokEB#w~Aly0Ij9Vx02h@C(sE8&+ z$@96tCAhH0IM<5b9Yt&8-eF}oM^{(;`b%u}|)OPBkxDaV`(YN{fXzzHJ zZNES>IOe{>^)SC5o8WE?#NR`tV{dbmU)WRjY{sKasU$1A5-%WJ2kx}7|Av?DxbZ`M zNXYN6sXubD3|<1L_0{KnSIN5ir%8s6=-VRfBvA^O`%ge(KLy zamHo|X}mgrW%Dyr6SX0qKXvK&19DZj*XQg&1SPE_N`B=}D=^BXiraYJA6X^6?;K;xgJ%Odn&#{7kG zvsMqM&o9RPAiPGT-k~5ysceCgAhd>IYJV>Fp#yx%5t_)`(0FEAngi#<{b)_=5@^k? z586Pu7NeitDnQuiW8#7IVq#v7Hgwh(cJ5T=?EI5;-gd7?>M zLe_qC4q~GvawHy%;?{x%lX~Tz*b9aX(@8U~#R1r)t}%BPX@RBroD#iB?5^eP(+3-g z^4D5YH+z#9t_AJB{8ciGrHd8B&80$pL4x(%Th#KSg4${}KOr5r8k6>lkO5 zlD;u%Av_}oMpxNpA9L4(e`@96_$dCHtjKt=^uq{m1nq7Jojh?}FqkK*O{KFWaKif| zg)daZ6S#1o*E*kLXp{Y1biergBI|!Los*wrf_T)yb2AntfdiRL3WWXXX$$GG?kbdzzZp1s4+Y$6EV-NS1^0qlFS{N0DpWFjdD{i)#5 zyeM;%ve8hq3-)E_(aajZ^EJ=c`=hqE~3m8Yj<-jp{KvJmg&+$6wl^s3WesVA% zw|U1rz7-{W!s#!0B|*aAc)7ryiaUbaub=;Y|NEE6vK`C!3Unzb5D?z~a1HwZuem~2 zg0>=#GUkYn$^8$@7E)8rV`mPk?cduT z*CjXk)0kWZEZ!jeiTjz-*ycLxGS%5rV=rIXUv4^|FV8bQexe8985GPe##yRG_gc&L z(kt{6_DeAK)vDj#a@7eQ8eEj#R&L!7&f3S29rHJpz0O%ofjhe$gFl0i`lg5$-%3;# zkfqwBPrGg|{#>!0v2|F2-_g7j{K17L$LXfk@Y_lp5~(_Zb>zRfu&=$&78CFsalY75 z6d0Vu#XixR3)9CO#LD<%pnwQp-psH{9|^qc{8quWMWY@}O^%}CNx!6V;@1jXn1_tzHtue)j$P$ZDJ-F~jWhsReSv$b1sLQKJ2|UnVJT+b-+@dhCi24^^ zJ_F!4dW2w_w@ZEpz&4EH9U2SPzTg`$0=GCOr39iptBpC6Hv4YNXd&JD?he7ID>{Rs<)SO;c;p9v~v9 zDP=3NMKDA%vOy{y&A4@hxFR*xG8P)Ia6_Hz*YH^U9`=vC*VWrQc#V^-QJvbx9=?D2 z)*nVQ9nGP2Q8Ijm^v%~UrDqoDSYS-UfbQ~nW*WhlFuqTWK_GHgi6F3?`|J3h2dqzC zjE`94^_&}fApiEr-^(ZSf`vy{c6WiJcw7Y8L!7K=mrx1bP3`uXEZInqtRqC1H9i-M zN*7>TZ3d#^&j-zzww!;A80YD4y1A}-s_zqjZa@6?9BY6p-gb8^=z??S`Q>wwCdUlG z+LqGW&wTP{vD;jRM{r3SlpdL?L4-LGv!yf# zI}cmIEX#e<2AiJkI*X2@=4dj_3@&c4r9En3T-3Qszg$-o7P@c-FDmZcsvC18RGVqv zIgBbcw`)Z@<7~KOAD!xFh#ZQ@xTfIqPu#}3-=uKmhelRhfUZtnccT#J80%PL_wF3_ z&A(RXhoi8OL-Km8;a}sfnph}8`*gh4?%h2Rc6!%6nWV+CtGN4xazO6*Au6H@t}Hs2 zH$ml!PTSvtZX6|LP(Q<}<<{wmwHQ`h(%V|mOzd=ORTBR#G}IWFJdQhyhrntz+O?6c zQfKG3pPncSoB_H|r*P070dxmf;COU!96SXk?lu}+p5+?d(GFl%hy03p7&Qx1dB%n7 zk}*s6?apVzCpXzO?b=&t7sj)&8?S!H7SRbrkWfXgI!M(&u$&VF_ryh8AvVb(K!(n+ z2bP?kCadIMM^D}g-?-!mVYLWHPzSUQAa)+EYg4?!hP{6kc2-c$sE?#qc)voFU{ks?K$(FyT@TV>-g(!P_bA3T@q2; zybj;VpvAayMWIedQPwofdMPYTMb$FP3cD_0U8OPG@+`PO9R?uREOV)BK(%xdUZvI{ zBl#OfM?l?g9k+}q^%>kcyNR`+Bz_)H_h*a&?6Y`z7;R#&iUI~OHQ>B~E6LH04JM3L z((sANzYj*1yxcafRfE->OIwm*_n=g%-jW5;SBaEwfY5Di7w|`&+tgh^+Uoub=}$l| zP;F~AYv_lKw3SCJV!{tfDRika&Q(*Z4&7v$rKwf(f$749;zQFhauPjqr-BMI1AruV zyBQ@eaxX7z;clQgW-*FRMNpI!^`S|-V<}H>o)TW{cqTWD zo?rfkG_P674E)X_VF(LLFPt&-pcKC-RCp6S7MoEF6s2e|gj;^FhFiW2qY>t#2uspD z^1PrX5jEvpAyOKwKxwhF;kf@K5qfSmNkC*tBN)P8WLM+icU&^4)o63hC+w}$PgE^g zc;+1N`6vT0GH2teOKY)9*UP?F0rWY-rWiCSdl&eruqvIkPASP$dL|6m@PsRX?Ib0- z=&c?ri{x*j-dkEUDV%NXG$!0GQJT?R=DtM$Nku!|y`__tgvF?xxtlM!3~Fd~3lfE-)acw4A%{GTJnv>yjb!Xxd`*zC|V(WlBnPc3sn5ePC}66DxOKFuylS)O<|Y^`*{Wpjqlw7n`ghmfZ@y~1YF&h4G-(od1Wk+V z25VhJl(EeiwI#qa#G-K91>DI1TGzHzhvFvP?D!sw*YE_w8tz;E4{bi8KU!rNlmTv( zTuh$wG-QO;!Qv2Hq~WLT@WOjVU>fld|8VtRF0%lq`hV!^(?e{ zy8Mk)V`J^r>h7b)U)N@B#unNscEv7@`58R_J(-0wdAz=U`9GYO4Q{N1;~lM<^>-ei z^vP3GIjktw6+Aa_OhVjbadosV9p1FH=#1CieR@Fy?Iu;mbS^(B2FdL%;?#7>1yJSP z;LBrW0ZS>AB3ov(wh(YXp1-oSv;u{0lU2C_j0GrI;lX!FSa@S%wSHz`Gps!(lx2l zGUuiFLoNnde!0~Y{7oo0KOULApSk= zG~#i$iNvn|v}JPxk;hX*cYYE^LcZMEh@PI9j0^hi2JZ%PGS#X66gYe+SXtgTe+N*NV?mbaCc zj{5rdRc_X}=F`7dM*kq%ytp#g7o1w(9dCdu>I=tDa>}ZMS@8-DI<}k_TH2u|%*_A$r%RR*N4f^vv(yX|zQDSbx zF9z7ONq57w%xI3UOC<7G;y*|oA>DoW?u=UQ7k!T6MGnrNLJa3(4WJBOj(T;VIg)5N zF_M)mw{45*lx2IyO!G5q@kqWaK}DzxH&A@|MUf*~suK<&!EmyZ4bKLIC(H^Gy0N%89KDlmBvd?L)II_&Xk7lL;Zz%bfNP~X=m6!kK$zok zHh{^xEW&Xi#B^q4t zZlRrESjL|e>Np4{aEC!>kPkQ|-29lGSwb*4(^E|MjMCkLoOQ^iELJ zl0S%90v&^^HY0N3qTX6D5PTpx$}mu3XKH!a=Xlt8<3RCwtF0+CHA+@Y+%9B(x$KW0 z?S(fw0SjGyDIfwWaZ;Z-t|QzJjNKjUL+w} zDX52B(`bDB??D)Iobe4oFR#9ERgzqvnN%q)%@ka9`IYOLyVlkHJ$7yz z`@|Idyzk3&sd{ihnYV7)60~Hm@NSstA+AfvQ}^Z6T%#4sV(PRL*XNL~G!rZq%~w7& z@rOlv$%%VwqGEa_{b(L(r@*x4JMQdOo(%7 z4-q}Ij20OUm!Y^$2$xHVa_I%C6YMuv%v8K5L6XS6+X2t}&rL^37HadT=`gE)Wcz6 z?rJW1EQECU*5BZSr~U^WW)@-Oe|;`?L3X=8fy~{0Of0OyrWnH=?avd>AJZ;Z&pCg4 zK)HEPon_eYGb-ZR9i;caJ5_0s;!rR4$Qk?Vy@Ah-0+;P}K*Ne^m=5Z42NY8Fm*kyF zA$jGLzg13}BnjYYXlu=`&A&&uzKV5?lc6kyKx%b18{6sBQ;oNH_-am za2ei(1M;4=ZJ}_wQ;tD|9Yg2g)1*b42-_k%BM;`XM-+Ms3*4G&q+rsAk)ljSvuQ$Q z0zkr)pcOPNRWrN(OwSNW13{iuD);ZXgqrrDa-m=hDAce&ubzAtCJss7aOgqJ{^lJ| z_BvcV4gu=)M~#9Mpa%Nm2rBk22T{e3_s0*y3v2C#S1q??U*n?cS=}V3ozJL<4qCbF zSmET@@TKhfU#0Rt^RJd0EUrGFHwX{xz&#ud-rysOYwwoJM4%DM9rrQ*dr&JS*Ef*A zgF5wne*dRIwYRY~vUhY7H?g*{H~OzWCMdQ`X5bHEpa}}HAP|-{^anVlf<1D+Ll7#7 z|7Z-dprDhT%5>VjwqXv*ojEEG2trUSm6#t{vzpg>7FN~M{rShwUifI}6$kV(Pt964 zBRUloVO+XG6!r+=cuf-g;)31Z4U*HF?F2X(b&$l3C~ffoNtB2=+TBsNQWUO?GS*?P z(?qttCk$RzC<-5v8VQ2ky}J2-4f1jIc0A9}$4lwZ-0Wm8fx{AK{?J=Kg$5PMjZe_q z2WFU0kf6Jj>&WA@#>Xi170-T0cfGbpiV7nisqNdVTal)P@tSB-RL3ZE$LsCWB z(UjsHeO@pfVmf6-@RRJbEm&>&zUxYWriB!mF# zl9W{}3hT8vCJ#b@6)}c_sTuJcp3HAY{P4ina+;_*87`x!))xT;qyF80K`H9twy&uf z*6WN>YrpYd0uww-W}>A<`0_eqxr_k&1WhJQFe!X}=lo9oZ_W|6nFP>=$aVM7CNKBN zM;E=ioCY~^W-b05)(N(@-nSD3twL#|6iSfn(tvK|bZF6!#ouW64e1qmM@uC~@BmOU zL~4*yq0)SaT;HwhGd+Yqc*2#SYLI#3tG(pS%$}f3&2{(S{~h!kU--b7Z?GG_595Cb zx}u}KnYGD(CADw_)31I;M6hQ-C@7IYABnHO#mNa3BC)WJ?j)5RV>9ZvK@+7bNDP27 ze^jMl?&A2({pYH_l-l1wn^*O)p74EkHIm}_I6*~wFNACaVQ*@E1Fy0_fo@frQ!;Py z_YQRA)NoJ@-Rq8^df0;3P!We`tNdTiqN%96bC_$I6j($8;ns=B%%#zcKVo%(|AYCK z&YnMt`_87S@9X$Kbw2&CFZaJmwxrl8>xDn4A~Vubo2|d3HwxuGVd*6D6>L`jApTI+ z_mk>?g3;%+6B#Rv#%r_QwOg$NFUv<3`+th}5=1~Bw4uXgn)f;5<>}*Ib%)O-Xe;lj z`0P?mRFvyM#sK9+w^D&YMr8ZocXa1**FbO&I5%$=h8i>3_AW209sUMNkdw6usPzHbw`eTb%3yR z?R*_4`qTTDn~JKM#C|?9cGc6j6Xgb4r5Vqn|3ydTLa9PNmD2Wsd{9(Rh(p`pVdNI- zSx?xv|0rbHlyZvlGH$iqN6edk*_3qO`pil-kbEAJV!=;F$JkhJD&{BJld^uj`*!7{ z0vc|YT!Y)?!(IaE?2J_DT7ICgvxL}F9h7FT;5gKdbGMwA*Yq7H2LI8bRc{{HMg1i2 zm7!8!JpeD3_G@$hSjW_PdbJecpZs~I-60Ccw!s)=?j*aeqK1#Y15JzST^JSfy?GpSw-e?Qql4zw%uEc|B*paUW;jb06#Yd@RlILa>f6 z0i#Qq0Y*-g!r5@QfQ)S4pmtCy48`;`sB-E%Fh{0?k@k7}M z3&OgCr?fdVwy+rJiBdT#b)pbqI-W%o=dL1?kPW+b>f^ioFHm{vgkqe;Y)1^BC2!yh zD}UA<=xW3|=MfqSdn^|2Ky)e5uGvOinIVNkEG#E)o|v_v4ZpIR`mvzi(9J2WOEgiS zbk%kDDY2qRk_R5*(2Hz|7dJzJSJes8E|4cGOM|I+Rn=bDBwiS`@06E!v;W~_`IyWF ztlDlP`>@z*+K$EusM(r44bKC%gu@)38iryr!a<4>(e!P@VTe$fWcPtT4;&K>@>gzHtq=~D~d3Wil%6^_YVYf?T zz1rtdvePT3vY#pdhDf&zoXs$JHkzv|ES~CAQp-fi^~WF%AIxYLX07?Vw5VJ?63V3K zyd_Ir8s|aR)5M36z37&3=vmvy{y?68box~Ul3_58f>Uf@+dMP`OZPX9sdTGQ5}=qr zN0qrWrFRU?usKpEEJ1;v9_W^DAWO_(kXoh-y`Qhr=&9r+srwM+o0gS(<0&!Gd|>Bm zR_I_{oVppS8GMK8%5?b53~5t)?#c_WH3aq7qjUqicI`L{plmXXjTuVzDn-^mi7S2;VN?=f0-p`pH>B=fgN7{ z^|OPT5y{uO;7=8o;WH+TSD>#PA|B?fhq{YiJ?CE?Nnf9%0EvT;KwITE3k05gh;6b& zI@W?Pe3oP}lfa*n32?mp0s&?LxtoIJaLgGoU1z-8YOovL%U=gD^7r3K( z$6g-aIpoNLdnf&rVO%F)HKx17TT zA^juZG;+--txFtIc17BF(UR3e->WXNZG{W}PgSPT1qSnUS9|og#MT*fAm3CVLZdy=;M1y=y zNlI+Xg89g7rxyIhi9*mo;q2q9ZG_tOy#21C3roVcr_!pL0EUI6Fqd{l1;Q{KEGe}NFh$yKijod{T!N79&45fHC5xF{{C=5R%5y>&j$V z{rrrc8Oo2N>RZ4Pg(?(yaeFY6SG`8;xmUdi+OJ!~b(m2u?Z@#X{|??Y187B5Ep9@p+gT_R$4#1wNS;e9 zjQu_x)MMnZlF09l8 zQi#j9sg=ohPi#uu_ySXJ87c{(Y_}d?F5_ntSZP9q@9C4SF$?6QY9$5=9rHn`=PwR= zU1>uw76tCfO)=@@auQdIXnDu@(dcv}^N_Nb)?oEQ@8r^)VX<9CX{0a4sq!X3lLi`c ziY16SqexotM8vL9b_!Ogs*~G&2V6&4-ijg^>#i|Y5Uas_zo$l6Z9K%f={FJR0B%*8 zpS7gHy8w}`XcJYCZBBKLr7@ROqt`)0zFr*wa?YUJp+Bpi zPBRmaVJs{Yu2z_-%gtBPo>>w%t(*pSShn!#UmWte!aYxG%1cMiZsKCx;gjVFwI7o6AKBe90S^+fSJX`H6Y{q99~NOBT1pdN56rwlJEVedPV4ja!d&UX6HE%W zFEi;Ad0jZ&bV*-RX|dC^p72Q+gQxKO+Njvd&)ll_DYqMBU8xmw<+m58x>#OwTI0@) zu&`lH4mdEJWpXFIl^uW{-N~Q5U`oEY;%85Un(3oDjXT~r{^nE0$I_oelDPP|Z;#n^ zK4dUS)FE=)ev->xy~}L5H1OL;;CIrm5FF$7AMxU07V6@j_%x7O%grLzX6g4+Ea#rc zxEikUG>DUonYcUJtZg~byV`H1G^9R!FsKhje^r-i@LtE1q@>ikF6yCN>kkMN0&U z**j!{aq}(Y+P)pz z59mi&x2)EmLeM6Z$FvPg0)g^p!XGVCYE%%a;Sp1NdL6D|;ruad9rl%LhnVu5L;PId zj)x;uTqNq-EzG#>DUZ7zrO}xOH_=Ge3|%A>S$VVs^xxsh(pzffn8;GcAGN<&d@1-e zdN*+8QRu=nF-^SrohQMGpBDVLSdo~;G*MlPZnyJcAx&_r3>t7#8zw6k$<)`ye`?Ac zD7m69o!3E0`LP%_d(4ZYZIzVwmZq&)*0_N_b~lUk9ycF?*1e%+Z}~8k07vuJN64r; zlf+|Y@Aj@nVRtT+1Smk=aE;;gGk!e=uYp)WPN$*e^Hhi70ylteg9L9Q$f#7mUmqHRWGq zu5C6#Vv$O0kw&e0^xiAjTW1iBGo$`?&tqO=^{_Y3?U>cjmSH&qEF@kqau5E%n1i}7 zsk`5V*Gjt&r?5q5@L;({TUSA!_@(*1!r zfq?LPUIh12=j;4fJsED-&D%1sfqlJPzH8U*wPsrckGNcZ59OFhpxJanyVAWu>UQhc z(ys4MP~6v4zpAt~X=`{;*NlOYO}@@5SDdT;Sgl|RY}K zKruL^H)PsB)g{F@0B`JF6CpNrzQrjaMDG;VluAAYQmXi7&mLGROOIs>T6?e?v~h%m z&Tu&wg=)@ov^Itrj&+LdSjZ6x^AiU3DGZX46h;IyVwomneF&su_w_j#WJ9u~0~7YX z3wPwx;{qawv6FEv0;0~55nR+cOHu3M?c z6Hf1c-n8npin?ZcDKvoy*%>=8Gcj%i2;3Cq=p&^#Ndt|_!B<6QLu>R=UZDd@ z$pi27Lq9D<48;OH8i4s&kOM6Lvc!v8xiV);Z1%CRXwnFt^7=G%VY+Q{j(1@F4N`Gn z6T4oO^y*yk^xUvr%3O)VdZ69$CTZo?;6&{uWwdqsS$xRN*Z}GT;ysyUQ=}Y4&LJ~r zny8Ewr`0+zt@}$r^jU)#e-P=vxKcsKdp+(s-ON(+~8FBNfH-@ zn^DNPn}aB@2c^B9@{%Yi?*K9ke0j|aUj^>1Bxl;xgkG?~BawC*@M zLl5;l|Gub%8*+>%ykfR~HqVs)3%TS0mdbcTW&Cx$c=w3$3m^9(B&P-K9NMBf9Q(0G z?MPj0h&H?3k@`=k5K{~&5ngcDKg(3S-2gG(`hKM1V1L*c1ym{&K_ovbii|6`V=QM= z23%;PAus(&S%APjAHYZfalV?lNv3G|k_!zN$4h)^&?Rf121k-#IRotrsY~roL*+2? zm>!eGL?J0nk!UJDRf7FV!N8ltMKsqf|;$2%y zf!yJ8IZ`ZbvY|++b!1Sfm7heJK(T;$zrcKk;1O&<^r7YTz9*_=mpc3?kk{mvI;7?y zhIzqQgkfO)@O-*N)7G6v*tu9){AoLd~{9dgwji^_zJIzx{^$$K{M9puwEf%v%D zynS-Up|W*?{I*4J2;6R}-VNu#)V>I)ES65U70`9K3Gdc!D=R_>D|VkDelt!xItHh?RwS|w?hS-G&8{=VG4 zQ!3eT&4yyKqttPlFivvu)UI7rdr<26*TNYW{x3W{FY9+o#ZGZyg)Hiwq=dj23m?rf z@>jEc>W@aNPOxW6BA7&NyN)f!ajiGy%oVEFWf7(RxuUM-CwGay)R+;qKXG-jWg=)q zB>rZLj5DUW!)k-X229e!$Qn^ajaVsj)~t3(b(zw`%> z-J!(Qu_B$XV6_1H1$)Hk5W3XJzlVIG4Ey8j+^y-3Me3xG?yrZI7_Mhg*P-c$k8NbMAx`j{5?<)`DD@k#R+T1`BoF1WWHc;=a20=MDY4I6BoGQ9YxLyx9}dT|nDeeKL=7W~ z1!r>p$2g;e!b7zVEC@&&=6}u~tJ<4=Pc!}(f1H#G?W(lm7W~i0xUh``VO|WXPHcEU zfkc}KIueB&iUFWILI@oRhN*@7S47ox*7S-oA}V7@QlH`$*`!SHU@(}}%4~CJH5e&y zIGD8#uS8vJgEeEp*u{WCI;F5mP%6jaYROat8@o_6#_^c;>&fe%&vaJGHt?dM_iztm zQlP_Nah>0iLi9pt#7c(8cqF*WG9+Ppp^jspl$-s*=4>N7+T_v^5sBENYNIkbFllq4 z&g@x-I~jft!<<~TsRG=e$^4R_QE^RA!20GV0}L9IiQ^)&VF@Cp6NHsOdr-L4a(EP! z700ud7-RK@Oka+Z0^sm+y=r!r1xrJf+K2+OY74qUu~Z91cDO+m_C8~R!EB8jn4#Gm zR94U(4&u-sSPHXiV4WHPRZ;SSp;i%A&KjI1+_dgymx}Fwb{fx!WWKaOOcaY3Z4!8h zk=UrYAtPXoK~0C(VQXav_*iq~X)9?kGJaa8H++2kVB$`S;vOTM>bJaPxpLgBEi>&$ z?e}UEB2Ks88>`G>SEHY#!pN<5@Ia$;gnB$ggsxY}TiQ~SZdmsWVytn!@3mcT+u9EZzoDbw615Thr}aV@Lv?sExPv%6N5 z4=}UU3WPokvy=s*H9!_{RXPXY_tkJV=eKou1QShXvzkyqcJM@G1ii*Xb2K{#(2-dU z(aFbx_T?zRAh5br09)#`gFF%l3;YMc4Eo(u`Pj?Gv;n2M>Nhc~E>J|$6G)oideEPG zG!FR2S8yl>wc+HJdRL1tO>M6Fa!q}_%5W^ArRph?WP40B7BuH`Ll7Q8&GqR#F?3VI zfy^Ze8kRdyBFlCwP>)j!F3!Pgq8e6Xr9Zw4Q57qNThq8dhLO`+(d{3-(!~U)x@PNx zKk_M@1Ef!3bGLSB_TUQLGk1{Ovbgk4ou@H;_x$bC*arNSJl_#iQv%=SsdIow&>0_h zLKMZb(52(LcneOaYH1ec1ec8CY_sAe#5IPe2cF)tmBiA|AcYh|?!0vS{w!WjmBkM} zLZ$ULdlh}^CUU7uxO)MDe$U7UVO(9QR@W*v z%q33YkojrTM%S&g(!LQ%*5wqOt*X@bRJp9hnd7p+j#{28NX|mWmqO~#+-VRCX`<*a3j;;`ngzQeXGk za}$slw(F{RnlWcLjQTUPhjM>d6ZN15#yT-M9ZgKuzA(J!h$_!{{#0W`d{u>clm##m zL|ELR5NiN)9m#0H?5{(EcF-z!Ovc7^g2$DkjK*8`X-GaT(v`pQy?QZqqRE89hFS55 zhoq!@)IsB83~Cskdn2os8B5PuoO>=HORu{ZLpgN@mQ9g7;A|c}(xemrEuMNR1$gI% zS*`6O?DkPEr(GCtXw5phf_7dd&ppYZ^vpHz4?|{JfzAn?axEz>x6o%%0=hwxrg@mP z<_gukcu^i|FjFW$F(83Vqkw}gks(Jek4(wk7~@r5^nN-Ge|gPunjMIdr#~uTXc<5N zQ?Hq(!6B$9e5QST@IZE20+1W&FcZ_Oug27Nkv2(H1+Gwg56_frqQlZzLkI-ArL8P7 zy}=4VfUB1vuQ3h?g_q9lmo`5UPy^wQIZr<;!oLny{7J#q5RU}#6_DO*7%Mi!jq6pI zE3Qodxtcxliw}bd%^S@Si&ipvr-PM)>@;|)IbD6~A7@1cD-v!w6)<3vl(B;GtcUycBe&)D)fc!Yy0xeav`Qct2oQoeL2fIkP$b_>? z8`~d(pk^Mup!*ubG!r=(Q`cL0I5aco7#U>FX#U;Y-72oHU;75F>l1*oBk8lSZH{_2 z*T#A$Gxp;A39v{ZqZR(3JwzxKzA2+2vjd7QrXzuQ zd<<|3)w!pEaH%ovR_&!^gPhSyrRz^A9GRF8vG6hZzPWK9d-f?pNJb%fe;Otgj^c4jzVE!72U z$5;mn+r2EZtwfxcfU&-{E1^a~rj_upm)l0R;~*%?uhi^X;F^R0ipIcC7#m&}``M$y zVSo1PudB&ZdPAZer$+FH%7W$i>FH()l8d}wiN#iZ(!mgAEffX%BIw@`(H|k9JNUBW z8-NoSkmcyI#W=u2I(d_Ws<^9BPCKF1p6HjPcwum$46FK&6QIkfALb!5lc*T3{BFUe zg34Er^(-!p%`*!^-By`i6g$W=lH$GhklNCios9x>OJ%XGhE<1mj90z=FllZx zY=s{kzaA%!b{4vQcxpwDaH9))Cqi95Q%^3o@a~`$&QR*zLJaO&vjBh2`6DwweTF54 zp*HZ?>=2y&j;#3xnH$K={+DP#wJo9M7QcJ?BXd`vyHC(FA?EJ#$Gc>x1BJ`(ejc$r zJ~iU59G3jHcJBoeLW5H^rIW$jnoN>$g8H*{)p!EyCFU`&M2YZ^t{S0SG@ym~@u?$* z0SG_IAkE^91NtrXROc|YcVE(b{L*`QDBAfrr4!CZm+s$NHKxK`pGlJ8Hy(Ydn_ck8 zP^Z-J(NSgTC`d{4%UK$wV^s@+p-?#dFEp z7=5bo#}+V7NjjU_NQbiuoNZdZYBc_4%%EQ|g5aUSjesB{tpa7uO68o_=Dvw9yLW7@ zC4qvbPM|nL$6JKt?7B^J^1*Jg*Ev&u0@l>P-+$Ldy9$kEln7hOWPqea1%?K7%E!hr z-0Ilie8t)G^4C+)E9yKou`T+|Qm^2U^@^i=EklXym`Hi`=TW44R>-+DVGq-epi>dn*w`_bxoTSX8kb)Qmi|pc4 z`SQtygAv9_o_+J9LCnV^DwzjLKuh#JhZ|qXaE`-B&N-*JPLvPd%s+gl`_9)}%Q zf}`Jm7Ya%^Swx!L!dcut1w1BMsy|6s5sjb`P-emZ3OTK=5FG3_1o~T6xn_ZzwEZ4k zUV&bOJetl@tf0v$hBhSAYm3Bg%Y*iP<@SPrlt__A5u|OV(@X>wn5dGw#(&VJtc*)-!#9A9`IWYDn?O@HfAuCmP zYcWToU5Yv&xSbm&wz0%`^i<@>Bb?;FwF;zwzQ^k>IeevNxcAaRru1&D=vG#H+@hj8 zG5TCHXgb_lCN|#36xl*pjC(7vW5=heb)xTv7dFhwy;vSA5DtzA#iBxF>5nN(KLS!@ z?^kq2XpCw(XR6-6+mE}EU3rbLon@)Mp8f+au@-#sO`VX9yhNpl)XlkMj-2@1^Djhp zt1zA=QyMnoA3rjS{ByxCaJm0bD9BdJy)IFzs< zgQJQX+{4z)#LkZC;3$?}7`y09C6it#KqQT(m%ue-O^U-|8vxN@0WleA6yqo6<;y2l zwEd8u1R!owAjq9j3NmL`v<YUQIi!F`c8_yK8aag=%u8nqk*KOX9XT*Ro4G2}q>1z^1{TbT)wkr%{8L5RL;j_1l3+vElgfjM>wbI`2Ixfo=Dz_0OY5BUO%?bS0PNm;LhE zO)@R}uteP#t9UDoTj^^T)I_ZpmNa^XOZCca9X& zl+V9}3ia767cQHV5NleEy=n}b-?bhl1w=ztGOatRRVs`3$RPv>#lk`Rp*TCyut|jr zkFTnR5t+2HDB$3Tx2Fj=L9MH>7JhU*I;Mx@^JHm-k&^O`0TItaB2`)_zc z56a-A8A?nbuv|t|8z6#+G@e9KxHrWd{1C7gY8_y7X*!5nVE;SM4gX>w(&!PxIfV9o zzxs2f0AUSy-_q+YEXcR*AD;Mr!Kq_^>6CK1bBQ7E{s~iU0JAd zAn}oGjXVtsZ#;H4vlx19@V_-Hu0KWI!U|?ThyBIMl42IX*IrN+V$ViTJ-?Wrb! z1i$);Q8p>}o7mvp}aN zIzl_JIZX3icRY7IXYzl&ykP7QTL9kcB+xFSl$AA9gBDR%#wy;+FN-$ECCsbNk`-EL ztksn}rBRT6Rj$ciDD}}-6Xo zl>?X?R*KE~?lv>pG}lD9cinmR9ZKC~^l*+!&AD!u({0KNgKoQWkJ<0aFF38o3T4Hv zy8Ooc7=LQmHKM2|2>PEY9fup)Eac3SLR8ePe?zica)a*+w#oiyHzXEl!sTD z710HA=F8%tL2v`4qM<)3oSnw1O56OY%i~p>TvOBn_cIZR$(nZAI08?cE6^hr&?z{J zc1t#X#NJcOm<=6NBC5-8Hyib@6${gqI_f{i3ugwB=b-u@rc-R5+S+ntd2w1X4Yxk+ zW$GqgX$C6YYu}$IXzMEcEYw-3mf|3nT`)M3tc`+xk~OkuBV;X?o6Y zKX7Fojq^(gklR<5jAh*V|7gzJOU&2=BRsqt2W>agB~$=LZyTLkhzHlF=~-76&68Jn4`x zSik)``_+aI(Msc3ac%CLM-A8Pyo&#iB;t){wq?}>FzSGl_4U?s0rrNJV{!4l@+ZVR zi+Ia#1M{z$Pd9pd_G(Z%vj4fcB4c%1Oww$QodmTe4SmJ9wny6z$EmM2e@0bm7$I|HOOkLe}fM}fx>ohrmx2E)6af*JlhmXIvc63k`_04NZWv z3rCV+8OI{$oIu;-8zO}$5K7(g@`q#UccXu0p4ZYxhO+aUkRqyNC1aDrfT}%nKzu#j zg;|ic>p?1nRr3^h*maoK*jzHqLTE`vX>82USO>16OTwFJtd=Yc%WmSZpYeD~{5xs& zeygA6K*;X;oWQ_&7s&CcWYyY*^TDz*dSNGe`^Lb=h2F;daokFZI zGB=fYh2e7WAfV%$uCKzy4NBB~X4mQIx4_0b09OY8^Y#6E?4hLw3<#(M?mv5d|F@Fy z|9FP~-!-?obQ_hQv0^+xp?NZ#j0gng*HM{14hplXRIgzh3S*hL%BV<^zUB}JEc2kL zNuGafUv1wN<^ndnU0xF-?=s@*Meb}f1d6eYUx&}%Plx%(T-J~E{r-O7F-^=l`mBqH zu@toyfIY@if{gm!OaqR4m#V&0gPQc{Q>$KkG%c!HO`~bL#dNw|x|`T6~dGq0Q0jy_f#JjO5NVzv-)i`BIeeJ;8>WgBc{7X{Y!*MBvYMc zXytW;a>5j01BliwNNgM&Ge^ltGNut8@Py=ZMx$mL19f^}i$!}yD zZ7$QOW!6qr&c{-FJB@Y9O6Q)LsMX4nmwN>gU0Qf*devtQ~Z@(rkzZE$jDG8CZxeNz+Qkv7z@x=7I)ZWN_DK zT7d0#5}(}jADRzT5;{Wr*;*&D7|rTv9XR{^HgDssbgGUo#vw&YHTt<)J7cKOD92R$ zfd|3}xEs;mBaO5g10{dzU+PesW$zqCjEl_+%R3s&cWFqP463v>B18sMYO+NhS0p(z z?06O2MeDrvd-UhAYVyzlq`Ce-xowd0R0l62&9i(rfzTn_jBz$|dd74ZD@3{fG9*oK zEuZu*XOZZ2v{}OZRMZ!HRQv#z>9;kEmsA-K5=LWGI+we8*SYO+pYL`i!+wZ9W_3`9 z7s_q9oDSOHDJHKAKt+;z`$CAt41Qp!fDl4ph~ylxoXfnNT zVW9UF*9oJTD=w0BeRr`FHu5ue&JPK%^OlUM&NLVeNsgueYC(WMne|+B@kpTHP)SFj znpjh14WF9FhP&wLu#;Zi(qvx&wN_qm5`qVgTau>g6>o!>^|LUD`bCnm0mR=)`T zvJLr;XmP%AXWRkJpEv>iGstv5$M?LTB6-Z8!C;6oC2EE0C)+y*l{bRE({-G!XlXd60G8fvztC zg68VNsr>R~3usI7?YouU0mtH#ol6?KRNl^&W4q;eDqw)!M9~*8*LbJ-N#}f84v04Q#Fha4Qh}q3{d)(M&g5 zi9K;}HPZf#D}nt1boXsy%MzrjW_dGj|!fd+|Ju!{h;7X=3%hB|POiH$}Jf z8QjZFc=`0db=iBl-v9o*r*zCIXWCV6!w={$IU1*_{zGn57#!bVBehj;=aB;V>sZIl zF35gMB+}dY=k~2_B4NAjxy}y#w{1nmaOK(p&9hrFLoU~9WSezOYo8`&cs@Gs4+P;H z9xqzt;!yiBJPLY!ZsfNjhP2TcaqR3&A5i^`GlB-yxU~W2QTi5UDKQ!;dWV}N)usGK zUKh12=X`2A0~+QK*GfzcnEkJ5>f90&vapYgJ4igT}|7ZFSWO zt0OE4)or)Y=O-#sHnr+*s_Nly34%tQ$FN!CI*sf@SC-bTjmV&`Y9>E!b8qGHefP^s zUwivL47iHe;ZZ=Ez^e11Zer^SrWDo{IX#*=4#cUPJyvQqlXtDw&1)5*Wg5>6>}la~ z#k3UT8iPaDaR-1NPB3HW5Pxvo^XETL1-ST1p0b)f}tF zr#Fv2&HvOve2R?;`tx^CLAZIIUH<-p|DEUT{_L7PX}nq&-rH{dCCq9XS7fq1R>ykk z4H2uGM4%D_kuRKWGAX32 zr%ObvmxAZyG$G547i28{-6gV0gzoq3RxMD-Nzg~?Te2{RCivJpge+^w8-*>2aOUXr zQf;zmpwVrh({13D;31lSdEz(t#ADRyl-h0dta7VGf76YDDDPT;AH+9rHQy1;e!)P&4_Ou4D5!e$J5fffS_j+zg4%wn5PC&<-^wzIJDtUE_I)tWW8{HZ-oW=e)RSnK>W-R8B>rb!JSmdhU{r!W_n7rO z`Phjs^ZmcT4Ua@!^^-sDiR_;qNBf`lw*S-DlJ#u6>PWR7u@_ft;M|pxTqY zj6<{tag}uG%Ji)HFA94LBM+2MEpK9Q@PI%;X;V&N5nN&oi#eaUo^$@&nOi|Yf8Yiy z*ly9&{5ONKDPQ#>-Wb=y;3kJ%DuT`wGL{>whHYOh5wHf{dy78Yck`1zTv=7aj3m+; z;{c@Ue`Yh9?8;3Z2hh(k88T?0V`jl9;zhn&R{{z)LvuJVGOD4oL^#ziNgNy{%}R8w z+cM7Y9Bbw${5nNjHO^tx3)<81D~UG2tdGOy$w@GA(;=ZpeH>c^P&zfPxvEtyTEye< z#QXA(5Zeg2J7_TOwWvj_dNN6rsn7j&9osSZ73vX>_;zSMLwi$0-}$x6`Yx}!Z=ex4 zh+#uUY^ox4HE}%o`S7Zr%xzq4R4|`+BMENVj04gJ39$|pO(@lpW0kK=Pbp&LB7fqb zho~0KS5(~b;K3^h<&K4VSBs9oxjB%efA!|$K%WFctRoXx5b|XZ%Uj&?>g2iD`%Qc2 z1BruinL}2ZTSRQ{0Ec$(kY+IVkQA)a2v+%B=05(u0uM%%0ZRiHX1(fE2-=mM(*;Yd zGDFS>0mrLn@P2G<*8GC~pW|=39$Fm-5fG4>7!VNkf7T-YyFIMxz`Clgu735o-1ayO zk)?o9kdYCQ8Bq`u1!oG8g#sn<1&ZaXV-F?5k+B+?iwM(Om$mg(x6K=>`6BBy!Qb(M z-G`~Qu5Cs97rmpeR{N@?uAL-1AkW7kSqdt+{;$DRLE8lXORnGF&4!lwy${{|&(}Uk z;9O#L)QUR9^mfShc8KAeV7oFMlA<$17aJmdoT&Cx`&jXzGTJL^JEFrqtPK<@JJ`@4 zDA}4F9LKnwBGkuJ*A}&NXr?PnCWU;Ow!rar>x^p_%f7@>@9iipr%V9=`XY&87Ht(Ji#7k1M5n=09IR(Wn>ms2*)dw zjSkZeio@!tc1aNyx%?}0m|~1ITvs^{Gxjpu3GqI3(1qCh{q+`8NMi&9S+<%f$_6%K z8@A&8uU6gRtjyCP+1%9xvk3tF5`i{o)BC|1Kz_*@-bL|n$TnB5(x{iWgwP{-GQ*~F zgb!LnycD0(8@HO=>+tnKNXJ5PSY+}HgpPy`Jm_z{Gw&R6ZBTr?p(nqRv-AUpnNEEX zhMTg)bEx(L24jawlx^F@8q1J&V<#)gwp=s*@5qsUUG^bt&zfC?$w)wSU{f@^x>e zRl>H3DmqX;d`k)oie_*Ns6!Ia-BC!h_+U0r$U2gq3XuhOu?McU8?u%~nsO$hYGil` z5?gE|rHRFnv;!`FFn2I$g}0>OU}%7kcPJkVK)|DBjt=PJ6oB}M1|IVmc4?>swO!HE z&(UTWVn|t4g|zNlS(jqG?-!SG-qrhyQ92%e)Fl3EGT1lEAuss;GTJ5@oJ@S5Na^2Y z4gC0sqir_bFcHf7kyYpQE4@&13|mBY)YR{4sBkD9uB8oRO9T{hN}c=MvGDiHy4k^a zPb!r5!z(dqHy=q~oxZ^}G~T|85zf-#MUf`-x*|^|b3^r*>QZWdNIm5#-ZGqV6uQ=9 z3X%?{er}X_NRdNLe1cUYkxD9 zGJMEVe7hUC*3s}hu5z%lV8kNb-5G8+eHI^NcH2x+<3A z$Dy?C^mvH2b@39XUMlRx(_gASrMklj_ob#^l%aajr3_ezmakPtC<>@0S0YWCR6EHA zno6038%T%}kFXXAjJd|+^X#QKe;;JvsU@y7a3rwn3{a1*VR-Y`6V0DK(BMAEC*0}TuieN@rVJEnYQmChmr}Qrq2qX5=@KO z7m|7^@f%R6LqNUPZIs_lqAn^7^Jz_r%I)w6tABaQFje#Z>9<~hlm9j^PB-2rK{Vjq zqZkV}rf~JEtYaZa2w!s>3z18DpKIrUzbzKo+T4%GJ$WQaYBwaZ9TtR^^I$fghc>QW z#rcVn&-Fz7rcl-nc1oG*5`s+PAmgiobxpP5ibg!G9*;~V7Ul4zn~%=C;b#{KC-oyM zY6UwU4sJnCU3FMgt(=O&AQ^QWltbpn;r8<73>=c^Mh!+FVMWuygLBU$sf@*LVe4{T zRKi8MS@mddfR(V5C1;8Cl9)eW>tLpj3(O-Bv4I}L6f2$`+)|_y(AA9-mXe`pIZ0g< z9a+VS-TqB}{VyVPtO+nbYI}xMJp&d_0SuZOXOa)AlteA))`o|Fbd-XV0v!1R33zWUCfi*=R z3m^Xtpa}y;;s;XPhd+EW(h6gibd=X`i-tn)K)mOc0Q8Q@w0VluUy`~eWv!*Xxss+M z+N61QS2@K+?))$d$FOma@WIv48f+T`nNFC>wv@$NTO{9y|Bpwaa9ci?HY{ig9smB$Us3V2pxEUdkKi;xcDl#+gwQwl;XLFWpSn-s! z*^X0EW6q)U_PITw%FMo7wgs}17QPh66sc=Mvd3OagEyz=r7p{V zE?YOQvx^DwQMf$ctnQ?|`k>g((E*Mv-0 z4&0}7%ej~KkRu^-NJ2X-ON192;km`mBzes8=WC#H z{1c}~s9P-kSw0|SJI8E1>rylxYbY}`mr}1zQyY-e6>9|)8}6?v6Z!Xva(@PS&yd=0 zFn0gx-(MU?n<$pk1|}Am%x5ldMVkIxDD#LDOr)&>g0(Sz(RrLaopLa?m3$wtpgo(D zIwy@LdQ|!~>mqD>(I}J-hjz}EN%=tX1Vy=|R(;D+;5%|$TO@}Vk6aqj6a^*s4E_p7 zuL4OtTx}dE_DxQRhtxcj*k#x2_x}baeO^UHYE$;?{w*vorJ@_bw}p}}5;6DvC39Do zFBJ>9|J^>7$(*AIa&Ps%zkJG4bSzt0z%TOiw9CsUihN``EFtMyEQF3j^N`R=EUR-7 zQIFrE73EJ-q}Tq#Mlwiog2lUGoNrV~D1?{bDN~SA+t*H7M6vD=e?FNHIh>1e^t{!A zG!>5(PW&GqsYUFx-R;>ArL_0q8m8EPB+L#wnm5Z@k7=7Au31 z=5!WU7`UX@+n| zT+}XEEF4ET>G&e;FYDN@7l?#QDs~FxbGb~j!{si5?9wOPBUdSJG-$PSqwgUCh0E+v zIx4Vhjk2qQlJ|JxbumyTJPsB3Ev3Pt?50`zi$(?7X2dn7MLsGT89VYz1Di|u4=33Y zIV^z}R>b=Bh_dMvP7jh6Se`p`c%waxy$#$LdlJ3NX|Hi@%;v+{@E%I6MjN;>Gd3id z$vW>eJG1-=cTu#j@k*m`PG%t{=~JK@Dyq^UT1Au;W#xmE3}%=-MkGnr!z4iKl?`pe zxCLo6G*izSQzcTVNHc9F^qH)IdZ9R~%KMA59J#hJ<|34;t}zBXl$5tKp-CzCLOUN4{G>0126d?a7pyNdw>mKk2M)HBl)cpztpo2hD4uHi{ z}UjBwHPP$CeG9%u|e${-!R>l!dE@Wil{G<=aa{F6021M%C;u#zhFQ2dSg&`Vcr*D3pT{LOut2_W-Y}n?_r_SMo{Jt~fL-Vp=Lr2FR zu!8$LN3Y)@eq+_HVj4ea53k{Kmo9@DLyE1DM>4(Y#fsEG41}#A4cZ3KNV1(!7O4(8 zcV~xD8Y5+pjCpldb+;iVjqlAvE*L|B)nx0*PLNa|o||1cc@XVXt$#xs=K3%4Q_ni+ z)M}gcxr^}Sf+`%GD~CTw*EiiZm1{kX{%{HKDU%6`;sjx8cwx_=ny&IROfUn)mH3b&g1E#Yhd1SX)~w#?7J2jDXe?MH_d-72-I8@Rc7lboUU zHPgQ8KwtlgPF*)~cO1g)Z-6mejK=A4$;C@C6ApMRJIEIfW(bMC*Ms4pSf;SLC+_+Q z?(!0)%T^LfNiO}c$Ojcb%EEt93?LkUYu=IIc|sHP#M<@*OTF?}-?LyG&x4vnuVl-b z9?9m34+V$u#E8&A76!jU{iaT0@i=oELxPmc;`Xy?1zcq`_3}NK9b)lX6@Q26h4`k zzRRF^iuK`X6_xB);dS_E4oTZ`9BVIi1h+L3g&!DJ0^Zd)O}FOR^-as9J|e4YW`ZD-nu}XAliOw* z8Q0hq*Mx2mL#mmzp5sSvydPe{ffa36(~^yhP8i)VN zVc=bEd^WClf*193j8^nD7!}snudW2-myY2pQ73o|r@WD@$*eMbRQ@`B-jG#dlM0z- zKap9Lbu&(HDEL)nSxW`CxWnW0N0>pgP0WE=_-HauwEg^T9%&XeH#^32cfZdWBscoS zvL00IVRYv-fx??1u#kALns_3a^{_WrNdq_D7uZP&D|O2 za8!C-n7dP@%P9it(8a}wWsaMUW$-?BciW8xq3}MyIkEAzewpk`jqv`ULf+>JxeVcm zE{0L-QQ0VfUozMk=Fjt`H^Wg1jR^XxOhJ)JKJx5t~2Tfa#sMZ{B+GD21UKTe}QuUbV0SpgH>CGpa$U)^ob5O28cEGnPBz zk|P1jGuB%C;%hoVqmCo8{CbHN?f!@~gE@inMzjYUml?M^mR~gCN2~yYdc6_-RpCXF z9Y3xqMUB8Xxh@SV*)umoh~(MfrHid;N^IWhxhKQW()+4`6L-eY*ydE6E-)gVW2J%% z`Qw3UuQR!Z6TGf*Sdx7ff6bwEeB&u6sQHl^Xc48p#_Q+%pqoEtd(Lc|+o-qUA-z1) z!t}GArbB$|1k_K-7`Cgi_0o)9fb47zcq+46G&4vYm4v~)x!X$4*kBKQLv$9p%^JNVdB z1Li0PW1Rgly1o!@U(EBnTg|ZmL-y||pncKE+meu5W7vcv*iR$_^L-%+i~%Z%r&wF; zv8E8O#zLHBfEMN(-U`zc7Mdy16xZVu$|QOh&dDu|o>Zuc{&r5*kK4q1id8?0z9nU8 zn@M7R;Oyk-Xe_Uh%IAshCk}x25PMU(!A3`(lWdA}9t;^z5>vLJ>80%Kx7V)Xm90Tju z+XH~jAUOJ~)(6^CD*uLv;4U-!%WJ9@~u#o!YVig_47GkzU!ZA`o)-X-rXol z)G2~0_AlRbFovp$q~8^%mryPW$cMyxB&SU^^<0D7yIvi`YPkpaz1KI3ymK5pH#cSq z3O}2!i^9SLG>VgUT(~aJ=X|!K9QLer2Y96fqCE8DiiXGTUKB7{&LJH3%N|19A&dN{K<59W2tNBua^rU$KL9hohUubTr}! zXHk4~Xm4B^vE%2XRvQ!)M|&@T4=2$q&-;yFXc`iLQ|%!!a51$zPebK>2hx}B*HC;pAwT>!lNn_Xll{PqmU zI$D8`bTDGnHuK&&0#4ncfJfV+^!;6)2sKz^+m#)*_*1&P!CMCCcIp3g~=R?c@8=%QQQw> z{rcs~eBOG2s_FsR&#im%<+1!j>&7U2Fl%)E_%NjpEMNnJ9Zj;&-b04?r_g)ItS>XVW4tsu@;bl&j}yw7s#)m5e2eP5t& zXIc=BRw$;P41aSLN1VWEw)Mb!NZ^9926ZC zbu+S6!XO%g2nzq)5%2J(*u8h$D+swA-8*`hL7Q2hQ0R!pfK}i=J;8R^_~a(&uMz>t zc4rjd{yhyAQ!mI7=F;uP{Uq0=9*AyF{A^Fqiz7MHsD5I}o;uVMRtzj6OOS7J=p~(R zb9}Jti8DwRWo(I)0$Gx7qeZ6~CRio62**>Jf^VRN$&k_p*iLeSK`{Ty{^wpbW!e6v z>CT^Z$Co1CuMUX&q*9*-c#X5G(u9=37~>GVCQ?18ff}x)27qFvWoCV>G)cf1&>QQ_ z<5oNhF#rwZf=do@I6R3FE#*_1s!LuO0eYHdAJ_nytz$NCcJYz3IU6h|A2;zL7TtmoSkRAVhK&{I!&G7=eO>kgKP zu*@$;X*el7g)uNR$4k@$UW8=!Ny9}J2VdZOqLM+rcwVfGR~H*-THEtiX{IWDs5oZo z@HwM;s(unP-fElF5+6aDtyFo0dZ{SMu8>1Fw7M63;f`7UqZ!7&(e)pcKVPJor){Ry zy~eFQes|hCEvNh;>qndhv?n$xc zLG?2TDj6TT2+V`vhEZ+vs;F62SX5ZrwiT1JoLP1CW45bOY-VMppaQh2*N^1VjQXjP zQg414dF(TGcTU*ccR_S_dzDB1t41|UfFqGz1;#{xk-%tyBqzl*b*e`*3}3uOZeH`k zGvs6;t6k3mt6lc+nrCXmcJcZLUvn9SR|d}8q_}fbu(*;v!KC=kq5Rd z>mu*JgeO&J1J$-mc>a-K7(SBL?GLmK1U(Edfh%;@Ht|8Tk8JxF-+WNM^z}Wh-@WiO z-TWX$-SAKNa7HP51j};P#fc(As20dN_$ zqi;Wy1Ho`=j#rs7+%uk9u+;5C#hYNAPF(CMf)nbOLd7@Waw@?Q*)mA8DScJy55W5R<1CYHU!HC=x>1+N2>9rbxc*6r0fil5(LK@{=>x0Rs_8YXm zdj8OU>i9k4sZ~$IXDr9523{H8G~Ma?j9R`$(F3l$n*X%c6l`uy&xH7tjBfE~yiHmL zwzB@A`RPlK<{N2)TK@TZnub@+1Lj^?&)C1IE56d6K|<<)Ql;hL@*n%a>DYa-AwMegOakfhJCgrv$Sk-PzC4A$Q zOEf2Taox)DsH+v%#WL>^dv=v;CCQk7)f#+-<}sv~T=&tJ=nd6xM{;h(zf~xytE_{P z>oI6ft&^0?#Ig?#tu$Zz2*Xs z`;NeIof*dfkJIT|4;+dRV7O#LVsbo6DFGwqS|a)L$2Ui97hyRbK{dPiG9(SC;uok~ zk8AM=L~Mj~QV39aJQEI1>HU*zbk1-ByDG83<4V0aj1Hj1g_#@A+I*ARu zepCuX3cW5AXk>`av*u;K(!2tfXtt8fE3#6YUw}5{|MQ)2Ng{(H-u`(qNA}(PHI#ax zYLmvzz1cDN$KxE)7vuhBVV>lva+_K(hAWyauaxd>HV!9PZ#PoulESnKH>so@@$QdU z*Bd-2lm2>+>_E?12CRFN;Q?*wwChn2j)!nVu(h{5YkTezfrr*g!yh|C#vU|JT6MrlI43tAYL%f1^I8 zPPZMcH$G@(|DYKMCzabOAE$S&jH(5l?u1%qg*{FAu+c2>tAq?;0e1ZfBvq!!%KJF% zVE|Mw=_s*w^eC>)Gc;h}413YFx&S-hjq%;EgA3U~nMlJ3^h zG?y3`akuAAa<%iS;N(NNzGG|s)p6{d#Ud|g-UZxHNI$r**z|QTeZ)e8;LyJu04Aa$ zP=9T|@S=)2p0ZPCK2b=e2NO=wK$RV_N}dwYc0;L+kX~WFct4c(l9UC66nXZ1M9L6FA=Di?xkX_zpzK z!y)8wyVT+_^{67sEo&mG+(gS^TsZ~YKm{edak5vqGzA$bteSaNz6+`m3`!li@@1>T z-C4N}fu*NyG56Khu&FmSJEh)Q8d&ZUWrz}v2JIFj7|P$43R^ScG;CQaXxgmDZ29Dm zR?x(xeHK_EHB)V5j2G4dKEaqzpNxKYse{0JzS|~7ffv13mqu;%wI!Q=(r`cLsTXDUcqz+8Eh#qd7=^Cu|XlXjtg2wn$r9Nl?EgSwM9F7;wyqq2LXS zprTmpV$)Jj->cr?0rUQXwvNYbDl@p(3ZF0yP=_ym|bY=|1?d0+mzPrcbS9r z8&Z$ZtT)7ygfC0me#v-0-H}| zizJM!=&WHIfK8L;LwRi{@O6@$j3QKJ9>^V4klV^`PI`D$`k;&IAbPdTihG@CNJ zRs*^<)E{cS$=VsJFq~rY{t+Zut5f6f0%aGx?!#b6ptr*i!oU~^5+J{m)YmP`h6zep4KSCrrv0w zFXl&08?+oxTdBMgKeW@bJwyiT1I<2&nWo#vx3q>*XvljjJ9W}8zne=u zYxTT0hEdV}`L*U(qH%{}51}29{cr-zmXbK;O(EtL)_~sixC7ESKi;5U(O2`JWffuD zN`m&Kq}+3BF${5lq_~~us5OKXa*2|XWa>sqF$k!!IQvHUOPwxpY-89WXoHr){D7!v zrIwnwE$dz%;yQR|uvTZVo-=reGq_S`@Xy)B863JZ*r_u(%58=+{1ir_8VmcY&CMHM z0J~?r9;<+-v9SwRXHV;e;zI_2EDTVU9Vr$%|LW)$oEB?)^kl4e7zHX^vU5w^EZ39K}mPT-y9Qv1l!I88;*7&i->b( zZ;w`NFT&l(6Mxk78oIl*L(n3Zt@-4#>ggB?@)0=dZI+Zree z!(y@{=b_mx$%MUKq9ZZwVV@nvjt4=Y;8O56tmo!9RI`D7AfeUZcfCm)`St4GD3dmE zyg140u@~*Q4uvH_{_^`sFDQY|Zv_38JRqEpg862Fg!R1I{hion=-km4?a=+fQAq6d z(3JZZ9T-!Wu~(!(NH_=LKtgR&ZgS#(&x?1B@r1i$3NvZv2zxzo1xvLnp0qdONRUVp zXuVuO;R#@EFa7^hs3~RckjU_36WYS~&!{{Es_b8=h!@+7y z900*3hJfy5HjD*BfWijz20|f%JE|aq2hFW2jd$u@cl}%x{ZmpqVM}EoUQ4ay^EW-M|!}@XEGXK^0?~lve zn?b<6%z*QkO5}6)bI4ed0PCg$!peSr^O1SaeB+f$me{{RTNOU@K?ibyD# zfSm8Mx%j_7r9ee*;(_Lnu|MHROeyVVgy`n&G@}0!?ci4&SepIpB)Wtxdv-3fA!1F7 z`fD*+r<-=g|9aDnw!e}^lId>#fgW+Tt%!; zjM5KUK5AEH^$Y{kFJW$$oB?fP&u9EXasH_N@ zayV+Qv?*t*wODq;-84vA&q$qE^D3N+n9i4&t|DrK^vi~Az#ZfWtlW@eH0fR;RIrzs z_lsLXHIWI@VFc2$OzfAcOtM(kVS!2WI8u5=%;c6&FS8 zX}hX7D$DHIT5GRRa5ihtvOyKqT6J{QtenW_vDd1la6)J2O*xde#Dh-( z^fQ}Cu?c^f&F;}e$i~5oIQ_L;;)rLnV5y2-VAGq_`8L?$_`NeDAwEJOd=$(EZ4A9c z;@QkE^5(-rg2r1WPj1I*WuSc>pC4krV~=DmzdS444%?NsyK1p%&S5tqH!7V%qk+jA z{oAyHiFP{!2j};nDb0|0gNldc3-xEX79GGDj4F<4!hzKvl;ULqTAmJPoygv6)S(O# zc;iZjSxjTts8N<6^dRRs69xapl!Nc&i*BTS8uaj{rHEE-T`LsDUrT5 zyL?DP^RfTD+d3ft$yb6WZPioeu5Np4vFb75P0ydm6QzAD0c<|7?p`7Ic0qA!?KSFP zyS=1D=DfmdJpfrGPo2bSL-exviM`BQuBdfYI{S}k2)}vx^lv3B(z+@DbK~-a=4HPP z-9>AhXFyJp`!^=KeM+t3N*2!4$ZSp>a05RFk|568~BVl!llNLTIvsrpmRB_&Nrd) z*?!|{kMx@FtlOx;efVbdxHeXHmP6e~={hnbAPx6JVoLMRNW)YE-ADq!swT;JEJX42 zuEPBtL7!6oC5CRxFFpIOX8e{4+qkptx~4{w$yYjYd|q&6I3i{`L-I}zOhkG7S|?`!u%YumCsgq}~{15p5k z5$*QFPer*0f!~Asm1eH#3XQyJ9(C4f`Xb6eHFu1$1pRQ#DA?qh&`rv!hD*-LF1{Sk z+~3`wPO_l;W+jaK8NUun0}-`8PVEG1B58|Vq;idTY175@C=7(Wg~~X$@>TR0EQF4x z{!aW8fi))z2*TexQ!cmjNqG_&Be@VK#_Z`)076qP`6a~|)M|@+M4sh(aLh20ETV~< zM;y(glkScC+8Ge*9>F*-?yH68dU|i7L76u3?h#wyS82r{ww#` zXH05wm857Ncv)m%Nv%nf237Pamw&X-$=+fn=lCt{4(F(^wd)BS`J2)UlNobBJmk=! z3*Qb`aQ!u6?(yOn=Ede8pDN3aj^g1f-f6CVZ?sV-=F) zuPuwrV-n+@MySKE~~zTSyl7?X5l*duftMbS#R2G!4aDeEyj1$ zi0J|IjYL7?fLddzEKH)N+i_)0kIR~JZ)RVM6m>sSH;+?xF|>OH*loj$>Lh@lp*%E( z`j4@6cP0u$d20;yp|La|bHyX!qgT!EaosRp;6>nb`V-o-vM`N&X3B<~G2H+;nJ7kD zH0fLX16|lnZO%V~pa8=$)Fr}NhI)*Vrz^G5Kw9b-wp5msMDY%T3qW+LQ-|g?k?sIM zcM_^QJ=`2QUYf1&3tnLgB|p8)SKIn6nC`fyIrz~Tv|*a2kDl`(z4lCUi3-=Wu61H| zi2~QG+m;%yVO;Af`Vx$Gw+3TVCvlxYL;swcdsh5&>SS1I?4HM%=Ryx#Gy8ZoZP4?$< z8euvFFG&)hLvWhOH|F;MP&oueV=ibO0{3oq49Y+0d6d>0tovj)7#B^i(d&mIbR{qr zsJ9^8FN7D=CpgM!MJ5`N)Dp%OmVRRj7`Cyr?2&K#SmVFE2BT+u7`bV5Pv;aEuVR)L z?_(eHaQ1b`zzGP5C6td3;2UF2ISwE&`9695!|Y!^b{R#75cdcT;<8wEKtt1+?xj2K zi_@7cr%L=8@tFJf`+sHNK^Hm+4*%3=S%0j9|4G34&)P%N#Y)x7(M;6A-u)*u?f*f8 z+QsqP4+)`#o^ykRhzdhe3N4t%ltz={kkKf@bQkKSQ~KaraB5@F>c$0wI#PwcQj^@V zvA(lqbK`mV0NwsZ+Eb_VKQWj)%aKvis?$7Og1urZM+168Zb+eU?#L1HFnP;6)XFI-tjO;#e*YI|Zy6L<|1Ap>EQ4!scXxLu zxVyW%dw>CgyIX+Z?(V_e-CYKE%jI83-uFCp>)vzEhpC!RQ!~HWyZ7o|Yjsy+lShV- zMCB58_0eNzG&3Q(i9QPDZ?@*}_)fwlkOoaO8%Uy8l!)@>>FBqPjACdR|Gp zVLv#T&CKt9eXCQTIY~UE6!&B|qnI=cxlD>4yd5kZWBBAd@6j=7^8S0%Z~=m4e_>0` zfF*OJkYc)1ZW+8)?@-X~OfG?$Tv7Do%`ynVTrLb*e7F*q=>n)Xb?mxXSq*~{;x<#hc;?RY9d$nDgETYyo~ZPq!WuV7kh&O$aF>`9YTqu_#3@j~yzrL&9GFegw= z20?AQ#58c{M0>xQMynWjkI;c%_DF&G*`{Ha7l*`Ri!}XzlmnUS=I0fvrG8{rkFB?{ zkG8!nHc>MKBkU^&B97UFh(c|q+yI}@x@5=CHrk7$2d&NLYiO$KQwc(=TniI{?X z>`QHe?J$f>8EC5~szoeP2oReL6^c09-R*M@d5Q8^Iw8J0ZII_Y6tv611 zt_~L;$53#tR*ec-Ef-a+7uR>*Bik8=335&7=CqAr9<9QEjptRLf zb5WLGL&HFQi&L3F6CcRX@ADa0-#LFh@7V;s&oy8slI50kndnr9yt?qT)n{5G3gq(! z&MZgl)Z*K)@ee^hs;_L!wO#f-tr17hoCPkC?hRUZIVW$T2xFlWokkiCO#|}BwO18L zvz)~0cBZII;^KprH4X%Sm!M++L04bw>oN)A&%K>w{R)7Ox!)Xw9JrveWTc&K>5O!1 zPECi+CVpaiqW4kZ@0C(#H^6pf(DWe5r+)YxonhUg>iv51ai*wEnRbib(VXNLI_}B z5JdmSK+gXUMum-CT%Cpf*Tb#TO3ygUqe8Ef^&((FF~({=4#4K`id9@YUlBpsC4HwA9~ir7?`tmXw>k@ zkBb6+q&ru9oKx;qpOS7FDFxUicl_f>52OcKlwChj{^0EEx|g=e-uEz6{&!K8q;wW3 zqX|*CWyLu^s(35`?K&|{LWPw*(B1=Sgx89yGDtnwLnFE z${;{^5k~kiEm;GaUO>x4q|qd1jdHxoM4f_ueU7c!ugW9?6x;*~$60ah&gL+g>x*~A zr{%@45umfp&PRL^u9HuL;GSbnQs6IEHkaqOv|ZF1t)l^7uc(#Pu_2+Od8)=4VT&0UGlwD%6#L_J{2 zix~M_$7>aENU>QDzwRyq+FRuS#ppj{(!VwRv7lRH#-(8_a${XF7C9(!)pB!s z90dL{S*x0^`*{r0TfuA(i%=hp#&Ia;G3~3+>pXt0W;$5S2_)s*BLOKo4&&* z&on)ric^t>CV6(L^#@HHZUt@QcqsMXR~BrS@nP2@Wpe>RdMIz#(-O7THUkT8+If{+ z#dr;^R=b#rEK&uevvo<`LNk$)F>dumxi*Q@r#O?}ve{qMraSS_R}Vef=CEm_GFMIl z=dtEXucL8M?YkKMupz?nVj`svLWta_owz5f_^GMfHqc$vvFhinuB1%{^P%qmkl@A+ zcc(p@k(+drrF}~=? z^5*3-jzQ#f8y0c&B??JdkZ(p!Ua8kkf5fDZ7hj)Ahe8QH?W0*Te%GCv%&li!N)?t-rvp-?>+`9@DZqG3m}BhtC)}$=E!EyZzEbk1#UDC*PN?Z040$n5(PG zwM=*vZcqx(3PgrSA6F-xvWu}*e#gS(+GpUWgzRW7zh7ELW|+MNf5iNS7+oe*{Y!w+ zC&8n~BOqSwA*pKZpJtI1Y7w${R&l7@1!~Kq+vr-Bjx?P}EJP~1Ha)L6v9PxoWVtEg zapoAXv#9XkD$3@q1Gz>rr0E%{tA4Tvqj6mgOIgzqNkQsI=%_zK)AFBaF#U>=m59ZV^;UaeDU z@u}M6G%%ioSth)ZJVn1vZbJ_6Ra?|mY0DA(#xaqB`G;Qyt;i+{hkD6f-o{t0gdmp$ zV35@{4Ne-)-I1=~cT6PD8RsrS0g#Go@OX z#j}MGh;Bp~qA|N;kTWFCrkn@aPz`}z{)Bl+)@c7-H{I~zS2t3~%4ip2XhcR5!%4mpm)@>In#qg zSBW(_E`BWi)UinjW+El_gaq}cqc{!Z3R;RZ@%gGiEJMaAma8Q&N8JnI|pETl2SZ0NuJlipedW7b+|#6-=D zA^RM8eblCIt-(XM$gTDIr>>>RJOk58m2Ua8w{Ey?V!aC@Xh ztiPp&aY&wh-`i7G?HO5VOvNKJik`PAVybU;Pc=Rh4!g&)K;N(Zo z8_`YP1=idb#5j*GZJ)QH!68vZfL5xa#J`l}syen_W-k+F9Yjw&sK+|C4l0E9!H^Bf zqhSkgUUABV5VBEDAXZTzOQLd2-p3-uFQ7ySYSk_vTyq06m$y6ZOjV?wY*c@J@u6JK zu=KNY7_jR-=yhYPZFTcVcxjQJ>nb6yVbuCNG9cZs0e<3$r7a2%s`B`w1J= z1A&J3Q{K@b{^N7dsZ#vH;hB$9e2eIruRK3hih_b`eswqC(t*}FpkcV3Ar2L_67$ne zTx-)i&}oN7@?}kX(j>R_COVbBoMe7_j3U2bMn}YiHV-J_a0P2qGR>9GToEtE=DkJo z#G!_Bs8ex`yt-G|$A#iNA6JNx35~ZHZ-GV`enVxKWz1W=GGYRMr61~z9a8uavwemOu|R;|O}FQV_X+@wfriE#~N(&8XB$ z^rRKRef%LUuoTjp@p{=&reXL|BGQ*hd;hgSJSPI<`H9<~3Wfd#25J7I3AKcFjY4<> z%~lVe?q>b-_#OD+$nk7od^7EEV!b@sy*(?h)L+2PJcFj3Y^@b8`sV0bta+NfMVN-72VGB zgip|mOnd)m@)mA}^cZSpR){8@u@zy-vpuGn(grr+%{}GM867!k2rAH9@ zXS43MK~hhE{Hi7&L=0g2zvG`Qlw=UQztovp;YgM9CQtN8!FRQ{m zd$0xxh5bjSD`y&3JS=i5nki$j3M!iFqcmbK-%NkfmByAJRQ-0L{M<$r$QGT$G1FY6 z$yUh#$n2SRC#whPJM%5Y>C&7<)*XC9lyjugOezW`MPT#-eqNGSFfxT&O25VVo4jH{ zr`H&lP8!E*8DX>(Vu9Smppl(Xn3ZJTkV-V$PR18)$pO7+8-sk})t35hIci@LB8w{D z+x+uDjh4s!^I(nNukh@TdI_65$J{Fh)`JQ+=sG27)`JCw<{hpz>rp;h;I2w?oCtTb z!~Ps|L+%#`@)re}T$dz)a)Jyn1ZF=UU4WEcKZ~&rtqAtD2pA;$i)L-+zVYc<&nXf^ z=&PWYl}K1L;@v6BB)-a&J};?WeSL!oD9SNyUT0|UEKWO5UjF-!uyrmJd?@5k&2Q%O z_2~}G*`dlxrHM~6@4&s5yg z#LUsv%EA8M8CvX=biWXCSOD6bm`S7d^w*NFe*=ZDKKK2OsgLtkUpcT77EC#9ISQ)} zq^`Cho)k&KgP?jNT`~!`d|7Wlu3wvx z(!*8jycFG3)K)d+t1)3T)U6MK0k5&f41FRcbh^eDG#vS6Dcj%rjup0~%1Jzrl%;S{ z+!1#Ppa0;L)?M#3AR+mKVU|NK9oA9k{`u=;DtjeUWfRYA^yEX`+c`^(23@P_yOzYt z@o22>+8{5+{7s_i-2+S3)daOG9%>IAQMW-{$cG1z2}Z)@xVnG*1Z6V?h6fJ@smCLN zH^g6xYPVgBos$AK#HIN7H<$M(sE|D`AXUr+oW{I@ZAO9NLN zeW;rg#umr;hbFRdB$P}hvSDOl1avu6Wo2j_4rFAG3FV5FX5C6*Jz7l16VlUO@7KRb z2HvXzPhUOXGWl}S5s1-;sjb#iH##3DJ9AqF%|AXK2MoY4uKA4ycg?fkTlzO}JjVT| zs;)*66U_(8m-xrf)^+*cScHIidQDTZVXbN|ogHJevqEWcme>=t{6;Us(>=NUQleOtdwZucw6QnWapGP(vM=Z>H!n6dx*z=*a{<-1(As$!V&J>?h+i}nq3BH~;Wn8#IRRWHRdL-eMw5l!V5y%v zkA!{VUdn89r9zy6g-?fMwMYl5vS9}4ho0| zEW6$qvBoiupd@*L0tdN;W=;TUG5rcbpAT?;o;g?`gAPU<;x`F908tfw?4ML|^L534Q1 zu(hM4ctM+;!Crz(d2HZ$2ZxJt`zT(qsSNvQ?)Ds({obUZp!z(rivem1FNa!@OYG1! zwtxb7i;O0h2L$Tj6elDwdecC|8oucx6^^>T6mg=0hwT;^a`#N>&%Cjq%2?PwDaP>b z-A-S!;V;*sSXa)Fh|miYuQs&{?Ck*Eh!p4_8V4ui%YQ%YgmvLauUQCC;qun?tnn!M zx#gk9ppJj|x=#5=T3=N4Nl)h_RGm-ZMY*Qa5a-Qb1b|XfEGTk%0d52#1aJ^~r$p}K z1p5#YTaz~%C%_h9auUu)4@GeMypX}0)@B=es>oZQqDCPwE zR#i?tKZ5@74{1L^juF7~34??0U9D*<)2FCLk}r&}phPB=$U@N>oUCGKH|j0f4p6L- z0$t@3S462MRAC#L$z9J447WAl_c$le=E+uerL1AC3ltO1Doc=%2Sve{k>PcP;P;jn zGX#8>bn7FS>~~4&{{F*&b;g>=C+X9CmGX18jO+giaX#JZ-K|XjqqXztK<$Ed^ld0W z5>?xa-c}Ob_8eRqcF;Wt9hEWN8*HmP#!TEvMtc(9;MrKt*Ur+?eD|VtQ^Z)p zi>Avmx39*MDAlc8Tu-aT!kOs$BH_*O*JbjrZtnYoAktr`@f(eu# zUkk|IE0dJvbfGa5R|alr5r{VMy#{|- zzH8B)HW1YMe6}GjtoU`qfX2Hmhq0NH6GXJ-yy~_#Q7sV=vilgTXjDZ+#<)_2KMr$U zdJ@eh3ZF@ag);aO)4BI59p+wb1+of`8i_|v0N^w+j(>n<`UElnM3Y0yY*SV%Q;H$f zzRAnU2~tws2c!HQs=+()>E4{))GI%y3U)H8+huo=yNo$lbTPAAsw}H?Wk7FQKMevk zSNxh)7l{e58xVbjuQ;~tRdQ~ z`u6=Jv-Q1!u8)>Lm%i{)3(DUrw$F-Z<^9x?aFJj00=|DpcT4b*1-dHsYnhMHE?cw%u|+TOrtqq?Y!%-Bz-LzJ!yQ zPl%z#^TSz)W@~=xF%4epa|F^ushhdz>3mzb+#0eBHU5k$r9gXA#%g%?Q(+Nce>chy zo`v{R19a7c>Rbo7Ov#-rbe7(Con4lKLi&Euknv8W=2LV`=Qd|AJ|N)q#i{2u*j1|Y zc@%=U`a+7wLxNS7pbA_2i^8KxD;`_S>;%ueg`0+$bT%iU5t8qdHZz~Y8-}TD4 zW>hlGlWHB={%T`Ut+*}0t{NSp-s-Et^oy^l(SppiPVQ@bfU+~?nvssIMmKT2X3_0owJEqaA)U%etZQzCC`h8HFt?B^9il;_ zggkB>oz*WcZ$?EAxBVNo4zk1O&O!SEPDCSMXi-dqP83`tWESZ}lZ2m8 zD+7%-A|4!W799R_Q9SgA3mNwm7`N;V{51_}M=#3eK9>XYB$T{!I%fJFodfH@H+>@k zXHprwM6#t8#HhsMsv_@l`I;D$Pz8HV)#kQ5oUu<4^oBhhf05-Z_jYF-(I)q{yjwKY z6%RZ5r+GeJhfIXHgr;#4?&~hsB0EQqr0+U=LaaSer|7dy)E^77-GInpU<48hYxVcv zJlJ1k;1zf7?NMp+%(m}ji@IcP7ZDE}xZa>n-3NVoC=D(YbDk+dSO!jyAN9-rz}_Le zLF1%Vfu`b8C!{CRI3dN~%NGkXr3?-xsatQs&RCJ}e8u6n#U-YY!Cz!!ZjXj5Mc<&f zB%;8boI3_>J=mwt|G90Ytj@Pz{)zBlpVd6)e+TImjU3IK|FgjS57X_>v`N^{9=`ac zOn$f$(>63+1wQx%5tZi8AX+TSSl_Ji@2J5x^^RW{q{RmMX4kUE0$l=h9l$WKPh?sA;CnDvoIyTK!fo; zU1B;;`~0I$P zw?=CY#gyq6)}IZvjg2T=v;Dyii4#~D9PLi5y%Jk*Xufyd$J~`_guZvZf@MRjV;o=0 zq6ayUGJPx#H!mOkFSDO-uN}G}G)iHba7OJ$e7@4~DF^!CJ?GpY{&w#yS%|<Hk^?gbyLU{WJWI z5w7l~=Mqr0l;aUW8-%iA|CPVDa<@JmVS0OvYPOi1oqXMkxHKw-Vl712dzB8GJApcr zaWSVW_S;c8pTpH5WVMS_4T~(J-6qDeTw$}B49sD~Wa*s`&qKdCxdIC15N{IX+27SD zoBNoU!eWJu>u@@;gQz7LRqIw$m#UWXaM@gPGlfEA=*$MlflarvibU%C-T*g#QF zTeG=<*{v3)?lU0_)Siz&Yf>97t#DID$MGDypz7L&y54|?8NXuCiuV49p}}C+_1r*6 zePriDV)O_SRI{8u^nikpWC5A1MI`^pUy4n+DYU+iz2J ze?_?2w>Zh_*X2tho`E26(jI0P-!VS^l&2Vytz6^tJ8WLxo-i2Nu-N7A*tzqD>NI{i z)!z(5V^LtH@L9qs+^xJ0P;#*_)WHQZfCS`*g^8qoOqrEzjT4Px1SWdL8FzcPVwP}l znamg?>2ZBn9trED|Aeq+Si#Qy&xO&k&*2sJ|My$?-|yJ8R~@{AZ$pBNbmMhVWW<5e zf1q~}H2ZL(GwRrq z3k{8&KHUcXXWcuA#SdOvdJMP#6ohW3+pZ)2BcI9V9GA@%jgQB^$R3Ul(w~K9wtYMT z6xmMc(Q<9>(nW(6L@Cac6zdIVBg;RM6ZNOo%L9?uT}MPW3jDXVN)2aPorTxPr|1)^ zk=a`GDL66+37dn{vXbPHWtRPk@K3O@g6<2ad1AB@Q??(}yj?+WiQY9?4Z>nI4}EIA ziwiWH){Y@W6%X{3Ah<;e^go&cHTA2BErw4)g;jqtLkPD}Fjd`GV(`zAbvF_;(f{aj znuOk-u$?GWxr?nYswsUwb!3#FkiTP|Gi zi-t6q_(V=xL2+re4NJs`+x|wKL(PR$whdS#9QkW75nG(rp*cT|aakX=UU_#w1GZQScg9-V3KJN_yoPD3XD6U` z1M3Zi%V;l)oD)pHyqfU66|ENLG3~(AxF9FHcDCuxIFQne&gkoGeVw>F*+#>B_*PRJ ztDYV2U2WW6ky)A!29S=i0r0>1rYQHicNp)LhUoDjQUGh`*~}WhE%$7rldnbz zL7{qIUq0(MY@Qm6sf)awuJ;jAh~QVr7k3Z=x~P?NE_!I z5Ts~eG7jNGMG~7=Dh~K77uio!)9LI+dV`lXrHZ)qMe&hSr+Tf@xo@1UiS0>+9&oD~ zi2=c+P_F~@y`d?Kg`mDa#%z5)-(fiD!g_>GXU-r~Ku;9dY5|hiXto_KoX|s%5)#l- zHX<3O$>B~EkY{`+nrL-DrFPKwHx*@QKwjm_hPw$W%$<_Ri)(>rFsDbm1*Fp&y0Mv0 zkfR43zuZr{dON{Q#%~gDJbzVx36m>osm|Ez&}j7P)Wvyy?t1%d=k5zoY5L@;Kog>> zj{4lFFLHYQ$N^A3)odS{>ETbTtu)*nc7PsOo?%WPdEv&O2~n_CxJm9BJ1&r`Dmd+L zlmsw#hy|*KELR^)X^`=JI~P6&0Awcof)r?6a#2++iF>2z%O^i9JjJM=D}=ra>lvqW zU}C1Ze4uN#xwvh3ST!v?mDZ@TBF{6({rKfn_(0^zx}kfum>d?{Q*-#Dx>lE6Vnc0O z*zEt@R4Blm74%Ge@;g%h>BYDNrX(mm#ech1#eX|Sp|v)vjT)UX9PS$QP@FX{j($R% z+``v(SY?)(vZ;Chp+6>NQHY6o-iCJw-$hqpOe*cy}hDA(2Rhtf4c-TTcXWvd)I z3xnxd-{|P|^fZyt7XzIQ4o9Z?rm&RLE24N|WG*Np3LZmAwZ?2<7Ta}WVAm4) zOOT@mjn3P!t#(OmPQ}5i6z8$qt-SZ+b<+5^qH7r_X!oR^<7p-ywx}2{EPUEtz^zJe z2HZLJo30_A8$Ww9)Dk@}6$2L<=QC7y)D=rN>UvF$SI_lrYt|VQOQ&G5c_D}=^6e@5 z7xM|Uoc^YhE#=%kj~tr4wzqtuyuC}>7VK)umm1r424A%|fj-(d;TmqIQm#QLHBtA$ z+Mo_fs5}BaWE<2b(!}R*r4t0&#xkXLYFb`nuO@FshQ(9{`GZ4_s_Et)e_9P+2dr`4 zKz?FL+bx2LQTrh$2>h=Gh7a5^cWgWhuB4*&n=&K%ytJ+HEFwutQdq*3FSTI3xq&Q; zN-*WW5Q3VH>2yZDBy#_j3*dwEz|>~5 z|FJ5H?EZ2OCFhDP`fhh9(L{p0{%0b|%4eu#UO9SZRmb9(XZZn7BfIcLQS~Qlhe^tY z8J=p&Xk#JHXy6+2@ga>nL)|QwLt(tma`5)$k8rB0yGfr8xwS2w8bWm`pjefDkA9GmdVS2c#VL)3;M0x%XPbYB8^(82r0RFxjY*J*g;V6QUH_ff%zCQ#lN7 zu7}_x+*=DU%lhD)kGs29Fi;wD3$pJGh%nG+JAEWfr6?k*XzVCxq@RVE2)wiQ15KGf z-S?_gtazkoATE4Vf#3strDi0u%Xje(R+)qA1cF@_B7NJ!rS0<~GQ(@4dF&Sis2i+* z35@2l%?6zGF&1R7$Z{wb^i(3;I&|vX2mIHr!gOB^Y4vBhg!IY${trGICjT)@$Ea*( zZfj=pucpm^lAiydf7#K@|Du0lN0c*`=(1K;Mj#SMMr3q4v1zo5Ob67Ba}kaRi~1`t zf=~K4IeKz<9oi~b#R&%E4b&RUPe=`#h;QUtQ?8{kCH)uuTgey*eaui((N6SV@x%!N zTnpzEPS5TeckC`wRwhOn9^_yD{1Arc8hATiC0%u zDm|5~VDz}cL(wB z^iRyl)kww6-c;7g#nsIIzsR6V*h2xawsdsWy?j#fkl#%IDQgk=kl?Tq?Ooldl80OuZD2p)$TdF9*Y$Zu$QR}-TVv=E{FP9 z96znEZ4Pm#2SY1nO{j0=RXEMOxF!HMt#8IHO1}n+;kZxS?y&#H>syi^K9WM{j!0)p zktCM2Lq?ItvGp>X)qxMqVr`8N%XK;K6eD`0QwS+8c`@Y@1g6|R)VIedTD%hPKn8K^ zNm+64$@aXly>eoY1y|Mk-DS97D|}p|67pSL{_pm`n7*CVX(&sy2dyn`-yNIUD1q;# z7xpDpr?@5wo$!F5a=1zPlI_?V9;#!S2&Evh%Cf3{W(gmxot7(2 zR*SJn-H=t9QxjQ7U$t!d@*N=qLV{PMCG*5Xjz~O$Be76)N)L}~F+xh9Bxh&F2-%Ie z-Fy~JI+M2a?eA$7JxqW>)1l2R)5Zi=lS`I<*~zO4hjP>7gx#V%)fS_-L_1=!BkLP* zuD#t;dO6RUJwS@9BhTK3KA@`A5#N7w?!Bj_CLj~MI31a%tc-#$-43@>FP3ktl^|z_ zduN-Xz}zKNDeEw(rXMJ7M{QAFq*L#PuW!im`D{X;EezGf8f&^;$;ZUkHysu68QBRO zG@;^wuUV?n$KaXhz=TWvsO}&8=56EOxl3L|Lj@PM%I}fajG_i*hAYWc8%ThoQkCMU zmRP4_K#JmrjcQsIQ3$=q|DHr@m8l?K7pJ8tMx?Z`!}JQ4KXl3L%vfy4O0h-b|SGYi>tHL|_d{;g2Y zt>ikBK!ilNf$71oy!K^N+w;J7<96%;*NnN}jaQ$==Jt7BJ_X32R!%-Z_@3KRhdt%y z8NFmT%n8hDf;XKuZP{*e`V+I7^y1|khl&DwZmXx6mm;_;b;{y zMlE2mk1_Xb(wz2;JKan8!|xod>vv+c=Z|Wypl+84hbJ)4miSN&&u_0Tt$k`_T3SD- z1t7E7M+B7RhJyiudq;9Tk-xuxDIgt7y5UUM@0Gch{-9tb9aGi#sqz-WFK1VkXD9SV z^_*$~v*^zg=&&;20e_7Yb3FxFJONi=!zIZ)k0JowhV1{v51Y3>9&!p>8Rjt1N4MKK z6ZgGa?{MOc#ev~=(g5v^Bl8|uod_Szig?9vN&Z7XYI<44EpbN}&%HKN_#@4;_*rG{ z2p<0}j_`Uf;2$#xAI?)|yPvAv+ox)$@IQrY6*K40H6&#-TL+_m;&rl(#=0tcXf8f9 zxDge)4qfF#{xTwj9o8RxtO_FZ$ZX}12yF-cL_A{h#CfusQ-y2HrJ2iHtsjI7vkTcLU~sN8U-OTDu&`_FHw zV}t_n_O`l?bMhwr1z}?cXm+@;oZ5l(r0-Z(VmVUfj@w!|A@v9cIxJS0s~kdoE7GK5 zW_f1ga2@;kj(O8q;(vptRgVpZ=SKV^@ix48mX|_%zYwR=dDS`=-gGapFW^BZE4KC- z=G7G>IboPohZNPwoH$e5F3Y<|qfaN@fC-UYI^V{A&t>apKo zB%hrO%YUJ9Ebe$}lFlN>Prk+5A&=zH$B@5Z7~D@YWH>ctfJfVL-Qmt8k6(0;C}kZF ztztOh?xn>T@^WLH+hLmg z&MB=}XFO}XM+ zAPWG+SA~>&JCEGz8Fkcz1=px3u68HNYIRJI=c%>YXE#4Nbm(xagnG2tGXIf1wLP}Z zQ(cL4F&T(7+ZoR>9u2b03s}MoG;GBMk)gYTUyO%*^Ro`hPu=}C z>+$dIL2ibZ>(X(a?48>K`U^{F!pIaf8~hkItirzvqzndXW1wsVbi?uZLC+b)@Xi2w zz{W`~v}ND-M2VI!vL=RL>em8{=wJPJw;zA>QuYXB{2&~@Cbe5XQ2AwnAcw@5AfP@- z0|+}F80HEX`URCnvw5%$5u=aaA-Q$F`ui%5wa>DO^D(Zzk2CALWI-R=phTv^BgdA> z+n2eO?G1;&FQjUTd#r|ngDNp;c%0ZQ*dK{1>B9wSJ)+uPVqXPSb}=Qq#<{v#MiOQ3 z024{a`>+~T@*#G^xyC6rfXb+Af=iR>Uhc!R;_#_JKIPbo>2b_BEibILUx{a*jw4Jg z<=PPtk6O!YOapx$>7jczKclmCo|sq}@csS_X<^%gZ>*FcME38i7;4bcn6UkY{;#F* z z;SxP)7jEwtmf#W2?5aL?ROYDK#a7vULO zLk}DG1ySpxsyi1z8rR_J8pwp)wb zj=zds9CZx7=20M3((`=R91L%UcimW6oLS(Gi}h8?xW5Q&`uLa(guq+f?wNe(?fTMJ z$2Y#lzQA;Ex;%eJWb}N;%kjeb6=qY64qHtXQGKo;aA*Ix#2ET~K&bm3jV$SuZd9(i z{(Cis5p>?Tc@A5$<_{n8@0Q*6Rwf>rkzaRLjSvkrHS(RsjJ$j1vEf{4DdrB+_Vgzc z`)k4MR8GKh3$>~Jxh;^@I zG=mD?O5T0z-z!Z6L-izK!^c0f`$m(lX=^}1SaOPxkFF=EfOO|Y^ba=-*vPuCdc1vWsl z*>lYGf)I~K@La1}AAUjyq}-sVOJk!fg=q9sK#2~2$rsxXmtA9n#L)`Xp_mvh@z<%MUR&TX($bC zNmdLp3_yNelU>9?CnC0L?WJxUA4Giy7M6yoXO8qTm%8*I^ykHUxHPb=0dK|eN1_1+oK%?rVVtU$?JQ#y=-@YT&uHv9_X3VMekI32?MB*Hea zzhF<@f(=@es}y$>D>54GVTvuX%>t=!w8LT7!n5ozT^?H=Z5cMp!PW6EhOl%X=dUqIvwCZMaP zD5EYP2h~5v;#FLiWL_3Qq^F&P;dWNDid8L!RT8HeotSxSGr8JrJR%Ti@W-R&VvGE# zVyUPIt!&n9L%DTK3rYIP>m^i&B(j8QK`K6LCBz?nn63V5%pF+&U=JA^ z?sf}VXpqCstM8VB1%iB&GPr;0wwYvt^LX5q4;S&S-&+@uOBL}Q32=5x!=I&Uj8as5 zZ$DQs!lg1n|4{b>2}WexU-GytvzEE|2si0U^2r26f(Oz6C!rHXfYdUZUuCCo<)P@dK_pf07BaA)E$jQ0z3UlX#E%Uq zokL{-!0qj12@pJeDdOB|{+h$++BE|0gV8Ry#?^Za1(I8D@XZTuVNiEC%R%j5qhRx z>llX8NABF|OIO{BWJm&iNgkAq5Y<@os5N%)%2JQAWB>=;e9mH(dwI3YLDykzZN z$yX6wTeLc@GyoqvpP_Bnon`24XVV#*qRUxtfyb0G#}bdBLZ3HUgIFWSZ$*9%Q5l

0B9dOM{x&deL0{w#&qH!^3!*T57Z2$*eSdaj5 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 P|0bT0L1Dg|?ccF;}yyQwW)! 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�CiwEU#wo92xHq+oblx$1U@1GUE*5Rv& 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\\d+)\\.(?\\d+)(?:\\.(?\\d+))?-R(?[\\d.-]+)(?:-SNAPSHOT)?$"); + protected final String revision; + + public BukkitVersion(int major, int minor, int patch, String revision) { + super(major, minor, patch); + this.revision = revision; + } + + private static BukkitVersion fromRegex(Matcher matcher) { + String major = matcher.group("major"); + String minor = matcher.group("minor"); + String patch = matcher.group("patch"); + String revision = matcher.group("revision"); + if (patch == null || patch.isEmpty()) { + patch = "0"; + } + return new BukkitVersion(Integer.parseInt(major), Integer.parseInt(minor), Integer.parseInt(patch), revision); + } + + @Nullable + public static BukkitVersion parseBukkitVersion(String bukkitVersion) { + Matcher targetMatcher = BUKKIT_VERSION_REGEX.matcher(bukkitVersion); + if (!targetMatcher.matches()) { + return null; + } + return BukkitVersion.fromRegex(targetMatcher); + } + + @Nullable + public static BukkitVersion getTargetVersion() { + return BukkitVersion.parseBukkitVersion("1.8.8-R0.1-SNAPSHOT"); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BukkitVersion)) { + return false; + } + BukkitVersion that = (BukkitVersion)o; + if (this.major != that.major) { + return false; + } + if (this.minor != that.minor) { + return false; + } + if (this.patch != that.patch) { + return false; + } + return this.revision.equals(that.revision); + } + + public String toString() { + if (this.patch <= 0) { + return this.major + "." + this.minor + "-R" + this.revision; + } + return this.major + "." + this.minor + "." + this.patch + "-R" + this.revision; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/FallbackTranslations.java b/source/decompiled/de/maxhenkel/voicechat/FallbackTranslations.java new file mode 100644 index 0000000..b2e6438 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/FallbackTranslations.java @@ -0,0 +1,246 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public class FallbackTranslations { + public static final Map FALLBACK_TRANSLATIONS; + + static { + HashMap translations = new HashMap(); + translations.put("key.category.voicechat.voicechat", "Voice Chat"); + translations.put("key.push_to_talk", "Push to Talk"); + translations.put("key.whisper", "Whisper"); + translations.put("key.mute_microphone", "Mute Microphone"); + translations.put("key.disable_voice_chat", "Disable Voice Chat"); + translations.put("key.hide_icons", "Hide Voice Chat Icons"); + translations.put("key.voice_chat_settings", "Voice Chat Settings"); + translations.put("key.voice_chat", "Voice Chat Menu"); + translations.put("key.voice_chat_group", "Group Management"); + translations.put("key.voice_chat_toggle_recording", "Toggle Recording"); + translations.put("key.voice_chat_adjust_volumes", "Adjust Volumes"); + translations.put("gui.voicechat.voice_chat.title", "Voice Chat"); + translations.put("gui.voicechat.voice_chat_settings.title", "Voice Chat Settings"); + translations.put("gui.voicechat.adjust_volume.title", "Adjust Volumes"); + translations.put("gui.voicechat.select_microphone.title", "Select Microphone"); + translations.put("gui.voicechat.select_speaker.title", "Select Speaker"); + translations.put("gui.voicechat.group.title", "Group Management"); + translations.put("gui.voicechat.join_create_group.title", "Join or Create Group"); + translations.put("gui.voicechat.create_group.title", "Create Group"); + translations.put("gui.voicechat.enter_password.title", "Enter Group Password"); + translations.put("message.voicechat.settings", "Settings"); + translations.put("message.voicechat.group", "Group"); + translations.put("message.voicechat.voice_chat_volume", "Voice Chat Volume: %s"); + translations.put("message.voicechat.gain", "Gain: %s"); + translations.put("message.voicechat.gain.auto", "Auto"); + translations.put("message.voicechat.gain.manual", "Manual"); + translations.put("message.voicechat.gain.manual.warning", "Please keep automatic microphone gain adjustment enabled to ensure consistent volume levels for everyone hearing you."); + translations.put("message.voicechat.microphone_gain", "Microphone Gain: %s dB"); + translations.put("message.voicechat.microphone_gain.warning", "Exceeding %s dB amplification may produce audio distortion and other issues!"); + translations.put("message.voicechat.mic_test_unavailable", "Microphone testing unavailable!"); + translations.put("message.voicechat.mic_test.disabled", "Press to enable microphone testing."); + translations.put("message.voicechat.mic_test.enabled", "Press to disable microphone testing."); + translations.put("message.voicechat.voice_chat_unavailable", "Voice chat unavailable"); + translations.put("message.voicechat.voice_chat_not_connected", "Voice chat not connected"); + translations.put("message.voicechat.activation_type", "Activation Method: %s"); + translations.put("message.voicechat.activation_type.ptt", "Push to Talk"); + translations.put("message.voicechat.activation_type.voice", "Voice"); + translations.put("message.voicechat.voice_activation_detection", "Voice Activation: %s"); + translations.put("message.voicechat.voice_activation_detection.auto", "Auto"); + translations.put("message.voicechat.voice_activation_detection.manual", "Manual"); + translations.put("message.voicechat.voice_activation", "Voice Activation Threshold: %s dB"); + translations.put("message.voicechat.voice_activation.disabled", "0 dB deactivates the microphone!"); + translations.put("message.voicechat.adjust_volumes", "Adjust Volumes"); + translations.put("message.voicechat.volume_amplification", "Amplification: %s"); + translations.put("message.voicechat.muted", "Muted"); + translations.put("message.voicechat.no_microphone", "No microphones available"); + translations.put("message.voicechat.default_microphone", "System Default"); + translations.put("message.voicechat.no_speaker", "No speakers available"); + translations.put("message.voicechat.default_speaker", "System Default"); + translations.put("message.voicechat.test_speaker", "Press to play a test sound."); + translations.put("message.voicechat.select_microphone", "Select Microphone"); + translations.put("message.voicechat.select_speaker", "Select Speaker"); + translations.put("message.voicechat.back", "Back"); + translations.put("message.voicechat.client_not_connected", "Client not connected"); + translations.put("message.voicechat.failed_to_send_ping", "Failed to send ping: %s"); + translations.put("message.voicechat.sending_ping", "Sending ping..."); + translations.put("message.voicechat.ping_sent_waiting", "Ping sent. Waiting for response..."); + translations.put("message.voicechat.ping_received", "Got a response in %s ms"); + translations.put("message.voicechat.ping_received_attempt", "Got a response in %s ms after %s attempts"); + translations.put("message.voicechat.ping_retry", "No response. Retrying..."); + translations.put("message.voicechat.ping_timed_out", "Request timed out after %s attempts"); + translations.put("message.voicechat.icons_hidden", "Voice chat icons hidden"); + translations.put("message.voicechat.icons_visible", "Voice chat icons visible"); + translations.put("message.voicechat.incompatible_version", "Your voice chat client version is not compatible with the server-side version.\nPlease install version %s of %s."); + translations.put("message.voicechat.incompatible", "Your voice chat client version is not compatible with the server-side version."); + translations.put("message.voicechat.group_members", "Group members:"); + translations.put("message.voicechat.no_group_members", "Empty group"); + translations.put("message.voicechat.join_create_group", "Join or Create Group"); + translations.put("message.voicechat.group_name", "Group Name"); + translations.put("message.voicechat.leave_group", "Press to leave the group."); + translations.put("message.voicechat.not_in_group", "You are not in a group"); + translations.put("message.voicechat.invite", "%s invited you to the group %s. %s"); + translations.put("message.voicechat.accept_invite", "ACCEPT"); + translations.put("message.voicechat.accept_invite.hover", "Click to accept the invitation"); + translations.put("message.voicechat.join_successful", "Successfully joined the group %s"); + translations.put("message.voicechat.leave_successful", "Successfully left the group"); + translations.put("message.voicechat.groups_disabled", "Groups are disabled on this server"); + translations.put("message.voicechat.recording_disabled", "Recording is disabled on this server"); + translations.put("message.voicechat.invite_successful", "Successfully invited %s"); + translations.put("message.voicechat.processing_recording_session", "Processing recording session..."); + translations.put("message.voicechat.processing_progress", "Processing %s%%"); + translations.put("message.voicechat.save_session", "Saved session to %s"); + translations.put("message.voicechat.save_session_failed", "Failed to save recording session: %s"); + translations.put("message.voicechat.recording_started", "Started Recording"); + translations.put("message.voicechat.recording_stopped", "Stopped Recording"); + translations.put("message.voicechat.open_folder", "Open folder"); + translations.put("message.voicechat.storage_size", "Estimated disk space usage: %s."); + translations.put("message.voicechat.microphone_unavailable", "Microphone unavailable"); + translations.put("message.voicechat.speaker_unavailable", "Speaker unavailable"); + translations.put("message.voicechat.playback_unavailable", "Playback unavailable"); + translations.put("message.voicechat.denoiser", "Noise Suppression: %s"); + translations.put("message.voicechat.denoiser.on", "ON"); + translations.put("message.voicechat.denoiser.off", "OFF"); + translations.put("message.voicechat.group_type_title", "%s (%s)"); + translations.put("message.voicechat.group_title", "%s"); + translations.put("message.voicechat.group_does_not_exist", "Such a group does not exist"); + translations.put("message.voicechat.group_name_not_unique", "Group name is ambiguous"); + translations.put("message.voicechat.create", "Create"); + translations.put("message.voicechat.create_group", "Create Group"); + translations.put("message.voicechat.create_group_button", "Create Group"); + translations.put("message.voicechat.optional_password", "Password (Optional)"); + translations.put("message.voicechat.group_type", "Group Type"); + translations.put("message.voicechat.group_type.normal", "Normal"); + translations.put("message.voicechat.group_type.normal.description", "Players who are not in your group can't hear you, but you can hear them."); + translations.put("message.voicechat.group_type.open", "Open"); + translations.put("message.voicechat.group_type.open.description", "Players who are not in your group can hear you, and you can hear them too."); + translations.put("message.voicechat.group_type.isolated", "Isolated"); + translations.put("message.voicechat.group_type.isolated.description", "Players who are not in your group can't hear you, and you can't hear them either."); + translations.put("message.voicechat.join_group", "Join Group"); + translations.put("message.voicechat.enter_group_password", "Enter Group Password"); + translations.put("message.voicechat.password", "Password"); + translations.put("message.voicechat.wrong_password", "Incorrect Password"); + translations.put("message.voicechat.server_port", "Voice chat server hosted on port %s"); + translations.put("message.voicechat.macos_no_mic_permission", "No permission to access your microphone. Please open System Settings > Privacy & Security and check the access options there"); + translations.put("message.voicechat.macos_unsupported_launcher", "Your launcher does not support macOS microphone permissions"); + translations.put("message.voicechat.player_no_voicechat", "%s does not have %s installed"); + translations.put("message.voicechat.mute.enabled", "Press to unmute your microphone."); + translations.put("message.voicechat.mute.disabled", "Press to mute your microphone."); + translations.put("message.voicechat.mute.disabled_ptt", "You can't mute your microphone when using push to talk."); + translations.put("message.voicechat.disable.enabled", "Press to enable all voice chat sounds."); + translations.put("message.voicechat.disable.disabled", "Press to disable all voice chat sounds."); + translations.put("message.voicechat.disable.speaker_unavailable", "Speaker unavailable."); + translations.put("message.voicechat.hide_icons.enabled", "Press to show all voice chat icons."); + translations.put("message.voicechat.hide_icons.disabled", "Press to hide all voice chat icons."); + translations.put("message.voicechat.show_group_hud.enabled", "Press to hide the group chat HUD."); + translations.put("message.voicechat.show_group_hud.disabled", "Press to show the group chat HUD."); + translations.put("message.voicechat.recording.disabled", "Press to start recording."); + translations.put("message.voicechat.recording.enabled", "Press to stop recording."); + translations.put("message.voicechat.no_speak_permission", "You do not have permission to speak"); + translations.put("message.voicechat.no_listen_permission", "You do not have permission to hear the voice chat"); + translations.put("message.voicechat.no_group_permission", "You do not have permission to join groups"); + translations.put("message.voicechat.search_hint", "Search..."); + translations.put("message.voicechat.search_empty", "Couldn't find any players with that name"); + translations.put("message.voicechat.other_volume", "Other"); + translations.put("message.voicechat.other_volume.description", "The volume of any sound not listed below."); + translations.put("message.voicechat.more_members", "and %s more..."); + translations.put("message.voicechat.no_groups", "No existing groups"); + translations.put("message.voicechat.audio_type", "3D Audio: %s"); + translations.put("message.voicechat.audio_type.normal", "Normal"); + translations.put("message.voicechat.audio_type.reduced", "Reduced"); + translations.put("message.voicechat.audio_type.off", "OFF"); + translations.put("message.voicechat.invite_player", "Press to invite %s to your group."); + translations.put("message.voicechat.press_to_reassign_key", "Press to assign a key."); + translations.put("message.voicechat.native_error", "Some modules failed to load - Your Operating System might not be supported."); + translations.put("message.voicechat.set_up", "Press %s to set up"); + translations.put("message.voicechat.onboarding.reset", "Onboarding status has been reset"); + translations.put("message.voicechat.onboarding.next", "Next"); + translations.put("message.voicechat.onboarding.confirm", "Confirm"); + translations.put("message.voicechat.onboarding.back", "Back"); + translations.put("message.voicechat.onboarding.cancel", "Cancel"); + translations.put("message.voicechat.onboarding.introduction.title", "Set Up %s"); + translations.put("message.voicechat.onboarding.introduction.description", "This setup guide will help you configure your voice chat.\n\nYou can skip it if you know what you are doing."); + translations.put("message.voicechat.onboarding.introduction.skip", "I Know What I Am Doing - Skip"); + translations.put("message.voicechat.onboarding.skip.title", "Skip Setup"); + translations.put("message.voicechat.onboarding.skip.description", "Are you sure you want to skip this setup guide?\n\nIf you skip it, you will have to adjust all the settings manually!"); + translations.put("message.voicechat.onboarding.microphone", "Select Microphone"); + translations.put("message.voicechat.onboarding.speaker", "Select Speaker"); + translations.put("message.voicechat.onboarding.activation.title", "Select Activation Method"); + translations.put("message.voicechat.onboarding.activation", "Please choose the method to activate your microphone."); + translations.put("message.voicechat.onboarding.activation.ptt", "When selecting %s, you need to press and hold a button to speak."); + translations.put("message.voicechat.onboarding.activation.ptt.name", "Push to Talk"); + translations.put("message.voicechat.onboarding.activation.voice", "%s automatically activates your microphone when you speak."); + translations.put("message.voicechat.onboarding.activation.voice.name", "Voice Activation"); + translations.put("message.voicechat.onboarding.ptt.title", "Choose Push to Talk Keybind"); + translations.put("message.voicechat.onboarding.ptt.description", "Please choose the key that should be used to activate your microphone."); + translations.put("message.voicechat.onboarding.ptt.button_description", "Press to assign"); + translations.put("message.voicechat.onboarding.voice.title", "Adjust Voice Activation"); + translations.put("message.voicechat.onboarding.voice.description", "Enable microphone testing and make sure you can hear your voice when speaking. If this doesn't work, use manual voice activation and adjust the voice activation threshold."); + translations.put("message.voicechat.onboarding.final", "Finish Setup"); + translations.put("message.voicechat.onboarding.final.description.success", "You have successfully configured your voice chat!\n\nOpen the voice chat menu with %s."); + translations.put("message.voicechat.onboarding.final.description.ptt", "Hold %s while speaking to use the voice chat."); + translations.put("message.voicechat.onboarding.final.description.voice", "You will be muted after exiting this screen. Press %s to unmute."); + translations.put("message.voicechat.onboarding.final.description.configuration", "You can always change all the settings in the voice chat menu.\nKeybinds, however, can be changed just like any other keybind in the game."); + translations.put("message.voicechat.onboarding.final.finish_setup", "Finish Setup"); + translations.put("resourcepack.voicechat.classic_icons", "Classic Icons"); + translations.put("resourcepack.voicechat.classic_icons.description", "Classic voice chat icons"); + translations.put("resourcepack.voicechat.white_icons", "White Icons"); + translations.put("resourcepack.voicechat.white_icons.description", "White voice chat icons"); + translations.put("resourcepack.voicechat.black_icons", "Black Icons"); + translations.put("resourcepack.voicechat.black_icons.description", "Black voice chat icons"); + translations.put("cloth_config.voicechat.settings", "Voice Chat Settings"); + translations.put("cloth_config.voicechat.category.general", "General"); + translations.put("cloth_config.voicechat.category.audio", "Audio"); + translations.put("cloth_config.voicechat.category.hud_icons", "HUD Icons"); + translations.put("cloth_config.voicechat.category.group_chat_icons", "Group Chat Icons"); + translations.put("cloth_config.voicechat.category.ingame_menu", "In-Game Menu"); + translations.put("cloth_config.voicechat.config.recording_destination", "Recording destination"); + translations.put("cloth_config.voicechat.config.recording_destination.description", "The location where recordings should be saved.\nLeave blank to use the default location."); + translations.put("cloth_config.voicechat.config.run_local_server", "Run in singleplayer or LAN worlds"); + translations.put("cloth_config.voicechat.config.run_local_server.description", "If the voice chat should work in singleplayer or in worlds shared over LAN."); + translations.put("cloth_config.voicechat.config.offline_player_volume_adjustment", "Offline player volume adjustment"); + translations.put("cloth_config.voicechat.config.offline_player_volume_adjustment.description", "If the volume adjustment interface should also display offline players."); + translations.put("cloth_config.voicechat.config.freecam_mode", "Freecam mode"); + translations.put("cloth_config.voicechat.config.freecam_mode.description", "How listening to other players should work when using freecam mods.\nCAMERA: You will hear the voice chat around your camera. Whether you will still be able to hear the voice chat when the camera is far away from your character depends on the voice chat broadcast range of the server.\nPLAYER: You will hear the voice chat around your character no matter where your camera is."); + translations.put("cloth_config.voicechat.config.freecam_mode.camera", "Camera"); + translations.put("cloth_config.voicechat.config.freecam_mode.player", "Player"); + translations.put("cloth_config.voicechat.config.mute_on_join", "Mute on join"); + translations.put("cloth_config.voicechat.config.mute_on_join.description", "If enabled, you will be automatically muted when joining a world."); + translations.put("cloth_config.voicechat.config.audio_packet_threshold", "Audio packet threshold"); + translations.put("cloth_config.voicechat.config.audio_packet_threshold.description", "The maximum number of audio packets that should be held back if a packet arrives out of order or is dropped.\nThis prevents audio packets that are only slightly out of order from being discarded.\nSet this to 0 to disable."); + translations.put("cloth_config.voicechat.config.voice_deactivation_delay", "Microphone deactivation delay"); + translations.put("cloth_config.voicechat.config.voice_deactivation_delay.description", "The time it takes for the microphone to deactivate when using voice activation.\nA value of 1 means 20 milliseconds, 2=40 ms, 3=60 ms, and so on."); + translations.put("cloth_config.voicechat.config.ptt_deactivation_delay", "Push to talk deactivation delay"); + translations.put("cloth_config.voicechat.config.ptt_deactivation_delay.description", "The time it takes for the microphone to deactivate when using push to talk.\nA value of 1 means 20 milliseconds, 2=40 ms, 3=60 ms, and so on."); + translations.put("cloth_config.voicechat.config.output_buffer_size", "Output buffer size"); + translations.put("cloth_config.voicechat.config.output_buffer_size.description", "The size of the audio output buffer (in packets).\nHigher values mean a higher latency but less crackling.\nIncrease this value if you have an unstable internet connection."); + translations.put("cloth_config.voicechat.config.show_nametag_icons", "Show voice chat name tag icons"); + translations.put("cloth_config.voicechat.config.show_nametag_icons.description", "If the voice chat icons next to player names should be visible"); + translations.put("cloth_config.voicechat.config.show_hud_icons", "Show voice chat HUD icons"); + translations.put("cloth_config.voicechat.config.show_hud_icons.description", "If the voice chat icons on the HUD should be visible"); + translations.put("cloth_config.voicechat.config.hud_icon_scale", "Voice chat HUD icon scale"); + translations.put("cloth_config.voicechat.config.hud_icon_scale.description", "The scale of the icons in the voice chat HUD, such as microphone or connection status."); + translations.put("cloth_config.voicechat.config.hud_icon_pos_x", "Voice chat HUD icon X position"); + translations.put("cloth_config.voicechat.config.hud_icon_pos_x.description", "The X position of the icons in the voice chat HUD.\nNegative values mean anchoring to the right instead."); + translations.put("cloth_config.voicechat.config.hud_icon_pos_y", "Voice chat HUD icon Y position"); + translations.put("cloth_config.voicechat.config.hud_icon_pos_y.description", "The Y position of the icons in the voice chat HUD.\nNegative values mean anchoring to the bottom instead."); + translations.put("cloth_config.voicechat.config.group_player_icon_orientation", "Group chat HUD icon orientation"); + translations.put("cloth_config.voicechat.config.group_player_icon_orientation.description", "The orientation of the player icons in the group chat HUD."); + translations.put("cloth_config.voicechat.config.group_player_icon_orientation.vertical", "Vertical"); + translations.put("cloth_config.voicechat.config.group_player_icon_orientation.horizontal", "Horizontal"); + translations.put("cloth_config.voicechat.config.group_hud_icon_scale", "Group chat HUD icon scale"); + translations.put("cloth_config.voicechat.config.group_hud_icon_scale.description", "The scale of the player icons in the group chat HUD."); + translations.put("cloth_config.voicechat.config.group_player_icon_pos_x", "Group chat HUD icon X position"); + translations.put("cloth_config.voicechat.config.group_player_icon_pos_x.description", "The X position of the player icons in the group chat HUD.\nNegative values mean anchoring to the right instead."); + translations.put("cloth_config.voicechat.config.group_player_icon_pos_y", "Group chat HUD icon Y position"); + translations.put("cloth_config.voicechat.config.group_player_icon_pos_y.description", "The Y position of the player icons in the group chat HUD.\nNegative values mean anchoring to the bottom instead."); + translations.put("cloth_config.voicechat.config.show_own_group_icon", "Show yourself in the group chat HUD"); + translations.put("cloth_config.voicechat.config.show_own_group_icon.description", "If your own player icon should be displayed in the group chat HUD when you are in a group."); + FALLBACK_TRANSLATIONS = Collections.unmodifiableMap(translations); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/PaperVersion.java b/source/decompiled/de/maxhenkel/voicechat/PaperVersion.java new file mode 100644 index 0000000..4896e6d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/PaperVersion.java @@ -0,0 +1,59 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat; + +import de.maxhenkel.voicechat.Version; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +public class PaperVersion +extends Version { + public static final Pattern PAPER_VERSION_REGEX = Pattern.compile("^(?\\d+)\\.(?\\d+)(?:\\.(?\\d+))?\\.(?.*)$"); + private final String build; + + public PaperVersion(int major, int minor, int patch, String build) { + super(major, minor, patch); + this.build = build; + } + + private static PaperVersion fromRegex(Matcher matcher) { + String major = matcher.group("major"); + String minor = matcher.group("minor"); + String patch = matcher.group("patch"); + String build = matcher.group("build"); + if (patch == null || patch.isEmpty()) { + patch = "0"; + } + return new PaperVersion(Integer.parseInt(major), Integer.parseInt(minor), Integer.parseInt(patch), build); + } + + public static boolean isPaper() { + return ReflectionUtils.doesClassExist("io.papermc.paper.ServerBuildInfo"); + } + + @Nullable + public static PaperVersion parsePaperVersion(String version) { + if (!PaperVersion.isPaper()) { + return null; + } + Matcher targetMatcher = PAPER_VERSION_REGEX.matcher(version); + if (!targetMatcher.matches()) { + return null; + } + return PaperVersion.fromRegex(targetMatcher); + } + + public String toString() { + if (this.patch <= 0) { + return this.major + "." + this.minor + "." + this.build; + } + return this.major + "." + this.minor + "." + this.patch + "." + this.build; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/Version.java b/source/decompiled/de/maxhenkel/voicechat/Version.java new file mode 100644 index 0000000..6c517e4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/Version.java @@ -0,0 +1,76 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.Bukkit + */ +package de.maxhenkel.voicechat; + +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.PaperVersion; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; + +public class Version { + protected final int major; + protected final int minor; + protected final int patch; + + public Version(int major, int minor, int patch) { + this.major = major; + this.minor = minor; + this.patch = patch; + } + + @Nullable + public static Version parse(String version) { + Version v = BukkitVersion.parseBukkitVersion(version); + if (v == null) { + v = PaperVersion.parsePaperVersion(version); + } + return v; + } + + @Nullable + public static Version getVersion() { + return Version.parse(Bukkit.getBukkitVersion()); + } + + public int getMajor() { + return this.major; + } + + public int getMinor() { + return this.minor; + } + + public int getPatch() { + return this.patch; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + Version that = (Version)o; + if (this.major != that.major) { + return false; + } + if (this.minor != that.minor) { + return false; + } + return this.patch == that.patch; + } + + public int hashCode() { + int result = this.major; + result = 31 * result + this.minor; + result = 31 * result + this.patch; + return result; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/Voicechat.java b/source/decompiled/de/maxhenkel/voicechat/Voicechat.java new file mode 100644 index 0000000..366a02a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/Voicechat.java @@ -0,0 +1,167 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.Bukkit + * org.bukkit.command.CommandExecutor + * org.bukkit.command.PluginCommand + * org.bukkit.command.TabCompleter + * org.bukkit.event.Listener + * org.bukkit.plugin.Plugin + * org.bukkit.plugin.ServicePriority + * org.bukkit.plugin.java.JavaPlugin + */ +package de.maxhenkel.voicechat; + +import de.maxhenkel.voicechat.BuildConstants; +import de.maxhenkel.voicechat.api.BukkitVoicechatService; +import de.maxhenkel.voicechat.command.VoiceChatCommands; +import de.maxhenkel.voicechat.commodore.Commodore; +import de.maxhenkel.voicechat.commodore.CommodoreProvider; +import de.maxhenkel.voicechat.compatibility.BukkitCompatibilityManager; +import de.maxhenkel.voicechat.compatibility.Compatibility; +import de.maxhenkel.voicechat.config.ServerConfig; +import de.maxhenkel.voicechat.config.Translations; +import de.maxhenkel.voicechat.configbuilder.ConfigBuilder; +import de.maxhenkel.voicechat.integration.commodore.CommodoreCommands; +import de.maxhenkel.voicechat.integration.commodore.MissingArgumentTypeException; +import de.maxhenkel.voicechat.integration.placeholderapi.VoicechatExpansion; +import de.maxhenkel.voicechat.integration.viaversion.ViaVersionCompatibility; +import de.maxhenkel.voicechat.logging.JavaLoggingLogger; +import de.maxhenkel.voicechat.logging.VoicechatLogger; +import de.maxhenkel.voicechat.net.NetManager; +import de.maxhenkel.voicechat.plugins.PluginManager; +import de.maxhenkel.voicechat.plugins.impl.BukkitVoicechatServiceImpl; +import de.maxhenkel.voicechat.voice.server.ServerPlayerManager; +import de.maxhenkel.voicechat.voice.server.ServerVoiceEvents; +import java.util.regex.Pattern; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.PluginCommand; +import org.bukkit.command.TabCompleter; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.ServicePriority; +import org.bukkit.plugin.java.JavaPlugin; + +public final class Voicechat +extends JavaPlugin { + public static Voicechat INSTANCE; + public static final String MODID = "voicechat"; + public static VoicechatLogger LOGGER; + public static int COMPATIBILITY_VERSION; + public static ServerConfig SERVER_CONFIG; + public static Translations TRANSLATIONS; + public static ServerVoiceEvents SERVER; + public static BukkitVoicechatServiceImpl apiService; + public static NetManager netManager; + public static Compatibility compatibility; + public static final int MAX_GROUP_NAME_LENGTH = 24; + public static final Pattern GROUP_REGEX; + + public void onEnable() { + block17: { + INSTANCE = this; + LOGGER = new JavaLoggingLogger(this.getLogger()); + if (Voicechat.debugMode()) { + LOGGER.warn("Running in debug mode - Don't leave this enabled in production!", new Object[0]); + } + try { + compatibility = BukkitCompatibilityManager.loadCompatibility(); + if (compatibility == null) { + this.disablePlugin(); + return; + } + } + catch (Throwable t) { + LOGGER.fatal("An unexpected error occurred while loading compatibility", t); + this.disablePlugin(); + return; + } + LOGGER.info("Compatibility version {}", COMPATIBILITY_VERSION); + SERVER_CONFIG = ConfigBuilder.builder(ServerConfig::new).path(this.getDataFolder().toPath().resolve("voicechat-server.properties")).build(); + TRANSLATIONS = ConfigBuilder.builder(Translations::new).path(this.getDataFolder().toPath().resolve("translations.properties")).build(); + netManager = new NetManager(); + netManager.onEnable(); + apiService = new BukkitVoicechatServiceImpl(); + this.getServer().getServicesManager().register(BukkitVoicechatService.class, (Object)apiService, (Plugin)this, ServicePriority.Normal); + PluginCommand voicechatCommand = this.getCommand(MODID); + if (voicechatCommand != null) { + VoiceChatCommands voiceChatCommands = new VoiceChatCommands(); + voicechatCommand.setExecutor((CommandExecutor)voiceChatCommands); + voicechatCommand.setTabCompleter((TabCompleter)voiceChatCommands); + try { + if (CommodoreProvider.isSupported()) { + Commodore commodore = CommodoreProvider.getCommodore((Plugin)this); + CommodoreCommands.registerCompletions(commodore); + LOGGER.info("Successfully initialized commodore command completion", new Object[0]); + break block17; + } + LOGGER.warn("Commodore command completion is not supported", new Object[0]); + } + catch (MissingArgumentTypeException t) { + LOGGER.warn("Failed to initialize commodore command completion: {}", t.getMessage()); + } + catch (Throwable t) { + LOGGER.warn("Failed to initialize commodore command completion", t); + } + } else { + LOGGER.error("Failed to register commands", new Object[0]); + } + } + try { + if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) { + new VoicechatExpansion().register(); + LOGGER.info("Successfully registered PlaceholderAPI expansion", new Object[0]); + } + } + catch (Throwable t) { + LOGGER.error("Failed to register PlaceholderAPI expansion", t); + } + try { + if (Bukkit.getPluginManager().getPlugin("ViaVersion") != null) { + ViaVersionCompatibility.register(); + LOGGER.info("Successfully added ViaVersion mappings", new Object[0]); + } + } + catch (Throwable t) { + LOGGER.error("Failed to add ViaVersion mappings", t); + } + if (System.getProperty("VOICECHAT_RELOADED") != null) { + LOGGER.error("{} does not support reloads! Expect that things will break!", "Simple Voice Chat"); + } + System.setProperty("VOICECHAT_RELOADED", "true"); + compatibility.runTask(() -> { + SERVER = new ServerVoiceEvents(); + PluginManager.instance().init(); + SERVER.init(); + Bukkit.getPluginManager().registerEvents((Listener)SERVER, (Plugin)this); + ServerPlayerManager.init((Plugin)this); + }); + } + + private void disablePlugin() { + LOGGER.fatal("Disabling {}", "Simple Voice Chat"); + Bukkit.getPluginManager().disablePlugin((Plugin)this); + } + + public void onDisable() { + if (netManager != null) { + netManager.onDisable(); + } + this.getServer().getServicesManager().unregister((Object)apiService); + if (SERVER != null) { + SERVER.getServer().close(); + } + } + + public static boolean debugMode() { + return System.getProperty("voicechat.debug") != null; + } + + static { + COMPATIBILITY_VERSION = BuildConstants.COMPATIBILITY_VERSION; + GROUP_REGEX = Pattern.compile("^[^\\p{C}\\s][^\\p{C}]{0,23}$"); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/BukkitVoicechatService.java b/source/decompiled/de/maxhenkel/voicechat/api/BukkitVoicechatService.java new file mode 100644 index 0000000..2a519a9 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/BukkitVoicechatService.java @@ -0,0 +1,11 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.VoicechatPlugin; + +public interface BukkitVoicechatService { + public void registerPlugin(VoicechatPlugin var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/ClientVoicechatSocket.java b/source/decompiled/de/maxhenkel/voicechat/api/ClientVoicechatSocket.java new file mode 100644 index 0000000..b163f31 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/ClientVoicechatSocket.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.RawUdpPacket; +import java.net.SocketAddress; + +public interface ClientVoicechatSocket { + public void open() throws Exception; + + public RawUdpPacket read() throws Exception; + + public void send(byte[] var1, SocketAddress var2) throws Exception; + + public void close(); + + public boolean isClosed(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/Entity.java b/source/decompiled/de/maxhenkel/voicechat/api/Entity.java new file mode 100644 index 0000000..32c9adc --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/Entity.java @@ -0,0 +1,16 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.Position; +import java.util.UUID; + +public interface Entity { + public UUID getUuid(); + + public Object getEntity(); + + public Position getPosition(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/ForgeVoicechatPlugin.java b/source/decompiled/de/maxhenkel/voicechat/api/ForgeVoicechatPlugin.java new file mode 100644 index 0000000..3c66d36 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/ForgeVoicechatPlugin.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(value=RetentionPolicy.RUNTIME) +public @interface ForgeVoicechatPlugin { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/Group.java b/source/decompiled/de/maxhenkel/voicechat/api/Group.java new file mode 100644 index 0000000..b80c830 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/Group.java @@ -0,0 +1,47 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api; + +import java.util.UUID; +import javax.annotation.Nullable; + +public interface Group { + public String getName(); + + public boolean hasPassword(); + + public UUID getId(); + + public boolean isPersistent(); + + public boolean isHidden(); + + public Type getType(); + + public static interface Builder { + public Builder setId(@Nullable UUID var1); + + public Builder setName(String var1); + + public Builder setPassword(@Nullable String var1); + + public Builder setPersistent(boolean var1); + + public Builder setHidden(boolean var1); + + public Builder setType(Type var1); + + public Group build(); + } + + public static interface Type { + public static final Type NORMAL = new Type(){}; + public static final Type OPEN = new Type(){}; + public static final Type ISOLATED = new Type(){}; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/Player.java b/source/decompiled/de/maxhenkel/voicechat/api/Player.java new file mode 100644 index 0000000..c268ecb --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/Player.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.Entity; + +public interface Player +extends Entity { + public Object getPlayer(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/Position.java b/source/decompiled/de/maxhenkel/voicechat/api/Position.java new file mode 100644 index 0000000..1a9eff9 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/Position.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +public interface Position { + public double getX(); + + public double getY(); + + public double getZ(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/RawUdpPacket.java b/source/decompiled/de/maxhenkel/voicechat/api/RawUdpPacket.java new file mode 100644 index 0000000..dedfd17 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/RawUdpPacket.java @@ -0,0 +1,15 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +import java.net.SocketAddress; + +public interface RawUdpPacket { + public byte[] getData(); + + public long getTimestamp(); + + public SocketAddress getSocketAddress(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/ServerLevel.java b/source/decompiled/de/maxhenkel/voicechat/api/ServerLevel.java new file mode 100644 index 0000000..ac3d59f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/ServerLevel.java @@ -0,0 +1,9 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +public interface ServerLevel { + public Object getServerLevel(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/ServerPlayer.java b/source/decompiled/de/maxhenkel/voicechat/api/ServerPlayer.java new file mode 100644 index 0000000..7a2b933 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/ServerPlayer.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.Player; +import de.maxhenkel.voicechat.api.ServerLevel; + +public interface ServerPlayer +extends Player { + public ServerLevel getServerLevel(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/VoicechatApi.java b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatApi.java new file mode 100644 index 0000000..54cfc12 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatApi.java @@ -0,0 +1,52 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.Entity; +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.ServerLevel; +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.audio.AudioConverter; +import de.maxhenkel.voicechat.api.mp3.Mp3Decoder; +import de.maxhenkel.voicechat.api.mp3.Mp3Encoder; +import de.maxhenkel.voicechat.api.opus.OpusDecoder; +import de.maxhenkel.voicechat.api.opus.OpusEncoder; +import de.maxhenkel.voicechat.api.opus.OpusEncoderMode; +import java.io.InputStream; +import java.io.OutputStream; +import javax.annotation.Nullable; +import javax.sound.sampled.AudioFormat; + +public interface VoicechatApi { + public OpusEncoder createEncoder(); + + public OpusEncoder createEncoder(OpusEncoderMode var1); + + public OpusDecoder createDecoder(); + + @Nullable + public Mp3Encoder createMp3Encoder(AudioFormat var1, int var2, int var3, OutputStream var4); + + @Nullable + public Mp3Decoder createMp3Decoder(InputStream var1); + + public AudioConverter getAudioConverter(); + + public Entity fromEntity(Object var1); + + public ServerLevel fromServerLevel(Object var1); + + public ServerPlayer fromServerPlayer(Object var1); + + public Position createPosition(double var1, double var3, double var5); + + public VolumeCategory.Builder volumeCategoryBuilder(); + + public double getVoiceChatDistance(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/VoicechatClientApi.java b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatClientApi.java new file mode 100644 index 0000000..9885918 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatClientApi.java @@ -0,0 +1,78 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.Entity; +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.VoicechatApi; +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.audiochannel.ClientEntityAudioChannel; +import de.maxhenkel.voicechat.api.audiochannel.ClientLocationalAudioChannel; +import de.maxhenkel.voicechat.api.audiochannel.ClientStaticAudioChannel; +import de.maxhenkel.voicechat.api.config.ConfigAccessor; +import de.maxhenkel.voicechat.api.config.VolumeConfigAccessor; +import java.util.UUID; +import javax.annotation.Nullable; + +public interface VoicechatClientApi +extends VoicechatApi { + public boolean isMuted(); + + default public boolean isDisabled() { + return this.isDisabled(null); + } + + public boolean isDisabled(@Nullable UUID var1); + + default public boolean isDisconnected() { + return this.isDisconnected(null); + } + + public boolean isDisconnected(@Nullable UUID var1); + + default public boolean isTalking() { + return this.isTalking(null); + } + + public boolean isTalking(@Nullable UUID var1); + + default public boolean isWhispering() { + return this.isWhispering(null); + } + + public boolean isWhispering(@Nullable UUID var1); + + public boolean isPushToTalkKeyPressed(); + + public boolean isWhisperKeyPressed(); + + @Nullable + public Group getGroup(); + + @Deprecated + public ClientEntityAudioChannel createEntityAudioChannel(UUID var1); + + public ClientEntityAudioChannel createEntityAudioChannel(UUID var1, Entity var2); + + public ClientLocationalAudioChannel createLocationalAudioChannel(UUID var1, Position var2); + + public ClientStaticAudioChannel createStaticAudioChannel(UUID var1); + + public void registerClientVolumeCategory(VolumeCategory var1); + + default public void unregisterClientVolumeCategory(VolumeCategory category) { + this.unregisterClientVolumeCategory(category.getId()); + } + + public void unregisterClientVolumeCategory(String var1); + + public ConfigAccessor getClientConfig(); + + public VolumeConfigAccessor getVolumeConfig(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/VoicechatConnection.java b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatConnection.java new file mode 100644 index 0000000..8c01fcc --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatConnection.java @@ -0,0 +1,33 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.ServerPlayer; +import javax.annotation.Nullable; + +public interface VoicechatConnection { + @Nullable + public Group getGroup(); + + public boolean isInGroup(); + + public void setGroup(@Nullable Group var1); + + public boolean isConnected(); + + public void setConnected(boolean var1); + + public boolean isDisabled(); + + public void setDisabled(boolean var1); + + public boolean isInstalled(); + + public ServerPlayer getPlayer(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/VoicechatPlugin.java b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatPlugin.java new file mode 100644 index 0000000..994a104 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatPlugin.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.VoicechatApi; +import de.maxhenkel.voicechat.api.events.EventRegistration; + +public interface VoicechatPlugin { + public String getPluginId(); + + default public void initialize(VoicechatApi api) { + } + + default public void registerEvents(EventRegistration registration) { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/VoicechatServerApi.java b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatServerApi.java new file mode 100644 index 0000000..4006362 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatServerApi.java @@ -0,0 +1,125 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.Entity; +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.ServerLevel; +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.VoicechatApi; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.audiochannel.AudioChannel; +import de.maxhenkel.voicechat.api.audiochannel.AudioPlayer; +import de.maxhenkel.voicechat.api.audiochannel.EntityAudioChannel; +import de.maxhenkel.voicechat.api.audiochannel.LocationalAudioChannel; +import de.maxhenkel.voicechat.api.audiochannel.StaticAudioChannel; +import de.maxhenkel.voicechat.api.audiolistener.AudioListener; +import de.maxhenkel.voicechat.api.audiolistener.PlayerAudioListener; +import de.maxhenkel.voicechat.api.audiosender.AudioSender; +import de.maxhenkel.voicechat.api.config.ConfigAccessor; +import de.maxhenkel.voicechat.api.opus.OpusEncoder; +import de.maxhenkel.voicechat.api.packets.ConvertablePacket; +import de.maxhenkel.voicechat.api.packets.EntitySoundPacket; +import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket; +import de.maxhenkel.voicechat.api.packets.StaticSoundPacket; +import java.util.Collection; +import java.util.UUID; +import java.util.function.Predicate; +import java.util.function.Supplier; +import javax.annotation.Nullable; + +public interface VoicechatServerApi +extends VoicechatApi { + public void sendEntitySoundPacketTo(VoicechatConnection var1, EntitySoundPacket var2); + + public void sendLocationalSoundPacketTo(VoicechatConnection var1, LocationalSoundPacket var2); + + public void sendStaticSoundPacketTo(VoicechatConnection var1, StaticSoundPacket var2); + + public ConvertablePacket createPacket(); + + @Nullable + public EntityAudioChannel createEntityAudioChannel(UUID var1, Entity var2); + + @Nullable + public LocationalAudioChannel createLocationalAudioChannel(UUID var1, ServerLevel var2, Position var3); + + @Deprecated + @Nullable + public StaticAudioChannel createStaticAudioChannel(UUID var1, ServerLevel var2, VoicechatConnection var3); + + @Nullable + public StaticAudioChannel createStaticAudioChannel(UUID var1); + + public AudioPlayer createAudioPlayer(AudioChannel var1, OpusEncoder var2, Supplier var3); + + public AudioPlayer createAudioPlayer(AudioChannel var1, OpusEncoder var2, short[] var3); + + public AudioSender createAudioSender(VoicechatConnection var1); + + public boolean registerAudioSender(AudioSender var1); + + public boolean unregisterAudioSender(AudioSender var1); + + public PlayerAudioListener.Builder playerAudioListenerBuilder(); + + public boolean registerAudioListener(AudioListener var1); + + public boolean unregisterAudioListener(AudioListener var1); + + public boolean unregisterAudioListener(UUID var1); + + @Nullable + public VoicechatConnection getConnectionOf(UUID var1); + + @Nullable + default public VoicechatConnection getConnectionOf(ServerPlayer player) { + return this.getConnectionOf(player.getUuid()); + } + + @Deprecated + public Group createGroup(String var1, @Nullable String var2); + + @Deprecated + public Group createGroup(String var1, @Nullable String var2, boolean var3); + + public Group.Builder groupBuilder(); + + public boolean removeGroup(UUID var1); + + @Nullable + public Group getGroup(UUID var1); + + public Collection getGroups(); + + @Nullable + @Deprecated + public UUID getSecret(UUID var1); + + public Collection getPlayersInRange(ServerLevel var1, Position var2, double var3, Predicate var5); + + public double getBroadcastRange(); + + default public Collection getPlayersInRange(ServerLevel level, Position pos, double range) { + return this.getPlayersInRange(level, pos, range, null); + } + + public void registerVolumeCategory(VolumeCategory var1); + + default public void unregisterVolumeCategory(VolumeCategory category) { + this.unregisterVolumeCategory(category.getId()); + } + + public void unregisterVolumeCategory(String var1); + + public Collection getVolumeCategories(); + + public ConfigAccessor getServerConfig(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/VoicechatSocket.java b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatSocket.java new file mode 100644 index 0000000..e975a0e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/VoicechatSocket.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api; + +import de.maxhenkel.voicechat.api.RawUdpPacket; +import java.net.SocketAddress; + +public interface VoicechatSocket { + public void open(int var1, String var2) throws Exception; + + public RawUdpPacket read() throws Exception; + + public void send(byte[] var1, SocketAddress var2) throws Exception; + + public int getLocalPort(); + + public void close(); + + public boolean isClosed(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/VolumeCategory.java b/source/decompiled/de/maxhenkel/voicechat/api/VolumeCategory.java new file mode 100644 index 0000000..f5edbdf --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/VolumeCategory.java @@ -0,0 +1,44 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api; + +import javax.annotation.Nullable; + +public interface VolumeCategory { + public String getId(); + + public String getName(); + + @Nullable + public String getNameTranslationKey(); + + @Nullable + public String getDescription(); + + @Nullable + public String getDescriptionTranslationKey(); + + @Nullable + public int[][] getIcon(); + + public static interface Builder { + public Builder setId(String var1); + + public Builder setName(String var1); + + public Builder setNameTranslationKey(@Nullable String var1); + + public Builder setDescription(@Nullable String var1); + + public Builder setDescriptionTranslationKey(@Nullable String var1); + + public Builder setIcon(@Nullable int[][] var1); + + public VolumeCategory build(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audio/AudioConverter.java b/source/decompiled/de/maxhenkel/voicechat/api/audio/AudioConverter.java new file mode 100644 index 0000000..36ac212 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audio/AudioConverter.java @@ -0,0 +1,19 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audio; + +public interface AudioConverter { + public short[] bytesToShorts(byte[] var1); + + public byte[] shortsToBytes(short[] var1); + + public short[] floatsToShorts(float[] var1); + + public float[] shortsToFloats(short[] var1); + + public byte[] floatsToBytes(float[] var1); + + public float[] bytesToFloats(byte[] var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/AudioChannel.java b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/AudioChannel.java new file mode 100644 index 0000000..8cf5367 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/AudioChannel.java @@ -0,0 +1,33 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.audiochannel; + +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.packets.MicrophonePacket; +import java.util.UUID; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public interface AudioChannel { + public void send(byte[] var1); + + public void send(MicrophonePacket var1); + + public void setFilter(Predicate var1); + + public void flush(); + + public boolean isClosed(); + + public UUID getId(); + + @Nullable + public String getCategory(); + + public void setCategory(@Nullable String var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/AudioPlayer.java b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/AudioPlayer.java new file mode 100644 index 0000000..c330938 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/AudioPlayer.java @@ -0,0 +1,19 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiochannel; + +public interface AudioPlayer { + public void startPlaying(); + + public void stopPlaying(); + + public boolean isStarted(); + + public boolean isPlaying(); + + public boolean isStopped(); + + public void setOnStopped(Runnable var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientAudioChannel.java b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientAudioChannel.java new file mode 100644 index 0000000..a6e3bf3 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientAudioChannel.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.audiochannel; + +import java.util.UUID; +import javax.annotation.Nullable; + +public interface ClientAudioChannel { + public UUID getId(); + + public void play(short[] var1); + + @Nullable + public String getCategory(); + + public void setCategory(@Nullable String var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientEntityAudioChannel.java b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientEntityAudioChannel.java new file mode 100644 index 0000000..d1d3d39 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientEntityAudioChannel.java @@ -0,0 +1,21 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiochannel; + +import de.maxhenkel.voicechat.api.audiochannel.ClientAudioChannel; +import java.util.UUID; + +public interface ClientEntityAudioChannel +extends ClientAudioChannel { + public UUID getEntityId(); + + public void setWhispering(boolean var1); + + public boolean isWhispering(); + + public float getDistance(); + + public void setDistance(float var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientLocationalAudioChannel.java b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientLocationalAudioChannel.java new file mode 100644 index 0000000..33c4e9f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientLocationalAudioChannel.java @@ -0,0 +1,19 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiochannel; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.audiochannel.ClientAudioChannel; + +public interface ClientLocationalAudioChannel +extends ClientAudioChannel { + public Position getLocation(); + + public void setLocation(Position var1); + + public float getDistance(); + + public void setDistance(float var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientStaticAudioChannel.java b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientStaticAudioChannel.java new file mode 100644 index 0000000..11e7b86 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/ClientStaticAudioChannel.java @@ -0,0 +1,11 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiochannel; + +import de.maxhenkel.voicechat.api.audiochannel.ClientAudioChannel; + +public interface ClientStaticAudioChannel +extends ClientAudioChannel { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/EntityAudioChannel.java b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/EntityAudioChannel.java new file mode 100644 index 0000000..d9424b6 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/EntityAudioChannel.java @@ -0,0 +1,23 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiochannel; + +import de.maxhenkel.voicechat.api.Entity; +import de.maxhenkel.voicechat.api.audiochannel.AudioChannel; + +public interface EntityAudioChannel +extends AudioChannel { + public boolean isWhispering(); + + public void setWhispering(boolean var1); + + public void updateEntity(Entity var1); + + public Entity getEntity(); + + public float getDistance(); + + public void setDistance(float var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/LocationalAudioChannel.java b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/LocationalAudioChannel.java new file mode 100644 index 0000000..06e1d8b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/LocationalAudioChannel.java @@ -0,0 +1,19 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiochannel; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.audiochannel.AudioChannel; + +public interface LocationalAudioChannel +extends AudioChannel { + public void updateLocation(Position var1); + + public Position getLocation(); + + public float getDistance(); + + public void setDistance(float var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/StaticAudioChannel.java b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/StaticAudioChannel.java new file mode 100644 index 0000000..b14ed16 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiochannel/StaticAudioChannel.java @@ -0,0 +1,21 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiochannel; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.audiochannel.AudioChannel; + +public interface StaticAudioChannel +extends AudioChannel { + public void setBypassGroupIsolation(boolean var1); + + public boolean bypassesGroupIsolation(); + + public void addTarget(VoicechatConnection var1); + + public void removeTarget(VoicechatConnection var1); + + public void clearTargets(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiolistener/AudioListener.java b/source/decompiled/de/maxhenkel/voicechat/api/audiolistener/AudioListener.java new file mode 100644 index 0000000..d50e83a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiolistener/AudioListener.java @@ -0,0 +1,11 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiolistener; + +import java.util.UUID; + +public interface AudioListener { + public UUID getListenerId(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiolistener/PlayerAudioListener.java b/source/decompiled/de/maxhenkel/voicechat/api/audiolistener/PlayerAudioListener.java new file mode 100644 index 0000000..9755797 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiolistener/PlayerAudioListener.java @@ -0,0 +1,26 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiolistener; + +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.audiolistener.AudioListener; +import de.maxhenkel.voicechat.api.packets.SoundPacket; +import java.util.UUID; +import java.util.function.Consumer; + +public interface PlayerAudioListener +extends AudioListener { + public UUID getPlayerUuid(); + + public static interface Builder { + public Builder setPlayer(ServerPlayer var1); + + public Builder setPlayer(UUID var1); + + public Builder setPacketListener(Consumer var1); + + public PlayerAudioListener build(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/audiosender/AudioSender.java b/source/decompiled/de/maxhenkel/voicechat/api/audiosender/AudioSender.java new file mode 100644 index 0000000..ed8d313 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/audiosender/AudioSender.java @@ -0,0 +1,19 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.audiosender; + +public interface AudioSender { + public AudioSender whispering(boolean var1); + + public boolean isWhispering(); + + public AudioSender sequenceNumber(long var1); + + public boolean canSend(); + + public boolean send(byte[] var1); + + public boolean reset(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/config/ConfigAccessor.java b/source/decompiled/de/maxhenkel/voicechat/api/config/ConfigAccessor.java new file mode 100644 index 0000000..e8c488e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/config/ConfigAccessor.java @@ -0,0 +1,49 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.config; + +import javax.annotation.Nullable; + +public interface ConfigAccessor { + public boolean hasKey(String var1); + + @Nullable + public String getValue(String var1); + + default public String getString(String key, String def) { + String value = this.getValue(key); + if (value == null) { + return def; + } + return value; + } + + default public boolean getBoolean(String key, boolean def) { + String value = this.getValue(key); + if (value == null) { + return def; + } + return Boolean.parseBoolean(value); + } + + default public int getInt(String key, int def) { + String value = this.getValue(key); + if (value == null) { + return def; + } + return Integer.parseInt(value); + } + + default public double getDouble(String key, double def) { + String value = this.getValue(key); + if (value == null) { + return def; + } + return Double.parseDouble(value); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/config/VolumeConfigAccessor.java b/source/decompiled/de/maxhenkel/voicechat/api/config/VolumeConfigAccessor.java new file mode 100644 index 0000000..466ad3e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/config/VolumeConfigAccessor.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.config; + +import java.util.UUID; + +public interface VolumeConfigAccessor { + public double getVolume(UUID var1); + + public double getCategoryVolume(String var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/ClientEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientEvent.java new file mode 100644 index 0000000..0e00374 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientEvent.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.VoicechatClientApi; +import de.maxhenkel.voicechat.api.events.Event; + +public interface ClientEvent +extends Event { + public VoicechatClientApi getVoicechat(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/ClientReceiveSoundEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientReceiveSoundEvent.java new file mode 100644 index 0000000..4806bd9 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientReceiveSoundEvent.java @@ -0,0 +1,42 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.events.ClientEvent; +import java.util.UUID; +import javax.annotation.Nullable; + +public interface ClientReceiveSoundEvent +extends ClientEvent { + public UUID getId(); + + public short[] getRawAudio(); + + public void setRawAudio(@Nullable short[] var1); + + public static interface StaticSound + extends ClientReceiveSoundEvent { + } + + public static interface LocationalSound + extends ClientReceiveSoundEvent { + public Position getPosition(); + + public float getDistance(); + } + + public static interface EntitySound + extends ClientReceiveSoundEvent { + public UUID getEntityId(); + + public boolean isWhispering(); + + public float getDistance(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/ClientSoundEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientSoundEvent.java new file mode 100644 index 0000000..494e7ff --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientSoundEvent.java @@ -0,0 +1,16 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ClientEvent; + +public interface ClientSoundEvent +extends ClientEvent { + public short[] getRawAudio(); + + public void setRawAudio(short[] var1); + + public boolean isWhispering(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/ClientVoicechatConnectionEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientVoicechatConnectionEvent.java new file mode 100644 index 0000000..994b730 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientVoicechatConnectionEvent.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ClientEvent; + +public interface ClientVoicechatConnectionEvent +extends ClientEvent { + public boolean isConnected(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/ClientVoicechatInitializationEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientVoicechatInitializationEvent.java new file mode 100644 index 0000000..6134ce7 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/ClientVoicechatInitializationEvent.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.ClientVoicechatSocket; +import de.maxhenkel.voicechat.api.events.ClientEvent; +import javax.annotation.Nullable; + +public interface ClientVoicechatInitializationEvent +extends ClientEvent { + public void setSocketImplementation(ClientVoicechatSocket var1); + + @Nullable + public ClientVoicechatSocket getSocketImplementation(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/CreateGroupEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/CreateGroupEvent.java new file mode 100644 index 0000000..f0cd60d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/CreateGroupEvent.java @@ -0,0 +1,23 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.GroupEvent; +import javax.annotation.Nullable; + +public interface CreateGroupEvent +extends GroupEvent { + @Override + public Group getGroup(); + + @Override + @Nullable + public VoicechatConnection getConnection(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/CreateOpenALContextEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/CreateOpenALContextEvent.java new file mode 100644 index 0000000..15ea83e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/CreateOpenALContextEvent.java @@ -0,0 +1,14 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ClientEvent; + +public interface CreateOpenALContextEvent +extends ClientEvent { + public long getContext(); + + public long getDevice(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/DestroyOpenALContextEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/DestroyOpenALContextEvent.java new file mode 100644 index 0000000..5e8004a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/DestroyOpenALContextEvent.java @@ -0,0 +1,14 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ClientEvent; + +public interface DestroyOpenALContextEvent +extends ClientEvent { + public long getContext(); + + public long getDevice(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/EntitySoundPacketEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/EntitySoundPacketEvent.java new file mode 100644 index 0000000..fb5c416 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/EntitySoundPacketEvent.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.SoundPacketEvent; +import de.maxhenkel.voicechat.api.packets.EntitySoundPacket; + +public interface EntitySoundPacketEvent +extends SoundPacketEvent { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/Event.java b/source/decompiled/de/maxhenkel/voicechat/api/events/Event.java new file mode 100644 index 0000000..81d99fc --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/Event.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +public interface Event { + public boolean isCancellable(); + + public boolean cancel(); + + public boolean isCancelled(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/EventRegistration.java b/source/decompiled/de/maxhenkel/voicechat/api/events/EventRegistration.java new file mode 100644 index 0000000..784a374 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/EventRegistration.java @@ -0,0 +1,16 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.Event; +import java.util.function.Consumer; + +public interface EventRegistration { + public void registerEvent(Class var1, Consumer var2, int var3); + + default public void registerEvent(Class eventClass, Consumer onPacket) { + this.registerEvent(eventClass, onPacket, 0); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/GroupEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/GroupEvent.java new file mode 100644 index 0000000..4405f6d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/GroupEvent.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.ServerEvent; +import javax.annotation.Nullable; + +public interface GroupEvent +extends ServerEvent { + @Nullable + public Group getGroup(); + + @Nullable + public VoicechatConnection getConnection(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/JoinGroupEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/JoinGroupEvent.java new file mode 100644 index 0000000..9912984 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/JoinGroupEvent.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.GroupEvent; + +public interface JoinGroupEvent +extends GroupEvent { + @Override + public Group getGroup(); + + @Override + public VoicechatConnection getConnection(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/LeaveGroupEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/LeaveGroupEvent.java new file mode 100644 index 0000000..7c72c47 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/LeaveGroupEvent.java @@ -0,0 +1,23 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.GroupEvent; +import javax.annotation.Nullable; + +public interface LeaveGroupEvent +extends GroupEvent { + @Override + @Nullable + public Group getGroup(); + + @Override + public VoicechatConnection getConnection(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/LocationalSoundPacketEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/LocationalSoundPacketEvent.java new file mode 100644 index 0000000..d477f6d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/LocationalSoundPacketEvent.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.SoundPacketEvent; +import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket; + +public interface LocationalSoundPacketEvent +extends SoundPacketEvent { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/MergeClientSoundEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/MergeClientSoundEvent.java new file mode 100644 index 0000000..7d97d4e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/MergeClientSoundEvent.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ClientEvent; + +public interface MergeClientSoundEvent +extends ClientEvent { + public void mergeAudio(short[] var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/MicrophoneMuteEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/MicrophoneMuteEvent.java new file mode 100644 index 0000000..1d802ed --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/MicrophoneMuteEvent.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ClientEvent; + +public interface MicrophoneMuteEvent +extends ClientEvent { + public boolean isDisabled(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/MicrophonePacketEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/MicrophonePacketEvent.java new file mode 100644 index 0000000..51f28ea --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/MicrophonePacketEvent.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.PacketEvent; +import de.maxhenkel.voicechat.api.packets.MicrophonePacket; + +public interface MicrophonePacketEvent +extends PacketEvent { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/NameTagIconRenderEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/NameTagIconRenderEvent.java new file mode 100644 index 0000000..1e783f7 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/NameTagIconRenderEvent.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ClientEvent; +import java.util.UUID; + +public interface NameTagIconRenderEvent +extends ClientEvent { + public UUID getEntityId(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/OpenALSoundEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/OpenALSoundEvent.java new file mode 100644 index 0000000..a3aa9ab --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/OpenALSoundEvent.java @@ -0,0 +1,35 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.events.ClientEvent; +import java.util.UUID; +import javax.annotation.Nullable; + +public interface OpenALSoundEvent +extends ClientEvent { + @Nullable + public Position getPosition(); + + @Nullable + public UUID getChannelId(); + + public int getSource(); + + @Nullable + public String getCategory(); + + public static interface Post + extends OpenALSoundEvent { + } + + public static interface Pre + extends OpenALSoundEvent { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/PacketEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/PacketEvent.java new file mode 100644 index 0000000..4b0fdfd --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/PacketEvent.java @@ -0,0 +1,24 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.ServerEvent; +import de.maxhenkel.voicechat.api.packets.Packet; +import javax.annotation.Nullable; + +public interface PacketEvent +extends ServerEvent { + public T getPacket(); + + @Nullable + public VoicechatConnection getReceiverConnection(); + + @Nullable + public VoicechatConnection getSenderConnection(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/PlayerConnectedEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/PlayerConnectedEvent.java new file mode 100644 index 0000000..e76096f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/PlayerConnectedEvent.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.ServerEvent; + +public interface PlayerConnectedEvent +extends ServerEvent { + public VoicechatConnection getConnection(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/PlayerDisconnectedEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/PlayerDisconnectedEvent.java new file mode 100644 index 0000000..d23382f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/PlayerDisconnectedEvent.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ServerEvent; +import java.util.UUID; + +public interface PlayerDisconnectedEvent +extends ServerEvent { + public UUID getPlayerUuid(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/PlayerStateChangedEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/PlayerStateChangedEvent.java new file mode 100644 index 0000000..c29eef5 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/PlayerStateChangedEvent.java @@ -0,0 +1,25 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.ServerEvent; +import java.util.UUID; +import javax.annotation.Nullable; + +public interface PlayerStateChangedEvent +extends ServerEvent { + public boolean isDisabled(); + + public boolean isDisconnected(); + + public UUID getPlayerUuid(); + + @Nullable + public VoicechatConnection getConnection(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/RegisterVolumeCategoryEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/RegisterVolumeCategoryEvent.java new file mode 100644 index 0000000..6562898 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/RegisterVolumeCategoryEvent.java @@ -0,0 +1,11 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.VolumeCategoryEvent; + +public interface RegisterVolumeCategoryEvent +extends VolumeCategoryEvent { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/RemoveGroupEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/RemoveGroupEvent.java new file mode 100644 index 0000000..ffdce3d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/RemoveGroupEvent.java @@ -0,0 +1,24 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.GroupEvent; +import javax.annotation.Nullable; + +public interface RemoveGroupEvent +extends GroupEvent { + @Override + public Group getGroup(); + + @Override + @Nullable + @Deprecated + public VoicechatConnection getConnection(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/ServerEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/ServerEvent.java new file mode 100644 index 0000000..addf083 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/ServerEvent.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.VoicechatServerApi; +import de.maxhenkel.voicechat.api.events.Event; + +public interface ServerEvent +extends Event { + public VoicechatServerApi getVoicechat(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/SoundPacketEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/SoundPacketEvent.java new file mode 100644 index 0000000..6e15ac6 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/SoundPacketEvent.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.PacketEvent; +import de.maxhenkel.voicechat.api.packets.Packet; + +public interface SoundPacketEvent +extends PacketEvent { + public static final String SOURCE_GROUP = "group"; + public static final String SOURCE_PROXIMITY = "proximity"; + public static final String SOURCE_SPECTATOR = "spectator"; + public static final String SOURCE_PLUGIN = "plugin"; + + public String getSource(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/StaticSoundPacketEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/StaticSoundPacketEvent.java new file mode 100644 index 0000000..af71625 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/StaticSoundPacketEvent.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.SoundPacketEvent; +import de.maxhenkel.voicechat.api.packets.StaticSoundPacket; + +public interface StaticSoundPacketEvent +extends SoundPacketEvent { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/UnregisterVolumeCategoryEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/UnregisterVolumeCategoryEvent.java new file mode 100644 index 0000000..a4eeefa --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/UnregisterVolumeCategoryEvent.java @@ -0,0 +1,11 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.VolumeCategoryEvent; + +public interface UnregisterVolumeCategoryEvent +extends VolumeCategoryEvent { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/VoiceDistanceEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/VoiceDistanceEvent.java new file mode 100644 index 0000000..ecf20c6 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/VoiceDistanceEvent.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.ServerEvent; +import de.maxhenkel.voicechat.api.packets.MicrophonePacket; + +public interface VoiceDistanceEvent +extends ServerEvent { + public MicrophonePacket getPacket(); + + public VoicechatConnection getSenderConnection(); + + public float getDistance(); + + public void setDistance(float var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/VoiceHostEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/VoiceHostEvent.java new file mode 100644 index 0000000..ad8053e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/VoiceHostEvent.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.events.ServerEvent; + +public interface VoiceHostEvent +extends ServerEvent { + public String getVoiceHost(); + + public void setVoiceHost(String var1); + + public ServerPlayer getPlayer(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatDisableEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatDisableEvent.java new file mode 100644 index 0000000..cbd2981 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatDisableEvent.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ClientEvent; + +public interface VoicechatDisableEvent +extends ClientEvent { + public boolean isDisabled(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStartedEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStartedEvent.java new file mode 100644 index 0000000..635fdc8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStartedEvent.java @@ -0,0 +1,11 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ServerEvent; + +public interface VoicechatServerStartedEvent +extends ServerEvent { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStartingEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStartingEvent.java new file mode 100644 index 0000000..de62eb2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStartingEvent.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.VoicechatSocket; +import de.maxhenkel.voicechat.api.events.ServerEvent; +import javax.annotation.Nullable; + +public interface VoicechatServerStartingEvent +extends ServerEvent { + public void setSocketImplementation(VoicechatSocket var1); + + @Nullable + public VoicechatSocket getSocketImplementation(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStoppedEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStoppedEvent.java new file mode 100644 index 0000000..79185ad --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/VoicechatServerStoppedEvent.java @@ -0,0 +1,11 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.events.ServerEvent; + +public interface VoicechatServerStoppedEvent +extends ServerEvent { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/events/VolumeCategoryEvent.java b/source/decompiled/de/maxhenkel/voicechat/api/events/VolumeCategoryEvent.java new file mode 100644 index 0000000..f56e44e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/events/VolumeCategoryEvent.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.events; + +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.events.ServerEvent; + +public interface VolumeCategoryEvent +extends ServerEvent { + public VolumeCategory getVolumeCategory(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/mp3/Mp3Decoder.java b/source/decompiled/de/maxhenkel/voicechat/api/mp3/Mp3Decoder.java new file mode 100644 index 0000000..4252b9a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/mp3/Mp3Decoder.java @@ -0,0 +1,16 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.mp3; + +import java.io.IOException; +import javax.sound.sampled.AudioFormat; + +public interface Mp3Decoder { + public short[] decode() throws IOException; + + public AudioFormat getAudioFormat() throws IOException; + + public int getBitrate() throws IOException; +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/mp3/Mp3Encoder.java b/source/decompiled/de/maxhenkel/voicechat/api/mp3/Mp3Encoder.java new file mode 100644 index 0000000..73e4ff1 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/mp3/Mp3Encoder.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.mp3; + +import java.io.IOException; + +public interface Mp3Encoder { + public void encode(short[] var1) throws IOException; + + public void close() throws IOException; +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/opus/OpusDecoder.java b/source/decompiled/de/maxhenkel/voicechat/api/opus/OpusDecoder.java new file mode 100644 index 0000000..b549cab --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/opus/OpusDecoder.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.opus; + +import javax.annotation.Nullable; + +public interface OpusDecoder { + public short[] decode(@Nullable byte[] var1); + + public short[][] decode(byte[] var1, int var2); + + public void resetState(); + + public boolean isClosed(); + + public void close(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/opus/OpusEncoder.java b/source/decompiled/de/maxhenkel/voicechat/api/opus/OpusEncoder.java new file mode 100644 index 0000000..41032d6 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/opus/OpusEncoder.java @@ -0,0 +1,15 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.opus; + +public interface OpusEncoder { + public byte[] encode(short[] var1); + + public void resetState(); + + public boolean isClosed(); + + public void close(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/opus/OpusEncoderMode.java b/source/decompiled/de/maxhenkel/voicechat/api/opus/OpusEncoderMode.java new file mode 100644 index 0000000..b324d2d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/opus/OpusEncoderMode.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.opus; + +public enum OpusEncoderMode { + VOIP, + AUDIO, + RESTRICTED_LOWDELAY; + +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/packets/ConvertablePacket.java b/source/decompiled/de/maxhenkel/voicechat/api/packets/ConvertablePacket.java new file mode 100644 index 0000000..b3a3c50 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/packets/ConvertablePacket.java @@ -0,0 +1,28 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.packets; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.packets.EntitySoundPacket; +import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket; +import de.maxhenkel.voicechat.api.packets.StaticSoundPacket; +import java.util.UUID; + +public interface ConvertablePacket { + public EntitySoundPacket.Builder entitySoundPacketBuilder(); + + public LocationalSoundPacket.Builder locationalSoundPacketBuilder(); + + public StaticSoundPacket.Builder staticSoundPacketBuilder(); + + @Deprecated + public EntitySoundPacket toEntitySoundPacket(UUID var1, boolean var2); + + @Deprecated + public LocationalSoundPacket toLocationalSoundPacket(Position var1); + + @Deprecated + public StaticSoundPacket toStaticSoundPacket(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/packets/EntitySoundPacket.java b/source/decompiled/de/maxhenkel/voicechat/api/packets/EntitySoundPacket.java new file mode 100644 index 0000000..1aad5df --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/packets/EntitySoundPacket.java @@ -0,0 +1,26 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.packets; + +import de.maxhenkel.voicechat.api.packets.SoundPacket; +import java.util.UUID; + +public interface EntitySoundPacket +extends SoundPacket { + public UUID getEntityUuid(); + + public boolean isWhispering(); + + public float getDistance(); + + public static interface Builder> + extends SoundPacket.Builder { + public T entityUuid(UUID var1); + + public T whispering(boolean var1); + + public T distance(float var1); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/packets/LocationalSoundPacket.java b/source/decompiled/de/maxhenkel/voicechat/api/packets/LocationalSoundPacket.java new file mode 100644 index 0000000..6be8ef2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/packets/LocationalSoundPacket.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.packets; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.packets.SoundPacket; + +public interface LocationalSoundPacket +extends SoundPacket { + public Position getPosition(); + + public float getDistance(); + + public static interface Builder> + extends SoundPacket.Builder { + public T position(Position var1); + + public T distance(float var1); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/packets/MicrophonePacket.java b/source/decompiled/de/maxhenkel/voicechat/api/packets/MicrophonePacket.java new file mode 100644 index 0000000..9d5b236 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/packets/MicrophonePacket.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.packets; + +import de.maxhenkel.voicechat.api.packets.ConvertablePacket; +import de.maxhenkel.voicechat.api.packets.Packet; + +public interface MicrophonePacket +extends Packet, +ConvertablePacket { + public boolean isWhispering(); + + public byte[] getOpusEncodedData(); + + public void setOpusEncodedData(byte[] var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/packets/Packet.java b/source/decompiled/de/maxhenkel/voicechat/api/packets/Packet.java new file mode 100644 index 0000000..3c586be --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/packets/Packet.java @@ -0,0 +1,8 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.packets; + +public interface Packet { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/packets/SoundPacket.java b/source/decompiled/de/maxhenkel/voicechat/api/packets/SoundPacket.java new file mode 100644 index 0000000..18f0771 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/packets/SoundPacket.java @@ -0,0 +1,42 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.api.packets; + +import de.maxhenkel.voicechat.api.packets.ConvertablePacket; +import de.maxhenkel.voicechat.api.packets.Packet; +import java.util.UUID; +import javax.annotation.Nullable; + +public interface SoundPacket +extends Packet, +ConvertablePacket { + public UUID getChannelId(); + + public UUID getSender(); + + public byte[] getOpusEncodedData(); + + public long getSequenceNumber(); + + @Nullable + public String getCategory(); + + public static interface Builder, P extends SoundPacket> { + public T channelId(UUID var1); + + public T sender(UUID var1); + + public T opusEncodedData(byte[] var1); + + public T sequenceNumber(long var1); + + public T category(@Nullable String var1); + + public P build(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/api/packets/StaticSoundPacket.java b/source/decompiled/de/maxhenkel/voicechat/api/packets/StaticSoundPacket.java new file mode 100644 index 0000000..3637202 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/api/packets/StaticSoundPacket.java @@ -0,0 +1,15 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.api.packets; + +import de.maxhenkel.voicechat.api.packets.SoundPacket; + +public interface StaticSoundPacket +extends SoundPacket { + + public static interface Builder> + extends SoundPacket.Builder { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/command/VoiceChatCommands.java b/source/decompiled/de/maxhenkel/voicechat/command/VoiceChatCommands.java new file mode 100644 index 0000000..6d74d14 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/command/VoiceChatCommands.java @@ -0,0 +1,308 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.command.Command + * org.bukkit.command.CommandExecutor + * org.bukkit.command.CommandSender + * org.bukkit.command.TabCompleter + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.command; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.NetManager; +import de.maxhenkel.voicechat.permission.PermissionManager; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import de.maxhenkel.voicechat.voice.server.ClientConnection; +import de.maxhenkel.voicechat.voice.server.Group; +import de.maxhenkel.voicechat.voice.server.PingManager; +import de.maxhenkel.voicechat.voice.server.Server; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; + +public class VoiceChatCommands +implements CommandExecutor, +TabCompleter { + public static final String VOICECHAT_COMMAND = "voicechat"; + + public boolean onCommand(CommandSender commandSender, Command command, String label, String[] args) { + if (VoiceChatCommands.checkNoVoicechat(commandSender)) { + return true; + } + if (!(commandSender instanceof Player)) { + return true; + } + Player sender = (Player)commandSender; + if (args.length >= 1) { + if (args[0].equalsIgnoreCase("help")) { + return this.helpCommand(sender, command, label, args); + } + if (args[0].equalsIgnoreCase("test")) { + if (commandSender.hasPermission(PermissionManager.ADMIN_PERMISSION)) { + return this.testCommand(sender, command, label, args); + } + return true; + } + if (args[0].equalsIgnoreCase("invite")) { + return this.inviteCommand(sender, command, label, args); + } + if (args[0].equalsIgnoreCase("join")) { + return this.joinCommand(sender, command, label, args); + } + if (args[0].equalsIgnoreCase("leave")) { + return this.leaveCommand(sender, command, label, args); + } + } + return this.helpCommand(sender, command, label, args); + } + + @Nullable + public List onTabComplete(CommandSender commandSender, Command command, String label, String[] args) { + String arg; + if (args.length == 1) { + return this.tabCompleteList(args[0], Arrays.asList("help", "test", "invite", "join", "leave")); + } + if (args.length == 2) { + arg = args[0]; + if (arg.equals("test") || arg.equals("invite")) { + return null; + } + if (arg.startsWith("join")) { + return Collections.emptyList(); + } + } + if (args.length == 3 && (arg = args[0]).startsWith("join")) { + return Collections.emptyList(); + } + return Collections.emptyList(); + } + + private List tabCompleteList(String arg, List list) { + return list.stream().filter(s -> s.startsWith(arg)).collect(Collectors.toList()); + } + + private boolean helpCommand(Player commandSender, Command command, String label, String[] args) { + commandSender.sendMessage("/voicechat [help]"); + commandSender.sendMessage("/voicechat [test] "); + commandSender.sendMessage("/voicechat [invite] "); + commandSender.sendMessage("/voicechat [join] []"); + commandSender.sendMessage("/voicechat [leave]"); + return true; + } + + private boolean testCommand(final Player commandSender, Command command, String label, String[] args) { + if (args.length < 2) { + return false; + } + Player player = VoiceChatCommands.parsePlayer((CommandSender)commandSender, args[1]); + if (player == null) { + NetManager.sendMessage(commandSender, "argument.entity.notfound.player", new String[0]); + return true; + } + if (!Voicechat.SERVER.isCompatible(player)) { + NetManager.sendMessage(commandSender, "message.voicechat.player_no_voicechat", player.getDisplayName(), "Simple Voice Chat"); + return true; + } + ClientConnection clientConnection = Voicechat.SERVER.getServer().getConnections().get(player.getUniqueId()); + if (clientConnection == null) { + NetManager.sendMessage(commandSender, "message.voicechat.client_not_connected", new String[0]); + return true; + } + try { + NetManager.sendMessage(commandSender, "message.voicechat.sending_ping", new String[0]); + Voicechat.SERVER.getServer().getPingManager().sendPing(clientConnection, 500L, 10, new PingManager.PingListener(){ + final /* synthetic */ VoiceChatCommands this$0; + { + this.this$0 = this$0; + } + + @Override + public void onPong(int attempts, long pingMilliseconds) { + if (attempts <= 1) { + NetManager.sendMessage(commandSender, "message.voicechat.ping_received", String.valueOf(pingMilliseconds)); + } else { + NetManager.sendMessage(commandSender, "message.voicechat.ping_received_attempt", String.valueOf(pingMilliseconds), String.valueOf(attempts)); + } + } + + @Override + public void onFailedAttempt(int attempts) { + NetManager.sendMessage(commandSender, "message.voicechat.ping_retry", new String[0]); + } + + @Override + public void onTimeout(int attempts) { + NetManager.sendMessage(commandSender, "message.voicechat.ping_timed_out", String.valueOf(attempts)); + } + }); + NetManager.sendMessage(commandSender, "message.voicechat.ping_sent_waiting", new String[0]); + } + catch (Exception e) { + NetManager.sendMessage(commandSender, "message.voicechat.failed_to_send_ping", e.getMessage()); + Voicechat.LOGGER.warn("Failed to send ping", e); + } + return true; + } + + private boolean inviteCommand(Player commandSender, Command command, String label, String[] args) { + if (args.length < 2) { + return false; + } + Player player = VoiceChatCommands.parsePlayer((CommandSender)commandSender, args[1]); + if (player == null || !Voicechat.compatibility.canSee(commandSender, player)) { + NetManager.sendMessage(commandSender, "argument.entity.notfound.player", new String[0]); + return true; + } + PlayerState state = Voicechat.SERVER.getServer().getPlayerStateManager().getState(commandSender.getUniqueId()); + if (state == null || !state.hasGroup()) { + NetManager.sendMessage(commandSender, "message.voicechat.not_in_group", new String[0]); + return true; + } + Group group = Voicechat.SERVER.getServer().getGroupManager().getGroup(state.getGroup()); + if (group == null) { + return true; + } + if (!Voicechat.SERVER.isCompatible(player)) { + NetManager.sendMessage(commandSender, "message.voicechat.player_no_voicechat", player.getDisplayName(), "Simple Voice Chat"); + return true; + } + String passwordSuffix = group.getPassword() == null ? "" : " \"" + group.getPassword() + "\""; + String cmd = "/voicechat join " + group.getId().toString() + passwordSuffix; + Voicechat.compatibility.sendInviteMessage(player, commandSender, group.getName(), cmd); + NetManager.sendMessage(commandSender, "message.voicechat.invite_successful", player.getName()); + return true; + } + + private boolean joinCommand(Player commandSender, Command command, String label, String[] args) { + UUID groupUUID; + int argIndex; + if (args.length < 2) { + return false; + } + try { + groupUUID = UUID.fromString(args[argIndex]); + } + catch (Exception e) { + String groupName; + if (args[argIndex].startsWith("\"")) { + String[] split; + StringBuilder sb = new StringBuilder(); + for (argIndex = 1; argIndex < args.length; ++argIndex) { + sb.append(args[argIndex]).append(" "); + if (args[argIndex].endsWith("\"") && !args[argIndex].endsWith("\\\"")) break; + } + if ((split = (groupName = sb.toString().trim()).split("\"")).length > 1) { + groupName = split[1]; + } + } else { + groupName = args[argIndex]; + } + groupUUID = this.getGroupUUID(commandSender, Voicechat.SERVER.getServer(), groupName); + } + if (groupUUID == null) { + return true; + } + String password = null; + if (args.length >= ++argIndex + 1) { + String[] split; + StringBuilder sb = new StringBuilder(); + while (argIndex < args.length) { + sb.append(args[argIndex]).append(" "); + ++argIndex; + } + password = sb.toString().trim(); + if (password.startsWith("\"") && (split = password.split("\"")).length > 1) { + password = split[1]; + } + } + VoiceChatCommands.joinGroup(commandSender, groupUUID, password); + return true; + } + + private UUID getGroupUUID(Player commandSender, Server server, String groupName) { + List groups = server.getGroupManager().getGroups().values().stream().filter(group -> group.getName().equals(groupName)).collect(Collectors.toList()); + if (groups.isEmpty()) { + NetManager.sendMessage(commandSender, "message.voicechat.group_does_not_exist", new String[0]); + return null; + } + if (groups.size() > 1) { + NetManager.sendMessage(commandSender, "message.voicechat.group_name_not_unique", new String[0]); + return null; + } + return ((Group)groups.get(0)).getId(); + } + + private static void joinGroup(Player commandSender, UUID groupID, @Nullable String password) { + if (!Voicechat.SERVER_CONFIG.groupsEnabled.get().booleanValue()) { + NetManager.sendMessage(commandSender, "message.voicechat.groups_disabled", new String[0]); + return; + } + Server server = Voicechat.SERVER.getServer(); + if (!commandSender.hasPermission(PermissionManager.GROUPS_PERMISSION)) { + NetManager.sendMessage(commandSender, "message.voicechat.no_group_permission", new String[0]); + return; + } + Group group = server.getGroupManager().getGroup(groupID); + if (group == null) { + NetManager.sendMessage(commandSender, "message.voicechat.group_does_not_exist", new String[0]); + return; + } + server.getGroupManager().joinGroup(group, commandSender, password); + NetManager.sendMessage(commandSender, "message.voicechat.join_successful", group.getName()); + } + + private boolean leaveCommand(Player commandSender, Command command, String label, String[] args) { + if (!Voicechat.SERVER_CONFIG.groupsEnabled.get().booleanValue()) { + NetManager.sendMessage(commandSender, "message.voicechat.groups_disabled", new String[0]); + return true; + } + Server server = Voicechat.SERVER.getServer(); + PlayerState state = server.getPlayerStateManager().getState(commandSender.getUniqueId()); + if (state == null || !state.hasGroup()) { + NetManager.sendMessage(commandSender, "message.voicechat.not_in_group", new String[0]); + return true; + } + server.getGroupManager().leaveGroup(commandSender); + NetManager.sendMessage(commandSender, "message.voicechat.leave_successful", new String[0]); + return true; + } + + private static boolean checkNoVoicechat(CommandSender commandSender) { + if (commandSender instanceof Player) { + if (Voicechat.SERVER.isCompatible((Player)commandSender)) { + return false; + } + commandSender.sendMessage(String.format(Voicechat.TRANSLATIONS.voicechatNeededForCommandMessage.get(), "Simple Voice Chat")); + } else { + commandSender.sendMessage(Voicechat.TRANSLATIONS.playerCommandMessage.get()); + } + return true; + } + + @Nullable + public static Player parsePlayer(CommandSender commandSender, String playerArg) { + Player player = commandSender.getServer().getPlayer(playerArg); + if (player != null) { + return player; + } + try { + UUID uuid = UUID.fromString(playerArg); + return commandSender.getServer().getPlayer(uuid); + } + catch (IllegalArgumentException e) { + return null; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/AbstractCommodore.java b/source/decompiled/de/maxhenkel/voicechat/commodore/AbstractCommodore.java new file mode 100644 index 0000000..af6f20c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/AbstractCommodore.java @@ -0,0 +1,120 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.Command + * com.mojang.brigadier.suggestion.SuggestionProvider + * com.mojang.brigadier.tree.ArgumentCommandNode + * com.mojang.brigadier.tree.CommandNode + * com.mojang.brigadier.tree.LiteralCommandNode + * com.mojang.brigadier.tree.RootCommandNode + * org.bukkit.command.Command + * org.bukkit.command.PluginCommand + */ +package de.maxhenkel.voicechat.commodore; + +import com.mojang.brigadier.Command; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.tree.ArgumentCommandNode; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; +import com.mojang.brigadier.tree.RootCommandNode; +import de.maxhenkel.voicechat.commodore.Commodore; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.bukkit.command.PluginCommand; + +abstract class AbstractCommodore +implements Commodore { + protected static final Field CUSTOM_SUGGESTIONS_FIELD; + protected static final Field COMMAND_EXECUTE_FUNCTION_FIELD; + protected static final Field CHILDREN_FIELD; + protected static final Field LITERALS_FIELD; + protected static final Field ARGUMENTS_FIELD; + protected static final Field[] CHILDREN_FIELDS; + protected static final Command DUMMY_COMMAND; + protected static final SuggestionProvider DUMMY_SUGGESTION_PROVIDER; + + AbstractCommodore() { + } + + protected static void removeChild(RootCommandNode root, String name) { + try { + for (Field field : CHILDREN_FIELDS) { + Map children = (Map)field.get(root); + children.remove(name); + } + } + catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + protected static void setRequiredHackyFieldsRecursively(CommandNode node, SuggestionProvider suggestionProvider) { + try { + COMMAND_EXECUTE_FUNCTION_FIELD.set(node, DUMMY_COMMAND); + } + catch (IllegalAccessException e) { + e.printStackTrace(); + } + if (suggestionProvider != null && node instanceof ArgumentCommandNode) { + ArgumentCommandNode argumentNode = (ArgumentCommandNode)node; + try { + CUSTOM_SUGGESTIONS_FIELD.set(argumentNode, suggestionProvider); + } + catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + for (CommandNode child : node.getChildren()) { + AbstractCommodore.setRequiredHackyFieldsRecursively(child, suggestionProvider); + } + } + + protected static LiteralCommandNode renameLiteralNode(LiteralCommandNode node, String newLiteral) { + LiteralCommandNode clone = new LiteralCommandNode(newLiteral, node.getCommand(), node.getRequirement(), node.getRedirect(), node.getRedirectModifier(), node.isFork()); + for (CommandNode child : node.getChildren()) { + clone.addChild(child); + } + return clone; + } + + protected static Collection getAliases(org.bukkit.command.Command command) { + Objects.requireNonNull(command, "command"); + Stream aliasesStream = Stream.concat(Stream.of(command.getLabel()), command.getAliases().stream()); + if (command instanceof PluginCommand) { + String fallbackPrefix = ((PluginCommand)command).getPlugin().getName().toLowerCase().trim(); + aliasesStream = aliasesStream.flatMap(alias -> Stream.of(alias, fallbackPrefix + ":" + alias)); + } + return aliasesStream.distinct().collect(Collectors.toList()); + } + + static { + try { + CUSTOM_SUGGESTIONS_FIELD = ArgumentCommandNode.class.getDeclaredField("customSuggestions"); + CUSTOM_SUGGESTIONS_FIELD.setAccessible(true); + COMMAND_EXECUTE_FUNCTION_FIELD = CommandNode.class.getDeclaredField("command"); + COMMAND_EXECUTE_FUNCTION_FIELD.setAccessible(true); + CHILDREN_FIELD = CommandNode.class.getDeclaredField("children"); + LITERALS_FIELD = CommandNode.class.getDeclaredField("literals"); + ARGUMENTS_FIELD = CommandNode.class.getDeclaredField("arguments"); + for (Field field : CHILDREN_FIELDS = new Field[]{CHILDREN_FIELD, LITERALS_FIELD, ARGUMENTS_FIELD}) { + field.setAccessible(true); + } + DUMMY_COMMAND = ctx -> { + throw new UnsupportedOperationException(); + }; + DUMMY_SUGGESTION_PROVIDER = (context, builder) -> { + throw new UnsupportedOperationException(); + }; + } + catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/BrigadierUnsupportedException.java b/source/decompiled/de/maxhenkel/voicechat/commodore/BrigadierUnsupportedException.java new file mode 100644 index 0000000..5076fa7 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/BrigadierUnsupportedException.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.commodore; + +public final class BrigadierUnsupportedException +extends UnsupportedOperationException { + BrigadierUnsupportedException(String message) { + super(message); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/Commodore.java b/source/decompiled/de/maxhenkel/voicechat/commodore/Commodore.java new file mode 100644 index 0000000..0ae4c27 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/Commodore.java @@ -0,0 +1,48 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.builder.LiteralArgumentBuilder + * com.mojang.brigadier.tree.LiteralCommandNode + * org.bukkit.command.Command + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.commodore; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.tree.LiteralCommandNode; +import java.util.Objects; +import java.util.function.Predicate; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; + +public interface Commodore { + public void register(Command var1, LiteralCommandNode var2, Predicate var3); + + default public void register(Command command, LiteralArgumentBuilder argumentBuilder, Predicate permissionTest) { + Objects.requireNonNull(command, "command"); + Objects.requireNonNull(argumentBuilder, "argumentBuilder"); + Objects.requireNonNull(permissionTest, "permissionTest"); + this.register(command, argumentBuilder.build(), permissionTest); + } + + default public void register(Command command, LiteralCommandNode node) { + Objects.requireNonNull(command, "command"); + Objects.requireNonNull(node, "node"); + this.register(command, node, arg_0 -> ((Command)command).testPermissionSilent(arg_0)); + } + + default public void register(Command command, LiteralArgumentBuilder argumentBuilder) { + Objects.requireNonNull(command, "command"); + Objects.requireNonNull(argumentBuilder, "argumentBuilder"); + this.register(command, argumentBuilder.build()); + } + + public void register(LiteralCommandNode var1); + + default public void register(LiteralArgumentBuilder argumentBuilder) { + Objects.requireNonNull(argumentBuilder, "argumentBuilder"); + this.register(argumentBuilder.build()); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/CommodoreProvider.java b/source/decompiled/de/maxhenkel/voicechat/commodore/CommodoreProvider.java new file mode 100644 index 0000000..87d0f0e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/CommodoreProvider.java @@ -0,0 +1,68 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.plugin.Plugin + */ +package de.maxhenkel.voicechat.commodore; + +import de.maxhenkel.voicechat.commodore.BrigadierUnsupportedException; +import de.maxhenkel.voicechat.commodore.Commodore; +import de.maxhenkel.voicechat.commodore.PaperCommodore; +import de.maxhenkel.voicechat.commodore.ReflectionCommodore; +import java.util.Objects; +import java.util.function.Function; +import org.bukkit.plugin.Plugin; + +public final class CommodoreProvider { + private static final Function PROVIDER = CommodoreProvider.checkSupported(); + + private CommodoreProvider() { + throw new AssertionError(); + } + + private static Function checkSupported() { + try { + Class.forName("com.mojang.brigadier.CommandDispatcher"); + } + catch (Throwable e) { + CommodoreProvider.printDebugInfo(e); + return null; + } + try { + PaperCommodore.ensureSetup(); + return PaperCommodore::new; + } + catch (Throwable e) { + CommodoreProvider.printDebugInfo(e); + try { + ReflectionCommodore.ensureSetup(); + return ReflectionCommodore::new; + } + catch (Throwable e2) { + CommodoreProvider.printDebugInfo(e2); + return null; + } + } + } + + private static void printDebugInfo(Throwable e) { + if (System.getProperty("commodore.debug") != null) { + System.err.println("Exception while initialising commodore:"); + e.printStackTrace(System.err); + } + } + + public static boolean isSupported() { + return PROVIDER != null; + } + + public static Commodore getCommodore(Plugin plugin) throws BrigadierUnsupportedException { + Objects.requireNonNull(plugin, "plugin"); + if (PROVIDER == null) { + throw new BrigadierUnsupportedException("Brigadier is not supported by the server. Set -Dcommodore.debug=true for debug info."); + } + return PROVIDER.apply(plugin); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/PaperCommodore.java b/source/decompiled/de/maxhenkel/voicechat/commodore/PaperCommodore.java new file mode 100644 index 0000000..be7b331 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/PaperCommodore.java @@ -0,0 +1,114 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent + * com.mojang.brigadier.builder.LiteralArgumentBuilder + * com.mojang.brigadier.tree.LiteralCommandNode + * com.mojang.brigadier.tree.RootCommandNode + * org.bukkit.command.Command + * org.bukkit.entity.Player + * org.bukkit.event.EventHandler + * org.bukkit.event.Listener + * org.bukkit.plugin.Plugin + */ +package de.maxhenkel.voicechat.commodore; + +import com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.tree.LiteralCommandNode; +import com.mojang.brigadier.tree.RootCommandNode; +import de.maxhenkel.voicechat.commodore.AbstractCommodore; +import de.maxhenkel.voicechat.commodore.Commodore; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.function.Predicate; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; + +final class PaperCommodore +extends AbstractCommodore +implements Commodore, +Listener { + private final List commands = new ArrayList(); + + PaperCommodore(Plugin plugin) { + plugin.getServer().getPluginManager().registerEvents((Listener)this, plugin); + } + + @Override + public void register(LiteralCommandNode node) { + Objects.requireNonNull(node, "node"); + this.commands.add(new CommodoreCommand(node, null)); + } + + @Override + public void register(Command command, LiteralCommandNode node, Predicate permissionTest) { + Objects.requireNonNull(command, "command"); + Objects.requireNonNull(node, "node"); + Objects.requireNonNull(permissionTest, "permissionTest"); + try { + PaperCommodore.setRequiredHackyFieldsRecursively(node, DUMMY_SUGGESTION_PROVIDER); + } + catch (Throwable e) { + e.printStackTrace(); + } + Collection aliases = PaperCommodore.getAliases(command); + if (!aliases.contains(node.getLiteral())) { + node = PaperCommodore.renameLiteralNode(node, command.getName()); + } + for (String alias : aliases) { + if (node.getLiteral().equals(alias)) { + this.commands.add(new CommodoreCommand(node, permissionTest)); + continue; + } + LiteralCommandNode redirectNode = ((LiteralArgumentBuilder)LiteralArgumentBuilder.literal((String)alias).redirect(node)).build(); + this.commands.add(new CommodoreCommand(redirectNode, permissionTest)); + } + } + + @EventHandler + public void onPlayerSendCommandsEvent(AsyncPlayerSendCommandsEvent event) { + if (event.isAsynchronous() || !event.hasFiredAsync()) { + for (CommodoreCommand command : this.commands) { + command.apply(event.getPlayer(), event.getCommandNode()); + } + } + } + + static void ensureSetup() { + } + + static { + try { + Class.forName("com.destroystokyo.paper.event.brigadier.AsyncPlayerSendCommandsEvent"); + } + catch (ClassNotFoundException e) { + throw new UnsupportedOperationException("Not running on modern Paper!", e); + } + } + + private static final class CommodoreCommand { + private final LiteralCommandNode node; + private final Predicate permissionTest; + + private CommodoreCommand(LiteralCommandNode node, Predicate permissionTest) { + this.node = node; + this.permissionTest = permissionTest; + } + + public void apply(Player player, RootCommandNode root) { + if (this.permissionTest != null && !this.permissionTest.test((Player)player)) { + return; + } + AbstractCommodore.removeChild(root, this.node.getName()); + root.addChild(this.node); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/ReflectionCommodore.java b/source/decompiled/de/maxhenkel/voicechat/commodore/ReflectionCommodore.java new file mode 100644 index 0000000..9d7bba5 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/ReflectionCommodore.java @@ -0,0 +1,186 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.CommandDispatcher + * com.mojang.brigadier.builder.LiteralArgumentBuilder + * com.mojang.brigadier.suggestion.SuggestionProvider + * com.mojang.brigadier.tree.CommandNode + * com.mojang.brigadier.tree.LiteralCommandNode + * com.mojang.brigadier.tree.RootCommandNode + * org.bukkit.Bukkit + * org.bukkit.command.Command + * org.bukkit.entity.Player + * org.bukkit.event.EventHandler + * org.bukkit.event.Listener + * org.bukkit.event.player.PlayerCommandSendEvent + * org.bukkit.event.server.ServerLoadEvent + * org.bukkit.plugin.Plugin + */ +package de.maxhenkel.voicechat.commodore; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; +import com.mojang.brigadier.tree.RootCommandNode; +import de.maxhenkel.voicechat.commodore.AbstractCommodore; +import de.maxhenkel.voicechat.commodore.Commodore; +import de.maxhenkel.voicechat.commodore.ReflectionUtil; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerCommandSendEvent; +import org.bukkit.event.server.ServerLoadEvent; +import org.bukkit.plugin.Plugin; + +final class ReflectionCommodore +extends AbstractCommodore +implements Commodore { + private static final Field CONSOLE_FIELD; + private static final Method GET_COMMAND_DISPATCHER_METHOD; + private static final Method GET_BRIGADIER_DISPATCHER_METHOD; + private static final Constructor COMMAND_WRAPPER_CONSTRUCTOR; + private final Plugin plugin; + private final List> registeredNodes = new ArrayList(); + + ReflectionCommodore(Plugin plugin) { + this.plugin = plugin; + this.plugin.getServer().getPluginManager().registerEvents((Listener)new ServerReloadListener(this), this.plugin); + } + + private CommandDispatcher getDispatcher() { + try { + Object mcServerObject = CONSOLE_FIELD.get(Bukkit.getServer()); + Object commandDispatcherObject = GET_COMMAND_DISPATCHER_METHOD.invoke(mcServerObject, new Object[0]); + return (CommandDispatcher)GET_BRIGADIER_DISPATCHER_METHOD.invoke(commandDispatcherObject, new Object[0]); + } + catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public void register(LiteralCommandNode node) { + Objects.requireNonNull(node, "node"); + CommandDispatcher dispatcher = this.getDispatcher(); + RootCommandNode root = dispatcher.getRoot(); + ReflectionCommodore.removeChild(root, node.getName()); + root.addChild(node); + this.registeredNodes.add(node); + } + + @Override + public void register(Command command, LiteralCommandNode node, Predicate permissionTest) { + Objects.requireNonNull(command, "command"); + Objects.requireNonNull(node, "node"); + Objects.requireNonNull(permissionTest, "permissionTest"); + try { + SuggestionProvider wrapper = (SuggestionProvider)COMMAND_WRAPPER_CONSTRUCTOR.newInstance(this.plugin.getServer(), command); + ReflectionCommodore.setRequiredHackyFieldsRecursively(node, wrapper); + } + catch (Throwable e) { + e.printStackTrace(); + } + Collection aliases = ReflectionCommodore.getAliases(command); + if (!aliases.contains(node.getLiteral())) { + node = ReflectionCommodore.renameLiteralNode(node, command.getName()); + } + for (String alias : aliases) { + if (node.getLiteral().equals(alias)) { + this.register(node); + continue; + } + this.register(((LiteralArgumentBuilder)LiteralArgumentBuilder.literal((String)alias).redirect(node)).build()); + } + this.plugin.getServer().getPluginManager().registerEvents((Listener)new CommandDataSendListener(command, permissionTest), this.plugin); + } + + static void ensureSetup() { + } + + static { + try { + Class commandDispatcher; + Class minecraftServer; + if (ReflectionUtil.minecraftVersion() >= 19) { + throw new UnsupportedOperationException("ReflectionCommodore is not supported on MC 1.19 or above. Switch to Paper :)"); + } + if (ReflectionUtil.minecraftVersion() > 16) { + minecraftServer = ReflectionUtil.mcClass("server.MinecraftServer"); + commandDispatcher = ReflectionUtil.mcClass("commands.CommandDispatcher"); + } else { + minecraftServer = ReflectionUtil.nmsClass("MinecraftServer"); + commandDispatcher = ReflectionUtil.nmsClass("CommandDispatcher"); + } + Class craftServer = ReflectionUtil.obcClass("CraftServer"); + CONSOLE_FIELD = craftServer.getDeclaredField("console"); + CONSOLE_FIELD.setAccessible(true); + GET_COMMAND_DISPATCHER_METHOD = Arrays.stream(minecraftServer.getDeclaredMethods()).filter(method -> method.getParameterCount() == 0).filter(method -> commandDispatcher.isAssignableFrom(method.getReturnType())).findFirst().orElseThrow(NoSuchMethodException::new); + GET_COMMAND_DISPATCHER_METHOD.setAccessible(true); + GET_BRIGADIER_DISPATCHER_METHOD = Arrays.stream(commandDispatcher.getDeclaredMethods()).filter(method -> method.getParameterCount() == 0).filter(method -> CommandDispatcher.class.isAssignableFrom(method.getReturnType())).findFirst().orElseThrow(NoSuchMethodException::new); + GET_BRIGADIER_DISPATCHER_METHOD.setAccessible(true); + Class commandWrapperClass = ReflectionUtil.obcClass("command.BukkitCommandWrapper"); + COMMAND_WRAPPER_CONSTRUCTOR = commandWrapperClass.getConstructor(craftServer, Command.class); + } + catch (ReflectiveOperationException e) { + throw new ExceptionInInitializerError(e); + } + } + + private static final class ServerReloadListener + implements Listener { + private final ReflectionCommodore commodore; + + private ServerReloadListener(ReflectionCommodore commodore) { + this.commodore = commodore; + } + + @EventHandler + public void onLoad(ServerLoadEvent e) { + CommandDispatcher dispatcher = this.commodore.getDispatcher(); + RootCommandNode root = dispatcher.getRoot(); + for (LiteralCommandNode node : this.commodore.registeredNodes) { + AbstractCommodore.removeChild(root, node.getName()); + root.addChild((CommandNode)node); + } + } + } + + private static final class CommandDataSendListener + implements Listener { + private final Set aliases; + private final Set minecraftPrefixedAliases; + private final Predicate permissionTest; + + CommandDataSendListener(Command pluginCommand, Predicate permissionTest) { + this.aliases = new HashSet(AbstractCommodore.getAliases(pluginCommand)); + this.minecraftPrefixedAliases = this.aliases.stream().map(alias -> "minecraft:" + alias).collect(Collectors.toSet()); + this.permissionTest = permissionTest; + } + + @EventHandler + public void onCommandSend(PlayerCommandSendEvent e) { + e.getCommands().removeAll(this.minecraftPrefixedAliases); + if (!this.permissionTest.test((Player)e.getPlayer())) { + e.getCommands().removeAll(this.aliases); + } + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/ReflectionUtil.java b/source/decompiled/de/maxhenkel/voicechat/commodore/ReflectionUtil.java new file mode 100644 index 0000000..21469ce --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/ReflectionUtil.java @@ -0,0 +1,68 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.Bukkit + */ +package de.maxhenkel.voicechat.commodore; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.bukkit.Bukkit; + +final class ReflectionUtil { + private static final String SERVER_VERSION = ReflectionUtil.getServerVersion(); + + private static String getServerVersion() { + Class server = Bukkit.getServer().getClass(); + if (!server.getSimpleName().equals("CraftServer")) { + return "."; + } + if (server.getName().equals("org.bukkit.craftbukkit.CraftServer")) { + return "."; + } + String version = server.getName().substring("org.bukkit.craftbukkit".length()); + return version.substring(0, version.length() - "CraftServer".length()); + } + + public static String mc(String name) { + return "net.minecraft." + name; + } + + public static String nms(String className) { + return "net.minecraft.server" + SERVER_VERSION + className; + } + + public static Class mcClass(String className) throws ClassNotFoundException { + return Class.forName(ReflectionUtil.mc(className)); + } + + public static Class nmsClass(String className) throws ClassNotFoundException { + return Class.forName(ReflectionUtil.nms(className)); + } + + public static String obc(String className) { + return "org.bukkit.craftbukkit" + SERVER_VERSION + className; + } + + public static Class obcClass(String className) throws ClassNotFoundException { + return Class.forName(ReflectionUtil.obc(className)); + } + + public static int minecraftVersion() { + try { + Matcher matcher = Pattern.compile("\\(MC: (\\d)\\.(\\d+)\\.?(\\d+?)?( .*)?\\)").matcher(Bukkit.getVersion()); + if (matcher.find()) { + return Integer.parseInt(matcher.toMatchResult().group(2), 10); + } + throw new IllegalArgumentException(String.format("No match found in '%s'", Bukkit.getVersion())); + } + catch (IllegalArgumentException ex) { + throw new RuntimeException("Failed to determine Minecraft version", ex); + } + } + + private ReflectionUtil() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/file/AbstractIterator.java b/source/decompiled/de/maxhenkel/voicechat/commodore/file/AbstractIterator.java new file mode 100644 index 0000000..7143455 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/file/AbstractIterator.java @@ -0,0 +1,81 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.commodore.file; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +abstract class AbstractIterator +implements Iterator { + private State state = State.NOT_READY; + private T next; + + protected AbstractIterator() { + } + + protected abstract T computeNext() throws Exception; + + protected final T endOfData() { + this.state = State.DONE; + return null; + } + + @Override + public final boolean hasNext() { + if (this.state == State.FAILED) { + throw new IllegalStateException(); + } + switch (this.state) { + case DONE: { + return false; + } + case READY: { + return true; + } + } + return this.tryToComputeNext(); + } + + private boolean tryToComputeNext() { + this.state = State.FAILED; + try { + this.next = this.computeNext(); + } + catch (Exception e) { + throw new RuntimeException("Exception whilst computing next value", e); + } + if (this.state != State.DONE) { + this.state = State.READY; + return true; + } + return false; + } + + @Override + public final T next() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } + this.state = State.NOT_READY; + T result = this.next; + this.next = null; + return result; + } + + public final T peek() { + if (!this.hasNext()) { + throw new NoSuchElementException(); + } + return this.next; + } + + private static enum State { + READY, + NOT_READY, + DONE, + FAILED; + + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/file/ArgumentTypeParser.java b/source/decompiled/de/maxhenkel/voicechat/commodore/file/ArgumentTypeParser.java new file mode 100644 index 0000000..3a592f7 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/file/ArgumentTypeParser.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + */ +package de.maxhenkel.voicechat.commodore.file; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.commodore.file.ParseException; +import de.maxhenkel.voicechat.commodore.file.TokenStream; + +public interface ArgumentTypeParser { + public boolean canParse(String var1, String var2); + + public ArgumentType parse(String var1, String var2, TokenStream var3) throws ParseException; +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/file/BrigadierArgumentTypeParser.java b/source/decompiled/de/maxhenkel/voicechat/commodore/file/BrigadierArgumentTypeParser.java new file mode 100644 index 0000000..558f72b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/file/BrigadierArgumentTypeParser.java @@ -0,0 +1,225 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * com.mojang.brigadier.arguments.BoolArgumentType + * com.mojang.brigadier.arguments.DoubleArgumentType + * com.mojang.brigadier.arguments.FloatArgumentType + * com.mojang.brigadier.arguments.IntegerArgumentType + * com.mojang.brigadier.arguments.LongArgumentType + * com.mojang.brigadier.arguments.StringArgumentType + */ +package de.maxhenkel.voicechat.commodore.file; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.arguments.DoubleArgumentType; +import com.mojang.brigadier.arguments.FloatArgumentType; +import com.mojang.brigadier.arguments.IntegerArgumentType; +import com.mojang.brigadier.arguments.LongArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import de.maxhenkel.voicechat.commodore.file.ArgumentTypeParser; +import de.maxhenkel.voicechat.commodore.file.ParseException; +import de.maxhenkel.voicechat.commodore.file.Token; +import de.maxhenkel.voicechat.commodore.file.TokenStream; + +public class BrigadierArgumentTypeParser +implements ArgumentTypeParser { + public static final BrigadierArgumentTypeParser INSTANCE = new BrigadierArgumentTypeParser(); + + private BrigadierArgumentTypeParser() { + } + + @Override + public boolean canParse(String namespace, String name) { + if (!namespace.equals("brigadier")) { + return false; + } + switch (name) { + case "bool": + case "string": + case "integer": + case "long": + case "float": + case "double": { + return true; + } + } + return false; + } + + @Override + public ArgumentType parse(String namespace, String name, TokenStream tokens) throws ParseException { + switch (name) { + case "bool": { + return BoolArgumentType.bool(); + } + case "string": { + return BrigadierArgumentTypeParser.parseStringArgumentType(tokens); + } + case "integer": { + return BrigadierArgumentTypeParser.parseIntegerArgumentType(tokens); + } + case "long": { + return BrigadierArgumentTypeParser.parseLongArgumentType(tokens); + } + case "float": { + return BrigadierArgumentTypeParser.parseFloatArgumentType(tokens); + } + case "double": { + return BrigadierArgumentTypeParser.parseDoubleArgumentType(tokens); + } + } + throw new AssertionError(); + } + + private static StringArgumentType parseStringArgumentType(TokenStream tokens) throws ParseException { + String stringType; + Token token = tokens.next(); + if (!(token instanceof Token.StringToken)) { + throw tokens.createException("Expected string token for string type but got " + token); + } + switch (stringType = ((Token.StringToken)token).getString()) { + case "single_word": { + return StringArgumentType.word(); + } + case "quotable_phrase": { + return StringArgumentType.string(); + } + case "greedy_phrase": { + return StringArgumentType.greedyString(); + } + } + throw tokens.createException("Unknown string type: " + stringType); + } + + private static IntegerArgumentType parseIntegerArgumentType(TokenStream tokens) throws ParseException { + if (tokens.peek() instanceof Token.StringToken) { + int min = BrigadierArgumentTypeParser.parseInt(tokens); + if (tokens.peek() instanceof Token.StringToken) { + int max = BrigadierArgumentTypeParser.parseInt(tokens); + return IntegerArgumentType.integer((int)min, (int)max); + } + return IntegerArgumentType.integer((int)min); + } + return IntegerArgumentType.integer(); + } + + private static LongArgumentType parseLongArgumentType(TokenStream tokens) throws ParseException { + if (tokens.peek() instanceof Token.StringToken) { + long min = BrigadierArgumentTypeParser.parseLong(tokens); + if (tokens.peek() instanceof Token.StringToken) { + long max = BrigadierArgumentTypeParser.parseLong(tokens); + return LongArgumentType.longArg((long)min, (long)max); + } + return LongArgumentType.longArg((long)min); + } + return LongArgumentType.longArg(); + } + + private static FloatArgumentType parseFloatArgumentType(TokenStream tokens) throws ParseException { + if (tokens.peek() instanceof Token.StringToken) { + float min = BrigadierArgumentTypeParser.parseFloat(tokens); + if (tokens.peek() instanceof Token.StringToken) { + float max = BrigadierArgumentTypeParser.parseFloat(tokens); + return FloatArgumentType.floatArg((float)min, (float)max); + } + return FloatArgumentType.floatArg((float)min); + } + return FloatArgumentType.floatArg(); + } + + private static DoubleArgumentType parseDoubleArgumentType(TokenStream tokens) throws ParseException { + if (tokens.peek() instanceof Token.StringToken) { + double min = BrigadierArgumentTypeParser.parseDouble(tokens); + if (tokens.peek() instanceof Token.StringToken) { + double max = BrigadierArgumentTypeParser.parseDouble(tokens); + return DoubleArgumentType.doubleArg((double)min, (double)max); + } + return DoubleArgumentType.doubleArg((double)min); + } + return DoubleArgumentType.doubleArg(); + } + + private static int parseInt(TokenStream tokens) throws ParseException { + Token token = tokens.next(); + if (!(token instanceof Token.StringToken)) { + throw tokens.createException("Expected string token for integer but got " + token); + } + String value = ((Token.StringToken)token).getString(); + if (value.equals("min")) { + return Integer.MIN_VALUE; + } + if (value.equals("max")) { + return Integer.MAX_VALUE; + } + try { + return Integer.parseInt(value); + } + catch (NumberFormatException e) { + throw tokens.createException("Expected int but got " + value, e); + } + } + + private static long parseLong(TokenStream tokens) throws ParseException { + Token token = tokens.next(); + if (!(token instanceof Token.StringToken)) { + throw tokens.createException("Expected string token for long but got " + token); + } + String value = ((Token.StringToken)token).getString(); + if (value.equals("min")) { + return Long.MIN_VALUE; + } + if (value.equals("max")) { + return Long.MAX_VALUE; + } + try { + return Long.parseLong(value); + } + catch (NumberFormatException e) { + throw tokens.createException("Expected long but got " + value, e); + } + } + + private static float parseFloat(TokenStream tokens) throws ParseException { + Token token = tokens.next(); + if (!(token instanceof Token.StringToken)) { + throw tokens.createException("Expected string token for float but got " + token); + } + String value = ((Token.StringToken)token).getString(); + if (value.equals("min")) { + return Float.MIN_VALUE; + } + if (value.equals("max")) { + return Float.MAX_VALUE; + } + try { + return Float.parseFloat(value); + } + catch (NumberFormatException e) { + throw tokens.createException("Expected float but got " + value, e); + } + } + + private static double parseDouble(TokenStream tokens) throws ParseException { + Token token = tokens.next(); + if (!(token instanceof Token.StringToken)) { + throw tokens.createException("Expected string token for double but got " + token); + } + String value = ((Token.StringToken)token).getString(); + if (value.equals("min")) { + return Double.MIN_VALUE; + } + if (value.equals("max")) { + return Double.MAX_VALUE; + } + try { + return Double.parseDouble(value); + } + catch (NumberFormatException e) { + throw tokens.createException("Expected double but got " + value); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/file/CommodoreFileReader.java b/source/decompiled/de/maxhenkel/voicechat/commodore/file/CommodoreFileReader.java new file mode 100644 index 0000000..8ded418 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/file/CommodoreFileReader.java @@ -0,0 +1,88 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.tree.LiteralCommandNode + */ +package de.maxhenkel.voicechat.commodore.file; + +import com.mojang.brigadier.tree.LiteralCommandNode; +import de.maxhenkel.voicechat.commodore.file.ArgumentTypeParser; +import de.maxhenkel.voicechat.commodore.file.BrigadierArgumentTypeParser; +import de.maxhenkel.voicechat.commodore.file.Lexer; +import de.maxhenkel.voicechat.commodore.file.ParseException; +import de.maxhenkel.voicechat.commodore.file.Parser; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class CommodoreFileReader { + public static final CommodoreFileReader INSTANCE = CommodoreFileReader.builder().withArgumentTypeParser(BrigadierArgumentTypeParser.INSTANCE).build(); + private final List argumentTypeParsers; + + public static Builder builder() { + return new Builder(); + } + + CommodoreFileReader(List argumentTypeParsers) { + this.argumentTypeParsers = Collections.unmodifiableList(argumentTypeParsers); + } + + public LiteralCommandNode parse(Reader reader) throws IOException { + try { + return new Parser(new Lexer(reader), this.argumentTypeParsers).parse(); + } + catch (ParseException e) { + if (e.getCause() instanceof IOException) { + throw (IOException)e.getCause(); + } + throw new IOException(e); + } + } + + public LiteralCommandNode parse(InputStream inputStream) throws IOException { + try (InputStreamReader reader = new InputStreamReader(inputStream);){ + LiteralCommandNode literalCommandNode = this.parse(reader); + return literalCommandNode; + } + } + + public LiteralCommandNode parse(Path path) throws IOException { + try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8);){ + LiteralCommandNode literalCommandNode = this.parse(reader); + return literalCommandNode; + } + } + + public LiteralCommandNode parse(File file) throws IOException { + return this.parse(file.toPath()); + } + + public static final class Builder { + private final List argumentTypeParsers = new ArrayList(); + + Builder() { + } + + public Builder withArgumentTypeParser(ArgumentTypeParser argumentTypeParser) { + Objects.requireNonNull(argumentTypeParser, "argumentTypeParser"); + this.argumentTypeParsers.add(argumentTypeParser); + return this; + } + + public CommodoreFileReader build() { + return new CommodoreFileReader(new ArrayList(this.argumentTypeParsers)); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/file/Lexer.java b/source/decompiled/de/maxhenkel/voicechat/commodore/file/Lexer.java new file mode 100644 index 0000000..fbce218 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/file/Lexer.java @@ -0,0 +1,78 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.commodore.file; + +import de.maxhenkel.voicechat.commodore.file.AbstractIterator; +import de.maxhenkel.voicechat.commodore.file.ParseException; +import de.maxhenkel.voicechat.commodore.file.Token; +import de.maxhenkel.voicechat.commodore.file.TokenStream; +import java.io.IOException; +import java.io.Reader; +import java.io.StreamTokenizer; + +class Lexer +extends AbstractIterator +implements TokenStream { + private final StreamTokenizer tokenizer; + private boolean end = false; + + Lexer(Reader reader) { + this.tokenizer = new StreamTokenizer(reader); + this.tokenizer.resetSyntax(); + this.tokenizer.wordChars(33, 126); + this.tokenizer.quoteChar(34); + this.tokenizer.whitespaceChars(0, 32); + "{};".chars().forEach(this.tokenizer::ordinaryChar); + this.tokenizer.slashSlashComments(true); + this.tokenizer.slashStarComments(true); + } + + @Override + protected Token computeNext() throws ParseException { + if (this.end) { + return (Token)this.endOfData(); + } + try { + int token = this.tokenizer.nextToken(); + switch (token) { + case -1: { + this.end = true; + return Token.ConstantToken.EOF; + } + case -3: { + return new Token.StringToken(this.tokenizer.sval); + } + case 123: { + return Token.ConstantToken.OPEN_BRACKET; + } + case 125: { + return Token.ConstantToken.CLOSE_BRACKET; + } + case 59: { + return Token.ConstantToken.SEMICOLON; + } + } + throw this.createException("Unknown token: " + (char)token + "(" + token + ")"); + } + catch (IOException e) { + throw this.createException(e); + } + } + + @Override + public ParseException createException(String message) { + return new ParseException(message, this.tokenizer.lineno()); + } + + @Override + public ParseException createException(Throwable cause) { + return new ParseException(cause, this.tokenizer.lineno()); + } + + @Override + public ParseException createException(String message, Throwable cause) { + return new ParseException(message, cause, this.tokenizer.lineno()); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/file/ParseException.java b/source/decompiled/de/maxhenkel/voicechat/commodore/file/ParseException.java new file mode 100644 index 0000000..d8fa1c4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/file/ParseException.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.commodore.file; + +public final class ParseException +extends Exception { + ParseException(String message, int currentLine) { + super(message + " (at line " + currentLine + ")"); + } + + ParseException(Throwable cause, int currentLine) { + super("At line " + currentLine, cause); + } + + ParseException(String message, Throwable cause, int currentLine) { + super(message + " (at line " + currentLine + ")", cause); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/file/Parser.java b/source/decompiled/de/maxhenkel/voicechat/commodore/file/Parser.java new file mode 100644 index 0000000..099efeb --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/file/Parser.java @@ -0,0 +1,85 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * com.mojang.brigadier.builder.LiteralArgumentBuilder + * com.mojang.brigadier.builder.RequiredArgumentBuilder + * com.mojang.brigadier.tree.CommandNode + * com.mojang.brigadier.tree.LiteralCommandNode + */ +package de.maxhenkel.voicechat.commodore.file; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; +import de.maxhenkel.voicechat.commodore.file.ArgumentTypeParser; +import de.maxhenkel.voicechat.commodore.file.Lexer; +import de.maxhenkel.voicechat.commodore.file.ParseException; +import de.maxhenkel.voicechat.commodore.file.Token; +import java.util.Arrays; +import java.util.Collection; + +class Parser { + private final Lexer lexer; + private final Collection argumentTypeParsers; + + Parser(Lexer lexer, Collection argumentTypeParsers) { + this.lexer = lexer; + this.argumentTypeParsers = argumentTypeParsers; + } + + LiteralCommandNode parse() throws ParseException { + CommandNode node = this.parseNode(); + if (!(node instanceof LiteralCommandNode)) { + throw this.lexer.createException("Root command node is not a literal command node"); + } + if (this.lexer.peek() != Token.ConstantToken.EOF) { + throw this.lexer.createException("Expected end of file but got " + this.lexer.peek()); + } + return (LiteralCommandNode)node; + } + + private CommandNode parseNode() throws ParseException { + Token token = (Token)this.lexer.next(); + if (!(token instanceof Token.StringToken)) { + throw this.lexer.createException("Expected string token for node name but got " + token); + } + String name = ((Token.StringToken)token).getString(); + Object node = this.lexer.peek() instanceof Token.StringToken ? RequiredArgumentBuilder.argument((String)name, this.parseArgumentType()) : LiteralArgumentBuilder.literal((String)name); + if (this.lexer.peek() == Token.ConstantToken.OPEN_BRACKET) { + this.lexer.next(); + while (this.lexer.peek() != Token.ConstantToken.CLOSE_BRACKET) { + CommandNode child = this.parseNode(); + node.then(child); + } + this.lexer.next(); + } else { + if (this.lexer.peek() != Token.ConstantToken.SEMICOLON) { + throw this.lexer.createException("Node definition not ended with semicolon, got " + this.lexer.peek()); + } + this.lexer.next(); + } + return node.build(); + } + + private ArgumentType parseArgumentType() throws ParseException { + Token token = (Token)this.lexer.next(); + if (!(token instanceof Token.StringToken)) { + throw this.lexer.createException("Expected string token for argument type but got " + token); + } + String argumentType = ((Token.StringToken)token).getString(); + Object[] key = argumentType.split(":"); + if (key.length != 2) { + throw this.lexer.createException("Invalid key for argument type: " + Arrays.toString(key)); + } + for (ArgumentTypeParser parser : this.argumentTypeParsers) { + if (!parser.canParse((String)key[0], (String)key[1])) continue; + return parser.parse((String)key[0], (String)key[1], this.lexer); + } + throw this.lexer.createException("Unable to parse argument type: " + argumentType); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/file/Token.java b/source/decompiled/de/maxhenkel/voicechat/commodore/file/Token.java new file mode 100644 index 0000000..089158f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/file/Token.java @@ -0,0 +1,30 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.commodore.file; + +public interface Token { + + public static final class StringToken + implements Token { + private final String string; + + StringToken(String string) { + this.string = string; + } + + public String getString() { + return this.string; + } + } + + public static enum ConstantToken implements Token + { + OPEN_BRACKET, + CLOSE_BRACKET, + SEMICOLON, + EOF; + + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/commodore/file/TokenStream.java b/source/decompiled/de/maxhenkel/voicechat/commodore/file/TokenStream.java new file mode 100644 index 0000000..73b0c38 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/commodore/file/TokenStream.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.commodore.file; + +import de.maxhenkel.voicechat.commodore.file.ParseException; +import de.maxhenkel.voicechat.commodore.file.Token; + +public interface TokenStream { + public boolean hasNext(); + + public Token next(); + + public Token peek(); + + public ParseException createException(String var1); + + public ParseException createException(Throwable var1); + + public ParseException createException(String var1, Throwable var2); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/BaseCompatibility.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/BaseCompatibility.java new file mode 100644 index 0000000..bcf683a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/BaseCompatibility.java @@ -0,0 +1,211 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.Bukkit + * org.bukkit.entity.Player + * org.bukkit.event.EventPriority + * org.bukkit.event.Listener + * org.bukkit.event.player.PlayerEvent + * org.bukkit.plugin.Plugin + */ +package de.maxhenkel.voicechat.compatibility; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.compatibility.Compatibility; +import de.maxhenkel.voicechat.compatibility.CompatibilityReflectionException; +import de.maxhenkel.voicechat.compatibility.PlayerHideEvent; +import de.maxhenkel.voicechat.compatibility.PlayerShowEvent; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import de.maxhenkel.voicechat.util.Key; +import java.lang.reflect.Method; +import java.util.function.Consumer; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerEvent; +import org.bukkit.plugin.Plugin; + +public abstract class BaseCompatibility +implements Compatibility { + private String baseBukkitPackage; + private String baseServerPackage; + private Method addChannel; + private Method removeChannel; + @Nullable + private Class playerHideEntityEvent; + @Nullable + private Method playerHideEntityEventGetEntity; + @Nullable + private Class playerShowEntityEvent; + @Nullable + private Method playerShowEntityEventGetEntity; + private Consumer runTask; + private TaskScheduler taskScheduler; + + @Override + public void init() throws Exception { + Compatibility.super.init(); + this.baseBukkitPackage = Bukkit.getServer().getClass().getPackage().getName(); + this.baseServerPackage = ReflectionUtils.callMethod((Object)Bukkit.getServer(), "getServer").getClass().getPackage().getName(); + Class craftPlayer = this.getBukkitClass("entity.CraftPlayer"); + this.addChannel = ReflectionUtils.getMethod(craftPlayer, new String[]{"addChannel"}, new Class[]{String.class}); + this.removeChannel = ReflectionUtils.getMethod(craftPlayer, new String[]{"removeChannel"}, new Class[]{String.class}); + try { + if (ReflectionUtils.doesClassExist("org.bukkit.event.player.PlayerHideEntityEvent") && ReflectionUtils.doesClassExist("org.bukkit.event.player.PlayerShowEntityEvent")) { + Class hide = ReflectionUtils.getClazz("org.bukkit.event.player.PlayerHideEntityEvent"); + if (!PlayerEvent.class.isAssignableFrom(hide)) { + throw new CompatibilityReflectionException("PlayerHideEntityEvent is not a subclass of PlayerEvent"); + } + Class show = ReflectionUtils.getClazz("org.bukkit.event.player.PlayerShowEntityEvent"); + if (!PlayerEvent.class.isAssignableFrom(show)) { + throw new CompatibilityReflectionException("PlayerShowEntityEvent is not a subclass of PlayerEvent"); + } + this.playerShowEntityEventGetEntity = ReflectionUtils.getMethod(show, "getEntity"); + this.playerHideEntityEventGetEntity = ReflectionUtils.getMethod(hide, "getEntity"); + this.playerShowEntityEvent = show; + this.playerHideEntityEvent = hide; + } else { + Voicechat.LOGGER.warn("Vanish plugin integration is not supported", new Object[0]); + } + } + catch (CompatibilityReflectionException e) { + Voicechat.LOGGER.warn("Vanish plugin integration is disabled", new Object[0]); + Voicechat.LOGGER.warn("Failed to load vanish compatibility", e); + this.playerHideEntityEvent = null; + this.playerShowEntityEvent = null; + } + this.runTask = runnable -> Bukkit.getScheduler().runTask((Plugin)Voicechat.INSTANCE, runnable); + this.taskScheduler = new TaskScheduler(){ + + @Override + public void scheduleSyncRepeatingTask(Runnable runnable, long delay, long period) { + Bukkit.getScheduler().scheduleSyncRepeatingTask((Plugin)Voicechat.INSTANCE, runnable, delay, period); + } + + @Override + public void runTaskLater(Runnable runnable, long delay) { + Bukkit.getScheduler().runTaskLater((Plugin)Voicechat.INSTANCE, runnable, delay); + } + }; + if (ReflectionUtils.doesMethodExist(Bukkit.class, "getGlobalRegionScheduler", new Class[0])) { + final Object globalRegionScheduler = ReflectionUtils.callMethod(Bukkit.class, "getGlobalRegionScheduler"); + Method run = ReflectionUtils.getMethod(globalRegionScheduler.getClass(), new String[]{"run"}, new Class[]{Plugin.class, Consumer.class}); + this.runTask = runnable -> ReflectionUtils.call(run, globalRegionScheduler, new Object[]{Voicechat.INSTANCE, task -> runnable.run()}); + if (ReflectionUtils.doesMethodExist(globalRegionScheduler.getClass(), "runAtFixedRate", Plugin.class, Consumer.class, Long.TYPE, Long.TYPE) && ReflectionUtils.doesMethodExist(globalRegionScheduler.getClass(), "runDelayed", Plugin.class, Consumer.class, Long.TYPE)) { + final Method runAtFixedRate = ReflectionUtils.getMethod(globalRegionScheduler.getClass(), new String[]{"runAtFixedRate"}, new Class[]{Plugin.class, Consumer.class, Long.TYPE, Long.TYPE}); + final Method runDelayed = ReflectionUtils.getMethod(globalRegionScheduler.getClass(), new String[]{"runDelayed"}, new Class[]{Plugin.class, Consumer.class, Long.TYPE}); + this.taskScheduler = new TaskScheduler(){ + final /* synthetic */ BaseCompatibility this$0; + { + this.this$0 = this$0; + } + + @Override + public void scheduleSyncRepeatingTask(Runnable runnable, long delay, long period) { + ReflectionUtils.call(runAtFixedRate, globalRegionScheduler, new Object[]{Voicechat.INSTANCE, task -> runnable.run(), delay <= 0L ? 1L : delay, period}); + } + + @Override + public void runTaskLater(Runnable runnable, long delay) { + ReflectionUtils.call(runDelayed, globalRegionScheduler, new Object[]{Voicechat.INSTANCE, task -> runnable.run(), delay <= 0L ? 1L : delay}); + } + }; + } + } + } + + @Override + public void addChannel(Player player, String channel) { + ReflectionUtils.call(this.addChannel, player, channel); + } + + @Override + public void removeChannel(Player player, String channel) { + ReflectionUtils.call(this.removeChannel, player, channel); + } + + @Override + public Key createNamespacedKey(String key) { + return Key.of(key); + } + + @Override + public void runTask(Runnable runnable) { + this.runTask.accept(runnable); + } + + @Override + public void scheduleSyncRepeatingTask(Runnable runnable, long delay, long period) { + this.taskScheduler.scheduleSyncRepeatingTask(runnable, delay, period); + } + + @Override + public void runTaskLater(Runnable runnable, long delay) { + this.taskScheduler.runTaskLater(runnable, delay); + } + + @Override + public boolean canSee(Player player, Player other) { + if (this.playerHideEntityEvent == null || this.playerShowEntityEvent == null) { + return true; + } + return player.canSee(other); + } + + @Override + public void registerPlayerHideEvent(Consumer event) { + if (this.playerHideEntityEvent == null) { + return; + } + Bukkit.getPluginManager().registerEvent(this.playerHideEntityEvent, new Listener(){}, EventPriority.NORMAL, (listener, evt) -> { + if (!evt.getClass().isAssignableFrom(this.playerHideEntityEvent)) { + return; + } + PlayerEvent playerEvent = (PlayerEvent)evt; + Object entityObj = ReflectionUtils.call(this.playerHideEntityEventGetEntity, playerEvent, new Object[0]); + if (!(entityObj instanceof Player)) { + return; + } + event.accept(new PlayerHideEvent((Player)entityObj, playerEvent.getPlayer())); + }, (Plugin)Voicechat.INSTANCE); + } + + @Override + public void registerPlayerShowEvent(Consumer event) { + if (this.playerShowEntityEvent == null) { + return; + } + Bukkit.getPluginManager().registerEvent(this.playerShowEntityEvent, new Listener(){}, EventPriority.NORMAL, (listener, evt) -> { + if (!evt.getClass().isAssignableFrom(this.playerShowEntityEvent)) { + return; + } + PlayerEvent playerEvent = (PlayerEvent)evt; + Object entityObj = ReflectionUtils.call(this.playerShowEntityEventGetEntity, playerEvent, new Object[0]); + if (!(entityObj instanceof Player)) { + return; + } + event.accept(new PlayerShowEvent((Player)entityObj, playerEvent.getPlayer())); + }, (Plugin)Voicechat.INSTANCE); + } + + @Override + public String getBaseBukkitPackage() { + return this.baseBukkitPackage; + } + + @Override + public String getBaseServerPackage() { + return this.baseServerPackage; + } + + private static interface TaskScheduler { + public void scheduleSyncRepeatingTask(Runnable var1, long var2, long var4); + + public void runTaskLater(Runnable var1, long var2); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/BukkitCompatibilityManager.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/BukkitCompatibilityManager.java new file mode 100644 index 0000000..33b7e90 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/BukkitCompatibilityManager.java @@ -0,0 +1,173 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.Bukkit + */ +package de.maxhenkel.voicechat.compatibility; + +import de.maxhenkel.voicechat.Version; +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.compatibility.Compatibility; +import de.maxhenkel.voicechat.compatibility.Compatibility1_12; +import de.maxhenkel.voicechat.compatibility.Compatibility1_16; +import de.maxhenkel.voicechat.compatibility.Compatibility1_17; +import de.maxhenkel.voicechat.compatibility.Compatibility1_18; +import de.maxhenkel.voicechat.compatibility.Compatibility1_19; +import de.maxhenkel.voicechat.compatibility.Compatibility1_19_1; +import de.maxhenkel.voicechat.compatibility.Compatibility1_20; +import de.maxhenkel.voicechat.compatibility.Compatibility1_20_2; +import de.maxhenkel.voicechat.compatibility.Compatibility1_20_3; +import de.maxhenkel.voicechat.compatibility.Compatibility1_21_5; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.CompatibilityReflectionException; +import de.maxhenkel.voicechat.compatibility.FallbackCompatibility; +import de.maxhenkel.voicechat.compatibility.SpigotCompatibility; +import java.util.HashMap; +import java.util.Map; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; + +public class BukkitCompatibilityManager { + private static final Map COMPATIBILITIES = new HashMap(); + + @Nullable + public static Compatibility loadCompatibility() { + Version forcedVersion = BukkitCompatibilityManager.getForcedVersion(); + Version version = forcedVersion != null ? forcedVersion : Version.getVersion(); + if (version == null) { + Voicechat.LOGGER.info("Failed to parse version {}, trying to fall back to Spigot API compatibility mode", Bukkit.getBukkitVersion()); + return BukkitCompatibilityManager.tryLoadSpigot(); + } + Voicechat.LOGGER.info("Initializing compatibility for version {}", version); + if (version.getMajor() == 1 && version.getMinor() <= 7) { + Voicechat.LOGGER.fatal("You are running a server that's too old to be able to join with any voice chat client", new Object[0]); + return null; + } + Compatibility compatibility = COMPATIBILITIES.get(version); + if (compatibility != null) { + try { + compatibility.init(); + } + catch (Throwable t) { + compatibility = null; + Voicechat.LOGGER.warn("Failed to load compatibility for version {}", version, t); + } + } + if (compatibility == null) { + Voicechat.LOGGER.warn("Incompatible version {}, trying to fall back to Spigot API compatibility mode", version); + compatibility = BukkitCompatibilityManager.tryLoadSpigot(); + } + if (compatibility == null) { + compatibility = FallbackCompatibility.INSTANCE; + try { + compatibility.init(); + Voicechat.LOGGER.warn("Falling back to Bukkit compatibility mode, expect issues and lack of features", new Object[0]); + Voicechat.LOGGER.warn("Chat messages won't be translatable", new Object[0]); + } + catch (CompatibilityReflectionException e) { + compatibility = null; + Voicechat.LOGGER.warn("Failed to load Bukkit compatibility mode: {}", e.getMessage()); + } + catch (Throwable t) { + compatibility = null; + Voicechat.LOGGER.warn("Failed to load Bukkit compatibility mode", t); + } + } + if (compatibility == null) { + Voicechat.LOGGER.fatal("Could not load any compatibility for {}", version); + } + return compatibility; + } + + @Nullable + private static Compatibility tryLoadSpigot() { + if (!SpigotCompatibility.isSpigotCompatible()) { + Voicechat.LOGGER.warn("Spigot API not found", new Object[0]); + return null; + } + SpigotCompatibility compatibility = SpigotCompatibility.INSTANCE; + try { + compatibility.init(); + Voicechat.LOGGER.warn("Falling back to Spigot API compatibility mode, expect issues and lack of features", new Object[0]); + } + catch (CompatibilityReflectionException e) { + compatibility = null; + Voicechat.LOGGER.warn("Failed to load Spigot API compatibility mode: {}", e.getMessage()); + } + catch (Throwable t) { + compatibility = null; + Voicechat.LOGGER.warn("Failed to load Spigot API compatibility mode", t); + } + return compatibility; + } + + @Nullable + public static Version getForcedVersion() { + String property = System.getProperty("voicechat.compatibility"); + if (property == null) { + return null; + } + Version forcedVersion = Version.parse(property); + if (forcedVersion == null) { + Voicechat.LOGGER.warn("Failed to parse forced compatibility version: {}", property); + return null; + } + Voicechat.LOGGER.info("Forcing compatibility for {}", forcedVersion); + Voicechat.LOGGER.warn("Forcing version compatibility could lead to issues - use with caution!", new Object[0]); + return forcedVersion; + } + + static { + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_PAPER_26_3, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_26_3, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_PAPER_26_2, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_26_2, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_PAPER_26_1_2, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_26_1_2, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_PAPER_26_1_1, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_26_1_1, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_26_1, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_1_21_11, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_1_21_10, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_1_21_9, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_1_21_8, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_1_21_7, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_1_21_6, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_21_5.VERSION_1_21_5, Compatibility1_21_5.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_3.VERSION_1_21_4, Compatibility1_20_3.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_3.VERSION_1_21_3, Compatibility1_20_3.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_3.VERSION_1_21_2, Compatibility1_20_3.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_3.VERSION_1_21_1, Compatibility1_20_3.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_3.VERSION_1_21, Compatibility1_20_3.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_3.VERSION_1_20_6, Compatibility1_20_3.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_3.VERSION_1_20_5, Compatibility1_20_3.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_3.VERSION_1_20_4, Compatibility1_20_3.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_3.VERSION_1_20_3, Compatibility1_20_3.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20_2.VERSION_1_20_2, Compatibility1_20_2.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20.VERSION_1_20_1, Compatibility1_20.INSTANCE); + COMPATIBILITIES.put(Compatibility1_20.VERSION_1_20, Compatibility1_20.INSTANCE); + COMPATIBILITIES.put(Compatibility1_19_1.VERSION_1_19_4, Compatibility1_19_1.INSTANCE); + COMPATIBILITIES.put(Compatibility1_19_1.VERSION_1_19_3, Compatibility1_19_1.INSTANCE); + COMPATIBILITIES.put(Compatibility1_19_1.VERSION_1_19_2, Compatibility1_19_1.INSTANCE); + COMPATIBILITIES.put(Compatibility1_19_1.VERSION_1_19_1, Compatibility1_19_1.INSTANCE); + COMPATIBILITIES.put(Compatibility1_19.VERSION_1_19, Compatibility1_19.INSTANCE); + COMPATIBILITIES.put(Compatibility1_18.VERSION_1_18_2, Compatibility1_18.INSTANCE); + COMPATIBILITIES.put(Compatibility1_18.VERSION_1_18_1, Compatibility1_18.INSTANCE); + COMPATIBILITIES.put(Compatibility1_18.VERSION_1_18, Compatibility1_18.INSTANCE); + COMPATIBILITIES.put(Compatibility1_17.VERSION_1_17_1, Compatibility1_17.INSTANCE); + COMPATIBILITIES.put(Compatibility1_17.VERSION_1_17, Compatibility1_17.INSTANCE); + COMPATIBILITIES.put(Compatibility1_16.VERSION_1_16_5, Compatibility1_16.INSTANCE); + COMPATIBILITIES.put(Compatibility1_16.VERSION_1_16_4, Compatibility1_16.INSTANCE); + COMPATIBILITIES.put(Compatibility1_16.VERSION_1_16_3, Compatibility1_16.INSTANCE); + COMPATIBILITIES.put(Compatibility1_16.VERSION_1_16_2, Compatibility1_16.INSTANCE); + COMPATIBILITIES.put(Compatibility1_16.VERSION_1_16_1, Compatibility1_16.INSTANCE); + COMPATIBILITIES.put(Compatibility1_16.VERSION_1_16, Compatibility1_16.INSTANCE); + COMPATIBILITIES.put(Compatibility1_12.VERSION_1_12_2, Compatibility1_12.INSTANCE); + COMPATIBILITIES.put(Compatibility1_12.VERSION_1_12_1, Compatibility1_12.INSTANCE); + COMPATIBILITIES.put(Compatibility1_12.VERSION_1_12, Compatibility1_12.INSTANCE); + COMPATIBILITIES.put(Compatibility1_8.VERSION_1_8_8, Compatibility1_8.INSTANCE); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility.java new file mode 100644 index 0000000..9fb6528 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility.java @@ -0,0 +1,82 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * javax.annotation.Nullable + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.compatibility.CompatibilityReflectionException; +import de.maxhenkel.voicechat.compatibility.PlayerHideEvent; +import de.maxhenkel.voicechat.compatibility.PlayerShowEvent; +import de.maxhenkel.voicechat.util.Key; +import java.util.function.Consumer; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public interface Compatibility { + default public void init() throws Exception { + } + + public void addChannel(Player var1, String var2); + + public void removeChannel(Player var1, String var2); + + public Key createNamespacedKey(String var1); + + public void sendTranslationMessage(Player var1, String var2, String ... var3); + + public void sendStatusMessage(Player var1, String var2, String ... var3); + + public void sendInviteMessage(Player var1, Player var2, String var3, String var4); + + public void sendIncompatibleMessage(Player var1, String var2, String var3); + + public void runTask(Runnable var1); + + public void scheduleSyncRepeatingTask(Runnable var1, long var2, long var4); + + public void runTaskLater(Runnable var1, long var2); + + public boolean canSee(Player var1, Player var2); + + public void registerPlayerHideEvent(Consumer var1); + + public void registerPlayerShowEvent(Consumer var1); + + @Nullable + public ArgumentType playerArgument(); + + @Nullable + public ArgumentType uuidArgument(); + + public String getBaseBukkitPackage(); + + public String getBaseServerPackage(); + + default public Class getBukkitClass(String ... classNames) { + for (String className : classNames) { + try { + return Class.forName(String.format("%s.%s", this.getBaseBukkitPackage(), className)); + } + catch (Throwable throwable) { + } + } + throw new CompatibilityReflectionException(String.format("Could not find any of the following classes: %s", String.join((CharSequence)", ", classNames))); + } + + default public Class getServerClass(String ... classNames) { + for (String className : classNames) { + try { + return Class.forName(String.format("%s.%s", this.getBaseServerPackage(), className)); + } + catch (Throwable throwable) { + } + } + throw new CompatibilityReflectionException(String.format("Could not find any of the following classes: %s", String.join((CharSequence)", ", classNames))); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_12.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_12.java new file mode 100644 index 0000000..466446b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_12.java @@ -0,0 +1,83 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import de.maxhenkel.voicechat.util.Key; +import org.bukkit.entity.Player; + +public class Compatibility1_12 +extends JsonMessageBaseCompatibility { + public static final String CHANNEL = "vc"; + public static final BukkitVersion VERSION_1_12_2 = BukkitVersion.parseBukkitVersion("1.12.2-R0.1"); + public static final BukkitVersion VERSION_1_12_1 = BukkitVersion.parseBukkitVersion("1.12.1-R0.1"); + public static final BukkitVersion VERSION_1_12 = BukkitVersion.parseBukkitVersion("1.12-R0.1"); + public static final Compatibility1_12 INSTANCE = new Compatibility1_12(); + + @Override + public Key createNamespacedKey(String key) { + return Key.of(CHANNEL, key); + } + + @Override + public void sendJsonMessage(Player player, String json) { + Class chatMessageTypeClass = this.getServerClass("ChatMessageType"); + Object b = ReflectionUtils.getField(chatMessageTypeClass, "CHAT"); + this.send(player, json, b); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + Class chatMessageTypeClass = this.getServerClass("ChatMessageType"); + Object b = ReflectionUtils.getField(chatMessageTypeClass, "GAME_INFO"); + this.send(player, json, b); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + @Override + public ArgumentType playerArgument() { + return null; + } + + @Override + public ArgumentType uuidArgument() { + return null; + } + + private void send(Player player, String json, Object chatMessageType) { + Object entityPlayer = ReflectionUtils.callMethod((Object)player, "getHandle"); + Object playerConnection = ReflectionUtils.getField(entityPlayer, "playerConnection"); + Class packet = this.getServerClass("Packet"); + Class chatSerializer = this.getServerClass("IChatBaseComponent$ChatSerializer"); + Class iChatBaseComponentClass = this.getServerClass("IChatBaseComponent"); + Object iChatBaseComponent = ReflectionUtils.callMethod(chatSerializer, "a", new Class[]{String.class}, json); + Class packetPlayOutChatClass = this.getServerClass("PacketPlayOutChat"); + Class chatMessageTypeClass = this.getServerClass("ChatMessageType"); + Object clientboundSystemChatPacket = ReflectionUtils.callConstructor(packetPlayOutChatClass, new Class[]{iChatBaseComponentClass, chatMessageTypeClass}, iChatBaseComponent, chatMessageType); + ReflectionUtils.callMethod(playerConnection, "sendPacket", new Class[]{packet}, clientboundSystemChatPacket); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_16.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_16.java new file mode 100644 index 0000000..8f7ee8c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_16.java @@ -0,0 +1,83 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import java.util.UUID; +import org.bukkit.entity.Player; + +public class Compatibility1_16 +extends JsonMessageBaseCompatibility { + public static final BukkitVersion VERSION_1_16_5 = BukkitVersion.parseBukkitVersion("1.16.5-R0.1"); + public static final BukkitVersion VERSION_1_16_4 = BukkitVersion.parseBukkitVersion("1.16.4-R0.1"); + public static final BukkitVersion VERSION_1_16_3 = BukkitVersion.parseBukkitVersion("1.16.3-R0.1"); + public static final BukkitVersion VERSION_1_16_2 = BukkitVersion.parseBukkitVersion("1.16.2-R0.1"); + public static final BukkitVersion VERSION_1_16_1 = BukkitVersion.parseBukkitVersion("1.16.1-R0.1"); + public static final BukkitVersion VERSION_1_16 = BukkitVersion.parseBukkitVersion("1.16-R0.1"); + public static final Compatibility1_16 INSTANCE = new Compatibility1_16(); + private static final UUID NUL_UUID = new UUID(0L, 0L); + + @Override + public void sendJsonMessage(Player player, String json) { + Class chatMessageTypeClass = this.getServerClass("ChatMessageType"); + Object b = ReflectionUtils.getField(chatMessageTypeClass, "CHAT"); + this.send(player, json, b); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + Class chatMessageTypeClass = this.getServerClass("ChatMessageType"); + Object b = ReflectionUtils.getField(chatMessageTypeClass, "GAME_INFO"); + this.send(player, json, b); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + @Override + public ArgumentType playerArgument() { + Class argumentEntity = this.getServerClass("ArgumentEntity"); + return (ArgumentType)ReflectionUtils.callMethod(argumentEntity, "c"); + } + + @Override + public ArgumentType uuidArgument() { + Class argumentEntity = this.getServerClass("ArgumentUUID"); + return (ArgumentType)ReflectionUtils.callMethod(argumentEntity, "a"); + } + + private void send(Player player, String json, Object chatMessageType) { + Object entityPlayer = ReflectionUtils.callMethod((Object)player, "getHandle"); + Object playerConnection = ReflectionUtils.getField(entityPlayer, "playerConnection"); + Class packet = this.getServerClass("Packet"); + Class chatSerializer = this.getServerClass("IChatBaseComponent$ChatSerializer"); + Class iChatBaseComponentClass = this.getServerClass("IChatBaseComponent"); + Object iChatBaseComponent = ReflectionUtils.callMethod(chatSerializer, "a", new Class[]{String.class}, json); + Class packetPlayOutChatClass = this.getServerClass("PacketPlayOutChat"); + Class chatMessageTypeClass = this.getServerClass("ChatMessageType"); + Object clientboundSystemChatPacket = ReflectionUtils.callConstructor(packetPlayOutChatClass, new Class[]{iChatBaseComponentClass, chatMessageTypeClass, UUID.class}, iChatBaseComponent, chatMessageType, NUL_UUID); + ReflectionUtils.callMethod(playerConnection, "sendPacket", new Class[]{packet}, clientboundSystemChatPacket); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_17.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_17.java new file mode 100644 index 0000000..c543a09 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_17.java @@ -0,0 +1,78 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_19; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import java.util.UUID; +import org.bukkit.entity.Player; + +public class Compatibility1_17 +extends JsonMessageBaseCompatibility { + public static final BukkitVersion VERSION_1_17_1 = BukkitVersion.parseBukkitVersion("1.17.1-R0.1"); + public static final BukkitVersion VERSION_1_17 = BukkitVersion.parseBukkitVersion("1.17-R0.1"); + public static final Compatibility1_17 INSTANCE = new Compatibility1_17(); + private static final UUID NUL_UUID = new UUID(0L, 0L); + + @Override + public void sendJsonMessage(Player player, String json) { + Class chatMessageTypeClass = ReflectionUtils.getClazz("net.minecraft.network.chat.ChatMessageType"); + Object b = ReflectionUtils.getField(chatMessageTypeClass, "b"); + this.send(player, json, b); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + Class chatMessageTypeClass = ReflectionUtils.getClazz("net.minecraft.network.chat.ChatMessageType"); + Object b = ReflectionUtils.getField(chatMessageTypeClass, "c"); + this.send(player, json, b); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + @Override + public ArgumentType playerArgument() { + return Compatibility1_19.INSTANCE.playerArgument(); + } + + @Override + public ArgumentType uuidArgument() { + return Compatibility1_19.INSTANCE.uuidArgument(); + } + + private void send(Player player, String json, Object chatMessageType) { + Object entityPlayer = ReflectionUtils.callMethod((Object)player, "getHandle"); + Object playerConnection = ReflectionUtils.getField(entityPlayer, "b"); + Class packet = ReflectionUtils.getClazz("net.minecraft.network.protocol.Packet"); + Class craftChatMessage = this.getBukkitClass("util.CraftChatMessage"); + Class iChatBaseComponentClass = ReflectionUtils.getClazz("net.minecraft.network.chat.IChatBaseComponent"); + Object iChatBaseComponent = ReflectionUtils.callMethod(craftChatMessage, "fromJSON", new Class[]{String.class}, json); + Class packetPlayOutChatClass = ReflectionUtils.getClazz("net.minecraft.network.protocol.game.PacketPlayOutChat"); + Class chatMessageTypeClass = ReflectionUtils.getClazz("net.minecraft.network.chat.ChatMessageType"); + Object clientboundSystemChatPacket = ReflectionUtils.callConstructor(packetPlayOutChatClass, new Class[]{iChatBaseComponentClass, chatMessageTypeClass, UUID.class}, iChatBaseComponent, chatMessageType, NUL_UUID); + ReflectionUtils.callMethod(playerConnection, "sendPacket", new Class[]{packet}, clientboundSystemChatPacket); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_18.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_18.java new file mode 100644 index 0000000..306d5bc --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_18.java @@ -0,0 +1,79 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_19; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import java.util.UUID; +import org.bukkit.entity.Player; + +public class Compatibility1_18 +extends JsonMessageBaseCompatibility { + public static final BukkitVersion VERSION_1_18_2 = BukkitVersion.parseBukkitVersion("1.18.2-R0.1"); + public static final BukkitVersion VERSION_1_18_1 = BukkitVersion.parseBukkitVersion("1.18.1-R0.1"); + public static final BukkitVersion VERSION_1_18 = BukkitVersion.parseBukkitVersion("1.18-R0.1"); + public static final Compatibility1_18 INSTANCE = new Compatibility1_18(); + private static final UUID NUL_UUID = new UUID(0L, 0L); + + @Override + public void sendJsonMessage(Player player, String json) { + Class chatMessageTypeClass = ReflectionUtils.getClazz("net.minecraft.network.chat.ChatMessageType"); + Object b = ReflectionUtils.getField(chatMessageTypeClass, "b"); + this.send(player, json, b); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + Class chatMessageTypeClass = ReflectionUtils.getClazz("net.minecraft.network.chat.ChatMessageType"); + Object b = ReflectionUtils.getField(chatMessageTypeClass, "c"); + this.send(player, json, b); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + @Override + public ArgumentType playerArgument() { + return Compatibility1_19.INSTANCE.playerArgument(); + } + + @Override + public ArgumentType uuidArgument() { + return Compatibility1_19.INSTANCE.uuidArgument(); + } + + private void send(Player player, String json, Object chatMessageType) { + Object entityPlayer = ReflectionUtils.callMethod((Object)player, "getHandle"); + Object playerConnection = ReflectionUtils.getField(entityPlayer, "b"); + Class packet = ReflectionUtils.getClazz("net.minecraft.network.protocol.Packet"); + Class craftChatMessage = this.getBukkitClass("util.CraftChatMessage"); + Class iChatBaseComponentClass = ReflectionUtils.getClazz("net.minecraft.network.chat.IChatBaseComponent"); + Object iChatBaseComponent = ReflectionUtils.callMethod(craftChatMessage, "fromJSON", new Class[]{String.class}, json); + Class packetPlayOutChatClass = ReflectionUtils.getClazz("net.minecraft.network.protocol.game.PacketPlayOutChat"); + Class chatMessageTypeClass = ReflectionUtils.getClazz("net.minecraft.network.chat.ChatMessageType"); + Object clientboundSystemChatPacket = ReflectionUtils.callConstructor(packetPlayOutChatClass, new Class[]{iChatBaseComponentClass, chatMessageTypeClass, UUID.class}, iChatBaseComponent, chatMessageType, NUL_UUID); + ReflectionUtils.callMethod(playerConnection, "a", new Class[]{packet}, clientboundSystemChatPacket); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_19.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_19.java new file mode 100644 index 0000000..beee97f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_19.java @@ -0,0 +1,84 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_19_1; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import org.bukkit.entity.Player; + +public class Compatibility1_19 +extends JsonMessageBaseCompatibility { + public static final BukkitVersion VERSION_1_19 = BukkitVersion.parseBukkitVersion("1.19-R0.1"); + public static final Compatibility1_19 INSTANCE = new Compatibility1_19(); + + @Override + public void sendJsonMessage(Player player, String json) { + this.send(player, json, false); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + this.send(player, json, true); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + @Override + public ArgumentType playerArgument() { + return Compatibility1_19_1.INSTANCE.playerArgument(); + } + + @Override + public ArgumentType uuidArgument() { + return Compatibility1_19_1.INSTANCE.uuidArgument(); + } + + private void send(Player player, String json, boolean status) { + Object entityPlayer = ReflectionUtils.callMethod((Object)player, "getHandle"); + Object playerConnection = ReflectionUtils.getField(entityPlayer, "b"); + Class packet = ReflectionUtils.getClazz("net.minecraft.network.protocol.Packet"); + Class craftChatMessage = this.getBukkitClass("util.CraftChatMessage"); + Class iChatBaseComponentClass = ReflectionUtils.getClazz("net.minecraft.network.chat.IChatBaseComponent"); + Object iChatBaseComponent = ReflectionUtils.callMethod(craftChatMessage, "fromJSON", new Class[]{String.class}, json); + Class clientboundSystemChatPacketClass = ReflectionUtils.getClazz("net.minecraft.network.protocol.game.ClientboundSystemChatPacket"); + Object clientboundSystemChatPacket = ReflectionUtils.callConstructor(clientboundSystemChatPacketClass, new Class[]{iChatBaseComponentClass, Integer.TYPE}, iChatBaseComponent, this.getId(player, status)); + ReflectionUtils.callMethod(playerConnection, "a", new Class[]{packet}, clientboundSystemChatPacket); + } + + private int getId(Player player, boolean status) { + Object world = ReflectionUtils.callMethod((Object)player.getWorld(), "getHandle"); + Class iRegistryCustomClass = ReflectionUtils.getClazz("net.minecraft.core.IRegistryCustom"); + Object iRegistryCustom = ReflectionUtils.callMethod(world, "s"); + Class resourceKeyClass = ReflectionUtils.getClazz("net.minecraft.resources.ResourceKey"); + Class iRegistry = ReflectionUtils.getClazz("net.minecraft.core.IRegistry"); + Object resourceKey = ReflectionUtils.getField(iRegistry, "bI"); + Object registry = ReflectionUtils.callMethod(iRegistryCustomClass, iRegistryCustom, "d", new Class[]{resourceKeyClass}, resourceKey); + Class chatMessageTypeClass = ReflectionUtils.getClazz("net.minecraft.network.chat.ChatMessageType"); + Object chatMessageType = status ? ReflectionUtils.getField(chatMessageTypeClass, "d") : ReflectionUtils.getField(chatMessageTypeClass, "c"); + Object messageType = ReflectionUtils.callMethod(registry, "a", new Class[]{resourceKeyClass}, chatMessageType); + return (Integer)ReflectionUtils.callMethod(registry, "a", new Class[]{Object.class}, messageType); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_19_1.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_19_1.java new file mode 100644 index 0000000..042c0e9 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_19_1.java @@ -0,0 +1,74 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import org.bukkit.entity.Player; + +public class Compatibility1_19_1 +extends JsonMessageBaseCompatibility { + public static final BukkitVersion VERSION_1_19_4 = BukkitVersion.parseBukkitVersion("1.19.4-R0.1"); + public static final BukkitVersion VERSION_1_19_3 = BukkitVersion.parseBukkitVersion("1.19.3-R0.1"); + public static final BukkitVersion VERSION_1_19_2 = BukkitVersion.parseBukkitVersion("1.19.2-R0.1"); + public static final BukkitVersion VERSION_1_19_1 = BukkitVersion.parseBukkitVersion("1.19.1-R0.1"); + public static final Compatibility1_19_1 INSTANCE = new Compatibility1_19_1(); + + @Override + public void sendJsonMessage(Player player, String json) { + this.send(player, json, false); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + this.send(player, json, true); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + @Override + public ArgumentType playerArgument() { + Class argumentEntity = ReflectionUtils.getClazz("net.minecraft.commands.arguments.ArgumentEntity"); + return (ArgumentType)ReflectionUtils.callMethod(argumentEntity, "c"); + } + + @Override + public ArgumentType uuidArgument() { + Class argumentEntity = ReflectionUtils.getClazz("net.minecraft.commands.arguments.ArgumentUUID"); + return (ArgumentType)ReflectionUtils.callMethod(argumentEntity, "a"); + } + + private void send(Player player, String json, boolean status) { + Object entityPlayer = ReflectionUtils.callMethod((Object)player, "getHandle"); + Object playerConnection = ReflectionUtils.getField(entityPlayer, "b"); + Class packet = ReflectionUtils.getClazz("net.minecraft.network.protocol.Packet"); + Class craftChatMessage = this.getBukkitClass("util.CraftChatMessage"); + Class iChatBaseComponentClass = ReflectionUtils.getClazz("net.minecraft.network.chat.IChatBaseComponent"); + Object iChatBaseComponent = ReflectionUtils.callMethod(craftChatMessage, "fromJSON", new Class[]{String.class}, json); + Class clientboundSystemChatPacketClass = ReflectionUtils.getClazz("net.minecraft.network.protocol.game.ClientboundSystemChatPacket"); + Object clientboundSystemChatPacket = ReflectionUtils.callConstructor(clientboundSystemChatPacketClass, new Class[]{iChatBaseComponentClass, Boolean.TYPE}, iChatBaseComponent, status); + ReflectionUtils.callMethod(playerConnection, "a", new Class[]{packet}, clientboundSystemChatPacket); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20.java new file mode 100644 index 0000000..984330c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20.java @@ -0,0 +1,72 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import org.bukkit.entity.Player; + +public class Compatibility1_20 +extends JsonMessageBaseCompatibility { + public static final BukkitVersion VERSION_1_20 = BukkitVersion.parseBukkitVersion("1.20-R0.1"); + public static final BukkitVersion VERSION_1_20_1 = BukkitVersion.parseBukkitVersion("1.20.1-R0.1"); + public static final Compatibility1_20 INSTANCE = new Compatibility1_20(); + + @Override + public void sendJsonMessage(Player player, String json) { + this.send(player, json, false); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + this.send(player, json, true); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + @Override + public ArgumentType playerArgument() { + Class argumentEntity = ReflectionUtils.getClazz("net.minecraft.commands.arguments.ArgumentEntity"); + return (ArgumentType)ReflectionUtils.callMethod(argumentEntity, "c"); + } + + @Override + public ArgumentType uuidArgument() { + Class argumentEntity = ReflectionUtils.getClazz("net.minecraft.commands.arguments.ArgumentUUID"); + return (ArgumentType)ReflectionUtils.callMethod(argumentEntity, "a"); + } + + private void send(Player player, String json, boolean status) { + Object entityPlayer = ReflectionUtils.callMethod((Object)player, "getHandle"); + Object playerConnection = ReflectionUtils.getField(entityPlayer, "c"); + Class packet = ReflectionUtils.getClazz("net.minecraft.network.protocol.Packet"); + Class craftChatMessage = this.getBukkitClass("util.CraftChatMessage"); + Class iChatBaseComponentClass = ReflectionUtils.getClazz("net.minecraft.network.chat.IChatBaseComponent"); + Object iChatBaseComponent = ReflectionUtils.callMethod(craftChatMessage, "fromJSON", new Class[]{String.class}, json); + Class clientboundSystemChatPacketClass = ReflectionUtils.getClazz("net.minecraft.network.protocol.game.ClientboundSystemChatPacket"); + Object clientboundSystemChatPacket = ReflectionUtils.callConstructor(clientboundSystemChatPacketClass, new Class[]{iChatBaseComponentClass, Boolean.TYPE}, iChatBaseComponent, status); + ReflectionUtils.callMethod(playerConnection, "a", new Class[]{packet}, clientboundSystemChatPacket); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20_2.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20_2.java new file mode 100644 index 0000000..faa0598 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20_2.java @@ -0,0 +1,67 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import org.bukkit.entity.Player; + +public class Compatibility1_20_2 +extends JsonMessageBaseCompatibility { + public static final BukkitVersion VERSION_1_20_2 = BukkitVersion.parseBukkitVersion("1.20.2-R0.1"); + public static final Compatibility1_20_2 INSTANCE = new Compatibility1_20_2(); + + @Override + public void sendJsonMessage(Player player, String json) { + this.send(player, json, false); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + this.send(player, json, true); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + @Override + public ArgumentType playerArgument() { + Class argumentEntity = ReflectionUtils.getClazz("net.minecraft.commands.arguments.ArgumentEntity"); + return (ArgumentType)ReflectionUtils.callMethod(argumentEntity, "c"); + } + + @Override + public ArgumentType uuidArgument() { + Class argumentEntity = ReflectionUtils.getClazz("net.minecraft.commands.arguments.ArgumentUUID"); + return (ArgumentType)ReflectionUtils.callMethod(argumentEntity, "a"); + } + + private void send(Player player, String json, boolean status) { + Object entityPlayer = ReflectionUtils.callMethod((Object)player, "getHandle"); + Class iChatBaseComponentClass = ReflectionUtils.getClazz("net.minecraft.network.chat.IChatBaseComponent"); + Class craftChatMessage = this.getBukkitClass("util.CraftChatMessage"); + Object iChatBaseComponent = ReflectionUtils.callMethod(craftChatMessage, "fromJSON", new Class[]{String.class}, json); + ReflectionUtils.callMethod(entityPlayer, "a", new Class[]{iChatBaseComponentClass, Boolean.TYPE}, iChatBaseComponent, status); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20_3.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20_3.java new file mode 100644 index 0000000..e2a968a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_20_3.java @@ -0,0 +1,91 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_8; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import java.lang.reflect.Method; +import org.bukkit.entity.Player; + +public class Compatibility1_20_3 +extends JsonMessageBaseCompatibility { + public static final BukkitVersion VERSION_1_20_3 = BukkitVersion.parseBukkitVersion("1.20.3-R0.1"); + public static final BukkitVersion VERSION_1_20_4 = BukkitVersion.parseBukkitVersion("1.20.4-R0.1"); + public static final BukkitVersion VERSION_1_20_5 = BukkitVersion.parseBukkitVersion("1.20.5-R0.1"); + public static final BukkitVersion VERSION_1_20_6 = BukkitVersion.parseBukkitVersion("1.20.6-R0.1"); + public static final BukkitVersion VERSION_1_21 = BukkitVersion.parseBukkitVersion("1.21-R0.1"); + public static final BukkitVersion VERSION_1_21_1 = BukkitVersion.parseBukkitVersion("1.21.1-R0.1"); + public static final BukkitVersion VERSION_1_21_2 = BukkitVersion.parseBukkitVersion("1.21.2-R0.1"); + public static final BukkitVersion VERSION_1_21_3 = BukkitVersion.parseBukkitVersion("1.21.3-R0.1"); + public static final BukkitVersion VERSION_1_21_4 = BukkitVersion.parseBukkitVersion("1.21.4-R0.1"); + public static final Compatibility1_20_3 INSTANCE = new Compatibility1_20_3(); + private Method playerArgument; + private Method uuidArgument; + private Method fromJson; + private Method getHandle; + private Method sendSystemMessage; + + @Override + public void init() throws Exception { + super.init(); + Class argumentEntityClass = ReflectionUtils.getClazz("net.minecraft.commands.arguments.ArgumentEntity", "net.minecraft.commands.arguments.EntityArgument"); + this.playerArgument = ReflectionUtils.getMethod(argumentEntityClass, "c", "entity"); + Class argumentUuidClass = ReflectionUtils.getClazz("net.minecraft.commands.arguments.ArgumentUUID", "net.minecraft.commands.arguments.UuidArgument"); + this.uuidArgument = ReflectionUtils.getMethod(argumentUuidClass, "a", "uuid"); + Class craftPlayer = this.getBukkitClass("entity.CraftPlayer"); + this.getHandle = ReflectionUtils.getMethod(craftPlayer, "getHandle"); + Class componentClass = ReflectionUtils.getClazz("net.minecraft.network.chat.IChatBaseComponent", "net.minecraft.network.chat.Component"); + Class craftChatMessageClass = this.getBukkitClass("util.CraftChatMessage"); + this.fromJson = ReflectionUtils.getMethod(craftChatMessageClass, new String[]{"fromJSON"}, new Class[]{String.class}); + Class player = ReflectionUtils.getClazz("net.minecraft.server.level.EntityPlayer", "net.minecraft.server.level.ServerPlayer"); + this.sendSystemMessage = ReflectionUtils.getMethod(player, new String[]{"a", "sendSystemMessage"}, new Class[]{componentClass, Boolean.TYPE}); + } + + @Override + public void sendJsonMessage(Player player, String json) { + this.send(player, json, false); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + this.send(player, json, true); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + @Override + public ArgumentType playerArgument() { + return (ArgumentType)ReflectionUtils.call(this.playerArgument, null, new Object[0]); + } + + @Override + public ArgumentType uuidArgument() { + return (ArgumentType)ReflectionUtils.call(this.uuidArgument, null, new Object[0]); + } + + protected void send(Player player, String json, boolean status) { + ReflectionUtils.call(this.sendSystemMessage, ReflectionUtils.call(this.getHandle, player, new Object[0]), ReflectionUtils.call(this.fromJson, null, json), status); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_21_5.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_21_5.java new file mode 100644 index 0000000..76588af --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_21_5.java @@ -0,0 +1,73 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.PaperVersion; +import de.maxhenkel.voicechat.compatibility.Compatibility1_20_3; +import de.maxhenkel.voicechat.org.json.JSONArray; +import de.maxhenkel.voicechat.org.json.JSONObject; +import org.bukkit.entity.Player; + +public class Compatibility1_21_5 +extends Compatibility1_20_3 { + public static final BukkitVersion VERSION_1_21_5 = BukkitVersion.parseBukkitVersion("1.21.5-R0.1"); + public static final BukkitVersion VERSION_1_21_6 = BukkitVersion.parseBukkitVersion("1.21.6-R0.1"); + public static final BukkitVersion VERSION_1_21_7 = BukkitVersion.parseBukkitVersion("1.21.7-R0.1"); + public static final BukkitVersion VERSION_1_21_8 = BukkitVersion.parseBukkitVersion("1.21.8-R0.1"); + public static final BukkitVersion VERSION_1_21_9 = BukkitVersion.parseBukkitVersion("1.21.9-R0.1"); + public static final BukkitVersion VERSION_1_21_10 = BukkitVersion.parseBukkitVersion("1.21.10-R0.1"); + public static final BukkitVersion VERSION_1_21_11 = BukkitVersion.parseBukkitVersion("1.21.11-R0.1"); + public static final BukkitVersion VERSION_26_1 = BukkitVersion.parseBukkitVersion("26.1-R0.1"); + public static final BukkitVersion VERSION_26_1_1 = BukkitVersion.parseBukkitVersion("26.1.1-R0.1"); + public static final PaperVersion VERSION_PAPER_26_1_1 = PaperVersion.parsePaperVersion("26.1.1.build.0"); + public static final BukkitVersion VERSION_26_1_2 = BukkitVersion.parseBukkitVersion("26.1.2-R0.1"); + public static final PaperVersion VERSION_PAPER_26_1_2 = PaperVersion.parsePaperVersion("26.1.2.build.0"); + public static final BukkitVersion VERSION_26_2 = BukkitVersion.parseBukkitVersion("26.2-R0.1"); + public static final PaperVersion VERSION_PAPER_26_2 = PaperVersion.parsePaperVersion("26.2.build.0"); + public static final BukkitVersion VERSION_26_3 = BukkitVersion.parseBukkitVersion("26.3-R0.1"); + public static final PaperVersion VERSION_PAPER_26_3 = PaperVersion.parsePaperVersion("26.3.build.0"); + public static final Compatibility1_21_5 INSTANCE = new Compatibility1_21_5(); + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_21_5.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + public static String constructInviteMessage(Player commandSender, String groupName, String joinCommand) { + JSONObject msg = new JSONObject(); + msg.put("translate", "message.voicechat.invite"); + JSONArray args = new JSONArray(); + args.put(commandSender.getName()); + JSONObject groupNameObj = new JSONObject(); + groupNameObj.put("color", "gray"); + groupNameObj.put("text", groupName); + args.put(groupNameObj); + JSONObject acceptInBrackets = new JSONObject(); + acceptInBrackets.put("translate", "chat.square_brackets"); + acceptInBrackets.put("color", "green"); + JSONArray withBrackets = new JSONArray(); + JSONObject accept = new JSONObject(); + accept.put("translate", "message.voicechat.accept_invite"); + JSONObject clickEvent = new JSONObject(); + clickEvent.put("action", "run_command"); + clickEvent.put("command", joinCommand); + accept.put("click_event", clickEvent); + JSONObject hoverEvent = new JSONObject(); + hoverEvent.put("action", "show_text"); + JSONObject contents = new JSONObject(); + contents.put("translate", "message.voicechat.accept_invite.hover"); + hoverEvent.put("value", contents); + accept.put("hover_event", hoverEvent); + withBrackets.put(accept); + acceptInBrackets.put("with", withBrackets); + args.put(acceptInBrackets); + msg.put("with", args); + return msg.toString(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_8.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_8.java new file mode 100644 index 0000000..afecbcc --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/Compatibility1_8.java @@ -0,0 +1,133 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.BukkitVersion; +import de.maxhenkel.voicechat.compatibility.JsonMessageBaseCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import de.maxhenkel.voicechat.org.json.JSONArray; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.util.Key; +import org.bukkit.entity.Player; + +public class Compatibility1_8 +extends JsonMessageBaseCompatibility { + public static final BukkitVersion VERSION_1_8_8 = BukkitVersion.parseBukkitVersion("1.8.8-R0.1"); + public static final Compatibility1_8 INSTANCE = new Compatibility1_8(); + + @Override + public Key createNamespacedKey(String key) { + return Key.of("vc", key); + } + + @Override + public void sendJsonMessage(Player player, String json) { + this.send(player, json, (byte)0); + } + + @Override + public void sendJsonStatusMessage(Player player, String json) { + this.send(player, json, (byte)2); + } + + @Override + public String createTranslationMessage(String key, String ... args) { + return Compatibility1_8.constructTranslationMessage(key, args); + } + + public static String constructTranslationMessage(String key, String ... args) { + JSONObject msg = new JSONObject(); + msg.put("translate", key); + msg.put("with", args); + return msg.toString(); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendJsonMessage(player, Compatibility1_8.constructInviteMessage(commandSender, groupName, joinCommand)); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendJsonMessage(player, Compatibility1_8.constructIncompatibleMessage(pluginVersion, pluginName)); + } + + public static String constructInviteMessage(Player commandSender, String groupName, String joinCommand) { + JSONObject msg = new JSONObject(); + msg.put("translate", "message.voicechat.invite"); + JSONArray args = new JSONArray(); + args.put(commandSender.getName()); + JSONObject groupNameObj = new JSONObject(); + groupNameObj.put("color", "gray"); + groupNameObj.put("text", groupName); + args.put(groupNameObj); + JSONObject acceptInBrackets = new JSONObject(); + acceptInBrackets.put("text", "["); + acceptInBrackets.put("color", "green"); + JSONArray extra = new JSONArray(); + JSONObject accept = new JSONObject(); + accept.put("translate", "message.voicechat.accept_invite"); + accept.put("color", "green"); + JSONObject clickEvent = new JSONObject(); + clickEvent.put("action", "run_command"); + clickEvent.put("value", joinCommand); + accept.put("clickEvent", clickEvent); + JSONObject hoverEvent = new JSONObject(); + hoverEvent.put("action", "show_text"); + JSONObject contents = new JSONObject(); + contents.put("translate", "message.voicechat.accept_invite.hover"); + hoverEvent.put("contents", contents); + accept.put("hoverEvent", hoverEvent); + extra.put(accept); + extra.put("]"); + acceptInBrackets.put("extra", extra); + args.put(acceptInBrackets); + msg.put("with", args); + return msg.toString(); + } + + public static String constructIncompatibleMessage(String pluginVersion, String pluginName) { + JSONObject msg = new JSONObject(); + msg.put("translate", "message.voicechat.incompatible_version"); + JSONArray with = new JSONArray(); + JSONObject version = new JSONObject(); + version.put("text", pluginVersion); + version.put("bold", true); + with.put(version); + JSONObject name = new JSONObject(); + name.put("text", pluginName); + name.put("bold", true); + with.put(name); + msg.put("with", with); + return msg.toString(); + } + + @Override + public ArgumentType playerArgument() { + return null; + } + + @Override + public ArgumentType uuidArgument() { + return null; + } + + private void send(Player player, String json, byte chatMessageType) { + Object entityPlayer = ReflectionUtils.callMethod((Object)player, "getHandle"); + Object playerConnection = ReflectionUtils.getField(entityPlayer, "playerConnection"); + Class packet = this.getServerClass("Packet"); + Class chatSerializer = this.getServerClass("IChatBaseComponent$ChatSerializer"); + Class iChatBaseComponentClass = this.getServerClass("IChatBaseComponent"); + Object iChatBaseComponent = ReflectionUtils.callMethod(chatSerializer, "a", new Class[]{String.class}, json); + Class packetPlayOutChatClass = this.getServerClass("PacketPlayOutChat"); + Object clientboundSystemChatPacket = ReflectionUtils.callConstructor(packetPlayOutChatClass, new Class[]{iChatBaseComponentClass, Byte.TYPE}, iChatBaseComponent, chatMessageType); + ReflectionUtils.callMethod(playerConnection, "sendPacket", new Class[]{packet}, clientboundSystemChatPacket); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/CompatibilityReflectionException.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/CompatibilityReflectionException.java new file mode 100644 index 0000000..2b8465f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/CompatibilityReflectionException.java @@ -0,0 +1,16 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.compatibility; + +public class CompatibilityReflectionException +extends RuntimeException { + public CompatibilityReflectionException(String message) { + super(message); + } + + public CompatibilityReflectionException(String message, Throwable cause) { + super(message, cause); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/FallbackCompatibility.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/FallbackCompatibility.java new file mode 100644 index 0000000..b310e63 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/FallbackCompatibility.java @@ -0,0 +1,78 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * javax.annotation.Nullable + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.FallbackTranslations; +import de.maxhenkel.voicechat.Version; +import de.maxhenkel.voicechat.compatibility.BaseCompatibility; +import de.maxhenkel.voicechat.util.Key; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public class FallbackCompatibility +extends BaseCompatibility { + public static final FallbackCompatibility INSTANCE = new FallbackCompatibility(); + + @Override + public void sendTranslationMessage(Player player, String key, String ... args) { + player.sendMessage(FallbackCompatibility.fallbackTranslation(key, args)); + } + + @Override + public void sendStatusMessage(Player player, String key, String ... args) { + this.sendTranslationMessage(player, key, args); + } + + @Override + public void sendInviteMessage(Player player, Player commandSender, String groupName, String joinCommand) { + this.sendTranslationMessage(player, "message.voicechat.invite", commandSender.getName(), groupName, ""); + } + + @Override + public void sendIncompatibleMessage(Player player, String pluginVersion, String pluginName) { + this.sendTranslationMessage(player, "message.voicechat.incompatible_version", pluginVersion, pluginName); + } + + @Override + public Key createNamespacedKey(String key) { + Version version = Version.getVersion(); + if (version == null) { + return super.createNamespacedKey(key); + } + if (version.getMajor() != 1) { + return super.createNamespacedKey(key); + } + if (version.getMinor() > 12) { + return super.createNamespacedKey(key); + } + return Key.of("vc", key); + } + + @Override + @Nullable + public ArgumentType playerArgument() { + return null; + } + + @Override + @Nullable + public ArgumentType uuidArgument() { + return null; + } + + public static String fallbackTranslation(String key, String ... args) { + String rawTranslation = FallbackTranslations.FALLBACK_TRANSLATIONS.get(key); + if (rawTranslation == null) { + return key; + } + return String.format(rawTranslation, args); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/JsonMessageBaseCompatibility.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/JsonMessageBaseCompatibility.java new file mode 100644 index 0000000..a893428 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/JsonMessageBaseCompatibility.java @@ -0,0 +1,30 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import de.maxhenkel.voicechat.compatibility.BaseCompatibility; +import org.bukkit.entity.Player; + +public abstract class JsonMessageBaseCompatibility +extends BaseCompatibility { + @Override + public void sendTranslationMessage(Player player, String key, String ... args) { + this.sendJsonMessage(player, this.createTranslationMessage(key, args)); + } + + @Override + public void sendStatusMessage(Player player, String key, String ... args) { + this.sendJsonStatusMessage(player, this.createTranslationMessage(key, args)); + } + + public abstract void sendJsonMessage(Player var1, String var2); + + public abstract void sendJsonStatusMessage(Player var1, String var2); + + public abstract String createTranslationMessage(String var1, String ... var2); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/PlayerHideEvent.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/PlayerHideEvent.java new file mode 100644 index 0000000..26f061f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/PlayerHideEvent.java @@ -0,0 +1,28 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import org.bukkit.entity.Player; + +public class PlayerHideEvent { + private final Player hiddenPlayer; + private final Player observer; + + public PlayerHideEvent(Player hiddenPlayer, Player observer) { + this.hiddenPlayer = hiddenPlayer; + this.observer = observer; + } + + public Player getHiddenPlayer() { + return this.hiddenPlayer; + } + + public Player getObserver() { + return this.observer; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/PlayerShowEvent.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/PlayerShowEvent.java new file mode 100644 index 0000000..4996235 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/PlayerShowEvent.java @@ -0,0 +1,28 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import org.bukkit.entity.Player; + +public class PlayerShowEvent { + private final Player shownPlayer; + private final Player observer; + + public PlayerShowEvent(Player shownPlayer, Player observer) { + this.shownPlayer = shownPlayer; + this.observer = observer; + } + + public Player getShownPlayer() { + return this.shownPlayer; + } + + public Player getObserver() { + return this.observer; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/ReflectionUtils.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/ReflectionUtils.java new file mode 100644 index 0000000..39da3db --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/ReflectionUtils.java @@ -0,0 +1,215 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.compatibility; + +import de.maxhenkel.voicechat.compatibility.CompatibilityReflectionException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import javax.annotation.Nullable; + +public class ReflectionUtils { + public static T callMethod(Object object, String ... methodNames) { + return ReflectionUtils.callMethod(object, methodNames, new Class[0], new Object[0]); + } + + public static T callMethod(Object object, String[] methodNames, Class[] parameterTypes, Object ... args) { + return ReflectionUtils.callMethod(object.getClass(), object, methodNames, parameterTypes, args); + } + + public static T callMethod(Object object, String methodName, Class[] parameterTypes, Object ... args) { + return ReflectionUtils.callMethod(object.getClass(), object, new String[]{methodName}, parameterTypes, args); + } + + public static T callMethod(Class clazz, Object object, String ... methodNames) { + return ReflectionUtils.callMethod(clazz, object, methodNames, new Class[0], new Object[0]); + } + + public static T callMethod(Class clazz, Object object, String methodName, Class[] parameterTypes, Object ... args) { + return ReflectionUtils.callMethod(clazz, object, new String[]{methodName}, parameterTypes, args); + } + + public static T callMethod(Class clazz, Object object, String[] methodNames, Class[] parameterTypes, Object ... args) { + for (String name : methodNames) { + try { + Method method = clazz.getDeclaredMethod(name, parameterTypes); + method.setAccessible(true); + return (T)method.invoke(object, args); + } + catch (Throwable ignored) { + try { + Method method = clazz.getMethod(name, parameterTypes); + method.setAccessible(true); + return (T)method.invoke(object, args); + } + catch (Throwable throwable) { + } + } + } + throw new CompatibilityReflectionException(String.format("Could not find any of the following methods in the class %s: %s", clazz.getSimpleName(), String.join((CharSequence)", ", methodNames))); + } + + public static T callMethod(Class object, String ... methodNames) { + return ReflectionUtils.callMethod(object, methodNames, new Class[0], new Object[0]); + } + + public static T callMethod(Class object, String[] methodNames, Class[] parameterTypes, Object ... args) { + return ReflectionUtils.callMethod(object, null, methodNames, parameterTypes, args); + } + + public static T callMethod(Class object, String methodName, Class[] parameterTypes, Object ... args) { + return ReflectionUtils.callMethod(object, null, new String[]{methodName}, parameterTypes, args); + } + + public static Method getMethod(Class clazz, String ... methodNames) { + return ReflectionUtils.getMethod(clazz, methodNames, new Class[0]); + } + + public static Method getMethod(Class clazz, String[] methodNames, Class[] parameterTypes) { + for (String name : methodNames) { + try { + Method method = clazz.getDeclaredMethod(name, parameterTypes); + method.setAccessible(true); + return method; + } + catch (Throwable ignored) { + try { + Method method = clazz.getMethod(name, parameterTypes); + method.setAccessible(true); + return method; + } + catch (Throwable throwable) { + } + } + } + throw new CompatibilityReflectionException(String.format("Could not find any of the following methods in the class %s: %s", clazz.getSimpleName(), String.join((CharSequence)", ", methodNames))); + } + + public static T call(Method method, @Nullable Object instance, Object ... args) { + try { + return (T)method.invoke(instance, args); + } + catch (Throwable t) { + throw new CompatibilityReflectionException(String.format("Failed to invoke method %s", method.getName()), t); + } + } + + public static T callConstructor(Class object) { + return ReflectionUtils.callConstructor(object, new Class[0], new Object[0]); + } + + public static T callConstructor(Class object, Class[] parameterTypes, Object ... args) { + try { + Constructor constructor = object.getDeclaredConstructor(parameterTypes); + constructor.setAccessible(true); + return (T)constructor.newInstance(args); + } + catch (Throwable t) { + throw new CompatibilityReflectionException(String.format("Failed to invoke constructor of %s", object.getName()), t); + } + } + + public static Constructor getConstructor(Class object, Class ... parameterTypes) { + try { + Constructor constructor = object.getDeclaredConstructor(parameterTypes); + constructor.setAccessible(true); + return constructor; + } + catch (Throwable t) { + throw new CompatibilityReflectionException(String.format("Failed to get constructor of %s", object.getName()), t); + } + } + + public static T newInstance(Constructor constructor, Object ... args) { + try { + return constructor.newInstance(args); + } + catch (Throwable t) { + throw new CompatibilityReflectionException(String.format("Failed to invoke constructor of %s", constructor.getDeclaringClass().getName()), t); + } + } + + public static T getField(Object object, String ... fieldNames) { + for (String fieldName : fieldNames) { + try { + Field field = object.getClass().getDeclaredField(fieldName); + field.setAccessible(true); + return (T)field.get(object); + } + catch (Throwable ignored) { + try { + Field field = object.getClass().getField(fieldName); + field.setAccessible(true); + return (T)field.get(object); + } + catch (Throwable throwable) { + } + } + } + throw new CompatibilityReflectionException(String.format("Could not find any of the following fields in the class %s: %s", object.getClass().getSimpleName(), String.join((CharSequence)", ", fieldNames))); + } + + public static T getField(Class clazz, String ... fieldNames) { + for (String fieldName : fieldNames) { + try { + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + return (T)field.get(null); + } + catch (Throwable ignored) { + try { + Field field = clazz.getField(fieldName); + field.setAccessible(true); + return (T)field.get(null); + } + catch (Throwable throwable) { + } + } + } + throw new CompatibilityReflectionException(String.format("Could not find any of the following fields in the class %s: %s", clazz.getSimpleName(), String.join((CharSequence)", ", fieldNames))); + } + + public static Class getClazz(String ... classNames) { + for (String className : classNames) { + try { + return Class.forName(className); + } + catch (Throwable throwable) { + } + } + throw new CompatibilityReflectionException(String.format("Could not find any of the following classes: %s", String.join((CharSequence)", ", classNames))); + } + + public static boolean doesClassExist(String ... classNames) { + for (String className : classNames) { + try { + Class.forName(className); + return true; + } + catch (Throwable throwable) { + } + } + return false; + } + + public static boolean doesMethodExist(Class clazz, String methodName, Class ... parameterTypes) { + try { + clazz.getDeclaredMethod(methodName, parameterTypes); + return true; + } + catch (Throwable ignored) { + try { + clazz.getMethod(methodName, parameterTypes); + return true; + } + catch (Throwable throwable) { + return false; + } + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/compatibility/SpigotCompatibility.java b/source/decompiled/de/maxhenkel/voicechat/compatibility/SpigotCompatibility.java new file mode 100644 index 0000000..110b2fe --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/compatibility/SpigotCompatibility.java @@ -0,0 +1,74 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * javax.annotation.Nullable + * org.bukkit.Bukkit + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.compatibility; + +import com.mojang.brigadier.arguments.ArgumentType; +import de.maxhenkel.voicechat.compatibility.FallbackCompatibility; +import de.maxhenkel.voicechat.compatibility.ReflectionUtils; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class SpigotCompatibility +extends FallbackCompatibility { + public static final SpigotCompatibility INSTANCE = new SpigotCompatibility(); + private MessageSender messageSender; + + @Override + public void init() throws Exception { + super.init(); + Class playerSpigotClass = ReflectionUtils.getClazz("org.bukkit.entity.Player$Spigot"); + Class baseComponentClass = ReflectionUtils.getClazz("net.md_5.bungee.api.chat.BaseComponent"); + Class chatMessageTypeClass = ReflectionUtils.getClazz("net.md_5.bungee.api.ChatMessageType"); + Object systemType = ReflectionUtils.getField(chatMessageTypeClass, "SYSTEM"); + Object actionBarType = ReflectionUtils.getField(chatMessageTypeClass, "ACTION_BAR"); + Method playerSpigot = ReflectionUtils.getMethod(Player.class, "spigot"); + Method sendMessage = ReflectionUtils.getMethod(playerSpigotClass, new String[]{"sendMessage"}, new Class[]{chatMessageTypeClass, baseComponentClass}); + Class translatableComponentClass = ReflectionUtils.getClazz("net.md_5.bungee.api.chat.TranslatableComponent"); + Constructor translatableComponentConstructor = ReflectionUtils.getConstructor(translatableComponentClass, String.class, Object[].class); + this.messageSender = (player, actionBar, key, args) -> { + Object messageType = actionBar ? actionBarType : systemType; + ReflectionUtils.call(sendMessage, ReflectionUtils.call(playerSpigot, player, new Object[0]), messageType, ReflectionUtils.newInstance(translatableComponentConstructor, key, args)); + }; + } + + public static boolean isSpigotCompatible() { + return ReflectionUtils.doesMethodExist(Bukkit.class, "spigot", new Class[0]); + } + + @Override + public void sendTranslationMessage(Player player, String key, String ... args) { + this.messageSender.send(player, false, key, args); + } + + @Override + public void sendStatusMessage(Player player, String key, String ... args) { + this.messageSender.send(player, true, key, args); + } + + @Override + @Nullable + public ArgumentType playerArgument() { + return null; + } + + @Override + @Nullable + public ArgumentType uuidArgument() { + return null; + } + + private static interface MessageSender { + public void send(Player var1, boolean var2, String var3, String ... var4); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Analysis.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Analysis.java new file mode 100644 index 0000000..0c253a3 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Analysis.java @@ -0,0 +1,397 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.AnalysisInfo; +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.CeltMode; +import de.maxhenkel.voicechat.concentus.Downmix; +import de.maxhenkel.voicechat.concentus.FFTState; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.KissFFT; +import de.maxhenkel.voicechat.concentus.MultiLayerPerceptron; +import de.maxhenkel.voicechat.concentus.OpusTables; +import de.maxhenkel.voicechat.concentus.TonalityAnalysisState; + +class Analysis { + private static final double M_PI = 3.141592653; + private static final float cA = 0.43157974f; + private static final float cB = 0.678484f; + private static final float cC = 0.08595542f; + private static final float cE = 1.5707964f; + private static final int NB_TONAL_SKIP_BANDS = 9; + + Analysis() { + } + + static float fast_atan2f(float y, float x) { + float y2; + float x2; + if (Inlines.ABS16(x) + Inlines.ABS16(y) < 1.0E-9f) { + x *= 1.0E12f; + y *= 1.0E12f; + } + if ((x2 = x * x) < (y2 = y * y)) { + float den = (y2 + 0.678484f * x2) * (y2 + 0.08595542f * x2); + if (den != 0.0f) { + return -x * y * (y2 + 0.43157974f * x2) / den + (y < 0.0f ? -1.5707964f : 1.5707964f); + } + return y < 0.0f ? -1.5707964f : 1.5707964f; + } + float den = (x2 + 0.678484f * y2) * (x2 + 0.08595542f * y2); + if (den != 0.0f) { + return x * y * (x2 + 0.43157974f * y2) / den + (y < 0.0f ? -1.5707964f : 1.5707964f) - (x * y < 0.0f ? -1.5707964f : 1.5707964f); + } + return (y < 0.0f ? -1.5707964f : 1.5707964f) - (x * y < 0.0f ? -1.5707964f : 1.5707964f); + } + + static void tonality_analysis_init(TonalityAnalysisState tonal) { + tonal.Reset(); + } + + static void tonality_get_info(TonalityAnalysisState tonal, AnalysisInfo info_out, int len) { + int i; + int pos = tonal.read_pos; + int curr_lookahead = tonal.write_pos - tonal.read_pos; + if (curr_lookahead < 0) { + curr_lookahead += 200; + } + if (len > 480 && pos != tonal.write_pos && ++pos == 200) { + pos = 0; + } + if (pos == tonal.write_pos) { + --pos; + } + if (pos < 0) { + pos = 199; + } + info_out.Assign(tonal.info[pos]); + tonal.read_subframe += len / 120; + while (tonal.read_subframe >= 4) { + tonal.read_subframe -= 4; + ++tonal.read_pos; + } + if (tonal.read_pos >= 200) { + tonal.read_pos -= 200; + } + curr_lookahead = Inlines.IMAX(curr_lookahead - 10, 0); + float psum = 0.0f; + for (i = 0; i < 200 - curr_lookahead; ++i) { + psum += tonal.pmusic[i]; + } + while (i < 200) { + psum += tonal.pspeech[i]; + ++i; + } + info_out.music_prob = psum = psum * tonal.music_confidence + (1.0f - psum) * tonal.speech_confidence; + } + + static void tonality_analysis(TonalityAnalysisState tonal, CeltMode celt_mode, short[] x, int x_ptr, int len, int offset, int c1, int c2, int C, int lsb_depth) { + float E; + int b; + int i; + int N = 480; + int N2 = 240; + float[] A = tonal.angle; + float[] dA = tonal.d_angle; + float[] d2A = tonal.d2_angle; + float[] band_tonality = new float[18]; + float[] logE = new float[18]; + float[] BFCC = new float[8]; + float[] features = new float[25]; + float pi4 = 97.40909f; + float slope = 0.0f; + float[] frame_probs = new float[2]; + int bandwidth = 0; + float maxE = 0.0f; + ++tonal.last_transition; + float alpha = 1.0f / (float)Inlines.IMIN(20, 1 + tonal.count); + float alphaE = 1.0f / (float)Inlines.IMIN(50, 1 + tonal.count); + float alphaE2 = 1.0f / (float)Inlines.IMIN(1000, 1 + tonal.count); + if (tonal.count < 4) { + tonal.music_prob = 0.5f; + } + FFTState kfft = celt_mode.mdct.kfft[0]; + if (tonal.count == 0) { + tonal.mem_fill = 240; + } + Downmix.downmix_int(x, x_ptr, tonal.inmem, tonal.mem_fill, Inlines.IMIN(len, 720 - tonal.mem_fill), offset, c1, c2, C); + if (tonal.mem_fill + len < 720) { + tonal.mem_fill += len; + return; + } + AnalysisInfo info = tonal.info[tonal.write_pos++]; + if (tonal.write_pos >= 200) { + tonal.write_pos -= 200; + } + int[] input = new int[960]; + int[] output = new int[960]; + float[] tonality = new float[240]; + float[] noisiness = new float[240]; + for (i = 0; i < N2; ++i) { + float w = OpusTables.analysis_window[i]; + input[2 * i] = (int)(w * (float)tonal.inmem[i]); + input[2 * i + 1] = (int)(w * (float)tonal.inmem[N2 + i]); + input[2 * (N - i - 1)] = (int)(w * (float)tonal.inmem[N - i - 1]); + input[2 * (N - i - 1) + 1] = (int)(w * (float)tonal.inmem[N + N2 - i - 1]); + } + Arrays.MemMove(tonal.inmem, 480, 0, 240); + int remaining = len - (720 - tonal.mem_fill); + Downmix.downmix_int(x, x_ptr, tonal.inmem, 240, remaining, offset + 720 - tonal.mem_fill, c1, c2, C); + tonal.mem_fill = 240 + remaining; + KissFFT.opus_fft(kfft, input, output); + for (i = 1; i < N2; ++i) { + float X1r = (float)output[2 * i] + (float)output[2 * (N - i)]; + float X1i = (float)output[2 * i + 1] - (float)output[2 * (N - i) + 1]; + float X2r = (float)output[2 * i + 1] + (float)output[2 * (N - i) + 1]; + float X2i = (float)output[2 * (N - i)] - (float)output[2 * i]; + float angle = 0.15915494f * Analysis.fast_atan2f(X1i, X1r); + float d_angle = angle - A[i]; + float d2_angle = d_angle - dA[i]; + float angle2 = 0.15915494f * Analysis.fast_atan2f(X2i, X2r); + float d_angle2 = angle2 - angle; + float d2_angle2 = d_angle2 - d_angle; + float mod1 = d2_angle - (float)Math.floor(0.5f + d2_angle); + noisiness[i] = Inlines.ABS16(mod1); + mod1 *= mod1; + mod1 *= mod1; + float mod2 = d2_angle2 - (float)Math.floor(0.5f + d2_angle2); + int n = i; + noisiness[n] = noisiness[n] + Inlines.ABS16(mod2); + mod2 *= mod2; + mod2 *= mod2; + float avg_mod = 0.25f * (d2A[i] + 2.0f * mod1 + mod2); + tonality[i] = 1.0f / (1.0f + 640.0f * pi4 * avg_mod) - 0.015f; + A[i] = angle2; + dA[i] = d_angle2; + d2A[i] = mod2; + } + float frame_tonality = 0.0f; + float max_frame_tonality = 0.0f; + info.activity = 0.0f; + float frame_noisiness = 0.0f; + float frame_stationarity = 0.0f; + if (tonal.count == 0) { + for (b = 0; b < 18; ++b) { + tonal.lowE[b] = 1.0E10f; + tonal.highE[b] = -1.0E10f; + } + } + float relativeE = 0.0f; + float frame_loudness = 0.0f; + for (b = 0; b < 18; ++b) { + E = 0.0f; + float tE = 0.0f; + float nE = 0.0f; + for (i = OpusTables.tbands[b]; i < OpusTables.tbands[b + 1]; ++i) { + float binE = (float)output[2 * i] * (float)output[2 * i] + (float)output[2 * (N - i)] * (float)output[2 * (N - i)] + (float)output[2 * i + 1] * (float)output[2 * i + 1] + (float)output[2 * (N - i) + 1] * (float)output[2 * (N - i) + 1]; + E += (binE *= 5.55E-17f); + tE += binE * tonality[i]; + nE += binE * 2.0f * (0.5f - noisiness[i]); + } + tonal.E[tonal.E_count][b] = E; + frame_noisiness += nE / (1.0E-15f + E); + frame_loudness += (float)Math.sqrt(E + 1.0E-10f); + logE[b] = (float)Math.log(E + 1.0E-10f); + tonal.lowE[b] = Inlines.MIN32(logE[b], tonal.lowE[b] + 0.01f); + tonal.highE[b] = Inlines.MAX32(logE[b], tonal.highE[b] - 0.1f); + if (tonal.highE[b] < tonal.lowE[b] + 1.0f) { + int n = b; + tonal.highE[n] = tonal.highE[n] + 0.5f; + int n2 = b; + tonal.lowE[n2] = tonal.lowE[n2] - 0.5f; + } + relativeE += (logE[b] - tonal.lowE[b]) / (1.0E-15f + tonal.highE[b] - tonal.lowE[b]); + float L2 = 0.0f; + float L1 = 0.0f; + for (i = 0; i < 8; ++i) { + L1 += (float)Math.sqrt(tonal.E[i][b]); + L2 += tonal.E[i][b]; + } + float stationarity = Inlines.MIN16(0.99f, L1 / (float)Math.sqrt(1.0E-15 + (double)(8.0f * L2))); + stationarity *= stationarity; + stationarity *= stationarity; + frame_stationarity += stationarity; + band_tonality[b] = Inlines.MAX16(tE / (1.0E-15f + E), stationarity * tonal.prev_band_tonality[b]); + frame_tonality += band_tonality[b]; + if (b >= 9) { + frame_tonality -= band_tonality[b - 18 + 9]; + } + max_frame_tonality = Inlines.MAX16(max_frame_tonality, (1.0f + 0.03f * (float)(b - 18)) * frame_tonality); + slope += band_tonality[b] * (float)(b - 8); + tonal.prev_band_tonality[b] = band_tonality[b]; + } + float bandwidth_mask = 0.0f; + bandwidth = 0; + maxE = 0.0f; + float noise_floor = 5.7E-4f / (float)(1 << Inlines.IMAX(0, lsb_depth - 8)); + noise_floor *= 1.3421773E8f; + noise_floor *= noise_floor; + for (b = 0; b < 21; ++b) { + E = 0.0f; + int band_start = OpusTables.extra_bands[b]; + int band_end = OpusTables.extra_bands[b + 1]; + for (i = band_start; i < band_end; ++i) { + float binE = (float)output[2 * i] * (float)output[2 * i] + (float)output[2 * (N - i)] * (float)output[2 * (N - i)] + (float)output[2 * i + 1] * (float)output[2 * i + 1] + (float)output[2 * (N - i) + 1] * (float)output[2 * (N - i) + 1]; + E += binE; + } + maxE = Inlines.MAX32(maxE, E); + tonal.meanE[b] = Inlines.MAX32((1.0f - alphaE2) * tonal.meanE[b], E); + if (!((double)(E = Inlines.MAX32(E, tonal.meanE[b])) > 0.1 * (double)(bandwidth_mask = Inlines.MAX32(0.05f * bandwidth_mask, E))) || !(E * 1.0E9f > maxE) || !(E > noise_floor * (float)(band_end - band_start))) continue; + bandwidth = b; + } + if (tonal.count <= 2) { + bandwidth = 20; + } + frame_loudness = 20.0f * (float)Math.log10(frame_loudness); + tonal.Etracker = Inlines.MAX32(tonal.Etracker - 0.03f, frame_loudness); + tonal.lowECount *= 1.0f - alphaE; + if (frame_loudness < tonal.Etracker - 30.0f) { + tonal.lowECount += alphaE; + } + for (i = 0; i < 8; ++i) { + float sum = 0.0f; + for (b = 0; b < 16; ++b) { + sum += OpusTables.dct_table[i * 16 + b] * logE[b]; + } + BFCC[i] = sum; + } + frame_stationarity /= 18.0f; + relativeE /= 18.0f; + if (tonal.count < 10) { + relativeE = 0.5f; + } + info.activity = (frame_noisiness /= 18.0f) + (1.0f - frame_noisiness) * relativeE; + frame_tonality = max_frame_tonality / 9.0f; + tonal.prev_tonality = frame_tonality = Inlines.MAX16(frame_tonality, tonal.prev_tonality * 0.8f); + info.tonality_slope = slope /= 64.0f; + tonal.E_count = (tonal.E_count + 1) % 8; + ++tonal.count; + info.tonality = frame_tonality; + for (i = 0; i < 4; ++i) { + features[i] = -0.12299f * (BFCC[i] + tonal.mem[i + 24]) + 0.49195f * (tonal.mem[i] + tonal.mem[i + 16]) + 0.69693f * tonal.mem[i + 8] - 1.4349f * tonal.cmean[i]; + } + for (i = 0; i < 4; ++i) { + tonal.cmean[i] = (1.0f - alpha) * tonal.cmean[i] + alpha * BFCC[i]; + } + for (i = 0; i < 4; ++i) { + features[4 + i] = 0.63246f * (BFCC[i] - tonal.mem[i + 24]) + 0.31623f * (tonal.mem[i] - tonal.mem[i + 16]); + } + for (i = 0; i < 3; ++i) { + features[8 + i] = 0.53452f * (BFCC[i] + tonal.mem[i + 24]) - 0.26726f * (tonal.mem[i] + tonal.mem[i + 16]) - 0.53452f * tonal.mem[i + 8]; + } + if (tonal.count > 5) { + for (i = 0; i < 9; ++i) { + tonal.std[i] = (1.0f - alpha) * tonal.std[i] + alpha * features[i] * features[i]; + } + } + for (i = 0; i < 8; ++i) { + tonal.mem[i + 24] = tonal.mem[i + 16]; + tonal.mem[i + 16] = tonal.mem[i + 8]; + tonal.mem[i + 8] = tonal.mem[i]; + tonal.mem[i] = BFCC[i]; + } + for (i = 0; i < 9; ++i) { + features[11 + i] = (float)Math.sqrt(tonal.std[i]); + } + features[20] = info.tonality; + features[21] = info.activity; + features[22] = frame_stationarity; + features[23] = info.tonality_slope; + features[24] = tonal.lowECount; + if (info.enabled) { + MultiLayerPerceptron.mlp_process(OpusTables.net, features, frame_probs); + frame_probs[0] = 0.5f * (frame_probs[0] + 1.0f); + frame_probs[0] = 0.01f + 1.21f * frame_probs[0] * frame_probs[0] - 0.23f * (float)Math.pow(frame_probs[0], 10.0); + frame_probs[1] = 0.5f * frame_probs[1] + 0.5f; + frame_probs[0] = frame_probs[1] * frame_probs[0] + (1.0f - frame_probs[1]) * 0.5f; + float tau = 5.0E-5f * frame_probs[1]; + float beta = 0.05f; + float p = Inlines.MAX16(0.05f, Inlines.MIN16(0.95f, frame_probs[0])); + float q = Inlines.MAX16(0.05f, Inlines.MIN16(0.95f, tonal.music_prob)); + beta = 0.01f + 0.05f * Inlines.ABS16(p - q) / (p * (1.0f - q) + q * (1.0f - p)); + float p0 = (1.0f - tonal.music_prob) * (1.0f - tau) + tonal.music_prob * tau; + float p1 = tonal.music_prob * (1.0f - tau) + (1.0f - tonal.music_prob) * tau; + info.music_prob = tonal.music_prob = (p1 *= (float)Math.pow(frame_probs[0], beta)) / ((p0 *= (float)Math.pow(1.0f - frame_probs[0], beta)) + p1); + float psum = 1.0E-20f; + float speech0 = (float)Math.pow(1.0f - frame_probs[0], beta); + float music0 = (float)Math.pow(frame_probs[0], beta); + if (tonal.count == 1) { + tonal.pspeech[0] = 0.5f; + tonal.pmusic[0] = 0.5f; + } + float s0 = tonal.pspeech[0] + tonal.pspeech[1]; + float m0 = tonal.pmusic[0] + tonal.pmusic[1]; + tonal.pspeech[0] = s0 * (1.0f - tau) * speech0; + tonal.pmusic[0] = m0 * (1.0f - tau) * music0; + for (i = 1; i < 199; ++i) { + tonal.pspeech[i] = tonal.pspeech[i + 1] * speech0; + tonal.pmusic[i] = tonal.pmusic[i + 1] * music0; + } + tonal.pspeech[199] = m0 * tau * speech0; + tonal.pmusic[199] = s0 * tau * music0; + for (i = 0; i < 200; ++i) { + psum += tonal.pspeech[i] + tonal.pmusic[i]; + } + psum = 1.0f / psum; + i = 0; + while (i < 200) { + int n = i; + tonal.pspeech[n] = tonal.pspeech[n] * psum; + int n3 = i++; + tonal.pmusic[n3] = tonal.pmusic[n3] * psum; + } + psum = tonal.pmusic[0]; + for (i = 1; i < 200; ++i) { + psum += tonal.pspeech[i]; + } + if ((double)frame_probs[1] > 0.75) { + float adapt; + if ((double)tonal.music_prob > 0.9) { + adapt = 1.0f / (float)(++tonal.music_confidence_count); + tonal.music_confidence_count = Inlines.IMIN(tonal.music_confidence_count, 500); + tonal.music_confidence += adapt * Inlines.MAX16(-0.2f, frame_probs[0] - tonal.music_confidence); + } + if ((double)tonal.music_prob < 0.1) { + adapt = 1.0f / (float)(++tonal.speech_confidence_count); + tonal.speech_confidence_count = Inlines.IMIN(tonal.speech_confidence_count, 500); + tonal.speech_confidence += adapt * Inlines.MIN16(0.2f, frame_probs[0] - tonal.speech_confidence); + } + } else { + if (tonal.music_confidence_count == 0) { + tonal.music_confidence = 0.9f; + } + if (tonal.speech_confidence_count == 0) { + tonal.speech_confidence = 0.1f; + } + } + if (tonal.last_music != (tonal.music_prob > 0.5f ? 1 : 0)) { + tonal.last_transition = 0; + } + tonal.last_music = tonal.music_prob > 0.5f ? 1 : 0; + } else { + info.music_prob = 0.0f; + } + info.bandwidth = bandwidth; + info.noisiness = frame_noisiness; + info.valid = 1; + } + + static void run_analysis(TonalityAnalysisState analysis, CeltMode celt_mode, short[] analysis_pcm, int analysis_pcm_ptr, int analysis_frame_size, int frame_size, int c1, int c2, int C, int Fs, int lsb_depth, AnalysisInfo analysis_info) { + if (analysis_pcm != null) { + analysis_frame_size = Inlines.IMIN(195 * Fs / 100, analysis_frame_size); + int pcm_len = analysis_frame_size - analysis.analysis_offset; + int offset = analysis.analysis_offset; + do { + Analysis.tonality_analysis(analysis, celt_mode, analysis_pcm, analysis_pcm_ptr, Inlines.IMIN(480, pcm_len), offset, c1, c2, C, lsb_depth); + offset += 480; + } while ((pcm_len -= 480) > 0); + analysis.analysis_offset = analysis_frame_size; + analysis.analysis_offset -= frame_size; + } + analysis_info.valid = 0; + Analysis.tonality_get_info(analysis, analysis_info, frame_size); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/AnalysisInfo.java b/source/decompiled/de/maxhenkel/voicechat/concentus/AnalysisInfo.java new file mode 100644 index 0000000..68fb9be --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/AnalysisInfo.java @@ -0,0 +1,39 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class AnalysisInfo { + boolean enabled = false; + int valid = 0; + float tonality = 0.0f; + float tonality_slope = 0.0f; + float noisiness = 0.0f; + float activity = 0.0f; + float music_prob = 0.0f; + int bandwidth = 0; + + AnalysisInfo() { + } + + void Assign(AnalysisInfo other) { + this.valid = other.valid; + this.tonality = other.tonality; + this.tonality_slope = other.tonality_slope; + this.noisiness = other.noisiness; + this.activity = other.activity; + this.music_prob = other.music_prob; + this.bandwidth = other.bandwidth; + } + + void Reset() { + this.valid = 0; + this.tonality = 0.0f; + this.tonality_slope = 0.0f; + this.noisiness = 0.0f; + this.activity = 0.0f; + this.music_prob = 0.0f; + this.bandwidth = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/ApplySineWindow.java b/source/decompiled/de/maxhenkel/voicechat/concentus/ApplySineWindow.java new file mode 100644 index 0000000..3768608 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/ApplySineWindow.java @@ -0,0 +1,46 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; + +class ApplySineWindow { + private static final short[] freq_table_Q16 = new short[]{12111, 9804, 8235, 7100, 6239, 5565, 5022, 4575, 4202, 3885, 3612, 3375, 3167, 2984, 2820, 2674, 2542, 2422, 2313, 2214, 2123, 2038, 1961, 1889, 1822, 1760, 1702}; + + ApplySineWindow() { + } + + static void silk_apply_sine_window(short[] px_win, int px_win_ptr, short[] px, int px_ptr, int win_type, int length) { + int S1_Q16; + int S0_Q16; + Inlines.OpusAssert(win_type == 1 || win_type == 2); + Inlines.OpusAssert(length >= 16 && length <= 120); + Inlines.OpusAssert((length & 3) == 0); + int k = (length >> 2) - 4; + Inlines.OpusAssert(k >= 0 && k <= 26); + short f_Q16 = freq_table_Q16[k]; + int c_Q16 = Inlines.silk_SMULWB(f_Q16, -f_Q16); + Inlines.OpusAssert(c_Q16 >= Short.MIN_VALUE); + if (win_type == 1) { + S0_Q16 = 0; + S1_Q16 = f_Q16 + Inlines.silk_RSHIFT(length, 3); + } else { + S0_Q16 = 65536; + S1_Q16 = 65536 + Inlines.silk_RSHIFT(c_Q16, 1) + Inlines.silk_RSHIFT(length, 4); + } + for (k = 0; k < length; k += 4) { + int pxwk = px_win_ptr + k; + int pxk = px_ptr + k; + px_win[pxwk] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk]); + px_win[pxwk + 1] = (short)Inlines.silk_SMULWB(S1_Q16, px[pxk + 1]); + S0_Q16 = Inlines.silk_SMULWB(S1_Q16, c_Q16) + Inlines.silk_LSHIFT(S1_Q16, 1) - S0_Q16 + 1; + S0_Q16 = Inlines.silk_min(S0_Q16, 65536); + px_win[pxwk + 2] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk + 2]); + px_win[pxwk + 3] = (short)Inlines.silk_SMULWB(S0_Q16, px[pxk + 3]); + S1_Q16 = Inlines.silk_SMULWB(S0_Q16, c_Q16) + Inlines.silk_LSHIFT(S0_Q16, 1) - S1_Q16; + S1_Q16 = Inlines.silk_min(S1_Q16, 65536); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Arrays.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Arrays.java new file mode 100644 index 0000000..5310f34 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Arrays.java @@ -0,0 +1,86 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class Arrays { + Arrays() { + } + + static int[][] InitTwoDimensionalArrayInt(int x, int y) { + return new int[x][y]; + } + + static float[][] InitTwoDimensionalArrayFloat(int x, int y) { + return new float[x][y]; + } + + static short[][] InitTwoDimensionalArrayShort(int x, int y) { + return new short[x][y]; + } + + static byte[][] InitTwoDimensionalArrayByte(int x, int y) { + return new byte[x][y]; + } + + static byte[][][] InitThreeDimensionalArrayByte(int x, int y, int z) { + return new byte[x][y][z]; + } + + static void MemSet(byte[] array, byte value) { + java.util.Arrays.fill(array, value); + } + + static void MemSet(short[] array, short value) { + java.util.Arrays.fill(array, value); + } + + static void MemSet(int[] array, int value) { + java.util.Arrays.fill(array, value); + } + + static void MemSet(float[] array, float value) { + java.util.Arrays.fill(array, value); + } + + static void MemSet(byte[] array, byte value, int length) { + java.util.Arrays.fill(array, 0, length, value); + } + + static void MemSet(short[] array, short value, int length) { + java.util.Arrays.fill(array, 0, length, value); + } + + static void MemSet(int[] array, int value, int length) { + java.util.Arrays.fill(array, 0, length, value); + } + + static void MemSet(float[] array, float value, int length) { + java.util.Arrays.fill(array, 0, length, value); + } + + static void MemSetWithOffset(byte[] array, byte value, int offset, int length) { + java.util.Arrays.fill(array, offset, offset + length, value); + } + + static void MemSetWithOffset(short[] array, short value, int offset, int length) { + java.util.Arrays.fill(array, offset, offset + length, value); + } + + static void MemSetWithOffset(int[] array, int value, int offset, int length) { + java.util.Arrays.fill(array, offset, offset + length, value); + } + + static void MemMove(byte[] array, int src_idx, int dst_idx, int length) { + System.arraycopy(array, src_idx, array, dst_idx, length); + } + + static void MemMove(short[] array, int src_idx, int dst_idx, int length) { + System.arraycopy(array, src_idx, array, dst_idx, length); + } + + static void MemMove(int[] array, int src_idx, int dst_idx, int length) { + System.arraycopy(array, src_idx, array, dst_idx, length); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Autocorrelation.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Autocorrelation.java new file mode 100644 index 0000000..42ba397 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Autocorrelation.java @@ -0,0 +1,186 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltPitchXCorr; +import de.maxhenkel.voicechat.concentus.Inlines; + +class Autocorrelation { + private static final int QC = 10; + private static final int QS = 14; + + Autocorrelation() { + } + + static void silk_autocorr(int[] results, BoxedValueInt scale, short[] inputData, int inputDataSize, int correlationCount) { + int corrCount = Inlines.silk_min_int(inputDataSize, correlationCount); + scale.Val = Autocorrelation._celt_autocorr(inputData, results, corrCount - 1, inputDataSize); + } + + static int _celt_autocorr(short[] x, int[] ac, int lag, int n) { + int shift2; + int i; + int fastN = n - lag; + short[] xx = new short[n]; + Inlines.OpusAssert(n > 0); + short[] xptr = x; + int shift = 0; + int ac0 = 1 + (n << 7); + if ((n & 1) != 0) { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9); + } + for (i = n & 1; i < n; i += 2) { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9); + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9); + } + shift = Inlines.celt_ilog2(ac0) - 30 + 10; + if ((shift /= 2) > 0) { + for (i = 0; i < n; ++i) { + xx[i] = (short)Inlines.PSHR32(xptr[i], shift); + } + xptr = xx; + } else { + shift = 0; + } + CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1); + int k = 0; + while (k <= lag) { + int d = 0; + for (i = k + fastN; i < n; ++i) { + d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]); + } + int n2 = k++; + ac[n2] = ac[n2] + d; + } + if ((shift = 2 * shift) <= 0) { + ac[0] = ac[0] + Inlines.SHL32(1, -shift); + } + if (ac[0] < 0x10000000) { + shift2 = 29 - Inlines.EC_ILOG(ac[0]); + for (i = 0; i <= lag; ++i) { + ac[i] = Inlines.SHL32(ac[i], shift2); + } + shift -= shift2; + } else if (ac[0] >= 0x20000000) { + shift2 = 1; + if (ac[0] >= 0x40000000) { + ++shift2; + } + for (i = 0; i <= lag; ++i) { + ac[i] = Inlines.SHR32(ac[i], shift2); + } + shift += shift2; + } + return shift; + } + + static int _celt_autocorr(int[] x, int[] ac, int[] window, int overlap, int lag, int n) { + int i; + int[] xptr; + int fastN = n - lag; + int[] xx = new int[n]; + Inlines.OpusAssert(n > 0); + Inlines.OpusAssert(overlap >= 0); + if (overlap == 0) { + xptr = x; + } else { + for (i = 0; i < n; ++i) { + xx[i] = x[i]; + } + for (i = 0; i < overlap; ++i) { + xx[i] = Inlines.MULT16_16_Q15(x[i], window[i]); + xx[n - i - 1] = Inlines.MULT16_16_Q15(x[n - i - 1], window[i]); + } + xptr = xx; + } + int shift = 0; + int ac0 = 1 + (n << 7); + if ((n & 1) != 0) { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9); + } + for (i = n & 1; i < n; i += 2) { + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9); + ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9); + } + shift = Inlines.celt_ilog2(ac0) - 30 + 10; + if ((shift /= 2) > 0) { + for (i = 0; i < n; ++i) { + xx[i] = Inlines.PSHR32(xptr[i], shift); + } + xptr = xx; + } else { + shift = 0; + } + CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1); + int k = 0; + while (k <= lag) { + int d = 0; + for (i = k + fastN; i < n; ++i) { + d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]); + } + int n2 = k++; + ac[n2] = ac[n2] + d; + } + if ((shift = 2 * shift) <= 0) { + ac[0] = ac[0] + Inlines.SHL32(1, -shift); + } + if (ac[0] < 0x10000000) { + int shift2 = 29 - Inlines.EC_ILOG(ac[0]); + for (i = 0; i <= lag; ++i) { + ac[i] = Inlines.SHL32(ac[i], shift2); + } + shift -= shift2; + } else if (ac[0] >= 0x20000000) { + int shift2 = 1; + if (ac[0] >= 0x40000000) { + ++shift2; + } + for (i = 0; i <= lag; ++i) { + ac[i] = Inlines.SHR32(ac[i], shift2); + } + shift += shift2; + } + return shift; + } + + static void silk_warped_autocorrelation(int[] corr, BoxedValueInt scale, short[] input, int warping_Q16, int length, int order) { + int i; + int[] state_QS = new int[17]; + long[] corr_QC = new long[17]; + Inlines.OpusAssert((order & 1) == 0); + Inlines.OpusAssert(true); + for (int n = 0; n < length; ++n) { + int tmp1_QS = Inlines.silk_LSHIFT32(input[n], 14); + for (i = 0; i < order; i += 2) { + int tmp2_QS = Inlines.silk_SMLAWB(state_QS[i], state_QS[i + 1] - tmp1_QS, warping_Q16); + state_QS[i] = tmp1_QS; + int n2 = i; + corr_QC[n2] = corr_QC[n2] + Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 18); + tmp1_QS = Inlines.silk_SMLAWB(state_QS[i + 1], state_QS[i + 2] - tmp2_QS, warping_Q16); + state_QS[i + 1] = tmp2_QS; + int n3 = i + 1; + corr_QC[n3] = corr_QC[n3] + Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp2_QS, state_QS[0]), 18); + } + state_QS[order] = tmp1_QS; + int n4 = order; + corr_QC[n4] = corr_QC[n4] + Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 18); + } + int lsh = Inlines.silk_CLZ64(corr_QC[0]) - 35; + lsh = Inlines.silk_LIMIT(lsh, -22, 20); + scale.Val = -(10 + lsh); + Inlines.OpusAssert(scale.Val >= -30 && scale.Val <= 12); + if (lsh >= 0) { + for (i = 0; i < order + 1; ++i) { + corr[i] = (int)Inlines.silk_LSHIFT64(corr_QC[i], lsh); + } + } else { + for (i = 0; i < order + 1; ++i) { + corr[i] = (int)Inlines.silk_RSHIFT64(corr_QC[i], -lsh); + } + } + Inlines.OpusAssert(corr_QC[0] >= 0L); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/BWExpander.java b/source/decompiled/de/maxhenkel/voicechat/concentus/BWExpander.java new file mode 100644 index 0000000..a0a4036 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/BWExpander.java @@ -0,0 +1,30 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; + +class BWExpander { + BWExpander() { + } + + static void silk_bwexpander_32(int[] ar, int d, int chirp_Q16) { + int chirp_minus_one_Q16 = chirp_Q16 - 65536; + for (int i = 0; i < d - 1; ++i) { + ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]); + chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16); + } + ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]); + } + + static void silk_bwexpander(short[] ar, int d, int chirp_Q16) { + int chirp_minus_one_Q16 = chirp_Q16 - 65536; + for (int i = 0; i < d - 1; ++i) { + ar[i] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[i]), 16); + chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16); + } + ar[d - 1] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[d - 1]), 16); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Bands.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Bands.java new file mode 100644 index 0000000..7b7c49d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Bands.java @@ -0,0 +1,986 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltMode; +import de.maxhenkel.voicechat.concentus.CeltTables; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Kernels; +import de.maxhenkel.voicechat.concentus.Rate; +import de.maxhenkel.voicechat.concentus.VQ; + +class Bands { + private static final byte[] bit_interleave_table = new byte[]{0, 1, 1, 1, 2, 3, 3, 3, 2, 3, 3, 3, 2, 3, 3, 3}; + private static final short[] bit_deinterleave_table = new short[]{0, 3, 12, 15, 48, 51, 60, 63, 192, 195, 204, 207, 240, 243, 252, 255}; + + Bands() { + } + + static int hysteresis_decision(int val, int[] thresholds, int[] hysteresis, int N, int prev) { + int i; + for (i = 0; i < N && val >= thresholds[i]; ++i) { + } + if (i > prev && val < thresholds[prev] + hysteresis[prev]) { + i = prev; + } + if (i < prev && val > thresholds[prev - 1] - hysteresis[prev - 1]) { + i = prev; + } + return i; + } + + static int celt_lcg_rand(int seed) { + return 1664525 * seed + 1013904223; + } + + static int bitexact_cos(int x) { + int tmp = 4096 + x * x >> 13; + Inlines.OpusAssert(tmp <= Short.MAX_VALUE); + int x2 = tmp; + x2 = Short.MAX_VALUE - x2 + Inlines.FRAC_MUL16(x2, -7651 + Inlines.FRAC_MUL16(x2, 8277 + Inlines.FRAC_MUL16(-626, x2))); + Inlines.OpusAssert(x2 <= 32766); + return 1 + x2; + } + + static int bitexact_log2tan(int isin, int icos) { + int lc = Inlines.EC_ILOG(icos); + int ls = Inlines.EC_ILOG(isin); + return (ls - lc) * 2048 + Inlines.FRAC_MUL16(isin <<= 15 - ls, Inlines.FRAC_MUL16(isin, -2597) + 7932) - Inlines.FRAC_MUL16(icos <<= 15 - lc, Inlines.FRAC_MUL16(icos, -2597) + 7932); + } + + static void compute_band_energies(CeltMode m, int[][] X, int[][] bandE, int end, int C, int LM) { + short[] eBands = m.eBands; + int N = m.shortMdctSize << LM; + int c = 0; + do { + for (int i = 0; i < end; ++i) { + int maxval = 0; + int sum = 0; + maxval = Inlines.celt_maxabs32(X[c], eBands[i] << LM, eBands[i + 1] - eBands[i] << LM); + if (maxval > 0) { + int shift = Inlines.celt_ilog2(maxval) - 14 + ((m.logN[i] >> 3) + LM + 1 >> 1); + int j = eBands[i] << LM; + if (shift > 0) { + do { + sum = Inlines.MAC16_16(sum, Inlines.EXTRACT16(Inlines.SHR32(X[c][j], shift)), Inlines.EXTRACT16(Inlines.SHR32(X[c][j], shift))); + } while (++j < eBands[i + 1] << LM); + } else { + do { + sum = Inlines.MAC16_16(sum, Inlines.EXTRACT16(Inlines.SHL32(X[c][j], -shift)), Inlines.EXTRACT16(Inlines.SHL32(X[c][j], -shift))); + } while (++j < eBands[i + 1] << LM); + } + bandE[c][i] = 1 + Inlines.VSHR32(Inlines.celt_sqrt(sum), -shift); + continue; + } + bandE[c][i] = 1; + } + } while (++c < C); + } + + static void normalise_bands(CeltMode m, int[][] freq, int[][] X, int[][] bandE, int end, int C, int M) { + short[] eBands = m.eBands; + int c = 0; + do { + int i = 0; + do { + int shift = Inlines.celt_zlog2(bandE[c][i]) - 13; + int E = Inlines.VSHR32(bandE[c][i], shift); + short g = Inlines.EXTRACT16(Inlines.celt_rcp(Inlines.SHL32(E, 3))); + int j = M * eBands[i]; + do { + X[c][j] = Inlines.MULT16_16_Q15(Inlines.VSHR32(freq[c][j], shift - 1), (int)g); + } while (++j < M * eBands[i + 1]); + } while (++i < end); + } while (++c < C); + } + + static void denormalise_bands(CeltMode m, int[] X, int[] freq, int freq_ptr, int[] bandLogE, int bandLogE_ptr, int start, int end, int M, int downsample, int silence) { + int i; + short[] eBands = m.eBands; + int N = M * m.shortMdctSize; + int bound = M * eBands[end]; + if (downsample != 1) { + bound = Inlines.IMIN(bound, N / downsample); + } + if (silence != 0) { + bound = 0; + end = 0; + start = 0; + } + int f = freq_ptr; + int x = M * eBands[start]; + for (i = 0; i < M * eBands[start]; ++i) { + freq[f++] = 0; + } + for (i = start; i < end; ++i) { + int g; + int j = M * eBands[i]; + int band_end = M * eBands[i + 1]; + int lg = Inlines.ADD16(bandLogE[bandLogE_ptr + i], (int)Inlines.SHL16(CeltTables.eMeans[i], 6)); + int shift = 16 - (lg >> 10); + if (shift > 31) { + shift = 0; + g = 0; + } else { + g = Inlines.celt_exp2_frac(lg & 0x3FF); + } + if (shift < 0) { + if (shift < -2) { + g = Short.MAX_VALUE; + shift = -2; + } + do { + freq[f] = Inlines.SHR32(Inlines.MULT16_16(X[x], g), -shift); + } while (++j < band_end); + continue; + } + do { + freq[f++] = Inlines.SHR32(Inlines.MULT16_16(X[x++], g), shift); + } while (++j < band_end); + } + Inlines.OpusAssert(start <= end); + Arrays.MemSetWithOffset(freq, 0, freq_ptr + bound, N - bound); + } + + static void anti_collapse(CeltMode m, int[][] X_, short[] collapse_masks, int LM, int C, int size, int start, int end, int[] logE, int[] prev1logE, int[] prev2logE, int[] pulses, int seed) { + for (int i = start; i < end; ++i) { + int N0 = m.eBands[i + 1] - m.eBands[i]; + Inlines.OpusAssert(pulses[i] >= 0); + int depth = Inlines.celt_udiv(1 + pulses[i], m.eBands[i + 1] - m.eBands[i]) >> LM; + int thresh32 = Inlines.SHR32(Inlines.celt_exp2(0 - Inlines.SHL16(depth, 7)), 1); + int thresh = Inlines.MULT16_32_Q15((short)16384, Inlines.MIN32(Short.MAX_VALUE, thresh32)); + int t = N0 << LM; + int shift = Inlines.celt_ilog2(t) >> 1; + t = Inlines.SHL32(t, 7 - shift << 1); + int sqrt_1 = Inlines.celt_rsqrt_norm(t); + int c = 0; + do { + int r; + boolean renormalize = false; + int prev1 = prev1logE[c * m.nbEBands + i]; + int prev2 = prev2logE[c * m.nbEBands + i]; + if (C == 1) { + prev1 = Inlines.MAX16(prev1, prev1logE[m.nbEBands + i]); + prev2 = Inlines.MAX16(prev2, prev2logE[m.nbEBands + i]); + } + int Ediff = Inlines.EXTEND32(logE[c * m.nbEBands + i]) - Inlines.EXTEND32(Inlines.MIN16(prev1, prev2)); + if ((Ediff = Inlines.MAX32(0, Ediff)) < 16384) { + int r32 = Inlines.SHR32(Inlines.celt_exp2((short)(0 - Inlines.EXTRACT16(Ediff))), 1); + r = 2 * Inlines.MIN16(16383, r32); + } else { + r = 0; + } + if (LM == 3) { + r = Inlines.MULT16_16_Q14(23170, Inlines.MIN32(23169, r)); + } + r = Inlines.SHR16(Inlines.MIN16(thresh, r), 1); + r = Inlines.SHR32(Inlines.MULT16_16_Q15(sqrt_1, r), shift); + int X = m.eBands[i] << LM; + for (int k = 0; k < 1 << LM; ++k) { + if ((collapse_masks[i * C + c] & 1 << k) != 0) continue; + int Xk = X + k; + for (int j = 0; j < N0; ++j) { + seed = Bands.celt_lcg_rand(seed); + X_[c][Xk + (j << LM)] = (seed & 0x8000) != 0 ? r : 0 - r; + } + renormalize = true; + } + if (!renormalize) continue; + VQ.renormalise_vector(X_[c], X, N0 << LM, Short.MAX_VALUE); + } while (++c < C); + } + } + + static void intensity_stereo(CeltMode m, int[] X, int X_ptr, int[] Y, int Y_ptr, int[][] bandE, int bandID, int N) { + int i = bandID; + int shift = Inlines.celt_zlog2(Inlines.MAX32(bandE[0][i], bandE[1][i])) - 13; + int left = Inlines.VSHR32(bandE[0][i], shift); + int right = Inlines.VSHR32(bandE[1][i], shift); + int norm = 1 + Inlines.celt_sqrt(1 + Inlines.MULT16_16(left, left) + Inlines.MULT16_16(right, right)); + int a1 = Inlines.DIV32_16(Inlines.SHL32(left, 14), norm); + int a2 = Inlines.DIV32_16(Inlines.SHL32(right, 14), norm); + for (int j = 0; j < N; ++j) { + int l = X[X_ptr + j]; + int r = Y[Y_ptr + j]; + X[X_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(a1, l), a2, r), 14)); + } + } + + static void stereo_split(int[] X, int X_ptr, int[] Y, int Y_ptr, int N) { + for (int j = 0; j < N; ++j) { + int l = Inlines.MULT16_16(23170, X[X_ptr + j]); + int r = Inlines.MULT16_16(23170, Y[Y_ptr + j]); + X[X_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(l, r), 15)); + Y[Y_ptr + j] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.SUB32(r, l), 15)); + } + } + + static void stereo_merge(int[] X, int X_ptr, int[] Y, int Y_ptr, int mid, int N) { + BoxedValueInt xp = new BoxedValueInt(0); + BoxedValueInt side = new BoxedValueInt(0); + Kernels.dual_inner_prod(Y, Y_ptr, X, X_ptr, Y, Y_ptr, N, xp, side); + xp.Val = Inlines.MULT16_32_Q15(mid, xp.Val); + int mid2 = Inlines.SHR16(mid, 1); + int El = Inlines.MULT16_16(mid2, mid2) + side.Val - 2 * xp.Val; + int Er = Inlines.MULT16_16(mid2, mid2) + side.Val + 2 * xp.Val; + if (Er < 161061 || El < 161061) { + System.arraycopy(X, X_ptr, Y, Y_ptr, N); + return; + } + int kl = Inlines.celt_ilog2(El) >> 1; + int kr = Inlines.celt_ilog2(Er) >> 1; + int t = Inlines.VSHR32(El, kl - 7 << 1); + int lgain = Inlines.celt_rsqrt_norm(t); + t = Inlines.VSHR32(Er, kr - 7 << 1); + int rgain = Inlines.celt_rsqrt_norm(t); + if (kl < 7) { + kl = 7; + } + if (kr < 7) { + kr = 7; + } + for (int j = 0; j < N; ++j) { + int l = Inlines.MULT16_16_P15(mid, X[X_ptr + j]); + int r = Y[Y_ptr + j]; + X[X_ptr + j] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(lgain, Inlines.SUB16(l, r)), kl + 1)); + Y[Y_ptr + j] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(rgain, Inlines.ADD16(l, r)), kr + 1)); + } + } + + static int spreading_decision(CeltMode m, int[][] X, BoxedValueInt average, int last_decision, BoxedValueInt hf_average, BoxedValueInt tapset_decision, int update_hf, int end, int C, int M) { + int sum = 0; + int nbBands = 0; + short[] eBands = m.eBands; + int hf_sum = 0; + Inlines.OpusAssert(end > 0); + if (M * (eBands[end] - eBands[end - 1]) <= 8) { + return 0; + } + int c = 0; + do { + for (int i = 0; i < end; ++i) { + int tmp = 0; + int[] tcount = new int[]{0, 0, 0}; + int[] x = X[c]; + int x_ptr = M * eBands[i]; + int N = M * (eBands[i + 1] - eBands[i]); + if (N <= 8) continue; + for (int j = x_ptr; j < N + x_ptr; ++j) { + int x2N = Inlines.MULT16_16(Inlines.MULT16_16_Q15(x[j], x[j]), N); + if (x2N < 2048) { + tcount[0] = tcount[0] + 1; + } + if (x2N < 512) { + tcount[1] = tcount[1] + 1; + } + if (x2N >= 128) continue; + tcount[2] = tcount[2] + 1; + } + if (i > m.nbEBands - 4) { + hf_sum += Inlines.celt_udiv(32 * (tcount[1] + tcount[0]), N); + } + tmp = (2 * tcount[2] >= N ? 1 : 0) + (2 * tcount[1] >= N ? 1 : 0) + (2 * tcount[0] >= N ? 1 : 0); + sum += tmp * 256; + ++nbBands; + } + } while (++c < C); + if (update_hf != 0) { + if (hf_sum != 0) { + hf_sum = Inlines.celt_udiv(hf_sum, C * (4 - m.nbEBands + end)); + } + hf_sum = hf_average.Val = hf_average.Val + hf_sum >> 1; + if (tapset_decision.Val == 2) { + hf_sum += 4; + } else if (tapset_decision.Val == 0) { + hf_sum -= 4; + } + tapset_decision.Val = hf_sum > 22 ? 2 : (hf_sum > 18 ? 1 : 0); + } + Inlines.OpusAssert(nbBands > 0); + Inlines.OpusAssert(sum >= 0); + sum = Inlines.celt_udiv(sum, nbBands); + average.Val = sum = sum + average.Val >> 1; + sum = 3 * sum + ((3 - last_decision << 7) + 64) + 2 >> 2; + int decision = sum < 80 ? 3 : (sum < 256 ? 2 : (sum < 384 ? 1 : 0)); + return decision; + } + + static void deinterleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard) { + int N = N0 * stride; + int[] tmp = new int[N]; + Inlines.OpusAssert(stride > 0); + if (hadamard != 0) { + int ordery = stride - 2; + for (int i = 0; i < stride; ++i) { + for (int j = 0; j < N0; ++j) { + tmp[CeltTables.ordery_table[ordery + i] * N0 + j] = X[j * stride + i + X_ptr]; + } + } + } else { + for (int i = 0; i < stride; ++i) { + for (int j = 0; j < N0; ++j) { + tmp[i * N0 + j] = X[j * stride + i + X_ptr]; + } + } + } + System.arraycopy(tmp, 0, X, X_ptr, N); + } + + static void interleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard) { + int N = N0 * stride; + int[] tmp = new int[N]; + if (hadamard != 0) { + int ordery = stride - 2; + for (int i = 0; i < stride; ++i) { + for (int j = 0; j < N0; ++j) { + tmp[j * stride + i] = X[CeltTables.ordery_table[ordery + i] * N0 + j + X_ptr]; + } + } + } else { + for (int i = 0; i < stride; ++i) { + for (int j = 0; j < N0; ++j) { + tmp[j * stride + i] = X[i * N0 + j + X_ptr]; + } + } + } + System.arraycopy(tmp, 0, X, X_ptr, N); + } + + static void haar1(int[] X, int X_ptr, int N0, int stride) { + N0 >>= 1; + for (int i = 0; i < stride; ++i) { + for (int j = 0; j < N0; ++j) { + int tmpidx = X_ptr + i + stride * 2 * j; + int tmp1 = Inlines.MULT16_16(23170, X[tmpidx]); + int tmp2 = Inlines.MULT16_16(23170, X[tmpidx + stride]); + X[tmpidx] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(tmp1, tmp2), 15)); + X[tmpidx + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(tmp1, tmp2), 15)); + } + } + } + + static void haar1ZeroOffset(int[] X, int N0, int stride) { + N0 >>= 1; + for (int i = 0; i < stride; ++i) { + for (int j = 0; j < N0; ++j) { + int tmpidx = i + stride * 2 * j; + int tmp1 = Inlines.MULT16_16(23170, X[tmpidx]); + int tmp2 = Inlines.MULT16_16(23170, X[tmpidx + stride]); + X[tmpidx] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(tmp1, tmp2), 15)); + X[tmpidx + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(tmp1, tmp2), 15)); + } + } + } + + static int compute_qn(int N, int b, int offset, int pulse_cap, int stereo) { + int qn; + short[] exp2_table8 = new short[]{16384, 17866, 19483, 21247, 23170, 25267, 27554, 30048}; + int N2 = 2 * N - 1; + if (stereo != 0 && N == 2) { + --N2; + } + int qb = Inlines.celt_sudiv(b + N2 * offset, N2); + qb = Inlines.IMIN(b - pulse_cap - 32, qb); + if ((qb = Inlines.IMIN(64, qb)) < 4) { + qn = 1; + } else { + qn = exp2_table8[qb & 7] >> 14 - (qb >> 3); + qn = qn + 1 >> 1 << 1; + } + Inlines.OpusAssert(qn <= 256); + return qn; + } + + static void compute_theta(band_ctx ctx, split_ctx sctx, int[] X, int X_ptr, int[] Y, int Y_ptr, int N, BoxedValueInt b, int B, int B0, int LM, int stereo, BoxedValueInt fill) { + int delta; + int iside; + int imid; + int itheta = 0; + int inv = 0; + int encode = ctx.encode; + CeltMode m = ctx.m; + int i = ctx.i; + int intensity = ctx.intensity; + EntropyCoder ec = ctx.ec; + int[][] bandE = ctx.bandE; + int pulse_cap = m.logN[i] + LM * 8; + int offset = (pulse_cap >> 1) - (stereo != 0 && N == 2 ? 16 : 4); + int qn = Bands.compute_qn(N, b.Val, offset, pulse_cap, stereo); + if (stereo != 0 && i >= intensity) { + qn = 1; + } + if (encode != 0) { + itheta = VQ.stereo_itheta(X, X_ptr, Y, Y_ptr, stereo, N); + } + int tell = ec.tell_frac(); + if (qn != 1) { + if (encode != 0) { + itheta = itheta * qn + 8192 >> 14; + } + if (stereo != 0 && N > 2) { + int p0 = 3; + int x = itheta; + int x0 = qn / 2; + long ft = Inlines.CapToUInt32(p0 * (x0 + 1) + x0); + if (encode != 0) { + ec.encode(x <= x0 ? p0 * x : x - 1 - x0 + (x0 + 1) * p0, x <= x0 ? p0 * (x + 1) : x - x0 + (x0 + 1) * p0, ft); + } else { + int fs = (int)ec.decode(ft); + x = fs < (x0 + 1) * p0 ? fs / p0 : x0 + 1 + (fs - (x0 + 1) * p0); + ec.dec_update(x <= x0 ? p0 * x : x - 1 - x0 + (x0 + 1) * p0, x <= x0 ? p0 * (x + 1) : x - x0 + (x0 + 1) * p0, ft); + itheta = x; + } + } else if (B0 > 1 || stereo != 0) { + if (encode != 0) { + ec.enc_uint(itheta, qn + 1); + } else { + itheta = (int)ec.dec_uint(qn + 1); + } + } else { + int fs = 1; + int ft = ((qn >> 1) + 1) * ((qn >> 1) + 1); + if (encode != 0) { + fs = itheta <= qn >> 1 ? itheta + 1 : qn + 1 - itheta; + int fl = itheta <= qn >> 1 ? itheta * (itheta + 1) >> 1 : ft - ((qn + 1 - itheta) * (qn + 2 - itheta) >> 1); + ec.encode(fl, fl + fs, ft); + } else { + int fl = 0; + int fm = (int)ec.decode(ft); + if (fm < (qn >> 1) * ((qn >> 1) + 1) >> 1) { + itheta = Inlines.isqrt32(8 * fm + 1) - 1 >> 1; + fs = itheta + 1; + fl = itheta * (itheta + 1) >> 1; + } else { + itheta = 2 * (qn + 1) - Inlines.isqrt32(8 * (ft - fm - 1) + 1) >> 1; + fs = qn + 1 - itheta; + fl = ft - ((qn + 1 - itheta) * (qn + 2 - itheta) >> 1); + } + ec.dec_update(fl, fl + fs, ft); + } + } + Inlines.OpusAssert(itheta >= 0); + itheta = Inlines.celt_udiv(itheta * 16384, qn); + if (encode != 0 && stereo != 0) { + if (itheta == 0) { + Bands.intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N); + } else { + Bands.stereo_split(X, X_ptr, Y, Y_ptr, N); + } + } + } else if (stereo != 0) { + if (encode != 0) { + int n = inv = itheta > 8192 ? 1 : 0; + if (inv != 0) { + for (int j = 0; j < N; ++j) { + Y[Y_ptr + j] = 0 - Y[Y_ptr + j]; + } + } + Bands.intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N); + } + if (b.Val > 16 && ctx.remaining_bits > 16) { + if (encode != 0) { + ec.enc_bit_logp(inv, 2); + } else { + inv = ec.dec_bit_logp(2L); + } + } else { + inv = 0; + } + itheta = 0; + } + int qalloc = ec.tell_frac() - tell; + b.Val -= qalloc; + if (itheta == 0) { + imid = Short.MAX_VALUE; + iside = 0; + fill.Val &= (1 << B) - 1; + delta = -16384; + } else if (itheta == 16384) { + imid = 0; + iside = Short.MAX_VALUE; + fill.Val &= (1 << B) - 1 << B; + delta = 16384; + } else { + imid = Bands.bitexact_cos((short)itheta); + iside = Bands.bitexact_cos((short)(16384 - itheta)); + delta = Inlines.FRAC_MUL16(N - 1 << 7, Bands.bitexact_log2tan(iside, imid)); + } + sctx.inv = inv; + sctx.imid = imid; + sctx.iside = iside; + sctx.delta = delta; + sctx.itheta = itheta; + sctx.qalloc = qalloc; + } + + static int quant_band_n1(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr, int b, int[] lowband_out, int lowband_out_ptr) { + boolean resynth = ctx.encode == 0; + int[] x = X; + int x_ptr = X_ptr; + int encode = ctx.encode; + EntropyCoder ec = ctx.ec; + int stereo = Y != null ? 1 : 0; + int c = 0; + do { + int sign = 0; + if (ctx.remaining_bits >= 8) { + if (encode != 0) { + sign = x[x_ptr] < 0 ? 1 : 0; + ec.enc_bits(sign, 1); + } else { + sign = ec.dec_bits(1); + } + ctx.remaining_bits -= 8; + b -= 8; + } + if (resynth) { + x[x_ptr] = sign != 0 ? -16384 : 16384; + } + x = Y; + x_ptr = Y_ptr; + } while (++c < 1 + stereo); + if (lowband_out != null) { + lowband_out[lowband_out_ptr] = Inlines.SHR16(X[X_ptr], 4); + } + return 1; + } + + static int quant_partition(band_ctx ctx, int[] X, int X_ptr, int N, int b, int B, int[] lowband, int lowband_ptr, int LM, int gain, int fill) { + int imid = 0; + int iside = 0; + int B0 = B; + int mid = 0; + int side = 0; + int cm = 0; + boolean resynth = ctx.encode == 0; + int Y = 0; + int encode = ctx.encode; + CeltMode m = ctx.m; + int i = ctx.i; + int spread = ctx.spread; + EntropyCoder ec = ctx.ec; + short[] cache = m.cache.bits; + short cache_ptr = m.cache.index[(LM + 1) * m.nbEBands + i]; + if (LM != -1 && b > cache[cache_ptr + cache[cache_ptr]] + 12 && N > 2) { + split_ctx sctx = new split_ctx(); + int next_lowband2 = 0; + Y = X_ptr + (N >>= 1); + --LM; + if (B == 1) { + fill = fill & 1 | fill << 1; + } + B = B + 1 >> 1; + BoxedValueInt boxed_b = new BoxedValueInt(b); + BoxedValueInt boxed_fill = new BoxedValueInt(fill); + Bands.compute_theta(ctx, sctx, X, X_ptr, X, Y, N, boxed_b, B, B0, LM, 0, boxed_fill); + b = boxed_b.Val; + fill = boxed_fill.Val; + imid = sctx.imid; + iside = sctx.iside; + int delta = sctx.delta; + int itheta = sctx.itheta; + int qalloc = sctx.qalloc; + mid = imid; + side = iside; + if (B0 > 1 && (itheta & 0x3FFF) != 0) { + delta = itheta > 8192 ? (delta -= delta >> 4 - LM) : Inlines.IMIN(0, delta + (N << 3 >> 5 - LM)); + } + int mbits = Inlines.IMAX(0, Inlines.IMIN(b, (b - delta) / 2)); + int sbits = b - mbits; + ctx.remaining_bits -= qalloc; + if (lowband != null) { + next_lowband2 = lowband_ptr + N; + } + int rebalance = ctx.remaining_bits; + if (mbits >= sbits) { + cm = Bands.quant_partition(ctx, X, X_ptr, N, mbits, B, lowband, lowband_ptr, LM, Inlines.MULT16_16_P15(gain, mid), fill); + if ((rebalance = mbits - (rebalance - ctx.remaining_bits)) > 24 && itheta != 0) { + sbits += rebalance - 24; + } + cm |= Bands.quant_partition(ctx, X, Y, N, sbits, B, lowband, next_lowband2, LM, Inlines.MULT16_16_P15(gain, side), fill >> B) << (B0 >> 1); + } else { + cm = Bands.quant_partition(ctx, X, Y, N, sbits, B, lowband, next_lowband2, LM, Inlines.MULT16_16_P15(gain, side), fill >> B) << (B0 >> 1); + if ((rebalance = sbits - (rebalance - ctx.remaining_bits)) > 24 && itheta != 16384) { + mbits += rebalance - 24; + } + cm |= Bands.quant_partition(ctx, X, X_ptr, N, mbits, B, lowband, lowband_ptr, LM, Inlines.MULT16_16_P15(gain, mid), fill); + } + } else { + int q = Rate.bits2pulses(m, i, LM, b); + int curr_bits = Rate.pulses2bits(m, i, LM, q); + ctx.remaining_bits -= curr_bits; + while (ctx.remaining_bits < 0 && q > 0) { + ctx.remaining_bits += curr_bits; + curr_bits = Rate.pulses2bits(m, i, LM, --q); + ctx.remaining_bits -= curr_bits; + } + if (q != 0) { + int K = Rate.get_pulses(q); + cm = encode != 0 ? VQ.alg_quant(X, X_ptr, N, K, spread, B, ec) : VQ.alg_unquant(X, X_ptr, N, K, spread, B, ec, gain); + } else if (resynth) { + int cm_mask = (1 << B) - 1; + if ((fill &= cm_mask) == 0) { + Arrays.MemSetWithOffset(X, 0, X_ptr, N); + } else { + if (lowband == null) { + for (int j = 0; j < N; ++j) { + ctx.seed = Bands.celt_lcg_rand(ctx.seed); + X[X_ptr + j] = ctx.seed >> 20; + } + cm = cm_mask; + } else { + for (int j = 0; j < N; ++j) { + ctx.seed = Bands.celt_lcg_rand(ctx.seed); + int tmp = 4; + tmp = (ctx.seed & 0x8000) != 0 ? tmp : 0 - tmp; + X[X_ptr + j] = lowband[lowband_ptr + j] + tmp; + } + cm = fill; + } + VQ.renormalise_vector(X, X_ptr, N, gain); + } + } + } + return cm; + } + + static int quant_band(band_ctx ctx, int[] X, int X_ptr, int N, int b, int B, int[] lowband, int lowband_ptr, int LM, int[] lowband_out, int lowband_out_ptr, int gain, int[] lowband_scratch, int lowband_scratch_ptr, int fill) { + int k; + int N0 = N; + int N_B = N; + int B0 = B; + int time_divide = 0; + int recombine = 0; + int cm = 0; + boolean resynth = ctx.encode == 0; + int encode = ctx.encode; + int tf_change = ctx.tf_change; + int longBlocks = B0 == 1 ? 1 : 0; + N_B = Inlines.celt_udiv(N_B, B); + if (N == 1) { + return Bands.quant_band_n1(ctx, X, X_ptr, null, 0, b, lowband_out, lowband_out_ptr); + } + if (tf_change > 0) { + recombine = tf_change; + } + if (lowband_scratch != null && lowband != null && (recombine != 0 || (N_B & 1) == 0 && tf_change < 0 || B0 > 1)) { + System.arraycopy(lowband, lowband_ptr, lowband_scratch, lowband_scratch_ptr, N); + lowband = lowband_scratch; + lowband_ptr = lowband_scratch_ptr; + } + for (k = 0; k < recombine; ++k) { + if (encode != 0) { + Bands.haar1(X, X_ptr, N >> k, 1 << k); + } + if (lowband != null) { + Bands.haar1(lowband, lowband_ptr, N >> k, 1 << k); + } + int idx1 = fill & 0xF; + int idx2 = fill >> 4; + if (idx1 < 0) { + System.out.println("e"); + } + if (idx2 < 0) { + System.out.println("e"); + } + fill = bit_interleave_table[fill & 0xF] | bit_interleave_table[fill >> 4] << 2; + } + B >>= recombine; + N_B <<= recombine; + while ((N_B & 1) == 0 && tf_change < 0) { + if (encode != 0) { + Bands.haar1(X, X_ptr, N_B, B); + } + if (lowband != null) { + Bands.haar1(lowband, lowband_ptr, N_B, B); + } + fill |= fill << B; + B <<= 1; + N_B >>= 1; + ++time_divide; + ++tf_change; + } + B0 = B; + int N_B0 = N_B; + if (B0 > 1) { + if (encode != 0) { + Bands.deinterleave_hadamard(X, X_ptr, N_B >> recombine, B0 << recombine, longBlocks); + } + if (lowband != null) { + Bands.deinterleave_hadamard(lowband, lowband_ptr, N_B >> recombine, B0 << recombine, longBlocks); + } + } + cm = Bands.quant_partition(ctx, X, X_ptr, N, b, B, lowband, lowband_ptr, LM, gain, fill); + if (resynth) { + if (B0 > 1) { + Bands.interleave_hadamard(X, X_ptr, N_B >> recombine, B0 << recombine, longBlocks); + } + N_B = N_B0; + B = B0; + for (k = 0; k < time_divide; ++k) { + cm |= cm >> (B >>= 1); + Bands.haar1(X, X_ptr, N_B <<= 1, B); + } + for (k = 0; k < recombine; ++k) { + cm = bit_deinterleave_table[cm]; + Bands.haar1(X, X_ptr, N0 >> k, 1 << k); + } + B <<= recombine; + if (lowband_out != null) { + int n = Inlines.celt_sqrt(Inlines.SHL32(N0, 22)); + for (int j = 0; j < N0; ++j) { + lowband_out[lowband_out_ptr + j] = Inlines.MULT16_16_Q15(n, X[X_ptr + j]); + } + } + cm &= (1 << B) - 1; + } + return cm; + } + + static int quant_band_stereo(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr, int N, int b, int B, int[] lowband, int lowband_ptr, int LM, int[] lowband_out, int lowband_out_ptr, int[] lowband_scratch, int lowband_scratch_ptr, int fill) { + int sbits; + int mbits; + int imid = 0; + int iside = 0; + int inv = 0; + int mid = 0; + int side = 0; + int cm = 0; + boolean resynth = ctx.encode == 0; + split_ctx sctx = new split_ctx(); + int encode = ctx.encode; + EntropyCoder ec = ctx.ec; + if (N == 1) { + return Bands.quant_band_n1(ctx, X, X_ptr, Y, Y_ptr, b, lowband_out, lowband_out_ptr); + } + int orig_fill = fill; + BoxedValueInt boxed_b = new BoxedValueInt(b); + BoxedValueInt boxed_fill = new BoxedValueInt(fill); + Bands.compute_theta(ctx, sctx, X, X_ptr, Y, Y_ptr, N, boxed_b, B, B, LM, 1, boxed_fill); + b = boxed_b.Val; + fill = boxed_fill.Val; + inv = sctx.inv; + imid = sctx.imid; + iside = sctx.iside; + int delta = sctx.delta; + int itheta = sctx.itheta; + int qalloc = sctx.qalloc; + mid = imid; + side = iside; + if (N == 2) { + int y2_ptr; + int[] y2; + int x2_ptr; + int[] x2; + int sign = 0; + mbits = b; + sbits = 0; + if (itheta != 0 && itheta != 16384) { + sbits = 8; + } + mbits -= sbits; + boolean c = itheta > 8192; + ctx.remaining_bits -= qalloc + sbits; + if (c) { + x2 = Y; + x2_ptr = Y_ptr; + y2 = X; + y2_ptr = X_ptr; + } else { + x2 = X; + x2_ptr = X_ptr; + y2 = Y; + y2_ptr = Y_ptr; + } + if (sbits != 0) { + if (encode != 0) { + sign = x2[x2_ptr] * y2[Y_ptr + 1] - x2[x2_ptr + 1] * y2[Y_ptr] < 0 ? 1 : 0; + ec.enc_bits(sign, 1); + } else { + sign = ec.dec_bits(1); + } + } + sign = 1 - 2 * sign; + cm = Bands.quant_band(ctx, x2, x2_ptr, N, mbits, B, lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, Short.MAX_VALUE, lowband_scratch, lowband_scratch_ptr, orig_fill); + y2[Y_ptr] = (0 - sign) * x2[x2_ptr + 1]; + y2[Y_ptr + 1] = sign * x2[x2_ptr]; + if (resynth) { + X[X_ptr] = Inlines.MULT16_16_Q15(mid, X[X_ptr]); + X[X_ptr + 1] = Inlines.MULT16_16_Q15(mid, X[X_ptr + 1]); + Y[Y_ptr] = Inlines.MULT16_16_Q15(side, Y[Y_ptr]); + Y[Y_ptr + 1] = Inlines.MULT16_16_Q15(side, Y[Y_ptr + 1]); + int tmp = X[X_ptr]; + X[X_ptr] = Inlines.SUB16(tmp, Y[Y_ptr]); + Y[Y_ptr] = Inlines.ADD16(tmp, Y[Y_ptr]); + tmp = X[X_ptr + 1]; + X[X_ptr + 1] = Inlines.SUB16(tmp, Y[Y_ptr + 1]); + Y[Y_ptr + 1] = Inlines.ADD16(tmp, Y[Y_ptr + 1]); + } + } else { + mbits = Inlines.IMAX(0, Inlines.IMIN(b, (b - delta) / 2)); + sbits = b - mbits; + ctx.remaining_bits -= qalloc; + int rebalance = ctx.remaining_bits; + if (mbits >= sbits) { + cm = Bands.quant_band(ctx, X, X_ptr, N, mbits, B, lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, Short.MAX_VALUE, lowband_scratch, lowband_scratch_ptr, fill); + if ((rebalance = mbits - (rebalance - ctx.remaining_bits)) > 24 && itheta != 0) { + sbits += rebalance - 24; + } + cm |= Bands.quant_band(ctx, Y, Y_ptr, N, sbits, B, null, 0, LM, null, 0, side, null, 0, fill >> B); + } else { + cm = Bands.quant_band(ctx, Y, Y_ptr, N, sbits, B, null, 0, LM, null, 0, side, null, 0, fill >> B); + if ((rebalance = sbits - (rebalance - ctx.remaining_bits)) > 24 && itheta != 16384) { + mbits += rebalance - 24; + } + cm |= Bands.quant_band(ctx, X, X_ptr, N, mbits, B, lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, Short.MAX_VALUE, lowband_scratch, lowband_scratch_ptr, fill); + } + } + if (resynth) { + if (N != 2) { + Bands.stereo_merge(X, X_ptr, Y, Y_ptr, mid, N); + } + if (inv != 0) { + for (int j = Y_ptr; j < N + Y_ptr; ++j) { + Y[j] = (short)(0 - Y[j]); + } + } + } + return cm; + } + + static void quant_all_bands(int encode, CeltMode m, int start, int end, int[] X_, int[] Y_, short[] collapse_masks, int[][] bandE, int[] pulses, int shortBlocks, int spread, int dual_stereo, int intensity, int[] tf_res, int total_bits, int balance, EntropyCoder ec, int LM, int codedBands, BoxedValueInt seed) { + short[] eBands = m.eBands; + boolean update_lowband = true; + int C = Y_ != null ? 2 : 1; + boolean resynth = encode == 0; + band_ctx ctx = new band_ctx(); + int M = 1 << LM; + int B = shortBlocks != 0 ? M : 1; + int norm_offset = M * eBands[start]; + int[] norm = new int[C * (M * eBands[m.nbEBands - 1] - norm_offset)]; + int norm2 = M * eBands[m.nbEBands - 1] - norm_offset; + int[] lowband_scratch = X_; + int lowband_scratch_ptr = M * eBands[m.nbEBands - 1]; + int lowband_offset = 0; + ctx.bandE = bandE; + ctx.ec = ec; + ctx.encode = encode; + ctx.intensity = intensity; + ctx.m = m; + ctx.seed = seed.Val; + ctx.spread = spread; + for (int i = start; i < end; ++i) { + long x_cm; + long y_cm; + int b; + int remaining_bits; + int[] Y; + int effective_lowband = -1; + int Y_ptr = 0; + int tf_change = 0; + ctx.i = i; + boolean last = i == end - 1; + int[] X = X_; + int X_ptr = M * eBands[i]; + if (Y_ != null) { + Y = Y_; + Y_ptr = M * eBands[i]; + } else { + Y = null; + } + int N = M * eBands[i + 1] - M * eBands[i]; + int tell = ec.tell_frac(); + if (i != start) { + balance -= tell; + } + ctx.remaining_bits = remaining_bits = total_bits - tell - 1; + if (i <= codedBands - 1) { + int curr_balance = Inlines.celt_sudiv(balance, Inlines.IMIN(3, codedBands - i)); + b = Inlines.IMAX(0, Inlines.IMIN(16383, Inlines.IMIN(remaining_bits + 1, pulses[i] + curr_balance))); + } else { + b = 0; + } + if (resynth && M * eBands[i] - N >= M * eBands[start] && (update_lowband || lowband_offset == 0)) { + lowband_offset = i; + } + ctx.tf_change = tf_change = tf_res[i]; + if (i >= m.effEBands) { + X = norm; + X_ptr = 0; + if (Y_ != null) { + Y = norm; + Y_ptr = 0; + } + lowband_scratch = null; + } + if (i == end - 1) { + lowband_scratch = null; + } + if (lowband_offset != 0 && (spread != 3 || B > 1 || tf_change < 0)) { + effective_lowband = Inlines.IMAX(0, M * eBands[lowband_offset] - norm_offset - N); + int fold_start = lowband_offset; + while (M * eBands[--fold_start] > effective_lowband + norm_offset) { + } + int fold_end = lowband_offset - 1; + while (M * eBands[++fold_end] < effective_lowband + norm_offset + N) { + } + y_cm = 0L; + x_cm = 0L; + int fold_i = fold_start; + do { + x_cm |= (long)collapse_masks[fold_i * C + 0]; + y_cm |= (long)collapse_masks[fold_i * C + C - 1]; + } while (++fold_i < fold_end); + } else { + x_cm = y_cm = (long)((1 << B) - 1); + } + if (dual_stereo != 0 && i == intensity) { + dual_stereo = 0; + if (resynth) { + for (int j = 0; j < M * eBands[i] - norm_offset; ++j) { + norm[j] = Inlines.HALF32(norm[j] + norm[norm2 + j]); + } + } + } + if (dual_stereo != 0) { + x_cm = Bands.quant_band(ctx, X, X_ptr, N, b / 2, B, (int[])(effective_lowband != -1 ? norm : null), effective_lowband, LM, last ? null : norm, M * eBands[i] - norm_offset, Short.MAX_VALUE, lowband_scratch, lowband_scratch_ptr, (int)x_cm); + y_cm = Bands.quant_band(ctx, Y, Y_ptr, N, b / 2, B, (int[])(effective_lowband != -1 ? norm : null), norm2 + effective_lowband, LM, last ? null : norm, norm2 + (M * eBands[i] - norm_offset), Short.MAX_VALUE, lowband_scratch, lowband_scratch_ptr, (int)y_cm); + } else { + x_cm = Y != null ? (long)Bands.quant_band_stereo(ctx, X, X_ptr, Y, Y_ptr, N, b, B, (int[])(effective_lowband != -1 ? norm : null), effective_lowband, LM, last ? null : norm, M * eBands[i] - norm_offset, lowband_scratch, lowband_scratch_ptr, (int)(x_cm | y_cm)) : (long)Bands.quant_band(ctx, X, X_ptr, N, b, B, (int[])(effective_lowband != -1 ? norm : null), effective_lowband, LM, last ? null : norm, M * eBands[i] - norm_offset, Short.MAX_VALUE, lowband_scratch, lowband_scratch_ptr, (int)(x_cm | y_cm)); + y_cm = x_cm; + } + collapse_masks[i * C + 0] = (short)(x_cm & 0xFFL); + collapse_masks[i * C + C - 1] = (short)(y_cm & 0xFFL); + balance += pulses[i] + tell; + update_lowband = b > N << 3; + } + seed.Val = ctx.seed; + } + + public static class band_ctx { + public int encode; + public CeltMode m; + public int i; + public int intensity; + public int spread; + public int tf_change; + public EntropyCoder ec; + public int remaining_bits; + public int[][] bandE; + public int seed; + } + + public static class split_ctx { + public int inv; + public int imid; + public int iside; + public int delta; + public int itheta; + public int qalloc; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueByte.java b/source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueByte.java new file mode 100644 index 0000000..a799b4e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueByte.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class BoxedValueByte { + public byte Val; + + public BoxedValueByte(byte v) { + this.Val = v; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueInt.java b/source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueInt.java new file mode 100644 index 0000000..64ae46c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueInt.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class BoxedValueInt { + public int Val; + + public BoxedValueInt(int v) { + this.Val = v; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueShort.java b/source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueShort.java new file mode 100644 index 0000000..1881674 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/BoxedValueShort.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class BoxedValueShort { + public short Val; + + public BoxedValueShort(short v) { + this.Val = v; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/BurgModified.java b/source/decompiled/de/maxhenkel/voicechat/concentus/BurgModified.java new file mode 100644 index 0000000..59ee3ab --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/BurgModified.java @@ -0,0 +1,209 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltPitchXCorr; +import de.maxhenkel.voicechat.concentus.Inlines; + +class BurgModified { + private static final int MAX_FRAME_SIZE = 384; + private static final int QA = 25; + private static final int N_BITS_HEAD_ROOM = 2; + private static final int MIN_RSHIFTS = -16; + private static final int MAX_RSHIFTS = 7; + + BurgModified() { + } + + static void silk_burg_modified(BoxedValueInt res_nrg, BoxedValueInt res_nrg_Q, int[] A_Q16, short[] x, int x_ptr, int minInvGain_Q30, int subfr_length, int nb_subfr, int D) { + int nrg; + int Atmp1; + int k; + int tmp1; + int n; + int x_offset; + int s; + int[] C_first_row = new int[16]; + int[] C_last_row = new int[16]; + int[] Af_QA = new int[16]; + int[] CAf = new int[17]; + int[] CAb = new int[17]; + int[] xcorr = new int[16]; + Inlines.OpusAssert(subfr_length * nb_subfr <= 384); + long C0_64 = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr); + int lz = Inlines.silk_CLZ64(C0_64); + int rshifts = 35 - lz; + if (rshifts > 7) { + rshifts = 7; + } + if (rshifts < -16) { + rshifts = -16; + } + int C0 = rshifts > 0 ? (int)Inlines.silk_RSHIFT64(C0_64, rshifts) : Inlines.silk_LSHIFT32((int)C0_64, -rshifts); + CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(42950, C0) + 1; + Arrays.MemSet(C_first_row, 0, 16); + if (rshifts > 0) { + for (s = 0; s < nb_subfr; ++s) { + x_offset = x_ptr + s * subfr_length; + for (n = 1; n < D + 1; ++n) { + int n2 = n - 1; + C_first_row[n2] = C_first_row[n2] + (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset + n, subfr_length - n), rshifts); + } + } + } else { + for (s = 0; s < nb_subfr; ++s) { + x_offset = x_ptr + s * subfr_length; + CeltPitchXCorr.pitch_xcorr(x, x_offset, x, x_offset + 1, xcorr, subfr_length - D, D); + for (n = 1; n < D + 1; ++n) { + int d = 0; + for (int i = n + subfr_length - D; i < subfr_length; ++i) { + d = Inlines.MAC16_16(d, x[x_offset + i], x[x_offset + i - n]); + } + int n3 = n - 1; + xcorr[n3] = xcorr[n3] + d; + } + for (n = 1; n < D + 1; ++n) { + int n4 = n - 1; + C_first_row[n4] = C_first_row[n4] + Inlines.silk_LSHIFT32(xcorr[n - 1], -rshifts); + } + } + } + System.arraycopy(C_first_row, 0, C_last_row, 0, 16); + CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(42950, C0) + 1; + int invGain_Q30 = 0x40000000; + boolean reached_max_gain = false; + for (n = 0; n < D; ++n) { + int Atmp_QA; + int tmp2; + int x2; + int x1; + if (rshifts > -2) { + for (s = 0; s < nb_subfr; ++s) { + x_offset = x_ptr + s * subfr_length; + x1 = -Inlines.silk_LSHIFT32(x[x_offset + n], 16 - rshifts); + x2 = -Inlines.silk_LSHIFT32(x[x_offset + subfr_length - n - 1], 16 - rshifts); + tmp1 = Inlines.silk_LSHIFT32(x[x_offset + n], 9); + tmp2 = Inlines.silk_LSHIFT32(x[x_offset + subfr_length - n - 1], 9); + for (k = 0; k < n; ++k) { + C_first_row[k] = Inlines.silk_SMLAWB(C_first_row[k], x1, x[x_offset + n - k - 1]); + C_last_row[k] = Inlines.silk_SMLAWB(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); + Atmp_QA = Af_QA[k]; + tmp1 = Inlines.silk_SMLAWB(tmp1, Atmp_QA, x[x_offset + n - k - 1]); + tmp2 = Inlines.silk_SMLAWB(tmp2, Atmp_QA, x[x_offset + subfr_length - n + k]); + } + tmp1 = Inlines.silk_LSHIFT32(-tmp1, 7 - rshifts); + tmp2 = Inlines.silk_LSHIFT32(-tmp2, 7 - rshifts); + for (k = 0; k <= n; ++k) { + CAf[k] = Inlines.silk_SMLAWB(CAf[k], tmp1, x[x_offset + n - k]); + CAb[k] = Inlines.silk_SMLAWB(CAb[k], tmp2, x[x_offset + subfr_length - n + k - 1]); + } + } + } else { + for (s = 0; s < nb_subfr; ++s) { + x_offset = x_ptr + s * subfr_length; + x1 = -Inlines.silk_LSHIFT32(x[x_offset + n], -rshifts); + x2 = -Inlines.silk_LSHIFT32(x[x_offset + subfr_length - n - 1], -rshifts); + tmp1 = Inlines.silk_LSHIFT32(x[x_offset + n], 17); + tmp2 = Inlines.silk_LSHIFT32(x[x_offset + subfr_length - n - 1], 17); + for (k = 0; k < n; ++k) { + C_first_row[k] = Inlines.silk_MLA(C_first_row[k], x1, x[x_offset + n - k - 1]); + C_last_row[k] = Inlines.silk_MLA(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); + Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], 8); + tmp1 = Inlines.silk_MLA(tmp1, x[x_offset + n - k - 1], Atmp1); + tmp2 = Inlines.silk_MLA(tmp2, x[x_offset + subfr_length - n + k], Atmp1); + } + tmp1 = -tmp1; + tmp2 = -tmp2; + for (k = 0; k <= n; ++k) { + CAf[k] = Inlines.silk_SMLAWW(CAf[k], tmp1, Inlines.silk_LSHIFT32(x[x_offset + n - k], -rshifts - 1)); + CAb[k] = Inlines.silk_SMLAWW(CAb[k], tmp2, Inlines.silk_LSHIFT32(x[x_offset + subfr_length - n + k - 1], -rshifts - 1)); + } + } + } + tmp1 = C_first_row[n]; + tmp2 = C_last_row[n]; + int num = 0; + nrg = Inlines.silk_ADD32(CAb[0], CAf[0]); + for (k = 0; k < n; ++k) { + Atmp_QA = Af_QA[k]; + lz = Inlines.silk_CLZ32(Inlines.silk_abs(Atmp_QA)) - 1; + lz = Inlines.silk_min(7, lz); + Atmp1 = Inlines.silk_LSHIFT32(Atmp_QA, lz); + tmp1 = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(C_last_row[n - k - 1], Atmp1), 7 - lz); + tmp2 = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(C_first_row[n - k - 1], Atmp1), 7 - lz); + num = Inlines.silk_ADD_LSHIFT32(num, Inlines.silk_SMMUL(CAb[n - k], Atmp1), 7 - lz); + nrg = Inlines.silk_ADD_LSHIFT32(nrg, Inlines.silk_SMMUL(Inlines.silk_ADD32(CAb[k + 1], CAf[k + 1]), Atmp1), 7 - lz); + } + CAf[n + 1] = tmp1; + CAb[n + 1] = tmp2; + num = Inlines.silk_ADD32(num, tmp2); + int rc_Q31 = Inlines.silk_abs(num = Inlines.silk_LSHIFT32(-num, 1)) < nrg ? Inlines.silk_DIV32_varQ(num, nrg, 31) : (num > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE); + tmp1 = 0x40000000 - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + if ((tmp1 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, tmp1), 2)) <= minInvGain_Q30) { + tmp2 = 0x40000000 - Inlines.silk_DIV32_varQ(minInvGain_Q30, invGain_Q30, 30); + rc_Q31 = Inlines.silk_SQRT_APPROX(tmp2); + rc_Q31 = Inlines.silk_RSHIFT32(rc_Q31 + Inlines.silk_DIV32(tmp2, rc_Q31), 1); + rc_Q31 = Inlines.silk_LSHIFT32(rc_Q31, 16); + if (num < 0) { + rc_Q31 = -rc_Q31; + } + invGain_Q30 = minInvGain_Q30; + reached_max_gain = true; + } else { + invGain_Q30 = tmp1; + } + for (k = 0; k < n + 1 >> 1; ++k) { + tmp1 = Af_QA[k]; + tmp2 = Af_QA[n - k - 1]; + Af_QA[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); + Af_QA[n - k - 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); + } + Af_QA[n] = Inlines.silk_RSHIFT32(rc_Q31, 6); + if (reached_max_gain) { + for (k = n + 1; k < D; ++k) { + Af_QA[k] = 0; + } + break; + } + for (k = 0; k <= n + 1; ++k) { + tmp1 = CAf[k]; + tmp2 = CAb[n - k + 1]; + CAf[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); + CAb[n - k + 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); + } + } + if (reached_max_gain) { + for (k = 0; k < D; ++k) { + A_Q16[k] = -Inlines.silk_RSHIFT_ROUND(Af_QA[k], 9); + } + if (rshifts > 0) { + for (s = 0; s < nb_subfr; ++s) { + x_offset = x_ptr + s * subfr_length; + C0 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset, D), rshifts); + } + } else { + for (s = 0; s < nb_subfr; ++s) { + x_offset = x_ptr + s * subfr_length; + C0 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, x_offset, D), -rshifts); + } + } + res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, C0), 2); + res_nrg_Q.Val = 0 - rshifts; + } else { + nrg = CAf[0]; + tmp1 = 65536; + for (k = 0; k < D; ++k) { + Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], 9); + nrg = Inlines.silk_SMLAWW(nrg, CAf[k + 1], Atmp1); + tmp1 = Inlines.silk_SMLAWW(tmp1, Atmp1, Atmp1); + A_Q16[k] = -Atmp1; + } + res_nrg.Val = Inlines.silk_SMLAWW(nrg, Inlines.silk_SMMUL(42950, C0), -tmp1); + res_nrg_Q.Val = -rshifts; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CNG.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CNG.java new file mode 100644 index 0000000..80155ba --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CNG.java @@ -0,0 +1,116 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CNGState; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.NLSF; +import de.maxhenkel.voicechat.concentus.SilkChannelDecoder; +import de.maxhenkel.voicechat.concentus.SilkDecoderControl; + +class CNG { + CNG() { + } + + static void silk_CNG_exc(int[] exc_Q10, int exc_Q10_ptr, int[] exc_buf_Q14, int Gain_Q16, int length, BoxedValueInt rand_seed) { + int exc_mask = 255; + while (exc_mask > length) { + exc_mask = Inlines.silk_RSHIFT(exc_mask, 1); + } + int seed = rand_seed.Val; + for (int i = exc_Q10_ptr; i < exc_Q10_ptr + length; ++i) { + int idx = Inlines.silk_RSHIFT(seed = Inlines.silk_RAND(seed), 24) & exc_mask; + Inlines.OpusAssert(idx >= 0); + Inlines.OpusAssert(idx <= 255); + exc_Q10[i] = (short)Inlines.silk_SAT16(Inlines.silk_SMULWW(exc_buf_Q14[idx], Gain_Q16 >> 4)); + } + rand_seed.Val = seed; + } + + static void silk_CNG_Reset(SilkChannelDecoder psDec) { + int NLSF_step_Q15 = Inlines.silk_DIV32_16(Short.MAX_VALUE, (short)(psDec.LPC_order + 1)); + int NLSF_acc_Q15 = 0; + for (int i = 0; i < psDec.LPC_order; ++i) { + psDec.sCNG.CNG_smth_NLSF_Q15[i] = (short)(NLSF_acc_Q15 += NLSF_step_Q15); + } + psDec.sCNG.CNG_smth_Gain_Q16 = 0; + psDec.sCNG.rand_seed = 3176576; + } + + static void silk_CNG(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, short[] frame, int frame_ptr, int length) { + int i; + short[] A_Q12 = new short[psDec.LPC_order]; + CNGState psCNG = psDec.sCNG; + if (psDec.fs_kHz != psCNG.fs_kHz) { + CNG.silk_CNG_Reset(psDec); + psCNG.fs_kHz = psDec.fs_kHz; + } + if (psDec.lossCnt == 0 && psDec.prevSignalType == 0) { + for (i = 0; i < psDec.LPC_order; ++i) { + int n = i; + psCNG.CNG_smth_NLSF_Q15[n] = (short)(psCNG.CNG_smth_NLSF_Q15[n] + (short)Inlines.silk_SMULWB(psDec.prevNLSF_Q15[i] - psCNG.CNG_smth_NLSF_Q15[i], 16348)); + } + int max_Gain_Q16 = 0; + int subfr = 0; + for (i = 0; i < psDec.nb_subfr; ++i) { + if (psDecCtrl.Gains_Q16[i] <= max_Gain_Q16) continue; + max_Gain_Q16 = psDecCtrl.Gains_Q16[i]; + subfr = i; + } + Arrays.MemMove(psCNG.CNG_exc_buf_Q14, 0, psDec.subfr_length, (psDec.nb_subfr - 1) * psDec.subfr_length); + for (i = 0; i < psDec.nb_subfr; ++i) { + psCNG.CNG_smth_Gain_Q16 += Inlines.silk_SMULWB(psDecCtrl.Gains_Q16[i] - psCNG.CNG_smth_Gain_Q16, 4634); + } + } + if (psDec.lossCnt != 0) { + int[] CNG_sig_Q10 = new int[length + 16]; + int gain_Q16 = Inlines.silk_SMULWW(psDec.sPLC.randScale_Q14, psDec.sPLC.prevGain_Q16[1]); + if (gain_Q16 >= 0x200000 || psCNG.CNG_smth_Gain_Q16 > 0x800000) { + gain_Q16 = Inlines.silk_SMULTT(gain_Q16, gain_Q16); + gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULTT(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5); + gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 16); + } else { + gain_Q16 = Inlines.silk_SMULWW(gain_Q16, gain_Q16); + gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULWW(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5); + gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 8); + } + BoxedValueInt boxed_rand_seed = new BoxedValueInt(psCNG.rand_seed); + CNG.silk_CNG_exc(CNG_sig_Q10, 16, psCNG.CNG_exc_buf_Q14, gain_Q16, length, boxed_rand_seed); + psCNG.rand_seed = boxed_rand_seed.Val; + NLSF.silk_NLSF2A(A_Q12, psCNG.CNG_smth_NLSF_Q15, psDec.LPC_order); + System.arraycopy(psCNG.CNG_synth_state, 0, CNG_sig_Q10, 0, 16); + for (i = 0; i < length; ++i) { + int lpci = 16 + i; + Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16); + int sum_Q6 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 1], A_Q12[0]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 2], A_Q12[1]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 3], A_Q12[2]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 4], A_Q12[3]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 5], A_Q12[4]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 6], A_Q12[5]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 7], A_Q12[6]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 8], A_Q12[7]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 9], A_Q12[8]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 10], A_Q12[9]); + if (psDec.LPC_order == 16) { + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 11], A_Q12[10]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 12], A_Q12[11]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 13], A_Q12[12]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 14], A_Q12[13]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 15], A_Q12[14]); + sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 16], A_Q12[15]); + } + CNG_sig_Q10[lpci] = Inlines.silk_ADD_LSHIFT(CNG_sig_Q10[lpci], sum_Q6, 4); + frame[frame_ptr + i] = Inlines.silk_ADD_SAT16(frame[frame_ptr + i], (short)Inlines.silk_RSHIFT_ROUND(CNG_sig_Q10[lpci], 10)); + } + System.arraycopy(CNG_sig_Q10, length, psCNG.CNG_synth_state, 0, 16); + } else { + Arrays.MemSet(psCNG.CNG_synth_state, 0, psDec.LPC_order); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CNGState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CNGState.java new file mode 100644 index 0000000..4013159 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CNGState.java @@ -0,0 +1,28 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class CNGState { + final int[] CNG_exc_buf_Q14 = new int[320]; + final short[] CNG_smth_NLSF_Q15 = new short[16]; + final int[] CNG_synth_state = new int[16]; + int CNG_smth_Gain_Q16 = 0; + int rand_seed = 0; + int fs_kHz = 0; + + CNGState() { + } + + void Reset() { + Arrays.MemSet(this.CNG_exc_buf_Q14, 0, 320); + Arrays.MemSet(this.CNG_smth_NLSF_Q15, (short)0, 16); + Arrays.MemSet(this.CNG_synth_state, 0, 16); + this.CNG_smth_Gain_Q16 = 0; + this.rand_seed = 0; + this.fs_kHz = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CWRS.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CWRS.java new file mode 100644 index 0000000..be2bc76 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CWRS.java @@ -0,0 +1,116 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.CeltTables; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; + +class CWRS { + static final int[] CELT_PVQ_U_ROW = new int[]{0, 176, 351, 525, 698, 870, 1041, 1131, 1178, 1207, 1226, 1240, 1248, 1254, 1257}; + + CWRS() { + } + + private static long CELT_PVQ_U(int _n, int _k) { + return CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[Inlines.IMIN(_n, _k)] + Inlines.IMAX(_n, _k)]; + } + + private static long CELT_PVQ_V(int _n, int _k) { + return CWRS.CELT_PVQ_U(_n, _k) + CWRS.CELT_PVQ_U(_n, _k + 1); + } + + static long icwrs(int _n, int[] _y) { + Inlines.OpusAssert(_n >= 2); + int j = _n - 1; + long i = _y[j] < 0 ? 1L : 0L; + int k = Inlines.abs(_y[j]); + do { + i += CWRS.CELT_PVQ_U(_n - --j, k); + k += Inlines.abs(_y[j]); + if (_y[j] >= 0) continue; + i += CWRS.CELT_PVQ_U(_n - j, k + 1); + } while (j > 0); + return i; + } + + static void encode_pulses(int[] _y, int _n, int _k, EntropyCoder _enc) { + Inlines.OpusAssert(_k > 0); + _enc.enc_uint(CWRS.icwrs(_n, _y), CWRS.CELT_PVQ_V(_n, _k)); + } + + static int cwrsi(int _n, int _k, long _i, int[] _y) { + short val; + int k0; + int s; + long p; + int yy = 0; + int y_ptr = 0; + Inlines.OpusAssert(_k > 0); + Inlines.OpusAssert(_n > 1); + while (_n > 2) { + long q; + if (_k >= _n) { + int row = CELT_PVQ_U_ROW[_n]; + p = CeltTables.CELT_PVQ_U_DATA[row + _k + 1]; + s = 0 - (_i >= p ? 1 : 0); + k0 = _k; + q = CeltTables.CELT_PVQ_U_DATA[row + _n]; + if (q > (_i -= Inlines.CapToUInt32(p & (long)s))) { + Inlines.OpusAssert(p > q); + _k = _n; + while ((p = CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[--_k] + _n]) > _i) { + } + } else { + p = CeltTables.CELT_PVQ_U_DATA[row + _k]; + while (p > _i) { + p = CeltTables.CELT_PVQ_U_DATA[row + --_k]; + } + } + _i -= p; + val = (short)(k0 - _k + s ^ s); + _y[y_ptr++] = val; + yy = Inlines.MAC16_16(yy, val, val); + } else { + p = CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[_k] + _n]; + q = CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[_k + 1] + _n]; + if (p <= _i && _i < q) { + _i -= p; + _y[y_ptr++] = 0; + } else { + s = 0 - (_i >= q ? 1 : 0); + _i -= Inlines.CapToUInt32(q & (long)s); + k0 = _k; + while ((p = CeltTables.CELT_PVQ_U_DATA[CELT_PVQ_U_ROW[--_k] + _n]) > _i) { + } + _i -= p; + val = (short)(k0 - _k + s ^ s); + _y[y_ptr++] = val; + yy = Inlines.MAC16_16(yy, val, val); + } + } + --_n; + } + p = 2L * (long)_k + 1L; + s = 0 - (_i >= p ? 1 : 0); + k0 = _k; + _k = (int)((_i -= Inlines.CapToUInt32(p & (long)s)) + 1L >> 1); + if (_k != 0) { + _i -= 2L * (long)_k - 1L; + } + val = (short)(k0 - _k + s ^ s); + _y[y_ptr++] = val; + yy = Inlines.MAC16_16(yy, val, val); + s = -((int)_i); + val = (short)(_k + s ^ s); + _y[y_ptr] = val; + yy = Inlines.MAC16_16(yy, val, val); + return yy; + } + + static int decode_pulses(int[] _y, int _n, int _k, EntropyCoder _dec) { + return CWRS.cwrsi(_n, _k, _dec.dec_uint(CWRS.CELT_PVQ_V(_n, _k)), _y); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CeltCommon.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltCommon.java new file mode 100644 index 0000000..b4f80d3 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltCommon.java @@ -0,0 +1,843 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.AnalysisInfo; +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Bands; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltMode; +import de.maxhenkel.voicechat.concentus.CeltTables; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Kernels; +import de.maxhenkel.voicechat.concentus.MDCT; +import de.maxhenkel.voicechat.concentus.OpusFramesize; +import de.maxhenkel.voicechat.concentus.Pitch; + +class CeltCommon { + private static final short[] inv_table = new short[]{255, 255, 156, 110, 86, 70, 59, 51, 45, 40, 37, 33, 31, 28, 26, 25, 23, 22, 21, 20, 19, 18, 17, 16, 16, 15, 15, 14, 13, 13, 12, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2}; + private static final short[][] gains = new short[][]{{10048, 7112, 4248}, {15200, 8784, 0}, {26208, 3280, 0}}; + private static final byte[][] tf_select_table = new byte[][]{{0, -1, 0, -1, 0, -1, 0, -1}, {0, -1, 0, -2, 1, 0, 1, -1}, {0, -2, 0, -3, 2, 0, 1, -1}, {0, -2, 0, -3, 3, 0, 1, -1}}; + + CeltCommon() { + } + + static int compute_vbr(CeltMode mode, AnalysisInfo analysis, int base_target, int LM, int bitrate, int lastCodedBands, int C, int intensity, int constrained_vbr, int stereo_saving, int tot_boost, int tf_estimate, int pitch_change, int maxDepth, OpusFramesize variable_duration, int lfe, int has_surround_mask, int surround_masking, int temporal_vbr) { + int nbEBands = mode.nbEBands; + short[] eBands = mode.eBands; + int coded_bands = lastCodedBands != 0 ? lastCodedBands : nbEBands; + int coded_bins = eBands[coded_bands] << LM; + if (C == 2) { + coded_bins += eBands[Inlines.IMIN(intensity, coded_bands)] << LM; + } + int target = base_target; + if (analysis.enabled && analysis.valid != 0 && (double)analysis.activity < 0.4) { + target -= (int)((float)(coded_bins << 3) * (0.4f - analysis.activity)); + } + if (C == 2) { + int coded_stereo_bands = Inlines.IMIN(intensity, coded_bands); + int coded_stereo_dof = (eBands[coded_stereo_bands] << LM) - coded_stereo_bands; + int max_frac = Inlines.DIV32_16(Inlines.MULT16_16(26214, coded_stereo_dof), coded_bins); + stereo_saving = Inlines.MIN16(stereo_saving, 256); + target -= Inlines.MIN32(Inlines.MULT16_32_Q15(max_frac, target), Inlines.SHR32(Inlines.MULT16_16(stereo_saving - 26, coded_stereo_dof << 3), 8)); + } + target += tot_boost - (16 << LM); + int tf_calibration = variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE ? 328 : 655; + target += Inlines.SHL32(Inlines.MULT16_32_Q15(tf_estimate - tf_calibration, target), 1); + if (analysis.enabled && analysis.valid != 0 && lfe == 0) { + float tonal = Inlines.MAX16(0.0f, analysis.tonality - 0.15f) - 0.09f; + int tonal_target = target + (int)((float)(coded_bins << 3) * 1.2f * tonal); + if (pitch_change != 0) { + tonal_target += (int)((float)(coded_bins << 3) * 0.8f); + } + target = tonal_target; + } + if (has_surround_mask != 0 && lfe == 0) { + int surround_target = target + Inlines.SHR32(Inlines.MULT16_16(surround_masking, coded_bins << 3), 10); + target = Inlines.IMAX(target / 4, surround_target); + } + int bins = eBands[nbEBands - 2] << LM; + int floor_depth = Inlines.SHR32(Inlines.MULT16_16(C * bins << 3, maxDepth), 10); + floor_depth = Inlines.IMAX(floor_depth, target >> 2); + target = Inlines.IMIN(target, floor_depth); + if (!(has_surround_mask != 0 && lfe == 0 || constrained_vbr == 0 && bitrate >= 64000)) { + int rate_factor = Inlines.MAX16(0, bitrate - 32000); + if (constrained_vbr != 0) { + rate_factor = Inlines.MIN16(rate_factor, 21955); + } + target = base_target + Inlines.MULT16_32_Q15(rate_factor, target - base_target); + } + if (has_surround_mask == 0 && tf_estimate < 3277) { + int amount = Inlines.MULT16_16_Q15(3329, Inlines.IMAX(0, Inlines.IMIN(32000, 96000 - bitrate))); + int tvbr_factor = Inlines.SHR32(Inlines.MULT16_16(temporal_vbr, amount), 10); + target += Inlines.MULT16_32_Q15(tvbr_factor, target); + } + target = Inlines.IMIN(2 * base_target, target); + return target; + } + + static int transient_analysis(int[][] input, int len, int C, BoxedValueInt tf_estimate, BoxedValueInt tf_chan) { + int is_transient = 0; + int mask_metric = 0; + tf_chan.Val = 0; + int[] tmp = new int[len]; + int len2 = len / 2; + for (int c = 0; c < C; ++c) { + int i; + int unmask = 0; + int mem0 = 0; + int mem1 = 0; + for (i = 0; i < len; ++i) { + int x = Inlines.SHR32(input[c][i], 12); + int y = Inlines.ADD32(mem0, x); + mem0 = mem1 + y - Inlines.SHL32(x, 1); + mem1 = x - Inlines.SHR32(y, 1); + tmp[i] = Inlines.EXTRACT16(Inlines.SHR32(y, 2)); + } + Arrays.MemSet(tmp, 0, 12); + int shift = 0; + shift = 14 - Inlines.celt_ilog2(1 + Inlines.celt_maxabs32(tmp, 0, len)); + if (shift != 0) { + for (i = 0; i < len; ++i) { + tmp[i] = Inlines.SHL16(tmp[i], shift); + } + } + int mean = 0; + mem0 = 0; + for (i = 0; i < len2; ++i) { + int x2 = Inlines.PSHR32(Inlines.MULT16_16(tmp[2 * i], tmp[2 * i]) + Inlines.MULT16_16(tmp[2 * i + 1], tmp[2 * i + 1]), 16); + mean += x2; + tmp[i] = mem0 + Inlines.PSHR32(x2 - mem0, 4); + mem0 = tmp[i]; + } + mem0 = 0; + int maxE = 0; + for (i = len2 - 1; i >= 0; --i) { + tmp[i] = mem0 + Inlines.PSHR32(tmp[i] - mem0, 3); + mem0 = tmp[i]; + maxE = Inlines.MAX16(maxE, mem0); + } + mean = Inlines.MULT16_16(Inlines.celt_sqrt(mean), Inlines.celt_sqrt(Inlines.MULT16_16(maxE, len2 >> 1))); + int norm = Inlines.SHL32(len2, 20) / Inlines.ADD32(1, Inlines.SHR32(mean, 1)); + unmask = 0; + for (i = 12; i < len2 - 5; i += 4) { + int id = Inlines.MAX32(0, Inlines.MIN32(127, Inlines.MULT16_32_Q15(tmp[i] + 1, norm))); + unmask += inv_table[id]; + } + if ((unmask = 64 * unmask * 4 / (6 * (len2 - 17))) <= mask_metric) continue; + tf_chan.Val = c; + mask_metric = unmask; + } + is_transient = mask_metric > 200 ? 1 : 0; + int tf_max = Inlines.MAX16(0, Inlines.celt_sqrt(27 * mask_metric) - 42); + tf_estimate.Val = Inlines.celt_sqrt(Inlines.MAX32(0, Inlines.SHL32(Inlines.MULT16_16(113, Inlines.MIN16(163, tf_max)), 14) - 37312528)); + return is_transient; + } + + static int patch_transient_decision(int[][] newE, int[][] oldE, int nbEBands, int start, int end, int C) { + int i; + int mean_diff = 0; + int[] spread_old = new int[26]; + if (C == 1) { + spread_old[start] = oldE[0][start]; + for (i = start + 1; i < end; ++i) { + spread_old[i] = Inlines.MAX16(spread_old[i - 1] - 1024, oldE[0][i]); + } + } else { + spread_old[start] = Inlines.MAX16(oldE[0][start], oldE[1][start]); + for (i = start + 1; i < end; ++i) { + spread_old[i] = Inlines.MAX16(spread_old[i - 1] - 1024, Inlines.MAX16(oldE[0][i], oldE[1][i])); + } + } + for (i = end - 2; i >= start; --i) { + spread_old[i] = Inlines.MAX16(spread_old[i], spread_old[i + 1] - 1024); + } + int c = 0; + do { + for (i = Inlines.IMAX(2, start); i < end - 1; ++i) { + int x1 = Inlines.MAX16(0, newE[c][i]); + int x2 = Inlines.MAX16(0, spread_old[i]); + mean_diff = Inlines.ADD32(mean_diff, Inlines.MAX16(0, Inlines.SUB16(x1, x2))); + } + } while (++c < C); + return (mean_diff = Inlines.DIV32(mean_diff, C * (end - 1 - Inlines.IMAX(2, start)))) > 1024 ? 1 : 0; + } + + static void compute_mdcts(CeltMode mode, int shortBlocks, int[][] input, int[][] output, int C, int CC, int LM, int upsample) { + int i; + int shift; + int N; + int B; + int overlap = mode.overlap; + if (shortBlocks != 0) { + B = shortBlocks; + N = mode.shortMdctSize; + shift = mode.maxLM; + } else { + B = 1; + N = mode.shortMdctSize << LM; + shift = mode.maxLM - LM; + } + int c = 0; + do { + for (int b = 0; b < B; ++b) { + MDCT.clt_mdct_forward(mode.mdct, input[c], b * N, output[c], b, mode.window, overlap, shift, B); + } + } while (++c < CC); + if (CC == 2 && C == 1) { + for (i = 0; i < B * N; ++i) { + output[0][i] = Inlines.ADD32(Inlines.HALF32(output[0][i]), Inlines.HALF32(output[1][i])); + } + } + if (upsample != 1) { + c = 0; + do { + int bound = B * N / upsample; + i = 0; + while (i < bound) { + int[] nArray = output[c]; + int n = i++; + nArray[n] = nArray[n] * upsample; + } + Arrays.MemSetWithOffset(output[c], 0, bound, B * N - bound); + } while (++c < C); + } + } + + static void celt_preemphasis(short[] pcmp, int pcmp_ptr, int[] inp, int inp_ptr, int N, int CC, int upsample, int[] coef, BoxedValueInt mem, int clip) { + int i; + int coef0 = coef[0]; + int m = mem.Val; + if (coef[1] == 0 && upsample == 1 && clip == 0) { + for (int i2 = 0; i2 < N; ++i2) { + short x = pcmp[pcmp_ptr + CC * i2]; + inp[inp_ptr + i2] = Inlines.SHL32(x, 12) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, (int)x), 3); + } + mem.Val = m; + return; + } + int Nu = N / upsample; + if (upsample != 1) { + Arrays.MemSetWithOffset(inp, 0, inp_ptr, N); + } + for (i = 0; i < Nu; ++i) { + inp[inp_ptr + i * upsample] = pcmp[pcmp_ptr + CC * i]; + } + for (i = 0; i < N; ++i) { + int x = inp[inp_ptr + i]; + inp[inp_ptr + i] = Inlines.SHL32(x, 12) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 3); + } + mem.Val = m; + } + + static void celt_preemphasis(short[] pcmp, int[] inp, int inp_ptr, int N, int CC, int upsample, int[] coef, BoxedValueInt mem, int clip) { + int i; + int coef0 = coef[0]; + int m = mem.Val; + if (coef[1] == 0 && upsample == 1 && clip == 0) { + for (int i2 = 0; i2 < N; ++i2) { + short x = pcmp[CC * i2]; + inp[inp_ptr + i2] = Inlines.SHL32(x, 12) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, (int)x), 3); + } + mem.Val = m; + return; + } + int Nu = N / upsample; + if (upsample != 1) { + Arrays.MemSetWithOffset(inp, 0, inp_ptr, N); + } + for (i = 0; i < Nu; ++i) { + inp[inp_ptr + i * upsample] = pcmp[CC * i]; + } + for (i = 0; i < N; ++i) { + int x = inp[inp_ptr + i]; + inp[inp_ptr + i] = Inlines.SHL32(x, 12) - m; + m = Inlines.SHR32(Inlines.MULT16_16(coef0, x), 3); + } + mem.Val = m; + } + + static int l1_metric(int[] tmp, int N, int LM, int bias) { + int L1 = 0; + for (int i = 0; i < N; ++i) { + L1 += Inlines.EXTEND32(Inlines.ABS32(tmp[i])); + } + L1 = Inlines.MAC16_32_Q15(L1, LM * bias, L1); + return L1; + } + + static int tf_analysis(CeltMode m, int len, int isTransient, int[] tf_res, int lambda, int[][] X, int N0, int LM, BoxedValueInt tf_sum, int tf_estimate, int tf_chan) { + int curr1; + int curr0; + int cost1; + int cost0; + int i; + int[] selcost = new int[2]; + int tf_select = 0; + int bias = Inlines.MULT16_16_Q14(1311, Inlines.MAX16(-4096, 8192 - tf_estimate)); + int[] metric = new int[len]; + int[] tmp = new int[m.eBands[len] - m.eBands[len - 1] << LM]; + int[] tmp_1 = new int[m.eBands[len] - m.eBands[len - 1] << LM]; + int[] path0 = new int[len]; + int[] path1 = new int[len]; + tf_sum.Val = 0; + for (i = 0; i < len; ++i) { + int L1; + int best_level = 0; + int N = m.eBands[i + 1] - m.eBands[i] << LM; + boolean narrow = m.eBands[i + 1] - m.eBands[i] == 1; + System.arraycopy(X[tf_chan], m.eBands[i] << LM, tmp, 0, N); + int best_L1 = L1 = CeltCommon.l1_metric(tmp, N, isTransient != 0 ? LM : 0, bias); + if (isTransient != 0 && !narrow) { + System.arraycopy(tmp, 0, tmp_1, 0, N); + Bands.haar1ZeroOffset(tmp_1, N >> LM, 1 << LM); + L1 = CeltCommon.l1_metric(tmp_1, N, LM + 1, bias); + if (L1 < best_L1) { + best_L1 = L1; + best_level = -1; + } + } + for (int k = 0; k < LM + (isTransient == 0 && !narrow ? 1 : 0); ++k) { + int B = isTransient != 0 ? LM - k - 1 : k + 1; + Bands.haar1ZeroOffset(tmp, N >> k, 1 << k); + L1 = CeltCommon.l1_metric(tmp, N, B, bias); + if (L1 >= best_L1) continue; + best_L1 = L1; + best_level = k + 1; + } + metric[i] = isTransient != 0 ? 2 * best_level : -2 * best_level; + tf_sum.Val = tf_sum.Val + ((isTransient != 0 ? LM : 0) - metric[i] / 2); + if (!narrow || metric[i] != 0 && metric[i] != -2 * LM) continue; + int n = i; + metric[n] = metric[n] - 1; + } + tf_select = 0; + for (int sel = 0; sel < 2; ++sel) { + cost0 = 0; + cost1 = isTransient != 0 ? 0 : lambda; + for (i = 1; i < len; ++i) { + curr0 = Inlines.IMIN(cost0, cost1 + lambda); + curr1 = Inlines.IMIN(cost0 + lambda, cost1); + cost0 = curr0 + Inlines.abs(metric[i] - 2 * CeltTables.tf_select_table[LM][4 * isTransient + 2 * sel + 0]); + cost1 = curr1 + Inlines.abs(metric[i] - 2 * CeltTables.tf_select_table[LM][4 * isTransient + 2 * sel + 1]); + } + selcost[sel] = cost0 = Inlines.IMIN(cost0, cost1); + } + if (selcost[1] < selcost[0] && isTransient != 0) { + tf_select = 1; + } + cost0 = 0; + cost1 = isTransient != 0 ? 0 : lambda; + for (i = 1; i < len; ++i) { + int from0 = cost0; + int from1 = cost1 + lambda; + if (from0 < from1) { + curr0 = from0; + path0[i] = 0; + } else { + curr0 = from1; + path0[i] = 1; + } + from0 = cost0 + lambda; + from1 = cost1; + if (from0 < from1) { + curr1 = from0; + path1[i] = 0; + } else { + curr1 = from1; + path1[i] = 1; + } + cost0 = curr0 + Inlines.abs(metric[i] - 2 * CeltTables.tf_select_table[LM][4 * isTransient + 2 * tf_select + 0]); + cost1 = curr1 + Inlines.abs(metric[i] - 2 * CeltTables.tf_select_table[LM][4 * isTransient + 2 * tf_select + 1]); + } + tf_res[len - 1] = cost0 < cost1 ? 0 : 1; + for (i = len - 2; i >= 0; --i) { + tf_res[i] = tf_res[i + 1] == 1 ? path1[i + 1] : path0[i + 1]; + } + return tf_select; + } + + static void tf_encode(int start, int end, int isTransient, int[] tf_res, int LM, int tf_select, EntropyCoder enc) { + int i; + int budget = enc.storage * 8; + int tell = enc.tell(); + int logp = isTransient != 0 ? 2 : 4; + int tf_select_rsv = LM > 0 && tell + logp + 1 <= budget ? 1 : 0; + budget -= tf_select_rsv; + int tf_changed = 0; + int curr = 0; + for (i = start; i < end; ++i) { + if (tell + logp <= budget) { + enc.enc_bit_logp(tf_res[i] ^ curr, logp); + tell = enc.tell(); + curr = tf_res[i]; + tf_changed |= curr; + } else { + tf_res[i] = curr; + } + logp = isTransient != 0 ? 4 : 5; + } + if (tf_select_rsv != 0 && CeltTables.tf_select_table[LM][4 * isTransient + 0 + tf_changed] != CeltTables.tf_select_table[LM][4 * isTransient + 2 + tf_changed]) { + enc.enc_bit_logp(tf_select, 1); + } else { + tf_select = 0; + } + for (i = start; i < end; ++i) { + tf_res[i] = CeltTables.tf_select_table[LM][4 * isTransient + 2 * tf_select + tf_res[i]]; + } + } + + static int alloc_trim_analysis(CeltMode m, int[][] X, int[][] bandLogE, int end, int LM, int C, AnalysisInfo analysis, BoxedValueInt stereo_saving, int tf_estimate, int intensity, int surround_trim) { + int i; + int diff = 0; + int trim = 1280; + if (C == 2) { + int partial; + int sum = 0; + for (i = 0; i < 8; ++i) { + partial = Kernels.celt_inner_prod(X[0], m.eBands[i] << LM, X[1], m.eBands[i] << LM, m.eBands[i + 1] - m.eBands[i] << LM); + sum = Inlines.ADD16(sum, (int)Inlines.EXTRACT16(Inlines.SHR32(partial, 18))); + } + sum = Inlines.MULT16_16_Q15(4096, sum); + int minXC = sum = Inlines.MIN16(1024, Inlines.ABS32(sum)); + for (i = 8; i < intensity; ++i) { + partial = Kernels.celt_inner_prod(X[0], m.eBands[i] << LM, X[1], m.eBands[i] << LM, m.eBands[i + 1] - m.eBands[i] << LM); + minXC = Inlines.MIN16(minXC, (int)Inlines.ABS16(Inlines.EXTRACT16(Inlines.SHR32(partial, 18)))); + } + minXC = Inlines.MIN16(1024, Inlines.ABS32(minXC)); + int logXC = Inlines.celt_log2(1049625 - Inlines.MULT16_16(sum, sum)); + int logXC2 = Inlines.MAX16(Inlines.HALF16(logXC), Inlines.celt_log2(1049625 - Inlines.MULT16_16(minXC, minXC))); + logXC = Inlines.PSHR32(logXC - 6144, 2); + logXC2 = Inlines.PSHR32(logXC2 - 6144, 2); + trim += Inlines.MAX16(-1024, Inlines.MULT16_16_Q15(24576, logXC)); + stereo_saving.Val = Inlines.MIN16(stereo_saving.Val + 64, 0 - Inlines.HALF16(logXC2)); + } + int c = 0; + do { + for (i = 0; i < end - 1; ++i) { + diff += bandLogE[c][i] * (2 + 2 * i - end); + } + } while (++c < C); + trim -= Inlines.MAX16((int)Inlines.NEG16((short)512), Inlines.MIN16(512, Inlines.SHR16((diff /= C * (end - 1)) + 1024, 2) / 6)); + trim -= Inlines.SHR16(surround_trim, 2); + trim -= 2 * Inlines.SHR16(tf_estimate, 6); + if (analysis.enabled && analysis.valid != 0) { + trim -= Inlines.MAX16(-512, Inlines.MIN16(512, (int)(512.0f * (analysis.tonality_slope + 0.05f)))); + } + int trim_index = Inlines.PSHR32(trim, 8); + trim_index = Inlines.IMAX(0, Inlines.IMIN(10, trim_index)); + return trim_index; + } + + static int stereo_analysis(CeltMode m, int[][] X, int LM) { + int sumLR = 1; + int sumMS = 1; + for (int i = 0; i < 13; ++i) { + for (int j = m.eBands[i] << LM; j < m.eBands[i + 1] << LM; ++j) { + int L = Inlines.EXTEND32(X[0][j]); + int R = Inlines.EXTEND32(X[1][j]); + int M = Inlines.ADD32(L, R); + int S = Inlines.SUB32(L, R); + sumLR = Inlines.ADD32(sumLR, Inlines.ADD32(Inlines.ABS32(L), Inlines.ABS32(R))); + sumMS = Inlines.ADD32(sumMS, Inlines.ADD32(Inlines.ABS32(M), Inlines.ABS32(S))); + } + } + sumMS = Inlines.MULT16_32_Q15((short)23170, sumMS); + int thetas = 13; + if (LM <= 1) { + thetas -= 8; + } + return Inlines.MULT16_32_Q15((m.eBands[13] << LM + 1) + thetas, sumMS) > Inlines.MULT16_32_Q15(m.eBands[13] << LM + 1, sumLR) ? 1 : 0; + } + + static int median_of_5(int[] x, int x_ptr) { + int t4; + int t3; + int t1; + int t0; + int t2 = x[x_ptr + 2]; + if (x[x_ptr] > x[x_ptr + 1]) { + t0 = x[x_ptr + 1]; + t1 = x[x_ptr]; + } else { + t0 = x[x_ptr]; + t1 = x[x_ptr + 1]; + } + if (x[x_ptr + 3] > x[x_ptr + 4]) { + t3 = x[x_ptr + 4]; + t4 = x[x_ptr + 3]; + } else { + t3 = x[x_ptr + 3]; + t4 = x[x_ptr + 4]; + } + if (t0 > t3) { + int tmp = t3; + t3 = t0; + t0 = tmp; + tmp = t4; + t4 = t1; + t1 = tmp; + } + if (t2 > t1) { + if (t1 < t3) { + return Inlines.MIN16(t2, t3); + } + return Inlines.MIN16(t4, t1); + } + if (t2 < t3) { + return Inlines.MIN16(t1, t3); + } + return Inlines.MIN16(t2, t4); + } + + static int median_of_3(int[] x, int x_ptr) { + int t1; + int t0; + if (x[x_ptr] > x[x_ptr + 1]) { + t0 = x[x_ptr + 1]; + t1 = x[x_ptr]; + } else { + t0 = x[x_ptr]; + t1 = x[x_ptr + 1]; + } + int t2 = x[x_ptr + 2]; + if (t1 < t2) { + return t1; + } + if (t0 < t2) { + return t2; + } + return t0; + } + + static int dynalloc_analysis(int[][] bandLogE, int[][] bandLogE2, int nbEBands, int start, int end, int C, int[] offsets, int lsb_depth, short[] logN, int isTransient, int vbr, int constrained_vbr, short[] eBands, int LM, int effectiveBytes, BoxedValueInt tot_boost_, int lfe, int[] surround_dynalloc) { + int i; + int tot_boost = 0; + int[][] follower = Arrays.InitTwoDimensionalArrayInt(2, nbEBands); + int[] noise_floor = new int[C * nbEBands]; + Arrays.MemSet(offsets, 0, nbEBands); + int maxDepth = -32666; + for (i = 0; i < end; ++i) { + noise_floor[i] = Inlines.MULT16_16((short)64, logN[i]) + 512 + Inlines.SHL16(9 - lsb_depth, 10) - Inlines.SHL16(CeltTables.eMeans[i], 6) + Inlines.MULT16_16(6, (i + 5) * (i + 5)); + } + int c = 0; + do { + for (i = 0; i < end; ++i) { + maxDepth = Inlines.MAX16(maxDepth, bandLogE[c][i] - noise_floor[i]); + } + } while (++c < C); + if (effectiveBytes > 50 && LM >= 1 && lfe == 0) { + int last = 0; + c = 0; + do { + int[] f = follower[c]; + f[0] = bandLogE2[c][0]; + for (i = 1; i < end; ++i) { + if (bandLogE2[c][i] > bandLogE2[c][i - 1] + 512) { + last = i; + } + f[i] = Inlines.MIN16(f[i - 1] + 1536, bandLogE2[c][i]); + } + for (i = last - 1; i >= 0; --i) { + f[i] = Inlines.MIN16(f[i], Inlines.MIN16(f[i + 1] + 2048, bandLogE2[c][i])); + } + int offset = 1024; + for (i = 2; i < end - 2; ++i) { + f[i] = Inlines.MAX16(f[i], CeltCommon.median_of_5(bandLogE2[c], i - 2) - offset); + } + int tmp = CeltCommon.median_of_3(bandLogE2[c], 0) - offset; + f[0] = Inlines.MAX16(f[0], tmp); + f[1] = Inlines.MAX16(f[1], tmp); + tmp = CeltCommon.median_of_3(bandLogE2[c], end - 3) - offset; + f[end - 2] = Inlines.MAX16(f[end - 2], tmp); + f[end - 1] = Inlines.MAX16(f[end - 1], tmp); + for (i = 0; i < end; ++i) { + f[i] = Inlines.MAX16(f[i], noise_floor[i]); + } + } while (++c < C); + if (C == 2) { + for (i = start; i < end; ++i) { + follower[1][i] = Inlines.MAX16(follower[1][i], follower[0][i] - 4096); + follower[0][i] = Inlines.MAX16(follower[0][i], follower[1][i] - 4096); + follower[0][i] = Inlines.HALF16(Inlines.MAX16(0, bandLogE[0][i] - follower[0][i]) + Inlines.MAX16(0, bandLogE[1][i] - follower[1][i])); + } + } else { + for (i = start; i < end; ++i) { + follower[0][i] = Inlines.MAX16(0, bandLogE[0][i] - follower[0][i]); + } + } + for (i = start; i < end; ++i) { + follower[0][i] = Inlines.MAX16(follower[0][i], surround_dynalloc[i]); + } + if ((vbr == 0 || constrained_vbr != 0) && isTransient == 0) { + for (i = start; i < end; ++i) { + follower[0][i] = Inlines.HALF16(follower[0][i]); + } + } + for (i = start; i < end; ++i) { + int boost_bits; + int boost; + if (i < 8) { + int[] nArray = follower[0]; + int n = i; + nArray[n] = nArray[n] * 2; + } + if (i >= 12) { + follower[0][i] = Inlines.HALF16(follower[0][i]); + } + follower[0][i] = Inlines.MIN16(follower[0][i], 4096); + int width = C * (eBands[i + 1] - eBands[i]) << LM; + if (width < 6) { + boost = Inlines.SHR32(follower[0][i], 10); + boost_bits = boost * width << 3; + } else if (width > 48) { + boost = Inlines.SHR32(follower[0][i] * 8, 10); + boost_bits = (boost * width << 3) / 8; + } else { + boost = Inlines.SHR32(follower[0][i] * width / 6, 10); + boost_bits = boost * 6 << 3; + } + if ((vbr == 0 || constrained_vbr != 0 && isTransient == 0) && tot_boost + boost_bits >> 3 >> 3 > effectiveBytes / 4) { + int cap = effectiveBytes / 4 << 3 << 3; + offsets[i] = cap - tot_boost; + tot_boost = cap; + break; + } + offsets[i] = boost; + tot_boost += boost_bits; + } + } + tot_boost_.Val = tot_boost; + return maxDepth; + } + + static void deemphasis(int[][] input, int[] input_ptrs, short[] pcm, int pcm_ptr, int N, int C, int downsample, int[] coef, int[] mem, int accum) { + boolean apply_downsampling = false; + int[] scratch = new int[N]; + int coef0 = coef[0]; + int Nd = N / downsample; + int c = 0; + do { + int tmp; + int j; + int m = mem[c]; + int[] x = input[c]; + int x_ptr = input_ptrs[c]; + int y = pcm_ptr + c; + if (downsample > 1) { + for (j = 0; j < N; ++j) { + tmp = x[x_ptr + j] + m + 0; + m = Inlines.MULT16_32_Q15(coef0, tmp); + scratch[j] = tmp; + } + apply_downsampling = true; + } else if (accum != 0) { + for (j = 0; j < N; ++j) { + tmp = x[x_ptr + j] + m + 0; + m = Inlines.MULT16_32_Q15(coef0, tmp); + pcm[y + j * C] = Inlines.SAT16(Inlines.ADD32(pcm[y + j * C], Inlines.SIG2WORD16(tmp))); + } + } else { + for (j = 0; j < N; ++j) { + tmp = x[x_ptr + j] + m + 0; + if (x[x_ptr + j] > 0 && m > 0 && tmp < 0) { + tmp = Integer.MAX_VALUE; + m = Integer.MAX_VALUE; + } else { + m = Inlines.MULT16_32_Q15(coef0, tmp); + } + pcm[y + j * C] = Inlines.SIG2WORD16(tmp); + } + } + mem[c] = m; + if (!apply_downsampling) continue; + for (j = 0; j < Nd; ++j) { + pcm[y + j * C] = Inlines.SIG2WORD16(scratch[j * downsample]); + } + } while (++c < C); + } + + static void celt_synthesis(CeltMode mode, int[][] X, int[][] out_syn, int[] out_syn_ptrs, int[] oldBandE, int start, int effEnd, int C, int CC, int isTransient, int LM, int downsample, int silence) { + int shift; + int NB; + int B; + int overlap = mode.overlap; + int nbEBands = mode.nbEBands; + int N = mode.shortMdctSize << LM; + int[] freq = new int[N]; + int M = 1 << LM; + if (isTransient != 0) { + B = M; + NB = mode.shortMdctSize; + shift = mode.maxLM; + } else { + B = 1; + NB = mode.shortMdctSize << LM; + shift = mode.maxLM - LM; + } + if (CC == 2 && C == 1) { + int b; + Bands.denormalise_bands(mode, X[0], freq, 0, oldBandE, 0, start, effEnd, M, downsample, silence); + int freq2 = out_syn_ptrs[1] + overlap / 2; + System.arraycopy(freq, 0, out_syn[1], freq2, N); + for (b = 0; b < B; ++b) { + MDCT.clt_mdct_backward(mode.mdct, out_syn[1], freq2 + b, out_syn[0], out_syn_ptrs[0] + NB * b, mode.window, overlap, shift, B); + } + for (b = 0; b < B; ++b) { + MDCT.clt_mdct_backward(mode.mdct, freq, b, out_syn[1], out_syn_ptrs[1] + NB * b, mode.window, overlap, shift, B); + } + } else if (CC == 1 && C == 2) { + int freq2 = out_syn_ptrs[0] + overlap / 2; + Bands.denormalise_bands(mode, X[0], freq, 0, oldBandE, 0, start, effEnd, M, downsample, silence); + Bands.denormalise_bands(mode, X[1], out_syn[0], freq2, oldBandE, nbEBands, start, effEnd, M, downsample, silence); + for (int i = 0; i < N; ++i) { + freq[i] = Inlines.HALF32(Inlines.ADD32(freq[i], out_syn[0][freq2 + i])); + } + for (int b = 0; b < B; ++b) { + MDCT.clt_mdct_backward(mode.mdct, freq, b, out_syn[0], out_syn_ptrs[0] + NB * b, mode.window, overlap, shift, B); + } + } else { + int c = 0; + do { + Bands.denormalise_bands(mode, X[c], freq, 0, oldBandE, c * nbEBands, start, effEnd, M, downsample, silence); + for (int b = 0; b < B; ++b) { + MDCT.clt_mdct_backward(mode.mdct, freq, b, out_syn[c], out_syn_ptrs[c] + NB * b, mode.window, overlap, shift, B); + } + } while (++c < CC); + } + } + + static void tf_decode(int start, int end, int isTransient, int[] tf_res, int LM, EntropyCoder dec) { + int i; + int budget = dec.storage * 8; + int tell = dec.tell(); + int logp = isTransient != 0 ? 2 : 4; + int tf_select_rsv = LM > 0 && tell + logp + 1 <= budget ? 1 : 0; + budget -= tf_select_rsv; + int curr = 0; + int tf_changed = 0; + for (i = start; i < end; ++i) { + if (tell + logp <= budget) { + tell = dec.tell(); + tf_changed |= (curr ^= dec.dec_bit_logp(logp)); + } + tf_res[i] = curr; + logp = isTransient != 0 ? 4 : 5; + } + int tf_select = 0; + if (tf_select_rsv != 0 && CeltTables.tf_select_table[LM][4 * isTransient + 0 + tf_changed] != CeltTables.tf_select_table[LM][4 * isTransient + 2 + tf_changed]) { + tf_select = dec.dec_bit_logp(1L); + } + for (i = start; i < end; ++i) { + tf_res[i] = CeltTables.tf_select_table[LM][4 * isTransient + 2 * tf_select + tf_res[i]]; + } + } + + static int celt_plc_pitch_search(int[][] decode_mem, int C) { + BoxedValueInt pitch_index = new BoxedValueInt(0); + int[] lp_pitch_buf = new int[1024]; + Pitch.pitch_downsample(decode_mem, lp_pitch_buf, 2048, C); + Pitch.pitch_search(lp_pitch_buf, 360, lp_pitch_buf, 1328, 620, pitch_index); + pitch_index.Val = 720 - pitch_index.Val; + return pitch_index.Val; + } + + static int resampling_factor(int rate) { + int ret; + switch (rate) { + case 48000: { + ret = 1; + break; + } + case 24000: { + ret = 2; + break; + } + case 16000: { + ret = 3; + break; + } + case 12000: { + ret = 4; + break; + } + case 8000: { + ret = 6; + break; + } + default: { + Inlines.OpusAssert(false); + ret = 0; + } + } + return ret; + } + + static void comb_filter_const(int[] y, int y_ptr, int[] x, int x_ptr, int T, int N, int g10, int g11, int g12) { + int xpt = x_ptr - T; + int x4 = x[xpt - 2]; + int x3 = x[xpt - 1]; + int x2 = x[xpt]; + int x1 = x[xpt + 1]; + for (int i = 0; i < N; ++i) { + int x0 = x[xpt + i + 2]; + y[y_ptr + i] = x[x_ptr + i] + Inlines.MULT16_32_Q15(g10, x2) + Inlines.MULT16_32_Q15(g11, Inlines.ADD32(x1, x3)) + Inlines.MULT16_32_Q15(g12, Inlines.ADD32(x0, x4)); + x4 = x3; + x3 = x2; + x2 = x1; + x1 = x0; + } + } + + static void comb_filter(int[] y, int y_ptr, int[] x, int x_ptr, int T0, int T1, int N, int g0, int g1, int tapset0, int tapset1, int[] window, int overlap) { + int i; + if (g0 == 0 && g1 == 0) { + if (x_ptr != y_ptr) { + // empty if block + } + return; + } + int g00 = Inlines.MULT16_16_P15(g0, (int)gains[tapset0][0]); + int g01 = Inlines.MULT16_16_P15(g0, (int)gains[tapset0][1]); + int g02 = Inlines.MULT16_16_P15(g0, (int)gains[tapset0][2]); + int g10 = Inlines.MULT16_16_P15(g1, (int)gains[tapset1][0]); + int g11 = Inlines.MULT16_16_P15(g1, (int)gains[tapset1][1]); + int g12 = Inlines.MULT16_16_P15(g1, (int)gains[tapset1][2]); + int x1 = x[x_ptr - T1 + 1]; + int x2 = x[x_ptr - T1]; + int x3 = x[x_ptr - T1 - 1]; + int x4 = x[x_ptr - T1 - 2]; + if (g0 == g1 && T0 == T1 && tapset0 == tapset1) { + overlap = 0; + } + for (i = 0; i < overlap; ++i) { + int x0 = x[x_ptr + i - T1 + 2]; + int f = Inlines.MULT16_16_Q15(window[i], window[i]); + y[y_ptr + i] = x[x_ptr + i] + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((int)((short)(Short.MAX_VALUE - f)), g00), x[x_ptr + i - T0]) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((int)((short)(Short.MAX_VALUE - f)), g01), Inlines.ADD32(x[x_ptr + i - T0 + 1], x[x_ptr + i - T0 - 1])) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((int)((short)(Short.MAX_VALUE - f)), g02), Inlines.ADD32(x[x_ptr + i - T0 + 2], x[x_ptr + i - T0 - 2])) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(f, g10), x2) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(f, g11), Inlines.ADD32(x1, x3)) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(f, g12), Inlines.ADD32(x0, x4)); + x4 = x3; + x3 = x2; + x2 = x1; + x1 = x0; + } + if (g1 == 0) { + if (x_ptr != y_ptr) { + // empty if block + } + return; + } + CeltCommon.comb_filter_const(y, y_ptr + i, x, x_ptr + i, T1, N - i, g10, g11, g12); + } + + static void init_caps(CeltMode m, int[] cap, int LM, int C) { + for (int i = 0; i < m.nbEBands; ++i) { + int N = m.eBands[i + 1] - m.eBands[i] << LM; + cap[i] = (m.cache.caps[m.nbEBands * (2 * LM + C - 1) + i] + 64) * C * N >> 2; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CeltConstants.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltConstants.java new file mode 100644 index 0000000..49a9630 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltConstants.java @@ -0,0 +1,36 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class CeltConstants { + public static final int Q15ONE = Short.MAX_VALUE; + public static final float CELT_SIG_SCALE = 32768.0f; + public static final int SIG_SHIFT = 12; + public static final int NORM_SCALING = 16384; + public static final int DB_SHIFT = 10; + public static final int EPSILON = 1; + public static final int VERY_SMALL = 0; + public static final short VERY_LARGE16 = Short.MAX_VALUE; + public static final short Q15_ONE = Short.MAX_VALUE; + public static final int COMBFILTER_MAXPERIOD = 1024; + public static final int COMBFILTER_MINPERIOD = 15; + public static final int DECODE_BUFFER_SIZE = 2048; + public static final int BITALLOC_SIZE = 11; + public static final int MAX_PERIOD = 1024; + public static final int TOTAL_MODES = 1; + public static final int MAX_PSEUDO = 40; + public static final int LOG_MAX_PSEUDO = 6; + public static final int CELT_MAX_PULSES = 128; + public static final int MAX_FINE_BITS = 8; + public static final int FINE_OFFSET = 21; + public static final int QTHETA_OFFSET = 4; + public static final int QTHETA_OFFSET_TWOPHASE = 16; + public static final int PLC_PITCH_LAG_MAX = 720; + public static final int PLC_PITCH_LAG_MIN = 100; + public static final int LPC_ORDER = 24; + + CeltConstants() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CeltDecoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltDecoder.java new file mode 100644 index 0000000..3ef5085 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltDecoder.java @@ -0,0 +1,534 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Autocorrelation; +import de.maxhenkel.voicechat.concentus.Bands; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltCommon; +import de.maxhenkel.voicechat.concentus.CeltLPC; +import de.maxhenkel.voicechat.concentus.CeltMode; +import de.maxhenkel.voicechat.concentus.CeltTables; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Kernels; +import de.maxhenkel.voicechat.concentus.OpusError; +import de.maxhenkel.voicechat.concentus.QuantizeBands; +import de.maxhenkel.voicechat.concentus.Rate; +import de.maxhenkel.voicechat.concentus.VQ; + +class CeltDecoder { + CeltMode mode = null; + int overlap = 0; + int channels = 0; + int stream_channels = 0; + int downsample = 0; + int start = 0; + int end = 0; + int signalling = 0; + int rng = 0; + int error = 0; + int last_pitch_index = 0; + int loss_count = 0; + int postfilter_period = 0; + int postfilter_period_old = 0; + int postfilter_gain = 0; + int postfilter_gain_old = 0; + int postfilter_tapset = 0; + int postfilter_tapset_old = 0; + final int[] preemph_memD = new int[2]; + int[][] decode_mem = null; + int[][] lpc = null; + int[] oldEBands = null; + int[] oldLogE = null; + int[] oldLogE2 = null; + int[] backgroundLogE = null; + + CeltDecoder() { + } + + private void Reset() { + this.mode = null; + this.overlap = 0; + this.channels = 0; + this.stream_channels = 0; + this.downsample = 0; + this.start = 0; + this.end = 0; + this.signalling = 0; + this.PartialReset(); + } + + private void PartialReset() { + this.rng = 0; + this.error = 0; + this.last_pitch_index = 0; + this.loss_count = 0; + this.postfilter_period = 0; + this.postfilter_period_old = 0; + this.postfilter_gain = 0; + this.postfilter_gain_old = 0; + this.postfilter_tapset = 0; + this.postfilter_tapset_old = 0; + Arrays.MemSet(this.preemph_memD, 0, 2); + this.decode_mem = null; + this.lpc = null; + this.oldEBands = null; + this.oldLogE = null; + this.oldLogE2 = null; + this.backgroundLogE = null; + } + + void ResetState() { + this.PartialReset(); + this.decode_mem = new int[this.channels][]; + this.lpc = new int[this.channels][]; + for (int c = 0; c < this.channels; ++c) { + this.decode_mem[c] = new int[2048 + this.mode.overlap]; + this.lpc[c] = new int[24]; + } + this.oldEBands = new int[2 * this.mode.nbEBands]; + this.oldLogE = new int[2 * this.mode.nbEBands]; + this.oldLogE2 = new int[2 * this.mode.nbEBands]; + this.backgroundLogE = new int[2 * this.mode.nbEBands]; + for (int i = 0; i < 2 * this.mode.nbEBands; ++i) { + this.oldLogE2[i] = -28672; + this.oldLogE[i] = -28672; + } + } + + int celt_decoder_init(int sampling_rate, int channels) { + int ret = this.opus_custom_decoder_init(CeltMode.mode48000_960_120, channels); + if (ret != OpusError.OPUS_OK) { + return ret; + } + this.downsample = CeltCommon.resampling_factor(sampling_rate); + if (this.downsample == 0) { + return OpusError.OPUS_BAD_ARG; + } + return OpusError.OPUS_OK; + } + + private int opus_custom_decoder_init(CeltMode mode, int channels) { + if (channels < 0 || channels > 2) { + return OpusError.OPUS_BAD_ARG; + } + if (this == null) { + return OpusError.OPUS_ALLOC_FAIL; + } + this.Reset(); + this.mode = mode; + this.overlap = mode.overlap; + this.stream_channels = this.channels = channels; + this.downsample = 1; + this.start = 0; + this.end = this.mode.effEBands; + this.signalling = 1; + this.loss_count = 0; + this.ResetState(); + return OpusError.OPUS_OK; + } + + void celt_decode_lost(int N, int LM) { + boolean noise_based; + int C = this.channels; + int[][] out_syn = new int[2][]; + int[] out_syn_ptrs = new int[2]; + CeltMode mode = this.mode; + int nbEBands = mode.nbEBands; + int overlap = mode.overlap; + short[] eBands = mode.eBands; + int c = 0; + do { + out_syn[c] = this.decode_mem[c]; + out_syn_ptrs[c] = 2048 - N; + } while (++c < C); + boolean bl = noise_based = this.loss_count >= 5 || this.start != 0; + if (noise_based) { + int i; + int end = this.end; + int effEnd = Inlines.IMAX(this.start, Inlines.IMIN(end, mode.effEBands)); + int[][] X = Arrays.InitTwoDimensionalArrayInt(C, N); + int decay = this.loss_count == 0 ? 1536 : 512; + c = 0; + do { + for (i = this.start; i < end; ++i) { + this.oldEBands[c * nbEBands + i] = Inlines.MAX16(this.backgroundLogE[c * nbEBands + i], this.oldEBands[c * nbEBands + i] - decay); + } + } while (++c < C); + int seed = this.rng; + for (c = 0; c < C; ++c) { + for (i = this.start; i < effEnd; ++i) { + int boffs = eBands[i] << LM; + int blen = eBands[i + 1] - eBands[i] << LM; + for (int j = 0; j < blen; ++j) { + seed = Bands.celt_lcg_rand(seed); + X[c][boffs + j] = seed >> 20; + } + VQ.renormalise_vector(X[c], 0, blen, Short.MAX_VALUE); + } + } + this.rng = seed; + c = 0; + do { + Arrays.MemMove(this.decode_mem[c], N, 0, 2048 - N + (overlap >> 1)); + } while (++c < C); + CeltCommon.celt_synthesis(mode, X, out_syn, out_syn_ptrs, this.oldEBands, this.start, effEnd, C, C, 0, LM, this.downsample, 0); + } else { + int pitch_index; + int fade = Short.MAX_VALUE; + if (this.loss_count == 0) { + this.last_pitch_index = pitch_index = CeltCommon.celt_plc_pitch_search(this.decode_mem, C); + } else { + pitch_index = this.last_pitch_index; + fade = 26214; + } + int[] etmp = new int[overlap]; + int[] exc = new int[1024]; + int[] window = mode.window; + c = 0; + do { + int i; + int S1 = 0; + int[] buf = this.decode_mem[c]; + for (i = 0; i < 1024; ++i) { + exc[i] = Inlines.ROUND16(buf[1024 + i], 12); + } + if (this.loss_count == 0) { + int[] ac = new int[25]; + Autocorrelation._celt_autocorr(exc, ac, window, overlap, 24, 1024); + ac[0] = ac[0] + Inlines.SHR32(ac[0], 13); + for (i = 1; i <= 24; ++i) { + int n = i; + ac[n] = ac[n] - Inlines.MULT16_32_Q15(2 * i * i, ac[i]); + } + CeltLPC.celt_lpc(this.lpc[c], ac, 24); + } + int exc_length = Inlines.IMIN(2 * pitch_index, 1024); + int[] lpc_mem = new int[24]; + for (i = 0; i < 24; ++i) { + lpc_mem[i] = Inlines.ROUND16(buf[2048 - exc_length - 1 - i], 12); + } + Kernels.celt_fir(exc, 1024 - exc_length, this.lpc[c], 0, exc, 1024 - exc_length, exc_length, 24, lpc_mem); + int E1 = 1; + int E2 = 1; + int shift = Inlines.IMAX(0, 2 * Inlines.celt_zlog2(Inlines.celt_maxabs16(exc, 1024 - exc_length, exc_length)) - 20); + int decay_length = exc_length >> 1; + for (i = 0; i < decay_length; ++i) { + int e = exc[1024 - decay_length + i]; + E1 += Inlines.SHR32(Inlines.MULT16_16(e, e), shift); + e = exc[1024 - 2 * decay_length + i]; + E2 += Inlines.SHR32(Inlines.MULT16_16(e, e), shift); + } + E1 = Inlines.MIN32(E1, E2); + int decay = Inlines.celt_sqrt(Inlines.frac_div32(Inlines.SHR32(E1, 1), E2)); + Arrays.MemMove(buf, N, 0, 2048 - N); + int extrapolation_offset = 1024 - pitch_index; + int extrapolation_len = N + overlap; + int attenuation = Inlines.MULT16_16_Q15(fade, decay); + int j = 0; + i = 0; + while (i < extrapolation_len) { + if (j >= pitch_index) { + j -= pitch_index; + attenuation = Inlines.MULT16_16_Q15(attenuation, decay); + } + buf[2048 - N + i] = Inlines.SHL32(Inlines.MULT16_16_Q15(attenuation, exc[extrapolation_offset + j]), 12); + int tmp = Inlines.ROUND16(buf[1024 - N + extrapolation_offset + j], 12); + S1 += Inlines.SHR32(Inlines.MULT16_16(tmp, tmp), 8); + ++i; + ++j; + } + lpc_mem = new int[24]; + for (i = 0; i < 24; ++i) { + lpc_mem[i] = Inlines.ROUND16(buf[2048 - N - 1 - i], 12); + } + CeltLPC.celt_iir(buf, 2048 - N, this.lpc[c], buf, 2048 - N, extrapolation_len, 24, lpc_mem); + int S2 = 0; + for (i = 0; i < extrapolation_len; ++i) { + int tmp = Inlines.ROUND16(buf[2048 - N + i], 12); + S2 += Inlines.SHR32(Inlines.MULT16_16(tmp, tmp), 8); + } + if (S1 <= Inlines.SHR32(S2, 2)) { + for (i = 0; i < extrapolation_len; ++i) { + buf[2048 - N + i] = 0; + } + } else if (S1 < S2) { + int ratio = Inlines.celt_sqrt(Inlines.frac_div32(Inlines.SHR32(S1, 1) + 1, S2 + 1)); + for (i = 0; i < overlap; ++i) { + int tmp_g = Short.MAX_VALUE - Inlines.MULT16_16_Q15(window[i], Short.MAX_VALUE - ratio); + buf[2048 - N + i] = Inlines.MULT16_32_Q15(tmp_g, buf[2048 - N + i]); + } + for (i = overlap; i < extrapolation_len; ++i) { + buf[2048 - N + i] = Inlines.MULT16_32_Q15(ratio, buf[2048 - N + i]); + } + } + CeltCommon.comb_filter(etmp, 0, buf, 2048, this.postfilter_period, this.postfilter_period, overlap, -this.postfilter_gain, -this.postfilter_gain, this.postfilter_tapset, this.postfilter_tapset, null, 0); + for (i = 0; i < overlap / 2; ++i) { + buf[2048 + i] = Inlines.MULT16_32_Q15(window[i], etmp[overlap - 1 - i]) + Inlines.MULT16_32_Q15(window[overlap - i - 1], etmp[i]); + } + } while (++c < C); + } + ++this.loss_count; + } + + int celt_decode_with_ec(byte[] data, int data_ptr, int len, short[] pcm, int pcm_ptr, int frame_size, EntropyCoder dec, int accum) { + int isTransient; + int i; + int LM; + int[][] out_syn = new int[2][]; + int[] out_syn_ptrs = new int[2]; + int CC = this.channels; + int intensity = 0; + int dual_stereo = 0; + int anti_collapse_on = 0; + int C = this.stream_channels; + CeltMode mode = this.mode; + int nbEBands = mode.nbEBands; + int overlap = mode.overlap; + short[] eBands = mode.eBands; + int start = this.start; + int end = this.end; + frame_size *= this.downsample; + int[] oldBandE = this.oldEBands; + int[] oldLogE = this.oldLogE; + int[] oldLogE2 = this.oldLogE2; + int[] backgroundLogE = this.backgroundLogE; + for (LM = 0; LM <= mode.maxLM && mode.shortMdctSize << LM != frame_size; ++LM) { + } + if (LM > mode.maxLM) { + return OpusError.OPUS_BAD_ARG; + } + int M = 1 << LM; + if (len < 0 || len > 1275 || pcm == null) { + return OpusError.OPUS_BAD_ARG; + } + int N = M * mode.shortMdctSize; + int c = 0; + do { + out_syn[c] = this.decode_mem[c]; + out_syn_ptrs[c] = 2048 - N; + } while (++c < CC); + int effEnd = end; + if (effEnd > mode.effEBands) { + effEnd = mode.effEBands; + } + if (data == null || len <= 1) { + this.celt_decode_lost(N, LM); + CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum); + return frame_size / this.downsample; + } + if (dec == null) { + dec = new EntropyCoder(); + dec.dec_init(data, data_ptr, len); + } + if (C == 1) { + for (i = 0; i < nbEBands; ++i) { + oldBandE[i] = Inlines.MAX16(oldBandE[i], oldBandE[nbEBands + i]); + } + } + int total_bits = len * 8; + int tell = dec.tell(); + int silence = tell >= total_bits ? 1 : (tell == 1 ? dec.dec_bit_logp(15L) : 0); + if (silence != 0) { + tell = len * 8; + dec.nbits_total += tell - dec.tell(); + } + int postfilter_gain = 0; + int postfilter_pitch = 0; + int postfilter_tapset = 0; + if (start == 0 && tell + 16 <= total_bits) { + if (dec.dec_bit_logp(1L) != 0) { + int octave = (int)dec.dec_uint(6L); + postfilter_pitch = (16 << octave) + dec.dec_bits(4 + octave) - 1; + int qg = dec.dec_bits(3); + if (dec.tell() + 2 <= total_bits) { + postfilter_tapset = dec.dec_icdf(CeltTables.tapset_icdf, 2); + } + postfilter_gain = 3072 * (qg + 1); + } + tell = dec.tell(); + } + if (LM > 0 && tell + 3 <= total_bits) { + isTransient = dec.dec_bit_logp(3L); + tell = dec.tell(); + } else { + isTransient = 0; + } + int shortBlocks = isTransient != 0 ? M : 0; + int intra_ener = tell + 3 <= total_bits ? dec.dec_bit_logp(3L) : 0; + QuantizeBands.unquant_coarse_energy(mode, start, end, oldBandE, intra_ener, dec, C, LM); + int[] tf_res = new int[nbEBands]; + CeltCommon.tf_decode(start, end, isTransient, tf_res, LM, dec); + tell = dec.tell(); + int spread_decision = 2; + if (tell + 4 <= total_bits) { + spread_decision = dec.dec_icdf(CeltTables.spread_icdf, 5); + } + int[] cap = new int[nbEBands]; + CeltCommon.init_caps(mode, cap, LM, C); + int[] offsets = new int[nbEBands]; + int dynalloc_logp = 6; + total_bits <<= 3; + tell = dec.tell_frac(); + for (i = start; i < end; ++i) { + int boost; + int width = C * (eBands[i + 1] - eBands[i]) << LM; + int quanta = Inlines.IMIN(width << 3, Inlines.IMAX(48, width)); + int dynalloc_loop_logp = dynalloc_logp; + for (boost = 0; tell + (dynalloc_loop_logp << 3) < total_bits && boost < cap[i]; boost += quanta, total_bits -= quanta) { + int flag = dec.dec_bit_logp(dynalloc_loop_logp); + tell = dec.tell_frac(); + if (flag == 0) break; + dynalloc_loop_logp = 1; + } + offsets[i] = boost; + if (boost <= 0) continue; + dynalloc_logp = Inlines.IMAX(2, dynalloc_logp - 1); + } + int[] fine_quant = new int[nbEBands]; + int alloc_trim = tell + 48 <= total_bits ? dec.dec_icdf(CeltTables.trim_icdf, 7) : 5; + int bits = (len * 8 << 3) - dec.tell_frac() - 1; + int anti_collapse_rsv = isTransient != 0 && LM >= 2 && bits >= LM + 2 << 3 ? 8 : 0; + bits -= anti_collapse_rsv; + int[] pulses = new int[nbEBands]; + int[] fine_priority = new int[nbEBands]; + BoxedValueInt boxed_intensity = new BoxedValueInt(intensity); + BoxedValueInt boxed_dual_stereo = new BoxedValueInt(dual_stereo); + BoxedValueInt boxed_balance = new BoxedValueInt(0); + int codedBands = Rate.compute_allocation(mode, start, end, offsets, cap, alloc_trim, boxed_intensity, boxed_dual_stereo, bits, boxed_balance, pulses, fine_quant, fine_priority, C, LM, dec, 0, 0, 0); + intensity = boxed_intensity.Val; + dual_stereo = boxed_dual_stereo.Val; + int balance = boxed_balance.Val; + QuantizeBands.unquant_fine_energy(mode, start, end, oldBandE, fine_quant, dec, C); + c = 0; + do { + Arrays.MemMove(this.decode_mem[c], N, 0, 2048 - N + overlap / 2); + } while (++c < CC); + short[] collapse_masks = new short[C * nbEBands]; + int[][] X = Arrays.InitTwoDimensionalArrayInt(C, N); + BoxedValueInt boxed_rng = new BoxedValueInt(this.rng); + Bands.quant_all_bands(0, mode, start, end, X[0], C == 2 ? X[1] : null, collapse_masks, null, pulses, shortBlocks, spread_decision, dual_stereo, intensity, tf_res, len * 64 - anti_collapse_rsv, balance, dec, LM, codedBands, boxed_rng); + this.rng = boxed_rng.Val; + if (anti_collapse_rsv > 0) { + anti_collapse_on = dec.dec_bits(1); + } + QuantizeBands.unquant_energy_finalise(mode, start, end, oldBandE, fine_quant, fine_priority, len * 8 - dec.tell(), dec, C); + if (anti_collapse_on != 0) { + Bands.anti_collapse(mode, X, collapse_masks, LM, C, N, start, end, oldBandE, oldLogE, oldLogE2, pulses, this.rng); + } + if (silence != 0) { + for (i = 0; i < C * nbEBands; ++i) { + oldBandE[i] = -28672; + } + } + CeltCommon.celt_synthesis(mode, X, out_syn, out_syn_ptrs, oldBandE, start, effEnd, C, CC, isTransient, LM, this.downsample, silence); + c = 0; + do { + this.postfilter_period = Inlines.IMAX(this.postfilter_period, 15); + this.postfilter_period_old = Inlines.IMAX(this.postfilter_period_old, 15); + CeltCommon.comb_filter(out_syn[c], out_syn_ptrs[c], out_syn[c], out_syn_ptrs[c], this.postfilter_period_old, this.postfilter_period, mode.shortMdctSize, this.postfilter_gain_old, this.postfilter_gain, this.postfilter_tapset_old, this.postfilter_tapset, mode.window, overlap); + if (LM == 0) continue; + CeltCommon.comb_filter(out_syn[c], out_syn_ptrs[c] + mode.shortMdctSize, out_syn[c], out_syn_ptrs[c] + mode.shortMdctSize, this.postfilter_period, postfilter_pitch, N - mode.shortMdctSize, this.postfilter_gain, postfilter_gain, this.postfilter_tapset, postfilter_tapset, mode.window, overlap); + } while (++c < CC); + this.postfilter_period_old = this.postfilter_period; + this.postfilter_gain_old = this.postfilter_gain; + this.postfilter_tapset_old = this.postfilter_tapset; + this.postfilter_period = postfilter_pitch; + this.postfilter_gain = postfilter_gain; + this.postfilter_tapset = postfilter_tapset; + if (LM != 0) { + this.postfilter_period_old = this.postfilter_period; + this.postfilter_gain_old = this.postfilter_gain; + this.postfilter_tapset_old = this.postfilter_tapset; + } + if (C == 1) { + System.arraycopy(oldBandE, 0, oldBandE, nbEBands, nbEBands); + } + if (isTransient == 0) { + System.arraycopy(oldLogE, 0, oldLogE2, 0, 2 * nbEBands); + System.arraycopy(oldBandE, 0, oldLogE, 0, 2 * nbEBands); + int max_background_increase = this.loss_count < 10 ? M * 1 : 1024; + for (i = 0; i < 2 * nbEBands; ++i) { + backgroundLogE[i] = Inlines.MIN16(backgroundLogE[i] + max_background_increase, oldBandE[i]); + } + } else { + for (i = 0; i < 2 * nbEBands; ++i) { + oldLogE[i] = Inlines.MIN16(oldLogE[i], oldBandE[i]); + } + } + c = 0; + do { + for (i = 0; i < start; ++i) { + oldBandE[c * nbEBands + i] = 0; + oldLogE2[c * nbEBands + i] = -28672; + oldLogE[c * nbEBands + i] = -28672; + } + for (i = end; i < nbEBands; ++i) { + oldBandE[c * nbEBands + i] = 0; + oldLogE2[c * nbEBands + i] = -28672; + oldLogE[c * nbEBands + i] = -28672; + } + } while (++c < 2); + this.rng = (int)dec.rng; + CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum); + this.loss_count = 0; + if (dec.tell() > 8 * len) { + return OpusError.OPUS_INTERNAL_ERROR; + } + if (dec.get_error() != 0) { + this.error = 1; + } + return frame_size / this.downsample; + } + + void SetStartBand(int value) { + if (value < 0 || value >= this.mode.nbEBands) { + throw new IllegalArgumentException("Start band above max number of ebands (or negative)"); + } + this.start = value; + } + + void SetEndBand(int value) { + if (value < 1 || value > this.mode.nbEBands) { + throw new IllegalArgumentException("End band above max number of ebands (or less than 1)"); + } + this.end = value; + } + + void SetChannels(int value) { + if (value < 1 || value > 2) { + throw new IllegalArgumentException("Channel count must be 1 or 2"); + } + this.stream_channels = value; + } + + int GetAndClearError() { + int returnVal = this.error; + this.error = 0; + return returnVal; + } + + public int GetLookahead() { + return this.overlap / this.downsample; + } + + public int GetPitch() { + return this.postfilter_period; + } + + public CeltMode GetMode() { + return this.mode; + } + + public void SetSignalling(int value) { + this.signalling = value; + } + + public int GetFinalRange() { + return this.rng; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CeltEncoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltEncoder.java new file mode 100644 index 0000000..d91ddde --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltEncoder.java @@ -0,0 +1,888 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.AnalysisInfo; +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Bands; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltCommon; +import de.maxhenkel.voicechat.concentus.CeltMode; +import de.maxhenkel.voicechat.concentus.CeltTables; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.OpusError; +import de.maxhenkel.voicechat.concentus.OpusFramesize; +import de.maxhenkel.voicechat.concentus.Pitch; +import de.maxhenkel.voicechat.concentus.QuantizeBands; +import de.maxhenkel.voicechat.concentus.Rate; + +class CeltEncoder { + CeltMode mode = null; + int channels = 0; + int stream_channels = 0; + int force_intra = 0; + int clip = 0; + int disable_pf = 0; + int complexity = 0; + int upsample = 0; + int start = 0; + int end = 0; + int bitrate = 0; + int vbr = 0; + int signalling = 0; + int constrained_vbr = 0; + int loss_rate = 0; + int lsb_depth = 0; + OpusFramesize variable_duration = OpusFramesize.OPUS_FRAMESIZE_UNKNOWN; + int lfe = 0; + int rng = 0; + int spread_decision = 0; + int delayedIntra = 0; + int tonal_average = 0; + int lastCodedBands = 0; + int hf_average = 0; + int tapset_decision = 0; + int prefilter_period = 0; + int prefilter_gain = 0; + int prefilter_tapset = 0; + int consec_transient = 0; + AnalysisInfo analysis = new AnalysisInfo(); + final int[] preemph_memE = new int[2]; + final int[] preemph_memD = new int[2]; + int vbr_reservoir = 0; + int vbr_drift = 0; + int vbr_offset = 0; + int vbr_count = 0; + int overlap_max = 0; + int stereo_saving = 0; + int intensity = 0; + int[] energy_mask = null; + int spec_avg = 0; + int[][] in_mem = null; + int[][] prefilter_mem = null; + int[][] oldBandE = null; + int[][] oldLogE = null; + int[][] oldLogE2 = null; + + CeltEncoder() { + } + + private void Reset() { + this.mode = null; + this.channels = 0; + this.stream_channels = 0; + this.force_intra = 0; + this.clip = 0; + this.disable_pf = 0; + this.complexity = 0; + this.upsample = 0; + this.start = 0; + this.end = 0; + this.bitrate = 0; + this.vbr = 0; + this.signalling = 0; + this.constrained_vbr = 0; + this.loss_rate = 0; + this.lsb_depth = 0; + this.variable_duration = OpusFramesize.OPUS_FRAMESIZE_UNKNOWN; + this.lfe = 0; + this.PartialReset(); + } + + private void PartialReset() { + this.rng = 0; + this.spread_decision = 0; + this.delayedIntra = 0; + this.tonal_average = 0; + this.lastCodedBands = 0; + this.hf_average = 0; + this.tapset_decision = 0; + this.prefilter_period = 0; + this.prefilter_gain = 0; + this.prefilter_tapset = 0; + this.consec_transient = 0; + this.analysis.Reset(); + this.preemph_memE[0] = 0; + this.preemph_memE[1] = 0; + this.preemph_memD[0] = 0; + this.preemph_memD[1] = 0; + this.vbr_reservoir = 0; + this.vbr_drift = 0; + this.vbr_offset = 0; + this.vbr_count = 0; + this.overlap_max = 0; + this.stereo_saving = 0; + this.intensity = 0; + this.energy_mask = null; + this.spec_avg = 0; + this.in_mem = null; + this.prefilter_mem = null; + this.oldBandE = null; + this.oldLogE = null; + this.oldLogE2 = null; + } + + void ResetState() { + int i; + this.PartialReset(); + this.in_mem = Arrays.InitTwoDimensionalArrayInt(this.channels, this.mode.overlap); + this.prefilter_mem = Arrays.InitTwoDimensionalArrayInt(this.channels, 1024); + this.oldBandE = Arrays.InitTwoDimensionalArrayInt(this.channels, this.mode.nbEBands); + this.oldLogE = Arrays.InitTwoDimensionalArrayInt(this.channels, this.mode.nbEBands); + this.oldLogE2 = Arrays.InitTwoDimensionalArrayInt(this.channels, this.mode.nbEBands); + for (i = 0; i < this.mode.nbEBands; ++i) { + this.oldLogE2[0][i] = -28672; + this.oldLogE[0][i] = -28672; + } + if (this.channels == 2) { + for (i = 0; i < this.mode.nbEBands; ++i) { + this.oldLogE2[1][i] = -28672; + this.oldLogE[1][i] = -28672; + } + } + this.vbr_offset = 0; + this.delayedIntra = 1; + this.spread_decision = 2; + this.tonal_average = 256; + this.hf_average = 0; + this.tapset_decision = 0; + } + + int opus_custom_encoder_init_arch(CeltMode mode, int channels) { + if (channels < 0 || channels > 2) { + return OpusError.OPUS_BAD_ARG; + } + if (this == null || mode == null) { + return OpusError.OPUS_ALLOC_FAIL; + } + this.Reset(); + this.mode = mode; + this.stream_channels = this.channels = channels; + this.upsample = 1; + this.start = 0; + this.end = this.mode.effEBands; + this.signalling = 1; + this.constrained_vbr = 1; + this.clip = 1; + this.bitrate = -1; + this.vbr = 0; + this.force_intra = 0; + this.complexity = 5; + this.lsb_depth = 24; + this.ResetState(); + return OpusError.OPUS_OK; + } + + int celt_encoder_init(int sampling_rate, int channels) { + int ret = this.opus_custom_encoder_init_arch(CeltMode.mode48000_960_120, channels); + if (ret != OpusError.OPUS_OK) { + return ret; + } + this.upsample = CeltCommon.resampling_factor(sampling_rate); + return OpusError.OPUS_OK; + } + + int run_prefilter(int[][] input, int[][] prefilter_mem, int CC, int N, int prefilter_tapset, BoxedValueInt pitch, BoxedValueInt gain, BoxedValueInt qgain, int enabled, int nbAvailableBytes) { + int qg; + int pf_on; + int gain1; + int[][] pre = new int[CC][]; + BoxedValueInt pitch_index = new BoxedValueInt(0); + CeltMode mode = this.mode; + int overlap = mode.overlap; + for (int z = 0; z < CC; ++z) { + pre[z] = new int[N + 1024]; + } + int c = 0; + do { + System.arraycopy(prefilter_mem[c], 0, pre[c], 0, 1024); + System.arraycopy(input[c], overlap, pre[c], 1024, N); + } while (++c < CC); + if (enabled != 0) { + int[] pitch_buf = new int[1024 + N >> 1]; + Pitch.pitch_downsample(pre, pitch_buf, 1024 + N, CC); + Pitch.pitch_search(pitch_buf, 512, pitch_buf, N, 979, pitch_index); + pitch_index.Val = 1024 - pitch_index.Val; + gain1 = Pitch.remove_doubling(pitch_buf, 1024, 15, N, pitch_index, this.prefilter_period, this.prefilter_gain); + if (pitch_index.Val > 1022) { + pitch_index.Val = 1022; + } + gain1 = Inlines.MULT16_16_Q15(22938, gain1); + if (this.loss_rate > 2) { + gain1 = Inlines.HALF32(gain1); + } + if (this.loss_rate > 4) { + gain1 = Inlines.HALF32(gain1); + } + if (this.loss_rate > 8) { + gain1 = 0; + } + } else { + gain1 = 0; + pitch_index.Val = 15; + } + int pf_threshold = 6554; + if (Inlines.abs(pitch_index.Val - this.prefilter_period) * 10 > pitch_index.Val) { + pf_threshold += 6554; + } + if (nbAvailableBytes < 25) { + pf_threshold += 3277; + } + if (nbAvailableBytes < 35) { + pf_threshold += 3277; + } + if (this.prefilter_gain > 13107) { + pf_threshold -= 3277; + } + if (this.prefilter_gain > 18022) { + pf_threshold -= 3277; + } + if (gain1 < (pf_threshold = Inlines.MAX16(pf_threshold, 6554))) { + gain1 = 0; + pf_on = 0; + qg = 0; + } else { + if (Inlines.ABS32(gain1 - this.prefilter_gain) < 3277) { + gain1 = this.prefilter_gain; + } + qg = (gain1 + 1536 >> 10) / 3 - 1; + qg = Inlines.IMAX(0, Inlines.IMIN(7, qg)); + gain1 = 3072 * (qg + 1); + pf_on = 1; + } + c = 0; + do { + int offset = mode.shortMdctSize - overlap; + this.prefilter_period = Inlines.IMAX(this.prefilter_period, 15); + System.arraycopy(this.in_mem[c], 0, input[c], 0, overlap); + if (offset != 0) { + CeltCommon.comb_filter(input[c], overlap, pre[c], 1024, this.prefilter_period, this.prefilter_period, offset, -this.prefilter_gain, -this.prefilter_gain, this.prefilter_tapset, this.prefilter_tapset, null, 0); + } + CeltCommon.comb_filter(input[c], overlap + offset, pre[c], 1024 + offset, this.prefilter_period, pitch_index.Val, N - offset, -this.prefilter_gain, -gain1, this.prefilter_tapset, prefilter_tapset, mode.window, overlap); + System.arraycopy(input[c], N, this.in_mem[c], 0, overlap); + if (N > 1024) { + System.arraycopy(pre[c], N, prefilter_mem[c], 0, 1024); + continue; + } + Arrays.MemMove(prefilter_mem[c], N, 0, 1024 - N); + System.arraycopy(pre[c], 1024, prefilter_mem[c], 1024 - N, N); + } while (++c < CC); + gain.Val = gain1; + pitch.Val = pitch_index.Val; + qgain.Val = qg; + return pf_on; + } + + int celt_encode_with_ec(short[] pcm, int pcm_ptr, int frame_size, byte[] compressed, int compressed_ptr, int nbCompressedBytes, EntropyCoder enc) { + int tf_sum; + int tf_select; + int i; + int vbr_bound; + int max_allowed; + int effectiveBytes; + int vbr_rate; + int nbFilledBytes; + int tell; + int LM; + int shortBlocks = 0; + int isTransient = 0; + int CC = this.channels; + int C = this.stream_channels; + int pitch_index = 15; + int gain1 = 0; + int dual_stereo = 0; + int prefilter_tapset = 0; + int anti_collapse_on = 0; + int silence = 0; + int tf_chan = 0; + int pitch_change = 0; + boolean transient_got_disabled = false; + int surround_masking = 0; + int temporal_vbr = 0; + int surround_trim = 0; + int equiv_rate = 510000; + CeltMode mode = this.mode; + int nbEBands = mode.nbEBands; + int overlap = mode.overlap; + short[] eBands = mode.eBands; + int start = this.start; + int end = this.end; + int tf_estimate = 0; + if (nbCompressedBytes < 2 || pcm == null) { + return OpusError.OPUS_BAD_ARG; + } + frame_size *= this.upsample; + for (LM = 0; LM <= mode.maxLM && mode.shortMdctSize << LM != frame_size; ++LM) { + } + if (LM > mode.maxLM) { + return OpusError.OPUS_BAD_ARG; + } + int M = 1 << LM; + int N = M * mode.shortMdctSize; + if (enc == null) { + tell = 1; + nbFilledBytes = 0; + } else { + tell = enc.tell(); + nbFilledBytes = tell + 4 >> 3; + } + Inlines.OpusAssert(this.signalling == 0); + nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, 1275); + int nbAvailableBytes = nbCompressedBytes - nbFilledBytes; + if (this.vbr != 0 && this.bitrate != -1) { + int den = mode.Fs >> 3; + vbr_rate = (this.bitrate * frame_size + (den >> 1)) / den; + effectiveBytes = vbr_rate >> 6; + } else { + vbr_rate = 0; + int tmp = this.bitrate * frame_size; + if (tell > 1) { + tmp += tell; + } + if (this.bitrate != -1) { + nbCompressedBytes = Inlines.IMAX(2, Inlines.IMIN(nbCompressedBytes, (tmp + 4 * mode.Fs) / (8 * mode.Fs) - (this.signalling != 0 ? 1 : 0))); + } + effectiveBytes = nbCompressedBytes; + } + if (this.bitrate != -1) { + equiv_rate = this.bitrate - (40 * C + 20) * ((400 >> LM) - 50); + } + if (enc == null) { + enc = new EntropyCoder(); + enc.enc_init(compressed, compressed_ptr, nbCompressedBytes); + } + if (vbr_rate > 0 && this.constrained_vbr != 0 && (max_allowed = Inlines.IMIN(Inlines.IMAX(tell == 1 ? 2 : 0, vbr_rate + (vbr_bound = vbr_rate) - this.vbr_reservoir >> 6), nbAvailableBytes)) < nbAvailableBytes) { + nbCompressedBytes = nbFilledBytes + max_allowed; + nbAvailableBytes = max_allowed; + enc.enc_shrink(nbCompressedBytes); + } + int total_bits = nbCompressedBytes * 8; + int effEnd = end; + if (effEnd > mode.effEBands) { + effEnd = mode.effEBands; + } + int[][] input = Arrays.InitTwoDimensionalArrayInt(CC, N + overlap); + int sample_max = Inlines.MAX32(this.overlap_max, Inlines.celt_maxabs32(pcm, pcm_ptr, C * (N - overlap) / this.upsample)); + this.overlap_max = Inlines.celt_maxabs32(pcm, pcm_ptr + C * (N - overlap) / this.upsample, C * overlap / this.upsample); + int n = silence = (sample_max = Inlines.MAX32(sample_max, this.overlap_max)) == 0 ? 1 : 0; + if (tell == 1) { + enc.enc_bit_logp(silence, 15); + } else { + silence = 0; + } + if (silence != 0) { + if (vbr_rate > 0) { + effectiveBytes = nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, nbFilledBytes + 2); + total_bits = nbCompressedBytes * 8; + nbAvailableBytes = 2; + enc.enc_shrink(nbCompressedBytes); + } + tell = nbCompressedBytes * 8; + enc.nbits_total += tell - enc.tell(); + } + int c = 0; + BoxedValueInt boxed_memE = new BoxedValueInt(0); + do { + int need_clip = 0; + boxed_memE.Val = this.preemph_memE[c]; + CeltCommon.celt_preemphasis(pcm, pcm_ptr + c, input[c], overlap, N, CC, this.upsample, mode.preemph, boxed_memE, need_clip); + this.preemph_memE[c] = boxed_memE.Val; + } while (++c < CC); + int enabled = (this.lfe != 0 && nbAvailableBytes > 3 || nbAvailableBytes > 12 * C) && start == 0 && silence == 0 && this.disable_pf == 0 && this.complexity >= 5 && (this.consec_transient == 0 || LM == 3 || this.variable_duration != OpusFramesize.OPUS_FRAMESIZE_VARIABLE) ? 1 : 0; + prefilter_tapset = this.tapset_decision; + BoxedValueInt boxed_pitch_index = new BoxedValueInt(0); + BoxedValueInt boxed_gain1 = new BoxedValueInt(0); + BoxedValueInt boxed_qg = new BoxedValueInt(0); + int pf_on = this.run_prefilter(input, this.prefilter_mem, CC, N, prefilter_tapset, boxed_pitch_index, boxed_gain1, boxed_qg, enabled, nbAvailableBytes); + pitch_index = boxed_pitch_index.Val; + gain1 = boxed_gain1.Val; + int qg = boxed_qg.Val; + if (!(gain1 <= 13107 && this.prefilter_gain <= 13107 || this.analysis.valid != 0 && !((double)this.analysis.tonality > 0.3) || !((double)pitch_index > 1.26 * (double)this.prefilter_period) && !((double)pitch_index < 0.79 * (double)this.prefilter_period))) { + pitch_change = 1; + } + if (pf_on == 0) { + if (start == 0 && tell + 16 <= total_bits) { + enc.enc_bit_logp(0, 1); + } + } else { + enc.enc_bit_logp(1, 1); + int octave = Inlines.EC_ILOG(++pitch_index) - 5; + enc.enc_uint(octave, 6L); + enc.enc_bits(pitch_index - (16 << octave), 4 + octave); + --pitch_index; + enc.enc_bits(qg, 3); + enc.enc_icdf(prefilter_tapset, CeltTables.tapset_icdf, 2); + } + isTransient = 0; + shortBlocks = 0; + if (this.complexity >= 1 && this.lfe == 0) { + BoxedValueInt boxed_tf_estimate = new BoxedValueInt(0); + BoxedValueInt boxed_tf_chan = new BoxedValueInt(0); + isTransient = CeltCommon.transient_analysis(input, N + overlap, CC, boxed_tf_estimate, boxed_tf_chan); + tf_estimate = boxed_tf_estimate.Val; + tf_chan = boxed_tf_chan.Val; + } + if (LM > 0 && enc.tell() + 3 <= total_bits) { + if (isTransient != 0) { + shortBlocks = M; + } + } else { + isTransient = 0; + transient_got_disabled = true; + } + int[][] freq = Arrays.InitTwoDimensionalArrayInt(CC, N); + int[][] bandE = Arrays.InitTwoDimensionalArrayInt(CC, nbEBands); + int[][] bandLogE = Arrays.InitTwoDimensionalArrayInt(CC, nbEBands); + boolean secondMdct = shortBlocks != 0 && this.complexity >= 8; + int[][] bandLogE2 = Arrays.InitTwoDimensionalArrayInt(CC, nbEBands); + if (secondMdct) { + CeltCommon.compute_mdcts(mode, 0, input, freq, C, CC, LM, this.upsample); + Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM); + QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE2, C); + i = 0; + while (i < nbEBands) { + int[] nArray = bandLogE2[0]; + int n2 = i++; + nArray[n2] = nArray[n2] + Inlines.HALF16(Inlines.SHL16(LM, 10)); + } + if (C == 2) { + i = 0; + while (i < nbEBands) { + int[] nArray = bandLogE2[1]; + int n3 = i++; + nArray[n3] = nArray[n3] + Inlines.HALF16(Inlines.SHL16(LM, 10)); + } + } + } + CeltCommon.compute_mdcts(mode, shortBlocks, input, freq, C, CC, LM, this.upsample); + if (CC == 2 && C == 1) { + tf_chan = 0; + } + Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM); + if (this.lfe != 0) { + for (i = 2; i < end; ++i) { + bandE[0][i] = Inlines.IMIN(bandE[0][i], Inlines.MULT16_32_Q15((short)3, bandE[0][0])); + bandE[0][i] = Inlines.MAX32(bandE[0][i], 1); + } + } + QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE, C); + int[] surround_dynalloc = new int[C * nbEBands]; + if (start == 0 && this.energy_mask != null && this.lfe == 0) { + int mask_avg = 0; + int diff = 0; + int count = 0; + int mask_end = Inlines.IMAX(2, this.lastCodedBands); + for (c = 0; c < C; ++c) { + for (i = 0; i < mask_end; ++i) { + int mask = Inlines.MAX16(Inlines.MIN16(this.energy_mask[nbEBands * c + i], 256), -2048); + if (mask > 0) { + mask = Inlines.HALF16(mask); + } + mask_avg += Inlines.MULT16_16(mask, eBands[i + 1] - eBands[i]); + count += eBands[i + 1] - eBands[i]; + diff += Inlines.MULT16_16(mask, 1 + 2 * i - mask_end); + } + } + Inlines.OpusAssert(count > 0); + mask_avg = Inlines.DIV32_16(mask_avg, count); + mask_avg += 205; + diff = diff * 6 / (C * (mask_end - 1) * (mask_end + 1) * mask_end); + diff = Inlines.HALF32(diff); + diff = Inlines.MAX32(Inlines.MIN32(diff, 32), -32); + int midband = 0; + while (eBands[midband + 1] < eBands[mask_end] / 2) { + ++midband; + } + int count_dynalloc = 0; + for (i = 0; i < mask_end; ++i) { + int lin = mask_avg + diff * (i - midband); + int unmask = C == 2 ? Inlines.MAX16(this.energy_mask[i], this.energy_mask[nbEBands + i]) : this.energy_mask[i]; + unmask = Inlines.MIN16(unmask, 0); + if ((unmask -= lin) <= 256) continue; + surround_dynalloc[i] = unmask - 256; + ++count_dynalloc; + } + if (count_dynalloc >= 3) { + if ((mask_avg += 256) > 0) { + mask_avg = 0; + diff = 0; + Arrays.MemSet(surround_dynalloc, 0, mask_end); + } else { + for (i = 0; i < mask_end; ++i) { + surround_dynalloc[i] = Inlines.MAX16(0, surround_dynalloc[i] - 256); + } + } + } + surround_trim = 64 * diff; + surround_masking = mask_avg += 205; + } + if (this.lfe == 0) { + int follow = -10240; + int frame_avg = 0; + int offset = shortBlocks != 0 ? Inlines.HALF16(Inlines.SHL16(LM, 10)) : 0; + for (i = start; i < end; ++i) { + follow = Inlines.MAX16(follow - 1024, bandLogE[0][i] - offset); + if (C == 2) { + follow = Inlines.MAX16(follow, bandLogE[1][i] - offset); + } + frame_avg += follow; + } + temporal_vbr = Inlines.SUB16(frame_avg /= end - start, this.spec_avg); + temporal_vbr = Inlines.MIN16(3072, Inlines.MAX16(-1536, temporal_vbr)); + this.spec_avg += (short)Inlines.MULT16_16_Q15(655, temporal_vbr); + } + if (!secondMdct) { + System.arraycopy(bandLogE[0], 0, bandLogE2[0], 0, nbEBands); + if (C == 2) { + System.arraycopy(bandLogE[1], 0, bandLogE2[1], 0, nbEBands); + } + } + if (LM > 0 && enc.tell() + 3 <= total_bits && isTransient == 0 && this.complexity >= 5 && this.lfe == 0 && CeltCommon.patch_transient_decision(bandLogE, this.oldBandE, nbEBands, start, end, C) != 0) { + isTransient = 1; + shortBlocks = M; + CeltCommon.compute_mdcts(mode, shortBlocks, input, freq, C, CC, LM, this.upsample); + Bands.compute_band_energies(mode, freq, bandE, effEnd, C, LM); + QuantizeBands.amp2Log2(mode, effEnd, end, bandE, bandLogE, C); + i = 0; + while (i < nbEBands) { + int[] nArray = bandLogE2[0]; + int n4 = i++; + nArray[n4] = nArray[n4] + Inlines.HALF16(Inlines.SHL16(LM, 10)); + } + if (C == 2) { + i = 0; + while (i < nbEBands) { + int[] nArray = bandLogE2[1]; + int n5 = i++; + nArray[n5] = nArray[n5] + Inlines.HALF16(Inlines.SHL16(LM, 10)); + } + } + tf_estimate = 3277; + } + if (LM > 0 && enc.tell() + 3 <= total_bits) { + enc.enc_bit_logp(isTransient, 3); + } + int[][] X = Arrays.InitTwoDimensionalArrayInt(C, N); + Bands.normalise_bands(mode, freq, X, bandE, effEnd, C, M); + int[] tf_res = new int[nbEBands]; + if (effectiveBytes >= 15 * C && start == 0 && this.complexity >= 2 && this.lfe == 0) { + int lambda = effectiveBytes < 40 ? 12 : (effectiveBytes < 60 ? 6 : (effectiveBytes < 100 ? 4 : 3)); + BoxedValueInt boxed_tf_sum = new BoxedValueInt(0); + tf_select = CeltCommon.tf_analysis(mode, effEnd, isTransient, tf_res, lambda *= 2, X, N, LM, boxed_tf_sum, tf_estimate, tf_chan); + tf_sum = boxed_tf_sum.Val; + for (i = effEnd; i < end; ++i) { + tf_res[i] = tf_res[effEnd - 1]; + } + } else { + tf_sum = 0; + for (i = 0; i < end; ++i) { + tf_res[i] = isTransient; + } + tf_select = 0; + } + int[][] error = Arrays.InitTwoDimensionalArrayInt(C, nbEBands); + BoxedValueInt boxed_delayedintra = new BoxedValueInt(this.delayedIntra); + QuantizeBands.quant_coarse_energy(mode, start, end, effEnd, bandLogE, this.oldBandE, total_bits, error, enc, C, LM, nbAvailableBytes, this.force_intra, boxed_delayedintra, this.complexity >= 4 ? 1 : 0, this.loss_rate, this.lfe); + this.delayedIntra = boxed_delayedintra.Val; + CeltCommon.tf_encode(start, end, isTransient, tf_res, LM, tf_select, enc); + if (enc.tell() + 4 <= total_bits) { + if (this.lfe != 0) { + this.tapset_decision = 0; + this.spread_decision = 2; + } else if (shortBlocks != 0 || this.complexity < 3 || nbAvailableBytes < 10 * C || start != 0) { + this.spread_decision = this.complexity == 0 ? 0 : 2; + } else { + BoxedValueInt boxed_tonal_average = new BoxedValueInt(this.tonal_average); + BoxedValueInt boxed_tapset_decision = new BoxedValueInt(this.tapset_decision); + BoxedValueInt boxed_hf_average = new BoxedValueInt(this.hf_average); + this.spread_decision = Bands.spreading_decision(mode, X, boxed_tonal_average, this.spread_decision, boxed_hf_average, boxed_tapset_decision, pf_on != 0 && shortBlocks == 0 ? 1 : 0, effEnd, C, M); + this.tonal_average = boxed_tonal_average.Val; + this.tapset_decision = boxed_tapset_decision.Val; + this.hf_average = boxed_hf_average.Val; + } + enc.enc_icdf(this.spread_decision, CeltTables.spread_icdf, 5); + } + int[] offsets = new int[nbEBands]; + BoxedValueInt boxed_tot_boost = new BoxedValueInt(0); + int maxDepth = CeltCommon.dynalloc_analysis(bandLogE, bandLogE2, nbEBands, start, end, C, offsets, this.lsb_depth, mode.logN, isTransient, this.vbr, this.constrained_vbr, eBands, LM, effectiveBytes, boxed_tot_boost, this.lfe, surround_dynalloc); + int tot_boost = boxed_tot_boost.Val; + if (this.lfe != 0) { + offsets[0] = Inlines.IMIN(8, effectiveBytes / 3); + } + int[] cap = new int[nbEBands]; + CeltCommon.init_caps(mode, cap, LM, C); + int dynalloc_logp = 6; + total_bits <<= 3; + int total_boost = 0; + tell = enc.tell_frac(); + for (i = start; i < end; ++i) { + int width = C * (eBands[i + 1] - eBands[i]) << LM; + int quanta = Inlines.IMIN(width << 3, Inlines.IMAX(48, width)); + int dynalloc_loop_logp = dynalloc_logp; + int boost = 0; + int j = 0; + while (tell + (dynalloc_loop_logp << 3) < total_bits - total_boost && boost < cap[i]) { + int flag = j < offsets[i] ? 1 : 0; + enc.enc_bit_logp(flag, dynalloc_loop_logp); + tell = enc.tell_frac(); + if (flag == 0) break; + boost += quanta; + total_boost += quanta; + dynalloc_loop_logp = 1; + ++j; + } + if (j != 0) { + dynalloc_logp = Inlines.IMAX(2, dynalloc_logp - 1); + } + offsets[i] = boost; + } + if (C == 2) { + if (LM != 0) { + dual_stereo = CeltCommon.stereo_analysis(mode, X, LM); + } + this.intensity = Bands.hysteresis_decision(equiv_rate / 1000, CeltTables.intensity_thresholds, CeltTables.intensity_histeresis, 21, this.intensity); + this.intensity = Inlines.IMIN(end, Inlines.IMAX(start, this.intensity)); + } + int alloc_trim = 5; + if (tell + 48 <= total_bits - total_boost) { + if (this.lfe != 0) { + alloc_trim = 5; + } else { + BoxedValueInt boxed_stereo_saving = new BoxedValueInt(this.stereo_saving); + alloc_trim = CeltCommon.alloc_trim_analysis(mode, X, bandLogE, end, LM, C, this.analysis, boxed_stereo_saving, tf_estimate, this.intensity, surround_trim); + this.stereo_saving = boxed_stereo_saving.Val; + } + enc.enc_icdf(alloc_trim, CeltTables.trim_icdf, 7); + tell = enc.tell_frac(); + } + if (vbr_rate > 0) { + int alpha; + int lm_diff = mode.maxLM - LM; + nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, 1275 >> 3 - LM); + int base_target = vbr_rate - (40 * C + 20 << 3); + if (this.constrained_vbr != 0) { + base_target += this.vbr_offset >> lm_diff; + } + int target = CeltCommon.compute_vbr(mode, this.analysis, base_target, LM, equiv_rate, this.lastCodedBands, C, this.intensity, this.constrained_vbr, this.stereo_saving, tot_boost, tf_estimate, pitch_change, maxDepth, this.variable_duration, this.lfe, this.energy_mask != null ? 1 : 0, surround_masking, temporal_vbr); + int min_allowed = (tell + total_boost + 64 - 1 >> 6) + 2 - nbFilledBytes; + nbAvailableBytes = (target += tell) + 32 >> 6; + nbAvailableBytes = Inlines.IMAX(min_allowed, nbAvailableBytes); + nbAvailableBytes = Inlines.IMIN(nbCompressedBytes, nbAvailableBytes + nbFilledBytes) - nbFilledBytes; + int delta = target - vbr_rate; + target = nbAvailableBytes << 6; + if (silence != 0) { + nbAvailableBytes = 2; + target = 128; + delta = 0; + } + if (this.vbr_count < 970) { + ++this.vbr_count; + alpha = Inlines.celt_rcp(Inlines.SHL32(this.vbr_count + 20, 16)); + } else { + alpha = 33; + } + if (this.constrained_vbr != 0) { + this.vbr_reservoir += target - vbr_rate; + } + if (this.constrained_vbr != 0) { + this.vbr_drift += Inlines.MULT16_32_Q15(alpha, delta * (1 << lm_diff) - this.vbr_offset - this.vbr_drift); + this.vbr_offset = -this.vbr_drift; + } + if (this.constrained_vbr != 0 && this.vbr_reservoir < 0) { + int adjust = -this.vbr_reservoir / 64; + nbAvailableBytes += silence != 0 ? 0 : adjust; + this.vbr_reservoir = 0; + } + nbCompressedBytes = Inlines.IMIN(nbCompressedBytes, nbAvailableBytes + nbFilledBytes); + enc.enc_shrink(nbCompressedBytes); + } + int[] fine_quant = new int[nbEBands]; + int[] pulses = new int[nbEBands]; + int[] fine_priority = new int[nbEBands]; + int bits = (nbCompressedBytes * 8 << 3) - enc.tell_frac() - 1; + int anti_collapse_rsv = isTransient != 0 && LM >= 2 && bits >= LM + 2 << 3 ? 8 : 0; + bits -= anti_collapse_rsv; + int signalBandwidth = end - 1; + if (this.analysis.enabled && this.analysis.valid != 0) { + int min_bandwidth = equiv_rate < 32000 * C ? 13 : (equiv_rate < 48000 * C ? 16 : (equiv_rate < 60000 * C ? 18 : (equiv_rate < 80000 * C ? 19 : 20))); + signalBandwidth = Inlines.IMAX(this.analysis.bandwidth, min_bandwidth); + } + if (this.lfe != 0) { + signalBandwidth = 1; + } + BoxedValueInt boxed_intensity = new BoxedValueInt(this.intensity); + BoxedValueInt boxed_balance = new BoxedValueInt(0); + BoxedValueInt boxed_dual_stereo = new BoxedValueInt(dual_stereo); + int codedBands = Rate.compute_allocation(mode, start, end, offsets, cap, alloc_trim, boxed_intensity, boxed_dual_stereo, bits, boxed_balance, pulses, fine_quant, fine_priority, C, LM, enc, 1, this.lastCodedBands, signalBandwidth); + this.intensity = boxed_intensity.Val; + int balance = boxed_balance.Val; + dual_stereo = boxed_dual_stereo.Val; + this.lastCodedBands = this.lastCodedBands != 0 ? Inlines.IMIN(this.lastCodedBands + 1, Inlines.IMAX(this.lastCodedBands - 1, codedBands)) : codedBands; + QuantizeBands.quant_fine_energy(mode, start, end, this.oldBandE, error, fine_quant, enc, C); + short[] collapse_masks = new short[C * nbEBands]; + BoxedValueInt boxed_rng = new BoxedValueInt(this.rng); + Bands.quant_all_bands(1, mode, start, end, X[0], C == 2 ? X[1] : null, collapse_masks, bandE, pulses, shortBlocks, this.spread_decision, dual_stereo, this.intensity, tf_res, nbCompressedBytes * 64 - anti_collapse_rsv, balance, enc, LM, codedBands, boxed_rng); + this.rng = boxed_rng.Val; + if (anti_collapse_rsv > 0) { + anti_collapse_on = this.consec_transient < 2 ? 1 : 0; + enc.enc_bits(anti_collapse_on, 1); + } + QuantizeBands.quant_energy_finalise(mode, start, end, this.oldBandE, error, fine_quant, fine_priority, nbCompressedBytes * 8 - enc.tell(), enc, C); + if (silence != 0) { + for (i = 0; i < nbEBands; ++i) { + this.oldBandE[0][i] = -28672; + } + if (C == 2) { + for (i = 0; i < nbEBands; ++i) { + this.oldBandE[1][i] = -28672; + } + } + } + this.prefilter_period = pitch_index; + this.prefilter_gain = gain1; + this.prefilter_tapset = prefilter_tapset; + if (CC == 2 && C == 1) { + System.arraycopy(this.oldBandE[0], 0, this.oldBandE[1], 0, nbEBands); + } + if (isTransient == 0) { + System.arraycopy(this.oldLogE[0], 0, this.oldLogE2[0], 0, nbEBands); + System.arraycopy(this.oldBandE[0], 0, this.oldLogE[0], 0, nbEBands); + if (CC == 2) { + System.arraycopy(this.oldLogE[1], 0, this.oldLogE2[1], 0, nbEBands); + System.arraycopy(this.oldBandE[1], 0, this.oldLogE[1], 0, nbEBands); + } + } else { + for (i = 0; i < nbEBands; ++i) { + this.oldLogE[0][i] = Inlines.MIN16(this.oldLogE[0][i], this.oldBandE[0][i]); + } + if (CC == 2) { + for (i = 0; i < nbEBands; ++i) { + this.oldLogE[1][i] = Inlines.MIN16(this.oldLogE[1][i], this.oldBandE[1][i]); + } + } + } + c = 0; + do { + for (i = 0; i < start; ++i) { + this.oldBandE[c][i] = 0; + this.oldLogE2[c][i] = -28672; + this.oldLogE[c][i] = -28672; + } + for (i = end; i < nbEBands; ++i) { + this.oldBandE[c][i] = 0; + this.oldLogE2[c][i] = -28672; + this.oldLogE[c][i] = -28672; + } + } while (++c < CC); + this.consec_transient = isTransient != 0 || transient_got_disabled ? ++this.consec_transient : 0; + this.rng = (int)enc.rng; + enc.enc_done(); + if (enc.get_error() != 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + return nbCompressedBytes; + } + + void SetComplexity(int value) { + if (value < 0 || value > 10) { + throw new IllegalArgumentException("Complexity must be between 0 and 10 inclusive"); + } + this.complexity = value; + } + + void SetStartBand(int value) { + if (value < 0 || value >= this.mode.nbEBands) { + throw new IllegalArgumentException("Start band above max number of ebands (or negative)"); + } + this.start = value; + } + + void SetEndBand(int value) { + if (value < 1 || value > this.mode.nbEBands) { + throw new IllegalArgumentException("End band above max number of ebands (or less than 1)"); + } + this.end = value; + } + + void SetPacketLossPercent(int value) { + if (value < 0 || value > 100) { + throw new IllegalArgumentException("Packet loss must be between 0 and 100"); + } + this.loss_rate = value; + } + + void SetPrediction(int value) { + if (value < 0 || value > 2) { + throw new IllegalArgumentException("CELT prediction mode must be 0, 1, or 2"); + } + this.disable_pf = value <= 1 ? 1 : 0; + this.force_intra = value == 0 ? 1 : 0; + } + + void SetVBRConstraint(boolean value) { + this.constrained_vbr = value ? 1 : 0; + } + + void SetVBR(boolean value) { + this.vbr = value ? 1 : 0; + } + + void SetBitrate(int value) { + if (value <= 500 && value != -1) { + throw new IllegalArgumentException("Bitrate out of range"); + } + this.bitrate = value = Inlines.IMIN(value, 260000 * this.channels); + } + + void SetChannels(int value) { + if (value < 1 || value > 2) { + throw new IllegalArgumentException("Channel count must be 1 or 2"); + } + this.stream_channels = value; + } + + void SetLSBDepth(int value) { + if (value < 8 || value > 24) { + throw new IllegalArgumentException("Bit depth must be between 8 and 24"); + } + this.lsb_depth = value; + } + + int GetLSBDepth() { + return this.lsb_depth; + } + + void SetExpertFrameDuration(OpusFramesize value) { + this.variable_duration = value; + } + + void SetSignalling(int value) { + this.signalling = value; + } + + void SetAnalysis(AnalysisInfo value) { + if (value == null) { + throw new IllegalArgumentException("AnalysisInfo"); + } + this.analysis.Assign(value); + } + + CeltMode GetMode() { + return this.mode; + } + + int GetFinalRange() { + return this.rng; + } + + void SetLFE(int value) { + this.lfe = value; + } + + void SetEnergyMask(int[] value) { + this.energy_mask = value; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CeltLPC.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltLPC.java new file mode 100644 index 0000000..fbd180e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltLPC.java @@ -0,0 +1,99 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Kernels; + +class CeltLPC { + CeltLPC() { + } + + static void celt_lpc(int[] _lpc, int[] ac, int p) { + int i; + int error = ac[0]; + int[] lpc = new int[p]; + if (ac[0] != 0) { + for (i = 0; i < p; ++i) { + int j; + int rr = 0; + for (j = 0; j < i; ++j) { + rr += Inlines.MULT32_32_Q31(lpc[j], ac[i - j]); + } + int r = 0 - Inlines.frac_div32(Inlines.SHL32(rr += Inlines.SHR32(ac[i + 1], 3), 3), error); + lpc[i] = Inlines.SHR32(r, 3); + for (j = 0; j < i + 1 >> 1; ++j) { + int tmp1 = lpc[j]; + int tmp2 = lpc[i - 1 - j]; + lpc[j] = tmp1 + Inlines.MULT32_32_Q31(r, tmp2); + lpc[i - 1 - j] = tmp2 + Inlines.MULT32_32_Q31(r, tmp1); + } + if ((error -= Inlines.MULT32_32_Q31(Inlines.MULT32_32_Q31(r, r), error)) < Inlines.SHR32(ac[0], 10)) break; + } + } + for (i = 0; i < p; ++i) { + _lpc[i] = Inlines.ROUND16(lpc[i], 16); + } + } + + static void celt_iir(int[] _x, int _x_ptr, int[] den, int[] _y, int _y_ptr, int N, int ord, int[] mem) { + int i; + int[] rden = new int[ord]; + int[] y = new int[N + ord]; + Inlines.OpusAssert((ord & 3) == 0); + BoxedValueInt _sum0 = new BoxedValueInt(0); + BoxedValueInt _sum1 = new BoxedValueInt(0); + BoxedValueInt _sum2 = new BoxedValueInt(0); + BoxedValueInt _sum3 = new BoxedValueInt(0); + for (i = 0; i < ord; ++i) { + rden[i] = den[ord - i - 1]; + } + for (i = 0; i < ord; ++i) { + y[i] = 0 - mem[ord - i - 1]; + } + while (i < N + ord) { + y[i] = 0; + ++i; + } + for (i = 0; i < N - 3; i += 4) { + _sum0.Val = _x[_x_ptr + i]; + _sum1.Val = _x[_x_ptr + i + 1]; + _sum2.Val = _x[_x_ptr + i + 2]; + _sum3.Val = _x[_x_ptr + i + 3]; + Kernels.xcorr_kernel(rden, y, i, _sum0, _sum1, _sum2, _sum3, ord); + int sum0 = _sum0.Val; + int sum1 = _sum1.Val; + int sum2 = _sum2.Val; + int sum3 = _sum3.Val; + y[i + ord] = 0 - Inlines.ROUND16(sum0, 12); + _y[_y_ptr + i] = sum0; + sum1 = Inlines.MAC16_16(sum1, y[i + ord], den[0]); + y[i + ord + 1] = 0 - Inlines.ROUND16(sum1, 12); + _y[_y_ptr + i + 1] = sum1; + sum2 = Inlines.MAC16_16(sum2, y[i + ord + 1], den[0]); + sum2 = Inlines.MAC16_16(sum2, y[i + ord], den[1]); + y[i + ord + 2] = 0 - Inlines.ROUND16(sum2, 12); + _y[_y_ptr + i + 2] = sum2; + sum3 = Inlines.MAC16_16(sum3, y[i + ord + 2], den[0]); + sum3 = Inlines.MAC16_16(sum3, y[i + ord + 1], den[1]); + sum3 = Inlines.MAC16_16(sum3, y[i + ord], den[2]); + y[i + ord + 3] = 0 - Inlines.ROUND16(sum3, 12); + _y[_y_ptr + i + 3] = sum3; + } + while (i < N) { + int sum = _x[_x_ptr + i]; + for (int j = 0; j < ord; ++j) { + sum -= Inlines.MULT16_16(rden[j], y[i + j]); + } + y[i + ord] = Inlines.ROUND16(sum, 12); + _y[_y_ptr + i] = sum; + ++i; + } + for (i = 0; i < ord; ++i) { + mem[i] = _y[_y_ptr + N - i - 1]; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CeltMode.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltMode.java new file mode 100644 index 0000000..fdc5261 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltMode.java @@ -0,0 +1,58 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.CeltTables; +import de.maxhenkel.voicechat.concentus.FFTState; +import de.maxhenkel.voicechat.concentus.MDCTLookup; +import de.maxhenkel.voicechat.concentus.PulseCache; + +class CeltMode { + int Fs = 0; + int overlap = 0; + int nbEBands = 0; + int effEBands = 0; + int[] preemph = new int[]{0, 0, 0, 0}; + short[] eBands = null; + int maxLM = 0; + int nbShortMdcts = 0; + int shortMdctSize = 0; + int nbAllocVectors = 0; + short[] allocVectors = null; + short[] logN = null; + int[] window = null; + MDCTLookup mdct = new MDCTLookup(); + PulseCache cache = new PulseCache(); + static final CeltMode mode48000_960_120 = new CeltMode(); + + private CeltMode() { + } + + static { + CeltMode.mode48000_960_120.Fs = 48000; + CeltMode.mode48000_960_120.overlap = 120; + CeltMode.mode48000_960_120.nbEBands = 21; + CeltMode.mode48000_960_120.effEBands = 21; + CeltMode.mode48000_960_120.preemph = new int[]{27853, 0, 4096, 8192}; + CeltMode.mode48000_960_120.eBands = CeltTables.eband5ms; + CeltMode.mode48000_960_120.maxLM = 3; + CeltMode.mode48000_960_120.nbShortMdcts = 8; + CeltMode.mode48000_960_120.shortMdctSize = 120; + CeltMode.mode48000_960_120.nbAllocVectors = 11; + CeltMode.mode48000_960_120.allocVectors = CeltTables.band_allocation; + CeltMode.mode48000_960_120.logN = CeltTables.logN400; + CeltMode.mode48000_960_120.window = CeltTables.window120; + CeltMode.mode48000_960_120.mdct = new MDCTLookup(); + CeltMode.mode48000_960_120.mdct.n = 1920; + CeltMode.mode48000_960_120.mdct.maxshift = 3; + CeltMode.mode48000_960_120.mdct.kfft = new FFTState[]{CeltTables.fft_state48000_960_0, CeltTables.fft_state48000_960_1, CeltTables.fft_state48000_960_2, CeltTables.fft_state48000_960_3}; + CeltMode.mode48000_960_120.mdct.trig = CeltTables.mdct_twiddles960; + CeltMode.mode48000_960_120.cache = new PulseCache(); + CeltMode.mode48000_960_120.cache.size = 392; + CeltMode.mode48000_960_120.cache.index = CeltTables.cache_index50; + CeltMode.mode48000_960_120.cache.bits = CeltTables.cache_bits50; + CeltMode.mode48000_960_120.cache.caps = CeltTables.cache_caps50; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CeltPitchXCorr.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltPitchXCorr.java new file mode 100644 index 0000000..e32c8ee --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltPitchXCorr.java @@ -0,0 +1,110 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Kernels; + +class CeltPitchXCorr { + CeltPitchXCorr() { + } + + static int pitch_xcorr(int[] _x, int[] _y, int[] xcorr, int len, int max_pitch) { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + for (i = 0; i < max_pitch - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + Kernels.xcorr_kernel(_x, _y, i, sum0, sum1, sum2, sum3, len); + xcorr[i] = sum0.Val; + xcorr[i + 1] = sum1.Val; + xcorr[i + 2] = sum2.Val; + xcorr[i + 3] = sum3.Val; + sum0.Val = Inlines.MAX32(sum0.Val, sum1.Val); + sum2.Val = Inlines.MAX32(sum2.Val, sum3.Val); + sum0.Val = Inlines.MAX32(sum0.Val, sum2.Val); + maxcorr = Inlines.MAX32(maxcorr, sum0.Val); + } + while (i < max_pitch) { + int inner_sum; + xcorr[i] = inner_sum = Kernels.celt_inner_prod(_x, 0, _y, i, len); + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + ++i; + } + return maxcorr; + } + + static int pitch_xcorr(short[] _x, int _x_ptr, short[] _y, int _y_ptr, int[] xcorr, int len, int max_pitch) { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + for (i = 0; i < max_pitch - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + Kernels.xcorr_kernel(_x, _x_ptr, _y, _y_ptr + i, sum0, sum1, sum2, sum3, len); + xcorr[i] = sum0.Val; + xcorr[i + 1] = sum1.Val; + xcorr[i + 2] = sum2.Val; + xcorr[i + 3] = sum3.Val; + sum0.Val = Inlines.MAX32(sum0.Val, sum1.Val); + sum2.Val = Inlines.MAX32(sum2.Val, sum3.Val); + sum0.Val = Inlines.MAX32(sum0.Val, sum2.Val); + maxcorr = Inlines.MAX32(maxcorr, sum0.Val); + } + while (i < max_pitch) { + int inner_sum; + xcorr[i] = inner_sum = Kernels.celt_inner_prod(_x, _x_ptr, _y, _y_ptr + i, len); + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + ++i; + } + return maxcorr; + } + + static int pitch_xcorr(short[] _x, short[] _y, int[] xcorr, int len, int max_pitch) { + int i; + int maxcorr = 1; + Inlines.OpusAssert(max_pitch > 0); + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + for (i = 0; i < max_pitch - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + Kernels.xcorr_kernel(_x, 0, _y, i, sum0, sum1, sum2, sum3, len); + xcorr[i] = sum0.Val; + xcorr[i + 1] = sum1.Val; + xcorr[i + 2] = sum2.Val; + xcorr[i + 3] = sum3.Val; + sum0.Val = Inlines.MAX32(sum0.Val, sum1.Val); + sum2.Val = Inlines.MAX32(sum2.Val, sum3.Val); + sum0.Val = Inlines.MAX32(sum0.Val, sum2.Val); + maxcorr = Inlines.MAX32(maxcorr, sum0.Val); + } + while (i < max_pitch) { + int inner_sum; + xcorr[i] = inner_sum = Kernels.celt_inner_prod(_x, _y, i, len); + maxcorr = Inlines.MAX32(maxcorr, inner_sum); + ++i; + } + return maxcorr; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CeltTables.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltTables.java new file mode 100644 index 0000000..bfa9a09 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CeltTables.java @@ -0,0 +1,80 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.FFTState; + +class CeltTables { + static final byte[] eMeans = new byte[]{103, 100, 92, 85, 81, 77, 72, 70, 78, 75, 73, 71, 78, 74, 69, 72, 70, 74, 76, 71, 60, 60, 60, 60, 60}; + static final int[] ordery_table = new int[]{1, 0, 3, 0, 2, 1, 7, 0, 4, 3, 6, 1, 5, 2, 15, 0, 8, 7, 12, 3, 11, 4, 14, 1, 9, 6, 13, 2, 10, 5}; + static final short[] eband5ms = new short[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100}; + static final short[][][] e_prob_model = new short[][][]{new short[][]{{72, 127, 65, 129, 66, 128, 65, 128, 64, 128, 62, 128, 64, 128, 64, 128, 92, 78, 92, 79, 92, 78, 90, 79, 116, 41, 115, 40, 114, 40, 132, 26, 132, 26, 145, 17, 161, 12, 176, 10, 177, 11}, {24, 179, 48, 138, 54, 135, 54, 132, 53, 134, 56, 133, 55, 132, 55, 132, 61, 114, 70, 96, 74, 88, 75, 88, 87, 74, 89, 66, 91, 67, 100, 59, 108, 50, 120, 40, 122, 37, 97, 43, 78, 50}}, new short[][]{{83, 78, 84, 81, 88, 75, 86, 74, 87, 71, 90, 73, 93, 74, 93, 74, 109, 40, 114, 36, 117, 34, 117, 34, 143, 17, 145, 18, 146, 19, 162, 12, 165, 10, 178, 7, 189, 6, 190, 8, 177, 9}, {23, 178, 54, 115, 63, 102, 66, 98, 69, 99, 74, 89, 71, 91, 73, 91, 78, 89, 86, 80, 92, 66, 93, 64, 102, 59, 103, 60, 104, 60, 117, 52, 123, 44, 138, 35, 133, 31, 97, 38, 77, 45}}, new short[][]{{61, 90, 93, 60, 105, 42, 107, 41, 110, 45, 116, 38, 113, 38, 112, 38, 124, 26, 132, 27, 136, 19, 140, 20, 155, 14, 159, 16, 158, 18, 170, 13, 177, 10, 187, 8, 192, 6, 175, 9, 159, 10}, {21, 178, 59, 110, 71, 86, 75, 85, 84, 83, 91, 66, 88, 73, 87, 72, 92, 75, 98, 72, 105, 58, 107, 54, 115, 52, 114, 55, 112, 56, 129, 51, 132, 40, 150, 33, 140, 29, 98, 35, 77, 42}}, new short[][]{{42, 121, 96, 66, 108, 43, 111, 40, 117, 44, 123, 32, 120, 36, 119, 33, 127, 33, 134, 34, 139, 21, 147, 23, 152, 20, 158, 25, 154, 26, 166, 21, 173, 16, 184, 13, 184, 10, 150, 13, 139, 15}, {22, 178, 63, 114, 74, 82, 84, 83, 92, 82, 103, 62, 96, 72, 96, 67, 101, 73, 107, 72, 113, 55, 118, 52, 125, 52, 118, 52, 117, 55, 135, 49, 137, 39, 157, 32, 145, 29, 97, 33, 77, 40}}}; + static final byte[][] tf_select_table = new byte[][]{{0, -1, 0, -1, 0, -1, 0, -1}, {0, -1, 0, -2, 1, 0, 1, -1}, {0, -2, 0, -3, 2, 0, 1, -1}, {0, -2, 0, -3, 3, 0, 1, -1}}; + static final short[] trim_icdf = new short[]{126, 124, 119, 109, 87, 41, 19, 9, 4, 2, 0}; + static final short[] spread_icdf = new short[]{25, 23, 2, 0}; + static final short[] tapset_icdf = new short[]{2, 1, 0}; + static final short[] band_allocation = new short[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90, 80, 75, 69, 63, 56, 49, 40, 34, 29, 20, 18, 10, 0, 0, 0, 0, 0, 0, 0, 0, 110, 100, 90, 84, 78, 71, 65, 58, 51, 45, 39, 32, 26, 20, 12, 0, 0, 0, 0, 0, 0, 118, 110, 103, 93, 86, 80, 75, 70, 65, 59, 53, 47, 40, 31, 23, 15, 4, 0, 0, 0, 0, 126, 119, 112, 104, 95, 89, 83, 78, 72, 66, 60, 54, 47, 39, 32, 25, 17, 12, 1, 0, 0, 134, 127, 120, 114, 103, 97, 91, 85, 78, 72, 66, 60, 54, 47, 41, 35, 29, 23, 16, 10, 1, 144, 137, 130, 124, 113, 107, 101, 95, 88, 82, 76, 70, 64, 57, 51, 45, 39, 33, 26, 15, 1, 152, 145, 138, 132, 123, 117, 111, 105, 98, 92, 86, 80, 74, 67, 61, 55, 49, 43, 36, 20, 1, 162, 155, 148, 142, 133, 127, 121, 115, 108, 102, 96, 90, 84, 77, 71, 65, 59, 53, 46, 30, 1, 172, 165, 158, 152, 143, 137, 131, 125, 118, 112, 106, 100, 94, 87, 81, 75, 69, 63, 56, 45, 20, 200, 200, 200, 200, 200, 200, 200, 200, 198, 193, 188, 183, 178, 173, 168, 163, 158, 153, 148, 129, 104}; + static final long[] CELT_PVQ_U_DATA = new long[]{1L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 3L, 5L, 7L, 9L, 11L, 13L, 15L, 17L, 19L, 21L, 23L, 25L, 27L, 29L, 31L, 33L, 35L, 37L, 39L, 41L, 43L, 45L, 47L, 49L, 51L, 53L, 55L, 57L, 59L, 61L, 63L, 65L, 67L, 69L, 71L, 73L, 75L, 77L, 79L, 81L, 83L, 85L, 87L, 89L, 91L, 93L, 95L, 97L, 99L, 101L, 103L, 105L, 107L, 109L, 111L, 113L, 115L, 117L, 119L, 121L, 123L, 125L, 127L, 129L, 131L, 133L, 135L, 137L, 139L, 141L, 143L, 145L, 147L, 149L, 151L, 153L, 155L, 157L, 159L, 161L, 163L, 165L, 167L, 169L, 171L, 173L, 175L, 177L, 179L, 181L, 183L, 185L, 187L, 189L, 191L, 193L, 195L, 197L, 199L, 201L, 203L, 205L, 207L, 209L, 211L, 213L, 215L, 217L, 219L, 221L, 223L, 225L, 227L, 229L, 231L, 233L, 235L, 237L, 239L, 241L, 243L, 245L, 247L, 249L, 251L, 253L, 255L, 257L, 259L, 261L, 263L, 265L, 267L, 269L, 271L, 273L, 275L, 277L, 279L, 281L, 283L, 285L, 287L, 289L, 291L, 293L, 295L, 297L, 299L, 301L, 303L, 305L, 307L, 309L, 311L, 313L, 315L, 317L, 319L, 321L, 323L, 325L, 327L, 329L, 331L, 333L, 335L, 337L, 339L, 341L, 343L, 345L, 347L, 349L, 351L, 13L, 25L, 41L, 61L, 85L, 113L, 145L, 181L, 221L, 265L, 313L, 365L, 421L, 481L, 545L, 613L, 685L, 761L, 841L, 925L, 1013L, 1105L, 1201L, 1301L, 1405L, 1513L, 1625L, 1741L, 1861L, 1985L, 2113L, 2245L, 2381L, 2521L, 2665L, 2813L, 2965L, 3121L, 3281L, 3445L, 3613L, 3785L, 3961L, 4141L, 4325L, 4513L, 4705L, 4901L, 5101L, 5305L, 5513L, 5725L, 5941L, 6161L, 6385L, 6613L, 6845L, 7081L, 7321L, 7565L, 7813L, 8065L, 8321L, 8581L, 8845L, 9113L, 9385L, 9661L, 9941L, 10225L, 10513L, 10805L, 11101L, 11401L, 11705L, 12013L, 12325L, 12641L, 12961L, 13285L, 13613L, 13945L, 14281L, 14621L, 14965L, 15313L, 15665L, 16021L, 16381L, 16745L, 17113L, 17485L, 17861L, 18241L, 18625L, 19013L, 19405L, 19801L, 20201L, 20605L, 21013L, 21425L, 21841L, 22261L, 22685L, 23113L, 23545L, 23981L, 24421L, 24865L, 25313L, 25765L, 26221L, 26681L, 27145L, 27613L, 28085L, 28561L, 29041L, 29525L, 30013L, 30505L, 31001L, 31501L, 32005L, 32513L, 33025L, 33541L, 34061L, 34585L, 35113L, 35645L, 36181L, 36721L, 37265L, 37813L, 38365L, 38921L, 39481L, 40045L, 40613L, 41185L, 41761L, 42341L, 42925L, 43513L, 44105L, 44701L, 45301L, 45905L, 46513L, 47125L, 47741L, 48361L, 48985L, 49613L, 50245L, 50881L, 51521L, 52165L, 52813L, 53465L, 54121L, 54781L, 55445L, 56113L, 56785L, 57461L, 58141L, 58825L, 59513L, 60205L, 60901L, 61601L, 63L, 129L, 231L, 377L, 575L, 833L, 1159L, 1561L, 2047L, 2625L, 3303L, 4089L, 4991L, 6017L, 7175L, 8473L, 9919L, 11521L, 13287L, 15225L, 17343L, 19649L, 22151L, 24857L, 27775L, 30913L, 34279L, 37881L, 41727L, 45825L, 50183L, 54809L, 59711L, 64897L, 70375L, 76153L, 82239L, 88641L, 95367L, 102425L, 109823L, 117569L, 125671L, 134137L, 142975L, 152193L, 161799L, 171801L, 182207L, 193025L, 204263L, 215929L, 228031L, 240577L, 253575L, 267033L, 280959L, 295361L, 310247L, 325625L, 341503L, 357889L, 374791L, 392217L, 410175L, 428673L, 447719L, 467321L, 487487L, 508225L, 529543L, 551449L, 573951L, 597057L, 620775L, 645113L, 670079L, 695681L, 721927L, 748825L, 776383L, 804609L, 833511L, 863097L, 893375L, 924353L, 956039L, 988441L, 1021567L, 1055425L, 1090023L, 1125369L, 1161471L, 1198337L, 1235975L, 1274393L, 1313599L, 1353601L, 1394407L, 1436025L, 1478463L, 1521729L, 1565831L, 1610777L, 1656575L, 1703233L, 1750759L, 1799161L, 1848447L, 1898625L, 1949703L, 2001689L, 2054591L, 2108417L, 2163175L, 2218873L, 2275519L, 2333121L, 2391687L, 2451225L, 2511743L, 2573249L, 2635751L, 0x292FF9L, 2763775L, 2829313L, 2895879L, 2963481L, 3032127L, 3101825L, 3172583L, 3244409L, 3317311L, 3391297L, 3466375L, 3542553L, 3619839L, 3698241L, 3777767L, 3858425L, 3940223L, 4023169L, 4107271L, 4192537L, 4278975L, 4366593L, 4455399L, 4545401L, 4636607L, 4729025L, 4822663L, 4917529L, 5013631L, 5110977L, 5209575L, 5309433L, 5410559L, 5512961L, 5616647L, 5721625L, 5827903L, 5935489L, 6044391L, 6154617L, 6266175L, 6379073L, 6493319L, 6608921L, 6725887L, 6844225L, 6963943L, 7085049L, 7207551L, 321L, 681L, 1289L, 2241L, 3649L, 5641L, 8361L, 11969L, 16641L, 22569L, 29961L, 39041L, 50049L, 63241L, 78889L, 97281L, 118721L, 143529L, 172041L, 204609L, 241601L, 283401L, 330409L, 383041L, 441729L, 506921L, 579081L, 658689L, 746241L, 842249L, 947241L, 1061761L, 1186369L, 1321641L, 1468169L, 1626561L, 1797441L, 1981449L, 2179241L, 2391489L, 2618881L, 2862121L, 3121929L, 3399041L, 3694209L, 4008201L, 4341801L, 4695809L, 5071041L, 5468329L, 5888521L, 6332481L, 6801089L, 7295241L, 7815849L, 8363841L, 8940161L, 9545769L, 10181641L, 10848769L, 11548161L, 12280841L, 13047849L, 13850241L, 14689089L, 15565481L, 16480521L, 17435329L, 18431041L, 19468809L, 20549801L, 21675201L, 22846209L, 24064041L, 25329929L, 26645121L, 28010881L, 29428489L, 30899241L, 32424449L, 34005441L, 35643561L, 37340169L, 39096641L, 40914369L, 42794761L, 0x2AAAAA9L, 46749249L, 48826241L, 50971689L, 53187081L, 55473921L, 57833729L, 60268041L, 62778409L, 65366401L, 68033601L, 70781609L, 73612041L, 76526529L, 79526721L, 82614281L, 85790889L, 89058241L, 92418049L, 95872041L, 99421961L, 103069569L, 106816641L, 110664969L, 114616361L, 118672641L, 122835649L, 127107241L, 131489289L, 135983681L, 140592321L, 145317129L, 150160041L, 155123009L, 160208001L, 165417001L, 170752009L, 176215041L, 181808129L, 187533321L, 193392681L, 199388289L, 205522241L, 211796649L, 218213641L, 224775361L, 231483969L, 238341641L, 245350569L, 252512961L, 259831041L, 267307049L, 274943241L, 282741889L, 290705281L, 298835721L, 307135529L, 315607041L, 324252609L, 333074601L, 342075401L, 351257409L, 360623041L, 370174729L, 379914921L, 389846081L, 399970689L, 410291241L, 420810249L, 431530241L, 442453761L, 453583369L, 464921641L, 476471169L, 488234561L, 500214441L, 512413449L, 524834241L, 537479489L, 550351881L, 563454121L, 576788929L, 590359041L, 604167209L, 618216201L, 632508801L, 1683L, 3653L, 7183L, 13073L, 22363L, 36365L, 56695L, 85305L, 124515L, 177045L, 246047L, 335137L, 448427L, 590557L, 766727L, 982729L, 1244979L, 1560549L, 1937199L, 2383409L, 2908411L, 3522221L, 4235671L, 5060441L, 6009091L, 7095093L, 8332863L, 9737793L, 11326283L, 13115773L, 15124775L, 17372905L, 19880915L, 22670725L, 25765455L, 29189457L, 32968347L, 37129037L, 41699767L, 46710137L, 52191139L, 58175189L, 64696159L, 71789409L, 79491819L, 87841821L, 96879431L, 106646281L, 117185651L, 128542501L, 140763503L, 153897073L, 167993403L, 183104493L, 199284183L, 216588185L, 235074115L, 254801525L, 275831935L, 298228865L, 322057867L, 347386557L, 374284647L, 402823977L, 433078547L, 465124549L, 499040399L, 534906769L, 572806619L, 612825229L, 655050231L, 699571641L, 746481891L, 795875861L, 847850911L, 902506913L, 959946283L, 1020274013L, 1083597703L, 1150027593L, 1219676595L, 1292660325L, 1369097135L, 1449108145L, 1532817275L, 1620351277L, 1711839767L, 1807415257L, 1907213187L, 2011371957L, 2120032959L, 8989L, 19825L, 40081L, 75517L, 134245L, 227305L, 369305L, 579125L, 880685L, 1303777L, 1884961L, 2668525L, 3707509L, 5064793L, 6814249L, 9041957L, 11847485L, 15345233L, 19665841L, 24957661L, 31388293L, 39146185L, 48442297L, 59511829L, 72616013L, 88043969L, 106114625L, 127178701L, 151620757L, 179861305L, 212358985L, 249612805L, 292164445L, 340600625L, 395555537L, 457713341L, 527810725L, 606639529L, 695049433L, 793950709L, 904317037L, 1027188385L, 1163673953L, 1314955181L, 1482288821L, 1667010073L, 1870535785L, 2094367717L, 48639L, 108545L, 224143L, 433905L, 795455L, 1392065L, 2340495L, 3800305L, 5984767L, 9173505L, 13726991L, 20103025L, 28875327L, 40754369L, 56610575L, 77500017L, 104692735L, 139703809L, 184327311L, 240673265L, 311207743L, 398796225L, 506750351L, 638878193L, 799538175L, 993696769L, 1226990095L, 1505789553L, 1837271615L, 2229491905L, 265729L, 598417L, 1256465L, 2485825L, 4673345L, 8405905L, 14546705L, 24331777L, 39490049L, 62390545L, 96220561L, 145198913L, 214828609L, 312193553L, 446304145L, 628496897L, 872893441L, 1196924561L, 1621925137L, 2173806145L, 1462563L, 3317445L, 7059735L, 14218905L, 27298155L, 50250765L, 89129247L, 152951073L, 254831667L, 413442773L, 654862247L, 1014889769L, 1541911931L, 2300409629L, 3375210671L, 8097453L, 18474633L, 39753273L, 81270333L, 158819253L, 298199265L, 540279585L, 948062325L, 1616336765L, 45046719L, 103274625L, 224298231L, 464387817L, 921406335L, 1759885185L, 3248227095L, 251595969L, 579168825L, 1267854873L, 2653649025L, 1409933619L}; + static final int[] window120 = new int[]{2, 20, 55, 108, 178, 266, 372, 494, 635, 792, 966, 1157, 1365, 1590, 1831, 2089, 2362, 2651, 2956, 3276, 3611, 3961, 4325, 4703, 5094, 5499, 5916, 6346, 6788, 7241, 7705, 8179, 8663, 9156, 9657, 10167, 10684, 11207, 11736, 12271, 12810, 13353, 13899, 14447, 14997, 15547, 16098, 16648, 17197, 17744, 18287, 18827, 19363, 19893, 20418, 20936, 21447, 21950, 22445, 22931, 23407, 23874, 24330, 24774, 25208, 25629, 26039, 26435, 26819, 27190, 27548, 27893, 28224, 28541, 28845, 29135, 29411, 29674, 29924, 30160, 30384, 30594, 30792, 30977, 31151, 31313, 31463, 31602, 31731, 31849, 31958, 32057, 32148, 32229, 32303, 32370, 32429, 32481, 32528, 32568, 32604, 32634, 32661, 32683, 32701, 32717, 32729, 32740, 32748, 32754, 32758, 32762, 32764, 32766, Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE}; + static final short[] logN400 = new short[]{0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 16, 16, 16, 21, 21, 24, 29, 34, 36}; + static final short[] cache_index50 = new short[]{-1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 41, 41, 41, 82, 82, 123, 164, 200, 222, 0, 0, 0, 0, 0, 0, 0, 0, 41, 41, 41, 41, 123, 123, 123, 164, 164, 240, 266, 283, 295, 41, 41, 41, 41, 41, 41, 41, 41, 123, 123, 123, 123, 240, 240, 240, 266, 266, 305, 318, 328, 336, 123, 123, 123, 123, 123, 123, 123, 123, 240, 240, 240, 240, 305, 305, 305, 318, 318, 343, 351, 358, 364, 240, 240, 240, 240, 240, 240, 240, 240, 305, 305, 305, 305, 343, 343, 343, 351, 351, 370, 376, 382, 387}; + static final short[] cache_bits50 = new short[]{40, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 40, 15, 23, 28, 31, 34, 36, 38, 39, 41, 42, 43, 44, 45, 46, 47, 47, 49, 50, 51, 52, 53, 54, 55, 55, 57, 58, 59, 60, 61, 62, 63, 63, 65, 66, 67, 68, 69, 70, 71, 71, 40, 20, 33, 41, 48, 53, 57, 61, 64, 66, 69, 71, 73, 75, 76, 78, 80, 82, 85, 87, 89, 91, 92, 94, 96, 98, 101, 103, 105, 107, 108, 110, 112, 114, 117, 119, 121, 123, 124, 126, 128, 40, 23, 39, 51, 60, 67, 73, 79, 83, 87, 91, 94, 97, 100, 102, 105, 107, 111, 115, 118, 121, 124, 126, 129, 131, 135, 139, 142, 145, 148, 150, 153, 155, 159, 163, 166, 169, 172, 174, 177, 179, 35, 28, 49, 65, 78, 89, 99, 107, 114, 120, 126, 132, 136, 141, 145, 149, 153, 159, 165, 171, 176, 180, 185, 189, 192, 199, 205, 211, 216, 220, 225, 229, 232, 239, 245, 251, 21, 33, 58, 79, 97, 112, 125, 137, 148, 157, 166, 174, 182, 189, 195, 201, 207, 217, 227, 235, 243, 251, 17, 35, 63, 86, 106, 123, 139, 152, 165, 177, 187, 197, 206, 214, 222, 230, 237, 250, 25, 31, 55, 75, 91, 105, 117, 128, 138, 146, 154, 161, 168, 174, 180, 185, 190, 200, 208, 215, 222, 229, 235, 240, 245, 255, 16, 36, 65, 89, 110, 128, 144, 159, 173, 185, 196, 207, 217, 226, 234, 242, 250, 11, 41, 74, 103, 128, 151, 172, 191, 209, 225, 241, 255, 9, 43, 79, 110, 138, 163, 186, 207, 227, 246, 12, 39, 71, 99, 123, 144, 164, 182, 198, 214, 228, 241, 253, 9, 44, 81, 113, 142, 168, 192, 214, 235, 255, 7, 49, 90, 127, 160, 191, 220, 247, 6, 51, 95, 134, 170, 203, 234, 7, 47, 87, 123, 155, 184, 212, 237, 6, 52, 97, 137, 174, 208, 240, 5, 57, 106, 151, 192, 231, 5, 59, 111, 158, 202, 243, 5, 55, 103, 147, 187, 224, 5, 60, 113, 161, 206, 248, 4, 65, 122, 175, 224, 4, 67, 127, 182, 234}; + static final short[] cache_caps50 = new short[]{224, 224, 224, 224, 224, 224, 224, 224, 160, 160, 160, 160, 185, 185, 185, 178, 178, 168, 134, 61, 37, 224, 224, 224, 224, 224, 224, 224, 224, 240, 240, 240, 240, 207, 207, 207, 198, 198, 183, 144, 66, 40, 160, 160, 160, 160, 160, 160, 160, 160, 185, 185, 185, 185, 193, 193, 193, 183, 183, 172, 138, 64, 38, 240, 240, 240, 240, 240, 240, 240, 240, 207, 207, 207, 207, 204, 204, 204, 193, 193, 180, 143, 66, 40, 185, 185, 185, 185, 185, 185, 185, 185, 193, 193, 193, 193, 193, 193, 193, 183, 183, 172, 138, 65, 39, 207, 207, 207, 207, 207, 207, 207, 207, 204, 204, 204, 204, 201, 201, 201, 188, 188, 176, 141, 66, 40, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 193, 194, 194, 194, 184, 184, 173, 139, 65, 39, 204, 204, 204, 204, 204, 204, 204, 204, 201, 201, 201, 201, 198, 198, 198, 187, 187, 175, 140, 66, 40}; + static final short[] fft_twiddles48000_960 = new short[]{Short.MAX_VALUE, 0, 32766, -429, 32757, -858, 32743, -1287, 32724, -1715, 32698, -2143, 32667, -2570, 32631, -2998, 32588, -3425, 32541, -3851, 32488, -4277, 32429, -4701, 32364, -5125, 32295, -5548, 32219, -5971, 32138, -6393, 32051, -6813, 31960, -7231, 31863, -7650, 31760, -8067, 31652, -8481, 31539, -8895, 31419, -9306, 31294, -9716, 31165, -10126, 31030, -10532, 30889, -10937, 30743, -11340, 30592, -11741, 30436, -12141, 30274, -12540, 30107, -12935, 29936, -13328, 29758, -13718, 29577, -14107, 29390, -14493, 29197, -14875, 29000, -15257, 28797, -15635, 28590, -16010, 28379, -16384, 28162, -16753, 27940, -17119, 27714, -17484, 27482, -17845, 27246, -18205, 27006, -18560, 26760, -18911, 26510, -19260, 26257, -19606, 25997, -19947, 25734, -20286, 25466, -20621, 25194, -20952, 24918, -21281, 24637, -21605, 24353, -21926, 24063, -22242, 23770, -22555, 23473, -22865, 23171, -23171, 22866, -23472, 22557, -23769, 22244, -24063, 21927, -24352, 21606, -24636, 21282, -24917, 20954, -25194, 20622, -25465, 20288, -25733, 19949, -25997, 19607, -26255, 19261, -26509, 18914, -26760, 18561, -27004, 18205, -27246, 17846, -27481, 17485, -27713, 17122, -27940, 16755, -28162, 16385, -28378, 16012, -28590, 15636, -28797, 15258, -28999, 14878, -29197, 14494, -29389, 14108, -29576, 13720, -29757, 13329, -29934, 12937, -30107, 12540, -30274, 12142, -30435, 11744, -30592, 11342, -30743, 10939, -30889, 10534, -31030, 10127, -31164, 9718, -31294, 9307, -31418, 8895, -31537, 8482, -31652, 8067, -31759, 7650, -31862, 7233, -31960, 6815, -32051, 6393, -32138, 5973, -32219, 5549, -32294, 5127, -32364, 4703, -32429, 4278, -32487, 3852, -32541, 3426, -32588, 2999, -32630, 2572, -32667, 2144, -32698, 1716, -32724, 1287, -32742, 860, -32757, 430, -32766, 0, -32767, -429, -32766, -858, -32757, -1287, -32743, -1715, -32724, -2143, -32698, -2570, -32667, -2998, -32631, -3425, -32588, -3851, -32541, -4277, -32488, -4701, -32429, -5125, -32364, -5548, -32295, -5971, -32219, -6393, -32138, -6813, -32051, -7231, -31960, -7650, -31863, -8067, -31760, -8481, -31652, -8895, -31539, -9306, -31419, -9716, -31294, -10126, -31165, -10532, -31030, -10937, -30889, -11340, -30743, -11741, -30592, -12141, -30436, -12540, -30274, -12935, -30107, -13328, -29936, -13718, -29758, -14107, -29577, -14493, -29390, -14875, -29197, -15257, -29000, -15635, -28797, -16010, -28590, -16384, -28379, -16753, -28162, -17119, -27940, -17484, -27714, -17845, -27482, -18205, -27246, -18560, -27006, -18911, -26760, -19260, -26510, -19606, -26257, -19947, -25997, -20286, -25734, -20621, -25466, -20952, -25194, -21281, -24918, -21605, -24637, -21926, -24353, -22242, -24063, -22555, -23770, -22865, -23473, -23171, -23171, -23472, -22866, -23769, -22557, -24063, -22244, -24352, -21927, -24636, -21606, -24917, -21282, -25194, -20954, -25465, -20622, -25733, -20288, -25997, -19949, -26255, -19607, -26509, -19261, -26760, -18914, -27004, -18561, -27246, -18205, -27481, -17846, -27713, -17485, -27940, -17122, -28162, -16755, -28378, -16385, -28590, -16012, -28797, -15636, -28999, -15258, -29197, -14878, -29389, -14494, -29576, -14108, -29757, -13720, -29934, -13329, -30107, -12937, -30274, -12540, -30435, -12142, -30592, -11744, -30743, -11342, -30889, -10939, -31030, -10534, -31164, -10127, -31294, -9718, -31418, -9307, -31537, -8895, -31652, -8482, -31759, -8067, -31862, -7650, -31960, -7233, -32051, -6815, -32138, -6393, -32219, -5973, -32294, -5549, -32364, -5127, -32429, -4703, -32487, -4278, -32541, -3852, -32588, -3426, -32630, -2999, -32667, -2572, -32698, -2144, -32724, -1716, -32742, -1287, -32757, -860, -32766, -430, -32767, 0, -32766, 429, -32757, 858, -32743, 1287, -32724, 1715, -32698, 2143, -32667, 2570, -32631, 2998, -32588, 3425, -32541, 3851, -32488, 4277, -32429, 4701, -32364, 5125, -32295, 5548, -32219, 5971, -32138, 6393, -32051, 6813, -31960, 7231, -31863, 7650, -31760, 8067, -31652, 8481, -31539, 8895, -31419, 9306, -31294, 9716, -31165, 10126, -31030, 10532, -30889, 10937, -30743, 11340, -30592, 11741, -30436, 12141, -30274, 12540, -30107, 12935, -29936, 13328, -29758, 13718, -29577, 14107, -29390, 14493, -29197, 14875, -29000, 15257, -28797, 15635, -28590, 16010, -28379, 16384, -28162, 16753, -27940, 17119, -27714, 17484, -27482, 17845, -27246, 18205, -27006, 18560, -26760, 18911, -26510, 19260, -26257, 19606, -25997, 19947, -25734, 20286, -25466, 20621, -25194, 20952, -24918, 21281, -24637, 21605, -24353, 21926, -24063, 22242, -23770, 22555, -23473, 22865, -23171, 23171, -22866, 23472, -22557, 23769, -22244, 24063, -21927, 24352, -21606, 24636, -21282, 24917, -20954, 25194, -20622, 25465, -20288, 25733, -19949, 25997, -19607, 26255, -19261, 26509, -18914, 26760, -18561, 27004, -18205, 27246, -17846, 27481, -17485, 27713, -17122, 27940, -16755, 28162, -16385, 28378, -16012, 28590, -15636, 28797, -15258, 28999, -14878, 29197, -14494, 29389, -14108, 29576, -13720, 29757, -13329, 29934, -12937, 30107, -12540, 30274, -12142, 30435, -11744, 30592, -11342, 30743, -10939, 30889, -10534, 31030, -10127, 31164, -9718, 31294, -9307, 31418, -8895, 31537, -8482, 31652, -8067, 31759, -7650, 31862, -7233, 31960, -6815, 32051, -6393, 32138, -5973, 32219, -5549, 32294, -5127, 32364, -4703, 32429, -4278, 32487, -3852, 32541, -3426, 32588, -2999, 32630, -2572, 32667, -2144, 32698, -1716, 32724, -1287, 32742, -860, 32757, -430, 32766, 0, Short.MAX_VALUE, 429, 32766, 858, 32757, 1287, 32743, 1715, 32724, 2143, 32698, 2570, 32667, 2998, 32631, 3425, 32588, 3851, 32541, 4277, 32488, 4701, 32429, 5125, 32364, 5548, 32295, 5971, 32219, 6393, 32138, 6813, 32051, 7231, 31960, 7650, 31863, 8067, 31760, 8481, 31652, 8895, 31539, 9306, 31419, 9716, 31294, 10126, 31165, 10532, 31030, 10937, 30889, 11340, 30743, 11741, 30592, 12141, 30436, 12540, 30274, 12935, 30107, 13328, 29936, 13718, 29758, 14107, 29577, 14493, 29390, 14875, 29197, 15257, 29000, 15635, 28797, 16010, 28590, 16384, 28379, 16753, 28162, 17119, 27940, 17484, 27714, 17845, 27482, 18205, 27246, 18560, 27006, 18911, 26760, 19260, 26510, 19606, 26257, 19947, 25997, 20286, 25734, 20621, 25466, 20952, 25194, 21281, 24918, 21605, 24637, 21926, 24353, 22242, 24063, 22555, 23770, 22865, 23473, 23171, 23171, 23472, 22866, 23769, 22557, 24063, 22244, 24352, 21927, 24636, 21606, 24917, 21282, 25194, 20954, 25465, 20622, 25733, 20288, 25997, 19949, 26255, 19607, 26509, 19261, 26760, 18914, 27004, 18561, 27246, 18205, 27481, 17846, 27713, 17485, 27940, 17122, 28162, 16755, 28378, 16385, 28590, 16012, 28797, 15636, 28999, 15258, 29197, 14878, 29389, 14494, 29576, 14108, 29757, 13720, 29934, 13329, 30107, 12937, 30274, 12540, 30435, 12142, 30592, 11744, 30743, 11342, 30889, 10939, 31030, 10534, 31164, 10127, 31294, 9718, 31418, 9307, 31537, 8895, 31652, 8482, 31759, 8067, 31862, 7650, 31960, 7233, 32051, 6815, 32138, 6393, 32219, 5973, 32294, 5549, 32364, 5127, 32429, 4703, 32487, 4278, 32541, 3852, 32588, 3426, 32630, 2999, 32667, 2572, 32698, 2144, 32724, 1716, 32742, 1287, 32757, 860, 32766, 430}; + static final short[] fft_bitrev480 = new short[]{0, 96, 192, 288, 384, 32, 128, 224, 320, 416, 64, 160, 256, 352, 448, 8, 104, 200, 296, 392, 40, 136, 232, 328, 424, 72, 168, 264, 360, 456, 16, 112, 208, 304, 400, 48, 144, 240, 336, 432, 80, 176, 272, 368, 464, 24, 120, 216, 312, 408, 56, 152, 248, 344, 440, 88, 184, 280, 376, 472, 4, 100, 196, 292, 388, 36, 132, 228, 324, 420, 68, 164, 260, 356, 452, 12, 108, 204, 300, 396, 44, 140, 236, 332, 428, 76, 172, 268, 364, 460, 20, 116, 212, 308, 404, 52, 148, 244, 340, 436, 84, 180, 276, 372, 468, 28, 124, 220, 316, 412, 60, 156, 252, 348, 444, 92, 188, 284, 380, 476, 1, 97, 193, 289, 385, 33, 129, 225, 321, 417, 65, 161, 257, 353, 449, 9, 105, 201, 297, 393, 41, 137, 233, 329, 425, 73, 169, 265, 361, 457, 17, 113, 209, 305, 401, 49, 145, 241, 337, 433, 81, 177, 273, 369, 465, 25, 121, 217, 313, 409, 57, 153, 249, 345, 441, 89, 185, 281, 377, 473, 5, 101, 197, 293, 389, 37, 133, 229, 325, 421, 69, 165, 261, 357, 453, 13, 109, 205, 301, 397, 45, 141, 237, 333, 429, 77, 173, 269, 365, 461, 21, 117, 213, 309, 405, 53, 149, 245, 341, 437, 85, 181, 277, 373, 469, 29, 125, 221, 317, 413, 61, 157, 253, 349, 445, 93, 189, 285, 381, 477, 2, 98, 194, 290, 386, 34, 130, 226, 322, 418, 66, 162, 258, 354, 450, 10, 106, 202, 298, 394, 42, 138, 234, 330, 426, 74, 170, 266, 362, 458, 18, 114, 210, 306, 402, 50, 146, 242, 338, 434, 82, 178, 274, 370, 466, 26, 122, 218, 314, 410, 58, 154, 250, 346, 442, 90, 186, 282, 378, 474, 6, 102, 198, 294, 390, 38, 134, 230, 326, 422, 70, 166, 262, 358, 454, 14, 110, 206, 302, 398, 46, 142, 238, 334, 430, 78, 174, 270, 366, 462, 22, 118, 214, 310, 406, 54, 150, 246, 342, 438, 86, 182, 278, 374, 470, 30, 126, 222, 318, 414, 62, 158, 254, 350, 446, 94, 190, 286, 382, 478, 3, 99, 195, 291, 387, 35, 131, 227, 323, 419, 67, 163, 259, 355, 451, 11, 107, 203, 299, 395, 43, 139, 235, 331, 427, 75, 171, 267, 363, 459, 19, 115, 211, 307, 403, 51, 147, 243, 339, 435, 83, 179, 275, 371, 467, 27, 123, 219, 315, 411, 59, 155, 251, 347, 443, 91, 187, 283, 379, 475, 7, 103, 199, 295, 391, 39, 135, 231, 327, 423, 71, 167, 263, 359, 455, 15, 111, 207, 303, 399, 47, 143, 239, 335, 431, 79, 175, 271, 367, 463, 23, 119, 215, 311, 407, 55, 151, 247, 343, 439, 87, 183, 279, 375, 471, 31, 127, 223, 319, 415, 63, 159, 255, 351, 447, 95, 191, 287, 383, 479}; + static final short[] fft_bitrev240 = new short[]{0, 48, 96, 144, 192, 16, 64, 112, 160, 208, 32, 80, 128, 176, 224, 4, 52, 100, 148, 196, 20, 68, 116, 164, 212, 36, 84, 132, 180, 228, 8, 56, 104, 152, 200, 24, 72, 120, 168, 216, 40, 88, 136, 184, 232, 12, 60, 108, 156, 204, 28, 76, 124, 172, 220, 44, 92, 140, 188, 236, 1, 49, 97, 145, 193, 17, 65, 113, 161, 209, 33, 81, 129, 177, 225, 5, 53, 101, 149, 197, 21, 69, 117, 165, 213, 37, 85, 133, 181, 229, 9, 57, 105, 153, 201, 25, 73, 121, 169, 217, 41, 89, 137, 185, 233, 13, 61, 109, 157, 205, 29, 77, 125, 173, 221, 45, 93, 141, 189, 237, 2, 50, 98, 146, 194, 18, 66, 114, 162, 210, 34, 82, 130, 178, 226, 6, 54, 102, 150, 198, 22, 70, 118, 166, 214, 38, 86, 134, 182, 230, 10, 58, 106, 154, 202, 26, 74, 122, 170, 218, 42, 90, 138, 186, 234, 14, 62, 110, 158, 206, 30, 78, 126, 174, 222, 46, 94, 142, 190, 238, 3, 51, 99, 147, 195, 19, 67, 115, 163, 211, 35, 83, 131, 179, 227, 7, 55, 103, 151, 199, 23, 71, 119, 167, 215, 39, 87, 135, 183, 231, 11, 59, 107, 155, 203, 27, 75, 123, 171, 219, 43, 91, 139, 187, 235, 15, 63, 111, 159, 207, 31, 79, 127, 175, 223, 47, 95, 143, 191, 239}; + static final short[] fft_bitrev120 = new short[]{0, 24, 48, 72, 96, 8, 32, 56, 80, 104, 16, 40, 64, 88, 112, 4, 28, 52, 76, 100, 12, 36, 60, 84, 108, 20, 44, 68, 92, 116, 1, 25, 49, 73, 97, 9, 33, 57, 81, 105, 17, 41, 65, 89, 113, 5, 29, 53, 77, 101, 13, 37, 61, 85, 109, 21, 45, 69, 93, 117, 2, 26, 50, 74, 98, 10, 34, 58, 82, 106, 18, 42, 66, 90, 114, 6, 30, 54, 78, 102, 14, 38, 62, 86, 110, 22, 46, 70, 94, 118, 3, 27, 51, 75, 99, 11, 35, 59, 83, 107, 19, 43, 67, 91, 115, 7, 31, 55, 79, 103, 15, 39, 63, 87, 111, 23, 47, 71, 95, 119}; + static final short[] fft_bitrev60 = new short[]{0, 12, 24, 36, 48, 4, 16, 28, 40, 52, 8, 20, 32, 44, 56, 1, 13, 25, 37, 49, 5, 17, 29, 41, 53, 9, 21, 33, 45, 57, 2, 14, 26, 38, 50, 6, 18, 30, 42, 54, 10, 22, 34, 46, 58, 3, 15, 27, 39, 51, 7, 19, 31, 43, 55, 11, 23, 35, 47, 59}; + static final FFTState fft_state48000_960_0 = new FFTState(); + static final FFTState fft_state48000_960_1; + static final FFTState fft_state48000_960_2; + static final FFTState fft_state48000_960_3; + static final short[] mdct_twiddles960; + static final int[] intensity_thresholds; + static final int[] intensity_histeresis; + + CeltTables() { + } + + /* + * Opcode count of 26407 triggered aggressive code reduction. Override with --aggressivesizethreshold. + */ + static { + CeltTables.fft_state48000_960_0.nfft = 480; + CeltTables.fft_state48000_960_0.scale = (short)17476; + CeltTables.fft_state48000_960_0.scale_shift = 8; + CeltTables.fft_state48000_960_0.shift = -1; + CeltTables.fft_state48000_960_0.factors = new short[]{5, 96, 3, 32, 4, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0}; + CeltTables.fft_state48000_960_0.bitrev = fft_bitrev480; + CeltTables.fft_state48000_960_0.twiddles = fft_twiddles48000_960; + fft_state48000_960_1 = new FFTState(); + CeltTables.fft_state48000_960_1.nfft = 240; + CeltTables.fft_state48000_960_1.scale = (short)17476; + CeltTables.fft_state48000_960_1.scale_shift = 7; + CeltTables.fft_state48000_960_1.shift = 1; + CeltTables.fft_state48000_960_1.factors = new short[]{5, 48, 3, 16, 4, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0}; + CeltTables.fft_state48000_960_1.bitrev = fft_bitrev240; + CeltTables.fft_state48000_960_1.twiddles = fft_twiddles48000_960; + fft_state48000_960_2 = new FFTState(); + CeltTables.fft_state48000_960_2.nfft = 120; + CeltTables.fft_state48000_960_2.scale = (short)17476; + CeltTables.fft_state48000_960_2.scale_shift = 6; + CeltTables.fft_state48000_960_2.shift = 2; + CeltTables.fft_state48000_960_2.factors = new short[]{5, 24, 3, 8, 2, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0}; + CeltTables.fft_state48000_960_2.bitrev = fft_bitrev120; + CeltTables.fft_state48000_960_2.twiddles = fft_twiddles48000_960; + fft_state48000_960_3 = new FFTState(); + CeltTables.fft_state48000_960_3.nfft = 60; + CeltTables.fft_state48000_960_3.scale = (short)17476; + CeltTables.fft_state48000_960_3.scale_shift = 5; + CeltTables.fft_state48000_960_3.shift = 3; + CeltTables.fft_state48000_960_3.factors = new short[]{5, 12, 3, 4, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + CeltTables.fft_state48000_960_3.bitrev = fft_bitrev60; + CeltTables.fft_state48000_960_3.twiddles = fft_twiddles48000_960; + mdct_twiddles960 = new short[]{Short.MAX_VALUE, Short.MAX_VALUE, Short.MAX_VALUE, 32766, 32765, 32763, 32761, 32759, 32756, 32753, 32750, 32746, 32742, 32738, 32733, 32728, 32722, 32717, 32710, 32704, 32697, 32690, 32682, 32674, 32666, 32657, 32648, 32639, 32629, 32619, 32609, 32598, 32587, 32576, 32564, 32552, 32539, 32526, 32513, 32500, 32486, 32472, 32457, 32442, 32427, 32411, 32395, 32379, 32362, 32345, 32328, 32310, 32292, 32274, 32255, 32236, 32217, 32197, 32177, 32157, 32136, 32115, 32093, 32071, 32049, 32027, 32004, 31981, 31957, 31933, 31909, 31884, 31859, 31834, 31809, 31783, 31756, 31730, 31703, 31676, 31648, 31620, 31592, 31563, 31534, 31505, 31475, 31445, 31415, 31384, 31353, 31322, 31290, 31258, 31226, 31193, 31160, 31127, 31093, 31059, 31025, 30990, 30955, 30920, 30884, 30848, 30812, 30775, 30738, 30701, 30663, 30625, 30587, 30548, 30509, 30470, 30430, 30390, 30350, 30309, 30269, 30227, 30186, 30144, 30102, 30059, 30016, 29973, 29930, 29886, 29842, 29797, 29752, 29707, 29662, 29616, 29570, 29524, 29477, 29430, 29383, 29335, 29287, 29239, 29190, 29142, 29092, 29043, 28993, 28943, 28892, 28842, 28791, 28739, 28688, 28636, 28583, 28531, 28478, 28425, 28371, 28317, 28263, 28209, 28154, 28099, 28044, 27988, 27932, 27876, 27820, 27763, 27706, 27648, 27591, 27533, 27474, 27416, 27357, 27298, 27238, 27178, 27118, 27058, 26997, 26936, 26875, 26814, 26752, 26690, 26628, 26565, 26502, 26439, 26375, 26312, 26247, 26183, 26119, 26054, 25988, 25923, 25857, 25791, 25725, 25658, 25592, 25524, 25457, 25389, 25322, 25253, 25185, 25116, 25047, 24978, 24908, 24838, 24768, 24698, 24627, 24557, 24485, 24414, 24342, 24270, 24198, 24126, 24053, 23980, 23907, 23834, 23760, 23686, 23612, 23537, 23462, 23387, 23312, 23237, 23161, 23085, 23009, 22932, 22856, 22779, 22701, 22624, 22546, 22468, 22390, 22312, 22233, 22154, 22075, 21996, 21916, 21836, 21756, 21676, 21595, 21515, 21434, 21352, 21271, 21189, 21107, 21025, 20943, 20860, 20777, 20694, 20611, 20528, 20444, 20360, 20276, 20192, 20107, 20022, 19937, 19852, 19767, 19681, 19595, 19509, 19423, 19336, 19250, 19163, 19076, 18988, 18901, 18813, 18725, 18637, 18549, 18460, 18372, 18283, 18194, 18104, 18015, 17925, 17835, 17745, 17655, 17565, 17474, 17383, 17292, 17201, 17110, 17018, 16927, 16835, 16743, 16650, 16558, 16465, 16372, 16279, 16186, 16093, 15999, 15906, 15812, 15718, 15624, 15529, 15435, 15340, 15245, 15150, 15055, 14960, 14864, 14769, 14673, 14577, 14481, 14385, 14288, 14192, 14095, 13998, 13901, 13804, 13706, 13609, 13511, 13414, 13316, 13218, 13119, 13021, 12923, 12824, 12725, 12626, 12527, 12428, 12329, 12230, 12130, 12030, 11930, 11831, 11730, 11630, 11530, 11430, 11329, 11228, 11128, 11027, 10926, 10824, 10723, 10622, 10520, 10419, 10317, 10215, 10113, 10011, 9909, 9807, 9704, 9602, 9499, 9397, 9294, 9191, 9088, 8985, 8882, 8778, 8675, 8572, 8468, 8364, 8261, 8157, 8053, 7949, 7845, 7741, 7637, 7532, 7428, 7323, 7219, 7114, 7009, 6905, 6800, 6695, 6590, 6485, 6380, 6274, 6169, 6064, 5958, 5853, 5747, 5642, 5536, 5430, 5325, 5219, 5113, 5007, 4901, 4795, 4689, 4583, 4476, 4370, 4264, 4157, 4051, 3945, 3838, 3732, 3625, 3518, 3412, 3305, 3198, 3092, 2985, 2878, 2771, 2664, 2558, 2451, 2344, 2237, 2130, 2023, 1916, 1809, 1702, 1594, 1487, 1380, 1273, 1166, 1059, 952, 844, 737, 630, 523, 416, 308, 201, 94, -13, -121, -228, -335, -442, -550, -657, -764, -871, -978, -1086, -1193, -1300, -1407, -1514, -1621, -1728, -1835, -1942, -2049, -2157, -2263, -2370, -2477, -2584, -2691, -2798, -2905, -3012, -3118, -3225, -3332, -3439, -3545, -3652, -3758, -3865, -3971, -4078, -4184, -4290, -4397, -4503, -4609, -4715, -4821, -4927, -5033, -5139, -5245, -5351, -5457, -5562, -5668, -5774, -5879, -5985, -6090, -6195, -6301, -6406, -6511, -6616, -6721, -6826, -6931, -7036, -7140, -7245, -7349, -7454, -7558, -7663, -7767, -7871, -7975, -8079, -8183, -8287, -8390, -8494, -8597, -8701, -8804, -8907, -9011, -9114, -9217, -9319, -9422, -9525, -9627, -9730, -9832, -9934, -10037, -10139, -10241, -10342, -10444, -10546, -10647, -10748, -10850, -10951, -11052, -11153, -11253, -11354, -11455, -11555, -11655, -11756, -11856, -11955, -12055, -12155, -12254, -12354, -12453, -12552, -12651, -12750, -12849, -12947, -13046, -13144, -13242, -13340, -13438, -13536, -13633, -13731, -13828, -13925, -14022, -14119, -14216, -14312, -14409, -14505, -14601, -14697, -14793, -14888, -14984, -15079, -15174, -15269, -15364, -15459, -15553, -15647, -15741, -15835, -15929, -16023, -16116, -16210, -16303, -16396, -16488, -16581, -16673, -16766, -16858, -16949, -17041, -17133, -17224, -17315, -17406, -17497, -17587, -17678, -17768, -17858, -17948, -18037, -18127, -18216, -18305, -18394, -18483, -18571, -18659, -18747, -18835, -18923, -19010, -19098, -19185, -19271, -19358, -19444, -19531, -19617, -19702, -19788, -19873, -19959, -20043, -20128, -20213, -20297, -20381, -20465, -20549, -20632, -20715, -20798, -20881, -20963, -21046, -21128, -21210, -21291, -21373, -21454, -21535, -21616, -21696, -21776, -21856, -21936, -22016, -22095, -22174, -22253, -22331, -22410, -22488, -22566, -22643, -22721, -22798, -22875, -22951, -23028, -23104, -23180, -23256, -23331, -23406, -23481, -23556, -23630, -23704, -23778, -23852, -23925, -23998, -24071, -24144, -24216, -24288, -24360, -24432, -24503, -24574, -24645, -24716, -24786, -24856, -24926, -24995, -25064, -25133, -25202, -25270, -25339, -25406, -25474, -25541, -25608, -25675, -25742, -25808, -25874, -25939, -26005, -26070, -26135, -26199, -26264, -26327, -26391, -26455, -26518, -26581, -26643, -26705, -26767, -26829, -26891, -26952, -27013, -27073, -27133, -27193, -27253, -27312, -27372, -27430, -27489, -27547, -27605, -27663, -27720, -27777, -27834, -27890, -27946, -28002, -28058, -28113, -28168, -28223, -28277, -28331, -28385, -28438, -28491, -28544, -28596, -28649, -28701, -28752, -28803, -28854, -28905, -28955, -29006, -29055, -29105, -29154, -29203, -29251, -29299, -29347, -29395, -29442, -29489, -29535, -29582, -29628, -29673, -29719, -29764, -29808, -29853, -29897, -29941, -29984, -30027, -30070, -30112, -30154, -30196, -30238, -30279, -30320, -30360, -30400, -30440, -30480, -30519, -30558, -30596, -30635, -30672, -30710, -30747, -30784, -30821, -30857, -30893, -30929, -30964, -30999, -31033, -31068, -31102, -31135, -31168, -31201, -31234, -31266, -31298, -31330, -31361, -31392, -31422, -31453, -31483, -31512, -31541, -31570, -31599, -31627, -31655, -31682, -31710, -31737, -31763, -31789, -31815, -31841, -31866, -31891, -31915, -31939, -31963, -31986, -32010, -32032, -32055, -32077, -32099, -32120, -32141, -32162, -32182, -32202, -32222, -32241, -32260, -32279, -32297, -32315, -32333, -32350, -32367, -32383, -32399, -32415, -32431, -32446, -32461, -32475, -32489, -32503, -32517, -32530, -32542, -32555, -32567, -32579, -32590, -32601, -32612, -32622, -32632, -32641, -32651, -32659, -32668, -32676, -32684, -32692, -32699, -32706, -32712, -32718, -32724, -32729, -32734, -32739, -32743, -32747, -32751, -32754, -32757, -32760, -32762, -32764, -32765, -32767, -32767, -32767, Short.MAX_VALUE, Short.MAX_VALUE, 32765, 32761, 32756, 32750, 32742, 32732, 32722, 32710, 32696, 32681, 32665, 32647, 32628, 32608, 32586, 32562, 32538, 32512, 32484, 32455, 32425, 32393, 32360, 32326, 32290, 32253, 32214, 32174, 32133, 32090, 32046, 32001, 31954, 31906, 31856, 31805, 31753, 31700, 31645, 31588, 31530, 31471, 31411, 31349, 31286, 31222, 31156, 31089, 31020, 30951, 30880, 30807, 30733, 30658, 30582, 30504, 30425, 30345, 30263, 30181, 30096, 30011, 29924, 29836, 29747, 29656, 29564, 29471, 29377, 29281, 29184, 29086, 28987, 28886, 28784, 28681, 28577, 28471, 28365, 28257, 28147, 28037, 27925, 27812, 27698, 27583, 27467, 27349, 27231, 27111, 26990, 26868, 26744, 26620, 26494, 26367, 26239, 26110, 25980, 25849, 25717, 25583, 25449, 25313, 25176, 25038, 24900, 24760, 24619, 24477, 24333, 24189, 24044, 23898, 23751, 23602, 23453, 23303, 23152, 22999, 22846, 22692, 22537, 22380, 22223, 22065, 21906, 21746, 21585, 21423, 21261, 21097, 20933, 20767, 20601, 20434, 20265, 20096, 19927, 19756, 19584, 19412, 19239, 19065, 18890, 18714, 18538, 18361, 18183, 18004, 17824, 17644, 17463, 17281, 17098, 16915, 16731, 16546, 16361, 16175, 15988, 15800, 15612, 15423, 15234, 15043, 14852, 14661, 14469, 14276, 14083, 13889, 13694, 13499, 13303, 13107, 12910, 12713, 12515, 12317, 12118, 11918, 11718, 11517, 11316, 11115, 10913, 10710, 10508, 10304, 10100, 9896, 9691, 9486, 9281, 9075, 8869, 8662, 8455, 8248, 8040, 7832, 7623, 7415, 7206, 6996, 6787, 6577, 6366, 6156, 5945, 5734, 5523, 5311, 5100, 4888, 4675, 4463, 4251, 4038, 3825, 3612, 3399, 3185, 2972, 2758, 2544, 2330, 2116, 1902, 1688, 1474, 1260, 1045, 831, 617, 402, 188, -27, -241, -456, -670, -885, -1099, -1313, -1528, -1742, -1956, -2170, -2384, -2598, -2811, -3025, -3239, -3452, -3665, -3878, -4091, -4304, -4516, -4728, -4941, -5153, -5364, -5576, -5787, -5998, -6209, -6419, -6629, -6839, -7049, -7258, -7467, -7676, -7884, -8092, -8300, -8507, -8714, -8920, -9127, -9332, -9538, -9743, -9947, -10151, -10355, -10558, -10761, -10963, -11165, -11367, -11568, -11768, -11968, -12167, -12366, -12565, -12762, -12960, -13156, -13352, -13548, -13743, -13937, -14131, -14324, -14517, -14709, -14900, -15091, -15281, -15470, -15659, -15847, -16035, -16221, -16407, -16593, -16777, -16961, -17144, -17326, -17508, -17689, -17869, -18049, -18227, -18405, -18582, -18758, -18934, -19108, -19282, -19455, -19627, -19799, -19969, -20139, -20308, -20475, -20642, -20809, -20974, -21138, -21301, -21464, -21626, -21786, -21946, -22105, -22263, -22420, -22575, -22730, -22884, -23037, -23189, -23340, -23490, -23640, -23788, -23935, -24080, -24225, -24369, -24512, -24654, -24795, -24934, -25073, -25211, -25347, -25482, -25617, -25750, -25882, -26013, -26143, -26272, -26399, -26526, -26651, -26775, -26898, -27020, -27141, -27260, -27379, -27496, -27612, -27727, -27841, -27953, -28065, -28175, -28284, -28391, -28498, -28603, -28707, -28810, -28911, -29012, -29111, -29209, -29305, -29401, -29495, -29587, -29679, -29769, -29858, -29946, -30032, -30118, -30201, -30284, -30365, -30445, -30524, -30601, -30677, -30752, -30825, -30897, -30968, -31038, -31106, -31172, -31238, -31302, -31365, -31426, -31486, -31545, -31602, -31658, -31713, -31766, -31818, -31869, -31918, -31966, -32012, -32058, -32101, -32144, -32185, -32224, -32262, -32299, -32335, -32369, -32401, -32433, -32463, -32491, -32518, -32544, -32568, -32591, -32613, -32633, -32652, -32669, -32685, -32700, -32713, -32724, -32735, -32744, -32751, -32757, -32762, -32766, -32767, Short.MAX_VALUE, 32764, 32755, 32741, 32720, 32694, 32663, 32626, 32583, 32535, 32481, 32421, 32356, 32286, 32209, 32128, 32041, 31948, 31850, 31747, 31638, 31523, 31403, 31278, 31148, 31012, 30871, 30724, 30572, 30415, 30253, 30086, 29913, 29736, 29553, 29365, 29172, 28974, 28771, 28564, 28351, 28134, 27911, 27684, 27452, 27216, 26975, 26729, 26478, 26223, 25964, 25700, 25432, 25159, 24882, 24601, 24315, 24026, 23732, 23434, 23133, 22827, 22517, 22204, 21886, 21565, 21240, 20912, 20580, 20244, 19905, 19563, 19217, 18868, 18516, 18160, 17802, 17440, 17075, 16708, 16338, 15964, 15588, 15210, 14829, 14445, 14059, 13670, 13279, 12886, 12490, 12093, 11693, 11291, 10888, 10482, 10075, 9666, 9255, 8843, 8429, 8014, 7597, 7180, 6760, 6340, 5919, 5496, 5073, 4649, 4224, 3798, 3372, 2945, 2517, 2090, 1661, 1233, 804, 375, -54, -483, -911, -1340, -1768, -2197, -2624, -3052, -3479, -3905, -4330, -4755, -5179, -5602, -6024, -6445, -6865, -7284, -7702, -8118, -8533, -8946, -9358, -9768, -10177, -10584, -10989, -11392, -11793, -12192, -12589, -12984, -13377, -13767, -14155, -14541, -14924, -15305, -15683, -16058, -16430, -16800, -17167, -17531, -17892, -18249, -18604, -18956, -19304, -19649, -19990, -20329, -20663, -20994, -21322, -21646, -21966, -22282, -22595, -22904, -23208, -23509, -23806, -24099, -24387, -24672, -24952, -25228, -25499, -25766, -26029, -26288, -26541, -26791, -27035, -27275, -27511, -27741, -27967, -28188, -28405, -28616, -28823, -29024, -29221, -29412, -29599, -29780, -29957, -30128, -30294, -30455, -30611, -30761, -30906, -31046, -31181, -31310, -31434, -31552, -31665, -31773, -31875, -31972, -32063, -32149, -32229, -32304, -32373, -32437, -32495, -32547, -32594, -32635, -32671, -32701, -32726, -32745, -32758, -32766, Short.MAX_VALUE, 32754, 32717, 32658, 32577, 32473, 32348, 32200, 32029, 31837, 31624, 31388, 31131, 30853, 30553, 30232, 29891, 29530, 29148, 28746, 28324, 27883, 27423, 26944, 26447, 25931, 25398, 24847, 24279, 23695, 23095, 22478, 21846, 21199, 20538, 19863, 19174, 18472, 17757, 17030, 16291, 15541, 14781, 14010, 13230, 12441, 11643, 10837, 10024, 9204, 8377, 7545, 6708, 5866, 5020, 4171, 3319, 2464, 1608, 751, -107, -965, -1822, -2678, -3532, -4383, -5232, -6077, -6918, -7754, -8585, -9409, -10228, -11039, -11843, -12639, -13426, -14204, -14972, -15730, -16477, -17213, -17937, -18648, -19347, -20033, -20705, -21363, -22006, -22634, -23246, -23843, -24423, -24986, -25533, -26062, -26573, -27066, -27540, -27995, -28431, -28848, -29245, -29622, -29979, -30315, -30630, -30924, -31197, -31449, -31679, -31887, -32074, -32239, -32381, -32501, -32600, -32675, -32729, -32759}; + intensity_thresholds = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 16, 24, 36, 44, 50, 56, 62, 67, 72, 79, 88, 106, 134}; + intensity_histeresis = new int[]{1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 4, 5, 6, 8, 8}; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/ChannelLayout.java b/source/decompiled/de/maxhenkel/voicechat/concentus/ChannelLayout.java new file mode 100644 index 0000000..fe17c8a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/ChannelLayout.java @@ -0,0 +1,24 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class ChannelLayout { + int nb_channels; + int nb_streams; + int nb_coupled_streams; + final short[] mapping = new short[256]; + + ChannelLayout() { + } + + void Reset() { + this.nb_channels = 0; + this.nb_streams = 0; + this.nb_coupled_streams = 0; + Arrays.MemSet(this.mapping, (short)0); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CodeSigns.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CodeSigns.java new file mode 100644 index 0000000..a358b9f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CodeSigns.java @@ -0,0 +1,65 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class CodeSigns { + CodeSigns() { + } + + private static int silk_enc_map(int a) { + return Inlines.silk_RSHIFT(a, 15) + 1; + } + + private static int silk_dec_map(int a) { + return Inlines.silk_LSHIFT(a, 1) - 1; + } + + static void silk_encode_signs(EntropyCoder psRangeEnc, byte[] pulses, int length, int signalType, int quantOffsetType, int[] sum_pulses) { + int i; + short[] icdf = new short[2]; + short[] sign_icdf = SilkTables.silk_sign_iCDF; + icdf[1] = 0; + int q_ptr = 0; + int icdf_ptr = i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1)); + length = Inlines.silk_RSHIFT(length + 8, 4); + for (i = 0; i < length; ++i) { + int p = sum_pulses[i]; + if (p > 0) { + icdf[0] = sign_icdf[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)]; + for (int j = q_ptr; j < q_ptr + 16; ++j) { + if (pulses[j] == 0) continue; + psRangeEnc.enc_icdf(CodeSigns.silk_enc_map(pulses[j]), icdf, 8); + } + } + q_ptr += 16; + } + } + + static void silk_decode_signs(EntropyCoder psRangeDec, short[] pulses, int length, int signalType, int quantOffsetType, int[] sum_pulses) { + int i; + short[] icdf = new short[2]; + short[] icdf_table = SilkTables.silk_sign_iCDF; + icdf[1] = 0; + int q_ptr = 0; + int icdf_ptr = i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1)); + length = Inlines.silk_RSHIFT(length + 8, 4); + for (i = 0; i < length; ++i) { + int p = sum_pulses[i]; + if (p > 0) { + icdf[0] = icdf_table[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)]; + for (int j = 0; j < 16; ++j) { + if (pulses[q_ptr + j] <= 0) continue; + int n = q_ptr + j; + pulses[n] = (short)(pulses[n] * (short)CodeSigns.silk_dec_map(psRangeDec.dec_icdf(icdf, 8))); + } + } + q_ptr += 16; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CodecHelpers.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CodecHelpers.java new file mode 100644 index 0000000..511d358 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CodecHelpers.java @@ -0,0 +1,370 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Downmix; +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.OpusBandwidth; +import de.maxhenkel.voicechat.concentus.OpusBandwidthHelpers; +import de.maxhenkel.voicechat.concentus.OpusFramesize; +import de.maxhenkel.voicechat.concentus.OpusFramesizeHelpers; +import de.maxhenkel.voicechat.concentus.OpusMode; +import de.maxhenkel.voicechat.concentus.StereoWidthState; + +public class CodecHelpers { + private static final int MAX_DYNAMIC_FRAMESIZE = 24; + + static byte gen_toc(OpusMode mode, int framerate, OpusBandwidth bandwidth, int channels) { + int toc; + int period = 0; + while (framerate < 400) { + framerate <<= 1; + ++period; + } + if (mode == OpusMode.MODE_SILK_ONLY) { + toc = (short)(OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) << 5); + toc = (short)(toc | (short)(period - 2 << 3)); + } else if (mode == OpusMode.MODE_CELT_ONLY) { + int tmp = OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND); + if (tmp < 0) { + tmp = 0; + } + toc = 128; + toc = (short)(toc | (short)(tmp << 5)); + toc = (short)(toc | (short)(period << 3)); + } else { + toc = 96; + toc = (short)(toc | (short)(OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) << 4)); + toc = (short)(toc | (short)(period - 2 << 3)); + } + toc = (short)(toc | (short)((channels == 2 ? 1 : 0) << 2)); + return (byte)(0xFF & toc); + } + + static void hp_cutoff(short[] input, int input_ptr, int cutoff_Hz, short[] output, int output_ptr, int[] hp_mem, int len, int channels, int Fs) { + int r_Q28; + int[] B_Q28 = new int[3]; + int[] A_Q28 = new int[2]; + Inlines.OpusAssert(cutoff_Hz <= 869074); + int Fc_Q19 = Inlines.silk_DIV32_16(Inlines.silk_SMULBB(2471, cutoff_Hz), Fs / 1000); + Inlines.OpusAssert(Fc_Q19 > 0 && Fc_Q19 < 32768); + B_Q28[0] = r_Q28 = 0x10000000 - Inlines.silk_MUL(471, Fc_Q19); + B_Q28[1] = Inlines.silk_LSHIFT(-r_Q28, 1); + B_Q28[2] = r_Q28; + int r_Q22 = Inlines.silk_RSHIFT(r_Q28, 6); + A_Q28[0] = Inlines.silk_SMULWW(r_Q22, Inlines.silk_SMULWW(Fc_Q19, Fc_Q19) - 0x800000); + A_Q28[1] = Inlines.silk_SMULWW(r_Q22, r_Q22); + Filters.silk_biquad_alt(input, input_ptr, B_Q28, A_Q28, hp_mem, 0, output, output_ptr, len, channels); + if (channels == 2) { + Filters.silk_biquad_alt(input, input_ptr + 1, B_Q28, A_Q28, hp_mem, 2, output, output_ptr + 1, len, channels); + } + } + + static void dc_reject(short[] input, int input_ptr, int cutoff_Hz, short[] output, int output_ptr, int[] hp_mem, int len, int channels, int Fs) { + int shift = Inlines.celt_ilog2(Fs / (cutoff_Hz * 3)); + for (int c = 0; c < channels; ++c) { + for (int i = 0; i < len; ++i) { + int x = Inlines.SHL32(Inlines.EXTEND32(input[channels * i + c + input_ptr]), 15); + int tmp = x - hp_mem[2 * c]; + hp_mem[2 * c] = hp_mem[2 * c] + Inlines.PSHR32(x - hp_mem[2 * c], shift); + int y = tmp - hp_mem[2 * c + 1]; + hp_mem[2 * c + 1] = hp_mem[2 * c + 1] + Inlines.PSHR32(tmp - hp_mem[2 * c + 1], shift); + output[channels * i + c + output_ptr] = Inlines.EXTRACT16(Inlines.SATURATE(Inlines.PSHR32(y, 15), Short.MAX_VALUE)); + } + } + } + + static void stereo_fade(short[] pcm_buf, int g1, int g2, int overlap48, int frame_size, int channels, int[] window, int Fs) { + int diff; + int i; + int inc = 48000 / Fs; + int overlap = overlap48 / inc; + g1 = Short.MAX_VALUE - g1; + g2 = Short.MAX_VALUE - g2; + for (i = 0; i < overlap; ++i) { + int w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + int g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2), Short.MAX_VALUE - w, g1), 15); + diff = Inlines.EXTRACT16(Inlines.HALF32(pcm_buf[i * channels] - pcm_buf[i * channels + 1])); + diff = Inlines.MULT16_16_Q15(g, diff); + pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - diff); + pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + diff); + } + while (i < frame_size) { + diff = Inlines.EXTRACT16(Inlines.HALF32(pcm_buf[i * channels] - pcm_buf[i * channels + 1])); + diff = Inlines.MULT16_16_Q15(g2, diff); + pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - diff); + pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + diff); + ++i; + } + } + + static void gain_fade(short[] buffer, int buf_ptr, int g1, int g2, int overlap48, int frame_size, int channels, int[] window, int Fs) { + int g; + int w; + int i; + int inc = 48000 / Fs; + int overlap = overlap48 / inc; + if (channels == 1) { + for (i = 0; i < overlap; ++i) { + w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2), Short.MAX_VALUE - w, g1), 15); + buffer[buf_ptr + i] = (short)Inlines.MULT16_16_Q15(g, (int)buffer[buf_ptr + i]); + } + } else { + for (i = 0; i < overlap; ++i) { + w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + g = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, g2), Short.MAX_VALUE - w, g1), 15); + buffer[buf_ptr + i * 2] = (short)Inlines.MULT16_16_Q15(g, (int)buffer[buf_ptr + i * 2]); + buffer[buf_ptr + i * 2 + 1] = (short)Inlines.MULT16_16_Q15(g, (int)buffer[buf_ptr + i * 2 + 1]); + } + } + int c = 0; + do { + for (i = overlap; i < frame_size; ++i) { + buffer[buf_ptr + i * channels + c] = (short)Inlines.MULT16_16_Q15(g2, (int)buffer[buf_ptr + i * channels + c]); + } + } while (++c < channels); + } + + static float transient_boost(float[] E, int E_ptr, float[] E_1, int LM, int maxM) { + float sumE = 0.0f; + float sumE_1 = 0.0f; + int M = Inlines.IMIN(maxM, (1 << LM) + 1); + for (int i = E_ptr; i < M + E_ptr; ++i) { + sumE += E[i]; + sumE_1 += E_1[i]; + } + float metric = sumE * sumE_1 / (float)(M * M); + return Inlines.MIN16(1.0f, (float)Math.sqrt(Inlines.MAX16(0.0f, 0.05f * (metric - 2.0f)))); + } + + static int transient_viterbi(float[] E, float[] E_1, int N, int frame_cost, int rate) { + int i; + float[][] cost = Arrays.InitTwoDimensionalArrayFloat(24, 16); + int[][] states = Arrays.InitTwoDimensionalArrayInt(24, 16); + float factor = rate < 80 ? 0.0f : (rate > 160 ? 1.0f : ((float)rate - 80.0f) / 80.0f); + for (i = 0; i < 16; ++i) { + states[0][i] = -1; + cost[0][i] = 1.0E10f; + } + for (i = 0; i < 4; ++i) { + cost[0][1 << i] = (float)(frame_cost + rate * (1 << i)) * (1.0f + factor * CodecHelpers.transient_boost(E, 0, E_1, i, N + 1)); + states[0][1 << i] = i; + } + for (i = 1; i < N; ++i) { + int j; + for (j = 2; j < 16; ++j) { + cost[i][j] = cost[i - 1][j - 1]; + states[i][j] = j - 1; + } + for (j = 0; j < 4; ++j) { + states[i][1 << j] = 1; + float min_cost = cost[i - 1][1]; + for (int k = 1; k < 4; ++k) { + float tmp = cost[i - 1][(1 << k + 1) - 1]; + if (!(tmp < min_cost)) continue; + states[i][1 << j] = (1 << k + 1) - 1; + min_cost = tmp; + } + float curr_cost = (float)(frame_cost + rate * (1 << j)) * (1.0f + factor * CodecHelpers.transient_boost(E, i, E_1, j, N - i + 1)); + cost[i][1 << j] = min_cost; + if (N - i < 1 << j) { + float[] fArray = cost[i]; + int n = 1 << j; + fArray[n] = fArray[n] + curr_cost * (float)(N - i) / (float)(1 << j); + continue; + } + float[] fArray = cost[i]; + int n = 1 << j; + fArray[n] = fArray[n] + curr_cost; + } + } + int best_state = 1; + float best_cost = cost[N - 1][1]; + for (i = 2; i < 16; ++i) { + if (!(cost[N - 1][i] < best_cost)) continue; + best_cost = cost[N - 1][i]; + best_state = i; + } + for (i = N - 1; i >= 0; --i) { + best_state = states[i][best_state]; + } + return best_state; + } + + static int optimize_framesize(short[] x, int x_ptr, int len, int C, int Fs, int bitrate, int tonality, float[] mem, int buffering) { + int i; + int pos; + int offset; + float[] e = new float[28]; + float[] e_1 = new float[27]; + int bestLM = 0; + int subframe = Fs / 400; + int[] sub = new int[subframe]; + e[0] = mem[0]; + e_1[0] = 1.0f / (1.0f + mem[0]); + if (buffering != 0) { + offset = 2 * subframe - buffering; + Inlines.OpusAssert(offset >= 0 && offset <= subframe); + len -= offset; + e[1] = mem[1]; + e_1[1] = 1.0f / (1.0f + mem[1]); + e[2] = mem[2]; + e_1[2] = 1.0f / (1.0f + mem[2]); + pos = 3; + } else { + pos = 1; + offset = 0; + } + int N = Inlines.IMIN(len / subframe, 24); + int memx = 0; + for (i = 0; i < N; ++i) { + float tmp = 1.0f; + Downmix.downmix_int(x, x_ptr, sub, 0, subframe, i * subframe + offset, 0, -2, C); + if (i == 0) { + memx = sub[0]; + } + for (int j = 0; j < subframe; ++j) { + int tmpx = sub[j]; + tmp += (float)(tmpx - memx) * (float)(tmpx - memx); + memx = tmpx; + } + e[i + pos] = tmp; + e_1[i + pos] = 1.0f / tmp; + } + e[i + pos] = e[i + pos - 1]; + if (buffering != 0) { + N = Inlines.IMIN(24, N + 2); + } + bestLM = CodecHelpers.transient_viterbi(e, e_1, N, (int)((1.0f + 0.5f * (float)tonality) * (float)(60 * C + 40)), bitrate / 400); + mem[0] = e[1 << bestLM]; + if (buffering != 0) { + mem[1] = e[(1 << bestLM) + 1]; + mem[2] = e[(1 << bestLM) + 2]; + } + return bestLM; + } + + static int frame_size_select(int frame_size, OpusFramesize variable_duration, int Fs) { + int new_size; + if (frame_size < Fs / 400) { + return -1; + } + if (variable_duration == OpusFramesize.OPUS_FRAMESIZE_ARG) { + new_size = frame_size; + } else if (variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE) { + new_size = Fs / 50; + } else if (OpusFramesizeHelpers.GetOrdinal(variable_duration) >= OpusFramesizeHelpers.GetOrdinal(OpusFramesize.OPUS_FRAMESIZE_2_5_MS) && OpusFramesizeHelpers.GetOrdinal(variable_duration) <= OpusFramesizeHelpers.GetOrdinal(OpusFramesize.OPUS_FRAMESIZE_60_MS)) { + new_size = Inlines.IMIN(3 * Fs / 50, Fs / 400 << OpusFramesizeHelpers.GetOrdinal(variable_duration) - OpusFramesizeHelpers.GetOrdinal(OpusFramesize.OPUS_FRAMESIZE_2_5_MS)); + } else { + return -1; + } + if (new_size > frame_size) { + return -1; + } + if (400 * new_size != Fs && 200 * new_size != Fs && 100 * new_size != Fs && 50 * new_size != Fs && 25 * new_size != Fs && 50 * new_size != 3 * Fs) { + return -1; + } + return new_size; + } + + static int compute_frame_size(short[] analysis_pcm, int analysis_pcm_ptr, int frame_size, OpusFramesize variable_duration, int C, int Fs, int bitrate_bps, int delay_compensation, float[] subframe_mem, boolean analysis_enabled) { + if (analysis_enabled && variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size >= Fs / 200) { + int LM = 3; + LM = CodecHelpers.optimize_framesize(analysis_pcm, analysis_pcm_ptr, frame_size, C, Fs, bitrate_bps, 0, subframe_mem, delay_compensation); + while (Fs / 400 << LM > frame_size) { + --LM; + } + frame_size = Fs / 400 << LM; + } else { + frame_size = CodecHelpers.frame_size_select(frame_size, variable_duration, Fs); + } + if (frame_size < 0) { + return -1; + } + return frame_size; + } + + static int compute_stereo_width(short[] pcm, int pcm_ptr, int frame_size, int Fs, StereoWidthState mem) { + int frame_rate = Fs / frame_size; + int short_alpha = Short.MAX_VALUE - 819175 / Inlines.IMAX(50, frame_rate); + int yy = 0; + int xy = 0; + int xx = 0; + for (int i = 0; i < frame_size - 3; i += 4) { + int pxx = 0; + int pxy = 0; + int pyy = 0; + int p2i = pcm_ptr + 2 * i; + short x = pcm[p2i]; + short y = pcm[p2i + 1]; + pxx = Inlines.SHR32(Inlines.MULT16_16((int)x, (int)x), 2); + pxy = Inlines.SHR32(Inlines.MULT16_16((int)x, (int)y), 2); + pyy = Inlines.SHR32(Inlines.MULT16_16((int)y, (int)y), 2); + x = pcm[p2i + 2]; + y = pcm[p2i + 3]; + pxx += Inlines.SHR32(Inlines.MULT16_16((int)x, (int)x), 2); + pxy += Inlines.SHR32(Inlines.MULT16_16((int)x, (int)y), 2); + pyy += Inlines.SHR32(Inlines.MULT16_16((int)y, (int)y), 2); + x = pcm[p2i + 4]; + y = pcm[p2i + 5]; + pxx += Inlines.SHR32(Inlines.MULT16_16((int)x, (int)x), 2); + pxy += Inlines.SHR32(Inlines.MULT16_16((int)x, (int)y), 2); + pyy += Inlines.SHR32(Inlines.MULT16_16((int)y, (int)y), 2); + x = pcm[p2i + 6]; + y = pcm[p2i + 7]; + xx += Inlines.SHR32(pxx += Inlines.SHR32(Inlines.MULT16_16((int)x, (int)x), 2), 10); + xy += Inlines.SHR32(pxy += Inlines.SHR32(Inlines.MULT16_16((int)x, (int)y), 2), 10); + yy += Inlines.SHR32(pyy += Inlines.SHR32(Inlines.MULT16_16((int)y, (int)y), 2), 10); + } + mem.XX += Inlines.MULT16_32_Q15(short_alpha, xx - mem.XX); + mem.XY += Inlines.MULT16_32_Q15(short_alpha, xy - mem.XY); + mem.YY += Inlines.MULT16_32_Q15(short_alpha, yy - mem.YY); + mem.XX = Inlines.MAX32(0, mem.XX); + mem.XY = Inlines.MAX32(0, mem.XY); + mem.YY = Inlines.MAX32(0, mem.YY); + if (Inlines.MAX32(mem.XX, mem.YY) > 210) { + int sqrt_xx = Inlines.celt_sqrt(mem.XX); + int sqrt_yy = Inlines.celt_sqrt(mem.YY); + int qrrt_xx = Inlines.celt_sqrt(sqrt_xx); + int qrrt_yy = Inlines.celt_sqrt(sqrt_yy); + mem.XY = Inlines.MIN32(mem.XY, sqrt_xx * sqrt_yy); + int corr = Inlines.SHR32(Inlines.frac_div32(mem.XY, 1 + Inlines.MULT16_16(sqrt_xx, sqrt_yy)), 16); + int ldiff = Short.MAX_VALUE * Inlines.ABS16(qrrt_xx - qrrt_yy) / (1 + qrrt_xx + qrrt_yy); + int width = Inlines.MULT16_16_Q15(Inlines.celt_sqrt(0x40000000 - Inlines.MULT16_16(corr, corr)), ldiff); + mem.smoothed_width += (width - mem.smoothed_width) / frame_rate; + mem.max_follower = Inlines.MAX16(mem.max_follower - 655 / frame_rate, mem.smoothed_width); + } else { + boolean width = false; + int corr = Short.MAX_VALUE; + boolean ldiff = false; + } + return Inlines.EXTRACT16(Inlines.MIN32(Short.MAX_VALUE, 20 * mem.max_follower)); + } + + static void smooth_fade(short[] in1, int in1_ptr, short[] in2, int in2_ptr, short[] output, int output_ptr, int overlap, int channels, int[] window, int Fs) { + int inc = 48000 / Fs; + for (int c = 0; c < channels; ++c) { + for (int i = 0; i < overlap; ++i) { + int w = Inlines.MULT16_16_Q15(window[i * inc], window[i * inc]); + output[output_ptr + i * channels + c] = (short)Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(w, (int)in2[in2_ptr + i * channels + c]), Short.MAX_VALUE - w, (int)in1[in1_ptr + i * channels + c]), 15); + } + } + } + + public static String opus_strerror(int error) { + String[] error_strings = new String[]{"success", "invalid argument", "buffer too small", "error", "corrupted stream", "request not implemented", "invalid state", "memory allocation failed"}; + if (error > 0 || error < -7) { + return "unknown error"; + } + return error_strings[-error]; + } + + public static String GetVersionString() { + return "concentus 1.0a-java-fixed"; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/CorrelateMatrix.java b/source/decompiled/de/maxhenkel/voicechat/concentus/CorrelateMatrix.java new file mode 100644 index 0000000..ba1197f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/CorrelateMatrix.java @@ -0,0 +1,94 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SumSqrShift; + +class CorrelateMatrix { + CorrelateMatrix() { + } + + static void silk_corrVector(short[] x, int x_ptr, short[] t, int t_ptr, int L, int order, int[] Xt, int rshifts) { + int ptr1 = x_ptr + order - 1; + int ptr2 = t_ptr; + if (rshifts > 0) { + for (int lag = 0; lag < order; ++lag) { + int inner_prod = 0; + for (int i = 0; i < L; ++i) { + inner_prod += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], t[ptr2 + i]), rshifts); + } + Xt[lag] = inner_prod; + --ptr1; + } + } else { + Inlines.OpusAssert(rshifts == 0); + for (int lag = 0; lag < order; ++lag) { + Xt[lag] = Inlines.silk_inner_prod(x, ptr1, t, ptr2, L); + --ptr1; + } + } + } + + static void silk_corrMatrix(short[] x, int x_ptr, int L, int order, int head_room, int[] XX, int XX_ptr, BoxedValueInt rshifts) { + int j; + int i; + BoxedValueInt boxed_energy = new BoxedValueInt(0); + BoxedValueInt boxed_rshifts_local = new BoxedValueInt(0); + SumSqrShift.silk_sum_sqr_shift(boxed_energy, boxed_rshifts_local, x, x_ptr, L + order - 1); + int energy = boxed_energy.Val; + int rshifts_local = boxed_rshifts_local.Val; + int head_room_rshifts = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0); + energy = Inlines.silk_RSHIFT32(energy, head_room_rshifts); + rshifts_local += head_room_rshifts; + for (i = x_ptr; i < x_ptr + order - 1; ++i) { + energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), rshifts_local); + } + if (rshifts_local < rshifts.Val) { + energy = Inlines.silk_RSHIFT32(energy, rshifts.Val - rshifts_local); + rshifts_local = rshifts.Val; + } + Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy); + int ptr1 = x_ptr + order - 1; + for (j = 1; j < order; ++j) { + energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr1 + L - j]), rshifts_local)); + energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr1 - j]), rshifts_local)); + Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy); + } + int ptr2 = x_ptr + order - 2; + if (rshifts_local > 0) { + for (int lag = 1; lag < order; ++lag) { + energy = 0; + for (i = 0; i < L; ++i) { + energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], x[ptr2 + i]), rshifts_local); + } + Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy); + Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy); + for (j = 1; j < order - lag; ++j) { + energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]), rshifts_local)); + energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr2 - j]), rshifts_local)); + Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy); + Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy); + } + --ptr2; + } + } else { + for (int lag = 1; lag < order; ++lag) { + energy = Inlines.silk_inner_prod(x, ptr1, x, ptr2, L); + Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy); + Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy); + for (j = 1; j < order - lag; ++j) { + energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j])); + energy = Inlines.silk_SMLABB(energy, x[ptr1 - j], x[ptr2 - j]); + Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy); + Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy); + } + --ptr2; + } + } + rshifts.Val = rshifts_local; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/DecControlState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/DecControlState.java new file mode 100644 index 0000000..048b1bc --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/DecControlState.java @@ -0,0 +1,26 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class DecControlState { + int nChannelsAPI = 0; + int nChannelsInternal = 0; + int API_sampleRate = 0; + int internalSampleRate = 0; + int payloadSize_ms = 0; + int prevPitchLag = 0; + + DecControlState() { + } + + void Reset() { + this.nChannelsAPI = 0; + this.nChannelsInternal = 0; + this.API_sampleRate = 0; + this.internalSampleRate = 0; + this.payloadSize_ms = 0; + this.prevPitchLag = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeAPI.java b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeAPI.java new file mode 100644 index 0000000..ba9205d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeAPI.java @@ -0,0 +1,245 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.DecControlState; +import de.maxhenkel.voicechat.concentus.DecodeIndices; +import de.maxhenkel.voicechat.concentus.DecodePulses; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Resampler; +import de.maxhenkel.voicechat.concentus.SilkChannelDecoder; +import de.maxhenkel.voicechat.concentus.SilkDecoder; +import de.maxhenkel.voicechat.concentus.SilkError; +import de.maxhenkel.voicechat.concentus.SilkTables; +import de.maxhenkel.voicechat.concentus.Stereo; + +class DecodeAPI { + DecodeAPI() { + } + + static int silk_InitDecoder(SilkDecoder decState) { + decState.Reset(); + int ret = SilkError.SILK_NO_ERROR; + SilkChannelDecoder[] channel_states = decState.channel_state; + for (int n = 0; n < 2; ++n) { + ret = channel_states[n].silk_init_decoder(); + } + decState.sStereo.Reset(); + decState.prev_decode_only_middle = 0; + return ret; + } + + static int silk_Decode(SilkDecoder psDec, DecControlState decControl, int lostFlag, int newPacketFlag, EntropyCoder psRangeDec, short[] samplesOut, int samplesOut_ptr, BoxedValueInt nSamplesOut) { + int resample_out_ptr; + short[] resample_out; + short[] samplesOut_tmp; + boolean delay_stack_alloc; + int condCoding; + int i; + boolean stereo_to_mono; + int n; + int decode_only_middle = 0; + int ret = SilkError.SILK_NO_ERROR; + BoxedValueInt nSamplesOutDec = new BoxedValueInt(0); + int[] samplesOut_tmp_ptrs = new int[2]; + int[] MS_pred_Q13 = new int[]{0, 0}; + SilkChannelDecoder[] channel_state = psDec.channel_state; + nSamplesOut.Val = 0; + Inlines.OpusAssert(decControl.nChannelsInternal == 1 || decControl.nChannelsInternal == 2); + if (newPacketFlag != 0) { + for (n = 0; n < decControl.nChannelsInternal; ++n) { + channel_state[n].nFramesDecoded = 0; + } + } + if (decControl.nChannelsInternal > psDec.nChannelsInternal) { + ret += channel_state[1].silk_init_decoder(); + } + boolean bl = stereo_to_mono = decControl.nChannelsInternal == 1 && psDec.nChannelsInternal == 2 && decControl.internalSampleRate == 1000 * channel_state[0].fs_kHz; + if (channel_state[0].nFramesDecoded == 0) { + for (n = 0; n < decControl.nChannelsInternal; ++n) { + if (decControl.payloadSize_ms == 0) { + channel_state[n].nFramesPerPacket = 1; + channel_state[n].nb_subfr = 2; + } else if (decControl.payloadSize_ms == 10) { + channel_state[n].nFramesPerPacket = 1; + channel_state[n].nb_subfr = 2; + } else if (decControl.payloadSize_ms == 20) { + channel_state[n].nFramesPerPacket = 1; + channel_state[n].nb_subfr = 4; + } else if (decControl.payloadSize_ms == 40) { + channel_state[n].nFramesPerPacket = 2; + channel_state[n].nb_subfr = 4; + } else if (decControl.payloadSize_ms == 60) { + channel_state[n].nFramesPerPacket = 3; + channel_state[n].nb_subfr = 4; + } else { + Inlines.OpusAssert(false); + return SilkError.SILK_DEC_INVALID_FRAME_SIZE; + } + int fs_kHz_dec = (decControl.internalSampleRate >> 10) + 1; + if (fs_kHz_dec != 8 && fs_kHz_dec != 12 && fs_kHz_dec != 16) { + Inlines.OpusAssert(false); + return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY; + } + ret += channel_state[n].silk_decoder_set_fs(fs_kHz_dec, decControl.API_sampleRate); + } + } + if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2 && (psDec.nChannelsAPI == 1 || psDec.nChannelsInternal == 1)) { + Arrays.MemSet(psDec.sStereo.pred_prev_Q13, (short)0, 2); + Arrays.MemSet(psDec.sStereo.sSide, (short)0, 2); + channel_state[1].resampler_state.Assign(channel_state[0].resampler_state); + } + psDec.nChannelsAPI = decControl.nChannelsAPI; + psDec.nChannelsInternal = decControl.nChannelsInternal; + if (decControl.API_sampleRate > 48000 || decControl.API_sampleRate < 8000) { + ret = SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY; + return ret; + } + if (lostFlag != 1 && channel_state[0].nFramesDecoded == 0) { + for (n = 0; n < decControl.nChannelsInternal; ++n) { + for (i = 0; i < channel_state[n].nFramesPerPacket; ++i) { + channel_state[n].VAD_flags[i] = psRangeDec.dec_bit_logp(1L); + } + channel_state[n].LBRR_flag = psRangeDec.dec_bit_logp(1L); + } + for (n = 0; n < decControl.nChannelsInternal; ++n) { + Arrays.MemSet(channel_state[n].LBRR_flags, 0, 3); + if (channel_state[n].LBRR_flag == 0) continue; + if (channel_state[n].nFramesPerPacket == 1) { + channel_state[n].LBRR_flags[0] = 1; + continue; + } + int LBRR_symbol = psRangeDec.dec_icdf(SilkTables.silk_LBRR_flags_iCDF_ptr[channel_state[n].nFramesPerPacket - 2], 8) + 1; + for (i = 0; i < channel_state[n].nFramesPerPacket; ++i) { + channel_state[n].LBRR_flags[i] = Inlines.silk_RSHIFT(LBRR_symbol, i) & 1; + } + } + if (lostFlag == 0) { + for (i = 0; i < channel_state[0].nFramesPerPacket; ++i) { + for (n = 0; n < decControl.nChannelsInternal; ++n) { + if (channel_state[n].LBRR_flags[i] == 0) continue; + short[] pulses = new short[320]; + if (decControl.nChannelsInternal == 2 && n == 0) { + Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13); + if (channel_state[1].LBRR_flags[i] == 0) { + BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle); + Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed); + decode_only_middle = decodeOnlyMiddleBoxed.Val; + } + } + condCoding = i > 0 && channel_state[n].LBRR_flags[i - 1] != 0 ? 2 : 0; + DecodeIndices.silk_decode_indices(channel_state[n], psRangeDec, i, 1, condCoding); + DecodePulses.silk_decode_pulses(psRangeDec, pulses, channel_state[n].indices.signalType, channel_state[n].indices.quantOffsetType, channel_state[n].frame_length); + } + } + } + } + if (decControl.nChannelsInternal == 2) { + if (lostFlag == 0 || lostFlag == 2 && channel_state[0].LBRR_flags[channel_state[0].nFramesDecoded] == 1) { + Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13); + if (lostFlag == 0 && channel_state[1].VAD_flags[channel_state[0].nFramesDecoded] == 0 || lostFlag == 2 && channel_state[1].LBRR_flags[channel_state[0].nFramesDecoded] == 0) { + BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle); + Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed); + decode_only_middle = decodeOnlyMiddleBoxed.Val; + } else { + decode_only_middle = 0; + } + } else { + for (n = 0; n < 2; ++n) { + MS_pred_Q13[n] = psDec.sStereo.pred_prev_Q13[n]; + } + } + } + if (decControl.nChannelsInternal == 2 && decode_only_middle == 0 && psDec.prev_decode_only_middle == 1) { + Arrays.MemSet(psDec.channel_state[1].outBuf, (short)0, 480); + Arrays.MemSet(psDec.channel_state[1].sLPC_Q14_buf, 0, 16); + psDec.channel_state[1].lagPrev = 100; + psDec.channel_state[1].LastGainIndex = (byte)10; + psDec.channel_state[1].prevSignalType = 0; + psDec.channel_state[1].first_frame_after_reset = 1; + } + boolean bl2 = delay_stack_alloc = decControl.internalSampleRate * decControl.nChannelsInternal < decControl.API_sampleRate * decControl.nChannelsAPI; + if (delay_stack_alloc) { + samplesOut_tmp = samplesOut; + samplesOut_tmp_ptrs[0] = samplesOut_ptr; + samplesOut_tmp_ptrs[1] = samplesOut_ptr + channel_state[0].frame_length + 2; + } else { + short[] samplesOut1_tmp_storage1 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)]; + samplesOut_tmp = samplesOut1_tmp_storage1; + samplesOut_tmp_ptrs[0] = 0; + samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2; + } + boolean has_side = lostFlag == 0 ? decode_only_middle == 0 : psDec.prev_decode_only_middle == 0 || decControl.nChannelsInternal == 2 && lostFlag == 2 && channel_state[1].LBRR_flags[channel_state[1].nFramesDecoded] == 1; + for (n = 0; n < decControl.nChannelsInternal; ++n) { + if (n == 0 || has_side) { + int FrameIndex = channel_state[0].nFramesDecoded - n; + condCoding = FrameIndex <= 0 ? 0 : (lostFlag == 2 ? (channel_state[n].LBRR_flags[FrameIndex - 1] != 0 ? 2 : 0) : (n > 0 && psDec.prev_decode_only_middle != 0 ? 1 : 2)); + ret += channel_state[n].silk_decode_frame(psRangeDec, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec, lostFlag, condCoding); + } else { + Arrays.MemSetWithOffset(samplesOut_tmp, (short)0, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec.Val); + } + ++channel_state[n].nFramesDecoded; + } + if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2) { + Stereo.silk_stereo_MS_to_LR(psDec.sStereo, samplesOut_tmp, samplesOut_tmp_ptrs[0], samplesOut_tmp, samplesOut_tmp_ptrs[1], MS_pred_Q13, channel_state[0].fs_kHz, nSamplesOutDec.Val); + } else { + System.arraycopy(psDec.sStereo.sMid, 0, samplesOut_tmp, samplesOut_tmp_ptrs[0], 2); + System.arraycopy(samplesOut_tmp, samplesOut_tmp_ptrs[0] + nSamplesOutDec.Val, psDec.sStereo.sMid, 0, 2); + } + nSamplesOut.Val = Inlines.silk_DIV32(nSamplesOutDec.Val * decControl.API_sampleRate, Inlines.silk_SMULBB(channel_state[0].fs_kHz, 1000)); + if (decControl.nChannelsAPI == 2) { + short[] samplesOut2_tmp = new short[nSamplesOut.Val]; + resample_out = samplesOut2_tmp; + resample_out_ptr = 0; + } else { + resample_out = samplesOut; + resample_out_ptr = samplesOut_ptr; + } + if (delay_stack_alloc) { + short[] samplesOut1_tmp_storage2 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)]; + System.arraycopy(samplesOut, samplesOut_ptr, samplesOut1_tmp_storage2, 0, decControl.nChannelsInternal * (channel_state[0].frame_length + 2)); + samplesOut_tmp = samplesOut1_tmp_storage2; + samplesOut_tmp_ptrs[0] = 0; + samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2; + } + for (n = 0; n < Inlines.silk_min(decControl.nChannelsAPI, decControl.nChannelsInternal); ++n) { + ret += Resampler.silk_resampler(channel_state[n].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 1, nSamplesOutDec.Val); + if (decControl.nChannelsAPI != 2) continue; + int nptr = samplesOut_ptr + n; + for (i = 0; i < nSamplesOut.Val; ++i) { + samplesOut[nptr + 2 * i] = resample_out[resample_out_ptr + i]; + } + } + if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 1) { + if (stereo_to_mono) { + ret += Resampler.silk_resampler(channel_state[1].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[0] + 1, nSamplesOutDec.Val); + for (i = 0; i < nSamplesOut.Val; ++i) { + samplesOut[samplesOut_ptr + 1 + 2 * i] = resample_out[resample_out_ptr + i]; + } + } else { + for (i = 0; i < nSamplesOut.Val; ++i) { + samplesOut[samplesOut_ptr + 1 + 2 * i] = samplesOut[samplesOut_ptr + 2 * i]; + } + } + } + if (channel_state[0].prevSignalType == 2) { + int[] mult_tab = new int[]{6, 4, 3}; + decControl.prevPitchLag = channel_state[0].lagPrev * mult_tab[channel_state[0].fs_kHz - 8 >> 2]; + } else { + decControl.prevPitchLag = 0; + } + if (lostFlag == 1) { + for (i = 0; i < psDec.nChannelsInternal; ++i) { + psDec.channel_state[i].LastGainIndex = (byte)10; + } + } else { + psDec.prev_decode_only_middle = decode_only_middle; + } + return ret; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeCore.java b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeCore.java new file mode 100644 index 0000000..e038e59 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeCore.java @@ -0,0 +1,145 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkChannelDecoder; +import de.maxhenkel.voicechat.concentus.SilkDecoderControl; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class DecodeCore { + DecodeCore() { + } + + static void silk_decode_core(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, short[] xq, int xq_ptr, short[] pulses) { + int i; + int lag = 0; + short[] B_Q14 = psDecCtrl.LTPCoef_Q14; + Inlines.OpusAssert(psDec.prev_gain_Q16 != 0); + short[] sLTP = new short[psDec.ltp_mem_length]; + int[] sLTP_Q15 = new int[psDec.ltp_mem_length + psDec.frame_length]; + int[] res_Q14 = new int[psDec.subfr_length]; + int[] sLPC_Q14 = new int[psDec.subfr_length + 16]; + short offset_Q10 = SilkTables.silk_Quantization_Offsets_Q10[psDec.indices.signalType >> 1][psDec.indices.quantOffsetType]; + boolean NLSF_interpolation_flag = psDec.indices.NLSFInterpCoef_Q2 < 4; + int rand_seed = psDec.indices.Seed; + for (i = 0; i < psDec.frame_length; ++i) { + rand_seed = Inlines.silk_RAND(rand_seed); + psDec.exc_Q14[i] = Inlines.silk_LSHIFT(pulses[i], 14); + if (psDec.exc_Q14[i] > 0) { + int n = i; + psDec.exc_Q14[n] = psDec.exc_Q14[n] - 1280; + } else if (psDec.exc_Q14[i] < 0) { + int n = i; + psDec.exc_Q14[n] = psDec.exc_Q14[n] + 1280; + } + int n = i; + psDec.exc_Q14[n] = psDec.exc_Q14[n] + (offset_Q10 << 4); + if (rand_seed < 0) { + psDec.exc_Q14[i] = -psDec.exc_Q14[i]; + } + rand_seed = Inlines.silk_ADD32_ovflw(rand_seed, pulses[i]); + } + System.arraycopy(psDec.sLPC_Q14_buf, 0, sLPC_Q14, 0, 16); + int pexc_Q14 = 0; + int pxq = xq_ptr; + int sLTP_buf_idx = psDec.ltp_mem_length; + for (int k = 0; k < psDec.nb_subfr; ++k) { + int gain_adj_Q16; + int[] pres_Q14 = res_Q14; + int pres_Q14_ptr = 0; + short[] A_Q12 = psDecCtrl.PredCoef_Q12[k >> 1]; + int B_Q14_ptr = k * 5; + int signalType = psDec.indices.signalType; + int Gain_Q10 = Inlines.silk_RSHIFT(psDecCtrl.Gains_Q16[k], 6); + int inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(psDecCtrl.Gains_Q16[k], 47); + if (psDecCtrl.Gains_Q16[k] != psDec.prev_gain_Q16) { + gain_adj_Q16 = Inlines.silk_DIV32_varQ(psDec.prev_gain_Q16, psDecCtrl.Gains_Q16[k], 16); + for (i = 0; i < 16; ++i) { + sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLPC_Q14[i]); + } + } else { + gain_adj_Q16 = 65536; + } + Inlines.OpusAssert(inv_gain_Q31 != 0); + psDec.prev_gain_Q16 = psDecCtrl.Gains_Q16[k]; + if (psDec.lossCnt != 0 && psDec.prevSignalType == 2 && psDec.indices.signalType != 2 && k < 2) { + Arrays.MemSetWithOffset(B_Q14, (short)0, B_Q14_ptr, 5); + B_Q14[B_Q14_ptr + 2] = 4096; + signalType = 2; + psDecCtrl.pitchL[k] = psDec.lagPrev; + } + if (signalType == 2) { + lag = psDecCtrl.pitchL[k]; + if (k == 0 || k == 2 && NLSF_interpolation_flag) { + int start_idx = psDec.ltp_mem_length - lag - psDec.LPC_order - 2; + Inlines.OpusAssert(start_idx > 0); + if (k == 2) { + System.arraycopy(xq, xq_ptr, psDec.outBuf, psDec.ltp_mem_length, 2 * psDec.subfr_length); + } + Filters.silk_LPC_analysis_filter(sLTP, start_idx, psDec.outBuf, start_idx + k * psDec.subfr_length, A_Q12, 0, psDec.ltp_mem_length - start_idx, psDec.LPC_order); + if (k == 0) { + inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, psDecCtrl.LTP_scale_Q14), 2); + } + for (i = 0; i < lag + 2; ++i) { + sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[psDec.ltp_mem_length - i - 1]); + } + } else if (gain_adj_Q16 != 65536) { + for (i = 0; i < lag + 2; ++i) { + sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[sLTP_buf_idx - i - 1]); + } + } + } + if (signalType == 2) { + int pred_lag_ptr = sLTP_buf_idx - lag + 2; + for (i = 0; i < psDec.subfr_length; ++i) { + int LTP_pred_Q13 = 2; + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr], B_Q14[B_Q14_ptr]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 1], B_Q14[B_Q14_ptr + 1]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 2], B_Q14[B_Q14_ptr + 2]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 3], B_Q14[B_Q14_ptr + 3]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 4], B_Q14[B_Q14_ptr + 4]); + ++pred_lag_ptr; + pres_Q14[pres_Q14_ptr + i] = Inlines.silk_ADD_LSHIFT32(psDec.exc_Q14[pexc_Q14 + i], LTP_pred_Q13, 1); + sLTP_Q15[sLTP_buf_idx] = Inlines.silk_LSHIFT(pres_Q14[pres_Q14_ptr + i], 1); + ++sLTP_buf_idx; + } + } else { + pres_Q14 = psDec.exc_Q14; + pres_Q14_ptr = pexc_Q14; + } + for (i = 0; i < psDec.subfr_length; ++i) { + Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16); + int LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 1], A_Q12[0]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 2], A_Q12[1]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 3], A_Q12[2]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 4], A_Q12[3]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 5], A_Q12[4]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 6], A_Q12[5]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 7], A_Q12[6]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 8], A_Q12[7]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 9], A_Q12[8]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 10], A_Q12[9]); + if (psDec.LPC_order == 16) { + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 11], A_Q12[10]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 12], A_Q12[11]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 13], A_Q12[12]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 14], A_Q12[13]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 15], A_Q12[14]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[16 + i - 16], A_Q12[15]); + } + sLPC_Q14[16 + i] = Inlines.silk_ADD_LSHIFT32(pres_Q14[pres_Q14_ptr + i], LPC_pred_Q10, 4); + xq[pxq + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLPC_Q14[16 + i], Gain_Q10), 8)); + } + System.arraycopy(sLPC_Q14, psDec.subfr_length, sLPC_Q14, 0, 16); + pexc_Q14 += psDec.subfr_length; + pxq += psDec.subfr_length; + } + System.arraycopy(sLPC_Q14, 0, psDec.sLPC_Q14_buf, 0, 16); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeIndices.java b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeIndices.java new file mode 100644 index 0000000..d15da8f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeIndices.java @@ -0,0 +1,68 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.NLSF; +import de.maxhenkel.voicechat.concentus.SilkChannelDecoder; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class DecodeIndices { + DecodeIndices() { + } + + static void silk_decode_indices(SilkChannelDecoder psDec, EntropyCoder psRangeDec, int FrameIndex, int decode_LBRR, int condCoding) { + int i; + short[] ec_ix = new short[psDec.LPC_order]; + short[] pred_Q8 = new short[psDec.LPC_order]; + int Ix = decode_LBRR != 0 || psDec.VAD_flags[FrameIndex] != 0 ? psRangeDec.dec_icdf(SilkTables.silk_type_offset_VAD_iCDF, 8) + 2 : psRangeDec.dec_icdf(SilkTables.silk_type_offset_no_VAD_iCDF, 8); + psDec.indices.signalType = (byte)Inlines.silk_RSHIFT(Ix, 1); + psDec.indices.quantOffsetType = (byte)(Ix & 1); + if (condCoding == 2) { + psDec.indices.GainsIndices[0] = (byte)psRangeDec.dec_icdf(SilkTables.silk_delta_gain_iCDF, 8); + } else { + psDec.indices.GainsIndices[0] = (byte)Inlines.silk_LSHIFT(psRangeDec.dec_icdf(SilkTables.silk_gain_iCDF[psDec.indices.signalType], 8), 3); + psDec.indices.GainsIndices[0] = (byte)(psDec.indices.GainsIndices[0] + (byte)psRangeDec.dec_icdf(SilkTables.silk_uniform8_iCDF, 8)); + } + for (i = 1; i < psDec.nb_subfr; ++i) { + psDec.indices.GainsIndices[i] = (byte)psRangeDec.dec_icdf(SilkTables.silk_delta_gain_iCDF, 8); + } + psDec.indices.NLSFIndices[0] = (byte)psRangeDec.dec_icdf(psDec.psNLSF_CB.CB1_iCDF, (psDec.indices.signalType >> 1) * psDec.psNLSF_CB.nVectors, 8); + NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psDec.psNLSF_CB, psDec.indices.NLSFIndices[0]); + Inlines.OpusAssert(psDec.psNLSF_CB.order == psDec.LPC_order); + for (i = 0; i < psDec.psNLSF_CB.order; ++i) { + Ix = psRangeDec.dec_icdf(psDec.psNLSF_CB.ec_iCDF, ec_ix[i], 8); + if (Ix == 0) { + Ix -= psRangeDec.dec_icdf(SilkTables.silk_NLSF_EXT_iCDF, 8); + } else if (Ix == 8) { + Ix += psRangeDec.dec_icdf(SilkTables.silk_NLSF_EXT_iCDF, 8); + } + psDec.indices.NLSFIndices[i + 1] = (byte)(Ix - 4); + } + psDec.indices.NLSFInterpCoef_Q2 = psDec.nb_subfr == 4 ? (byte)psRangeDec.dec_icdf(SilkTables.silk_NLSF_interpolation_factor_iCDF, 8) : (byte)4; + if (psDec.indices.signalType == 2) { + int delta_lagIndex; + boolean decode_absolute_lagIndex = true; + if (condCoding == 2 && psDec.ec_prevSignalType == 2 && (delta_lagIndex = (int)psRangeDec.dec_icdf(SilkTables.silk_pitch_delta_iCDF, 8)) > 0) { + psDec.indices.lagIndex = (short)(psDec.ec_prevLagIndex + (delta_lagIndex -= 9)); + decode_absolute_lagIndex = false; + } + if (decode_absolute_lagIndex) { + psDec.indices.lagIndex = (short)(psRangeDec.dec_icdf(SilkTables.silk_pitch_lag_iCDF, 8) * Inlines.silk_RSHIFT(psDec.fs_kHz, 1)); + psDec.indices.lagIndex = (short)(psDec.indices.lagIndex + (short)psRangeDec.dec_icdf(psDec.pitch_lag_low_bits_iCDF, 8)); + } + psDec.ec_prevLagIndex = psDec.indices.lagIndex; + psDec.indices.contourIndex = (byte)psRangeDec.dec_icdf(psDec.pitch_contour_iCDF, 8); + psDec.indices.PERIndex = (byte)psRangeDec.dec_icdf(SilkTables.silk_LTP_per_index_iCDF, 8); + for (int k = 0; k < psDec.nb_subfr; ++k) { + psDec.indices.LTPIndex[k] = (byte)psRangeDec.dec_icdf(SilkTables.silk_LTP_gain_iCDF_ptrs[psDec.indices.PERIndex], 8); + } + psDec.indices.LTP_scaleIndex = condCoding == 0 ? (byte)psRangeDec.dec_icdf(SilkTables.silk_LTPscale_iCDF, 8) : (byte)0; + } + psDec.ec_prevSignalType = psDec.indices.signalType; + psDec.indices.Seed = (byte)psRangeDec.dec_icdf(SilkTables.silk_uniform4_iCDF, 8); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeParameters.java b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeParameters.java new file mode 100644 index 0000000..3f5b6cd --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodeParameters.java @@ -0,0 +1,66 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BWExpander; +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.DecodePitch; +import de.maxhenkel.voicechat.concentus.GainQuantization; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.NLSF; +import de.maxhenkel.voicechat.concentus.SilkChannelDecoder; +import de.maxhenkel.voicechat.concentus.SilkDecoderControl; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class DecodeParameters { + DecodeParameters() { + } + + static void silk_decode_parameters(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, int condCoding) { + int i; + short[] pNLSF_Q15 = new short[psDec.LPC_order]; + short[] pNLSF0_Q15 = new short[psDec.LPC_order]; + BoxedValueByte boxedLastGainIndex = new BoxedValueByte(psDec.LastGainIndex); + GainQuantization.silk_gains_dequant(psDecCtrl.Gains_Q16, psDec.indices.GainsIndices, boxedLastGainIndex, condCoding == 2 ? 1 : 0, psDec.nb_subfr); + psDec.LastGainIndex = boxedLastGainIndex.Val; + NLSF.silk_NLSF_decode(pNLSF_Q15, psDec.indices.NLSFIndices, psDec.psNLSF_CB); + NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[1], pNLSF_Q15, psDec.LPC_order); + if (psDec.first_frame_after_reset == 1) { + psDec.indices.NLSFInterpCoef_Q2 = (byte)4; + } + if (psDec.indices.NLSFInterpCoef_Q2 < 4) { + for (i = 0; i < psDec.LPC_order; ++i) { + pNLSF0_Q15[i] = (short)(psDec.prevNLSF_Q15[i] + Inlines.silk_RSHIFT(Inlines.silk_MUL(psDec.indices.NLSFInterpCoef_Q2, pNLSF_Q15[i] - psDec.prevNLSF_Q15[i]), 2)); + } + NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[0], pNLSF0_Q15, psDec.LPC_order); + } else { + System.arraycopy(psDecCtrl.PredCoef_Q12[1], 0, psDecCtrl.PredCoef_Q12[0], 0, psDec.LPC_order); + } + System.arraycopy(pNLSF_Q15, 0, psDec.prevNLSF_Q15, 0, psDec.LPC_order); + if (psDec.lossCnt != 0) { + BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[0], psDec.LPC_order, 63570); + BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[1], psDec.LPC_order, 63570); + } + if (psDec.indices.signalType == 2) { + byte Ix; + DecodePitch.silk_decode_pitch(psDec.indices.lagIndex, psDec.indices.contourIndex, psDecCtrl.pitchL, psDec.fs_kHz, psDec.nb_subfr); + byte[][] cbk_ptr_Q7 = SilkTables.silk_LTP_vq_ptrs_Q7[psDec.indices.PERIndex]; + for (int k = 0; k < psDec.nb_subfr; ++k) { + Ix = psDec.indices.LTPIndex[k]; + for (i = 0; i < 5; ++i) { + psDecCtrl.LTPCoef_Q14[k * 5 + i] = (short)Inlines.silk_LSHIFT(cbk_ptr_Q7[Ix][i], 7); + } + } + Ix = psDec.indices.LTP_scaleIndex; + psDecCtrl.LTP_scale_Q14 = SilkTables.silk_LTPScales_table_Q14[Ix]; + } else { + Arrays.MemSet(psDecCtrl.pitchL, 0, psDec.nb_subfr); + Arrays.MemSet(psDecCtrl.LTPCoef_Q14, (short)0, 5 * psDec.nb_subfr); + psDec.indices.PERIndex = 0; + psDecCtrl.LTP_scale_Q14 = 0; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/DecodePitch.java b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodePitch.java new file mode 100644 index 0000000..555f395 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodePitch.java @@ -0,0 +1,37 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class DecodePitch { + DecodePitch() { + } + + static void silk_decode_pitch(short lagIndex, byte contourIndex, int[] pitch_lags, int Fs_kHz, int nb_subfr) { + byte[][] Lag_CB_ptr; + if (Fs_kHz == 8) { + if (nb_subfr == 4) { + Lag_CB_ptr = SilkTables.silk_CB_lags_stage2; + } else { + Inlines.OpusAssert(nb_subfr == 2); + Lag_CB_ptr = SilkTables.silk_CB_lags_stage2_10_ms; + } + } else if (nb_subfr == 4) { + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3; + } else { + Inlines.OpusAssert(nb_subfr == 2); + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3_10_ms; + } + int min_lag = Inlines.silk_SMULBB(2, Fs_kHz); + int max_lag = Inlines.silk_SMULBB(18, Fs_kHz); + int lag = min_lag + lagIndex; + for (int k = 0; k < nb_subfr; ++k) { + pitch_lags[k] = lag + Lag_CB_ptr[k][contourIndex]; + pitch_lags[k] = Inlines.silk_LIMIT(pitch_lags[k], min_lag, max_lag); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/DecodePulses.java b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodePulses.java new file mode 100644 index 0000000..c9daee8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/DecodePulses.java @@ -0,0 +1,62 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.CodeSigns; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.ShellCoder; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class DecodePulses { + DecodePulses() { + } + + static void silk_decode_pulses(EntropyCoder psRangeDec, short[] pulses, int signalType, int quantOffsetType, int frame_length) { + int i; + int[] sum_pulses = new int[20]; + int[] nLshifts = new int[20]; + int RateLevelIndex = psRangeDec.dec_icdf(SilkTables.silk_rate_levels_iCDF[signalType >> 1], 8); + Inlines.OpusAssert(true); + int iter = Inlines.silk_RSHIFT(frame_length, 4); + if (iter * 16 < frame_length) { + Inlines.OpusAssert(frame_length == 120); + ++iter; + } + for (i = 0; i < iter; ++i) { + nLshifts[i] = 0; + sum_pulses[i] = psRangeDec.dec_icdf(SilkTables.silk_pulses_per_block_iCDF[RateLevelIndex], 8); + while (sum_pulses[i] == 17) { + int n = i; + nLshifts[n] = nLshifts[n] + 1; + sum_pulses[i] = psRangeDec.dec_icdf(SilkTables.silk_pulses_per_block_iCDF[9], nLshifts[i] == 10 ? 1 : 0, 8); + } + } + for (i = 0; i < iter; ++i) { + if (sum_pulses[i] > 0) { + ShellCoder.silk_shell_decoder(pulses, Inlines.silk_SMULBB(i, 16), psRangeDec, sum_pulses[i]); + continue; + } + Arrays.MemSetWithOffset(pulses, (short)0, Inlines.silk_SMULBB(i, 16), 16); + } + for (i = 0; i < iter; ++i) { + if (nLshifts[i] <= 0) continue; + int nLS = nLshifts[i]; + int pulses_ptr = Inlines.silk_SMULBB(i, 16); + for (int k = 0; k < 16; ++k) { + int abs_q = pulses[pulses_ptr + k]; + for (int j = 0; j < nLS; ++j) { + abs_q = Inlines.silk_LSHIFT(abs_q, 1); + abs_q += psRangeDec.dec_icdf(SilkTables.silk_lsb_iCDF, 8); + } + pulses[pulses_ptr + k] = (short)abs_q; + } + int n = i; + sum_pulses[n] = sum_pulses[n] | nLS << 5; + } + CodeSigns.silk_decode_signs(psRangeDec, pulses, frame_length, signalType, quantOffsetType, sum_pulses); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/DecoderAPIFlag.java b/source/decompiled/de/maxhenkel/voicechat/concentus/DecoderAPIFlag.java new file mode 100644 index 0000000..80363a7 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/DecoderAPIFlag.java @@ -0,0 +1,14 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class DecoderAPIFlag { + public static final int FLAG_DECODE_NORMAL = 0; + public static final int FLAG_PACKET_LOST = 1; + public static final int FLAG_DECODE_LBRR = 2; + + DecoderAPIFlag() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Downmix.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Downmix.java new file mode 100644 index 0000000..33f05c3 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Downmix.java @@ -0,0 +1,36 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class Downmix { + Downmix() { + } + + static void downmix_int(short[] x, int x_ptr, int[] sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C) { + int j; + for (j = 0; j < subframe; ++j) { + sub[j + sub_ptr] = x[(j + offset) * C + c1]; + } + if (c2 > -1) { + for (j = 0; j < subframe; ++j) { + int n = j + sub_ptr; + sub[n] = sub[n] + x[(j + offset) * C + c2]; + } + } else if (c2 == -2) { + for (int c = 1; c < C; ++c) { + for (j = 0; j < subframe; ++j) { + int n = j + sub_ptr; + sub[n] = sub[n] + x[(j + offset) * C + c]; + } + } + } + int scale = 4096; + scale = C == -2 ? (scale /= C) : (scale /= 2); + for (j = 0; j < subframe; ++j) { + int n = j + sub_ptr; + sub[n] = sub[n] * scale; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/EncControlState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/EncControlState.java new file mode 100644 index 0000000..06db12e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/EncControlState.java @@ -0,0 +1,105 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkError; + +class EncControlState { + int nChannelsAPI = 0; + int nChannelsInternal = 0; + int API_sampleRate = 0; + int maxInternalSampleRate = 0; + int minInternalSampleRate = 0; + int desiredInternalSampleRate = 0; + int payloadSize_ms = 0; + int bitRate = 0; + int packetLossPercentage = 0; + int complexity = 0; + int useInBandFEC = 0; + int useDTX = 0; + int useCBR = 0; + int maxBits = 0; + int toMono = 0; + int opusCanSwitch = 0; + int reducedDependency = 0; + int internalSampleRate = 0; + int allowBandwidthSwitch = 0; + int inWBmodeWithoutVariableLP = 0; + int stereoWidth_Q14 = 0; + int switchReady = 0; + + EncControlState() { + } + + void Reset() { + this.nChannelsAPI = 0; + this.nChannelsInternal = 0; + this.API_sampleRate = 0; + this.maxInternalSampleRate = 0; + this.minInternalSampleRate = 0; + this.desiredInternalSampleRate = 0; + this.payloadSize_ms = 0; + this.bitRate = 0; + this.packetLossPercentage = 0; + this.complexity = 0; + this.useInBandFEC = 0; + this.useDTX = 0; + this.useCBR = 0; + this.maxBits = 0; + this.toMono = 0; + this.opusCanSwitch = 0; + this.reducedDependency = 0; + this.internalSampleRate = 0; + this.allowBandwidthSwitch = 0; + this.inWBmodeWithoutVariableLP = 0; + this.stereoWidth_Q14 = 0; + this.switchReady = 0; + } + + int check_control_input() { + if (this.API_sampleRate != 8000 && this.API_sampleRate != 12000 && this.API_sampleRate != 16000 && this.API_sampleRate != 24000 && this.API_sampleRate != 32000 && this.API_sampleRate != 44100 && this.API_sampleRate != 48000 || this.desiredInternalSampleRate != 8000 && this.desiredInternalSampleRate != 12000 && this.desiredInternalSampleRate != 16000 || this.maxInternalSampleRate != 8000 && this.maxInternalSampleRate != 12000 && this.maxInternalSampleRate != 16000 || this.minInternalSampleRate != 8000 && this.minInternalSampleRate != 12000 && this.minInternalSampleRate != 16000 || this.minInternalSampleRate > this.desiredInternalSampleRate || this.maxInternalSampleRate < this.desiredInternalSampleRate || this.minInternalSampleRate > this.maxInternalSampleRate) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_FS_NOT_SUPPORTED; + } + if (this.payloadSize_ms != 10 && this.payloadSize_ms != 20 && this.payloadSize_ms != 40 && this.payloadSize_ms != 60) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_PACKET_SIZE_NOT_SUPPORTED; + } + if (this.packetLossPercentage < 0 || this.packetLossPercentage > 100) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_LOSS_RATE; + } + if (this.useDTX < 0 || this.useDTX > 1) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_DTX_SETTING; + } + if (this.useCBR < 0 || this.useCBR > 1) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_CBR_SETTING; + } + if (this.useInBandFEC < 0 || this.useInBandFEC > 1) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_INBAND_FEC_SETTING; + } + if (this.nChannelsAPI < 1 || this.nChannelsAPI > 2) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; + } + if (this.nChannelsInternal < 1 || this.nChannelsInternal > 2) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; + } + if (this.nChannelsInternal > this.nChannelsAPI) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR; + } + if (this.complexity < 0 || this.complexity > 10) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INVALID_COMPLEXITY_SETTING; + } + return SilkError.SILK_NO_ERROR; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/EncodeAPI.java b/source/decompiled/de/maxhenkel/voicechat/concentus/EncodeAPI.java new file mode 100644 index 0000000..a804aa0 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/EncodeAPI.java @@ -0,0 +1,352 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.EncControlState; +import de.maxhenkel.voicechat.concentus.EncodeIndices; +import de.maxhenkel.voicechat.concentus.EncodePulses; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.HPVariableCutoff; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Resampler; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkEncoder; +import de.maxhenkel.voicechat.concentus.SilkError; +import de.maxhenkel.voicechat.concentus.SilkTables; +import de.maxhenkel.voicechat.concentus.Stereo; + +class EncodeAPI { + EncodeAPI() { + } + + static int silk_InitEncoder(SilkEncoder encState, EncControlState encStatus) { + int ret = SilkError.SILK_NO_ERROR; + encState.Reset(); + for (int n = 0; n < 2; ++n) { + Inlines.OpusAssert((ret += SilkEncoder.silk_init_encoder(encState.state_Fxx[n])) == SilkError.SILK_NO_ERROR); + } + encState.nChannelsAPI = 1; + encState.nChannelsInternal = 1; + Inlines.OpusAssert((ret += EncodeAPI.silk_QueryEncoder(encState, encStatus)) == SilkError.SILK_NO_ERROR); + return ret; + } + + static int silk_QueryEncoder(SilkEncoder encState, EncControlState encStatus) { + int ret = SilkError.SILK_NO_ERROR; + SilkChannelEncoder state_Fxx = encState.state_Fxx[0]; + encStatus.Reset(); + encStatus.nChannelsAPI = encState.nChannelsAPI; + encStatus.nChannelsInternal = encState.nChannelsInternal; + encStatus.API_sampleRate = state_Fxx.API_fs_Hz; + encStatus.maxInternalSampleRate = state_Fxx.maxInternal_fs_Hz; + encStatus.minInternalSampleRate = state_Fxx.minInternal_fs_Hz; + encStatus.desiredInternalSampleRate = state_Fxx.desiredInternal_fs_Hz; + encStatus.payloadSize_ms = state_Fxx.PacketSize_ms; + encStatus.bitRate = state_Fxx.TargetRate_bps; + encStatus.packetLossPercentage = state_Fxx.PacketLoss_perc; + encStatus.complexity = state_Fxx.Complexity; + encStatus.useInBandFEC = state_Fxx.useInBandFEC; + encStatus.useDTX = state_Fxx.useDTX; + encStatus.useCBR = state_Fxx.useCBR; + encStatus.internalSampleRate = Inlines.silk_SMULBB(state_Fxx.fs_kHz, 1000); + encStatus.allowBandwidthSwitch = state_Fxx.allow_bandwidth_switch; + encStatus.inWBmodeWithoutVariableLP = state_Fxx.fs_kHz == 16 && state_Fxx.sLP.mode == 0 ? 1 : 0; + return ret; + } + + static int silk_Encode(SilkEncoder psEnc, EncControlState encControl, short[] samplesIn, int nSamplesIn, EntropyCoder psRangeEnc, BoxedValueInt nBytesOut, int prefillFlag) { + int i; + int n; + int ret = SilkError.SILK_NO_ERROR; + int tmp_payloadSize_ms = 0; + int tmp_complexity = 0; + int nSamplesFromInput = 0; + int[] MStargetRates_bps = new int[2]; + nBytesOut.Val = 0; + if (encControl.reducedDependency != 0) { + psEnc.state_Fxx[0].first_frame_after_reset = 1; + psEnc.state_Fxx[1].first_frame_after_reset = 1; + } + psEnc.state_Fxx[1].nFramesEncoded = 0; + psEnc.state_Fxx[0].nFramesEncoded = 0; + if ((ret += encControl.check_control_input()) != SilkError.SILK_NO_ERROR) { + Inlines.OpusAssert(false); + return ret; + } + encControl.switchReady = 0; + if (encControl.nChannelsInternal > psEnc.nChannelsInternal) { + ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[1]); + Arrays.MemSet(psEnc.sStereo.pred_prev_Q13, (short)0, 2); + Arrays.MemSet(psEnc.sStereo.sSide, (short)0, 2); + psEnc.sStereo.mid_side_amp_Q0[0] = 0; + psEnc.sStereo.mid_side_amp_Q0[1] = 1; + psEnc.sStereo.mid_side_amp_Q0[2] = 0; + psEnc.sStereo.mid_side_amp_Q0[3] = 1; + psEnc.sStereo.width_prev_Q14 = 0; + psEnc.sStereo.smth_width_Q14 = (short)16384; + if (psEnc.nChannelsAPI == 2) { + psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state); + System.arraycopy(psEnc.state_Fxx[0].In_HP_State, 0, psEnc.state_Fxx[1].In_HP_State, 0, 2); + } + } + boolean transition = encControl.payloadSize_ms != psEnc.state_Fxx[0].PacketSize_ms || psEnc.nChannelsInternal != encControl.nChannelsInternal; + psEnc.nChannelsAPI = encControl.nChannelsAPI; + psEnc.nChannelsInternal = encControl.nChannelsInternal; + int nBlocksOf10ms = Inlines.silk_DIV32(100 * nSamplesIn, encControl.API_sampleRate); + int tot_blocks = nBlocksOf10ms > 1 ? nBlocksOf10ms >> 1 : 1; + int curr_block = 0; + if (prefillFlag != 0) { + if (nBlocksOf10ms != 1) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; + } + for (n = 0; n < encControl.nChannelsInternal; ++n) { + Inlines.OpusAssert((ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[n])) == SilkError.SILK_NO_ERROR); + } + tmp_payloadSize_ms = encControl.payloadSize_ms; + encControl.payloadSize_ms = 10; + tmp_complexity = encControl.complexity; + encControl.complexity = 0; + for (n = 0; n < encControl.nChannelsInternal; ++n) { + psEnc.state_Fxx[n].controlled_since_last_payload = 0; + psEnc.state_Fxx[n].prefillFlag = 1; + } + } else { + if (nBlocksOf10ms * encControl.API_sampleRate != 100 * nSamplesIn || nSamplesIn < 0) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; + } + if (1000 * nSamplesIn > encControl.payloadSize_ms * encControl.API_sampleRate) { + Inlines.OpusAssert(false); + return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES; + } + } + int TargetRate_bps = Inlines.silk_RSHIFT32(encControl.bitRate, encControl.nChannelsInternal - 1); + for (n = 0; n < encControl.nChannelsInternal; ++n) { + int force_fs_kHz; + int n2 = force_fs_kHz = n == 1 ? psEnc.state_Fxx[0].fs_kHz : 0; + if ((ret += psEnc.state_Fxx[n].silk_control_encoder(encControl, TargetRate_bps, psEnc.allowBandwidthSwitch, n, force_fs_kHz)) != SilkError.SILK_NO_ERROR) { + Inlines.OpusAssert(false); + return ret; + } + if (psEnc.state_Fxx[n].first_frame_after_reset != 0 || transition) { + for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; ++i) { + psEnc.state_Fxx[n].LBRR_flags[i] = 0; + } + } + psEnc.state_Fxx[n].inDTX = psEnc.state_Fxx[n].useDTX; + } + Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[0].fs_kHz == psEnc.state_Fxx[1].fs_kHz); + int nSamplesToBufferMax = 10 * nBlocksOf10ms * psEnc.state_Fxx[0].fs_kHz; + int nSamplesFromInputMax = Inlines.silk_DIV32_16(nSamplesToBufferMax * psEnc.state_Fxx[0].API_fs_Hz, (short)(psEnc.state_Fxx[0].fs_kHz * 1000)); + short[] buf = new short[nSamplesFromInputMax]; + int samplesIn_ptr = 0; + while (true) { + int nSamplesToBuffer = psEnc.state_Fxx[0].frame_length - psEnc.state_Fxx[0].inputBufIx; + nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, nSamplesToBufferMax); + nSamplesFromInput = Inlines.silk_DIV32_16(nSamplesToBuffer * psEnc.state_Fxx[0].API_fs_Hz, psEnc.state_Fxx[0].fs_kHz * 1000); + if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 2) { + int id = psEnc.state_Fxx[0].nFramesEncoded; + for (n = 0; n < nSamplesFromInput; ++n) { + buf[n] = samplesIn[samplesIn_ptr + 2 * n]; + } + if (psEnc.nPrevChannelsInternal == 1 && id == 0) { + psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state); + } + ret += Resampler.silk_resampler(psEnc.state_Fxx[0].resampler_state, psEnc.state_Fxx[0].inputBuf, psEnc.state_Fxx[0].inputBufIx + 2, buf, 0, nSamplesFromInput); + psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer; + nSamplesToBuffer = psEnc.state_Fxx[1].frame_length - psEnc.state_Fxx[1].inputBufIx; + nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, 10 * nBlocksOf10ms * psEnc.state_Fxx[1].fs_kHz); + for (n = 0; n < nSamplesFromInput; ++n) { + buf[n] = samplesIn[samplesIn_ptr + 2 * n + 1]; + } + ret += Resampler.silk_resampler(psEnc.state_Fxx[1].resampler_state, psEnc.state_Fxx[1].inputBuf, psEnc.state_Fxx[1].inputBufIx + 2, buf, 0, nSamplesFromInput); + psEnc.state_Fxx[1].inputBufIx += nSamplesToBuffer; + } else if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 1) { + for (n = 0; n < nSamplesFromInput; ++n) { + int sum = samplesIn[samplesIn_ptr + 2 * n] + samplesIn[samplesIn_ptr + 2 * n + 1]; + buf[n] = (short)Inlines.silk_RSHIFT_ROUND(sum, 1); + } + ret += Resampler.silk_resampler(psEnc.state_Fxx[0].resampler_state, psEnc.state_Fxx[0].inputBuf, psEnc.state_Fxx[0].inputBufIx + 2, buf, 0, nSamplesFromInput); + if (psEnc.nPrevChannelsInternal == 2 && psEnc.state_Fxx[0].nFramesEncoded == 0) { + ret += Resampler.silk_resampler(psEnc.state_Fxx[1].resampler_state, psEnc.state_Fxx[1].inputBuf, psEnc.state_Fxx[1].inputBufIx + 2, buf, 0, nSamplesFromInput); + for (n = 0; n < psEnc.state_Fxx[0].frame_length; ++n) { + psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2] = (short)Inlines.silk_RSHIFT(psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2] + psEnc.state_Fxx[1].inputBuf[psEnc.state_Fxx[1].inputBufIx + n + 2], 1); + } + } + psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer; + } else { + Inlines.OpusAssert(encControl.nChannelsAPI == 1 && encControl.nChannelsInternal == 1); + System.arraycopy(samplesIn, samplesIn_ptr, buf, 0, nSamplesFromInput); + ret += Resampler.silk_resampler(psEnc.state_Fxx[0].resampler_state, psEnc.state_Fxx[0].inputBuf, psEnc.state_Fxx[0].inputBufIx + 2, buf, 0, nSamplesFromInput); + psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer; + } + samplesIn_ptr += nSamplesFromInput * encControl.nChannelsAPI; + nSamplesIn -= nSamplesFromInput; + psEnc.allowBandwidthSwitch = 0; + if (psEnc.state_Fxx[0].inputBufIx < psEnc.state_Fxx[0].frame_length) break; + Inlines.OpusAssert(psEnc.state_Fxx[0].inputBufIx == psEnc.state_Fxx[0].frame_length); + Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inputBufIx == psEnc.state_Fxx[1].frame_length); + if (psEnc.state_Fxx[0].nFramesEncoded == 0 && prefillFlag == 0) { + short[] iCDF = new short[]{0, 0}; + iCDF[0] = (short)(256 - Inlines.silk_RSHIFT(256, (psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal)); + psRangeEnc.enc_icdf(0, iCDF, 8); + for (n = 0; n < encControl.nChannelsInternal; ++n) { + int LBRR_symbol = 0; + for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; ++i) { + LBRR_symbol |= Inlines.silk_LSHIFT(psEnc.state_Fxx[n].LBRR_flags[i], i); + } + psEnc.state_Fxx[n].LBRR_flag = (byte)(LBRR_symbol > 0 ? 1 : 0); + if (LBRR_symbol == 0 || psEnc.state_Fxx[n].nFramesPerPacket <= 1) continue; + psRangeEnc.enc_icdf(LBRR_symbol - 1, SilkTables.silk_LBRR_flags_iCDF_ptr[psEnc.state_Fxx[n].nFramesPerPacket - 2], 8); + } + for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; ++i) { + for (n = 0; n < encControl.nChannelsInternal; ++n) { + if (psEnc.state_Fxx[n].LBRR_flags[i] == 0) continue; + if (encControl.nChannelsInternal == 2 && n == 0) { + Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[i]); + if (psEnc.state_Fxx[1].LBRR_flags[i] == 0) { + Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[i]); + } + } + int condCoding = i > 0 && psEnc.state_Fxx[n].LBRR_flags[i - 1] != 0 ? 2 : 0; + EncodeIndices.silk_encode_indices(psEnc.state_Fxx[n], psRangeEnc, i, 1, condCoding); + EncodePulses.silk_encode_pulses(psRangeEnc, psEnc.state_Fxx[n].indices_LBRR[i].signalType, psEnc.state_Fxx[n].indices_LBRR[i].quantOffsetType, psEnc.state_Fxx[n].pulses_LBRR[i], psEnc.state_Fxx[n].frame_length); + } + } + for (n = 0; n < encControl.nChannelsInternal; ++n) { + Arrays.MemSet(psEnc.state_Fxx[n].LBRR_flags, 0, 3); + } + psEnc.nBitsUsedLBRR = psRangeEnc.tell(); + } + HPVariableCutoff.silk_HP_variable_cutoff(psEnc.state_Fxx); + int nBits = Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000); + if (prefillFlag == 0) { + nBits -= psEnc.nBitsUsedLBRR; + } + nBits = Inlines.silk_DIV32_16(nBits, psEnc.state_Fxx[0].nFramesPerPacket); + TargetRate_bps = encControl.payloadSize_ms == 10 ? Inlines.silk_SMULBB(nBits, 100) : Inlines.silk_SMULBB(nBits, 50); + TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(psEnc.nBitsExceeded, 1000), 500); + if (prefillFlag == 0 && psEnc.state_Fxx[0].nFramesEncoded > 0) { + int bitsBalance = psRangeEnc.tell() - psEnc.nBitsUsedLBRR - nBits * psEnc.state_Fxx[0].nFramesEncoded; + TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(bitsBalance, 1000), 500); + } + TargetRate_bps = Inlines.silk_LIMIT(TargetRate_bps, encControl.bitRate, 5000); + if (encControl.nChannelsInternal == 2) { + BoxedValueByte midOnlyFlagBoxed = new BoxedValueByte(psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]); + Stereo.silk_stereo_LR_to_MS(psEnc.sStereo, psEnc.state_Fxx[0].inputBuf, 2, psEnc.state_Fxx[1].inputBuf, 2, psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded], midOnlyFlagBoxed, MStargetRates_bps, TargetRate_bps, psEnc.state_Fxx[0].speech_activity_Q8, encControl.toMono, psEnc.state_Fxx[0].fs_kHz, psEnc.state_Fxx[0].frame_length); + psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded] = midOnlyFlagBoxed.Val; + if (midOnlyFlagBoxed.Val == 0) { + if (psEnc.prev_decode_only_middle == 1) { + psEnc.state_Fxx[1].sShape.Reset(); + psEnc.state_Fxx[1].sPrefilt.Reset(); + psEnc.state_Fxx[1].sNSQ.Reset(); + Arrays.MemSet(psEnc.state_Fxx[1].prev_NLSFq_Q15, (short)0, 16); + Arrays.MemSet(psEnc.state_Fxx[1].sLP.In_LP_State, 0, 2); + psEnc.state_Fxx[1].prevLag = 100; + psEnc.state_Fxx[1].sNSQ.lagPrev = 100; + psEnc.state_Fxx[1].sShape.LastGainIndex = (byte)10; + psEnc.state_Fxx[1].prevSignalType = 0; + psEnc.state_Fxx[1].sNSQ.prev_gain_Q16 = 65536; + psEnc.state_Fxx[1].first_frame_after_reset = 1; + } + psEnc.state_Fxx[1].silk_encode_do_VAD(); + } else { + psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] = 0; + } + if (prefillFlag == 0) { + Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded]); + if (psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] == 0) { + Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]); + } + } + } else { + System.arraycopy(psEnc.sStereo.sMid, 0, psEnc.state_Fxx[0].inputBuf, 0, 2); + System.arraycopy(psEnc.state_Fxx[0].inputBuf, psEnc.state_Fxx[0].frame_length, psEnc.sStereo.sMid, 0, 2); + } + psEnc.state_Fxx[0].silk_encode_do_VAD(); + for (n = 0; n < encControl.nChannelsInternal; ++n) { + int channelRate_bps; + int useCBR; + int maxBits = encControl.maxBits; + if (tot_blocks == 2 && curr_block == 0) { + maxBits = maxBits * 3 / 5; + } else if (tot_blocks == 3) { + if (curr_block == 0) { + maxBits = maxBits * 2 / 5; + } else if (curr_block == 1) { + maxBits = maxBits * 3 / 4; + } + } + int n3 = useCBR = encControl.useCBR != 0 && curr_block == tot_blocks - 1 ? 1 : 0; + if (encControl.nChannelsInternal == 1) { + channelRate_bps = TargetRate_bps; + } else { + channelRate_bps = MStargetRates_bps[n]; + if (n == 0 && MStargetRates_bps[1] > 0) { + useCBR = 0; + maxBits -= encControl.maxBits / (tot_blocks * 2); + } + } + if (channelRate_bps > 0) { + psEnc.state_Fxx[n].silk_control_SNR(channelRate_bps); + int condCoding = psEnc.state_Fxx[0].nFramesEncoded - n <= 0 ? 0 : (n > 0 && psEnc.prev_decode_only_middle != 0 ? 1 : 2); + Inlines.OpusAssert((ret += psEnc.state_Fxx[n].silk_encode_frame(nBytesOut, psRangeEnc, condCoding, maxBits, useCBR)) == SilkError.SILK_NO_ERROR); + } + psEnc.state_Fxx[n].controlled_since_last_payload = 0; + psEnc.state_Fxx[n].inputBufIx = 0; + ++psEnc.state_Fxx[n].nFramesEncoded; + } + psEnc.prev_decode_only_middle = psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded - 1]; + if (nBytesOut.Val > 0 && psEnc.state_Fxx[0].nFramesEncoded == psEnc.state_Fxx[0].nFramesPerPacket) { + int flags = 0; + for (n = 0; n < encControl.nChannelsInternal; ++n) { + for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; ++i) { + flags = Inlines.silk_LSHIFT(flags, 1); + flags |= psEnc.state_Fxx[n].VAD_flags[i]; + } + flags = Inlines.silk_LSHIFT(flags, 1); + flags |= psEnc.state_Fxx[n].LBRR_flag; + } + if (prefillFlag == 0) { + psRangeEnc.enc_patch_initial_bits(flags, (psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal); + } + if (psEnc.state_Fxx[0].inDTX != 0 && (encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inDTX != 0)) { + nBytesOut.Val = 0; + } + psEnc.nBitsExceeded += nBytesOut.Val * 8; + psEnc.nBitsExceeded -= Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000); + psEnc.nBitsExceeded = Inlines.silk_LIMIT(psEnc.nBitsExceeded, 0, 10000); + int speech_act_thr_for_switch_Q8 = Inlines.silk_SMLAWB(13, 3188, psEnc.timeSinceSwitchAllowed_ms); + if (psEnc.state_Fxx[0].speech_activity_Q8 < speech_act_thr_for_switch_Q8) { + psEnc.allowBandwidthSwitch = 1; + psEnc.timeSinceSwitchAllowed_ms = 0; + } else { + psEnc.allowBandwidthSwitch = 0; + psEnc.timeSinceSwitchAllowed_ms += encControl.payloadSize_ms; + } + } + if (nSamplesIn == 0) break; + ++curr_block; + } + psEnc.nPrevChannelsInternal = encControl.nChannelsInternal; + encControl.allowBandwidthSwitch = psEnc.allowBandwidthSwitch; + encControl.inWBmodeWithoutVariableLP = psEnc.state_Fxx[0].fs_kHz == 16 && psEnc.state_Fxx[0].sLP.mode == 0 ? 1 : 0; + encControl.internalSampleRate = Inlines.silk_SMULBB(psEnc.state_Fxx[0].fs_kHz, 1000); + int n4 = encControl.stereoWidth_Q14 = encControl.toMono != 0 ? 0 : (int)psEnc.sStereo.smth_width_Q14; + if (prefillFlag != 0) { + encControl.payloadSize_ms = tmp_payloadSize_ms; + encControl.complexity = tmp_complexity; + for (n = 0; n < encControl.nChannelsInternal; ++n) { + psEnc.state_Fxx[n].controlled_since_last_payload = 0; + psEnc.state_Fxx[n].prefillFlag = 0; + } + } + return ret; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/EncodeIndices.java b/source/decompiled/de/maxhenkel/voicechat/concentus/EncodeIndices.java new file mode 100644 index 0000000..bed616b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/EncodeIndices.java @@ -0,0 +1,104 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.NLSF; +import de.maxhenkel.voicechat.concentus.SideInfoIndices; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class EncodeIndices { + EncodeIndices() { + } + + static void silk_encode_indices(SilkChannelEncoder psEncC, EntropyCoder psRangeEnc, int FrameIndex, int encode_LBRR, int condCoding) { + int i; + short[] ec_ix = new short[16]; + short[] pred_Q8 = new short[16]; + SideInfoIndices psIndices = encode_LBRR != 0 ? psEncC.indices_LBRR[FrameIndex] : psEncC.indices; + int typeOffset = 2 * psIndices.signalType + psIndices.quantOffsetType; + Inlines.OpusAssert(typeOffset >= 0 && typeOffset < 6); + Inlines.OpusAssert(encode_LBRR == 0 || typeOffset >= 2); + if (encode_LBRR != 0 || typeOffset >= 2) { + psRangeEnc.enc_icdf(typeOffset - 2, SilkTables.silk_type_offset_VAD_iCDF, 8); + } else { + psRangeEnc.enc_icdf(typeOffset, SilkTables.silk_type_offset_no_VAD_iCDF, 8); + } + if (condCoding == 2) { + Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < 41); + psRangeEnc.enc_icdf(psIndices.GainsIndices[0], SilkTables.silk_delta_gain_iCDF, 8); + } else { + Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < 64); + psRangeEnc.enc_icdf(Inlines.silk_RSHIFT(psIndices.GainsIndices[0], 3), SilkTables.silk_gain_iCDF[psIndices.signalType], 8); + psRangeEnc.enc_icdf(psIndices.GainsIndices[0] & 7, SilkTables.silk_uniform8_iCDF, 8); + } + for (i = 1; i < psEncC.nb_subfr; ++i) { + Inlines.OpusAssert(psIndices.GainsIndices[i] >= 0 && psIndices.GainsIndices[i] < 41); + psRangeEnc.enc_icdf(psIndices.GainsIndices[i], SilkTables.silk_delta_gain_iCDF, 8); + } + psRangeEnc.enc_icdf(psIndices.NLSFIndices[0], psEncC.psNLSF_CB.CB1_iCDF, (psIndices.signalType >> 1) * psEncC.psNLSF_CB.nVectors, 8); + NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psEncC.psNLSF_CB, psIndices.NLSFIndices[0]); + Inlines.OpusAssert(psEncC.psNLSF_CB.order == psEncC.predictLPCOrder); + for (i = 0; i < psEncC.psNLSF_CB.order; ++i) { + if (psIndices.NLSFIndices[i + 1] >= 4) { + psRangeEnc.enc_icdf(8, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8); + psRangeEnc.enc_icdf(psIndices.NLSFIndices[i + 1] - 4, SilkTables.silk_NLSF_EXT_iCDF, 8); + continue; + } + if (psIndices.NLSFIndices[i + 1] <= -4) { + psRangeEnc.enc_icdf(0, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8); + psRangeEnc.enc_icdf(-psIndices.NLSFIndices[i + 1] - 4, SilkTables.silk_NLSF_EXT_iCDF, 8); + continue; + } + psRangeEnc.enc_icdf(psIndices.NLSFIndices[i + 1] + 4, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8); + } + if (psEncC.nb_subfr == 4) { + Inlines.OpusAssert(psIndices.NLSFInterpCoef_Q2 >= 0 && psIndices.NLSFInterpCoef_Q2 < 5); + psRangeEnc.enc_icdf(psIndices.NLSFInterpCoef_Q2, SilkTables.silk_NLSF_interpolation_factor_iCDF, 8); + } + if (psIndices.signalType == 2) { + boolean encode_absolute_lagIndex = true; + if (condCoding == 2 && psEncC.ec_prevSignalType == 2) { + int delta_lagIndex = psIndices.lagIndex - psEncC.ec_prevLagIndex; + if (delta_lagIndex < -8 || delta_lagIndex > 11) { + delta_lagIndex = 0; + } else { + delta_lagIndex += 9; + encode_absolute_lagIndex = false; + } + Inlines.OpusAssert(delta_lagIndex >= 0 && delta_lagIndex < 21); + psRangeEnc.enc_icdf(delta_lagIndex, SilkTables.silk_pitch_delta_iCDF, 8); + } + if (encode_absolute_lagIndex) { + int pitch_high_bits = Inlines.silk_DIV32_16(psIndices.lagIndex, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1)); + int pitch_low_bits = psIndices.lagIndex - Inlines.silk_SMULBB(pitch_high_bits, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1)); + Inlines.OpusAssert(pitch_low_bits < psEncC.fs_kHz / 2); + Inlines.OpusAssert(pitch_high_bits < 32); + psRangeEnc.enc_icdf(pitch_high_bits, SilkTables.silk_pitch_lag_iCDF, 8); + psRangeEnc.enc_icdf(pitch_low_bits, psEncC.pitch_lag_low_bits_iCDF, 8); + } + psEncC.ec_prevLagIndex = psIndices.lagIndex; + Inlines.OpusAssert(psIndices.contourIndex >= 0); + Inlines.OpusAssert(psIndices.contourIndex < 34 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 4 || psIndices.contourIndex < 11 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 4 || psIndices.contourIndex < 12 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 2 || psIndices.contourIndex < 3 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 2); + psRangeEnc.enc_icdf(psIndices.contourIndex, psEncC.pitch_contour_iCDF, 8); + Inlines.OpusAssert(psIndices.PERIndex >= 0 && psIndices.PERIndex < 3); + psRangeEnc.enc_icdf(psIndices.PERIndex, SilkTables.silk_LTP_per_index_iCDF, 8); + for (int k = 0; k < psEncC.nb_subfr; ++k) { + Inlines.OpusAssert(psIndices.LTPIndex[k] >= 0 && psIndices.LTPIndex[k] < 8 << psIndices.PERIndex); + psRangeEnc.enc_icdf(psIndices.LTPIndex[k], SilkTables.silk_LTP_gain_iCDF_ptrs[psIndices.PERIndex], 8); + } + if (condCoding == 0) { + Inlines.OpusAssert(psIndices.LTP_scaleIndex >= 0 && psIndices.LTP_scaleIndex < 3); + psRangeEnc.enc_icdf(psIndices.LTP_scaleIndex, SilkTables.silk_LTPscale_iCDF, 8); + } + Inlines.OpusAssert(condCoding == 0 || psIndices.LTP_scaleIndex == 0); + } + psEncC.ec_prevSignalType = psIndices.signalType; + Inlines.OpusAssert(psIndices.Seed >= 0 && psIndices.Seed < 4); + psRangeEnc.enc_icdf(psIndices.Seed, SilkTables.silk_uniform4_iCDF, 8); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/EncodePulses.java b/source/decompiled/de/maxhenkel/voicechat/concentus/EncodePulses.java new file mode 100644 index 0000000..66d6c61 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/EncodePulses.java @@ -0,0 +1,132 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.CodeSigns; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.ShellCoder; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class EncodePulses { + EncodePulses() { + } + + static int combine_and_check(int[] pulses_comb, int pulses_comb_ptr, int[] pulses_in, int pulses_in_ptr, int max_pulses, int len) { + for (int k = 0; k < len; ++k) { + int k2p = 2 * k + pulses_in_ptr; + int sum = pulses_in[k2p] + pulses_in[k2p + 1]; + if (sum > max_pulses) { + return 1; + } + pulses_comb[pulses_comb_ptr + k] = sum; + } + return 0; + } + + static int combine_and_check(int[] pulses_comb, int[] pulses_in, int max_pulses, int len) { + for (int k = 0; k < len; ++k) { + int sum = pulses_in[2 * k] + pulses_in[2 * k + 1]; + if (sum > max_pulses) { + return 1; + } + pulses_comb[k] = sum; + } + return 0; + } + + static void silk_encode_pulses(EntropyCoder psRangeEnc, int signalType, int quantOffsetType, byte[] pulses, int frame_length) { + int k; + int i; + int RateLevelIndex = 0; + int[] pulses_comb = new int[8]; + Arrays.MemSet(pulses_comb, 0, 8); + Inlines.OpusAssert(true); + int iter = Inlines.silk_RSHIFT(frame_length, 4); + if (iter * 16 < frame_length) { + Inlines.OpusAssert(frame_length == 120); + ++iter; + Arrays.MemSetWithOffset(pulses, (byte)0, frame_length, 16); + } + int[] abs_pulses = new int[iter * 16]; + Inlines.OpusAssert(true); + for (i = 0; i < iter * 16; i += 4) { + abs_pulses[i + 0] = Inlines.silk_abs(pulses[i + 0]); + abs_pulses[i + 1] = Inlines.silk_abs(pulses[i + 1]); + abs_pulses[i + 2] = Inlines.silk_abs(pulses[i + 2]); + abs_pulses[i + 3] = Inlines.silk_abs(pulses[i + 3]); + } + int[] sum_pulses = new int[iter]; + int[] nRshifts = new int[iter]; + int abs_pulses_ptr = 0; + for (i = 0; i < iter; ++i) { + nRshifts[i] = 0; + block2: while (true) { + int scale_down = EncodePulses.combine_and_check(pulses_comb, 0, abs_pulses, abs_pulses_ptr, SilkTables.silk_max_pulses_table[0], 8); + scale_down += EncodePulses.combine_and_check(pulses_comb, pulses_comb, SilkTables.silk_max_pulses_table[1], 4); + scale_down += EncodePulses.combine_and_check(pulses_comb, pulses_comb, SilkTables.silk_max_pulses_table[2], 2); + if ((scale_down += EncodePulses.combine_and_check(sum_pulses, i, pulses_comb, 0, SilkTables.silk_max_pulses_table[3], 1)) == 0) break; + int n = i; + nRshifts[n] = nRshifts[n] + 1; + k = abs_pulses_ptr; + while (true) { + if (k >= abs_pulses_ptr + 16) continue block2; + abs_pulses[k] = Inlines.silk_RSHIFT(abs_pulses[k], 1); + ++k; + } + break; + } + abs_pulses_ptr += 16; + } + int minSumBits_Q5 = Integer.MAX_VALUE; + for (k = 0; k < 9; ++k) { + short[] nBits_ptr = SilkTables.silk_pulses_per_block_BITS_Q5[k]; + int sumBits_Q5 = SilkTables.silk_rate_levels_BITS_Q5[signalType >> 1][k]; + for (i = 0; i < iter; ++i) { + if (nRshifts[i] > 0) { + sumBits_Q5 += nBits_ptr[17]; + continue; + } + sumBits_Q5 += nBits_ptr[sum_pulses[i]]; + } + if (sumBits_Q5 >= minSumBits_Q5) continue; + minSumBits_Q5 = sumBits_Q5; + RateLevelIndex = k; + } + psRangeEnc.enc_icdf(RateLevelIndex, SilkTables.silk_rate_levels_iCDF[signalType >> 1], 8); + for (i = 0; i < iter; ++i) { + if (nRshifts[i] == 0) { + psRangeEnc.enc_icdf(sum_pulses[i], SilkTables.silk_pulses_per_block_iCDF[RateLevelIndex], 8); + continue; + } + psRangeEnc.enc_icdf(17, SilkTables.silk_pulses_per_block_iCDF[RateLevelIndex], 8); + for (k = 0; k < nRshifts[i] - 1; ++k) { + psRangeEnc.enc_icdf(17, SilkTables.silk_pulses_per_block_iCDF[9], 8); + } + psRangeEnc.enc_icdf(sum_pulses[i], SilkTables.silk_pulses_per_block_iCDF[9], 8); + } + for (i = 0; i < iter; ++i) { + if (sum_pulses[i] <= 0) continue; + ShellCoder.silk_shell_encoder(psRangeEnc, abs_pulses, i * 16); + } + for (i = 0; i < iter; ++i) { + if (nRshifts[i] <= 0) continue; + int pulses_ptr = i * 16; + int nLS = nRshifts[i] - 1; + for (k = 0; k < 16; ++k) { + int bit; + byte abs_q = (byte)Inlines.silk_abs(pulses[pulses_ptr + k]); + for (int j = nLS; j > 0; --j) { + bit = Inlines.silk_RSHIFT(abs_q, j) & 1; + psRangeEnc.enc_icdf(bit, SilkTables.silk_lsb_iCDF, 8); + } + bit = abs_q & 1; + psRangeEnc.enc_icdf(bit, SilkTables.silk_lsb_iCDF, 8); + } + } + CodeSigns.silk_encode_signs(psRangeEnc, pulses, frame_length, signalType, quantOffsetType, sum_pulses); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/EntropyCoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/EntropyCoder.java new file mode 100644 index 0000000..ae07e66 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/EntropyCoder.java @@ -0,0 +1,451 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Inlines; + +class EntropyCoder { + private final int EC_WINDOW_SIZE = 32; + private final int EC_UINT_BITS = 8; + static final int BITRES = 3; + private final int EC_SYM_BITS = 8; + private final int EC_CODE_BITS = 32; + private final long EC_SYM_MAX = 255L; + private final int EC_CODE_SHIFT = 23; + private final long EC_CODE_TOP = 0x80000000L; + private final long EC_CODE_BOT = 0x800000L; + private final int EC_CODE_EXTRA = 7; + private byte[] buf; + private int buf_ptr; + int storage; + int end_offs; + long end_window; + int nend_bits; + int nbits_total; + int offs; + long rng; + long val; + long ext; + int rem; + int error; + private static final int[] correction = new int[]{35733, 38967, 42495, 46340, 50535, 55109, 60097, 65535}; + + EntropyCoder() { + this.Reset(); + } + + void Reset() { + this.buf = null; + this.buf_ptr = 0; + this.storage = 0; + this.end_offs = 0; + this.end_window = 0L; + this.nend_bits = 0; + this.offs = 0; + this.rng = 0L; + this.val = 0L; + this.ext = 0L; + this.rem = 0; + this.error = 0; + } + + void Assign(EntropyCoder other) { + this.buf = other.buf; + this.buf_ptr = other.buf_ptr; + this.storage = other.storage; + this.end_offs = other.end_offs; + this.end_window = other.end_window; + this.nend_bits = other.nend_bits; + this.nbits_total = other.nbits_total; + this.offs = other.offs; + this.rng = other.rng; + this.val = other.val; + this.ext = other.ext; + this.rem = other.rem; + this.error = other.error; + } + + byte[] get_buffer() { + byte[] convertedBuf = new byte[this.storage]; + System.arraycopy(this.buf, this.buf_ptr, convertedBuf, 0, this.storage); + return convertedBuf; + } + + void write_buffer(byte[] data, int data_ptr, int target_offset, int size) { + System.arraycopy(data, data_ptr, this.buf, this.buf_ptr + target_offset, size); + } + + int read_byte() { + return this.offs < this.storage ? Inlines.SignedByteToUnsignedInt(this.buf[this.buf_ptr + this.offs++]) : 0; + } + + int read_byte_from_end() { + return this.end_offs < this.storage ? Inlines.SignedByteToUnsignedInt(this.buf[this.buf_ptr + (this.storage - ++this.end_offs)]) : 0; + } + + int write_byte(long _value) { + if (this.offs + this.end_offs >= this.storage) { + return -1; + } + this.buf[this.buf_ptr + this.offs++] = (byte)(_value & 0xFFL); + return 0; + } + + int write_byte_at_end(long _value) { + if (this.offs + this.end_offs >= this.storage) { + return -1; + } + this.buf[this.buf_ptr + (this.storage - ++this.end_offs)] = (byte)(_value & 0xFFL); + return 0; + } + + void dec_normalize() { + while (this.rng <= 0x800000L) { + this.nbits_total += 8; + this.rng = Inlines.CapToUInt32(this.rng << 8); + int sym = this.rem; + this.rem = this.read_byte(); + sym = (sym << 8 | this.rem) >> 1; + this.val = Inlines.CapToUInt32((this.val << 8) + (0xFFL & (long)(~sym)) & Integer.MAX_VALUE); + } + } + + void dec_init(byte[] _buf, int _buf_ptr, int _storage) { + this.buf = _buf; + this.buf_ptr = _buf_ptr; + this.storage = _storage; + this.end_offs = 0; + this.end_window = 0L; + this.nend_bits = 0; + this.nbits_total = 9; + this.offs = 0; + this.rng = 128L; + this.rem = this.read_byte(); + this.val = Inlines.CapToUInt32(this.rng - 1L - (long)(this.rem >> 1)); + this.error = 0; + this.dec_normalize(); + } + + long decode(long _ft) { + _ft = Inlines.CapToUInt32(_ft); + this.ext = Inlines.CapToUInt32(this.rng / _ft); + long s = Inlines.CapToUInt32(this.val / this.ext); + return Inlines.CapToUInt32(_ft - Inlines.EC_MINI(Inlines.CapToUInt32(s + 1L), _ft)); + } + + long decode_bin(int _bits) { + this.ext = this.rng >> _bits; + long s = Inlines.CapToUInt32(this.val / this.ext); + return Inlines.CapToUInt32(Inlines.CapToUInt32(1L << _bits) - Inlines.EC_MINI(Inlines.CapToUInt32(s + 1L), 1L << _bits)); + } + + void dec_update(long _fl, long _fh, long _ft) { + _fl = Inlines.CapToUInt32(_fl); + _fh = Inlines.CapToUInt32(_fh); + _ft = Inlines.CapToUInt32(_ft); + long s = Inlines.CapToUInt32(this.ext * (_ft - _fh)); + this.val -= s; + this.rng = _fl > 0L ? Inlines.CapToUInt32(this.ext * (_fh - _fl)) : this.rng - s; + this.dec_normalize(); + } + + int dec_bit_logp(long _logp) { + int ret; + long d = this.val; + long r = this.rng; + long s = r >> (int)_logp; + int n = ret = d < s ? 1 : 0; + if (ret == 0) { + this.val = Inlines.CapToUInt32(d - s); + } + this.rng = ret != 0 ? s : r - s; + this.dec_normalize(); + return ret; + } + + int dec_icdf(short[] _icdf, int _ftb) { + long t; + long s = this.rng; + long d = this.val; + long r = s >> _ftb; + int ret = -1; + do { + t = s; + } while (d < (s = Inlines.CapToUInt32(r * (long)_icdf[++ret]))); + this.val = Inlines.CapToUInt32(d - s); + this.rng = Inlines.CapToUInt32(t - s); + this.dec_normalize(); + return ret; + } + + int dec_icdf(short[] _icdf, int _icdf_offset, int _ftb) { + long t; + long s = this.rng; + long d = this.val; + long r = s >> _ftb; + int ret = _icdf_offset - 1; + do { + t = s; + } while (d < (s = Inlines.CapToUInt32(r * (long)_icdf[++ret]))); + this.val = Inlines.CapToUInt32(d - s); + this.rng = Inlines.CapToUInt32(t - s); + this.dec_normalize(); + return ret - _icdf_offset; + } + + long dec_uint(long _ft) { + Inlines.OpusAssert((_ft = Inlines.CapToUInt32(_ft)) > 1L); + int ftb = Inlines.EC_ILOG(--_ft); + if (ftb > 8) { + long ft = Inlines.CapToUInt32((_ft >> (ftb -= 8)) + 1L); + long s = Inlines.CapToUInt32(this.decode(ft)); + this.dec_update(s, s + 1L, ft); + long t = Inlines.CapToUInt32(s << ftb | (long)this.dec_bits(ftb)); + if (t <= _ft) { + return t; + } + this.error = 1; + return _ft; + } + long s = Inlines.CapToUInt32(this.decode(++_ft)); + this.dec_update(s, s + 1L, _ft); + return s; + } + + int dec_bits(int _bits) { + long window = this.end_window; + int available = this.nend_bits; + if (available < _bits) { + do { + window = Inlines.CapToUInt32(window | (long)(this.read_byte_from_end() << available)); + } while ((available += 8) <= 24); + } + int ret = (int)(0xFFFFFFFFFFFFFFFFL & (window & (long)((1 << _bits) - 1))); + this.end_window = Inlines.CapToUInt32(window >>= _bits); + this.nend_bits = available -= _bits; + this.nbits_total += _bits; + return ret; + } + + void enc_carry_out(int _c) { + if ((long)_c != 255L) { + int carry = _c >> 8; + if (this.rem >= 0) { + this.error |= this.write_byte(Inlines.CapToUInt32(this.rem + carry)); + } + if (this.ext > 0L) { + long sym = 255L + (long)carry & 0xFFL; + do { + this.error |= this.write_byte(sym); + } while (--this.ext > 0L); + } + this.rem = (int)((long)_c & 0xFFL); + } else { + ++this.ext; + } + } + + void enc_normalize() { + while (this.rng <= 0x800000L) { + this.enc_carry_out((int)(this.val >> 23)); + this.val = Inlines.CapToUInt32(this.val << 8 & Integer.MAX_VALUE); + this.rng = Inlines.CapToUInt32(this.rng << 8); + this.nbits_total += 8; + } + } + + void enc_init(byte[] _buf, int buf_ptr, int _size) { + this.buf = _buf; + this.buf_ptr = buf_ptr; + this.end_offs = 0; + this.end_window = 0L; + this.nend_bits = 0; + this.nbits_total = 33; + this.offs = 0; + this.rng = Inlines.CapToUInt32(0x80000000L); + this.rem = -1; + this.val = 0L; + this.ext = 0L; + this.storage = _size; + this.error = 0; + } + + void encode(long _fl, long _fh, long _ft) { + _fl = Inlines.CapToUInt32(_fl); + _fh = Inlines.CapToUInt32(_fh); + _ft = Inlines.CapToUInt32(_ft); + long r = Inlines.CapToUInt32(this.rng / _ft); + if (_fl > 0L) { + this.val += Inlines.CapToUInt32(this.rng - r * (_ft - _fl)); + this.rng = Inlines.CapToUInt32(r * (_fh - _fl)); + } else { + this.rng = Inlines.CapToUInt32(this.rng - r * (_ft - _fh)); + } + this.enc_normalize(); + } + + void encode_bin(long _fl, long _fh, int _bits) { + _fl = Inlines.CapToUInt32(_fl); + _fh = Inlines.CapToUInt32(_fh); + long r = Inlines.CapToUInt32(this.rng >> _bits); + if (_fl > 0L) { + this.val = Inlines.CapToUInt32(this.val + Inlines.CapToUInt32(this.rng - r * ((long)(1 << _bits) - _fl))); + this.rng = Inlines.CapToUInt32(r * (_fh - _fl)); + } else { + this.rng = Inlines.CapToUInt32(this.rng - r * ((long)(1 << _bits) - _fh)); + } + this.enc_normalize(); + } + + void enc_bit_logp(int _val, int _logp) { + long r = this.rng; + long l = this.val; + long s = r >> _logp; + r -= s; + if (_val != 0) { + this.val = Inlines.CapToUInt32(l + r); + } + this.rng = _val != 0 ? s : r; + this.enc_normalize(); + } + + void enc_icdf(int _s, short[] _icdf, int _ftb) { + long r = Inlines.CapToUInt32(this.rng >> _ftb); + if (_s > 0) { + this.val += Inlines.CapToUInt32(this.rng - Inlines.CapToUInt32(r * (long)_icdf[_s - 1])); + this.rng = r * Inlines.CapToUInt32(_icdf[_s - 1] - _icdf[_s]); + } else { + this.rng = Inlines.CapToUInt32(this.rng - r * (long)_icdf[_s]); + } + this.enc_normalize(); + } + + void enc_icdf(int _s, short[] _icdf, int icdf_ptr, int _ftb) { + long r = Inlines.CapToUInt32(this.rng >> _ftb); + if (_s > 0) { + this.val += Inlines.CapToUInt32(this.rng - Inlines.CapToUInt32(r * (long)_icdf[icdf_ptr + _s - 1])); + this.rng = Inlines.CapToUInt32(r * Inlines.CapToUInt32(_icdf[icdf_ptr + _s - 1] - _icdf[icdf_ptr + _s])); + } else { + this.rng = Inlines.CapToUInt32(this.rng - r * (long)_icdf[icdf_ptr + _s]); + } + this.enc_normalize(); + } + + void enc_uint(long _fl, long _ft) { + _fl = Inlines.CapToUInt32(_fl); + Inlines.OpusAssert((_ft = Inlines.CapToUInt32(_ft)) > 1L); + int ftb = Inlines.EC_ILOG(--_ft); + if (ftb > 8) { + long ft = Inlines.CapToUInt32((_ft >> (ftb -= 8)) + 1L); + long fl = Inlines.CapToUInt32(_fl >> ftb); + this.encode(fl, fl + 1L, ft); + this.enc_bits(_fl & Inlines.CapToUInt32((1 << ftb) - 1), ftb); + } else { + this.encode(_fl, _fl + 1L, _ft + 1L); + } + } + + void enc_bits(long _fl, int _bits) { + _fl = Inlines.CapToUInt32(_fl); + long window = this.end_window; + int used = this.nend_bits; + Inlines.OpusAssert(_bits > 0); + if (used + _bits > 32) { + do { + this.error |= this.write_byte_at_end(window & 0xFFL); + window >>= 8; + } while ((used -= 8) >= 8); + } + window |= Inlines.CapToUInt32(_fl << used); + this.end_window = window; + this.nend_bits = used += _bits; + this.nbits_total += _bits; + } + + void enc_patch_initial_bits(long _val, int _nbits) { + Inlines.OpusAssert(_nbits <= 8); + int shift = 8 - _nbits; + long mask = (1 << _nbits) - 1 << shift; + if (this.offs > 0) { + this.buf[this.buf_ptr] = (byte)((long)this.buf[this.buf_ptr] & (mask ^ 0xFFFFFFFFFFFFFFFFL) | Inlines.CapToUInt32(_val << shift)); + } else if (this.rem >= 0) { + this.rem = (int)Inlines.CapToUInt32(Inlines.CapToUInt32((long)this.rem & (mask ^ 0xFFFFFFFFFFFFFFFFL) | _val) << shift); + } else if (this.rng <= 0x80000000L >> _nbits) { + this.val = Inlines.CapToUInt32(this.val & (mask << 23 ^ 0xFFFFFFFFFFFFFFFFL) | Inlines.CapToUInt32(Inlines.CapToUInt32(_val) << 23 + shift)); + } else { + this.error = -1; + } + } + + void enc_shrink(int _size) { + Inlines.OpusAssert(this.offs + this.end_offs <= _size); + Arrays.MemMove(this.buf, this.buf_ptr + _size - this.end_offs, this.buf_ptr + this.storage - this.end_offs, this.end_offs); + this.storage = _size; + } + + int range_bytes() { + return this.offs; + } + + int get_error() { + return this.error; + } + + int tell() { + int returnVal = this.nbits_total - Inlines.EC_ILOG(this.rng); + return returnVal; + } + + int tell_frac() { + long b; + int nbits = this.nbits_total << 3; + int l = Inlines.EC_ILOG(this.rng); + int r = (int)(this.rng >> l - 16); + b = Inlines.CapToUInt32(b + (long)(r > correction[(int)(b = Inlines.CapToUInt32((r >> 12) - 8))] ? 1 : 0)); + l = (int)((long)(l << 3) + b); + return nbits - l; + } + + void enc_done() { + int used; + int l = 32 - Inlines.EC_ILOG(this.rng); + long msk = Inlines.CapToUInt32(Integer.MAX_VALUE >>> l); + long end = Inlines.CapToUInt32(Inlines.CapToUInt32(this.val + msk) & (msk ^ 0xFFFFFFFFFFFFFFFFL)); + if ((end | msk) >= this.val + this.rng) { + ++l; + end = Inlines.CapToUInt32(Inlines.CapToUInt32(this.val + (msk >>= 1)) & (msk ^ 0xFFFFFFFFFFFFFFFFL)); + } + while (l > 0) { + this.enc_carry_out((int)(end >> 23)); + end = Inlines.CapToUInt32(end << 8 & Integer.MAX_VALUE); + l -= 8; + } + if (this.rem >= 0 || this.ext > 0L) { + this.enc_carry_out(0); + } + long window = this.end_window; + for (used = this.nend_bits; used >= 8; used -= 8) { + this.error |= this.write_byte_at_end(window & 0xFFL); + window >>= 8; + } + if (this.error == 0) { + Arrays.MemSetWithOffset(this.buf, (byte)0, this.buf_ptr + this.offs, this.storage - this.offs - this.end_offs); + if (used > 0) { + if (this.end_offs >= this.storage) { + this.error = -1; + } else { + l = -l; + if (this.offs + this.end_offs >= this.storage && l < used) { + window = Inlines.CapToUInt32(window & (long)((1 << l) - 1)); + this.error = -1; + } + int z = this.buf_ptr + this.storage - this.end_offs - 1; + this.buf[z] = (byte)(this.buf[z] | (byte)(window & 0xFFL)); + } + } + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/FFTState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/FFTState.java new file mode 100644 index 0000000..f9f6330 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/FFTState.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class FFTState { + int nfft = 0; + short scale = 0; + int scale_shift = 0; + int shift = 0; + short[] factors = new short[16]; + short[] bitrev = null; + short[] twiddles = null; + + FFTState() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Filters.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Filters.java new file mode 100644 index 0000000..6742f15 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Filters.java @@ -0,0 +1,274 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Kernels; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkEncoderControl; +import de.maxhenkel.voicechat.concentus.SilkPrefilterState; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class Filters { + private static final short A_fb1_20 = 10788; + private static final short A_fb1_21 = -24290; + private static final int QA = 24; + private static final int A_LIMIT = 0xFFEF9E; + + Filters() { + } + + static void silk_warped_LPC_analysis_filter(int[] state, int[] res_Q2, short[] coef_Q13, int coef_Q13_ptr, short[] input, int input_ptr, short lambda_Q16, int length, int order) { + Inlines.OpusAssert((order & 1) == 0); + for (int n = 0; n < length; ++n) { + int tmp2 = Inlines.silk_SMLAWB(state[0], state[1], lambda_Q16); + state[0] = Inlines.silk_LSHIFT(input[input_ptr + n], 14); + int tmp1 = Inlines.silk_SMLAWB(state[1], state[2] - tmp2, lambda_Q16); + state[1] = tmp2; + int acc_Q11 = Inlines.silk_RSHIFT(order, 1); + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr]); + for (int i = 2; i < order; i += 2) { + tmp2 = Inlines.silk_SMLAWB(state[i], state[i + 1] - tmp1, lambda_Q16); + state[i] = tmp1; + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + i - 1]); + tmp1 = Inlines.silk_SMLAWB(state[i + 1], state[i + 2] - tmp2, lambda_Q16); + state[i + 1] = tmp2; + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr + i]); + } + state[order] = tmp1; + acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + order - 1]); + res_Q2[n] = Inlines.silk_LSHIFT(input[input_ptr + n], 2) - Inlines.silk_RSHIFT_ROUND(acc_Q11, 9); + } + } + + static void silk_prefilter(SilkChannelEncoder psEnc, SilkEncoderControl psEncCtrl, int[] xw_Q3, short[] x, int x_ptr) { + SilkPrefilterState P = psEnc.sPrefilt; + short[] B_Q10 = new short[2]; + int px = x_ptr; + int pxw_Q3 = 0; + int lag = P.lagPrev; + int[] x_filt_Q12 = new int[psEnc.subfr_length]; + int[] st_res_Q2 = new int[psEnc.subfr_length]; + for (int k = 0; k < psEnc.nb_subfr; ++k) { + int HarmShapeGain_Q12; + if (psEnc.indices.signalType == 2) { + lag = psEncCtrl.pitchL[k]; + } + Inlines.OpusAssert((HarmShapeGain_Q12 = Inlines.silk_SMULWB(psEncCtrl.HarmShapeGain_Q14[k], 16384 - psEncCtrl.HarmBoost_Q14[k])) >= 0); + int HarmShapeFIRPacked_Q12 = Inlines.silk_RSHIFT(HarmShapeGain_Q12, 2); + HarmShapeFIRPacked_Q12 |= Inlines.silk_LSHIFT(Inlines.silk_RSHIFT(HarmShapeGain_Q12, 1), 16); + int Tilt_Q14 = psEncCtrl.Tilt_Q14[k]; + int LF_shp_Q14 = psEncCtrl.LF_shp_Q14[k]; + int AR1_shp_Q13 = k * 16; + Filters.silk_warped_LPC_analysis_filter(P.sAR_shp, st_res_Q2, psEncCtrl.AR1_Q13, AR1_shp_Q13, x, px, (short)psEnc.warping_Q16, psEnc.subfr_length, psEnc.shapingLPCOrder); + B_Q10[0] = (short)Inlines.silk_RSHIFT_ROUND(psEncCtrl.GainsPre_Q14[k], 4); + int tmp_32 = Inlines.silk_SMLABB(0x333333, psEncCtrl.HarmBoost_Q14[k], HarmShapeGain_Q12); + tmp_32 = Inlines.silk_SMLABB(tmp_32, psEncCtrl.coding_quality_Q14, 410); + tmp_32 = Inlines.silk_SMULWB(tmp_32, -psEncCtrl.GainsPre_Q14[k]); + tmp_32 = Inlines.silk_RSHIFT_ROUND(tmp_32, 14); + B_Q10[1] = (short)Inlines.silk_SAT16(tmp_32); + x_filt_Q12[0] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[0], B_Q10[0]), P.sHarmHP_Q2, B_Q10[1]); + for (int j = 1; j < psEnc.subfr_length; ++j) { + x_filt_Q12[j] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[j], B_Q10[0]), st_res_Q2[j - 1], B_Q10[1]); + } + P.sHarmHP_Q2 = st_res_Q2[psEnc.subfr_length - 1]; + Filters.silk_prefilt(P, x_filt_Q12, xw_Q3, pxw_Q3, HarmShapeFIRPacked_Q12, Tilt_Q14, LF_shp_Q14, lag, psEnc.subfr_length); + px += psEnc.subfr_length; + pxw_Q3 += psEnc.subfr_length; + } + P.lagPrev = psEncCtrl.pitchL[psEnc.nb_subfr - 1]; + } + + static void silk_prefilt(SilkPrefilterState P, int[] st_res_Q12, int[] xw_Q3, int xw_Q3_ptr, int HarmShapeFIRPacked_Q12, int Tilt_Q14, int LF_shp_Q14, int lag, int length) { + short[] LTP_shp_buf = P.sLTP_shp; + int LTP_shp_buf_idx = P.sLTP_shp_buf_idx; + int sLF_AR_shp_Q12 = P.sLF_AR_shp_Q12; + int sLF_MA_shp_Q12 = P.sLF_MA_shp_Q12; + for (int i = 0; i < length; ++i) { + int n_LTP_Q12; + if (lag > 0) { + Inlines.OpusAssert(true); + int idx = lag + LTP_shp_buf_idx; + n_LTP_Q12 = Inlines.silk_SMULBB(LTP_shp_buf[idx - 1 - 1 & 0x1FF], HarmShapeFIRPacked_Q12); + n_LTP_Q12 = Inlines.silk_SMLABT(n_LTP_Q12, LTP_shp_buf[idx - 1 & 0x1FF], HarmShapeFIRPacked_Q12); + n_LTP_Q12 = Inlines.silk_SMLABB(n_LTP_Q12, LTP_shp_buf[idx - 1 + 1 & 0x1FF], HarmShapeFIRPacked_Q12); + } else { + n_LTP_Q12 = 0; + } + int n_Tilt_Q10 = Inlines.silk_SMULWB(sLF_AR_shp_Q12, Tilt_Q14); + int n_LF_Q10 = Inlines.silk_SMLAWB(Inlines.silk_SMULWT(sLF_AR_shp_Q12, LF_shp_Q14), sLF_MA_shp_Q12, LF_shp_Q14); + sLF_AR_shp_Q12 = Inlines.silk_SUB32(st_res_Q12[i], Inlines.silk_LSHIFT(n_Tilt_Q10, 2)); + sLF_MA_shp_Q12 = Inlines.silk_SUB32(sLF_AR_shp_Q12, Inlines.silk_LSHIFT(n_LF_Q10, 2)); + LTP_shp_buf_idx = LTP_shp_buf_idx - 1 & 0x1FF; + LTP_shp_buf[LTP_shp_buf_idx] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sLF_MA_shp_Q12, 12)); + xw_Q3[xw_Q3_ptr + i] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(sLF_MA_shp_Q12, n_LTP_Q12), 9); + } + P.sLF_AR_shp_Q12 = sLF_AR_shp_Q12; + P.sLF_MA_shp_Q12 = sLF_MA_shp_Q12; + P.sLTP_shp_buf_idx = LTP_shp_buf_idx; + } + + static void silk_biquad_alt(short[] input, int input_ptr, int[] B_Q28, int[] A_Q28, int[] S, short[] output, int output_ptr, int len, int stride) { + int A0_L_Q28 = -A_Q28[0] & 0x3FFF; + int A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); + int A1_L_Q28 = -A_Q28[1] & 0x3FFF; + int A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); + for (int k = 0; k < len; ++k) { + short inval = input[input_ptr + k * stride]; + int out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[0], B_Q28[0], inval), 2); + S[0] = S[1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14); + S[0] = Inlines.silk_SMLAWB(S[0], out32_Q14, A0_U_Q28); + S[0] = Inlines.silk_SMLAWB(S[0], B_Q28[1], inval); + S[1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14); + S[1] = Inlines.silk_SMLAWB(S[1], out32_Q14, A1_U_Q28); + S[1] = Inlines.silk_SMLAWB(S[1], B_Q28[2], inval); + output[output_ptr + k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + 16384 - 1, 14)); + } + } + + static void silk_biquad_alt(short[] input, int input_ptr, int[] B_Q28, int[] A_Q28, int[] S, int S_ptr, short[] output, int output_ptr, int len, int stride) { + int A0_L_Q28 = -A_Q28[0] & 0x3FFF; + int A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); + int A1_L_Q28 = -A_Q28[1] & 0x3FFF; + int A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); + for (int k = 0; k < len; ++k) { + int s1 = S_ptr + 1; + short inval = input[input_ptr + k * stride]; + int out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[S_ptr], B_Q28[0], inval), 2); + S[S_ptr] = S[s1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14); + S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], out32_Q14, A0_U_Q28); + S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], B_Q28[1], inval); + S[s1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14); + S[s1] = Inlines.silk_SMLAWB(S[s1], out32_Q14, A1_U_Q28); + S[s1] = Inlines.silk_SMLAWB(S[s1], B_Q28[2], inval); + output[output_ptr + k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + 16384 - 1, 14)); + } + } + + static void silk_ana_filt_bank_1(short[] input, int input_ptr, int[] S, short[] outL, short[] outH, int outH_ptr, int N) { + int N2 = Inlines.silk_RSHIFT(N, 1); + for (int k = 0; k < N2; ++k) { + int in32 = Inlines.silk_LSHIFT(input[input_ptr + 2 * k], 10); + int Y = Inlines.silk_SUB32(in32, S[0]); + int X = Inlines.silk_SMLAWB(Y, Y, -24290); + int out_1 = Inlines.silk_ADD32(S[0], X); + S[0] = Inlines.silk_ADD32(in32, X); + in32 = Inlines.silk_LSHIFT(input[input_ptr + 2 * k + 1], 10); + Y = Inlines.silk_SUB32(in32, S[1]); + X = Inlines.silk_SMULWB(Y, 10788); + int out_2 = Inlines.silk_ADD32(S[1], X); + S[1] = Inlines.silk_ADD32(in32, X); + outL[k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD32(out_2, out_1), 11)); + outH[outH_ptr + k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(out_2, out_1), 11)); + } + } + + static void silk_bwexpander_32(int[] ar, int d, int chirp_Q16) { + int chirp_minus_one_Q16 = chirp_Q16 - 65536; + for (int i = 0; i < d - 1; ++i) { + ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]); + chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16); + } + ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]); + } + + static void silk_LP_interpolate_filter_taps(int[] B_Q28, int[] A_Q28, int ind, int fac_Q16) { + if (ind < 4) { + if (fac_Q16 > 0) { + if (fac_Q16 < 32768) { + for (int nb = 0; nb < 3; ++nb) { + B_Q28[nb] = Inlines.silk_SMLAWB(SilkTables.silk_Transition_LP_B_Q28[ind][nb], SilkTables.silk_Transition_LP_B_Q28[ind + 1][nb] - SilkTables.silk_Transition_LP_B_Q28[ind][nb], fac_Q16); + } + for (int na = 0; na < 2; ++na) { + A_Q28[na] = Inlines.silk_SMLAWB(SilkTables.silk_Transition_LP_A_Q28[ind][na], SilkTables.silk_Transition_LP_A_Q28[ind + 1][na] - SilkTables.silk_Transition_LP_A_Q28[ind][na], fac_Q16); + } + } else { + Inlines.OpusAssert(fac_Q16 - 65536 == Inlines.silk_SAT16(fac_Q16 - 65536)); + for (int nb = 0; nb < 3; ++nb) { + B_Q28[nb] = Inlines.silk_SMLAWB(SilkTables.silk_Transition_LP_B_Q28[ind + 1][nb], SilkTables.silk_Transition_LP_B_Q28[ind + 1][nb] - SilkTables.silk_Transition_LP_B_Q28[ind][nb], fac_Q16 - 65536); + } + for (int na = 0; na < 2; ++na) { + A_Q28[na] = Inlines.silk_SMLAWB(SilkTables.silk_Transition_LP_A_Q28[ind + 1][na], SilkTables.silk_Transition_LP_A_Q28[ind + 1][na] - SilkTables.silk_Transition_LP_A_Q28[ind][na], fac_Q16 - 65536); + } + } + } else { + System.arraycopy(SilkTables.silk_Transition_LP_B_Q28[ind], 0, B_Q28, 0, 3); + System.arraycopy(SilkTables.silk_Transition_LP_A_Q28[ind], 0, A_Q28, 0, 2); + } + } else { + System.arraycopy(SilkTables.silk_Transition_LP_B_Q28[4], 0, B_Q28, 0, 3); + System.arraycopy(SilkTables.silk_Transition_LP_A_Q28[4], 0, A_Q28, 0, 2); + } + } + + static void silk_LPC_analysis_filter(short[] output, int output_ptr, short[] input, int input_ptr, short[] B, int B_ptr, int len, int d) { + int j; + short[] mem = new short[16]; + short[] num = new short[16]; + Inlines.OpusAssert(d >= 6); + Inlines.OpusAssert((d & 1) == 0); + Inlines.OpusAssert(d <= len); + Inlines.OpusAssert(d <= 16); + for (j = 0; j < d; ++j) { + num[j] = (short)(0 - B[B_ptr + j]); + } + for (j = 0; j < d; ++j) { + mem[j] = input[input_ptr + d - j - 1]; + } + Kernels.celt_fir(input, input_ptr + d, num, output, output_ptr + d, len - d, d, mem); + for (j = output_ptr; j < output_ptr + d; ++j) { + output[j] = 0; + } + } + + static int LPC_inverse_pred_gain_QA(int[][] A_QA, int order) { + int rc_mult1_Q30; + int rc_Q31; + int[] Anew_QA = A_QA[order & 1]; + int invGain_Q30 = 0x40000000; + for (int k = order - 1; k > 0; --k) { + if (Anew_QA[k] > 0xFFEF9E || Anew_QA[k] < -16773022) { + return 0; + } + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[k], 7); + rc_mult1_Q30 = 0x40000000 - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + Inlines.OpusAssert(rc_mult1_Q30 > 32768); + Inlines.OpusAssert(rc_mult1_Q30 <= 0x40000000); + int mult2Q = 32 - Inlines.silk_CLZ32(Inlines.silk_abs(rc_mult1_Q30)); + int rc_mult2 = Inlines.silk_INVERSE32_varQ(rc_mult1_Q30, mult2Q + 30); + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= 0x40000000); + int[] Aold_QA = Anew_QA; + Anew_QA = A_QA[k & 1]; + for (int n = 0; n < k; ++n) { + int tmp_QA = Aold_QA[n] - Inlines.MUL32_FRAC_Q(Aold_QA[k - n - 1], rc_Q31, 31); + Anew_QA[n] = Inlines.MUL32_FRAC_Q(tmp_QA, rc_mult2, mult2Q); + } + } + if (Anew_QA[0] > 0xFFEF9E || Anew_QA[0] < -16773022) { + return 0; + } + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[0], 7); + rc_mult1_Q30 = 0x40000000 - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + Inlines.OpusAssert((invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2)) >= 0); + Inlines.OpusAssert(invGain_Q30 <= 0x40000000); + return invGain_Q30; + } + + static int silk_LPC_inverse_pred_gain(short[] A_Q12, int order) { + int[][] Atmp_QA = new int[][]{new int[order], new int[order]}; + int DC_resp = 0; + int[] Anew_QA = Atmp_QA[order & 1]; + for (int k = 0; k < order; ++k) { + DC_resp += A_Q12[k]; + Anew_QA[k] = Inlines.silk_LSHIFT32(A_Q12[k], 12); + } + if (DC_resp >= 4096) { + return 0; + } + return Filters.LPC_inverse_pred_gain_QA(Atmp_QA, order); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/FindLPC.java b/source/decompiled/de/maxhenkel/voicechat/concentus/FindLPC.java new file mode 100644 index 0000000..9b33036 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/FindLPC.java @@ -0,0 +1,80 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.BurgModified; +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.NLSF; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SumSqrShift; + +class FindLPC { + FindLPC() { + } + + static void silk_find_LPC(SilkChannelEncoder psEncC, short[] NLSF_Q15, short[] x, int minInvGain_Q30) { + int[] a_Q16 = new int[16]; + BoxedValueInt res_nrg0 = new BoxedValueInt(0); + BoxedValueInt res_nrg1 = new BoxedValueInt(0); + BoxedValueInt rshift0 = new BoxedValueInt(0); + BoxedValueInt rshift1 = new BoxedValueInt(0); + BoxedValueInt scratch_box1 = new BoxedValueInt(0); + BoxedValueInt scratch_box2 = new BoxedValueInt(0); + int[] a_tmp_Q16 = new int[16]; + short[] a_tmp_Q12 = new short[16]; + short[] NLSF0_Q15 = new short[16]; + int subfr_length = psEncC.subfr_length + psEncC.predictLPCOrder; + psEncC.indices.NLSFInterpCoef_Q2 = (byte)4; + BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_Q16, x, 0, minInvGain_Q30, subfr_length, psEncC.nb_subfr, psEncC.predictLPCOrder); + int res_nrg = scratch_box1.Val; + int res_nrg_Q = scratch_box2.Val; + if (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == 4) { + BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_tmp_Q16, x, 2 * subfr_length, minInvGain_Q30, subfr_length, 2, psEncC.predictLPCOrder); + int res_tmp_nrg = scratch_box1.Val; + int res_tmp_nrg_Q = scratch_box2.Val; + int shift = res_tmp_nrg_Q - res_nrg_Q; + if (shift >= 0) { + if (shift < 32) { + res_nrg -= Inlines.silk_RSHIFT(res_tmp_nrg, shift); + } + } else { + Inlines.OpusAssert(shift > -32); + res_nrg = Inlines.silk_RSHIFT(res_nrg, -shift) - res_tmp_nrg; + res_nrg_Q = res_tmp_nrg_Q; + } + NLSF.silk_A2NLSF(NLSF_Q15, a_tmp_Q16, psEncC.predictLPCOrder); + short[] LPC_res = new short[2 * subfr_length]; + for (int k = 3; k >= 0; --k) { + int res_nrg_interp_Q; + Inlines.silk_interpolate(NLSF0_Q15, psEncC.prev_NLSFq_Q15, NLSF_Q15, k, psEncC.predictLPCOrder); + NLSF.silk_NLSF2A(a_tmp_Q12, NLSF0_Q15, psEncC.predictLPCOrder); + Filters.silk_LPC_analysis_filter(LPC_res, 0, x, 0, a_tmp_Q12, 0, 2 * subfr_length, psEncC.predictLPCOrder); + SumSqrShift.silk_sum_sqr_shift(res_nrg0, rshift0, LPC_res, psEncC.predictLPCOrder, subfr_length - psEncC.predictLPCOrder); + SumSqrShift.silk_sum_sqr_shift(res_nrg1, rshift1, LPC_res, psEncC.predictLPCOrder + subfr_length, subfr_length - psEncC.predictLPCOrder); + shift = rshift0.Val - rshift1.Val; + if (shift >= 0) { + res_nrg1.Val = Inlines.silk_RSHIFT(res_nrg1.Val, shift); + res_nrg_interp_Q = 0 - rshift0.Val; + } else { + res_nrg0.Val = Inlines.silk_RSHIFT(res_nrg0.Val, 0 - shift); + res_nrg_interp_Q = 0 - rshift1.Val; + } + int res_nrg_interp = Inlines.silk_ADD32(res_nrg0.Val, res_nrg1.Val); + shift = res_nrg_interp_Q - res_nrg_Q; + boolean isInterpLower = shift >= 0 ? Inlines.silk_RSHIFT(res_nrg_interp, shift) < res_nrg : (-shift < 32 ? res_nrg_interp < Inlines.silk_RSHIFT(res_nrg, -shift) : false); + if (!isInterpLower) continue; + res_nrg = res_nrg_interp; + res_nrg_Q = res_nrg_interp_Q; + psEncC.indices.NLSFInterpCoef_Q2 = (byte)k; + } + } + if (psEncC.indices.NLSFInterpCoef_Q2 == 4) { + NLSF.silk_A2NLSF(NLSF_Q15, a_Q16, psEncC.predictLPCOrder); + } + Inlines.OpusAssert(psEncC.indices.NLSFInterpCoef_Q2 == 4 || psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == 4); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/FindLTP.java b/source/decompiled/de/maxhenkel/voicechat/concentus/FindLTP.java new file mode 100644 index 0000000..fc1f234 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/FindLTP.java @@ -0,0 +1,153 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CorrelateMatrix; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.LinearAlgebra; +import de.maxhenkel.voicechat.concentus.RegularizeCorrelations; +import de.maxhenkel.voicechat.concentus.ResidualEnergy; +import de.maxhenkel.voicechat.concentus.SumSqrShift; + +class FindLTP { + private static final int LTP_CORRS_HEAD_ROOM = 2; + + FindLTP() { + } + + static void silk_find_LTP(short[] b_Q14, int[] WLTP, BoxedValueInt LTPredCodGain_Q7, short[] r_lpc, int[] lag, int[] Wght_Q15, int subfr_length, int nb_subfr, int mem_offset, int[] corr_rshifts) { + int i; + int temp32; + int extra_shifts; + int k; + int[] b_Q16 = new int[5]; + int[] delta_b_Q14 = new int[5]; + int[] d_Q14 = new int[4]; + int[] nrg = new int[4]; + int[] w = new int[4]; + int[] Rr = new int[5]; + int[] rr = new int[4]; + int b_Q14_ptr = 0; + int WLTP_ptr = 0; + int r_ptr = mem_offset; + for (k = 0; k < nb_subfr; ++k) { + int lag_ptr = r_ptr - (lag[k] + 2); + BoxedValueInt boxed_rr = new BoxedValueInt(0); + BoxedValueInt boxed_rr_shift = new BoxedValueInt(0); + SumSqrShift.silk_sum_sqr_shift(boxed_rr, boxed_rr_shift, r_lpc, r_ptr, subfr_length); + rr[k] = boxed_rr.Val; + int rr_shifts = boxed_rr_shift.Val; + int LZs = Inlines.silk_CLZ32(rr[k]); + if (LZs < 2) { + rr[k] = Inlines.silk_RSHIFT_ROUND(rr[k], 2 - LZs); + rr_shifts += 2 - LZs; + } + corr_rshifts[k] = rr_shifts; + BoxedValueInt boxed_shifts = new BoxedValueInt(corr_rshifts[k]); + CorrelateMatrix.silk_corrMatrix(r_lpc, lag_ptr, subfr_length, 5, 2, WLTP, WLTP_ptr, boxed_shifts); + corr_rshifts[k] = boxed_shifts.Val; + CorrelateMatrix.silk_corrVector(r_lpc, lag_ptr, r_lpc, r_ptr, subfr_length, 5, Rr, corr_rshifts[k]); + if (corr_rshifts[k] > rr_shifts) { + rr[k] = Inlines.silk_RSHIFT(rr[k], corr_rshifts[k] - rr_shifts); + } + Inlines.OpusAssert(rr[k] >= 0); + int regu = 1; + regu = Inlines.silk_SMLAWB(regu, rr[k], 1092); + regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, 0, 0, 5), 1092); + regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, 4, 4, 5), 1092); + RegularizeCorrelations.silk_regularize_correlations(WLTP, WLTP_ptr, rr, k, regu, 5); + LinearAlgebra.silk_solve_LDL(WLTP, WLTP_ptr, 5, Rr, b_Q16); + FindLTP.silk_fit_LTP(b_Q16, b_Q14, b_Q14_ptr); + nrg[k] = ResidualEnergy.silk_residual_energy16_covar(b_Q14, b_Q14_ptr, WLTP, WLTP_ptr, Rr, rr[k], 5, 14); + extra_shifts = Inlines.silk_min_int(corr_rshifts[k], 2); + int denom32 = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1 + extra_shifts) + Inlines.silk_RSHIFT(Inlines.silk_SMULWB(subfr_length, 655), corr_rshifts[k] - extra_shifts); + denom32 = Inlines.silk_max(denom32, 1); + Inlines.OpusAssert((long)Wght_Q15[k] << 16 < Integer.MAX_VALUE); + temp32 = Inlines.silk_DIV32(Inlines.silk_LSHIFT(Wght_Q15[k], 16), denom32); + temp32 = Inlines.silk_RSHIFT(temp32, 31 + corr_rshifts[k] - extra_shifts - 26); + int WLTP_max = 0; + for (i = WLTP_ptr; i < WLTP_ptr + 25; ++i) { + WLTP_max = Inlines.silk_max(WLTP[i], WLTP_max); + } + int lshift = Inlines.silk_CLZ32(WLTP_max) - 1 - 3; + Inlines.OpusAssert(8 + lshift >= 0); + if (8 + lshift < 31) { + temp32 = Inlines.silk_min_32(temp32, Inlines.silk_LSHIFT(1, 8 + lshift)); + } + Inlines.silk_scale_vector32_Q26_lshift_18(WLTP, WLTP_ptr, temp32, 25); + w[k] = Inlines.MatrixGet(WLTP, WLTP_ptr, 2, 2, 5); + Inlines.OpusAssert(w[k] >= 0); + r_ptr += subfr_length; + b_Q14_ptr += 5; + WLTP_ptr += 25; + } + int maxRshifts = 0; + for (k = 0; k < nb_subfr; ++k) { + maxRshifts = Inlines.silk_max_int(corr_rshifts[k], maxRshifts); + } + if (LTPredCodGain_Q7 != null) { + int LPC_LTP_res_nrg = 0; + int LPC_res_nrg = 0; + Inlines.OpusAssert(true); + for (k = 0; k < nb_subfr; ++k) { + LPC_res_nrg = Inlines.silk_ADD32(LPC_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(rr[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); + LPC_LTP_res_nrg = Inlines.silk_ADD32(LPC_LTP_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); + } + LPC_LTP_res_nrg = Inlines.silk_max(LPC_LTP_res_nrg, 1); + int div_Q16 = Inlines.silk_DIV32_varQ(LPC_res_nrg, LPC_LTP_res_nrg, 16); + LTPredCodGain_Q7.Val = Inlines.silk_SMULBB(3, Inlines.silk_lin2log(div_Q16) - 2048); + Inlines.OpusAssert(LTPredCodGain_Q7.Val == Inlines.silk_SAT16(Inlines.silk_MUL(3, Inlines.silk_lin2log(div_Q16) - 2048))); + } + b_Q14_ptr = 0; + for (k = 0; k < nb_subfr; ++k) { + d_Q14[k] = 0; + for (i = b_Q14_ptr; i < b_Q14_ptr + 5; ++i) { + int n = k; + d_Q14[n] = d_Q14[n] + b_Q14[i]; + } + b_Q14_ptr += 5; + } + int max_abs_d_Q14 = 0; + int max_w_bits = 0; + for (k = 0; k < nb_subfr; ++k) { + max_abs_d_Q14 = Inlines.silk_max_32(max_abs_d_Q14, Inlines.silk_abs(d_Q14[k])); + max_w_bits = Inlines.silk_max_32(max_w_bits, 32 - Inlines.silk_CLZ32(w[k]) + corr_rshifts[k] - maxRshifts); + } + Inlines.OpusAssert(max_abs_d_Q14 <= 163840); + extra_shifts = max_w_bits + 32 - Inlines.silk_CLZ32(max_abs_d_Q14) - 14; + extra_shifts -= 29 + maxRshifts; + extra_shifts = Inlines.silk_max_int(extra_shifts, 0); + int maxRshifts_wxtra = maxRshifts + extra_shifts; + temp32 = Inlines.silk_RSHIFT(262, maxRshifts + extra_shifts) + 1; + int wd = 0; + for (k = 0; k < nb_subfr; ++k) { + temp32 = Inlines.silk_ADD32(temp32, Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k])); + wd = Inlines.silk_ADD32(wd, Inlines.silk_LSHIFT(Inlines.silk_SMULWW(Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k]), d_Q14[k]), 2)); + } + int m_Q12 = Inlines.silk_DIV32_varQ(wd, temp32, 12); + b_Q14_ptr = 0; + for (k = 0; k < nb_subfr; ++k) { + temp32 = 2 - corr_rshifts[k] > 0 ? Inlines.silk_RSHIFT(w[k], 2 - corr_rshifts[k]) : Inlines.silk_LSHIFT_SAT32(w[k], corr_rshifts[k] - 2); + int g_Q26 = Inlines.silk_MUL(Inlines.silk_DIV32(0x666667, Inlines.silk_RSHIFT(0x666667, 10) + temp32), Inlines.silk_LSHIFT_SAT32(Inlines.silk_SUB_SAT32(m_Q12, Inlines.silk_RSHIFT(d_Q14[k], 2)), 4)); + temp32 = 0; + for (i = 0; i < 5; ++i) { + delta_b_Q14[i] = Inlines.silk_max_16(b_Q14[b_Q14_ptr + i], (short)1638); + temp32 += delta_b_Q14[i]; + } + temp32 = Inlines.silk_DIV32(g_Q26, temp32); + for (i = 0; i < 5; ++i) { + b_Q14[b_Q14_ptr + i] = (short)Inlines.silk_LIMIT_32(b_Q14[b_Q14_ptr + i] + Inlines.silk_SMULWB(Inlines.silk_LSHIFT_SAT32(temp32, 4), delta_b_Q14[i]), -16000, 28000); + } + b_Q14_ptr += 5; + } + } + + static void silk_fit_LTP(int[] LTP_coefs_Q16, short[] LTP_coefs_Q14, int LTP_coefs_Q14_ptr) { + for (int i = 0; i < 5; ++i) { + LTP_coefs_Q14[LTP_coefs_Q14_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(LTP_coefs_Q16[i], 2)); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/FindPitchLags.java b/source/decompiled/de/maxhenkel/voicechat/concentus/FindPitchLags.java new file mode 100644 index 0000000..292a83b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/FindPitchLags.java @@ -0,0 +1,72 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.ApplySineWindow; +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Autocorrelation; +import de.maxhenkel.voicechat.concentus.BWExpander; +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.BoxedValueShort; +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.K2A; +import de.maxhenkel.voicechat.concentus.PitchAnalysisCore; +import de.maxhenkel.voicechat.concentus.Schur; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkEncoderControl; + +class FindPitchLags { + FindPitchLags() { + } + + static void silk_find_pitch_lags(SilkChannelEncoder psEnc, SilkEncoderControl psEncCtrl, short[] res, short[] x, int x_ptr) { + int[] auto_corr = new int[17]; + short[] rc_Q15 = new short[16]; + int[] A_Q24 = new int[16]; + short[] A_Q12 = new short[16]; + int buf_len = psEnc.la_pitch + psEnc.frame_length + psEnc.ltp_mem_length; + Inlines.OpusAssert(buf_len >= psEnc.pitch_LPC_win_length); + int x_buf = x_ptr - psEnc.ltp_mem_length; + short[] Wsig = new short[psEnc.pitch_LPC_win_length]; + int x_buf_ptr = x_buf + buf_len - psEnc.pitch_LPC_win_length; + int Wsig_ptr = 0; + ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr, x, x_buf_ptr, 1, psEnc.la_pitch); + System.arraycopy(x, x_buf_ptr += psEnc.la_pitch, Wsig, Wsig_ptr += psEnc.la_pitch, psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1)); + ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1), x, x_buf_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1), 2, psEnc.la_pitch); + BoxedValueInt boxed_scale = new BoxedValueInt(0); + Autocorrelation.silk_autocorr(auto_corr, boxed_scale, Wsig, psEnc.pitch_LPC_win_length, psEnc.pitchEstimationLPCOrder + 1); + int scale = boxed_scale.Val; + auto_corr[0] = Inlines.silk_SMLAWB(auto_corr[0], auto_corr[0], 66) + 1; + int res_nrg = Schur.silk_schur(rc_Q15, auto_corr, psEnc.pitchEstimationLPCOrder); + psEncCtrl.predGain_Q16 = Inlines.silk_DIV32_varQ(auto_corr[0], Inlines.silk_max_int(res_nrg, 1), 16); + K2A.silk_k2a(A_Q24, rc_Q15, psEnc.pitchEstimationLPCOrder); + for (int i = 0; i < psEnc.pitchEstimationLPCOrder; ++i) { + A_Q12[i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(A_Q24[i], 12)); + } + BWExpander.silk_bwexpander(A_Q12, psEnc.pitchEstimationLPCOrder, 64881); + Filters.silk_LPC_analysis_filter(res, 0, x, x_buf, A_Q12, 0, buf_len, psEnc.pitchEstimationLPCOrder); + if (psEnc.indices.signalType != 0 && psEnc.first_frame_after_reset == 0) { + int thrhld_Q13 = 4915; + thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, -32, psEnc.pitchEstimationLPCOrder); + thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, -209714, psEnc.speech_activity_Q8); + thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, -1228, Inlines.silk_RSHIFT(psEnc.prevSignalType, 1)); + thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, -1637, psEnc.input_tilt_Q15); + BoxedValueShort boxed_lagIndex = new BoxedValueShort(psEnc.indices.lagIndex); + BoxedValueByte boxed_contourIndex = new BoxedValueByte(psEnc.indices.contourIndex); + BoxedValueInt boxed_LTPcorr = new BoxedValueInt(psEnc.LTPCorr_Q15); + psEnc.indices.signalType = PitchAnalysisCore.silk_pitch_analysis_core(res, psEncCtrl.pitchL, boxed_lagIndex, boxed_contourIndex, boxed_LTPcorr, psEnc.prevLag, psEnc.pitchEstimationThreshold_Q16, thrhld_Q13 = Inlines.silk_SAT16(thrhld_Q13), psEnc.fs_kHz, psEnc.pitchEstimationComplexity, psEnc.nb_subfr) == 0 ? (byte)2 : (byte)1; + psEnc.indices.lagIndex = boxed_lagIndex.Val; + psEnc.indices.contourIndex = boxed_contourIndex.Val; + psEnc.LTPCorr_Q15 = boxed_LTPcorr.Val; + } else { + Arrays.MemSet(psEncCtrl.pitchL, 0, 4); + psEnc.indices.lagIndex = 0; + psEnc.indices.contourIndex = 0; + psEnc.LTPCorr_Q15 = 0; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/FindPredCoefs.java b/source/decompiled/de/maxhenkel/voicechat/concentus/FindPredCoefs.java new file mode 100644 index 0000000..023e657 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/FindPredCoefs.java @@ -0,0 +1,83 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.FindLPC; +import de.maxhenkel.voicechat.concentus.FindLTP; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.LTPAnalysisFilter; +import de.maxhenkel.voicechat.concentus.LTPScaleControl; +import de.maxhenkel.voicechat.concentus.NLSF; +import de.maxhenkel.voicechat.concentus.QuantizeLTPGains; +import de.maxhenkel.voicechat.concentus.ResidualEnergy; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkEncoderControl; + +class FindPredCoefs { + FindPredCoefs() { + } + + static void silk_find_pred_coefs(SilkChannelEncoder psEnc, SilkEncoderControl psEncCtrl, short[] res_pitch, short[] x, int x_ptr, int condCoding) { + int minInvGain_Q30; + int i; + int[] invGains_Q16 = new int[4]; + int[] local_gains = new int[4]; + int[] Wght_Q15 = new int[4]; + short[] NLSF_Q15 = new short[16]; + int[] LTP_corrs_rshift = new int[4]; + int min_gain_Q16 = 0x1FFFFFF; + for (i = 0; i < psEnc.nb_subfr; ++i) { + min_gain_Q16 = Inlines.silk_min(min_gain_Q16, psEncCtrl.Gains_Q16[i]); + } + for (i = 0; i < psEnc.nb_subfr; ++i) { + Inlines.OpusAssert(psEncCtrl.Gains_Q16[i] > 0); + invGains_Q16[i] = Inlines.silk_DIV32_varQ(min_gain_Q16, psEncCtrl.Gains_Q16[i], 14); + invGains_Q16[i] = Inlines.silk_max(invGains_Q16[i], 363); + Inlines.OpusAssert(invGains_Q16[i] == Inlines.silk_SAT16(invGains_Q16[i])); + int tmp = Inlines.silk_SMULWB(invGains_Q16[i], invGains_Q16[i]); + Wght_Q15[i] = Inlines.silk_RSHIFT(tmp, 1); + local_gains[i] = Inlines.silk_DIV32(65536, invGains_Q16[i]); + } + short[] LPC_in_pre = new short[psEnc.nb_subfr * psEnc.predictLPCOrder + psEnc.frame_length]; + if (psEnc.indices.signalType == 2) { + Inlines.OpusAssert(psEnc.ltp_mem_length - psEnc.predictLPCOrder >= psEncCtrl.pitchL[0] + 2); + int[] WLTP = new int[psEnc.nb_subfr * 5 * 5]; + BoxedValueInt boxed_codgain = new BoxedValueInt(psEncCtrl.LTPredCodGain_Q7); + FindLTP.silk_find_LTP(psEncCtrl.LTPCoef_Q14, WLTP, boxed_codgain, res_pitch, psEncCtrl.pitchL, Wght_Q15, psEnc.subfr_length, psEnc.nb_subfr, psEnc.ltp_mem_length, LTP_corrs_rshift); + psEncCtrl.LTPredCodGain_Q7 = boxed_codgain.Val; + BoxedValueByte boxed_periodicity = new BoxedValueByte(psEnc.indices.PERIndex); + BoxedValueInt boxed_gain = new BoxedValueInt(psEnc.sum_log_gain_Q7); + QuantizeLTPGains.silk_quant_LTP_gains(psEncCtrl.LTPCoef_Q14, psEnc.indices.LTPIndex, boxed_periodicity, boxed_gain, WLTP, psEnc.mu_LTP_Q9, psEnc.LTPQuantLowComplexity, psEnc.nb_subfr); + psEnc.indices.PERIndex = boxed_periodicity.Val; + psEnc.sum_log_gain_Q7 = boxed_gain.Val; + LTPScaleControl.silk_LTP_scale_ctrl(psEnc, psEncCtrl, condCoding); + LTPAnalysisFilter.silk_LTP_analysis_filter(LPC_in_pre, x, x_ptr - psEnc.predictLPCOrder, psEncCtrl.LTPCoef_Q14, psEncCtrl.pitchL, invGains_Q16, psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder); + } else { + int x_ptr2 = x_ptr - psEnc.predictLPCOrder; + int x_pre_ptr = 0; + for (i = 0; i < psEnc.nb_subfr; ++i) { + Inlines.silk_scale_copy_vector16(LPC_in_pre, x_pre_ptr, x, x_ptr2, invGains_Q16[i], psEnc.subfr_length + psEnc.predictLPCOrder); + x_pre_ptr += psEnc.subfr_length + psEnc.predictLPCOrder; + x_ptr2 += psEnc.subfr_length; + } + Arrays.MemSet(psEncCtrl.LTPCoef_Q14, (short)0, psEnc.nb_subfr * 5); + psEncCtrl.LTPredCodGain_Q7 = 0; + psEnc.sum_log_gain_Q7 = 0; + } + if (psEnc.first_frame_after_reset != 0) { + minInvGain_Q30 = 10737418; + } else { + minInvGain_Q30 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(2048, psEncCtrl.LTPredCodGain_Q7, 21845)); + minInvGain_Q30 = Inlines.silk_DIV32_varQ(minInvGain_Q30, Inlines.silk_SMULWW(10000, Inlines.silk_SMLAWB(65536, 196608, psEncCtrl.coding_quality_Q14)), 14); + } + FindLPC.silk_find_LPC(psEnc, NLSF_Q15, LPC_in_pre, minInvGain_Q30); + NLSF.silk_process_NLSFs(psEnc, psEncCtrl.PredCoef_Q12, NLSF_Q15, psEnc.prev_NLSFq_Q15); + ResidualEnergy.silk_residual_energy(psEncCtrl.ResNrg, psEncCtrl.ResNrgQ, LPC_in_pre, psEncCtrl.PredCoef_Q12, local_gains, psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder); + System.arraycopy(NLSF_Q15, 0, psEnc.prev_NLSFq_Q15, 0, 16); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/GainQuantization.java b/source/decompiled/de/maxhenkel/voicechat/concentus/GainQuantization.java new file mode 100644 index 0000000..ffd3562 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/GainQuantization.java @@ -0,0 +1,61 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.Inlines; + +class GainQuantization { + private static final int OFFSET = 2090; + private static final int SCALE_Q16 = 2251; + private static final int INV_SCALE_Q16 = 1907825; + + GainQuantization() { + } + + static void silk_gains_quant(byte[] ind, int[] gain_Q16, BoxedValueByte prev_ind, int conditional, int nb_subfr) { + for (int k = 0; k < nb_subfr; ++k) { + ind[k] = (byte)Inlines.silk_SMULWB(2251, Inlines.silk_lin2log(gain_Q16[k]) - 2090); + if (ind[k] < prev_ind.Val) { + int n = k; + ind[n] = (byte)(ind[n] + 1); + } + ind[k] = (byte)Inlines.silk_LIMIT_int(ind[k], 0, 63); + if (k == 0 && conditional == 0) { + ind[k] = (byte)Inlines.silk_LIMIT_int(ind[k], prev_ind.Val + -4, 63); + prev_ind.Val = ind[k]; + } else { + ind[k] = (byte)(ind[k] - prev_ind.Val); + int double_step_size_threshold = 8 + prev_ind.Val; + if (ind[k] > double_step_size_threshold) { + ind[k] = (byte)(double_step_size_threshold + Inlines.silk_RSHIFT(ind[k] - double_step_size_threshold + 1, 1)); + } + ind[k] = (byte)Inlines.silk_LIMIT_int(ind[k], -4, 36); + prev_ind.Val = ind[k] > double_step_size_threshold ? (byte)(prev_ind.Val + (byte)(Inlines.silk_LSHIFT(ind[k], 1) - double_step_size_threshold)) : (byte)(prev_ind.Val + ind[k]); + int n = k; + ind[n] = (byte)(ind[n] - -4); + } + gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(1907825, prev_ind.Val) + 2090, 3967)); + } + } + + static void silk_gains_dequant(int[] gain_Q16, byte[] ind, BoxedValueByte prev_ind, int conditional, int nb_subfr) { + for (int k = 0; k < nb_subfr; ++k) { + int double_step_size_threshold; + int ind_tmp; + prev_ind.Val = k == 0 && conditional == 0 ? (byte)Inlines.silk_max_int(ind[k], prev_ind.Val - 16) : ((ind_tmp = ind[k] + -4) > (double_step_size_threshold = 8 + prev_ind.Val) ? (byte)(prev_ind.Val + (byte)(Inlines.silk_LSHIFT(ind_tmp, 1) - double_step_size_threshold)) : (byte)(prev_ind.Val + (byte)ind_tmp)); + prev_ind.Val = (byte)Inlines.silk_LIMIT_int(prev_ind.Val, 0, 63); + gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(1907825, prev_ind.Val) + 2090, 3967)); + } + } + + static int silk_gains_ID(byte[] ind, int nb_subfr) { + int gainsID = 0; + for (int k = 0; k < nb_subfr; ++k) { + gainsID = Inlines.silk_ADD_LSHIFT32(ind[k], gainsID, 8); + } + return gainsID; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/HPVariableCutoff.java b/source/decompiled/de/maxhenkel/voicechat/concentus/HPVariableCutoff.java new file mode 100644 index 0000000..924b732 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/HPVariableCutoff.java @@ -0,0 +1,29 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; + +class HPVariableCutoff { + HPVariableCutoff() { + } + + static void silk_HP_variable_cutoff(SilkChannelEncoder[] state_Fxx) { + SilkChannelEncoder psEncC1 = state_Fxx[0]; + if (psEncC1.prevSignalType == 2) { + int pitch_freq_Hz_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(Inlines.silk_MUL(psEncC1.fs_kHz, 1000), 16), psEncC1.prevLag); + int pitch_freq_log_Q7 = Inlines.silk_lin2log(pitch_freq_Hz_Q16) - 2048; + int quality_Q15 = psEncC1.input_quality_bands_Q15[0]; + int delta_freq_Q7 = (pitch_freq_log_Q7 = Inlines.silk_SMLAWB(pitch_freq_log_Q7, Inlines.silk_SMULWB(Inlines.silk_LSHIFT(-quality_Q15, 2), quality_Q15), pitch_freq_log_Q7 - (Inlines.silk_lin2log(0x3C0000) - 2048))) - Inlines.silk_RSHIFT(psEncC1.variable_HP_smth1_Q15, 8); + if (delta_freq_Q7 < 0) { + delta_freq_Q7 = Inlines.silk_MUL(delta_freq_Q7, 3); + } + delta_freq_Q7 = Inlines.silk_LIMIT_32(delta_freq_Q7, -51, 51); + psEncC1.variable_HP_smth1_Q15 = Inlines.silk_SMLAWB(psEncC1.variable_HP_smth1_Q15, Inlines.silk_SMULBB(psEncC1.speech_activity_Q8, delta_freq_Q7), 6554); + psEncC1.variable_HP_smth1_Q15 = Inlines.silk_LIMIT_32(psEncC1.variable_HP_smth1_Q15, Inlines.silk_LSHIFT(Inlines.silk_lin2log(60), 8), Inlines.silk_LSHIFT(Inlines.silk_lin2log(100), 8)); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Inlines.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Inlines.java new file mode 100644 index 0000000..7d17551 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Inlines.java @@ -0,0 +1,1263 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.PitchAnalysisCore; + +class Inlines { + private static short[] sqrt_C = new short[]{23175, 11561, -3011, 1699, -664}; + private static short log2_C0 = (short)-6793; + + Inlines() { + } + + static void OpusAssert(boolean condition) { + if (!condition) { + throw new AssertionError(); + } + } + + static void OpusAssert(boolean condition, String message) { + if (!condition) { + throw new AssertionError((Object)message); + } + } + + static long CapToUInt32(long val) { + return 0xFFFFFFFFL & (long)((int)val); + } + + static long CapToUInt32(int val) { + return val; + } + + static int MULT16_16SU(int a, int b) { + return (short)a * (b & 0xFFFF); + } + + static int MULT16_32_Q16(short a, int b) { + return Inlines.ADD32(Inlines.MULT16_16((int)a, Inlines.SHR(b, 16)), Inlines.SHR(Inlines.MULT16_16SU(a, b & 0xFFFF), 16)); + } + + static int MULT16_32_Q16(int a, int b) { + return Inlines.ADD32(Inlines.MULT16_16(a, Inlines.SHR(b, 16)), Inlines.SHR(Inlines.MULT16_16SU(a, b & 0xFFFF), 16)); + } + + static int MULT16_32_P16(short a, int b) { + return Inlines.ADD32(Inlines.MULT16_16((int)a, Inlines.SHR(b, 16)), Inlines.PSHR(Inlines.MULT16_16SU(a, b & 0xFFFF), 16)); + } + + static int MULT16_32_P16(int a, int b) { + return Inlines.ADD32(Inlines.MULT16_16(a, Inlines.SHR(b, 16)), Inlines.PSHR(Inlines.MULT16_16SU(a, b & 0xFFFF), 16)); + } + + static int MULT16_32_Q15(short a, int b) { + return (a * (b >> 16) << 1) + (a * (b & 0xFFFF) >> 15); + } + + static int MULT16_32_Q15(int a, int b) { + return (a * (b >> 16) << 1) + (a * (b & 0xFFFF) >> 15); + } + + static int MULT32_32_Q31(int a, int b) { + return Inlines.ADD32(Inlines.ADD32(Inlines.SHL(Inlines.MULT16_16(Inlines.SHR(a, 16), Inlines.SHR(b, 16)), 1), Inlines.SHR(Inlines.MULT16_16SU(Inlines.SHR(a, 16), b & 0xFFFF), 15)), Inlines.SHR(Inlines.MULT16_16SU(Inlines.SHR(b, 16), a & 0xFFFF), 15)); + } + + static short QCONST16(float x, int bits) { + return (short)(0.5 + (double)(x * (float)(1 << bits))); + } + + static int QCONST32(float x, int bits) { + return (int)(0.5 + (double)(x * (float)(1 << bits))); + } + + static short NEG16(short x) { + return (short)(0 - x); + } + + static int NEG16(int x) { + return 0 - x; + } + + static int NEG32(int x) { + return 0 - x; + } + + static short EXTRACT16(int x) { + return (short)x; + } + + static int EXTEND32(short x) { + return x; + } + + static int EXTEND32(int x) { + return x; + } + + static short SHR16(short a, int shift) { + return (short)(a >> shift); + } + + static int SHR16(int a, int shift) { + return a >> shift; + } + + static short SHL16(short a, int shift) { + return (short)((a & 0xFFFF) << shift); + } + + static int SHL16(int a, int shift) { + return (int)(0xFFFFFFFFFFFFFFFFL & (long)a << shift); + } + + static int SHR32(int a, int shift) { + return a >> shift; + } + + static int SHL32(int a, int shift) { + return (int)(0xFFFFFFFFFFFFFFFFL & (long)a << shift); + } + + static int PSHR32(int a, int shift) { + return Inlines.SHR32(a + (Inlines.EXTEND32(1) << shift >> 1), shift); + } + + static short PSHR16(short a, int shift) { + return Inlines.SHR16((short)(a + (1 << shift >> 1)), shift); + } + + static int PSHR16(int a, int shift) { + return Inlines.SHR32(a + (1 << shift >> 1), shift); + } + + static int VSHR32(int a, int shift) { + return shift > 0 ? Inlines.SHR32(a, shift) : Inlines.SHL32(a, -shift); + } + + private static int SHR(int a, int shift) { + return a >> shift; + } + + private static int SHL(int a, int shift) { + return Inlines.SHL32(a, shift); + } + + private static int SHR(short a, int shift) { + return a >> shift; + } + + private static int SHL(short a, int shift) { + return Inlines.SHL32(a, shift); + } + + private static int PSHR(int a, int shift) { + return Inlines.SHR(a + (Inlines.EXTEND32(1) << shift >> 1), shift); + } + + static int SATURATE(int x, int a) { + return x > a ? a : (x < -a ? -a : x); + } + + static short SATURATE16(int x) { + return Inlines.EXTRACT16(x > Short.MAX_VALUE ? Short.MAX_VALUE : (x < Short.MIN_VALUE ? Short.MIN_VALUE : x)); + } + + static short ROUND16(short x, short a) { + return Inlines.EXTRACT16(Inlines.PSHR32(x, a)); + } + + static int ROUND16(int x, int a) { + return Inlines.PSHR32(x, a); + } + + static int PDIV32(int a, int b) { + return a / b; + } + + static short HALF16(short x) { + return Inlines.SHR16(x, 1); + } + + static int HALF16(int x) { + return Inlines.SHR32(x, 1); + } + + static int HALF32(int x) { + return Inlines.SHR32(x, 1); + } + + static short ADD16(short a, short b) { + return (short)(a + b); + } + + static int ADD16(int a, int b) { + return a + b; + } + + static short SUB16(short a, short b) { + return (short)(a - b); + } + + static int SUB16(int a, int b) { + return a - b; + } + + static int ADD32(int a, int b) { + return a + b; + } + + static int SUB32(int a, int b) { + return a - b; + } + + static short MULT16_16_16(short a, short b) { + return (short)(a * b); + } + + static int MULT16_16_16(int a, int b) { + return a * b; + } + + static int MULT16_16(int a, int b) { + return a * b; + } + + static int MULT16_16(short a, short b) { + return a * b; + } + + static int MAC16_16(short c, short a, short b) { + return c + a * b; + } + + static int MAC16_16(int c, short a, short b) { + return c + a * b; + } + + static int MAC16_16(int c, int a, int b) { + return c + a * b; + } + + static int MAC16_32_Q15(int c, short a, short b) { + return Inlines.ADD32(c, Inlines.ADD32(Inlines.MULT16_16((int)a, Inlines.SHR(b, 15)), Inlines.SHR(Inlines.MULT16_16((int)a, b & Short.MAX_VALUE), 15))); + } + + static int MAC16_32_Q15(int c, int a, int b) { + return Inlines.ADD32(c, Inlines.ADD32(Inlines.MULT16_16(a, Inlines.SHR(b, 15)), Inlines.SHR(Inlines.MULT16_16(a, b & Short.MAX_VALUE), 15))); + } + + static int MAC16_32_Q16(int c, short a, short b) { + return Inlines.ADD32(c, Inlines.ADD32(Inlines.MULT16_16((int)a, Inlines.SHR(b, 16)), Inlines.SHR(Inlines.MULT16_16SU(a, b & 0xFFFF), 16))); + } + + static int MAC16_32_Q16(int c, int a, int b) { + return Inlines.ADD32(c, Inlines.ADD32(Inlines.MULT16_16(a, Inlines.SHR(b, 16)), Inlines.SHR(Inlines.MULT16_16SU(a, b & 0xFFFF), 16))); + } + + static int MULT16_16_Q11_32(short a, short b) { + return Inlines.SHR(Inlines.MULT16_16(a, b), 11); + } + + static int MULT16_16_Q11_32(int a, int b) { + return Inlines.SHR(Inlines.MULT16_16(a, b), 11); + } + + static short MULT16_16_Q11(short a, short b) { + return (short)Inlines.SHR(Inlines.MULT16_16(a, b), 11); + } + + static int MULT16_16_Q11(int a, int b) { + return Inlines.SHR(Inlines.MULT16_16(a, b), 11); + } + + static short MULT16_16_Q13(short a, short b) { + return (short)Inlines.SHR(Inlines.MULT16_16(a, b), 13); + } + + static int MULT16_16_Q13(int a, int b) { + return Inlines.SHR(Inlines.MULT16_16(a, b), 13); + } + + static short MULT16_16_Q14(short a, short b) { + return (short)Inlines.SHR(Inlines.MULT16_16(a, b), 14); + } + + static int MULT16_16_Q14(int a, int b) { + return Inlines.SHR(Inlines.MULT16_16(a, b), 14); + } + + static short MULT16_16_Q15(short a, short b) { + return (short)Inlines.SHR(Inlines.MULT16_16(a, b), 15); + } + + static int MULT16_16_Q15(int a, int b) { + return Inlines.SHR(Inlines.MULT16_16(a, b), 15); + } + + static short MULT16_16_P13(short a, short b) { + return (short)Inlines.SHR(Inlines.ADD32(4096, Inlines.MULT16_16(a, b)), 13); + } + + static int MULT16_16_P13(int a, int b) { + return Inlines.SHR(Inlines.ADD32(4096, Inlines.MULT16_16(a, b)), 13); + } + + static short MULT16_16_P14(short a, short b) { + return (short)Inlines.SHR(Inlines.ADD32(8192, Inlines.MULT16_16(a, b)), 14); + } + + static int MULT16_16_P14(int a, int b) { + return Inlines.SHR(Inlines.ADD32(8192, Inlines.MULT16_16(a, b)), 14); + } + + static short MULT16_16_P15(short a, short b) { + return (short)Inlines.SHR(Inlines.ADD32(16384, Inlines.MULT16_16(a, b)), 15); + } + + static int MULT16_16_P15(int a, int b) { + return Inlines.SHR(Inlines.ADD32(16384, Inlines.MULT16_16(a, b)), 15); + } + + static short DIV32_16(int a, short b) { + return (short)(a / b); + } + + static int DIV32_16(int a, int b) { + return a / b; + } + + static int DIV32(int a, int b) { + return a / b; + } + + static short SAT16(int x) { + return (short)(x > Short.MAX_VALUE ? Short.MAX_VALUE : (short)(x < Short.MIN_VALUE ? Short.MIN_VALUE : (short)x)); + } + + static short SIG2WORD16(int x) { + x = Inlines.PSHR32(x, 12); + x = Inlines.MAX32(x, Short.MIN_VALUE); + x = Inlines.MIN32(x, Short.MAX_VALUE); + return Inlines.EXTRACT16(x); + } + + static short MIN(short a, short b) { + return a < b ? a : b; + } + + static short MAX(short a, short b) { + return a > b ? a : b; + } + + static short MIN16(short a, short b) { + return a < b ? a : b; + } + + static short MAX16(short a, short b) { + return a > b ? a : b; + } + + static int MIN16(int a, int b) { + return a < b ? a : b; + } + + static int MAX16(int a, int b) { + return a > b ? a : b; + } + + static float MIN16(float a, float b) { + return a < b ? a : b; + } + + static float MAX16(float a, float b) { + return a > b ? a : b; + } + + static int MIN(int a, int b) { + return a < b ? a : b; + } + + static int MAX(int a, int b) { + return a > b ? a : b; + } + + static int IMIN(int a, int b) { + return a < b ? a : b; + } + + static long IMIN(long a, long b) { + return a < b ? a : b; + } + + static int IMAX(int a, int b) { + return a > b ? a : b; + } + + static int MIN32(int a, int b) { + return a < b ? a : b; + } + + static int MAX32(int a, int b) { + return a > b ? a : b; + } + + static float MIN32(float a, float b) { + return a < b ? a : b; + } + + static float MAX32(float a, float b) { + return a > b ? a : b; + } + + static int ABS16(int x) { + return x < 0 ? -x : x; + } + + static float ABS16(float x) { + return x < 0.0f ? -x : x; + } + + static short ABS16(short x) { + return x < 0 ? -x : x; + } + + static int ABS32(int x) { + return x < 0 ? -x : x; + } + + static int celt_udiv(int n, int d) { + Inlines.OpusAssert(d > 0); + return n / d; + } + + static int celt_sudiv(int n, int d) { + Inlines.OpusAssert(d > 0); + return n / d; + } + + static int celt_div(int a, int b) { + return Inlines.MULT32_32_Q31(a, Inlines.celt_rcp(b)); + } + + static int celt_ilog2(int x) { + Inlines.OpusAssert(x > 0, "celt_ilog2() only defined for strictly positive numbers"); + return Inlines.EC_ILOG(x) - 1; + } + + static int celt_zlog2(int x) { + return x <= 0 ? 0 : Inlines.celt_ilog2(x); + } + + static int celt_maxabs16(int[] x, int x_ptr, int len) { + int maxval = 0; + int minval = 0; + for (int i = x_ptr; i < len + x_ptr; ++i) { + maxval = Inlines.MAX32(maxval, x[i]); + minval = Inlines.MIN32(minval, x[i]); + } + return Inlines.MAX32(Inlines.EXTEND32(maxval), -Inlines.EXTEND32(minval)); + } + + static int celt_maxabs32(int[] x, int x_ptr, int len) { + int maxval = 0; + int minval = 0; + for (int i = x_ptr; i < x_ptr + len; ++i) { + maxval = Inlines.MAX32(maxval, x[i]); + minval = Inlines.MIN32(minval, x[i]); + } + return Inlines.MAX32(maxval, 0 - minval); + } + + static short celt_maxabs32(short[] x, int x_ptr, int len) { + short maxval = 0; + short minval = 0; + for (int i = x_ptr; i < x_ptr + len; ++i) { + maxval = Inlines.MAX16(maxval, x[i]); + minval = Inlines.MIN16(minval, x[i]); + } + return Inlines.MAX(maxval, (short)(0 - minval)); + } + + static int FRAC_MUL16(int a, int b) { + return 16384 + (short)a * (short)b >> 15; + } + + static int isqrt32(long _val) { + int g = 0; + int bshift = Inlines.EC_ILOG(_val) - 1 >> 1; + int b = 1 << bshift; + do { + long t; + if ((t = (long)((g << 1) + b << bshift)) <= _val) { + g += b; + _val -= t; + } + b >>= 1; + } while (--bshift >= 0); + return g; + } + + static int celt_sqrt(int x) { + if (x == 0) { + return 0; + } + if (x >= 0x40000000) { + return Short.MAX_VALUE; + } + int k = (Inlines.celt_ilog2(x) >> 1) - 7; + x = Inlines.VSHR32(x, 2 * k); + short n = (short)(x - 32768); + int rt = Inlines.ADD16(sqrt_C[0], Inlines.MULT16_16_Q15(n, Inlines.ADD16(sqrt_C[1], Inlines.MULT16_16_Q15(n, Inlines.ADD16(sqrt_C[2], Inlines.MULT16_16_Q15(n, Inlines.ADD16(sqrt_C[3], Inlines.MULT16_16_Q15(n, sqrt_C[4])))))))); + rt = Inlines.VSHR32(rt, 7 - k); + return rt; + } + + static int celt_rcp(int x) { + Inlines.OpusAssert(x > 0, "celt_rcp() only defined for positive values"); + int i = Inlines.celt_ilog2(x); + int n = Inlines.VSHR32(x, i - 15) - 32768; + int r = Inlines.ADD16(30840, Inlines.MULT16_16_Q15(-15420, n)); + r = Inlines.SUB16(r, Inlines.MULT16_16_Q15(r, Inlines.ADD16(Inlines.MULT16_16_Q15(r, n), Inlines.ADD16(r, Short.MIN_VALUE)))); + r = Inlines.SUB16(r, Inlines.ADD16(1, Inlines.MULT16_16_Q15(r, Inlines.ADD16(Inlines.MULT16_16_Q15(r, n), Inlines.ADD16(r, Short.MIN_VALUE))))); + return Inlines.VSHR32(Inlines.EXTEND32(r), i - 16); + } + + static int celt_rsqrt_norm(int x) { + int n = x - 32768; + int r = Inlines.ADD16(23557, Inlines.MULT16_16_Q15(n, Inlines.ADD16(-13490, Inlines.MULT16_16_Q15(n, 6713)))); + int r2 = Inlines.MULT16_16_Q15(r, r); + int y = Inlines.SHL16(Inlines.SUB16(Inlines.ADD16(Inlines.MULT16_16_Q15(r2, n), r2), 16384), 1); + return Inlines.ADD16(r, Inlines.MULT16_16_Q15(r, Inlines.MULT16_16_Q15(y, Inlines.SUB16(Inlines.MULT16_16_Q15(y, 12288), 16384)))); + } + + static int frac_div32(int a, int b) { + int shift = Inlines.celt_ilog2(b) - 29; + a = Inlines.VSHR32(a, shift); + b = Inlines.VSHR32(b, shift); + int rcp = Inlines.ROUND16(Inlines.celt_rcp(Inlines.ROUND16(b, 16)), 3); + int result = Inlines.MULT16_32_Q15(rcp, a); + int rem = Inlines.PSHR32(a, 2) - Inlines.MULT32_32_Q31(result, b); + if ((result = Inlines.ADD32(result, Inlines.SHL32(Inlines.MULT16_32_Q15(rcp, rem), 2))) >= 0x20000000) { + return Integer.MAX_VALUE; + } + if (result <= -536870912) { + return -2147483647; + } + return Inlines.SHL32(result, 2); + } + + static int celt_log2(int x) { + if (x == 0) { + return -32767; + } + int i = Inlines.celt_ilog2(x); + int n = Inlines.VSHR32(x, i - 15) - 32768 - 16384; + int frac = Inlines.ADD16((int)log2_C0, Inlines.MULT16_16_Q15(n, Inlines.ADD16(15746, Inlines.MULT16_16_Q15(n, Inlines.ADD16(-5217, Inlines.MULT16_16_Q15(n, Inlines.ADD16(2545, Inlines.MULT16_16_Q15(n, -1401)))))))); + return Inlines.SHL16((short)(i - 13), 10) + Inlines.SHR16(frac, 4); + } + + static int celt_exp2_frac(int x) { + int frac = Inlines.SHL16(x, 4); + return Inlines.ADD16(16383, Inlines.MULT16_16_Q15(frac, Inlines.ADD16(22804, Inlines.MULT16_16_Q15(frac, Inlines.ADD16(14819, Inlines.MULT16_16_Q15(10204, frac)))))); + } + + static int celt_exp2(int x) { + int integer = Inlines.SHR16(x, 10); + if (integer > 14) { + return 0x7F000000; + } + if (integer < -15) { + return 0; + } + short frac = (short)Inlines.celt_exp2_frac((short)(x - Inlines.SHL16((short)integer, 10))); + return Inlines.VSHR32(Inlines.EXTEND32((int)frac), -integer - 2); + } + + static int celt_atan01(int x) { + return Inlines.MULT16_16_P15(x, Inlines.ADD32(Short.MAX_VALUE, Inlines.MULT16_16_P15(x, Inlines.ADD32(-21, Inlines.MULT16_16_P15(x, Inlines.ADD32(-11943, Inlines.MULT16_16_P15(4936, x))))))); + } + + static int celt_atan2p(int y, int x) { + if (y < x) { + int arg = Inlines.celt_div(Inlines.SHL32(Inlines.EXTEND32(y), 15), x); + if (arg >= Short.MAX_VALUE) { + arg = Short.MAX_VALUE; + } + return Inlines.SHR32(Inlines.celt_atan01(Inlines.EXTRACT16(arg)), 1); + } + int arg = Inlines.celt_div(Inlines.SHL32(Inlines.EXTEND32(x), 15), y); + if (arg >= Short.MAX_VALUE) { + arg = Short.MAX_VALUE; + } + return 25736 - Inlines.SHR16(Inlines.celt_atan01(Inlines.EXTRACT16(arg)), 1); + } + + static int celt_cos_norm(int x) { + if ((x &= 0x1FFFF) > Inlines.SHL32(Inlines.EXTEND32(1), 16)) { + x = Inlines.SUB32(Inlines.SHL32(Inlines.EXTEND32(1), 17), x); + } + if ((x & Short.MAX_VALUE) != 0) { + if (x < Inlines.SHL32(Inlines.EXTEND32(1), 15)) { + return Inlines._celt_cos_pi_2(Inlines.EXTRACT16(x)); + } + return Inlines.NEG32(Inlines._celt_cos_pi_2(Inlines.EXTRACT16(65536 - x))); + } + if ((x & 0xFFFF) != 0) { + return 0; + } + if ((x & 0x1FFFF) != 0) { + return -32767; + } + return Short.MAX_VALUE; + } + + static int _celt_cos_pi_2(int x) { + int x2 = Inlines.MULT16_16_P15(x, x); + return Inlines.ADD32(1, Inlines.MIN32(32766, Inlines.ADD32(Inlines.SUB16(Short.MAX_VALUE, x2), Inlines.MULT16_16_P15(x2, Inlines.ADD32(-7651, Inlines.MULT16_16_P15(x2, Inlines.ADD32(8277, Inlines.MULT16_16_P15(-626, x2)))))))); + } + + static short FLOAT2INT16(float x) { + if ((x *= 32768.0f) < -32768.0f) { + x = -32768.0f; + } + if (x > 32767.0f) { + x = 32767.0f; + } + return (short)x; + } + + static int silk_ROR32(int a32, int rot) { + int m = 0 - rot; + if (rot == 0) { + return a32; + } + if (rot < 0) { + return a32 << m | a32 >> 32 - m; + } + return a32 << 32 - rot | a32 >> rot; + } + + static int silk_MUL(int a32, int b32) { + int ret = a32 * b32; + return ret; + } + + static int silk_MLA(int a32, int b32, int c32) { + int ret = Inlines.silk_ADD32(a32, b32 * c32); + Inlines.OpusAssert((long)ret == (long)a32 + (long)b32 * (long)c32); + return ret; + } + + static int silk_SMULTT(int a32, int b32) { + return (a32 >> 16) * (b32 >> 16); + } + + static int silk_SMLATT(int a32, int b32, int c32) { + return Inlines.silk_ADD32(a32, (b32 >> 16) * (c32 >> 16)); + } + + static long silk_SMLALBB(long a64, short b16, short c16) { + return Inlines.silk_ADD64(a64, b16 * c16); + } + + static long silk_SMULL(int a32, int b32) { + return (long)a32 * (long)b32; + } + + static int silk_ADD32_ovflw(int a, int b) { + return (int)((long)a + (long)b); + } + + static int silk_ADD32_ovflw(long a, long b) { + return (int)(a + b); + } + + static int silk_SUB32_ovflw(int a, int b) { + return (int)((long)a - (long)b); + } + + static int silk_MLA_ovflw(int a32, int b32, int c32) { + return Inlines.silk_ADD32_ovflw((long)a32, (long)b32 * (long)c32); + } + + static int silk_SMLABB_ovflw(int a32, int b32, int c32) { + return Inlines.silk_ADD32_ovflw(a32, (short)b32 * (short)c32); + } + + static int silk_SMULBB(int a32, int b32) { + return (short)a32 * (short)b32; + } + + static int silk_SMULWB(int a32, int b32) { + return (int)((long)a32 * (long)((short)b32) >> 16); + } + + static int silk_SMLABB(int a32, int b32, int c32) { + return a32 + (short)b32 * (short)c32; + } + + static int silk_DIV32_16(int a32, int b32) { + return a32 / b32; + } + + static int silk_DIV32(int a32, int b32) { + return a32 / b32; + } + + static short silk_ADD16(short a, short b) { + short ret = (short)(a + b); + return ret; + } + + static int silk_ADD32(int a, int b) { + int ret = a + b; + return ret; + } + + static long silk_ADD64(long a, long b) { + long ret = a + b; + Inlines.OpusAssert(ret == Inlines.silk_ADD_SAT64(a, b)); + return ret; + } + + static short silk_SUB16(short a, short b) { + short ret = (short)(a - b); + Inlines.OpusAssert(ret == Inlines.silk_SUB_SAT16(a, b)); + return ret; + } + + static int silk_SUB32(int a, int b) { + int ret = a - b; + Inlines.OpusAssert(ret == Inlines.silk_SUB_SAT32(a, b)); + return ret; + } + + static long silk_SUB64(long a, long b) { + long ret = a - b; + Inlines.OpusAssert(ret == Inlines.silk_SUB_SAT64(a, b)); + return ret; + } + + static int silk_SAT8(int a) { + return a > 127 ? 127 : (a < -128 ? -128 : a); + } + + static int silk_SAT16(int a) { + return a > Short.MAX_VALUE ? Short.MAX_VALUE : (a < Short.MIN_VALUE ? Short.MIN_VALUE : a); + } + + static int silk_SAT32(long a) { + return a > Integer.MAX_VALUE ? Integer.MAX_VALUE : (a < Integer.MIN_VALUE ? Integer.MIN_VALUE : (int)a); + } + + static short silk_ADD_SAT16(short a16, short b16) { + short res = (short)Inlines.silk_SAT16(Inlines.silk_ADD32(a16, b16)); + Inlines.OpusAssert(res == Inlines.silk_SAT16(a16 + b16)); + return res; + } + + static int silk_ADD_SAT32(int a32, int b32) { + int res = ((long)a32 + (long)b32 & Integer.MIN_VALUE) == 0L ? ((a32 & b32 & Integer.MIN_VALUE) != 0 ? Integer.MIN_VALUE : a32 + b32) : (((a32 | b32) & Integer.MIN_VALUE) == 0 ? Integer.MAX_VALUE : a32 + b32); + Inlines.OpusAssert(res == Inlines.silk_SAT32((long)a32 + (long)b32)); + return res; + } + + static long silk_ADD_SAT64(long a64, long b64) { + long res = (a64 + b64 & Long.MIN_VALUE) == 0L ? ((a64 & b64 & Long.MIN_VALUE) != 0L ? Long.MIN_VALUE : a64 + b64) : (((a64 | b64) & Long.MIN_VALUE) == 0L ? Long.MAX_VALUE : a64 + b64); + return res; + } + + static short silk_SUB_SAT16(short a16, short b16) { + short res = (short)Inlines.silk_SAT16(Inlines.silk_SUB32(a16, b16)); + Inlines.OpusAssert(res == Inlines.silk_SAT16(a16 - b16)); + return res; + } + + static int silk_SUB_SAT32(int a32, int b32) { + int res = ((long)a32 - (long)b32 & Integer.MIN_VALUE) == 0L ? ((a32 & (b32 ^ Integer.MIN_VALUE) & Integer.MIN_VALUE) != 0 ? Integer.MIN_VALUE : a32 - b32) : (((a32 ^ Integer.MIN_VALUE) & b32 & Integer.MIN_VALUE) != 0 ? Integer.MAX_VALUE : a32 - b32); + Inlines.OpusAssert(res == Inlines.silk_SAT32((long)a32 - (long)b32)); + return res; + } + + static long silk_SUB_SAT64(long a64, long b64) { + long res = (a64 - b64 & Long.MIN_VALUE) == 0L ? ((a64 & (b64 ^ Long.MIN_VALUE) & Long.MIN_VALUE) != 0L ? Long.MIN_VALUE : a64 - b64) : (((a64 ^ Long.MIN_VALUE) & b64 & Long.MIN_VALUE) != 0L ? Long.MAX_VALUE : a64 - b64); + return res; + } + + static byte silk_ADD_POS_SAT8(byte a, byte b) { + return (byte)((a + b & 0x80) != 0 ? 127 : a + b); + } + + static short silk_ADD_POS_SAT16(short a, short b) { + return (short)((a + b & 0x8000) != 0 ? Short.MAX_VALUE : a + b); + } + + static int silk_ADD_POS_SAT32(int a, int b) { + return (a + b & Integer.MIN_VALUE) != 0 ? Integer.MAX_VALUE : a + b; + } + + static long silk_ADD_POS_SAT64(long a, long b) { + return (a + b & Long.MIN_VALUE) != 0L ? Long.MAX_VALUE : a + b; + } + + static byte silk_LSHIFT8(byte a, int shift) { + byte ret = (byte)(a << shift); + return ret; + } + + static short silk_LSHIFT16(short a, int shift) { + short ret = (short)(a << shift); + return ret; + } + + static int silk_LSHIFT32(int a, int shift) { + int ret = a << shift; + return ret; + } + + static long silk_LSHIFT64(long a, int shift) { + long ret = a << shift; + return ret; + } + + static int silk_LSHIFT(int a, int shift) { + int ret = a << shift; + return ret; + } + + static int silk_LSHIFT_ovflw(int a, int shift) { + return a << shift; + } + + static int silk_LSHIFT_SAT32(int a, int shift) { + return Inlines.silk_LSHIFT32(Inlines.silk_LIMIT(a, Inlines.silk_RSHIFT32(Integer.MIN_VALUE, shift), Inlines.silk_RSHIFT32(Integer.MAX_VALUE, shift)), shift); + } + + static byte silk_RSHIFT8(byte a, int shift) { + return (byte)(a >> shift); + } + + static short silk_RSHIFT16(short a, int shift) { + return (short)(a >> shift); + } + + static int silk_RSHIFT32(int a, int shift) { + return a >> shift; + } + + static int silk_RSHIFT(int a, int shift) { + return a >> shift; + } + + static long silk_RSHIFT64(long a, int shift) { + return a >> shift; + } + + static long silk_RSHIFT_uint(long a, int shift) { + return Inlines.CapToUInt32(a) >> shift; + } + + static int silk_ADD_LSHIFT(int a, int b, int shift) { + int ret = a + (b << shift); + return ret; + } + + static int silk_ADD_LSHIFT32(int a, int b, int shift) { + int ret = a + (b << shift); + return ret; + } + + static int silk_ADD_RSHIFT(int a, int b, int shift) { + int ret = a + (b >> shift); + return ret; + } + + static int silk_ADD_RSHIFT32(int a, int b, int shift) { + int ret = a + (b >> shift); + return ret; + } + + static long silk_ADD_RSHIFT_uint(long a, long b, int shift) { + long ret = Inlines.CapToUInt32(a + (Inlines.CapToUInt32(b) >> shift)); + return ret; + } + + static int silk_SUB_LSHIFT32(int a, int b, int shift) { + int ret = a - (b << shift); + return ret; + } + + static int silk_SUB_RSHIFT32(int a, int b, int shift) { + int ret = a - (b >> shift); + return ret; + } + + static int silk_RSHIFT_ROUND(int a, int shift) { + int ret = shift == 1 ? (a >> 1) + (a & 1) : (a >> shift - 1) + 1 >> 1; + return ret; + } + + static long silk_RSHIFT_ROUND64(long a, int shift) { + long ret = shift == 1 ? (a >> 1) + (a & 1L) : (a >> shift - 1) + 1L >> 1; + return ret; + } + + static int silk_min(int a, int b) { + return a < b ? a : b; + } + + static int silk_max(int a, int b) { + return a > b ? a : b; + } + + static float silk_min(float a, float b) { + return a < b ? a : b; + } + + static float silk_max(float a, float b) { + return a > b ? a : b; + } + + static int SILK_CONST(float number, int scale) { + return (int)((double)(number * (float)(1L << scale)) + 0.5); + } + + static int silk_min_int(int a, int b) { + return a < b ? a : b; + } + + static short silk_min_16(short a, short b) { + return a < b ? a : b; + } + + static int silk_min_32(int a, int b) { + return a < b ? a : b; + } + + static long silk_min_64(long a, long b) { + return a < b ? a : b; + } + + static int silk_max_int(int a, int b) { + return a > b ? a : b; + } + + static short silk_max_16(short a, short b) { + return a > b ? a : b; + } + + static int silk_max_32(int a, int b) { + return a > b ? a : b; + } + + static long silk_max_64(long a, long b) { + return a > b ? a : b; + } + + static float silk_LIMIT(float a, float limit1, float limit2) { + return limit1 > limit2 ? (a > limit1 ? limit1 : (a < limit2 ? limit2 : a)) : (a > limit2 ? limit2 : (a < limit1 ? limit1 : a)); + } + + static int silk_LIMIT(int a, int limit1, int limit2) { + return Inlines.silk_LIMIT_32(a, limit1, limit2); + } + + static int silk_LIMIT_int(int a, int limit1, int limit2) { + return Inlines.silk_LIMIT_32(a, limit1, limit2); + } + + static short silk_LIMIT_16(short a, short limit1, short limit2) { + return limit1 > limit2 ? (a > limit1 ? limit1 : (a < limit2 ? limit2 : a)) : (a > limit2 ? limit2 : (a < limit1 ? limit1 : a)); + } + + static int silk_LIMIT_32(int a, int limit1, int limit2) { + return limit1 > limit2 ? (a > limit1 ? limit1 : (a < limit2 ? limit2 : a)) : (a > limit2 ? limit2 : (a < limit1 ? limit1 : a)); + } + + static int silk_abs(int a) { + return a > 0 ? a : -a; + } + + static int silk_abs_int16(int a) { + return (a ^ a >> 15) - (a >> 15); + } + + static int silk_abs_int32(int a) { + return (a ^ a >> 31) - (a >> 31); + } + + static long silk_abs_int64(long a) { + return a > 0L ? a : -a; + } + + static long silk_sign(int a) { + return a > 0 ? 1L : (long)(a < 0 ? -1 : 0); + } + + static int silk_RAND(int seed) { + return Inlines.silk_MLA_ovflw(907633515, seed, 196314165); + } + + static int silk_SMMUL(int a32, int b32) { + return (int)Inlines.silk_RSHIFT64(Inlines.silk_SMULL(a32, b32), 32); + } + + static int silk_SMLAWT(int a32, int b32, int c32) { + int ret = a32 + (b32 >> 16) * (c32 >> 16) + ((b32 & 0xFFFF) * (c32 >> 16) >> 16); + return ret; + } + + static int silk_DIV32_varQ(int a32, int b32, int Qres) { + Inlines.OpusAssert(b32 != 0); + Inlines.OpusAssert(Qres >= 0); + int a_headrm = Inlines.silk_CLZ32(Inlines.silk_abs(a32)) - 1; + int a32_nrm = Inlines.silk_LSHIFT(a32, a_headrm); + int b_headrm = Inlines.silk_CLZ32(Inlines.silk_abs(b32)) - 1; + int b32_nrm = Inlines.silk_LSHIFT(b32, b_headrm); + int b32_inv = Inlines.silk_DIV32_16(0x1FFFFFFF, Inlines.silk_RSHIFT(b32_nrm, 16)); + int result = Inlines.silk_SMULWB(a32_nrm, b32_inv); + a32_nrm = Inlines.silk_SUB32_ovflw(a32_nrm, Inlines.silk_LSHIFT_ovflw(Inlines.silk_SMMUL(b32_nrm, result), 3)); + result = Inlines.silk_SMLAWB(result, a32_nrm, b32_inv); + int lshift = 29 + a_headrm - b_headrm - Qres; + if (lshift < 0) { + return Inlines.silk_LSHIFT_SAT32(result, -lshift); + } + if (lshift < 32) { + return Inlines.silk_RSHIFT(result, lshift); + } + return 0; + } + + static int silk_INVERSE32_varQ(int b32, int Qres) { + Inlines.OpusAssert(b32 != 0); + Inlines.OpusAssert(Qres > 0); + int b_headrm = Inlines.silk_CLZ32(Inlines.silk_abs(b32)) - 1; + int b32_nrm = Inlines.silk_LSHIFT(b32, b_headrm); + int b32_inv = Inlines.silk_DIV32_16(0x1FFFFFFF, (short)Inlines.silk_RSHIFT(b32_nrm, 16)); + int result = Inlines.silk_LSHIFT(b32_inv, 16); + int err_Q32 = Inlines.silk_LSHIFT(0x20000000 - Inlines.silk_SMULWB(b32_nrm, b32_inv), 3); + result = Inlines.silk_SMLAWW(result, err_Q32, b32_inv); + int lshift = 61 - b_headrm - Qres; + if (lshift <= 0) { + return Inlines.silk_LSHIFT_SAT32(result, -lshift); + } + if (lshift < 32) { + return Inlines.silk_RSHIFT(result, lshift); + } + return 0; + } + + static int silk_SMLAWB(int a32, int b32, int c32) { + int ret = a32 + Inlines.silk_SMULWB(b32, c32); + return ret; + } + + static int silk_SMULWT(int a32, int b32) { + return (a32 >> 16) * (b32 >> 16) + ((a32 & 0xFFFF) * (b32 >> 16) >> 16); + } + + static int silk_SMULBT(int a32, int b32) { + return (short)a32 * (b32 >> 16); + } + + static int silk_SMLABT(int a32, int b32, int c32) { + return a32 + (short)b32 * (c32 >> 16); + } + + static long silk_SMLAL(long a64, int b32, int c32) { + return Inlines.silk_ADD64(a64, (long)b32 * (long)c32); + } + + static int MatrixGetPointer(int row, int column, int N) { + return row * N + column; + } + + static int MatrixGet(int[] Matrix_base_adr, int row, int column, int N) { + return Matrix_base_adr[row * N + column]; + } + + static short MatrixGet(short[] Matrix_base_adr, int row, int column, int N) { + return Matrix_base_adr[row * N + column]; + } + + static PitchAnalysisCore.silk_pe_stage3_vals MatrixGet(PitchAnalysisCore.silk_pe_stage3_vals[] Matrix_base_adr, int row, int column, int N) { + return Matrix_base_adr[row * N + column]; + } + + static int MatrixGet(int[] Matrix_base_adr, int matrix_ptr, int row, int column, int N) { + return Matrix_base_adr[matrix_ptr + row * N + column]; + } + + static short MatrixGet(short[] Matrix_base_adr, int matrix_ptr, int row, int column, int N) { + return Matrix_base_adr[matrix_ptr + row * N + column]; + } + + static void MatrixSet(int[] Matrix_base_adr, int matrix_ptr, int row, int column, int N, int value) { + Matrix_base_adr[matrix_ptr + row * N + column] = value; + } + + static void MatrixSet(short[] Matrix_base_adr, int matrix_ptr, int row, int column, int N, short value) { + Matrix_base_adr[matrix_ptr + row * N + column] = value; + } + + static void MatrixSet(int[] Matrix_base_adr, int row, int column, int N, int value) { + Matrix_base_adr[row * N + column] = value; + } + + static void MatrixSet(short[] Matrix_base_adr, int row, int column, int N, short value) { + Matrix_base_adr[row * N + column] = value; + } + + static int silk_SMULWW(int a32, int b32) { + return Inlines.silk_MLA(Inlines.silk_SMULWB(a32, b32), a32, Inlines.silk_RSHIFT_ROUND(b32, 16)); + } + + static int silk_SMLAWW(int a32, int b32, int c32) { + return Inlines.silk_MLA(Inlines.silk_SMLAWB(a32, b32, c32), b32, Inlines.silk_RSHIFT_ROUND(c32, 16)); + } + + static int silk_CLZ64(long input) { + int in_upper = (int)Inlines.silk_RSHIFT64(input, 32); + if (in_upper == 0) { + return 32 + Inlines.silk_CLZ32((int)input); + } + return Inlines.silk_CLZ32(in_upper); + } + + static int silk_CLZ32(int in32) { + return in32 == 0 ? 32 : 32 - Inlines.EC_ILOG(in32); + } + + static void silk_CLZ_FRAC(int input, BoxedValueInt lz, BoxedValueInt frac_Q7) { + int lzeros; + lz.Val = lzeros = Inlines.silk_CLZ32(input); + frac_Q7.Val = Inlines.silk_ROR32(input, 24 - lzeros) & 0x7F; + } + + static int silk_SQRT_APPROX(int x) { + if (x <= 0) { + return 0; + } + BoxedValueInt boxed_lz = new BoxedValueInt(0); + BoxedValueInt boxed_frac_Q7 = new BoxedValueInt(0); + Inlines.silk_CLZ_FRAC(x, boxed_lz, boxed_frac_Q7); + int lz = boxed_lz.Val; + int frac_Q7 = boxed_frac_Q7.Val; + int y = (lz & 1) != 0 ? 32768 : 46214; + y >>= Inlines.silk_RSHIFT(lz, 1); + y = Inlines.silk_SMLAWB(y, y, Inlines.silk_SMULBB(213, frac_Q7)); + return y; + } + + static int MUL32_FRAC_Q(int a32, int b32, int Q) { + return (int)Inlines.silk_RSHIFT_ROUND64(Inlines.silk_SMULL(a32, b32), Q); + } + + static int silk_lin2log(int inLin) { + BoxedValueInt lz = new BoxedValueInt(0); + BoxedValueInt frac_Q7 = new BoxedValueInt(0); + Inlines.silk_CLZ_FRAC(inLin, lz, frac_Q7); + return Inlines.silk_LSHIFT(31 - lz.Val, 7) + Inlines.silk_SMLAWB(frac_Q7.Val, Inlines.silk_MUL(frac_Q7.Val, 128 - frac_Q7.Val), 179); + } + + static int silk_log2lin(int inLog_Q7) { + if (inLog_Q7 < 0) { + return 0; + } + if (inLog_Q7 >= 3967) { + return Integer.MAX_VALUE; + } + int output = Inlines.silk_LSHIFT(1, Inlines.silk_RSHIFT(inLog_Q7, 7)); + int frac_Q7 = inLog_Q7 & 0x7F; + output = inLog_Q7 < 2048 ? Inlines.silk_ADD_RSHIFT32(output, Inlines.silk_MUL(output, Inlines.silk_SMLAWB(frac_Q7, Inlines.silk_SMULBB(frac_Q7, 128 - frac_Q7), -174)), 7) : Inlines.silk_MLA(output, Inlines.silk_RSHIFT(output, 7), Inlines.silk_SMLAWB(frac_Q7, Inlines.silk_SMULBB(frac_Q7, 128 - frac_Q7), -174)); + return output; + } + + static void silk_interpolate(short[] xi, short[] x0, short[] x1, int ifact_Q2, int d) { + Inlines.OpusAssert(ifact_Q2 >= 0); + Inlines.OpusAssert(ifact_Q2 <= 4); + for (int i = 0; i < d; ++i) { + xi[i] = (short)Inlines.silk_ADD_RSHIFT(x0[i], Inlines.silk_SMULBB(x1[i] - x0[i], ifact_Q2), 2); + } + } + + static int silk_inner_prod_aligned_scale(short[] inVec1, short[] inVec2, int scale, int len) { + int sum = 0; + for (int i = 0; i < len; ++i) { + sum = Inlines.silk_ADD_RSHIFT32(sum, Inlines.silk_SMULBB(inVec1[i], inVec2[i]), scale); + } + return sum; + } + + static void silk_scale_copy_vector16(short[] data_out, int data_out_ptr, short[] data_in, int data_in_ptr, int gain_Q16, int dataSize) { + for (int i = 0; i < dataSize; ++i) { + data_out[data_out_ptr + i] = (short)Inlines.silk_SMULWB(gain_Q16, data_in[data_in_ptr + i]); + } + } + + static void silk_scale_vector32_Q26_lshift_18(int[] data1, int data1_ptr, int gain_Q26, int dataSize) { + for (int i = data1_ptr; i < data1_ptr + dataSize; ++i) { + data1[i] = (int)Inlines.silk_RSHIFT64(Inlines.silk_SMULL(data1[i], gain_Q26), 8); + } + } + + static int silk_inner_prod(short[] inVec1, int inVec1_ptr, short[] inVec2, int inVec2_ptr, int len) { + int xy = 0; + for (int i = 0; i < len; ++i) { + xy = Inlines.MAC16_16(xy, inVec1[inVec1_ptr + i], inVec2[inVec2_ptr + i]); + } + return xy; + } + + static int silk_inner_prod_self(short[] inVec, int inVec_ptr, int len) { + int xy = 0; + for (int i = inVec_ptr; i < inVec_ptr + len; ++i) { + xy = Inlines.MAC16_16(xy, inVec[i], inVec[i]); + } + return xy; + } + + static long silk_inner_prod16_aligned_64(short[] inVec1, int inVec1_ptr, short[] inVec2, int inVec2_ptr, int len) { + long sum = 0L; + for (int i = 0; i < len; ++i) { + sum = Inlines.silk_SMLALBB(sum, inVec1[inVec1_ptr + i], inVec2[inVec2_ptr + i]); + } + return sum; + } + + static long EC_MINI(long a, long b) { + return a + (b - a & (long)(b < a ? -1 : 0)); + } + + static int EC_ILOG(long x) { + if (x == 0L) { + return 1; + } + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + long y = x - (x >> 1 & 0x55555555L); + y = (y >> 2 & 0x33333333L) + (y & 0x33333333L); + y = (y >> 4) + y & 0xF0F0F0FL; + y += y >> 8; + y += y >> 16; + return (int)(y &= 0x3FL); + } + + static int abs(int a) { + if (a < 0) { + return 0 - a; + } + return a; + } + + static int SignedByteToUnsignedInt(byte b) { + return b & 0xFF; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/K2A.java b/source/decompiled/de/maxhenkel/voicechat/concentus/K2A.java new file mode 100644 index 0000000..6908a7c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/K2A.java @@ -0,0 +1,40 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; + +class K2A { + K2A() { + } + + static void silk_k2a(int[] A_Q24, short[] rc_Q15, int order) { + int[] Atmp = new int[16]; + for (int k = 0; k < order; ++k) { + int n; + for (n = 0; n < k; ++n) { + Atmp[n] = A_Q24[n]; + } + for (n = 0; n < k; ++n) { + A_Q24[n] = Inlines.silk_SMLAWB(A_Q24[n], Inlines.silk_LSHIFT(Atmp[k - n - 1], 1), rc_Q15[k]); + } + A_Q24[k] = 0 - Inlines.silk_LSHIFT(rc_Q15[k], 9); + } + } + + static void silk_k2a_Q16(int[] A_Q24, int[] rc_Q16, int order) { + int[] Atmp = new int[16]; + for (int k = 0; k < order; ++k) { + int n; + for (n = 0; n < k; ++n) { + Atmp[n] = A_Q24[n]; + } + for (n = 0; n < k; ++n) { + A_Q24[n] = Inlines.silk_SMLAWW(A_Q24[n], Atmp[k - n - 1], rc_Q16[k]); + } + A_Q24[k] = 0 - Inlines.silk_LSHIFT(rc_Q16[k], 8); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Kernels.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Kernels.java new file mode 100644 index 0000000..8e69f8a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Kernels.java @@ -0,0 +1,267 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Inlines; + +class Kernels { + Kernels() { + } + + static void celt_fir(short[] x, int x_ptr, short[] num, short[] y, int y_ptr, int N, int ord, short[] mem) { + int i; + short[] rnum = new short[ord]; + short[] local_x = new short[N + ord]; + for (i = 0; i < ord; ++i) { + rnum[i] = num[ord - i - 1]; + } + for (i = 0; i < ord; ++i) { + local_x[i] = mem[ord - i - 1]; + } + for (i = 0; i < N; ++i) { + local_x[i + ord] = x[x_ptr + i]; + } + for (i = 0; i < ord; ++i) { + mem[i] = x[x_ptr + N - i - 1]; + } + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + for (i = 0; i < N - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + Kernels.xcorr_kernel(rnum, 0, local_x, i, sum0, sum1, sum2, sum3, ord); + y[y_ptr + i] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum0.Val, 12))); + y[y_ptr + i + 1] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 1]), Inlines.PSHR32(sum1.Val, 12))); + y[y_ptr + i + 2] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 2]), Inlines.PSHR32(sum2.Val, 12))); + y[y_ptr + i + 3] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 3]), Inlines.PSHR32(sum3.Val, 12))); + } + while (i < N) { + int sum = 0; + for (int j = 0; j < ord; ++j) { + sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]); + } + y[y_ptr + i] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum, 12))); + ++i; + } + } + + static void celt_fir(int[] x, int x_ptr, int[] num, int num_ptr, int[] y, int y_ptr, int N, int ord, int[] mem) { + int i; + int[] rnum = new int[ord]; + int[] local_x = new int[N + ord]; + for (i = 0; i < ord; ++i) { + rnum[i] = num[num_ptr + ord - i - 1]; + } + for (i = 0; i < ord; ++i) { + local_x[i] = mem[ord - i - 1]; + } + for (i = 0; i < N; ++i) { + local_x[i + ord] = x[x_ptr + i]; + } + for (i = 0; i < ord; ++i) { + mem[i] = x[x_ptr + N - i - 1]; + } + BoxedValueInt sum0 = new BoxedValueInt(0); + BoxedValueInt sum1 = new BoxedValueInt(0); + BoxedValueInt sum2 = new BoxedValueInt(0); + BoxedValueInt sum3 = new BoxedValueInt(0); + for (i = 0; i < N - 3; i += 4) { + sum0.Val = 0; + sum1.Val = 0; + sum2.Val = 0; + sum3.Val = 0; + Kernels.xcorr_kernel(rnum, local_x, i, sum0, sum1, sum2, sum3, ord); + y[y_ptr + i] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum0.Val, 12))); + y[y_ptr + i + 1] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 1]), Inlines.PSHR32(sum1.Val, 12))); + y[y_ptr + i + 2] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 2]), Inlines.PSHR32(sum2.Val, 12))); + y[y_ptr + i + 3] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 3]), Inlines.PSHR32(sum3.Val, 12))); + } + while (i < N) { + int sum = 0; + for (int j = 0; j < ord; ++j) { + sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]); + } + y[y_ptr + i] = Inlines.SATURATE16(Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum, 12))); + ++i; + } + } + + static void xcorr_kernel(short[] x, int x_ptr, short[] y, int y_ptr, BoxedValueInt _sum0, BoxedValueInt _sum1, BoxedValueInt _sum2, BoxedValueInt _sum3, int len) { + short tmp; + int j; + int sum0 = _sum0.Val; + int sum1 = _sum1.Val; + int sum2 = _sum2.Val; + int sum3 = _sum3.Val; + Inlines.OpusAssert(len >= 3); + short y_3 = 0; + short y_0 = y[y_ptr++]; + short y_1 = y[y_ptr++]; + short y_2 = y[y_ptr++]; + for (j = 0; j < len - 3; j += 4) { + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + tmp = x[x_ptr++]; + y_2 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_3); + sum1 = Inlines.MAC16_16(sum1, tmp, y_0); + sum2 = Inlines.MAC16_16(sum2, tmp, y_1); + sum3 = Inlines.MAC16_16(sum3, tmp, y_2); + } + if (j++ < len) { + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + } + if (j++ < len) { + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + } + if (j < len) { + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + } + _sum0.Val = sum0; + _sum1.Val = sum1; + _sum2.Val = sum2; + _sum3.Val = sum3; + } + + static void xcorr_kernel(int[] x, int[] y, int y_ptr, BoxedValueInt _sum0, BoxedValueInt _sum1, BoxedValueInt _sum2, BoxedValueInt _sum3, int len) { + int tmp; + int j; + int sum0 = _sum0.Val; + int sum1 = _sum1.Val; + int sum2 = _sum2.Val; + int sum3 = _sum3.Val; + int x_ptr = 0; + Inlines.OpusAssert(len >= 3); + int y_3 = 0; + int y_0 = y[y_ptr++]; + int y_1 = y[y_ptr++]; + int y_2 = y[y_ptr++]; + for (j = 0; j < len - 3; j += 4) { + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + tmp = x[x_ptr++]; + y_2 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_3); + sum1 = Inlines.MAC16_16(sum1, tmp, y_0); + sum2 = Inlines.MAC16_16(sum2, tmp, y_1); + sum3 = Inlines.MAC16_16(sum3, tmp, y_2); + } + if (j++ < len) { + tmp = x[x_ptr++]; + y_3 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_0); + sum1 = Inlines.MAC16_16(sum1, tmp, y_1); + sum2 = Inlines.MAC16_16(sum2, tmp, y_2); + sum3 = Inlines.MAC16_16(sum3, tmp, y_3); + } + if (j++ < len) { + tmp = x[x_ptr++]; + y_0 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_1); + sum1 = Inlines.MAC16_16(sum1, tmp, y_2); + sum2 = Inlines.MAC16_16(sum2, tmp, y_3); + sum3 = Inlines.MAC16_16(sum3, tmp, y_0); + } + if (j < len) { + tmp = x[x_ptr++]; + y_1 = y[y_ptr++]; + sum0 = Inlines.MAC16_16(sum0, tmp, y_2); + sum1 = Inlines.MAC16_16(sum1, tmp, y_3); + sum2 = Inlines.MAC16_16(sum2, tmp, y_0); + sum3 = Inlines.MAC16_16(sum3, tmp, y_1); + } + _sum0.Val = sum0; + _sum1.Val = sum1; + _sum2.Val = sum2; + _sum3.Val = sum3; + } + + static int celt_inner_prod(short[] x, int x_ptr, short[] y, int y_ptr, int N) { + int xy = 0; + for (int i = 0; i < N; ++i) { + xy = Inlines.MAC16_16(xy, x[x_ptr + i], y[y_ptr + i]); + } + return xy; + } + + static int celt_inner_prod(short[] x, short[] y, int y_ptr, int N) { + int xy = 0; + for (int i = 0; i < N; ++i) { + xy = Inlines.MAC16_16(xy, x[i], y[y_ptr + i]); + } + return xy; + } + + static int celt_inner_prod(int[] x, int x_ptr, int[] y, int y_ptr, int N) { + int xy = 0; + for (int i = 0; i < N; ++i) { + xy = Inlines.MAC16_16(xy, x[x_ptr + i], y[y_ptr + i]); + } + return xy; + } + + static void dual_inner_prod(int[] x, int x_ptr, int[] y01, int y01_ptr, int[] y02, int y02_ptr, int N, BoxedValueInt xy1, BoxedValueInt xy2) { + int xy01 = 0; + int xy02 = 0; + for (int i = 0; i < N; ++i) { + xy01 = Inlines.MAC16_16(xy01, x[x_ptr + i], y01[y01_ptr + i]); + xy02 = Inlines.MAC16_16(xy02, x[x_ptr + i], y02[y02_ptr + i]); + } + xy1.Val = xy01; + xy2.Val = xy02; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/KissFFT.java b/source/decompiled/de/maxhenkel/voicechat/concentus/KissFFT.java new file mode 100644 index 0000000..43f4ea5 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/KissFFT.java @@ -0,0 +1,296 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.FFTState; +import de.maxhenkel.voicechat.concentus.Inlines; + +class KissFFT { + static final int MAXFACTORS = 8; + + KissFFT() { + } + + static int S_MUL(int a, int b) { + return Inlines.MULT16_32_Q15(b, a); + } + + static int S_MUL(int a, short b) { + return Inlines.MULT16_32_Q15(b, a); + } + + static int HALF_OF(int x) { + return x >> 1; + } + + static void kf_bfly2(int[] Fout, int fout_ptr, int m, int N) { + short tw = 23170; + Inlines.OpusAssert(m == 4); + for (int i = 0; i < N; ++i) { + int Fout2 = fout_ptr + 8; + int t_r = Fout[Fout2 + 0]; + int t_i = Fout[Fout2 + 1]; + Fout[Fout2 + 0] = Fout[fout_ptr + 0] - t_r; + Fout[Fout2 + 1] = Fout[fout_ptr + 1] - t_i; + int n = fout_ptr + 0; + Fout[n] = Fout[n] + t_r; + int n2 = fout_ptr + 1; + Fout[n2] = Fout[n2] + t_i; + t_r = KissFFT.S_MUL(Fout[Fout2 + 2] + Fout[Fout2 + 3], tw); + t_i = KissFFT.S_MUL(Fout[Fout2 + 3] - Fout[Fout2 + 2], tw); + Fout[Fout2 + 2] = Fout[fout_ptr + 2] - t_r; + Fout[Fout2 + 3] = Fout[fout_ptr + 3] - t_i; + int n3 = fout_ptr + 2; + Fout[n3] = Fout[n3] + t_r; + int n4 = fout_ptr + 3; + Fout[n4] = Fout[n4] + t_i; + t_r = Fout[Fout2 + 5]; + t_i = 0 - Fout[Fout2 + 4]; + Fout[Fout2 + 4] = Fout[fout_ptr + 4] - t_r; + Fout[Fout2 + 5] = Fout[fout_ptr + 5] - t_i; + int n5 = fout_ptr + 4; + Fout[n5] = Fout[n5] + t_r; + int n6 = fout_ptr + 5; + Fout[n6] = Fout[n6] + t_i; + t_r = KissFFT.S_MUL(Fout[Fout2 + 7] - Fout[Fout2 + 6], tw); + t_i = KissFFT.S_MUL(0 - Fout[Fout2 + 7] - Fout[Fout2 + 6], tw); + Fout[Fout2 + 6] = Fout[fout_ptr + 6] - t_r; + Fout[Fout2 + 7] = Fout[fout_ptr + 7] - t_i; + int n7 = fout_ptr + 6; + Fout[n7] = Fout[n7] + t_r; + int n8 = fout_ptr + 7; + Fout[n8] = Fout[n8] + t_i; + fout_ptr += 16; + } + } + + static void kf_bfly4(int[] Fout, int fout_ptr, int fstride, FFTState st, int m, int N, int mm) { + if (m == 1) { + for (int i = 0; i < N; ++i) { + int scratch0 = Fout[fout_ptr + 0] - Fout[fout_ptr + 4]; + int scratch1 = Fout[fout_ptr + 1] - Fout[fout_ptr + 5]; + int n = fout_ptr + 0; + Fout[n] = Fout[n] + Fout[fout_ptr + 4]; + int n2 = fout_ptr + 1; + Fout[n2] = Fout[n2] + Fout[fout_ptr + 5]; + int scratch2 = Fout[fout_ptr + 2] + Fout[fout_ptr + 6]; + int scratch3 = Fout[fout_ptr + 3] + Fout[fout_ptr + 7]; + Fout[fout_ptr + 4] = Fout[fout_ptr + 0] - scratch2; + Fout[fout_ptr + 5] = Fout[fout_ptr + 1] - scratch3; + int n3 = fout_ptr + 0; + Fout[n3] = Fout[n3] + scratch2; + int n4 = fout_ptr + 1; + Fout[n4] = Fout[n4] + scratch3; + scratch2 = Fout[fout_ptr + 2] - Fout[fout_ptr + 6]; + scratch3 = Fout[fout_ptr + 3] - Fout[fout_ptr + 7]; + Fout[fout_ptr + 2] = scratch0 + scratch3; + Fout[fout_ptr + 3] = scratch1 - scratch2; + Fout[fout_ptr + 6] = scratch0 - scratch3; + Fout[fout_ptr + 7] = scratch1 + scratch2; + fout_ptr += 8; + } + } else { + int Fout_beg = fout_ptr; + for (int i = 0; i < N; ++i) { + fout_ptr = Fout_beg + 2 * i * mm; + int m1 = fout_ptr + 2 * m; + int m2 = fout_ptr + 4 * m; + int m3 = fout_ptr + 6 * m; + int tw1 = 0; + int tw2 = 0; + int tw3 = 0; + for (int j = 0; j < m; ++j) { + int scratch0 = KissFFT.S_MUL(Fout[m1], st.twiddles[tw1]) - KissFFT.S_MUL(Fout[m1 + 1], st.twiddles[tw1 + 1]); + int scratch1 = KissFFT.S_MUL(Fout[m1], st.twiddles[tw1 + 1]) + KissFFT.S_MUL(Fout[m1 + 1], st.twiddles[tw1]); + int scratch2 = KissFFT.S_MUL(Fout[m2], st.twiddles[tw2]) - KissFFT.S_MUL(Fout[m2 + 1], st.twiddles[tw2 + 1]); + int scratch3 = KissFFT.S_MUL(Fout[m2], st.twiddles[tw2 + 1]) + KissFFT.S_MUL(Fout[m2 + 1], st.twiddles[tw2]); + int scratch4 = KissFFT.S_MUL(Fout[m3], st.twiddles[tw3]) - KissFFT.S_MUL(Fout[m3 + 1], st.twiddles[tw3 + 1]); + int scratch5 = KissFFT.S_MUL(Fout[m3], st.twiddles[tw3 + 1]) + KissFFT.S_MUL(Fout[m3 + 1], st.twiddles[tw3]); + int scratch10 = Fout[fout_ptr] - scratch2; + int scratch11 = Fout[fout_ptr + 1] - scratch3; + int n = fout_ptr; + Fout[n] = Fout[n] + scratch2; + int n5 = fout_ptr + 1; + Fout[n5] = Fout[n5] + scratch3; + int scratch6 = scratch0 + scratch4; + int scratch7 = scratch1 + scratch5; + int scratch8 = scratch0 - scratch4; + int scratch9 = scratch1 - scratch5; + Fout[m2] = Fout[fout_ptr] - scratch6; + Fout[m2 + 1] = Fout[fout_ptr + 1] - scratch7; + tw1 += fstride * 2; + tw2 += fstride * 4; + tw3 += fstride * 6; + int n6 = fout_ptr; + Fout[n6] = Fout[n6] + scratch6; + int n7 = fout_ptr + 1; + Fout[n7] = Fout[n7] + scratch7; + Fout[m1] = scratch10 + scratch9; + Fout[m1 + 1] = scratch11 - scratch8; + Fout[m3] = scratch10 - scratch9; + Fout[m3 + 1] = scratch11 + scratch8; + fout_ptr += 2; + m1 += 2; + m2 += 2; + m3 += 2; + } + } + } + } + + static void kf_bfly3(int[] Fout, int fout_ptr, int fstride, FFTState st, int m, int N, int mm) { + int m1 = 2 * m; + int m2 = 4 * m; + int Fout_beg = fout_ptr; + for (int i = 0; i < N; ++i) { + fout_ptr = Fout_beg + 2 * i * mm; + int tw2 = 0; + int tw1 = 0; + int k = m; + do { + int scratch2 = KissFFT.S_MUL(Fout[fout_ptr + m1], st.twiddles[tw1]) - KissFFT.S_MUL(Fout[fout_ptr + m1 + 1], st.twiddles[tw1 + 1]); + int scratch3 = KissFFT.S_MUL(Fout[fout_ptr + m1], st.twiddles[tw1 + 1]) + KissFFT.S_MUL(Fout[fout_ptr + m1 + 1], st.twiddles[tw1]); + int scratch4 = KissFFT.S_MUL(Fout[fout_ptr + m2], st.twiddles[tw2]) - KissFFT.S_MUL(Fout[fout_ptr + m2 + 1], st.twiddles[tw2 + 1]); + int scratch5 = KissFFT.S_MUL(Fout[fout_ptr + m2], st.twiddles[tw2 + 1]) + KissFFT.S_MUL(Fout[fout_ptr + m2 + 1], st.twiddles[tw2]); + int scratch6 = scratch2 + scratch4; + int scratch7 = scratch3 + scratch5; + int scratch0 = scratch2 - scratch4; + int scratch1 = scratch3 - scratch5; + tw1 += fstride * 2; + tw2 += fstride * 4; + Fout[fout_ptr + m1] = Fout[fout_ptr + 0] - KissFFT.HALF_OF(scratch6); + Fout[fout_ptr + m1 + 1] = Fout[fout_ptr + 1] - KissFFT.HALF_OF(scratch7); + scratch0 = KissFFT.S_MUL(scratch0, -28378); + scratch1 = KissFFT.S_MUL(scratch1, -28378); + int n = fout_ptr + 0; + Fout[n] = Fout[n] + scratch6; + int n2 = fout_ptr + 1; + Fout[n2] = Fout[n2] + scratch7; + Fout[fout_ptr + m2] = Fout[fout_ptr + m1] + scratch1; + Fout[fout_ptr + m2 + 1] = Fout[fout_ptr + m1 + 1] - scratch0; + int n3 = fout_ptr + m1; + Fout[n3] = Fout[n3] - scratch1; + int n4 = fout_ptr + m1 + 1; + Fout[n4] = Fout[n4] + scratch0; + fout_ptr += 2; + } while (--k != 0); + } + } + + static void kf_bfly5(int[] Fout, int fout_ptr, int fstride, FFTState st, int m, int N, int mm) { + int Fout_beg = fout_ptr; + short ya_r = 10126; + short ya_i = -31164; + short yb_r = -26510; + short yb_i = -19261; + for (int i = 0; i < N; ++i) { + int tw4 = 0; + int tw3 = 0; + int tw2 = 0; + int tw1 = 0; + int Fout0 = fout_ptr = Fout_beg + 2 * i * mm; + int Fout1 = fout_ptr + 2 * m; + int Fout2 = fout_ptr + 4 * m; + int Fout3 = fout_ptr + 6 * m; + int Fout4 = fout_ptr + 8 * m; + for (int u = 0; u < m; ++u) { + int scratch0 = Fout[Fout0 + 0]; + int scratch1 = Fout[Fout0 + 1]; + int scratch2 = KissFFT.S_MUL(Fout[Fout1 + 0], st.twiddles[tw1]) - KissFFT.S_MUL(Fout[Fout1 + 1], st.twiddles[tw1 + 1]); + int scratch3 = KissFFT.S_MUL(Fout[Fout1 + 0], st.twiddles[tw1 + 1]) + KissFFT.S_MUL(Fout[Fout1 + 1], st.twiddles[tw1]); + int scratch4 = KissFFT.S_MUL(Fout[Fout2 + 0], st.twiddles[tw2]) - KissFFT.S_MUL(Fout[Fout2 + 1], st.twiddles[tw2 + 1]); + int scratch5 = KissFFT.S_MUL(Fout[Fout2 + 0], st.twiddles[tw2 + 1]) + KissFFT.S_MUL(Fout[Fout2 + 1], st.twiddles[tw2]); + int scratch6 = KissFFT.S_MUL(Fout[Fout3 + 0], st.twiddles[tw3]) - KissFFT.S_MUL(Fout[Fout3 + 1], st.twiddles[tw3 + 1]); + int scratch7 = KissFFT.S_MUL(Fout[Fout3 + 0], st.twiddles[tw3 + 1]) + KissFFT.S_MUL(Fout[Fout3 + 1], st.twiddles[tw3]); + int scratch8 = KissFFT.S_MUL(Fout[Fout4 + 0], st.twiddles[tw4]) - KissFFT.S_MUL(Fout[Fout4 + 1], st.twiddles[tw4 + 1]); + int scratch9 = KissFFT.S_MUL(Fout[Fout4 + 0], st.twiddles[tw4 + 1]) + KissFFT.S_MUL(Fout[Fout4 + 1], st.twiddles[tw4]); + tw1 += 2 * fstride; + tw2 += 4 * fstride; + tw3 += 6 * fstride; + tw4 += 8 * fstride; + int scratch14 = scratch2 + scratch8; + int scratch15 = scratch3 + scratch9; + int scratch20 = scratch2 - scratch8; + int scratch21 = scratch3 - scratch9; + int scratch16 = scratch4 + scratch6; + int scratch17 = scratch5 + scratch7; + int scratch18 = scratch4 - scratch6; + int scratch19 = scratch5 - scratch7; + int n = Fout0 + 0; + Fout[n] = Fout[n] + (scratch14 + scratch16); + int n2 = Fout0 + 1; + Fout[n2] = Fout[n2] + (scratch15 + scratch17); + int scratch10 = scratch0 + KissFFT.S_MUL(scratch14, ya_r) + KissFFT.S_MUL(scratch16, yb_r); + int scratch11 = scratch1 + KissFFT.S_MUL(scratch15, ya_r) + KissFFT.S_MUL(scratch17, yb_r); + int scratch12 = KissFFT.S_MUL(scratch21, ya_i) + KissFFT.S_MUL(scratch19, yb_i); + int scratch13 = 0 - KissFFT.S_MUL(scratch20, ya_i) - KissFFT.S_MUL(scratch18, yb_i); + Fout[Fout1 + 0] = scratch10 - scratch12; + Fout[Fout1 + 1] = scratch11 - scratch13; + Fout[Fout4 + 0] = scratch10 + scratch12; + Fout[Fout4 + 1] = scratch11 + scratch13; + int scratch22 = scratch0 + KissFFT.S_MUL(scratch14, yb_r) + KissFFT.S_MUL(scratch16, ya_r); + int scratch23 = scratch1 + KissFFT.S_MUL(scratch15, yb_r) + KissFFT.S_MUL(scratch17, ya_r); + int scratch24 = 0 - KissFFT.S_MUL(scratch21, yb_i) + KissFFT.S_MUL(scratch19, ya_i); + int scratch25 = KissFFT.S_MUL(scratch20, yb_i) - KissFFT.S_MUL(scratch18, ya_i); + Fout[Fout2 + 0] = scratch22 + scratch24; + Fout[Fout2 + 1] = scratch23 + scratch25; + Fout[Fout3 + 0] = scratch22 - scratch24; + Fout[Fout3 + 1] = scratch23 - scratch25; + Fout0 += 2; + Fout1 += 2; + Fout2 += 2; + Fout3 += 2; + Fout4 += 2; + } + } + } + + static void opus_fft_impl(FFTState st, int[] fout, int fout_ptr) { + short m; + int[] fstride = new int[8]; + int shift = st.shift > 0 ? st.shift : 0; + fstride[0] = 1; + int L = 0; + do { + short p = st.factors[2 * L]; + m = st.factors[2 * L + 1]; + fstride[L + 1] = fstride[L] * p; + ++L; + } while (m != 1); + m = st.factors[2 * L - 1]; + for (int i = L - 1; i >= 0; --i) { + short m2 = i != 0 ? st.factors[2 * i - 1] : (short)1; + switch (st.factors[2 * i]) { + case 2: { + KissFFT.kf_bfly2(fout, fout_ptr, m, fstride[i]); + break; + } + case 4: { + KissFFT.kf_bfly4(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2); + break; + } + case 3: { + KissFFT.kf_bfly3(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2); + break; + } + case 5: { + KissFFT.kf_bfly5(fout, fout_ptr, fstride[i] << shift, st, m, fstride[i], m2); + } + } + m = m2; + } + } + + static void opus_fft(FFTState st, int[] fin, int[] fout) { + int scale_shift = st.scale_shift - 1; + short scale = st.scale; + Inlines.OpusAssert(fin != fout, "In-place FFT not supported"); + for (int i = 0; i < st.nfft; ++i) { + fout[2 * st.bitrev[i]] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[2 * i]), scale_shift); + fout[2 * st.bitrev[i] + 1] = Inlines.SHR32(Inlines.MULT16_32_Q16(scale, fin[2 * i + 1]), scale_shift); + } + KissFFT.opus_fft_impl(st, fout, 0); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/LPCInversePredGain.java b/source/decompiled/de/maxhenkel/voicechat/concentus/LPCInversePredGain.java new file mode 100644 index 0000000..d38e295 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/LPCInversePredGain.java @@ -0,0 +1,74 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Inlines; + +class LPCInversePredGain { + private static final int QA = 24; + private static final int A_LIMIT = 0xFFEF9E; + + LPCInversePredGain() { + } + + static int LPC_inverse_pred_gain_QA(int[][] A_QA, int order) { + int rc_mult1_Q30; + int rc_Q31; + int[] Anew_QA = A_QA[order & 1]; + int invGain_Q30 = 0x40000000; + for (int k = order - 1; k > 0; --k) { + if (Anew_QA[k] > 0xFFEF9E || Anew_QA[k] < -16773022) { + return 0; + } + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[k], 7); + rc_mult1_Q30 = 0x40000000 - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + Inlines.OpusAssert(rc_mult1_Q30 > 32768); + Inlines.OpusAssert(rc_mult1_Q30 <= 0x40000000); + int mult2Q = 32 - Inlines.silk_CLZ32(Inlines.silk_abs(rc_mult1_Q30)); + int rc_mult2 = Inlines.silk_INVERSE32_varQ(rc_mult1_Q30, mult2Q + 30); + invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2); + Inlines.OpusAssert(invGain_Q30 >= 0); + Inlines.OpusAssert(invGain_Q30 <= 0x40000000); + int[] Aold_QA = Anew_QA; + Anew_QA = A_QA[k & 1]; + for (int n = 0; n < k; ++n) { + int tmp_QA = Aold_QA[n] - Inlines.MUL32_FRAC_Q(Aold_QA[k - n - 1], rc_Q31, 31); + Anew_QA[n] = Inlines.MUL32_FRAC_Q(tmp_QA, rc_mult2, mult2Q); + } + } + if (Anew_QA[0] > 0xFFEF9E || Anew_QA[0] < -16773022) { + return 0; + } + rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[0], 7); + rc_mult1_Q30 = 0x40000000 - Inlines.silk_SMMUL(rc_Q31, rc_Q31); + Inlines.OpusAssert((invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2)) >= 0); + Inlines.OpusAssert(invGain_Q30 <= 0x40000000); + return invGain_Q30; + } + + static int silk_LPC_inverse_pred_gain(short[] A_Q12, int order) { + int[][] Atmp_QA = Arrays.InitTwoDimensionalArrayInt(2, 16); + int DC_resp = 0; + int[] Anew_QA = Atmp_QA[order & 1]; + for (int k = 0; k < order; ++k) { + DC_resp += A_Q12[k]; + Anew_QA[k] = Inlines.silk_LSHIFT32(A_Q12[k], 12); + } + if (DC_resp >= 4096) { + return 0; + } + return LPCInversePredGain.LPC_inverse_pred_gain_QA(Atmp_QA, order); + } + + static int silk_LPC_inverse_pred_gain_Q24(int[] A_Q24, int order) { + int[][] Atmp_QA = Arrays.InitTwoDimensionalArrayInt(2, 16); + int[] Anew_QA = Atmp_QA[order & 1]; + for (int k = 0; k < order; ++k) { + Anew_QA[k] = Inlines.silk_RSHIFT32(A_Q24[k], 0); + } + return LPCInversePredGain.LPC_inverse_pred_gain_QA(Atmp_QA, order); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/LTPAnalysisFilter.java b/source/decompiled/de/maxhenkel/voicechat/concentus/LTPAnalysisFilter.java new file mode 100644 index 0000000..3c5fcb5 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/LTPAnalysisFilter.java @@ -0,0 +1,41 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; + +class LTPAnalysisFilter { + LTPAnalysisFilter() { + } + + static void silk_LTP_analysis_filter(short[] LTP_res, short[] x, int x_ptr, short[] LTPCoef_Q14, int[] pitchL, int[] invGains_Q16, int subfr_length, int nb_subfr, int pre_length) { + short[] Btmp_Q14 = new short[5]; + int x_ptr2 = x_ptr; + int LTP_res_ptr = 0; + for (int k = 0; k < nb_subfr; ++k) { + int x_lag_ptr = x_ptr2 - pitchL[k]; + Btmp_Q14[0] = LTPCoef_Q14[k * 5]; + Btmp_Q14[1] = LTPCoef_Q14[k * 5 + 1]; + Btmp_Q14[2] = LTPCoef_Q14[k * 5 + 2]; + Btmp_Q14[3] = LTPCoef_Q14[k * 5 + 3]; + Btmp_Q14[4] = LTPCoef_Q14[k * 5 + 4]; + for (int i = 0; i < subfr_length + pre_length; ++i) { + int LTP_res_ptri = LTP_res_ptr + i; + LTP_res[LTP_res_ptri] = x[x_ptr2 + i]; + int LTP_est = Inlines.silk_SMULBB(x[x_lag_ptr + 2], Btmp_Q14[0]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr + 1], Btmp_Q14[1]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr], Btmp_Q14[2]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr - 1], Btmp_Q14[3]); + LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr - 2], Btmp_Q14[4]); + LTP_est = Inlines.silk_RSHIFT_ROUND(LTP_est, 14); + LTP_res[LTP_res_ptri] = (short)Inlines.silk_SAT16(x[x_ptr2 + i] - LTP_est); + LTP_res[LTP_res_ptri] = (short)Inlines.silk_SMULWB(invGains_Q16[k], LTP_res[LTP_res_ptri]); + ++x_lag_ptr; + } + LTP_res_ptr += subfr_length + pre_length; + x_ptr2 += subfr_length; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/LTPScaleControl.java b/source/decompiled/de/maxhenkel/voicechat/concentus/LTPScaleControl.java new file mode 100644 index 0000000..323130c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/LTPScaleControl.java @@ -0,0 +1,25 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkEncoderControl; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class LTPScaleControl { + LTPScaleControl() { + } + + static void silk_LTP_scale_ctrl(SilkChannelEncoder psEnc, SilkEncoderControl psEncCtrl, int condCoding) { + if (condCoding == 0) { + int round_loss = psEnc.PacketLoss_perc + psEnc.nFramesPerPacket; + psEnc.indices.LTP_scaleIndex = (byte)Inlines.silk_LIMIT(Inlines.silk_SMULWB(Inlines.silk_SMULBB(round_loss, psEncCtrl.LTPredCodGain_Q7), 51), 0, 2); + } else { + psEnc.indices.LTP_scaleIndex = 0; + } + psEncCtrl.LTP_scale_Q14 = SilkTables.silk_LTPScales_table_Q14[psEnc.indices.LTP_scaleIndex]; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Laplace.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Laplace.java new file mode 100644 index 0000000..897592b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Laplace.java @@ -0,0 +1,85 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; + +class Laplace { + private static final int LAPLACE_LOG_MINP = 0; + private static final long LAPLACE_MINP = 1L; + private static final int LAPLACE_NMIN = 16; + + Laplace() { + } + + static long ec_laplace_get_freq1(long fs0, int decay) { + long ft = Inlines.CapToUInt32(32736L - fs0); + return Inlines.CapToUInt32(ft * (long)(16384 - decay)) >> 15; + } + + static void ec_laplace_encode(EntropyCoder enc, BoxedValueInt value, long fs, int decay) { + int val = value.Val; + long fl = 0L; + if (val != 0) { + int i; + int s = 0 - (val < 0 ? 1 : 0); + val = val + s ^ s; + fl = fs; + fs = Laplace.ec_laplace_get_freq1(fs, decay); + for (i = 1; fs > 0L && i < val; ++i) { + fl = Inlines.CapToUInt32(fl + (fs *= 2L) + 2L); + fs = Inlines.CapToUInt32(fs * (long)decay >> 15); + } + if (fs == 0L) { + int ndi_max = (int)(32768L - fl + 1L - 1L) >> 0; + ndi_max = ndi_max - s >> 1; + int di = Inlines.IMIN(val - i, ndi_max - 1); + fl = Inlines.CapToUInt32(fl + (long)(2 * di + 1 + s) * 1L); + fs = Inlines.IMIN(1L, 32768L - fl); + value.Val = i + di + s ^ s; + } else { + fl += Inlines.CapToUInt32(++fs & (long)(~s)); + } + Inlines.OpusAssert(fl + fs <= 32768L); + Inlines.OpusAssert(fs > 0L); + } + enc.encode_bin(fl, fl + fs, 15); + } + + static int ec_laplace_decode(EntropyCoder dec, long fs, int decay) { + int val = 0; + long fm = dec.decode_bin(15); + long fl = 0L; + if (fm >= fs) { + ++val; + fl = fs; + fs = Laplace.ec_laplace_get_freq1(fs, decay) + 1L; + while (fs > 1L && fm >= fl + 2L * fs) { + fl = Inlines.CapToUInt32(fl + (fs *= 2L)); + fs = Inlines.CapToUInt32((fs - 2L) * (long)decay >> 15); + ++fs; + ++val; + } + if (fs <= 1L) { + int di = (int)(fm - fl) >> 1; + val += di; + fl = Inlines.CapToUInt32(fl + Inlines.CapToUInt32((long)(2 * di) * 1L)); + } + if (fm < fl + fs) { + val = -val; + } else { + fl = Inlines.CapToUInt32(fl + fs); + } + } + Inlines.OpusAssert(fl < 32768L); + Inlines.OpusAssert(fs > 0L); + Inlines.OpusAssert(fl <= fm); + Inlines.OpusAssert(fm < Inlines.IMIN(fl + fs, 32768L)); + dec.dec_update(fl, Inlines.IMIN(fl + fs, 32768L), 32768L); + return val; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/LinearAlgebra.java b/source/decompiled/de/maxhenkel/voicechat/concentus/LinearAlgebra.java new file mode 100644 index 0000000..a6d7042 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/LinearAlgebra.java @@ -0,0 +1,106 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; + +class LinearAlgebra { + LinearAlgebra() { + } + + static void silk_solve_LDL(int[] A, int A_ptr, int M, int[] b, int[] x_Q16) { + Inlines.OpusAssert(M <= 16); + int[] L_Q16 = new int[M * M]; + int[] Y = new int[16]; + int[] inv_D = new int[32]; + LinearAlgebra.silk_LDL_factorize(A, A_ptr, M, L_Q16, inv_D); + LinearAlgebra.silk_LS_SolveFirst(L_Q16, M, b, Y); + LinearAlgebra.silk_LS_divide_Q16(Y, inv_D, M); + LinearAlgebra.silk_LS_SolveLast(L_Q16, M, Y, x_Q16); + } + + private static void silk_LDL_factorize(int[] A, int A_ptr, int M, int[] L_Q16, int[] inv_D) { + int[] v_Q0 = new int[M]; + int[] D_Q0 = new int[M]; + Inlines.OpusAssert(M <= 16); + boolean status = true; + int diag_min_value = Inlines.silk_max_32(Inlines.silk_SMMUL(Inlines.silk_ADD_SAT32(A[A_ptr], A[A_ptr + Inlines.silk_SMULBB(M, M) - 1]), 21475), 512); + block0: for (int loop_count = 0; loop_count < M && status; ++loop_count) { + status = false; + for (int j = 0; j < M; ++j) { + int i; + int[] scratch1 = L_Q16; + int scratch1_ptr = Inlines.MatrixGetPointer(j, 0, M); + int tmp_32 = 0; + for (i = 0; i < j; ++i) { + v_Q0[i] = Inlines.silk_SMULWW(D_Q0[i], scratch1[scratch1_ptr + i]); + tmp_32 = Inlines.silk_SMLAWW(tmp_32, v_Q0[i], scratch1[scratch1_ptr + i]); + } + tmp_32 = Inlines.silk_SUB32(Inlines.MatrixGet(A, A_ptr, j, j, M), tmp_32); + if (tmp_32 < diag_min_value) { + tmp_32 = Inlines.silk_SUB32(Inlines.silk_SMULBB(loop_count + 1, diag_min_value), tmp_32); + for (i = 0; i < M; ++i) { + Inlines.MatrixSet(A, A_ptr, i, i, M, Inlines.silk_ADD32(Inlines.MatrixGet(A, A_ptr, i, i, M), tmp_32)); + } + status = true; + continue block0; + } + D_Q0[j] = tmp_32; + int one_div_diag_Q36 = Inlines.silk_INVERSE32_varQ(tmp_32, 36); + int one_div_diag_Q40 = Inlines.silk_LSHIFT(one_div_diag_Q36, 4); + int err = Inlines.silk_SUB32(0x1000000, Inlines.silk_SMULWW(tmp_32, one_div_diag_Q40)); + int one_div_diag_Q48 = Inlines.silk_SMULWW(err, one_div_diag_Q40); + inv_D[j * 2 + 0] = one_div_diag_Q36; + inv_D[j * 2 + 1] = one_div_diag_Q48; + Inlines.MatrixSet(L_Q16, j, j, M, 65536); + scratch1 = A; + scratch1_ptr = Inlines.MatrixGetPointer(j, 0, M) + A_ptr; + int[] scratch2 = L_Q16; + int scratch2_ptr = Inlines.MatrixGetPointer(j + 1, 0, M); + for (i = j + 1; i < M; ++i) { + tmp_32 = 0; + for (int k = 0; k < j; ++k) { + tmp_32 = Inlines.silk_SMLAWW(tmp_32, v_Q0[k], scratch2[scratch2_ptr + k]); + } + tmp_32 = Inlines.silk_SUB32(scratch1[scratch1_ptr + i], tmp_32); + Inlines.MatrixSet(L_Q16, i, j, M, Inlines.silk_ADD32(Inlines.silk_SMMUL(tmp_32, one_div_diag_Q48), Inlines.silk_RSHIFT(Inlines.silk_SMULWW(tmp_32, one_div_diag_Q36), 4))); + scratch2_ptr += M; + } + } + } + Inlines.OpusAssert(!status); + } + + private static void silk_LS_divide_Q16(int[] T, int[] inv_D, int M) { + for (int i = 0; i < M; ++i) { + int one_div_diag_Q36 = inv_D[i * 2 + 0]; + int one_div_diag_Q48 = inv_D[i * 2 + 1]; + int tmp_32 = T[i]; + T[i] = Inlines.silk_ADD32(Inlines.silk_SMMUL(tmp_32, one_div_diag_Q48), Inlines.silk_RSHIFT(Inlines.silk_SMULWW(tmp_32, one_div_diag_Q36), 4)); + } + } + + private static void silk_LS_SolveFirst(int[] L_Q16, int M, int[] b, int[] x_Q16) { + for (int i = 0; i < M; ++i) { + int ptr32 = Inlines.MatrixGetPointer(i, 0, M); + int tmp_32 = 0; + for (int j = 0; j < i; ++j) { + tmp_32 = Inlines.silk_SMLAWW(tmp_32, L_Q16[ptr32 + j], x_Q16[j]); + } + x_Q16[i] = Inlines.silk_SUB32(b[i], tmp_32); + } + } + + private static void silk_LS_SolveLast(int[] L_Q16, int M, int[] b, int[] x_Q16) { + for (int i = M - 1; i >= 0; --i) { + int ptr32 = Inlines.MatrixGetPointer(0, i, M); + int tmp_32 = 0; + for (int j = M - 1; j > i; --j) { + tmp_32 = Inlines.silk_SMLAWW(tmp_32, L_Q16[ptr32 + Inlines.silk_SMULBB(j, M)], x_Q16[j]); + } + x_Q16[i] = Inlines.silk_SUB32(b[i], tmp_32); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/MDCT.java b/source/decompiled/de/maxhenkel/voicechat/concentus/MDCT.java new file mode 100644 index 0000000..2f2708e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/MDCT.java @@ -0,0 +1,150 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.FFTState; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.KissFFT; +import de.maxhenkel.voicechat.concentus.MDCTLookup; + +class MDCT { + MDCT() { + } + + static void clt_mdct_forward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr, int[] window, int overlap, int shift, int stride) { + int i; + FFTState st = l.kfft[shift]; + int trig_ptr = 0; + int scale_shift = st.scale_shift - 1; + short scale = st.scale; + int N = l.n; + short[] trig = l.trig; + for (i = 0; i < shift; ++i) { + trig_ptr += (N >>= 1); + } + int N2 = N >> 1; + int N4 = N >> 2; + int[] f = new int[N2]; + int[] f2 = new int[N4 * 2]; + int xp1 = input_ptr + (overlap >> 1); + int xp2 = input_ptr + N2 - 1 + (overlap >> 1); + int yp = 0; + int wp1 = overlap >> 1; + int wp2 = (overlap >> 1) - 1; + for (i = 0; i < overlap + 3 >> 2; ++i) { + f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp1 + N2]) + Inlines.MULT16_32_Q15(window[wp1], input[xp2]); + f[yp++] = Inlines.MULT16_32_Q15(window[wp1], input[xp1]) - Inlines.MULT16_32_Q15(window[wp2], input[xp2 - N2]); + xp1 += 2; + xp2 -= 2; + wp1 += 2; + wp2 -= 2; + } + wp1 = 0; + wp2 = overlap - 1; + while (i < N4 - (overlap + 3 >> 2)) { + f[yp++] = input[xp2]; + f[yp++] = input[xp1]; + xp1 += 2; + xp2 -= 2; + ++i; + } + while (i < N4) { + f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp2]) - Inlines.MULT16_32_Q15(window[wp1], input[xp1 - N2]); + f[yp++] = Inlines.MULT16_32_Q15(window[wp2], input[xp1]) + Inlines.MULT16_32_Q15(window[wp1], input[xp2 + N2]); + xp1 += 2; + xp2 -= 2; + wp1 += 2; + wp2 -= 2; + ++i; + } + int yp2 = 0; + int t = trig_ptr; + for (i = 0; i < N4; ++i) { + short t0 = trig[t + i]; + short t1 = trig[t + N4 + i]; + int re = f[yp2++]; + int im = f[yp2++]; + int yr = KissFFT.S_MUL(re, t0) - KissFFT.S_MUL(im, t1); + int yi = KissFFT.S_MUL(im, t0) + KissFFT.S_MUL(re, t1); + f2[2 * st.bitrev[i]] = Inlines.PSHR32(Inlines.MULT16_32_Q16((int)scale, yr), scale_shift); + f2[2 * st.bitrev[i] + 1] = Inlines.PSHR32(Inlines.MULT16_32_Q16((int)scale, yi), scale_shift); + } + KissFFT.opus_fft_impl(st, f2, 0); + int fp = 0; + int yp1 = output_ptr; + int yp22 = output_ptr + stride * (N2 - 1); + int t2 = trig_ptr; + for (i = 0; i < N4; ++i) { + int yr = KissFFT.S_MUL(f2[fp + 1], trig[t2 + N4 + i]) - KissFFT.S_MUL(f2[fp], trig[t2 + i]); + int yi = KissFFT.S_MUL(f2[fp], trig[t2 + N4 + i]) + KissFFT.S_MUL(f2[fp + 1], trig[t2 + i]); + output[yp1] = yr; + output[yp22] = yi; + fp += 2; + yp1 += 2 * stride; + yp22 -= 2 * stride; + } + } + + static void clt_mdct_backward(MDCTLookup l, int[] input, int input_ptr, int[] output, int output_ptr, int[] window, int overlap, int shift, int stride) { + int i; + int trig = 0; + int N = l.n; + for (i = 0; i < shift; ++i) { + trig += (N >>= 1); + } + int N2 = N >> 1; + int N4 = N >> 2; + int xp2 = input_ptr + stride * (N2 - 1); + int yp = output_ptr + (overlap >> 1); + short[] bitrev = l.kfft[shift].bitrev; + int bitrav_ptr = 0; + for (i = 0; i < N4; ++i) { + short rev = bitrev[bitrav_ptr++]; + int ypr = yp + 2 * rev; + output[ypr + 1] = KissFFT.S_MUL(input[xp2], l.trig[trig + i]) + KissFFT.S_MUL(input[input_ptr], l.trig[trig + N4 + i]); + output[ypr] = KissFFT.S_MUL(input[input_ptr], l.trig[trig + i]) - KissFFT.S_MUL(input[xp2], l.trig[trig + N4 + i]); + input_ptr += 2 * stride; + xp2 -= 2 * stride; + } + KissFFT.opus_fft_impl(l.kfft[shift], output, output_ptr + (overlap >> 1)); + int yp0 = output_ptr + (overlap >> 1); + int yp1 = output_ptr + (overlap >> 1) + N2 - 2; + int t = trig; + int tN4m1 = t + N4 - 1; + int tN2m1 = t + N2 - 1; + for (i = 0; i < N4 + 1 >> 1; ++i) { + int re = output[yp0 + 1]; + int im = output[yp0]; + short t0 = l.trig[t + i]; + short t1 = l.trig[t + N4 + i]; + int yr = KissFFT.S_MUL(re, t0) + KissFFT.S_MUL(im, t1); + int yi = KissFFT.S_MUL(re, t1) - KissFFT.S_MUL(im, t0); + re = output[yp1 + 1]; + im = output[yp1]; + output[yp0] = yr; + output[yp1 + 1] = yi; + t0 = l.trig[tN4m1 - i]; + t1 = l.trig[tN2m1 - i]; + yr = KissFFT.S_MUL(re, t0) + KissFFT.S_MUL(im, t1); + yi = KissFFT.S_MUL(re, t1) - KissFFT.S_MUL(im, t0); + output[yp1] = yr; + output[yp0 + 1] = yi; + yp0 += 2; + yp1 -= 2; + } + int xp1 = output_ptr + overlap - 1; + yp1 = output_ptr; + int wp1 = 0; + int wp2 = overlap - 1; + for (i = 0; i < overlap / 2; ++i) { + int x1 = output[xp1]; + int x2 = output[yp1]; + output[yp1++] = Inlines.MULT16_32_Q15(window[wp2], x2) - Inlines.MULT16_32_Q15(window[wp1], x1); + output[xp1--] = Inlines.MULT16_32_Q15(window[wp1], x2) + Inlines.MULT16_32_Q15(window[wp2], x1); + ++wp1; + --wp2; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/MDCTLookup.java b/source/decompiled/de/maxhenkel/voicechat/concentus/MDCTLookup.java new file mode 100644 index 0000000..09b9865 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/MDCTLookup.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.FFTState; + +class MDCTLookup { + int n = 0; + int maxshift = 0; + FFTState[] kfft = new FFTState[4]; + short[] trig = null; + + MDCTLookup() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/MLPState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/MLPState.java new file mode 100644 index 0000000..ba0c328 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/MLPState.java @@ -0,0 +1,14 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class MLPState { + int layers; + int[] topo; + float[] weights; + + MLPState() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/MultiLayerPerceptron.java b/source/decompiled/de/maxhenkel/voicechat/concentus/MultiLayerPerceptron.java new file mode 100644 index 0000000..9d49615 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/MultiLayerPerceptron.java @@ -0,0 +1,61 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.MLPState; +import de.maxhenkel.voicechat.concentus.OpusTables; + +class MultiLayerPerceptron { + private static final int MAX_NEURONS = 100; + + MultiLayerPerceptron() { + } + + static float tansig_approx(float x) { + float sign = 1.0f; + if (!(x < 8.0f)) { + return 1.0f; + } + if (!(x > -8.0f)) { + return -1.0f; + } + if (x < 0.0f) { + x = -x; + sign = -1.0f; + } + int i = (int)Math.floor(0.5f + 25.0f * x); + float y = OpusTables.tansig_table[i]; + float dy = 1.0f - y * y; + y += (x -= 0.04f * (float)i) * dy * (1.0f - y * x); + return sign * y; + } + + static void mlp_process(MLPState m, float[] input, float[] output) { + int k; + float sum; + int j; + float[] hidden = new float[100]; + float[] W = m.weights; + int W_ptr = 0; + for (j = 0; j < m.topo[1]; ++j) { + sum = W[W_ptr]; + ++W_ptr; + for (k = 0; k < m.topo[0]; ++k) { + sum += input[k] * W[W_ptr]; + ++W_ptr; + } + hidden[j] = MultiLayerPerceptron.tansig_approx(sum); + } + for (j = 0; j < m.topo[2]; ++j) { + sum = W[W_ptr]; + ++W_ptr; + for (k = 0; k < m.topo[1]; ++k) { + sum += hidden[k] * W[W_ptr]; + ++W_ptr; + } + output[j] = MultiLayerPerceptron.tansig_approx(sum); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/NLSF.java b/source/decompiled/de/maxhenkel/voicechat/concentus/NLSF.java new file mode 100644 index 0000000..debf93b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/NLSF.java @@ -0,0 +1,625 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.NLSFCodebook; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkTables; +import de.maxhenkel.voicechat.concentus.Sort; + +class NLSF { + private static final int MAX_STABILIZE_LOOPS = 20; + private static final int QA = 16; + private static final int BIN_DIV_STEPS_A2NLSF = 3; + private static final int MAX_ITERATIONS_A2NLSF = 30; + private static final byte[] ordering16 = new byte[]{0, 15, 8, 7, 4, 11, 12, 3, 2, 13, 10, 5, 6, 9, 14, 1}; + private static final byte[] ordering10 = new byte[]{0, 9, 6, 3, 4, 5, 8, 1, 2, 7}; + + NLSF() { + } + + static void silk_NLSF_VQ(int[] err_Q26, short[] in_Q15, short[] pCB_Q8, int K, int LPC_order) { + int pCB_idx = 0; + Inlines.OpusAssert(err_Q26 != null); + Inlines.OpusAssert(LPC_order <= 16); + Inlines.OpusAssert((LPC_order & 1) == 0); + for (int i = 0; i < K; ++i) { + int sum_error_Q26 = 0; + for (int m = 0; m < LPC_order; m += 2) { + int diff_Q15 = Inlines.silk_SUB_LSHIFT32(in_Q15[m], pCB_Q8[pCB_idx++], 7); + int sum_error_Q30 = Inlines.silk_SMULBB(diff_Q15, diff_Q15); + Inlines.OpusAssert((sum_error_Q26 = Inlines.silk_ADD_RSHIFT32(sum_error_Q26, sum_error_Q30 = Inlines.silk_SMLABB(sum_error_Q30, diff_Q15 = Inlines.silk_SUB_LSHIFT32(in_Q15[m + 1], pCB_Q8[pCB_idx++], 7), diff_Q15), 4)) >= 0); + Inlines.OpusAssert(sum_error_Q30 >= 0); + } + err_Q26[i] = sum_error_Q26; + } + } + + static void silk_NLSF_VQ_weights_laroia(short[] pNLSFW_Q_OUT, short[] pNLSF_Q15, int D) { + Inlines.OpusAssert(pNLSFW_Q_OUT != null); + Inlines.OpusAssert(D > 0); + Inlines.OpusAssert((D & 1) == 0); + int tmp1_int = Inlines.silk_max_int(pNLSF_Q15[0], 1); + tmp1_int = Inlines.silk_DIV32(131072, tmp1_int); + int tmp2_int = Inlines.silk_max_int(pNLSF_Q15[1] - pNLSF_Q15[0], 1); + tmp2_int = Inlines.silk_DIV32(131072, tmp2_int); + pNLSFW_Q_OUT[0] = (short)Inlines.silk_min_int(tmp1_int + tmp2_int, Short.MAX_VALUE); + Inlines.OpusAssert(pNLSFW_Q_OUT[0] > 0); + for (int k = 1; k < D - 1; k += 2) { + tmp1_int = Inlines.silk_max_int(pNLSF_Q15[k + 1] - pNLSF_Q15[k], 1); + tmp1_int = Inlines.silk_DIV32(131072, tmp1_int); + pNLSFW_Q_OUT[k] = (short)Inlines.silk_min_int(tmp1_int + tmp2_int, Short.MAX_VALUE); + Inlines.OpusAssert(pNLSFW_Q_OUT[k] > 0); + tmp2_int = Inlines.silk_max_int(pNLSF_Q15[k + 2] - pNLSF_Q15[k + 1], 1); + tmp2_int = Inlines.silk_DIV32(131072, tmp2_int); + pNLSFW_Q_OUT[k + 1] = (short)Inlines.silk_min_int(tmp1_int + tmp2_int, Short.MAX_VALUE); + Inlines.OpusAssert(pNLSFW_Q_OUT[k + 1] > 0); + } + tmp1_int = Inlines.silk_max_int(32768 - pNLSF_Q15[D - 1], 1); + tmp1_int = Inlines.silk_DIV32(131072, tmp1_int); + pNLSFW_Q_OUT[D - 1] = (short)Inlines.silk_min_int(tmp1_int + tmp2_int, Short.MAX_VALUE); + Inlines.OpusAssert(pNLSFW_Q_OUT[D - 1] > 0); + } + + static void silk_NLSF_residual_dequant(short[] x_Q10, byte[] indices, int indices_ptr, short[] pred_coef_Q8, int quant_step_size_Q16, short order) { + short out_Q10 = 0; + for (int i = order - 1; i >= 0; --i) { + int pred_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(out_Q10, pred_coef_Q8[i]), 8); + out_Q10 = Inlines.silk_LSHIFT16(indices[indices_ptr + i], 10); + if (out_Q10 > 0) { + out_Q10 = Inlines.silk_SUB16(out_Q10, (short)102); + } else if (out_Q10 < 0) { + out_Q10 = Inlines.silk_ADD16(out_Q10, (short)102); + } + x_Q10[i] = out_Q10 = (short)Inlines.silk_SMLAWB(pred_Q10, out_Q10, quant_step_size_Q16); + } + } + + static void silk_NLSF_unpack(short[] ec_ix, short[] pred_Q8, NLSFCodebook psNLSF_CB, int CB1_index) { + short[] ec_sel = psNLSF_CB.ec_sel; + int ec_sel_ptr = CB1_index * psNLSF_CB.order / 2; + for (int i = 0; i < psNLSF_CB.order; i += 2) { + short entry = ec_sel[ec_sel_ptr]; + ++ec_sel_ptr; + ec_ix[i] = (short)Inlines.silk_SMULBB(Inlines.silk_RSHIFT(entry, 1) & 7, 9); + pred_Q8[i] = psNLSF_CB.pred_Q8[i + (entry & 1) * (psNLSF_CB.order - 1)]; + ec_ix[i + 1] = (short)Inlines.silk_SMULBB(Inlines.silk_RSHIFT(entry, 5) & 7, 9); + pred_Q8[i + 1] = psNLSF_CB.pred_Q8[i + (Inlines.silk_RSHIFT(entry, 4) & 1) * (psNLSF_CB.order - 1) + 1]; + } + } + + static void silk_NLSF_stabilize(short[] NLSF_Q15, short[] NDeltaMin_Q15, int L) { + int i; + int loops; + int I = 0; + Inlines.OpusAssert(NDeltaMin_Q15[L] >= 1); + for (loops = 0; loops < 20; ++loops) { + int k; + int diff_Q15; + int min_diff_Q15 = NLSF_Q15[0] - NDeltaMin_Q15[0]; + I = 0; + for (i = 1; i <= L - 1; ++i) { + diff_Q15 = NLSF_Q15[i] - (NLSF_Q15[i - 1] + NDeltaMin_Q15[i]); + if (diff_Q15 >= min_diff_Q15) continue; + min_diff_Q15 = diff_Q15; + I = i; + } + diff_Q15 = 32768 - (NLSF_Q15[L - 1] + NDeltaMin_Q15[L]); + if (diff_Q15 < min_diff_Q15) { + min_diff_Q15 = diff_Q15; + I = L; + } + if (min_diff_Q15 >= 0) { + return; + } + if (I == 0) { + NLSF_Q15[0] = NDeltaMin_Q15[0]; + continue; + } + if (I == L) { + NLSF_Q15[L - 1] = (short)(32768 - NDeltaMin_Q15[L]); + continue; + } + int min_center_Q15 = 0; + for (k = 0; k < I; ++k) { + min_center_Q15 += NDeltaMin_Q15[k]; + } + min_center_Q15 += Inlines.silk_RSHIFT(NDeltaMin_Q15[I], 1); + int max_center_Q15 = 32768; + for (k = L; k > I; --k) { + max_center_Q15 -= NDeltaMin_Q15[k]; + } + short center_freq_Q15 = (short)Inlines.silk_LIMIT_32(Inlines.silk_RSHIFT_ROUND(NLSF_Q15[I - 1] + NLSF_Q15[I], 1), min_center_Q15, max_center_Q15 -= Inlines.silk_RSHIFT(NDeltaMin_Q15[I], 1)); + NLSF_Q15[I - 1] = (short)(center_freq_Q15 - Inlines.silk_RSHIFT(NDeltaMin_Q15[I], 1)); + NLSF_Q15[I] = (short)(NLSF_Q15[I - 1] + NDeltaMin_Q15[I]); + } + if (loops == 20) { + Sort.silk_insertion_sort_increasing_all_values_int16(NLSF_Q15, L); + NLSF_Q15[0] = (short)Inlines.silk_max_int(NLSF_Q15[0], NDeltaMin_Q15[0]); + for (i = 1; i < L; ++i) { + NLSF_Q15[i] = (short)Inlines.silk_max_int(NLSF_Q15[i], NLSF_Q15[i - 1] + NDeltaMin_Q15[i]); + } + NLSF_Q15[L - 1] = (short)Inlines.silk_min_int(NLSF_Q15[L - 1], 32768 - NDeltaMin_Q15[L]); + for (i = L - 2; i >= 0; --i) { + NLSF_Q15[i] = (short)Inlines.silk_min_int(NLSF_Q15[i], NLSF_Q15[i + 1] - NDeltaMin_Q15[i + 1]); + } + } + } + + static void silk_NLSF_decode(short[] pNLSF_Q15, byte[] NLSFIndices, NLSFCodebook psNLSF_CB) { + int i; + short[] pred_Q8 = new short[psNLSF_CB.order]; + short[] ec_ix = new short[psNLSF_CB.order]; + short[] res_Q10 = new short[psNLSF_CB.order]; + short[] W_tmp_QW = new short[psNLSF_CB.order]; + short[] pCB = psNLSF_CB.CB1_NLSF_Q8; + int pCB_element = NLSFIndices[0] * psNLSF_CB.order; + for (i = 0; i < psNLSF_CB.order; ++i) { + pNLSF_Q15[i] = Inlines.silk_LSHIFT16(pCB[pCB_element + i], 7); + } + NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psNLSF_CB, NLSFIndices[0]); + NLSF.silk_NLSF_residual_dequant(res_Q10, NLSFIndices, 1, pred_Q8, psNLSF_CB.quantStepSize_Q16, psNLSF_CB.order); + NLSF.silk_NLSF_VQ_weights_laroia(W_tmp_QW, pNLSF_Q15, psNLSF_CB.order); + for (i = 0; i < psNLSF_CB.order; ++i) { + int W_tmp_Q9 = Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT(W_tmp_QW[i], 16)); + int NLSF_Q15_tmp = Inlines.silk_ADD32(pNLSF_Q15[i], Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(res_Q10[i], 14), (short)W_tmp_Q9)); + pNLSF_Q15[i] = (short)Inlines.silk_LIMIT(NLSF_Q15_tmp, 0, Short.MAX_VALUE); + } + NLSF.silk_NLSF_stabilize(pNLSF_Q15, psNLSF_CB.deltaMin_Q15, psNLSF_CB.order); + } + + static int silk_NLSF_del_dec_quant(byte[] indices, short[] x_Q10, short[] w_Q5, short[] pred_coef_Q8, short[] ec_ix, short[] ec_rates_Q5, int quant_step_size_Q16, short inv_quant_step_size_Q6, int mu_Q20, short order) { + int ind_tmp; + int j; + int out1_Q10; + int out0_Q10; + int i; + int[] ind_sort = new int[4]; + byte[][] ind = new byte[4][]; + for (i = 0; i < 4; ++i) { + ind[i] = new byte[16]; + } + short[] prev_out_Q10 = new short[8]; + int[] RD_Q25 = new int[8]; + int[] RD_min_Q25 = new int[4]; + int[] RD_max_Q25 = new int[4]; + int[] out0_Q10_table = new int[20]; + int[] out1_Q10_table = new int[20]; + for (i = -10; i <= 9; ++i) { + out0_Q10 = Inlines.silk_LSHIFT(i, 10); + out1_Q10 = Inlines.silk_ADD16((short)out0_Q10, (short)1024); + if (i > 0) { + out0_Q10 = Inlines.silk_SUB16((short)out0_Q10, (short)102); + out1_Q10 = Inlines.silk_SUB16((short)out1_Q10, (short)102); + } else if (i == 0) { + out1_Q10 = Inlines.silk_SUB16((short)out1_Q10, (short)102); + } else if (i == -1) { + out0_Q10 = Inlines.silk_ADD16((short)out0_Q10, (short)102); + } else { + out0_Q10 = Inlines.silk_ADD16((short)out0_Q10, (short)102); + out1_Q10 = Inlines.silk_ADD16((short)out1_Q10, (short)102); + } + out0_Q10_table[i + 10] = Inlines.silk_SMULWB(out0_Q10, quant_step_size_Q16); + out1_Q10_table[i + 10] = Inlines.silk_SMULWB(out1_Q10, quant_step_size_Q16); + } + Inlines.OpusAssert(true); + int nStates = 1; + RD_Q25[0] = 0; + prev_out_Q10[0] = 0; + i = order - 1; + while (true) { + int pred_coef_Q16 = Inlines.silk_LSHIFT(pred_coef_Q8[i], 8); + short in_Q10 = x_Q10[i]; + for (j = 0; j < nStates; ++j) { + int rate1_Q5; + int rate0_Q5; + int pred_Q10 = Inlines.silk_SMULWB(pred_coef_Q16, prev_out_Q10[j]); + short res_Q10 = Inlines.silk_SUB16(in_Q10, (short)pred_Q10); + ind_tmp = Inlines.silk_SMULWB(inv_quant_step_size_Q6, res_Q10); + ind_tmp = Inlines.silk_LIMIT(ind_tmp, -10, 9); + ind[j][i] = (byte)ind_tmp; + int rates_Q5 = ec_ix[i] + ind_tmp; + out0_Q10 = out0_Q10_table[ind_tmp + 10]; + out1_Q10 = out1_Q10_table[ind_tmp + 10]; + out0_Q10 = Inlines.silk_ADD16((short)out0_Q10, (short)pred_Q10); + out1_Q10 = Inlines.silk_ADD16((short)out1_Q10, (short)pred_Q10); + prev_out_Q10[j] = (short)out0_Q10; + prev_out_Q10[j + nStates] = (short)out1_Q10; + if (ind_tmp + 1 >= 4) { + if (ind_tmp + 1 == 4) { + rate0_Q5 = ec_rates_Q5[rates_Q5 + 4]; + rate1_Q5 = 280; + } else { + rate0_Q5 = Inlines.silk_SMLABB(108, 43, ind_tmp); + rate1_Q5 = Inlines.silk_ADD16((short)rate0_Q5, (short)43); + } + } else if (ind_tmp <= -4) { + if (ind_tmp == -4) { + rate0_Q5 = 280; + rate1_Q5 = ec_rates_Q5[rates_Q5 + 1 + 4]; + } else { + rate0_Q5 = Inlines.silk_SMLABB(108, -43, ind_tmp); + rate1_Q5 = Inlines.silk_SUB16((short)rate0_Q5, (short)43); + } + } else { + rate0_Q5 = ec_rates_Q5[rates_Q5 + 4]; + rate1_Q5 = ec_rates_Q5[rates_Q5 + 1 + 4]; + } + int RD_tmp_Q25 = RD_Q25[j]; + short diff_Q10 = Inlines.silk_SUB16(in_Q10, (short)out0_Q10); + RD_Q25[j] = Inlines.silk_SMLABB(Inlines.silk_MLA(RD_tmp_Q25, Inlines.silk_SMULBB(diff_Q10, diff_Q10), w_Q5[i]), mu_Q20, rate0_Q5); + diff_Q10 = Inlines.silk_SUB16(in_Q10, (short)out1_Q10); + RD_Q25[j + nStates] = Inlines.silk_SMLABB(Inlines.silk_MLA(RD_tmp_Q25, Inlines.silk_SMULBB(diff_Q10, diff_Q10), w_Q5[i]), mu_Q20, rate1_Q5); + } + if (nStates <= 2) { + for (j = 0; j < nStates; ++j) { + ind[j + nStates][i] = (byte)(ind[j][i] + 1); + } + for (j = nStates = Inlines.silk_LSHIFT(nStates, 1); j < 4; ++j) { + ind[j][i] = ind[j - nStates][i]; + } + } else { + if (i <= 0) break; + for (j = 0; j < 4; ++j) { + if (RD_Q25[j] > RD_Q25[j + 4]) { + RD_max_Q25[j] = RD_Q25[j]; + RD_min_Q25[j] = RD_Q25[j + 4]; + RD_Q25[j] = RD_min_Q25[j]; + RD_Q25[j + 4] = RD_max_Q25[j]; + out0_Q10 = prev_out_Q10[j]; + prev_out_Q10[j] = prev_out_Q10[j + 4]; + prev_out_Q10[j + 4] = (short)out0_Q10; + ind_sort[j] = j + 4; + continue; + } + RD_min_Q25[j] = RD_Q25[j]; + RD_max_Q25[j] = RD_Q25[j + 4]; + ind_sort[j] = j; + } + while (true) { + int min_max_Q25 = Integer.MAX_VALUE; + int max_min_Q25 = 0; + int ind_min_max = 0; + int ind_max_min = 0; + for (j = 0; j < 4; ++j) { + if (min_max_Q25 > RD_max_Q25[j]) { + min_max_Q25 = RD_max_Q25[j]; + ind_min_max = j; + } + if (max_min_Q25 >= RD_min_Q25[j]) continue; + max_min_Q25 = RD_min_Q25[j]; + ind_max_min = j; + } + if (min_max_Q25 >= max_min_Q25) break; + ind_sort[ind_max_min] = ind_sort[ind_min_max] ^ 4; + RD_Q25[ind_max_min] = RD_Q25[ind_min_max + 4]; + prev_out_Q10[ind_max_min] = prev_out_Q10[ind_min_max + 4]; + RD_min_Q25[ind_max_min] = 0; + RD_max_Q25[ind_min_max] = Integer.MAX_VALUE; + System.arraycopy(ind[ind_min_max], 0, ind[ind_max_min], 0, order); + } + for (j = 0; j < 4; ++j) { + byte x = (byte)Inlines.silk_RSHIFT(ind_sort[j], 2); + byte[] byArray = ind[j]; + int n = i; + byArray[n] = (byte)(byArray[n] + x); + } + } + --i; + } + ind_tmp = 0; + int min_Q25 = Integer.MAX_VALUE; + for (j = 0; j < 8; ++j) { + if (min_Q25 <= RD_Q25[j]) continue; + min_Q25 = RD_Q25[j]; + ind_tmp = j; + } + for (j = 0; j < order; ++j) { + indices[j] = ind[ind_tmp & 3][j]; + Inlines.OpusAssert(indices[j] >= -10); + Inlines.OpusAssert(indices[j] <= 10); + } + indices[0] = (byte)(indices[0] + Inlines.silk_RSHIFT(ind_tmp, 2)); + Inlines.OpusAssert(indices[0] <= 10); + Inlines.OpusAssert(min_Q25 >= 0); + return min_Q25; + } + + static int silk_NLSF_encode(byte[] NLSFIndices, short[] pNLSF_Q15, NLSFCodebook psNLSF_CB, short[] pW_QW, int NLSF_mu_Q20, int nSurvivors, int signalType) { + short[] res_Q15 = new short[psNLSF_CB.order]; + short[] res_Q10 = new short[psNLSF_CB.order]; + short[] NLSF_tmp_Q15 = new short[psNLSF_CB.order]; + short[] W_tmp_QW = new short[psNLSF_CB.order]; + short[] W_adj_Q5 = new short[psNLSF_CB.order]; + short[] pred_Q8 = new short[psNLSF_CB.order]; + short[] ec_ix = new short[psNLSF_CB.order]; + short[] pCB = psNLSF_CB.CB1_NLSF_Q8; + Inlines.OpusAssert(nSurvivors <= 32); + Inlines.OpusAssert(signalType >= 0 && signalType <= 2); + Inlines.OpusAssert(NLSF_mu_Q20 <= Short.MAX_VALUE && NLSF_mu_Q20 >= 0); + NLSF.silk_NLSF_stabilize(pNLSF_Q15, psNLSF_CB.deltaMin_Q15, psNLSF_CB.order); + int[] err_Q26 = new int[psNLSF_CB.nVectors]; + NLSF.silk_NLSF_VQ(err_Q26, pNLSF_Q15, psNLSF_CB.CB1_NLSF_Q8, psNLSF_CB.nVectors, psNLSF_CB.order); + int[] tempIndices1 = new int[nSurvivors]; + Sort.silk_insertion_sort_increasing(err_Q26, tempIndices1, psNLSF_CB.nVectors, nSurvivors); + int[] RD_Q25 = new int[nSurvivors]; + byte[][] tempIndices2 = Arrays.InitTwoDimensionalArrayByte(nSurvivors, 16); + for (int s = 0; s < nSurvivors; ++s) { + int i; + int ind1 = tempIndices1[s]; + int pCB_element = ind1 * psNLSF_CB.order; + for (i = 0; i < psNLSF_CB.order; ++i) { + NLSF_tmp_Q15[i] = Inlines.silk_LSHIFT16(pCB[pCB_element + i], 7); + res_Q15[i] = (short)(pNLSF_Q15[i] - NLSF_tmp_Q15[i]); + } + NLSF.silk_NLSF_VQ_weights_laroia(W_tmp_QW, NLSF_tmp_Q15, psNLSF_CB.order); + for (i = 0; i < psNLSF_CB.order; ++i) { + int W_tmp_Q9 = Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT(W_tmp_QW[i], 16)); + res_Q10[i] = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(res_Q15[i], W_tmp_Q9), 14); + } + for (i = 0; i < psNLSF_CB.order; ++i) { + W_adj_Q5[i] = (short)Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(pW_QW[i], 5), W_tmp_QW[i]); + } + NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psNLSF_CB, ind1); + RD_Q25[s] = NLSF.silk_NLSF_del_dec_quant(tempIndices2[s], res_Q10, W_adj_Q5, pred_Q8, ec_ix, psNLSF_CB.ec_Rates_Q5, psNLSF_CB.quantStepSize_Q16, psNLSF_CB.invQuantStepSize_Q6, NLSF_mu_Q20, psNLSF_CB.order); + int iCDF_ptr = (signalType >> 1) * psNLSF_CB.nVectors; + int prob_Q8 = ind1 == 0 ? 256 - psNLSF_CB.CB1_iCDF[iCDF_ptr + ind1] : psNLSF_CB.CB1_iCDF[iCDF_ptr + ind1 - 1] - psNLSF_CB.CB1_iCDF[iCDF_ptr + ind1]; + int bits_q7 = 1024 - Inlines.silk_lin2log(prob_Q8); + RD_Q25[s] = Inlines.silk_SMLABB(RD_Q25[s], bits_q7, Inlines.silk_RSHIFT(NLSF_mu_Q20, 2)); + } + int[] bestIndex = new int[1]; + Sort.silk_insertion_sort_increasing(RD_Q25, bestIndex, nSurvivors, 1); + NLSFIndices[0] = (byte)tempIndices1[bestIndex[0]]; + System.arraycopy(tempIndices2[bestIndex[0]], 0, NLSFIndices, 1, psNLSF_CB.order); + NLSF.silk_NLSF_decode(pNLSF_Q15, NLSFIndices, psNLSF_CB); + return RD_Q25[0]; + } + + static void silk_NLSF2A_find_poly(int[] o, int[] cLSF, int cLSF_ptr, int dd) { + o[0] = Inlines.silk_LSHIFT(1, 16); + o[1] = 0 - cLSF[cLSF_ptr]; + for (int k = 1; k < dd; ++k) { + int ftmp = cLSF[cLSF_ptr + 2 * k]; + o[k + 1] = Inlines.silk_LSHIFT(o[k - 1], 1) - (int)Inlines.silk_RSHIFT_ROUND64(Inlines.silk_SMULL(ftmp, o[k]), 16); + for (int n = k; n > 1; --n) { + int n2 = n; + o[n2] = o[n2] + (o[n - 2] - (int)Inlines.silk_RSHIFT_ROUND64(Inlines.silk_SMULL(ftmp, o[n - 1]), 16)); + } + o[1] = o[1] - ftmp; + } + } + + static void silk_NLSF2A(short[] a_Q12, short[] NLSF2, int d) { + int i; + int k; + int[] cos_LSF_QA = new int[d]; + int[] P = new int[d / 2 + 1]; + int[] Q = new int[d / 2 + 1]; + int[] a32_QA1 = new int[d]; + int idx = 0; + Inlines.OpusAssert(true); + Inlines.OpusAssert(d == 10 || d == 16); + byte[] ordering = d == 16 ? ordering16 : ordering10; + for (k = 0; k < d; ++k) { + Inlines.OpusAssert(NLSF2[k] >= 0); + int f_int = Inlines.silk_RSHIFT(NLSF2[k], 8); + int f_frac = NLSF2[k] - Inlines.silk_LSHIFT(f_int, 8); + Inlines.OpusAssert(f_int >= 0); + Inlines.OpusAssert(f_int < 128); + short cos_val = SilkTables.silk_LSFCosTab_Q12[f_int]; + int delta = SilkTables.silk_LSFCosTab_Q12[f_int + 1] - cos_val; + cos_LSF_QA[ordering[k]] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_LSHIFT(cos_val, 8) + Inlines.silk_MUL(delta, f_frac), 4); + } + int dd = Inlines.silk_RSHIFT(d, 1); + NLSF.silk_NLSF2A_find_poly(P, cos_LSF_QA, 0, dd); + NLSF.silk_NLSF2A_find_poly(Q, cos_LSF_QA, 1, dd); + for (k = 0; k < dd; ++k) { + int Ptmp = P[k + 1] + P[k]; + int Qtmp = Q[k + 1] - Q[k]; + a32_QA1[k] = -Qtmp - Ptmp; + a32_QA1[d - k - 1] = Qtmp - Ptmp; + } + for (i = 0; i < 10; ++i) { + int maxabs = 0; + for (k = 0; k < d; ++k) { + int absval = Inlines.silk_abs(a32_QA1[k]); + if (absval <= maxabs) continue; + maxabs = absval; + idx = k; + } + if ((maxabs = Inlines.silk_RSHIFT_ROUND(maxabs, 5)) <= Short.MAX_VALUE) break; + maxabs = Inlines.silk_min(maxabs, 163838); + int sc_Q16 = 65470 - Inlines.silk_DIV32(Inlines.silk_LSHIFT(maxabs - Short.MAX_VALUE, 14), Inlines.silk_RSHIFT32(Inlines.silk_MUL(maxabs, idx + 1), 2)); + Filters.silk_bwexpander_32(a32_QA1, d, sc_Q16); + } + if (i == 10) { + for (k = 0; k < d; ++k) { + a_Q12[k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(a32_QA1[k], 5)); + a32_QA1[k] = Inlines.silk_LSHIFT(a_Q12[k], 5); + } + } else { + for (k = 0; k < d; ++k) { + a_Q12[k] = (short)Inlines.silk_RSHIFT_ROUND(a32_QA1[k], 5); + } + } + for (i = 0; i < 16 && Filters.silk_LPC_inverse_pred_gain(a_Q12, d) < 107374; ++i) { + Filters.silk_bwexpander_32(a32_QA1, d, 65536 - Inlines.silk_LSHIFT(2, i)); + for (k = 0; k < d; ++k) { + a_Q12[k] = (short)Inlines.silk_RSHIFT_ROUND(a32_QA1[k], 5); + } + } + } + + static void silk_A2NLSF_trans_poly(int[] p, int dd) { + for (int k = 2; k <= dd; ++k) { + for (int n = dd; n > k; --n) { + int n2 = n - 2; + p[n2] = p[n2] - p[n]; + } + int n = k - 2; + p[n] = p[n] - Inlines.silk_LSHIFT(p[k], 1); + } + } + + static int silk_A2NLSF_eval_poly(int[] p, int x, int dd) { + int y32 = p[dd]; + int x_Q16 = Inlines.silk_LSHIFT(x, 4); + if (8 == dd) { + y32 = Inlines.silk_SMLAWW(p[7], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[6], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[5], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[4], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[3], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[2], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[1], y32, x_Q16); + y32 = Inlines.silk_SMLAWW(p[0], y32, x_Q16); + } else { + for (int n = dd - 1; n >= 0; --n) { + y32 = Inlines.silk_SMLAWW(p[n], y32, x_Q16); + } + } + return y32; + } + + static void silk_A2NLSF_init(int[] a_Q16, int[] P, int[] Q, int dd) { + int k; + P[dd] = Inlines.silk_LSHIFT(1, 16); + Q[dd] = Inlines.silk_LSHIFT(1, 16); + for (k = 0; k < dd; ++k) { + P[k] = -a_Q16[dd - k - 1] - a_Q16[dd + k]; + Q[k] = -a_Q16[dd - k - 1] + a_Q16[dd + k]; + } + for (k = dd; k > 0; --k) { + int n = k - 1; + P[n] = P[n] - P[k]; + int n2 = k - 1; + Q[n2] = Q[n2] + Q[k]; + } + NLSF.silk_A2NLSF_trans_poly(P, dd); + NLSF.silk_A2NLSF_trans_poly(Q, dd); + } + + static void silk_A2NLSF(short[] NLSF2, int[] a_Q16, int d) { + int root_ix; + int[] P = new int[9]; + int[] Q = new int[9]; + int[][] PQ = new int[][]{P, Q}; + int dd = Inlines.silk_RSHIFT(d, 1); + NLSF.silk_A2NLSF_init(a_Q16, P, Q, dd); + int[] p = P; + int xlo = SilkTables.silk_LSFCosTab_Q12[0]; + int ylo = NLSF.silk_A2NLSF_eval_poly(p, xlo, dd); + if (ylo < 0) { + NLSF2[0] = 0; + p = Q; + ylo = NLSF.silk_A2NLSF_eval_poly(p, xlo, dd); + root_ix = 1; + } else { + root_ix = 0; + } + int k = 1; + int i = 0; + int thr = 0; + while (true) { + int xhi = SilkTables.silk_LSFCosTab_Q12[k]; + int yhi = NLSF.silk_A2NLSF_eval_poly(p, xhi, dd); + if (ylo <= 0 && yhi >= thr || ylo >= 0 && yhi <= -thr) { + thr = yhi == 0 ? 1 : 0; + int ffrac = -256; + for (int m = 0; m < 3; ++m) { + int xmid = Inlines.silk_RSHIFT_ROUND(xlo + xhi, 1); + int ymid = NLSF.silk_A2NLSF_eval_poly(p, xmid, dd); + if (ylo <= 0 && ymid >= 0 || ylo >= 0 && ymid <= 0) { + xhi = xmid; + yhi = ymid; + continue; + } + xlo = xmid; + ylo = ymid; + ffrac = Inlines.silk_ADD_RSHIFT(ffrac, 128, m); + } + if (Inlines.silk_abs(ylo) < 65536) { + int den = ylo - yhi; + int nom = Inlines.silk_LSHIFT(ylo, 5) + Inlines.silk_RSHIFT(den, 1); + if (den != 0) { + ffrac += Inlines.silk_DIV32(nom, den); + } + } else { + ffrac += Inlines.silk_DIV32(ylo, Inlines.silk_RSHIFT(ylo - yhi, 5)); + } + NLSF2[root_ix] = (short)Inlines.silk_min_32(Inlines.silk_LSHIFT(k, 8) + ffrac, Short.MAX_VALUE); + Inlines.OpusAssert(NLSF2[root_ix] >= 0); + if (++root_ix >= d) break; + p = PQ[root_ix & 1]; + xlo = SilkTables.silk_LSFCosTab_Q12[k - 1]; + ylo = Inlines.silk_LSHIFT(1 - (root_ix & 2), 12); + continue; + } + xlo = xhi; + ylo = yhi; + thr = 0; + if (++k <= 128) continue; + if (++i > 30) { + NLSF2[0] = (short)Inlines.silk_DIV32_16(32768, (short)(d + 1)); + for (k = 1; k < d; ++k) { + NLSF2[k] = (short)Inlines.silk_SMULBB(k + 1, NLSF2[0]); + } + return; + } + Filters.silk_bwexpander_32(a_Q16, d, 65536 - Inlines.silk_SMULBB(10 + i, i)); + NLSF.silk_A2NLSF_init(a_Q16, P, Q, dd); + p = P; + xlo = SilkTables.silk_LSFCosTab_Q12[0]; + ylo = NLSF.silk_A2NLSF_eval_poly(p, xlo, dd); + if (ylo < 0) { + NLSF2[0] = 0; + p = Q; + ylo = NLSF.silk_A2NLSF_eval_poly(p, xlo, dd); + root_ix = 1; + } else { + root_ix = 0; + } + k = 1; + } + } + + static void silk_process_NLSFs(SilkChannelEncoder psEncC, short[][] PredCoef_Q12, short[] pNLSF_Q15, short[] prev_NLSFq_Q15) { + boolean doInterpolate; + short[] pNLSF0_temp_Q15 = new short[16]; + short[] pNLSFW_QW = new short[16]; + short[] pNLSFW0_temp_QW = new short[16]; + Inlines.OpusAssert(psEncC.speech_activity_Q8 >= 0); + Inlines.OpusAssert(psEncC.speech_activity_Q8 <= 256); + Inlines.OpusAssert(psEncC.useInterpolatedNLSFs == 1 || psEncC.indices.NLSFInterpCoef_Q2 == 4); + int NLSF_mu_Q20 = Inlines.silk_SMLAWB(3146, -268434, psEncC.speech_activity_Q8); + if (psEncC.nb_subfr == 2) { + NLSF_mu_Q20 = Inlines.silk_ADD_RSHIFT(NLSF_mu_Q20, NLSF_mu_Q20, 1); + } + Inlines.OpusAssert(NLSF_mu_Q20 > 0); + Inlines.OpusAssert(NLSF_mu_Q20 <= 5243); + NLSF.silk_NLSF_VQ_weights_laroia(pNLSFW_QW, pNLSF_Q15, psEncC.predictLPCOrder); + boolean bl = doInterpolate = psEncC.useInterpolatedNLSFs == 1 && psEncC.indices.NLSFInterpCoef_Q2 < 4; + if (doInterpolate) { + Inlines.silk_interpolate(pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15, psEncC.indices.NLSFInterpCoef_Q2, psEncC.predictLPCOrder); + NLSF.silk_NLSF_VQ_weights_laroia(pNLSFW0_temp_QW, pNLSF0_temp_Q15, psEncC.predictLPCOrder); + int i_sqr_Q15 = Inlines.silk_LSHIFT(Inlines.silk_SMULBB(psEncC.indices.NLSFInterpCoef_Q2, psEncC.indices.NLSFInterpCoef_Q2), 11); + for (int i = 0; i < psEncC.predictLPCOrder; ++i) { + pNLSFW_QW[i] = (short)Inlines.silk_SMLAWB(Inlines.silk_RSHIFT(pNLSFW_QW[i], 1), pNLSFW0_temp_QW[i], i_sqr_Q15); + Inlines.OpusAssert(pNLSFW_QW[i] >= 1); + } + } + NLSF.silk_NLSF_encode(psEncC.indices.NLSFIndices, pNLSF_Q15, psEncC.psNLSF_CB, pNLSFW_QW, NLSF_mu_Q20, psEncC.NLSF_MSVQ_Survivors, psEncC.indices.signalType); + NLSF.silk_NLSF2A(PredCoef_Q12[1], pNLSF_Q15, psEncC.predictLPCOrder); + if (doInterpolate) { + Inlines.silk_interpolate(pNLSF0_temp_Q15, prev_NLSFq_Q15, pNLSF_Q15, psEncC.indices.NLSFInterpCoef_Q2, psEncC.predictLPCOrder); + NLSF.silk_NLSF2A(PredCoef_Q12[0], pNLSF0_temp_Q15, psEncC.predictLPCOrder); + } else { + System.arraycopy(PredCoef_Q12[1], 0, PredCoef_Q12[0], 0, psEncC.predictLPCOrder); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/NLSFCodebook.java b/source/decompiled/de/maxhenkel/voicechat/concentus/NLSFCodebook.java new file mode 100644 index 0000000..b5505a2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/NLSFCodebook.java @@ -0,0 +1,36 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class NLSFCodebook { + short nVectors = 0; + short order = 0; + short quantStepSize_Q16 = 0; + short invQuantStepSize_Q6 = 0; + short[] CB1_NLSF_Q8 = null; + short[] CB1_iCDF = null; + short[] pred_Q8 = null; + short[] ec_sel = null; + short[] ec_iCDF = null; + short[] ec_Rates_Q5 = null; + short[] deltaMin_Q15 = null; + + NLSFCodebook() { + } + + void Reset() { + this.nVectors = 0; + this.order = 0; + this.quantStepSize_Q16 = 0; + this.invQuantStepSize_Q6 = 0; + this.CB1_NLSF_Q8 = null; + this.CB1_iCDF = null; + this.pred_Q8 = null; + this.ec_sel = null; + this.ec_iCDF = null; + this.ec_Rates_Q5 = null; + this.deltaMin_Q15 = null; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/NoiseShapeAnalysis.java b/source/decompiled/de/maxhenkel/voicechat/concentus/NoiseShapeAnalysis.java new file mode 100644 index 0000000..f430c48 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/NoiseShapeAnalysis.java @@ -0,0 +1,251 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.ApplySineWindow; +import de.maxhenkel.voicechat.concentus.Autocorrelation; +import de.maxhenkel.voicechat.concentus.BWExpander; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.K2A; +import de.maxhenkel.voicechat.concentus.LPCInversePredGain; +import de.maxhenkel.voicechat.concentus.Schur; +import de.maxhenkel.voicechat.concentus.Sigmoid; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkEncoderControl; +import de.maxhenkel.voicechat.concentus.SilkShapeState; +import de.maxhenkel.voicechat.concentus.SumSqrShift; + +class NoiseShapeAnalysis { + NoiseShapeAnalysis() { + } + + static int warped_gain(int[] coefs_Q24, int lambda_Q16, int order) { + lambda_Q16 = -lambda_Q16; + int gain_Q24 = coefs_Q24[order - 1]; + for (int i = order - 2; i >= 0; --i) { + gain_Q24 = Inlines.silk_SMLAWB(coefs_Q24[i], gain_Q24, lambda_Q16); + } + gain_Q24 = Inlines.silk_SMLAWB(0x1000000, gain_Q24, -lambda_Q16); + return Inlines.silk_INVERSE32_varQ(gain_Q24, 40); + } + + static void limit_warped_coefs(int[] coefs_syn_Q24, int[] coefs_ana_Q24, int lambda_Q16, int limit_Q24, int order) { + int i; + int ind = 0; + lambda_Q16 = -lambda_Q16; + for (i = order - 1; i > 0; --i) { + coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16); + coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16); + } + lambda_Q16 = -lambda_Q16; + int nom_Q16 = Inlines.silk_SMLAWB(65536, -lambda_Q16, lambda_Q16); + int den_Q24 = Inlines.silk_SMLAWB(0x1000000, coefs_syn_Q24[0], lambda_Q16); + int gain_syn_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + den_Q24 = Inlines.silk_SMLAWB(0x1000000, coefs_ana_Q24[0], lambda_Q16); + int gain_ana_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + for (i = 0; i < order; ++i) { + coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]); + coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]); + } + for (int iter = 0; iter < 10; ++iter) { + int maxabs_Q24 = -1; + for (i = 0; i < order; ++i) { + int tmp = Inlines.silk_max(Inlines.silk_abs_int32(coefs_syn_Q24[i]), Inlines.silk_abs_int32(coefs_ana_Q24[i])); + if (tmp <= maxabs_Q24) continue; + maxabs_Q24 = tmp; + ind = i; + } + if (maxabs_Q24 <= limit_Q24) { + return; + } + for (i = 1; i < order; ++i) { + coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16); + coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16); + } + gain_syn_Q16 = Inlines.silk_INVERSE32_varQ(gain_syn_Q16, 32); + gain_ana_Q16 = Inlines.silk_INVERSE32_varQ(gain_ana_Q16, 32); + for (i = 0; i < order; ++i) { + coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]); + coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]); + } + int chirp_Q16 = 64881 - Inlines.silk_DIV32_varQ(Inlines.silk_SMULWB(maxabs_Q24 - limit_Q24, Inlines.silk_SMLABB(819, 102, iter)), Inlines.silk_MUL(maxabs_Q24, ind + 1), 22); + BWExpander.silk_bwexpander_32(coefs_syn_Q24, order, chirp_Q16); + BWExpander.silk_bwexpander_32(coefs_ana_Q24, order, chirp_Q16); + lambda_Q16 = -lambda_Q16; + for (i = order - 1; i > 0; --i) { + coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16); + coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16); + } + lambda_Q16 = -lambda_Q16; + nom_Q16 = Inlines.silk_SMLAWB(65536, -lambda_Q16, lambda_Q16); + den_Q24 = Inlines.silk_SMLAWB(0x1000000, coefs_syn_Q24[0], lambda_Q16); + gain_syn_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + den_Q24 = Inlines.silk_SMLAWB(0x1000000, coefs_ana_Q24[0], lambda_Q16); + gain_ana_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24); + for (i = 0; i < order; ++i) { + coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]); + coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]); + } + } + Inlines.OpusAssert(false); + } + + static void silk_noise_shape_analysis(SilkChannelEncoder psEnc, SilkEncoderControl psEncCtrl, short[] pitch_res, int pitch_res_ptr, short[] x, int x_ptr) { + int HarmShapeGain_Q16; + int Tilt_Q16; + int b_Q14; + int gain_mult_Q16; + int BWExp2_Q16; + int nrg; + int k; + SilkShapeState psShapeSt = psEnc.sShape; + int scale = 0; + int[] auto_corr = new int[17]; + int[] refl_coef_Q16 = new int[16]; + int[] AR1_Q24 = new int[16]; + int[] AR2_Q24 = new int[16]; + int x_ptr2 = x_ptr - psEnc.la_shape; + int SNR_adj_dB_Q7 = psEnc.SNR_dB_Q7; + psEncCtrl.input_quality_Q14 = Inlines.silk_RSHIFT(psEnc.input_quality_bands_Q15[0] + psEnc.input_quality_bands_Q15[1], 2); + psEncCtrl.coding_quality_Q14 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(SNR_adj_dB_Q7 - 2560, 4)), 1); + if (psEnc.useCBR == 0) { + int b_Q8 = 256 - psEnc.speech_activity_Q8; + b_Q8 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(b_Q8, 8), b_Q8); + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, Inlines.silk_SMULBB(-8, b_Q8), Inlines.silk_SMULWB(16384 + psEncCtrl.input_quality_Q14, psEncCtrl.coding_quality_Q14)); + } + SNR_adj_dB_Q7 = psEnc.indices.signalType == 2 ? Inlines.silk_SMLAWB(SNR_adj_dB_Q7, 512, psEnc.LTPCorr_Q15) : Inlines.silk_SMLAWB(SNR_adj_dB_Q7, Inlines.silk_SMLAWB(3072, -104858, psEnc.SNR_dB_Q7), 16384 - psEncCtrl.input_quality_Q14); + if (psEnc.indices.signalType == 2) { + psEnc.indices.quantOffsetType = 0; + psEncCtrl.sparseness_Q8 = 0; + } else { + int nSamples = Inlines.silk_LSHIFT(psEnc.fs_kHz, 1); + int energy_variation_Q7 = 0; + int log_energy_prev_Q7 = 0; + int pitch_res_ptr2 = pitch_res_ptr; + BoxedValueInt boxed_nrg = new BoxedValueInt(0); + BoxedValueInt boxed_scale = new BoxedValueInt(0); + for (k = 0; k < Inlines.silk_SMULBB(5, psEnc.nb_subfr) / 2; ++k) { + SumSqrShift.silk_sum_sqr_shift(boxed_nrg, boxed_scale, pitch_res, pitch_res_ptr2, nSamples); + nrg = boxed_nrg.Val; + scale = boxed_scale.Val; + int log_energy_Q7 = Inlines.silk_lin2log(nrg += Inlines.silk_RSHIFT(nSamples, scale)); + if (k > 0) { + energy_variation_Q7 += Inlines.silk_abs(log_energy_Q7 - log_energy_prev_Q7); + } + log_energy_prev_Q7 = log_energy_Q7; + pitch_res_ptr2 += nSamples; + } + psEncCtrl.sparseness_Q8 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(energy_variation_Q7 - 640, 6554)), 7); + psEnc.indices.quantOffsetType = psEncCtrl.sparseness_Q8 > 192 ? (byte)0 : 1; + SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, 65536, psEncCtrl.sparseness_Q8 - 128); + } + int strength_Q16 = Inlines.silk_SMULWB(psEncCtrl.predGain_Q16, 66); + int BWExp1_Q16 = BWExp2_Q16 = Inlines.silk_DIV32_varQ(62259, Inlines.silk_SMLAWW(65536, strength_Q16, strength_Q16), 16); + int delta_Q16 = Inlines.silk_SMULWB(65536 - Inlines.silk_SMULBB(3, psEncCtrl.coding_quality_Q14), 655); + BWExp1_Q16 = Inlines.silk_SUB32(BWExp1_Q16, delta_Q16); + BWExp2_Q16 = Inlines.silk_ADD32(BWExp2_Q16, delta_Q16); + BWExp1_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(BWExp1_Q16, 14), Inlines.silk_RSHIFT(BWExp2_Q16, 2)); + int warping_Q16 = psEnc.warping_Q16 > 0 ? Inlines.silk_SMLAWB(psEnc.warping_Q16, psEncCtrl.coding_quality_Q14, 2621) : 0; + short[] x_windowed = new short[psEnc.shapeWinLength]; + for (k = 0; k < psEnc.nb_subfr; ++k) { + int flat_part = psEnc.fs_kHz * 3; + int slope_part = Inlines.silk_RSHIFT(psEnc.shapeWinLength - flat_part, 1); + ApplySineWindow.silk_apply_sine_window(x_windowed, 0, x, x_ptr2, 1, slope_part); + int shift = slope_part; + System.arraycopy(x, x_ptr2 + shift, x_windowed, shift, flat_part); + ApplySineWindow.silk_apply_sine_window(x_windowed, shift += flat_part, x, x_ptr2 + shift, 2, slope_part); + x_ptr2 += psEnc.subfr_length; + BoxedValueInt scale_boxed = new BoxedValueInt(scale); + if (psEnc.warping_Q16 > 0) { + Autocorrelation.silk_warped_autocorrelation(auto_corr, scale_boxed, x_windowed, warping_Q16, psEnc.shapeWinLength, psEnc.shapingLPCOrder); + } else { + Autocorrelation.silk_autocorr(auto_corr, scale_boxed, x_windowed, psEnc.shapeWinLength, psEnc.shapingLPCOrder + 1); + } + scale = scale_boxed.Val; + auto_corr[0] = Inlines.silk_ADD32(auto_corr[0], Inlines.silk_max_32(Inlines.silk_SMULWB(Inlines.silk_RSHIFT(auto_corr[0], 4), 52), 1)); + nrg = Schur.silk_schur64(refl_coef_Q16, auto_corr, psEnc.shapingLPCOrder); + Inlines.OpusAssert(nrg >= 0); + K2A.silk_k2a_Q16(AR2_Q24, refl_coef_Q16, psEnc.shapingLPCOrder); + int Qnrg = -scale; + Inlines.OpusAssert(Qnrg >= -12); + Inlines.OpusAssert(Qnrg <= 30); + if ((Qnrg & 1) != 0) { + --Qnrg; + nrg >>= 1; + } + int tmp32 = Inlines.silk_SQRT_APPROX(nrg); + psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(tmp32, 16 - (Qnrg >>= 1)); + if (psEnc.warping_Q16 > 0) { + gain_mult_Q16 = NoiseShapeAnalysis.warped_gain(AR2_Q24, warping_Q16, psEnc.shapingLPCOrder); + Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0); + psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(Inlines.silk_RSHIFT_ROUND(psEncCtrl.Gains_Q16[k], 1), gain_mult_Q16) >= 0x3FFFFFFF ? Integer.MAX_VALUE : Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16); + } + BWExpander.silk_bwexpander_32(AR2_Q24, psEnc.shapingLPCOrder, BWExp2_Q16); + System.arraycopy(AR2_Q24, 0, AR1_Q24, 0, psEnc.shapingLPCOrder); + Inlines.OpusAssert(BWExp1_Q16 <= 65536); + BWExpander.silk_bwexpander_32(AR1_Q24, psEnc.shapingLPCOrder, BWExp1_Q16); + int pre_nrg_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR2_Q24, psEnc.shapingLPCOrder); + nrg = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR1_Q24, psEnc.shapingLPCOrder); + pre_nrg_Q30 = Inlines.silk_LSHIFT32(Inlines.silk_SMULWB(pre_nrg_Q30, 22938), 1); + psEncCtrl.GainsPre_Q14[k] = 4915 + Inlines.silk_DIV32_varQ(pre_nrg_Q30, nrg, 14); + NoiseShapeAnalysis.limit_warped_coefs(AR2_Q24, AR1_Q24, warping_Q16, 67092088, psEnc.shapingLPCOrder); + for (int i = 0; i < psEnc.shapingLPCOrder; ++i) { + psEncCtrl.AR1_Q13[k * 16 + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR1_Q24[i], 11)); + psEncCtrl.AR2_Q13[k * 16 + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR2_Q24[i], 11)); + } + } + gain_mult_Q16 = Inlines.silk_log2lin(-Inlines.silk_SMLAWB(-2048, SNR_adj_dB_Q7, 10486)); + int gain_add_Q16 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(2048, 256, 10486)); + Inlines.OpusAssert(gain_mult_Q16 > 0); + for (k = 0; k < psEnc.nb_subfr; ++k) { + psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16); + Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0); + psEncCtrl.Gains_Q16[k] = Inlines.silk_ADD_POS_SAT32(psEncCtrl.Gains_Q16[k], gain_add_Q16); + } + gain_mult_Q16 = 65536 + Inlines.silk_RSHIFT_ROUND(Inlines.silk_MLA(0x333333, psEncCtrl.coding_quality_Q14, 410), 10); + for (k = 0; k < psEnc.nb_subfr; ++k) { + psEncCtrl.GainsPre_Q14[k] = Inlines.silk_SMULWB(gain_mult_Q16, psEncCtrl.GainsPre_Q14[k]); + } + strength_Q16 = Inlines.silk_MUL(64, Inlines.silk_SMLAWB(4096, 4096, psEnc.input_quality_bands_Q15[0] - 32768)); + strength_Q16 = Inlines.silk_RSHIFT(Inlines.silk_MUL(strength_Q16, psEnc.speech_activity_Q8), 8); + if (psEnc.indices.signalType == 2) { + int fs_kHz_inv = Inlines.silk_DIV32_16(3277, psEnc.fs_kHz); + k = 0; + while (k < psEnc.nb_subfr) { + b_Q14 = fs_kHz_inv + Inlines.silk_DIV32_16(49152, psEncCtrl.pitchL[k]); + psEncCtrl.LF_shp_Q14[k] = Inlines.silk_LSHIFT(16384 - b_Q14 - Inlines.silk_SMULWB(strength_Q16, b_Q14), 16); + int n = k++; + psEncCtrl.LF_shp_Q14[n] = psEncCtrl.LF_shp_Q14[n] | b_Q14 - 16384 & 0xFFFF; + } + Inlines.OpusAssert(true); + Tilt_Q16 = -16384 - Inlines.silk_SMULWB(49152, Inlines.silk_SMULWB(0x59999A, psEnc.speech_activity_Q8)); + } else { + b_Q14 = Inlines.silk_DIV32_16(21299, psEnc.fs_kHz); + psEncCtrl.LF_shp_Q14[0] = Inlines.silk_LSHIFT(16384 - b_Q14 - Inlines.silk_SMULWB(strength_Q16, Inlines.silk_SMULWB(39322, b_Q14)), 16); + psEncCtrl.LF_shp_Q14[0] = psEncCtrl.LF_shp_Q14[0] | b_Q14 - 16384 & 0xFFFF; + for (k = 1; k < psEnc.nb_subfr; ++k) { + psEncCtrl.LF_shp_Q14[k] = psEncCtrl.LF_shp_Q14[0]; + } + Tilt_Q16 = -16384; + } + int HarmBoost_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULWB(131072 - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 3), psEnc.LTPCorr_Q15), 6554); + HarmBoost_Q16 = Inlines.silk_SMLAWB(HarmBoost_Q16, 65536 - Inlines.silk_LSHIFT(psEncCtrl.input_quality_Q14, 2), 6554); + if (psEnc.indices.signalType == 2) { + HarmShapeGain_Q16 = Inlines.silk_SMLAWB(19661, 65536 - Inlines.silk_SMULWB(262144 - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 4), psEncCtrl.input_quality_Q14), 13107); + HarmShapeGain_Q16 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(HarmShapeGain_Q16, 1), Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT(psEnc.LTPCorr_Q15, 15))); + } else { + HarmShapeGain_Q16 = 0; + } + for (k = 0; k < 4; ++k) { + psShapeSt.HarmBoost_smth_Q16 = Inlines.silk_SMLAWB(psShapeSt.HarmBoost_smth_Q16, HarmBoost_Q16 - psShapeSt.HarmBoost_smth_Q16, 26214); + psShapeSt.HarmShapeGain_smth_Q16 = Inlines.silk_SMLAWB(psShapeSt.HarmShapeGain_smth_Q16, HarmShapeGain_Q16 - psShapeSt.HarmShapeGain_smth_Q16, 26214); + psShapeSt.Tilt_smth_Q16 = Inlines.silk_SMLAWB(psShapeSt.Tilt_smth_Q16, Tilt_Q16 - psShapeSt.Tilt_smth_Q16, 26214); + psEncCtrl.HarmBoost_Q14[k] = Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmBoost_smth_Q16, 2); + psEncCtrl.HarmShapeGain_Q14[k] = Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmShapeGain_smth_Q16, 2); + psEncCtrl.Tilt_Q14[k] = Inlines.silk_RSHIFT_ROUND(psShapeSt.Tilt_smth_Q16, 2); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusApplication.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusApplication.java new file mode 100644 index 0000000..93f9029 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusApplication.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +public enum OpusApplication { + OPUS_APPLICATION_UNIMPLEMENTED, + OPUS_APPLICATION_VOIP, + OPUS_APPLICATION_AUDIO, + OPUS_APPLICATION_RESTRICTED_LOWDELAY; + +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusBandwidth.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusBandwidth.java new file mode 100644 index 0000000..06e698a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusBandwidth.java @@ -0,0 +1,16 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +public enum OpusBandwidth { + OPUS_BANDWIDTH_UNKNOWN, + OPUS_BANDWIDTH_AUTO, + OPUS_BANDWIDTH_NARROWBAND, + OPUS_BANDWIDTH_MEDIUMBAND, + OPUS_BANDWIDTH_WIDEBAND, + OPUS_BANDWIDTH_SUPERWIDEBAND, + OPUS_BANDWIDTH_FULLBAND; + +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusBandwidthHelpers.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusBandwidthHelpers.java new file mode 100644 index 0000000..97002f4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusBandwidthHelpers.java @@ -0,0 +1,72 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.OpusBandwidth; + +class OpusBandwidthHelpers { + OpusBandwidthHelpers() { + } + + static int GetOrdinal(OpusBandwidth bw) { + switch (bw) { + case OPUS_BANDWIDTH_NARROWBAND: { + return 1; + } + case OPUS_BANDWIDTH_MEDIUMBAND: { + return 2; + } + case OPUS_BANDWIDTH_WIDEBAND: { + return 3; + } + case OPUS_BANDWIDTH_SUPERWIDEBAND: { + return 4; + } + case OPUS_BANDWIDTH_FULLBAND: { + return 5; + } + } + return -1; + } + + static OpusBandwidth GetBandwidth(int ordinal) { + switch (ordinal) { + case 1: { + return OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + case 2: { + return OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + } + case 3: { + return OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + case 4: { + return OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + } + case 5: { + return OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + } + } + return OpusBandwidth.OPUS_BANDWIDTH_AUTO; + } + + static OpusBandwidth MIN(OpusBandwidth a, OpusBandwidth b) { + if (OpusBandwidthHelpers.GetOrdinal(a) < OpusBandwidthHelpers.GetOrdinal(b)) { + return a; + } + return b; + } + + static OpusBandwidth MAX(OpusBandwidth a, OpusBandwidth b) { + if (OpusBandwidthHelpers.GetOrdinal(a) > OpusBandwidthHelpers.GetOrdinal(b)) { + return a; + } + return b; + } + + static OpusBandwidth SUBTRACT(OpusBandwidth a, int b) { + return OpusBandwidthHelpers.GetBandwidth(OpusBandwidthHelpers.GetOrdinal(a) - b); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusConstants.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusConstants.java new file mode 100644 index 0000000..42ee1b8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusConstants.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +public class OpusConstants { + public static final int OPUS_AUTO = -1000; + public static final int OPUS_BITRATE_MAX = -1; + public static final int NB_FRAMES = 8; + public static final int NB_TBANDS = 18; + public static final int NB_TOT_BANDS = 21; + public static final int NB_TONAL_SKIP_BANDS = 9; + public static final int ANALYSIS_BUF_SIZE = 720; + public static final int DETECT_SIZE = 200; + public static final int MAX_ENCODER_BUFFER = 480; +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusDecoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusDecoder.java new file mode 100644 index 0000000..61c1960 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusDecoder.java @@ -0,0 +1,499 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltDecoder; +import de.maxhenkel.voicechat.concentus.CodecHelpers; +import de.maxhenkel.voicechat.concentus.DecControlState; +import de.maxhenkel.voicechat.concentus.DecodeAPI; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.OpusBandwidth; +import de.maxhenkel.voicechat.concentus.OpusError; +import de.maxhenkel.voicechat.concentus.OpusException; +import de.maxhenkel.voicechat.concentus.OpusMode; +import de.maxhenkel.voicechat.concentus.OpusPacketInfo; +import de.maxhenkel.voicechat.concentus.SilkDecoder; + +public class OpusDecoder { + int channels; + int Fs; + final DecControlState DecControl = new DecControlState(); + int decode_gain; + int stream_channels; + OpusBandwidth bandwidth; + OpusMode mode; + OpusMode prev_mode; + int frame_size; + int prev_redundancy; + int last_packet_duration; + int rangeFinal; + SilkDecoder SilkDecoder = new SilkDecoder(); + CeltDecoder Celt_Decoder = new CeltDecoder(); + private static final byte[] SILENCE = new byte[]{-1, -1}; + + OpusDecoder() { + } + + void reset() { + this.channels = 0; + this.Fs = 0; + this.DecControl.Reset(); + this.decode_gain = 0; + this.partialReset(); + } + + void partialReset() { + this.stream_channels = 0; + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + this.mode = OpusMode.MODE_UNKNOWN; + this.prev_mode = OpusMode.MODE_UNKNOWN; + this.frame_size = 0; + this.prev_redundancy = 0; + this.last_packet_duration = 0; + this.rangeFinal = 0; + } + + int opus_decoder_init(int Fs, int channels) { + if (Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000 || channels != 1 && channels != 2) { + return OpusError.OPUS_BAD_ARG; + } + this.reset(); + SilkDecoder silk_dec = this.SilkDecoder; + CeltDecoder celt_dec = this.Celt_Decoder; + this.stream_channels = this.channels = channels; + this.DecControl.API_sampleRate = this.Fs = Fs; + this.DecControl.nChannelsAPI = this.channels; + int ret = DecodeAPI.silk_InitDecoder(silk_dec); + if (ret != 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + ret = celt_dec.celt_decoder_init(Fs, channels); + if (ret != OpusError.OPUS_OK) { + return OpusError.OPUS_INTERNAL_ERROR; + } + celt_dec.SetSignalling(0); + this.prev_mode = OpusMode.MODE_UNKNOWN; + this.frame_size = Fs / 400; + return OpusError.OPUS_OK; + } + + public OpusDecoder(int Fs, int channels) throws OpusException { + if (Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000) { + throw new IllegalArgumentException("Sample rate is invalid (must be 8/12/16/24/48 Khz)"); + } + if (channels != 1 && channels != 2) { + throw new IllegalArgumentException("Number of channels must be 1 or 2"); + } + int ret = this.opus_decoder_init(Fs, channels); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG when creating decoder"); + } + throw new OpusException("Error while initializing decoder", ret); + } + } + + int opus_decode_frame(byte[] data, int data_ptr, int len, short[] pcm, int pcm_ptr, int frame_size, int decode_fec) { + int i; + OpusMode mode; + int audiosize; + int silk_ret = 0; + int celt_ret = 0; + EntropyCoder dec = new EntropyCoder(); + short[] pcm_transition = null; + boolean transition = false; + int redundancy = 0; + int redundancy_bytes = 0; + int celt_to_silk = 0; + int redundant_rng = 0; + SilkDecoder silk_dec = this.SilkDecoder; + CeltDecoder celt_dec = this.Celt_Decoder; + int F20 = this.Fs / 50; + int F10 = F20 >> 1; + int F5 = F10 >> 1; + int F2_5 = F5 >> 1; + if (frame_size < F2_5) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + frame_size = Inlines.IMIN(frame_size, this.Fs / 25 * 3); + if (len <= 1) { + data = null; + frame_size = Inlines.IMIN(frame_size, this.frame_size); + } + if (data != null) { + audiosize = this.frame_size; + mode = this.mode; + dec.dec_init(data, data_ptr, len); + } else { + audiosize = frame_size; + mode = this.prev_mode; + if (mode == OpusMode.MODE_UNKNOWN) { + for (int i2 = pcm_ptr; i2 < pcm_ptr + audiosize * this.channels; ++i2) { + pcm[i2] = 0; + } + return audiosize; + } + if (audiosize > F20) { + int ret; + do { + if ((ret = this.opus_decode_frame(null, 0, 0, pcm, pcm_ptr, Inlines.IMIN(audiosize, F20), 0)) < 0) { + return ret; + } + pcm_ptr += ret * this.channels; + } while ((audiosize -= ret) > 0); + return frame_size; + } + if (audiosize < F20) { + if (audiosize > F10) { + audiosize = F10; + } else if (mode != OpusMode.MODE_SILK_ONLY && audiosize > F5 && audiosize < F10) { + audiosize = F5; + } + } + } + int celt_accum = mode != OpusMode.MODE_CELT_ONLY && frame_size >= F10 ? 1 : 0; + int pcm_transition_silk_size = 0; + int pcm_transition_celt_size = 0; + if (data != null && this.prev_mode != OpusMode.MODE_UNKNOWN && this.prev_mode != OpusMode.MODE_AUTO && (mode == OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY && this.prev_redundancy == 0 || mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY)) { + transition = true; + if (mode == OpusMode.MODE_CELT_ONLY) { + pcm_transition_celt_size = F5 * this.channels; + } else { + pcm_transition_silk_size = F5 * this.channels; + } + } + short[] pcm_transition_celt = new short[pcm_transition_celt_size]; + if (transition && mode == OpusMode.MODE_CELT_ONLY) { + pcm_transition = pcm_transition_celt; + this.opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0); + } + if (audiosize > frame_size) { + return OpusError.OPUS_BAD_ARG; + } + frame_size = audiosize; + int pcm_silk_size = mode != OpusMode.MODE_CELT_ONLY && celt_accum == 0 ? Inlines.IMAX(F10, frame_size) * this.channels : 0; + short[] pcm_silk = new short[pcm_silk_size]; + if (mode != OpusMode.MODE_CELT_ONLY) { + int silk_frame_size; + short[] pcm_ptr2; + int pcm_ptr2_ptr = 0; + if (celt_accum != 0) { + pcm_ptr2 = pcm; + pcm_ptr2_ptr = pcm_ptr; + } else { + pcm_ptr2 = pcm_silk; + pcm_ptr2_ptr = 0; + } + if (this.prev_mode == OpusMode.MODE_CELT_ONLY) { + DecodeAPI.silk_InitDecoder(silk_dec); + } + this.DecControl.payloadSize_ms = Inlines.IMAX(10, 1000 * audiosize / this.Fs); + if (data != null) { + this.DecControl.nChannelsInternal = this.stream_channels; + if (mode == OpusMode.MODE_SILK_ONLY) { + if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) { + this.DecControl.internalSampleRate = 8000; + } else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + this.DecControl.internalSampleRate = 12000; + } else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND) { + this.DecControl.internalSampleRate = 16000; + } else { + this.DecControl.internalSampleRate = 16000; + Inlines.OpusAssert(false); + } + } else { + this.DecControl.internalSampleRate = 16000; + } + } + int lost_flag = data == null ? 1 : 2 * decode_fec; + int decoded_samples = 0; + do { + int first_frame = decoded_samples == 0 ? 1 : 0; + BoxedValueInt boxed_silk_frame_size = new BoxedValueInt(0); + silk_ret = DecodeAPI.silk_Decode(silk_dec, this.DecControl, lost_flag, first_frame, dec, pcm_ptr2, pcm_ptr2_ptr, boxed_silk_frame_size); + silk_frame_size = boxed_silk_frame_size.Val; + if (silk_ret != 0) { + if (lost_flag != 0) { + silk_frame_size = frame_size; + Arrays.MemSetWithOffset(pcm_ptr2, (short)0, pcm_ptr2_ptr, frame_size * this.channels); + } else { + return OpusError.OPUS_INTERNAL_ERROR; + } + } + pcm_ptr2_ptr += silk_frame_size * this.channels; + } while ((decoded_samples += silk_frame_size) < frame_size); + } + int start_band = 0; + if (decode_fec == 0 && mode != OpusMode.MODE_CELT_ONLY && data != null && dec.tell() + 17 + 20 * (this.mode == OpusMode.MODE_HYBRID ? 1 : 0) <= 8 * len && (redundancy = mode == OpusMode.MODE_HYBRID ? dec.dec_bit_logp(12L) : 1) != 0) { + celt_to_silk = dec.dec_bit_logp(1L); + int n = redundancy_bytes = mode == OpusMode.MODE_HYBRID ? (int)dec.dec_uint(256L) + 2 : len - (dec.tell() + 7 >> 3); + if ((len -= redundancy_bytes) * 8 < dec.tell()) { + len = 0; + redundancy_bytes = 0; + redundancy = 0; + } + dec.storage -= redundancy_bytes; + } + if (mode != OpusMode.MODE_CELT_ONLY) { + start_band = 17; + } + int endband = 21; + switch (this.bandwidth) { + case OPUS_BANDWIDTH_NARROWBAND: { + endband = 13; + break; + } + case OPUS_BANDWIDTH_MEDIUMBAND: + case OPUS_BANDWIDTH_WIDEBAND: { + endband = 17; + break; + } + case OPUS_BANDWIDTH_SUPERWIDEBAND: { + endband = 19; + break; + } + case OPUS_BANDWIDTH_FULLBAND: { + endband = 21; + } + } + celt_dec.SetEndBand(endband); + celt_dec.SetChannels(this.stream_channels); + if (redundancy != 0) { + transition = false; + pcm_transition_silk_size = 0; + } + short[] pcm_transition_silk = new short[pcm_transition_silk_size]; + if (transition && mode != OpusMode.MODE_CELT_ONLY) { + pcm_transition = pcm_transition_silk; + this.opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0); + } + int redundant_audio_size = redundancy != 0 ? F5 * this.channels : 0; + short[] redundant_audio = new short[redundant_audio_size]; + if (redundancy != 0 && celt_to_silk != 0) { + celt_dec.SetStartBand(0); + celt_dec.celt_decode_with_ec(data, data_ptr + len, redundancy_bytes, redundant_audio, 0, F5, null, 0); + redundant_rng = celt_dec.GetFinalRange(); + } + celt_dec.SetStartBand(start_band); + if (mode != OpusMode.MODE_SILK_ONLY) { + int celt_frame_size = Inlines.IMIN(F20, frame_size); + if (mode != this.prev_mode && this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN && this.prev_redundancy == 0) { + celt_dec.ResetState(); + } + celt_ret = celt_dec.celt_decode_with_ec(decode_fec != 0 ? null : data, data_ptr, len, pcm, pcm_ptr, celt_frame_size, dec, celt_accum); + } else { + if (celt_accum == 0) { + for (i = pcm_ptr; i < frame_size * this.channels + pcm_ptr; ++i) { + pcm[i] = 0; + } + } + if (this.prev_mode == OpusMode.MODE_HYBRID && (redundancy == 0 || celt_to_silk == 0 || this.prev_redundancy == 0)) { + celt_dec.SetStartBand(0); + celt_dec.celt_decode_with_ec(SILENCE, 0, 2, pcm, pcm_ptr, F2_5, null, celt_accum); + } + } + if (mode != OpusMode.MODE_CELT_ONLY && celt_accum == 0) { + for (i = 0; i < frame_size * this.channels; ++i) { + pcm[pcm_ptr + i] = Inlines.SAT16(Inlines.ADD32(pcm[pcm_ptr + i], pcm_silk[i])); + } + } + int[] window = celt_dec.GetMode().window; + if (redundancy != 0 && celt_to_silk == 0) { + celt_dec.ResetState(); + celt_dec.SetStartBand(0); + celt_dec.celt_decode_with_ec(data, data_ptr + len, redundancy_bytes, redundant_audio, 0, F5, null, 0); + redundant_rng = celt_dec.GetFinalRange(); + CodecHelpers.smooth_fade(pcm, pcm_ptr + this.channels * (frame_size - F2_5), redundant_audio, this.channels * F2_5, pcm, pcm_ptr + this.channels * (frame_size - F2_5), F2_5, this.channels, window, this.Fs); + } + if (redundancy != 0 && celt_to_silk != 0) { + for (int c = 0; c < this.channels; ++c) { + for (i = 0; i < F2_5; ++i) { + pcm[this.channels * i + c + pcm_ptr] = redundant_audio[this.channels * i + c]; + } + } + CodecHelpers.smooth_fade(redundant_audio, this.channels * F2_5, pcm, pcm_ptr + this.channels * F2_5, pcm, pcm_ptr + this.channels * F2_5, F2_5, this.channels, window, this.Fs); + } + if (transition) { + if (audiosize >= F5) { + for (i = 0; i < this.channels * F2_5; ++i) { + pcm[i] = pcm_transition[i]; + } + CodecHelpers.smooth_fade(pcm_transition, this.channels * F2_5, pcm, pcm_ptr + this.channels * F2_5, pcm, pcm_ptr + this.channels * F2_5, F2_5, this.channels, window, this.Fs); + } else { + CodecHelpers.smooth_fade(pcm_transition, 0, pcm, pcm_ptr, pcm, pcm_ptr, F2_5, this.channels, window, this.Fs); + } + } + if (this.decode_gain != 0) { + int gain = Inlines.celt_exp2(Inlines.MULT16_16_P15(21771, this.decode_gain)); + for (i = pcm_ptr; i < pcm_ptr + frame_size * this.channels; ++i) { + int x = Inlines.MULT16_32_P16(pcm[i], gain); + pcm[i] = (short)Inlines.SATURATE(x, Short.MAX_VALUE); + } + } + this.rangeFinal = len <= 1 ? 0 : (int)dec.rng ^ redundant_rng; + this.prev_mode = mode; + this.prev_redundancy = redundancy != 0 && celt_to_silk == 0 ? 1 : 0; + return celt_ret < 0 ? celt_ret : audiosize; + } + + int opus_decode_native(byte[] data, int data_ptr, int len, short[] pcm_out, int pcm_out_ptr, int frame_size, int decode_fec, int self_delimited, BoxedValueInt packet_offset, int soft_clip) { + packet_offset.Val = 0; + short[] size = new short[48]; + if (decode_fec < 0 || decode_fec > 1) { + return OpusError.OPUS_BAD_ARG; + } + if ((decode_fec != 0 || len == 0 || data == null) && frame_size % (this.Fs / 400) != 0) { + return OpusError.OPUS_BAD_ARG; + } + if (len == 0 || data == null) { + int ret; + int pcm_count = 0; + do { + if ((ret = this.opus_decode_frame(null, 0, 0, pcm_out, pcm_out_ptr + pcm_count * this.channels, frame_size - pcm_count, 0)) >= 0) continue; + return ret; + } while ((pcm_count += ret) < frame_size); + Inlines.OpusAssert(pcm_count == frame_size); + this.last_packet_duration = pcm_count; + return pcm_count; + } + if (len < 0) { + return OpusError.OPUS_BAD_ARG; + } + OpusMode packet_mode = OpusPacketInfo.getEncoderMode(data, data_ptr); + OpusBandwidth packet_bandwidth = OpusPacketInfo.getBandwidth(data, data_ptr); + int packet_frame_size = OpusPacketInfo.getNumSamplesPerFrame(data, data_ptr, this.Fs); + int packet_stream_channels = OpusPacketInfo.getNumEncodedChannels(data, data_ptr); + BoxedValueByte boxed_toc = new BoxedValueByte(0); + BoxedValueInt boxed_offset = new BoxedValueInt(0); + int count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, boxed_toc, null, 0, size, 0, boxed_offset, packet_offset); + int offset = boxed_offset.Val; + if (count < 0) { + return count; + } + data_ptr += offset; + if (decode_fec != 0) { + int ret; + BoxedValueInt dummy = new BoxedValueInt(0); + if (frame_size < packet_frame_size || packet_mode == OpusMode.MODE_CELT_ONLY || this.mode == OpusMode.MODE_CELT_ONLY) { + return this.opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size, 0, 0, dummy, soft_clip); + } + int duration_copy = this.last_packet_duration; + if (frame_size - packet_frame_size != 0) { + ret = this.opus_decode_native(null, 0, 0, pcm_out, pcm_out_ptr, frame_size - packet_frame_size, 0, 0, dummy, soft_clip); + if (ret < 0) { + this.last_packet_duration = duration_copy; + return ret; + } + Inlines.OpusAssert(ret == frame_size - packet_frame_size); + } + this.mode = packet_mode; + this.bandwidth = packet_bandwidth; + this.frame_size = packet_frame_size; + this.stream_channels = packet_stream_channels; + ret = this.opus_decode_frame(data, data_ptr, size[0], pcm_out, pcm_out_ptr + this.channels * (frame_size - packet_frame_size), packet_frame_size, 1); + if (ret < 0) { + return ret; + } + this.last_packet_duration = frame_size; + return frame_size; + } + if (count * packet_frame_size > frame_size) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + this.mode = packet_mode; + this.bandwidth = packet_bandwidth; + this.frame_size = packet_frame_size; + this.stream_channels = packet_stream_channels; + int nb_samples = 0; + for (int i = 0; i < count; ++i) { + int ret = this.opus_decode_frame(data, data_ptr, size[i], pcm_out, pcm_out_ptr + nb_samples * this.channels, frame_size - nb_samples, 0); + if (ret < 0) { + return ret; + } + Inlines.OpusAssert(ret == packet_frame_size); + data_ptr += size[i]; + nb_samples += ret; + } + this.last_packet_duration = nb_samples; + return nb_samples; + } + + public int decode(byte[] in_data, int in_data_offset, int len, short[] out_pcm, int out_pcm_offset, int frame_size, boolean decode_fec) throws OpusException { + if (frame_size <= 0) { + throw new IllegalArgumentException("Frame size must be > 0"); + } + try { + BoxedValueInt dummy = new BoxedValueInt(0); + int ret = this.opus_decode_native(in_data, in_data_offset, len, out_pcm, out_pcm_offset, frame_size, decode_fec ? 1 : 0, 0, dummy, 0); + if (ret < 0) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG while decoding"); + } + throw new OpusException("An error occurred during decoding", ret); + } + return ret; + } + catch (ArithmeticException e) { + throw new OpusException("Internal error during decoding: " + e.getMessage()); + } + } + + public int decode(byte[] in_data, int in_data_offset, int len, byte[] out_pcm, int out_pcm_offset, int frame_size, boolean decode_fec) throws OpusException { + short[] spcm = new short[Math.min(frame_size, 5760) * this.channels]; + int decSamples = this.decode(in_data, in_data_offset, len, spcm, 0, frame_size, decode_fec); + int idx = out_pcm_offset; + for (int c = 0; c < spcm.length; ++c) { + out_pcm[idx++] = (byte)(spcm[c] & 0xFF); + out_pcm[idx++] = (byte)(spcm[c] >> 8 & 0xFF); + } + return decSamples; + } + + public OpusBandwidth getBandwidth() { + return this.bandwidth; + } + + public int getFinalRange() { + return this.rangeFinal; + } + + public int getSampleRate() { + return this.Fs; + } + + public int getPitch() { + if (this.prev_mode == OpusMode.MODE_CELT_ONLY) { + return this.Celt_Decoder.GetPitch(); + } + return this.DecControl.prevPitchLag; + } + + public int getGain() { + return this.decode_gain; + } + + public void setGain(int value) { + if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { + throw new IllegalArgumentException("Gain must be within the range of a signed int16"); + } + this.decode_gain = value; + } + + public int getLastPacketDuration() { + return this.last_packet_duration; + } + + public void resetState() { + this.partialReset(); + this.Celt_Decoder.ResetState(); + DecodeAPI.silk_InitDecoder(this.SilkDecoder); + this.stream_channels = this.channels; + this.frame_size = this.Fs / 400; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusEncoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusEncoder.java new file mode 100644 index 0000000..4f7edc8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusEncoder.java @@ -0,0 +1,1062 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Analysis; +import de.maxhenkel.voicechat.concentus.AnalysisInfo; +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltEncoder; +import de.maxhenkel.voicechat.concentus.CeltMode; +import de.maxhenkel.voicechat.concentus.CodecHelpers; +import de.maxhenkel.voicechat.concentus.EncControlState; +import de.maxhenkel.voicechat.concentus.EncodeAPI; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.OpusApplication; +import de.maxhenkel.voicechat.concentus.OpusBandwidth; +import de.maxhenkel.voicechat.concentus.OpusBandwidthHelpers; +import de.maxhenkel.voicechat.concentus.OpusError; +import de.maxhenkel.voicechat.concentus.OpusException; +import de.maxhenkel.voicechat.concentus.OpusFramesize; +import de.maxhenkel.voicechat.concentus.OpusMode; +import de.maxhenkel.voicechat.concentus.OpusRepacketizer; +import de.maxhenkel.voicechat.concentus.OpusSignal; +import de.maxhenkel.voicechat.concentus.OpusTables; +import de.maxhenkel.voicechat.concentus.SilkEncoder; +import de.maxhenkel.voicechat.concentus.StereoWidthState; +import de.maxhenkel.voicechat.concentus.TonalityAnalysisState; + +public class OpusEncoder { + final EncControlState silk_mode = new EncControlState(); + OpusApplication application; + int channels; + int delay_compensation; + int force_channels; + OpusSignal signal_type; + OpusBandwidth user_bandwidth; + OpusBandwidth max_bandwidth; + OpusMode user_forced_mode; + int voice_ratio; + int Fs; + int use_vbr; + int vbr_constraint; + OpusFramesize variable_duration; + int bitrate_bps; + int user_bitrate_bps; + int lsb_depth; + int encoder_buffer; + int lfe; + final TonalityAnalysisState analysis = new TonalityAnalysisState(); + int stream_channels; + short hybrid_stereo_width_Q14; + int variable_HP_smth2_Q15; + int prev_HB_gain; + final int[] hp_mem = new int[4]; + OpusMode mode; + OpusMode prev_mode; + int prev_channels; + int prev_framesize; + OpusBandwidth bandwidth; + int silk_bw_switch; + int first; + int[] energy_masking; + final StereoWidthState width_mem = new StereoWidthState(); + final short[] delay_buffer = new short[960]; + OpusBandwidth detected_bandwidth; + int rangeFinal; + final SilkEncoder SilkEncoder = new SilkEncoder(); + final CeltEncoder Celt_Encoder = new CeltEncoder(); + + OpusEncoder() { + } + + void reset() { + this.silk_mode.Reset(); + this.application = OpusApplication.OPUS_APPLICATION_UNIMPLEMENTED; + this.channels = 0; + this.delay_compensation = 0; + this.force_channels = 0; + this.signal_type = OpusSignal.OPUS_SIGNAL_UNKNOWN; + this.user_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + this.max_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + this.user_forced_mode = OpusMode.MODE_UNKNOWN; + this.voice_ratio = 0; + this.Fs = 0; + this.use_vbr = 0; + this.vbr_constraint = 0; + this.variable_duration = OpusFramesize.OPUS_FRAMESIZE_UNKNOWN; + this.bitrate_bps = 0; + this.user_bitrate_bps = 0; + this.lsb_depth = 0; + this.encoder_buffer = 0; + this.lfe = 0; + this.analysis.Reset(); + this.PartialReset(); + } + + void PartialReset() { + this.stream_channels = 0; + this.hybrid_stereo_width_Q14 = 0; + this.variable_HP_smth2_Q15 = 0; + this.prev_HB_gain = 0; + Arrays.MemSet(this.hp_mem, 0, 4); + this.mode = OpusMode.MODE_UNKNOWN; + this.prev_mode = OpusMode.MODE_UNKNOWN; + this.prev_channels = 0; + this.prev_framesize = 0; + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + this.silk_bw_switch = 0; + this.first = 0; + this.energy_masking = null; + this.width_mem.Reset(); + Arrays.MemSet(this.delay_buffer, (short)0, 960); + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + this.rangeFinal = 0; + } + + public void resetState() { + EncControlState dummy = new EncControlState(); + this.analysis.Reset(); + this.PartialReset(); + this.Celt_Encoder.ResetState(); + EncodeAPI.silk_InitEncoder(this.SilkEncoder, dummy); + this.stream_channels = this.channels; + this.hybrid_stereo_width_Q14 = (short)16384; + this.prev_HB_gain = Short.MAX_VALUE; + this.first = 1; + this.mode = OpusMode.MODE_HYBRID; + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + this.variable_HP_smth2_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(60), 8); + } + + public OpusEncoder(int Fs, int channels, OpusApplication application) throws OpusException { + if (Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000) { + throw new IllegalArgumentException("Sample rate is invalid (must be 8/12/16/24/48 Khz)"); + } + if (channels != 1 && channels != 2) { + throw new IllegalArgumentException("Number of channels must be 1 or 2"); + } + int ret = this.opus_init_encoder(Fs, channels, application); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG when creating encoder"); + } + throw new OpusException("Error while initializing encoder", ret); + } + } + + int opus_init_encoder(int Fs, int channels, OpusApplication application) { + if (Fs != 48000 && Fs != 24000 && Fs != 16000 && Fs != 12000 && Fs != 8000 || channels != 1 && channels != 2 || application == OpusApplication.OPUS_APPLICATION_UNIMPLEMENTED) { + return OpusError.OPUS_BAD_ARG; + } + this.reset(); + SilkEncoder silk_enc = this.SilkEncoder; + CeltEncoder celt_enc = this.Celt_Encoder; + this.stream_channels = this.channels = channels; + this.Fs = Fs; + int ret = EncodeAPI.silk_InitEncoder(silk_enc, this.silk_mode); + if (ret != 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + this.silk_mode.nChannelsAPI = channels; + this.silk_mode.nChannelsInternal = channels; + this.silk_mode.API_sampleRate = this.Fs; + this.silk_mode.maxInternalSampleRate = 16000; + this.silk_mode.minInternalSampleRate = 8000; + this.silk_mode.desiredInternalSampleRate = 16000; + this.silk_mode.payloadSize_ms = 20; + this.silk_mode.bitRate = 25000; + this.silk_mode.packetLossPercentage = 0; + this.silk_mode.complexity = 9; + this.silk_mode.useInBandFEC = 0; + this.silk_mode.useDTX = 0; + this.silk_mode.useCBR = 0; + this.silk_mode.reducedDependency = 0; + int err = celt_enc.celt_encoder_init(Fs, channels); + if (err != OpusError.OPUS_OK) { + return OpusError.OPUS_INTERNAL_ERROR; + } + celt_enc.SetSignalling(0); + celt_enc.SetComplexity(this.silk_mode.complexity); + this.use_vbr = 1; + this.vbr_constraint = 1; + this.user_bitrate_bps = -1000; + this.bitrate_bps = 3000 + Fs * channels; + this.application = application; + this.signal_type = OpusSignal.OPUS_SIGNAL_AUTO; + this.user_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_AUTO; + this.max_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + this.force_channels = -1000; + this.user_forced_mode = OpusMode.MODE_AUTO; + this.voice_ratio = -1; + this.encoder_buffer = this.Fs / 100; + this.lsb_depth = 24; + this.variable_duration = OpusFramesize.OPUS_FRAMESIZE_ARG; + this.delay_compensation = this.Fs / 250; + this.hybrid_stereo_width_Q14 = (short)16384; + this.prev_HB_gain = Short.MAX_VALUE; + this.variable_HP_smth2_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(60), 8); + this.first = 1; + this.mode = OpusMode.MODE_HYBRID; + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + Analysis.tonality_analysis_init(this.analysis); + return OpusError.OPUS_OK; + } + + int user_bitrate_to_bitrate(int frame_size, int max_data_bytes) { + if (frame_size == 0) { + frame_size = this.Fs / 400; + } + if (this.user_bitrate_bps == -1000) { + return 60 * this.Fs / frame_size + this.Fs * this.channels; + } + if (this.user_bitrate_bps == -1) { + return max_data_bytes * 8 * this.Fs / frame_size; + } + return this.user_bitrate_bps; + } + + int opus_encode_native(short[] pcm, int pcm_ptr, int frame_size, byte[] data, int data_ptr, int out_data_bytes, int lsb_depth, short[] analysis_pcm, int analysis_pcm_ptr, int analysis_size, int c1, int c2, int analysis_channels, int float_api) { + int nb_compr_bytes; + int i; + int voice_est; + int ret = 0; + EntropyCoder enc = new EntropyCoder(); + boolean prefill = false; + int start_band = 0; + int redundancy = 0; + int redundancy_bytes = 0; + int celt_to_silk = 0; + boolean to_celt = false; + int redundant_rng = 0; + AnalysisInfo analysis_info = new AnalysisInfo(); + int analysis_read_pos_bak = -1; + int analysis_read_subframe_bak = -1; + int max_data_bytes = Inlines.IMIN(1276, out_data_bytes); + this.rangeFinal = 0; + if (this.variable_duration == OpusFramesize.OPUS_FRAMESIZE_UNKNOWN && 400 * frame_size != this.Fs && 200 * frame_size != this.Fs && 100 * frame_size != this.Fs && 50 * frame_size != this.Fs && 25 * frame_size != this.Fs && 50 * frame_size != 3 * this.Fs || 400 * frame_size < this.Fs || max_data_bytes <= 0) { + return OpusError.OPUS_BAD_ARG; + } + SilkEncoder silk_enc = this.SilkEncoder; + CeltEncoder celt_enc = this.Celt_Encoder; + int delay_compensation = this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY ? 0 : this.delay_compensation; + lsb_depth = Inlines.IMIN(lsb_depth, this.lsb_depth); + CeltMode celt_mode = celt_enc.GetMode(); + this.voice_ratio = -1; + if (this.analysis.enabled) { + analysis_info.valid = 0; + if (this.silk_mode.complexity >= 7 && this.Fs == 48000) { + analysis_read_pos_bak = this.analysis.read_pos; + analysis_read_subframe_bak = this.analysis.read_subframe; + Analysis.run_analysis(this.analysis, celt_mode, (short[])(analysis_pcm != null ? analysis_pcm : null), analysis_pcm_ptr, analysis_size, frame_size, c1, c2, analysis_channels, this.Fs, lsb_depth, analysis_info); + } + this.detected_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN; + if (analysis_info.valid != 0) { + int analysis_bandwidth; + if (this.signal_type == OpusSignal.OPUS_SIGNAL_AUTO) { + this.voice_ratio = (int)Math.floor(0.5f + 100.0f * (1.0f - analysis_info.music_prob)); + } + this.detected_bandwidth = (analysis_bandwidth = analysis_info.bandwidth) <= 12 ? OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND : (analysis_bandwidth <= 14 ? OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND : (analysis_bandwidth <= 16 ? OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND : (analysis_bandwidth <= 18 ? OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND : OpusBandwidth.OPUS_BANDWIDTH_FULLBAND))); + } + } + int stereo_width = this.channels == 2 && this.force_channels != 1 ? CodecHelpers.compute_stereo_width(pcm, pcm_ptr, frame_size, this.Fs, this.width_mem) : 0; + int total_buffer = delay_compensation; + this.bitrate_bps = this.user_bitrate_to_bitrate(frame_size, max_data_bytes); + int frame_rate = this.Fs / frame_size; + if (this.use_vbr == 0) { + int frame_rate3 = 3 * this.Fs / frame_size; + int cbrBytes = Inlines.IMIN((3 * this.bitrate_bps / 8 + frame_rate3 / 2) / frame_rate3, max_data_bytes); + this.bitrate_bps = cbrBytes * frame_rate3 * 8 / 3; + max_data_bytes = cbrBytes; + } + if (max_data_bytes < 3 || this.bitrate_bps < 3 * frame_rate * 8 || frame_rate < 50 && (max_data_bytes * frame_rate < 300 || this.bitrate_bps < 2400)) { + OpusBandwidth bw; + OpusMode tocmode = this.mode; + OpusBandwidth opusBandwidth = bw = this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN ? OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND : this.bandwidth; + if (tocmode == OpusMode.MODE_UNKNOWN) { + tocmode = OpusMode.MODE_SILK_ONLY; + } + if (frame_rate > 100) { + tocmode = OpusMode.MODE_CELT_ONLY; + } + if (frame_rate < 50) { + tocmode = OpusMode.MODE_SILK_ONLY; + } + if (tocmode == OpusMode.MODE_SILK_ONLY && OpusBandwidthHelpers.GetOrdinal(bw) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + bw = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } else if (tocmode == OpusMode.MODE_CELT_ONLY && OpusBandwidthHelpers.GetOrdinal(bw) == OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)) { + bw = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } else if (tocmode == OpusMode.MODE_HYBRID && OpusBandwidthHelpers.GetOrdinal(bw) <= OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND)) { + bw = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + } + data[data_ptr] = CodecHelpers.gen_toc(tocmode, frame_rate, bw, this.stream_channels); + ret = 1; + if (this.use_vbr == 0 && (ret = OpusRepacketizer.padPacket(data, data_ptr, ret, max_data_bytes)) == OpusError.OPUS_OK) { + ret = max_data_bytes; + } + return ret; + } + int max_rate = frame_rate * max_data_bytes * 8; + int equiv_rate = this.bitrate_bps - (40 * this.channels + 20) * (this.Fs / frame_size - 50); + if (this.signal_type == OpusSignal.OPUS_SIGNAL_VOICE) { + voice_est = 127; + } else if (this.signal_type == OpusSignal.OPUS_SIGNAL_MUSIC) { + voice_est = 0; + } else if (this.voice_ratio >= 0) { + voice_est = this.voice_ratio * 327 >> 8; + if (this.application == OpusApplication.OPUS_APPLICATION_AUDIO) { + voice_est = Inlines.IMIN(voice_est, 115); + } + } else { + voice_est = this.application == OpusApplication.OPUS_APPLICATION_VOIP ? 115 : 48; + } + if (this.force_channels != -1000 && this.channels == 2) { + this.stream_channels = this.force_channels; + } else if (this.channels == 2) { + int stereo_threshold = 30000 + (voice_est * voice_est * 0 >> 14); + stereo_threshold = this.stream_channels == 2 ? (stereo_threshold -= 1000) : (stereo_threshold += 1000); + this.stream_channels = equiv_rate > stereo_threshold ? 2 : 1; + } else { + this.stream_channels = this.channels; + } + equiv_rate = this.bitrate_bps - (40 * this.stream_channels + 20) * (this.Fs / frame_size - 50); + if (this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) { + this.mode = OpusMode.MODE_CELT_ONLY; + } else if (this.user_forced_mode == OpusMode.MODE_AUTO) { + int mode_voice = Inlines.MULT16_32_Q15(Short.MAX_VALUE - stereo_width, OpusTables.mode_thresholds[0][0]) + Inlines.MULT16_32_Q15(stereo_width, OpusTables.mode_thresholds[1][0]); + int mode_music = Inlines.MULT16_32_Q15(Short.MAX_VALUE - stereo_width, OpusTables.mode_thresholds[1][1]) + Inlines.MULT16_32_Q15(stereo_width, OpusTables.mode_thresholds[1][1]); + int threshold = mode_music + (voice_est * voice_est * (mode_voice - mode_music) >> 14); + if (this.application == OpusApplication.OPUS_APPLICATION_VOIP) { + threshold += 8000; + } + if (this.prev_mode == OpusMode.MODE_CELT_ONLY) { + threshold -= 4000; + } else if (this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN) { + threshold += 4000; + } + OpusMode opusMode = this.mode = equiv_rate >= threshold ? OpusMode.MODE_CELT_ONLY : OpusMode.MODE_SILK_ONLY; + if (this.silk_mode.useInBandFEC != 0 && this.silk_mode.packetLossPercentage > 128 - voice_est >> 4) { + this.mode = OpusMode.MODE_SILK_ONLY; + } + if (this.silk_mode.useDTX != 0 && voice_est > 100) { + this.mode = OpusMode.MODE_SILK_ONLY; + } + } else { + this.mode = this.user_forced_mode; + } + if (this.mode != OpusMode.MODE_CELT_ONLY && frame_size < this.Fs / 100) { + this.mode = OpusMode.MODE_CELT_ONLY; + } + if (this.lfe != 0) { + this.mode = OpusMode.MODE_CELT_ONLY; + } + if (max_data_bytes < (frame_rate > 50 ? 12000 : 8000) * frame_size / (this.Fs * 8)) { + this.mode = OpusMode.MODE_CELT_ONLY; + } + if (this.stream_channels == 1 && this.prev_channels == 2 && this.silk_mode.toMono == 0 && this.mode != OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY) { + this.silk_mode.toMono = 1; + this.stream_channels = 2; + } else { + this.silk_mode.toMono = 0; + } + if (this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN && (this.mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY || this.mode == OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY)) { + redundancy = 1; + int n = celt_to_silk = this.mode != OpusMode.MODE_CELT_ONLY ? 1 : 0; + if (celt_to_silk == 0) { + if (frame_size >= this.Fs / 100) { + this.mode = this.prev_mode; + to_celt = true; + } else { + redundancy = 0; + } + } + } + if (this.silk_bw_switch != 0) { + redundancy = 1; + celt_to_silk = 1; + this.silk_bw_switch = 0; + prefill = true; + } + if (redundancy != 0) { + redundancy_bytes = Inlines.IMIN(257, max_data_bytes * (this.Fs / 200) / (frame_size + this.Fs / 200)); + if (this.use_vbr != 0) { + redundancy_bytes = Inlines.IMIN(redundancy_bytes, this.bitrate_bps / 1600); + } + } + if (this.mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY) { + EncControlState dummy = new EncControlState(); + EncodeAPI.silk_InitEncoder(silk_enc, dummy); + prefill = true; + } + if (this.mode == OpusMode.MODE_CELT_ONLY || this.first != 0 || this.silk_mode.allowBandwidthSwitch != 0) { + int threshold; + int[] music_bandwidth_thresholds; + int[] voice_bandwidth_thresholds; + int[] bandwidth_thresholds = new int[8]; + OpusBandwidth bandwidth = OpusBandwidth.OPUS_BANDWIDTH_FULLBAND; + int equiv_rate2 = equiv_rate; + if (this.mode != OpusMode.MODE_CELT_ONLY) { + equiv_rate2 = equiv_rate2 * (45 + this.silk_mode.complexity) / 50; + if (this.use_vbr == 0) { + equiv_rate2 -= 1000; + } + } + if (this.channels == 2 && this.force_channels != 1) { + voice_bandwidth_thresholds = OpusTables.stereo_voice_bandwidth_thresholds; + music_bandwidth_thresholds = OpusTables.stereo_music_bandwidth_thresholds; + } else { + voice_bandwidth_thresholds = OpusTables.mono_voice_bandwidth_thresholds; + music_bandwidth_thresholds = OpusTables.mono_music_bandwidth_thresholds; + } + for (i = 0; i < 8; ++i) { + bandwidth_thresholds[i] = music_bandwidth_thresholds[i] + (voice_est * voice_est * (voice_bandwidth_thresholds[i] - music_bandwidth_thresholds[i]) >> 14); + } + do { + threshold = bandwidth_thresholds[2 * (OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND))]; + int hysteresis = bandwidth_thresholds[2 * (OpusBandwidthHelpers.GetOrdinal(bandwidth) - OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)) + 1]; + if (this.first != 0) continue; + if (OpusBandwidthHelpers.GetOrdinal(this.bandwidth) >= OpusBandwidthHelpers.GetOrdinal(bandwidth)) { + threshold -= hysteresis; + continue; + } + threshold += hysteresis; + } while (equiv_rate2 < threshold && OpusBandwidthHelpers.GetOrdinal(bandwidth = OpusBandwidthHelpers.SUBTRACT(bandwidth, 1)) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND)); + this.bandwidth = bandwidth; + if (this.first == 0 && this.mode != OpusMode.MODE_CELT_ONLY && this.silk_mode.inWBmodeWithoutVariableLP == 0 && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + } + if (OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(this.max_bandwidth)) { + this.bandwidth = this.max_bandwidth; + } + if (this.user_bandwidth != OpusBandwidth.OPUS_BANDWIDTH_AUTO) { + this.bandwidth = this.user_bandwidth; + } + if (this.mode != OpusMode.MODE_CELT_ONLY && max_rate < 15000) { + this.bandwidth = OpusBandwidthHelpers.MIN(this.bandwidth, OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); + } + if (this.Fs <= 24000 && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND; + } + if (this.Fs <= 16000 && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + if (this.Fs <= 12000 && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + } + if (this.Fs <= 8000 && OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND)) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + if (this.detected_bandwidth != OpusBandwidth.OPUS_BANDWIDTH_UNKNOWN && this.user_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_AUTO) { + OpusBandwidth min_detected_bandwidth = equiv_rate <= 18000 * this.stream_channels && this.mode == OpusMode.MODE_CELT_ONLY ? OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND : (equiv_rate <= 24000 * this.stream_channels && this.mode == OpusMode.MODE_CELT_ONLY ? OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND : (equiv_rate <= 30000 * this.stream_channels ? OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND : (equiv_rate <= 44000 * this.stream_channels ? OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND : OpusBandwidth.OPUS_BANDWIDTH_FULLBAND))); + this.detected_bandwidth = OpusBandwidthHelpers.MAX(this.detected_bandwidth, min_detected_bandwidth); + this.bandwidth = OpusBandwidthHelpers.MIN(this.bandwidth, this.detected_bandwidth); + } + celt_enc.SetLSBDepth(lsb_depth); + if (this.mode == OpusMode.MODE_CELT_ONLY && this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + if (this.lfe != 0) { + this.bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + if (frame_size > this.Fs / 50 && (this.mode == OpusMode.MODE_CELT_ONLY || OpusBandwidthHelpers.GetOrdinal(this.bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND))) { + if (this.analysis.enabled && analysis_read_pos_bak != -1) { + this.analysis.read_pos = analysis_read_pos_bak; + this.analysis.read_subframe = analysis_read_subframe_bak; + } + int nb_frames = frame_size > this.Fs / 25 ? 3 : 2; + int bytes_per_frame = Inlines.IMIN(1276, (out_data_bytes - 3) / nb_frames); + byte[] tmp_data = new byte[nb_frames * bytes_per_frame]; + OpusRepacketizer rp = new OpusRepacketizer(); + OpusMode bak_mode = this.user_forced_mode; + OpusBandwidth bak_bandwidth = this.user_bandwidth; + int bak_channels = this.force_channels; + this.user_forced_mode = this.mode; + this.user_bandwidth = this.bandwidth; + this.force_channels = this.stream_channels; + int bak_to_mono = this.silk_mode.toMono; + if (bak_to_mono != 0) { + this.force_channels = 1; + } else { + this.prev_channels = this.stream_channels; + } + for (i = 0; i < nb_frames; ++i) { + int tmp_len; + this.silk_mode.toMono = 0; + if (to_celt && i == nb_frames - 1) { + this.user_forced_mode = OpusMode.MODE_CELT_ONLY; + } + if ((tmp_len = this.opus_encode_native(pcm, pcm_ptr + i * (this.channels * this.Fs / 50), this.Fs / 50, tmp_data, i * bytes_per_frame, bytes_per_frame, lsb_depth, null, 0, 0, c1, c2, analysis_channels, float_api)) < 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + ret = rp.addPacket(tmp_data, i * bytes_per_frame, tmp_len); + if (ret >= 0) continue; + return OpusError.OPUS_INTERNAL_ERROR; + } + int repacketize_len = this.use_vbr != 0 ? out_data_bytes : Inlines.IMIN(3 * this.bitrate_bps / (1200 / nb_frames), out_data_bytes); + ret = rp.opus_repacketizer_out_range_impl(0, nb_frames, data, data_ptr, repacketize_len, 0, this.use_vbr == 0 ? 1 : 0); + if (ret < 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + this.user_forced_mode = bak_mode; + this.user_bandwidth = bak_bandwidth; + this.force_channels = bak_channels; + this.silk_mode.toMono = bak_to_mono; + return ret; + } + OpusBandwidth curr_bandwidth = this.bandwidth; + if (this.mode == OpusMode.MODE_SILK_ONLY && OpusBandwidthHelpers.GetOrdinal(curr_bandwidth) > OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + this.mode = OpusMode.MODE_HYBRID; + } + if (this.mode == OpusMode.MODE_HYBRID && OpusBandwidthHelpers.GetOrdinal(curr_bandwidth) <= OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)) { + this.mode = OpusMode.MODE_SILK_ONLY; + } + int bytes_target = Inlines.IMIN(max_data_bytes - redundancy_bytes, this.bitrate_bps * frame_size / (this.Fs * 8)) - 1; + enc.enc_init(data, ++data_ptr, max_data_bytes - 1); + short[] pcm_buf = new short[(total_buffer + frame_size) * this.channels]; + System.arraycopy(this.delay_buffer, (this.encoder_buffer - total_buffer) * this.channels, pcm_buf, 0, total_buffer * this.channels); + int hp_freq_smth1 = this.mode == OpusMode.MODE_CELT_ONLY ? Inlines.silk_LSHIFT(Inlines.silk_lin2log(60), 8) : silk_enc.state_Fxx[0].variable_HP_smth1_Q15; + this.variable_HP_smth2_Q15 = Inlines.silk_SMLAWB(this.variable_HP_smth2_Q15, hp_freq_smth1 - this.variable_HP_smth2_Q15, 983); + int cutoff_Hz = Inlines.silk_log2lin(Inlines.silk_RSHIFT(this.variable_HP_smth2_Q15, 8)); + if (this.application == OpusApplication.OPUS_APPLICATION_VOIP) { + CodecHelpers.hp_cutoff(pcm, pcm_ptr, cutoff_Hz, pcm_buf, total_buffer * this.channels, this.hp_mem, frame_size, this.channels, this.Fs); + } else { + CodecHelpers.dc_reject(pcm, pcm_ptr, 3, pcm_buf, total_buffer * this.channels, this.hp_mem, frame_size, this.channels, this.Fs); + } + int HB_gain = Short.MAX_VALUE; + if (this.mode != OpusMode.MODE_CELT_ONLY) { + short[] pcm_silk = new short[this.channels * frame_size]; + int total_bitRate = 8 * bytes_target * frame_rate; + if (this.mode == OpusMode.MODE_HYBRID) { + this.silk_mode.bitRate = this.stream_channels * (5000 + (this.Fs == 100 * frame_size ? 1000 : 0)); + this.silk_mode.bitRate = curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND ? (this.silk_mode.bitRate += (total_bitRate - this.silk_mode.bitRate) * 2 / 3) : (this.silk_mode.bitRate += (total_bitRate - this.silk_mode.bitRate) * 3 / 5); + if (this.silk_mode.bitRate > total_bitRate * 4 / 5) { + this.silk_mode.bitRate = total_bitRate * 4 / 5; + } + if (this.energy_masking == null) { + int celt_rate = total_bitRate - this.silk_mode.bitRate; + int HB_gain_ref = curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND ? 3000 : 3600; + HB_gain = Inlines.SHL32(celt_rate, 9) / Inlines.SHR32(celt_rate + this.stream_channels * HB_gain_ref, 6); + HB_gain = HB_gain < 28086 ? HB_gain + 4681 : Short.MAX_VALUE; + } + } else { + this.silk_mode.bitRate = total_bitRate; + } + if (this.energy_masking != null && this.use_vbr != 0 && this.lfe == 0) { + int mask_sum = 0; + int end = 17; + int srate = 16000; + if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) { + end = 13; + srate = 8000; + } else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + end = 15; + srate = 12000; + } + for (int c = 0; c < this.channels; ++c) { + for (i = 0; i < end; ++i) { + int mask = Inlines.MAX16(Inlines.MIN16(this.energy_masking[21 * c + i], 512), -2048); + if (mask > 0) { + mask = Inlines.HALF16(mask); + } + mask_sum += mask; + } + } + int masking_depth = mask_sum / end * this.channels; + int rate_offset = Inlines.PSHR32(Inlines.MULT16_16(srate, masking_depth += 205), 10); + rate_offset = Inlines.MAX32(rate_offset, -2 * this.silk_mode.bitRate / 3); + this.silk_mode.bitRate = this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND || this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_FULLBAND ? (this.silk_mode.bitRate += 3 * rate_offset / 5) : (this.silk_mode.bitRate += rate_offset); + bytes_target += rate_offset * frame_size / (8 * this.Fs); + } + this.silk_mode.payloadSize_ms = 1000 * frame_size / this.Fs; + this.silk_mode.nChannelsAPI = this.channels; + this.silk_mode.nChannelsInternal = this.stream_channels; + if (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) { + this.silk_mode.desiredInternalSampleRate = 8000; + } else if (curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + this.silk_mode.desiredInternalSampleRate = 12000; + } else { + Inlines.OpusAssert(this.mode == OpusMode.MODE_HYBRID || curr_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); + this.silk_mode.desiredInternalSampleRate = 16000; + } + this.silk_mode.minInternalSampleRate = this.mode == OpusMode.MODE_HYBRID ? 16000 : 8000; + if (this.mode == OpusMode.MODE_SILK_ONLY) { + int effective_max_rate = max_rate; + this.silk_mode.maxInternalSampleRate = 16000; + if (frame_rate > 50) { + effective_max_rate = effective_max_rate * 2 / 3; + } + if (effective_max_rate < 13000) { + this.silk_mode.maxInternalSampleRate = 12000; + this.silk_mode.desiredInternalSampleRate = Inlines.IMIN(12000, this.silk_mode.desiredInternalSampleRate); + } + if (effective_max_rate < 9600) { + this.silk_mode.maxInternalSampleRate = 8000; + this.silk_mode.desiredInternalSampleRate = Inlines.IMIN(8000, this.silk_mode.desiredInternalSampleRate); + } + } else { + this.silk_mode.maxInternalSampleRate = 16000; + } + this.silk_mode.useCBR = this.use_vbr == 0 ? 1 : 0; + int nBytes = Inlines.IMIN(1275, max_data_bytes - 1 - redundancy_bytes); + this.silk_mode.maxBits = nBytes * 8; + if (this.mode == OpusMode.MODE_HYBRID) { + this.silk_mode.maxBits = this.silk_mode.maxBits * 9 / 10; + } + if (this.silk_mode.useCBR != 0) { + this.silk_mode.maxBits = this.silk_mode.bitRate * frame_size / (this.Fs * 8) * 8; + this.silk_mode.bitRate = Inlines.IMAX(1, this.silk_mode.bitRate - 2000); + } + if (prefill) { + BoxedValueInt zero = new BoxedValueInt(0); + int prefill_offset = this.channels * (this.encoder_buffer - this.delay_compensation - this.Fs / 400); + CodecHelpers.gain_fade(this.delay_buffer, prefill_offset, 0, Short.MAX_VALUE, celt_mode.overlap, this.Fs / 400, this.channels, celt_mode.window, this.Fs); + Arrays.MemSet(this.delay_buffer, (short)0, prefill_offset); + System.arraycopy(this.delay_buffer, 0, pcm_silk, 0, this.encoder_buffer * this.channels); + EncodeAPI.silk_Encode(silk_enc, this.silk_mode, pcm_silk, this.encoder_buffer, null, zero, 1); + } + System.arraycopy(pcm_buf, total_buffer * this.channels, pcm_silk, 0, frame_size * this.channels); + BoxedValueInt boxed_silkBytes = new BoxedValueInt(nBytes); + ret = EncodeAPI.silk_Encode(silk_enc, this.silk_mode, pcm_silk, frame_size, enc, boxed_silkBytes, 0); + nBytes = boxed_silkBytes.Val; + if (ret != 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + if (nBytes == 0) { + this.rangeFinal = 0; + data[data_ptr - 1] = CodecHelpers.gen_toc(this.mode, this.Fs / frame_size, curr_bandwidth, this.stream_channels); + return 1; + } + if (this.mode == OpusMode.MODE_SILK_ONLY) { + if (this.silk_mode.internalSampleRate == 8000) { + curr_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } else if (this.silk_mode.internalSampleRate == 12000) { + curr_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND; + } else if (this.silk_mode.internalSampleRate == 16000) { + curr_bandwidth = OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND; + } + } else { + Inlines.OpusAssert(this.silk_mode.internalSampleRate == 16000); + } + this.silk_mode.opusCanSwitch = this.silk_mode.switchReady; + if (this.silk_mode.opusCanSwitch != 0) { + redundancy = 1; + celt_to_silk = 0; + this.silk_bw_switch = 1; + } + } + int endband = 21; + switch (curr_bandwidth) { + case OPUS_BANDWIDTH_NARROWBAND: { + endband = 13; + break; + } + case OPUS_BANDWIDTH_MEDIUMBAND: + case OPUS_BANDWIDTH_WIDEBAND: { + endband = 17; + break; + } + case OPUS_BANDWIDTH_SUPERWIDEBAND: { + endband = 19; + break; + } + case OPUS_BANDWIDTH_FULLBAND: { + endband = 21; + } + } + celt_enc.SetEndBand(endband); + celt_enc.SetChannels(this.stream_channels); + celt_enc.SetBitrate(-1); + if (this.mode != OpusMode.MODE_SILK_ONLY) { + int celt_pred = 2; + celt_enc.SetVBR(false); + if (this.silk_mode.reducedDependency != 0) { + celt_pred = 0; + } + celt_enc.SetPrediction(celt_pred); + if (this.mode == OpusMode.MODE_HYBRID) { + int len = enc.tell() + 7 >> 3; + if (redundancy != 0) { + len += this.mode == OpusMode.MODE_HYBRID ? 3 : 1; + } + nb_compr_bytes = this.use_vbr != 0 ? len + bytes_target - this.silk_mode.bitRate * frame_size / (8 * this.Fs) : (len > bytes_target ? len : bytes_target); + } else if (this.use_vbr != 0) { + int bonus = 0; + if (this.analysis.enabled && this.variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size != this.Fs / 50) { + bonus = (60 * this.stream_channels + 40) * (this.Fs / frame_size - 50); + if (analysis_info.valid != 0) { + bonus = (int)((float)bonus * (1.0f + 0.5f * analysis_info.tonality)); + } + } + celt_enc.SetVBR(true); + celt_enc.SetVBRConstraint(this.vbr_constraint != 0); + celt_enc.SetBitrate(this.bitrate_bps + bonus); + nb_compr_bytes = max_data_bytes - 1 - redundancy_bytes; + } else { + nb_compr_bytes = bytes_target; + } + } else { + nb_compr_bytes = 0; + } + short[] tmp_prefill = new short[this.channels * this.Fs / 400]; + if (this.mode != OpusMode.MODE_SILK_ONLY && this.mode != this.prev_mode && this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN) { + System.arraycopy(this.delay_buffer, (this.encoder_buffer - total_buffer - this.Fs / 400) * this.channels, tmp_prefill, 0, this.channels * this.Fs / 400); + } + if (this.channels * (this.encoder_buffer - (frame_size + total_buffer)) > 0) { + Arrays.MemMove(this.delay_buffer, this.channels * frame_size, 0, this.channels * (this.encoder_buffer - frame_size - total_buffer)); + System.arraycopy(pcm_buf, 0, this.delay_buffer, this.channels * (this.encoder_buffer - frame_size - total_buffer), (frame_size + total_buffer) * this.channels); + } else { + System.arraycopy(pcm_buf, (frame_size + total_buffer - this.encoder_buffer) * this.channels, this.delay_buffer, 0, this.encoder_buffer * this.channels); + } + if (this.prev_HB_gain < Short.MAX_VALUE || HB_gain < Short.MAX_VALUE) { + CodecHelpers.gain_fade(pcm_buf, 0, this.prev_HB_gain, HB_gain, celt_mode.overlap, frame_size, this.channels, celt_mode.window, this.Fs); + } + this.prev_HB_gain = HB_gain; + if (this.mode != OpusMode.MODE_HYBRID || this.stream_channels == 1) { + this.silk_mode.stereoWidth_Q14 = Inlines.IMIN(16384, 2 * Inlines.IMAX(0, equiv_rate - 30000)); + } + if (this.energy_masking == null && this.channels == 2 && (this.hybrid_stereo_width_Q14 < 16384 || this.silk_mode.stereoWidth_Q14 < 16384)) { + int g1 = this.hybrid_stereo_width_Q14; + int g2 = this.silk_mode.stereoWidth_Q14; + g1 = g1 == 16384 ? Short.MAX_VALUE : Inlines.SHL16(g1, 1); + g2 = g2 == 16384 ? Short.MAX_VALUE : Inlines.SHL16(g2, 1); + CodecHelpers.stereo_fade(pcm_buf, g1, g2, celt_mode.overlap, frame_size, this.channels, celt_mode.window, this.Fs); + this.hybrid_stereo_width_Q14 = (short)this.silk_mode.stereoWidth_Q14; + } + if (this.mode != OpusMode.MODE_CELT_ONLY && enc.tell() + 17 + 20 * (this.mode == OpusMode.MODE_HYBRID ? 1 : 0) <= 8 * (max_data_bytes - 1)) { + if (this.mode == OpusMode.MODE_HYBRID && (redundancy != 0 || enc.tell() + 37 <= 8 * nb_compr_bytes)) { + enc.enc_bit_logp(redundancy, 12); + } + if (redundancy != 0) { + enc.enc_bit_logp(celt_to_silk, 1); + int max_redundancy = this.mode == OpusMode.MODE_HYBRID ? max_data_bytes - 1 - nb_compr_bytes : max_data_bytes - 1 - (enc.tell() + 7 >> 3); + redundancy_bytes = Inlines.IMIN(max_redundancy, this.bitrate_bps / 1600); + redundancy_bytes = Inlines.IMIN(257, Inlines.IMAX(2, redundancy_bytes)); + if (this.mode == OpusMode.MODE_HYBRID) { + enc.enc_uint(redundancy_bytes - 2, 256L); + } + } + } else { + redundancy = 0; + } + if (redundancy == 0) { + this.silk_bw_switch = 0; + redundancy_bytes = 0; + } + if (this.mode != OpusMode.MODE_CELT_ONLY) { + start_band = 17; + } + if (this.mode == OpusMode.MODE_SILK_ONLY) { + ret = enc.tell() + 7 >> 3; + enc.enc_done(); + nb_compr_bytes = ret; + } else { + nb_compr_bytes = Inlines.IMIN(max_data_bytes - 1 - redundancy_bytes, nb_compr_bytes); + enc.enc_shrink(nb_compr_bytes); + } + if (this.analysis.enabled && redundancy != 0 || this.mode != OpusMode.MODE_SILK_ONLY) { + analysis_info.enabled = this.analysis.enabled; + celt_enc.SetAnalysis(analysis_info); + } + if (redundancy != 0 && celt_to_silk != 0) { + celt_enc.SetStartBand(0); + celt_enc.SetVBR(false); + int err = celt_enc.celt_encode_with_ec(pcm_buf, 0, this.Fs / 200, data, data_ptr + nb_compr_bytes, redundancy_bytes, null); + if (err < 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + redundant_rng = celt_enc.GetFinalRange(); + celt_enc.ResetState(); + } + celt_enc.SetStartBand(start_band); + if (this.mode != OpusMode.MODE_SILK_ONLY) { + if (this.mode != this.prev_mode && this.prev_mode != OpusMode.MODE_AUTO && this.prev_mode != OpusMode.MODE_UNKNOWN) { + byte[] dummy = new byte[2]; + celt_enc.ResetState(); + celt_enc.celt_encode_with_ec(tmp_prefill, 0, this.Fs / 400, dummy, 0, 2, null); + celt_enc.SetPrediction(0); + } + if (enc.tell() <= 8 * nb_compr_bytes && (ret = celt_enc.celt_encode_with_ec(pcm_buf, 0, frame_size, null, 0, nb_compr_bytes, enc)) < 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + } + if (redundancy != 0 && celt_to_silk == 0) { + byte[] dummy = new byte[2]; + int N2 = this.Fs / 200; + int N4 = this.Fs / 400; + celt_enc.ResetState(); + celt_enc.SetStartBand(0); + celt_enc.SetPrediction(0); + celt_enc.celt_encode_with_ec(pcm_buf, this.channels * (frame_size - N2 - N4), N4, dummy, 0, 2, null); + int err = celt_enc.celt_encode_with_ec(pcm_buf, this.channels * (frame_size - N2), N2, data, data_ptr + nb_compr_bytes, redundancy_bytes, null); + if (err < 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + redundant_rng = celt_enc.GetFinalRange(); + } + data[--data_ptr] = CodecHelpers.gen_toc(this.mode, this.Fs / frame_size, curr_bandwidth, this.stream_channels); + this.rangeFinal = (int)enc.rng ^ redundant_rng; + this.prev_mode = to_celt ? OpusMode.MODE_CELT_ONLY : this.mode; + this.prev_channels = this.stream_channels; + this.prev_framesize = frame_size; + this.first = 0; + if (enc.tell() > (max_data_bytes - 1) * 8) { + if (max_data_bytes < 2) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[data_ptr + 1] = 0; + ret = 1; + this.rangeFinal = 0; + } else if (this.mode == OpusMode.MODE_SILK_ONLY && redundancy == 0) { + while (ret > 2 && data[data_ptr + ret] == 0) { + --ret; + } + } + ret += 1 + redundancy_bytes; + if (this.use_vbr == 0) { + if (OpusRepacketizer.padPacket(data, data_ptr, ret, max_data_bytes) != OpusError.OPUS_OK) { + return OpusError.OPUS_INTERNAL_ERROR; + } + ret = max_data_bytes; + } + return ret; + } + + public int encode(short[] in_pcm, int pcm_offset, int frame_size, byte[] out_data, int out_data_offset, int max_data_bytes) throws OpusException { + if (out_data_offset + max_data_bytes > out_data.length) { + throw new IllegalArgumentException("Output buffer is too small: Stated size is " + max_data_bytes + " bytes, actual size is " + (out_data.length - out_data_offset) + " bytes"); + } + int delay_compensation = this.application == OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY ? 0 : this.delay_compensation; + int internal_frame_size = CodecHelpers.compute_frame_size(in_pcm, pcm_offset, frame_size, this.variable_duration, this.channels, this.Fs, this.bitrate_bps, delay_compensation, this.analysis.subframe_mem, this.analysis.enabled); + if (pcm_offset + internal_frame_size > in_pcm.length) { + throw new IllegalArgumentException("Not enough samples provided in input signal: Expected " + internal_frame_size + " samples, found " + (in_pcm.length - pcm_offset)); + } + try { + int ret = this.opus_encode_native(in_pcm, pcm_offset, internal_frame_size, out_data, out_data_offset, max_data_bytes, 16, in_pcm, pcm_offset, frame_size, 0, -2, this.channels, 0); + if (ret < 0) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG while encoding"); + } + throw new OpusException("An error occurred during encoding", ret); + } + return ret; + } + catch (ArithmeticException e) { + throw new OpusException("Internal error during encoding: " + e.getMessage()); + } + } + + public int encode(byte[] in_pcm, int pcm_offset, int frame_size, byte[] out_data, int out_data_offset, int max_data_bytes) throws OpusException { + short[] spcm = new short[frame_size * this.channels]; + int idx = pcm_offset; + for (int c = 0; c < spcm.length; ++c) { + spcm[c] = (short)((in_pcm[idx] & 0xFF | in_pcm[idx + 1] << 8) & 0xFFFF); + idx += 2; + } + return this.encode(spcm, 0, frame_size, out_data, out_data_offset, max_data_bytes); + } + + public OpusApplication getApplication() { + return this.application; + } + + public void setApplication(OpusApplication value) { + if (this.first == 0 && this.application != value) { + throw new IllegalArgumentException("Application cannot be changed after encoding has started"); + } + this.application = value; + } + + public int getBitrate() { + return this.user_bitrate_to_bitrate(this.prev_framesize, 1276); + } + + public void setBitrate(int value) { + if (value != -1000 && value != -1) { + if (value <= 0) { + throw new IllegalArgumentException("Bitrate must be positive"); + } + if (value <= 500) { + value = 500; + } else if (value > 300000 * this.channels) { + value = 300000 * this.channels; + } + } + this.user_bitrate_bps = value; + } + + public int getForceChannels() { + return this.force_channels; + } + + public void setForceChannels(int value) { + if ((value < 1 || value > this.channels) && value != -1000) { + throw new IllegalArgumentException("Force channels must be <= num. of channels"); + } + this.force_channels = value; + } + + public OpusBandwidth getMaxBandwidth() { + return this.max_bandwidth; + } + + public void setMaxBandwidth(OpusBandwidth value) { + this.max_bandwidth = value; + this.silk_mode.maxInternalSampleRate = this.max_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND ? 8000 : (this.max_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND ? 12000 : 16000); + } + + public OpusBandwidth getBandwidth() { + return this.bandwidth; + } + + public void setBandwidth(OpusBandwidth value) { + this.user_bandwidth = value; + this.silk_mode.maxInternalSampleRate = this.user_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND ? 8000 : (this.user_bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND ? 12000 : 16000); + } + + public boolean getUseDTX() { + return this.silk_mode.useDTX != 0; + } + + public void setUseDTX(boolean value) { + this.silk_mode.useDTX = value ? 1 : 0; + } + + public int getComplexity() { + return this.silk_mode.complexity; + } + + public void setComplexity(int value) { + if (value < 0 || value > 10) { + throw new IllegalArgumentException("Complexity must be between 0 and 10"); + } + this.silk_mode.complexity = value; + this.Celt_Encoder.SetComplexity(value); + } + + public boolean getUseInbandFEC() { + return this.silk_mode.useInBandFEC != 0; + } + + public void setUseInbandFEC(boolean value) { + this.silk_mode.useInBandFEC = value ? 1 : 0; + } + + public int getPacketLossPercent() { + return this.silk_mode.packetLossPercentage; + } + + public void setPacketLossPercent(int value) { + if (value < 0 || value > 100) { + throw new IllegalArgumentException("Packet loss must be between 0 and 100"); + } + this.silk_mode.packetLossPercentage = value; + this.Celt_Encoder.SetPacketLossPercent(value); + } + + public boolean getUseVBR() { + return this.use_vbr != 0; + } + + public void setUseVBR(boolean value) { + this.use_vbr = value ? 1 : 0; + this.silk_mode.useCBR = value ? 0 : 1; + } + + public boolean getUseConstrainedVBR() { + return this.vbr_constraint != 0; + } + + public void setUseConstrainedVBR(boolean value) { + this.vbr_constraint = value ? 1 : 0; + } + + public OpusSignal getSignalType() { + return this.signal_type; + } + + public void setSignalType(OpusSignal value) { + this.signal_type = value; + } + + public int getLookahead() { + int returnVal = this.Fs / 400; + if (this.application != OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY) { + returnVal += this.delay_compensation; + } + return returnVal; + } + + public int getSampleRate() { + return this.Fs; + } + + public int getFinalRange() { + return this.rangeFinal; + } + + public int getLSBDepth() { + return this.lsb_depth; + } + + public void setLSBDepth(int value) { + if (value < 8 || value > 24) { + throw new IllegalArgumentException("LSB depth must be between 8 and 24"); + } + this.lsb_depth = value; + } + + public OpusFramesize getExpertFrameDuration() { + return this.variable_duration; + } + + public void setExpertFrameDuration(OpusFramesize value) { + this.variable_duration = value; + this.Celt_Encoder.SetExpertFrameDuration(value); + } + + public OpusMode getForceMode() { + return this.user_forced_mode; + } + + public void setForceMode(OpusMode value) { + this.user_forced_mode = value; + } + + public boolean getIsLFE() { + return this.lfe != 0; + } + + public void setIsLFE(boolean value) { + this.lfe = value ? 1 : 0; + this.Celt_Encoder.SetLFE(value ? 1 : 0); + } + + public boolean getPredictionDisabled() { + return this.silk_mode.reducedDependency != 0; + } + + public void setPredictionDisabled(boolean value) { + this.silk_mode.reducedDependency = value ? 1 : 0; + } + + public boolean getEnableAnalysis() { + return this.analysis.enabled; + } + + public void setEnableAnalysis(boolean value) { + this.analysis.enabled = value; + } + + void SetEnergyMask(int[] value) { + this.energy_masking = value; + this.Celt_Encoder.SetEnergyMask(value); + } + + CeltMode GetCeltMode() { + return this.Celt_Encoder.GetMode(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusError.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusError.java new file mode 100644 index 0000000..e32167e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusError.java @@ -0,0 +1,16 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +public class OpusError { + public static int OPUS_OK = 0; + public static int OPUS_BAD_ARG = -1; + public static int OPUS_BUFFER_TOO_SMALL = -2; + public static int OPUS_INTERNAL_ERROR = -3; + public static int OPUS_INVALID_PACKET = -4; + public static int OPUS_UNIMPLEMENTED = -5; + public static int OPUS_INVALID_STATE = -6; + public static int OPUS_ALLOC_FAIL = -7; +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusException.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusException.java new file mode 100644 index 0000000..95a1c05 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusException.java @@ -0,0 +1,31 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.CodecHelpers; + +public class OpusException +extends Exception { + private String _message; + private int _opus_error_code; + + public OpusException() { + this("", 0); + } + + public OpusException(String message) { + this(message, 1); + } + + public OpusException(String message, int opus_error_code) { + this._message = message + ": " + CodecHelpers.opus_strerror(opus_error_code); + this._opus_error_code = opus_error_code; + } + + @Override + public String getMessage() { + return this._message; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusFramesize.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusFramesize.java new file mode 100644 index 0000000..86e7eb9 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusFramesize.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +public enum OpusFramesize { + OPUS_FRAMESIZE_UNKNOWN, + OPUS_FRAMESIZE_ARG, + OPUS_FRAMESIZE_2_5_MS, + OPUS_FRAMESIZE_5_MS, + OPUS_FRAMESIZE_10_MS, + OPUS_FRAMESIZE_20_MS, + OPUS_FRAMESIZE_40_MS, + OPUS_FRAMESIZE_60_MS, + OPUS_FRAMESIZE_VARIABLE; + +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusFramesizeHelpers.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusFramesizeHelpers.java new file mode 100644 index 0000000..f7d121c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusFramesizeHelpers.java @@ -0,0 +1,42 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.OpusFramesize; + +class OpusFramesizeHelpers { + OpusFramesizeHelpers() { + } + + static int GetOrdinal(OpusFramesize size) { + switch (size) { + case OPUS_FRAMESIZE_ARG: { + return 1; + } + case OPUS_FRAMESIZE_2_5_MS: { + return 2; + } + case OPUS_FRAMESIZE_5_MS: { + return 3; + } + case OPUS_FRAMESIZE_10_MS: { + return 4; + } + case OPUS_FRAMESIZE_20_MS: { + return 5; + } + case OPUS_FRAMESIZE_40_MS: { + return 6; + } + case OPUS_FRAMESIZE_60_MS: { + return 7; + } + case OPUS_FRAMESIZE_VARIABLE: { + return 8; + } + } + return -1; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMSDecoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMSDecoder.java new file mode 100644 index 0000000..bd2d04b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMSDecoder.java @@ -0,0 +1,235 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.ChannelLayout; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.OpusBandwidth; +import de.maxhenkel.voicechat.concentus.OpusDecoder; +import de.maxhenkel.voicechat.concentus.OpusError; +import de.maxhenkel.voicechat.concentus.OpusException; +import de.maxhenkel.voicechat.concentus.OpusMultistream; +import de.maxhenkel.voicechat.concentus.OpusPacketInfo; + +public class OpusMSDecoder { + ChannelLayout layout = new ChannelLayout(); + OpusDecoder[] decoders = null; + + private OpusMSDecoder(int nb_streams, int nb_coupled_streams) { + this.decoders = new OpusDecoder[nb_streams]; + for (int c = 0; c < nb_streams; ++c) { + this.decoders[c] = new OpusDecoder(); + } + } + + int opus_multistream_decoder_init(int Fs, int channels, int streams, int coupled_streams, short[] mapping) { + int ret; + int i; + int decoder_ptr = 0; + if (channels > 255 || channels < 1 || coupled_streams > streams || streams < 1 || coupled_streams < 0 || streams > 255 - coupled_streams) { + throw new IllegalArgumentException("Invalid channel or coupled stream count"); + } + this.layout.nb_channels = channels; + this.layout.nb_streams = streams; + this.layout.nb_coupled_streams = coupled_streams; + for (i = 0; i < this.layout.nb_channels; ++i) { + this.layout.mapping[i] = mapping[i]; + } + if (OpusMultistream.validate_layout(this.layout) == 0) { + throw new IllegalArgumentException("Invalid surround channel layout"); + } + for (i = 0; i < this.layout.nb_coupled_streams; ++i) { + ret = this.decoders[decoder_ptr].opus_decoder_init(Fs, 2); + if (ret != OpusError.OPUS_OK) { + return ret; + } + ++decoder_ptr; + } + while (i < this.layout.nb_streams) { + ret = this.decoders[decoder_ptr].opus_decoder_init(Fs, 1); + if (ret != OpusError.OPUS_OK) { + return ret; + } + ++decoder_ptr; + ++i; + } + return OpusError.OPUS_OK; + } + + public static OpusMSDecoder create(int Fs, int channels, int streams, int coupled_streams, short[] mapping) throws OpusException { + if (channels > 255 || channels < 1 || coupled_streams > streams || streams < 1 || coupled_streams < 0 || streams > 255 - coupled_streams) { + throw new IllegalArgumentException("Invalid channel / stream configuration"); + } + OpusMSDecoder st = new OpusMSDecoder(streams, coupled_streams); + int ret = st.opus_multistream_decoder_init(Fs, channels, streams, coupled_streams, mapping); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("Bad argument while creating MS decoder"); + } + throw new OpusException("Could not create MS decoder", ret); + } + return st; + } + + static int opus_multistream_packet_validate(byte[] data, int data_ptr, int len, int nb_streams, int Fs) { + BoxedValueByte toc = new BoxedValueByte(0); + short[] size = new short[48]; + int samples = 0; + BoxedValueInt packet_offset = new BoxedValueInt(0); + BoxedValueInt dummy = new BoxedValueInt(0); + for (int s = 0; s < nb_streams; ++s) { + if (len <= 0) { + return OpusError.OPUS_INVALID_PACKET; + } + int count = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, s != nb_streams - 1 ? 1 : 0, toc, null, 0, size, 0, dummy, packet_offset); + if (count < 0) { + return count; + } + int tmp_samples = OpusPacketInfo.getNumSamples(data, data_ptr, packet_offset.Val, Fs); + if (s != 0 && samples != tmp_samples) { + return OpusError.OPUS_INVALID_PACKET; + } + samples = tmp_samples; + data_ptr += packet_offset.Val; + len -= packet_offset.Val; + } + return samples; + } + + int opus_multistream_decode_native(byte[] data, int data_ptr, int len, short[] pcm, int pcm_ptr, int frame_size, int decode_fec, int soft_clip) { + boolean do_plc = false; + int Fs = this.getSampleRate(); + frame_size = Inlines.IMIN(frame_size, Fs / 25 * 3); + short[] buf = new short[2 * frame_size]; + int decoder_ptr = 0; + if (len == 0) { + do_plc = true; + } + if (len < 0) { + return OpusError.OPUS_BAD_ARG; + } + if (!do_plc && len < 2 * this.layout.nb_streams - 1) { + return OpusError.OPUS_INVALID_PACKET; + } + if (!do_plc) { + int ret = OpusMSDecoder.opus_multistream_packet_validate(data, data_ptr, len, this.layout.nb_streams, Fs); + if (ret < 0) { + return ret; + } + if (ret > frame_size) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + } + for (int s = 0; s < this.layout.nb_streams; ++s) { + int chan; + int prev; + OpusDecoder dec = this.decoders[decoder_ptr++]; + if (!do_plc && len <= 0) { + return OpusError.OPUS_INTERNAL_ERROR; + } + BoxedValueInt packet_offset = new BoxedValueInt(0); + int ret = dec.opus_decode_native(data, data_ptr, len, buf, 0, frame_size, decode_fec, s != this.layout.nb_streams - 1 ? 1 : 0, packet_offset, soft_clip); + data_ptr += packet_offset.Val; + len -= packet_offset.Val; + if (ret <= 0) { + return ret; + } + frame_size = ret; + if (s < this.layout.nb_coupled_streams) { + prev = -1; + while ((chan = OpusMultistream.get_left_channel(this.layout, s, prev)) != -1) { + OpusMSDecoder.opus_copy_channel_out_short(pcm, pcm_ptr, this.layout.nb_channels, chan, buf, 0, 2, frame_size); + prev = chan; + } + prev = -1; + while ((chan = OpusMultistream.get_right_channel(this.layout, s, prev)) != -1) { + OpusMSDecoder.opus_copy_channel_out_short(pcm, pcm_ptr, this.layout.nb_channels, chan, buf, 1, 2, frame_size); + prev = chan; + } + continue; + } + prev = -1; + while ((chan = OpusMultistream.get_mono_channel(this.layout, s, prev)) != -1) { + OpusMSDecoder.opus_copy_channel_out_short(pcm, pcm_ptr, this.layout.nb_channels, chan, buf, 0, 1, frame_size); + prev = chan; + } + } + for (int c = 0; c < this.layout.nb_channels; ++c) { + if (this.layout.mapping[c] != 255) continue; + OpusMSDecoder.opus_copy_channel_out_short(pcm, pcm_ptr, this.layout.nb_channels, c, null, 0, 0, frame_size); + } + return frame_size; + } + + static void opus_copy_channel_out_short(short[] dst, int dst_ptr, int dst_stride, int dst_channel, short[] src, int src_ptr, int src_stride, int frame_size) { + if (src != null) { + for (int i = 0; i < frame_size; ++i) { + dst[i * dst_stride + dst_channel + dst_ptr] = src[i * src_stride + src_ptr]; + } + } else { + for (int i = 0; i < frame_size; ++i) { + dst[i * dst_stride + dst_channel + dst_ptr] = 0; + } + } + } + + public int decodeMultistream(byte[] data, int data_offset, int len, short[] out_pcm, int out_pcm_offset, int frame_size, int decode_fec) { + return this.opus_multistream_decode_native(data, data_offset, len, out_pcm, out_pcm_offset, frame_size, decode_fec, 0); + } + + public OpusBandwidth getBandwidth() { + if (this.decoders == null || this.decoders.length == 0) { + throw new IllegalStateException("Decoder not initialized"); + } + return this.decoders[0].getBandwidth(); + } + + public int getSampleRate() { + if (this.decoders == null || this.decoders.length == 0) { + throw new IllegalStateException("Decoder not initialized"); + } + return this.decoders[0].getSampleRate(); + } + + public int getGain() { + if (this.decoders == null || this.decoders.length == 0) { + throw new IllegalStateException("Decoder not initialized"); + } + return this.decoders[0].getGain(); + } + + public void setGain(int value) { + for (int s = 0; s < this.layout.nb_streams; ++s) { + this.decoders[s].setGain(value); + } + } + + public int getLastPacketDuration() { + if (this.decoders == null || this.decoders.length == 0) { + return OpusError.OPUS_INVALID_STATE; + } + return this.decoders[0].getLastPacketDuration(); + } + + public int getFinalRange() { + int value = 0; + for (int s = 0; s < this.layout.nb_streams; ++s) { + value ^= this.decoders[s].getFinalRange(); + } + return value; + } + + public void ResetState() { + for (int s = 0; s < this.layout.nb_streams; ++s) { + this.decoders[s].resetState(); + } + } + + public OpusDecoder GetMultistreamDecoderState(int streamId) { + return this.decoders[streamId]; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMSEncoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMSEncoder.java new file mode 100644 index 0000000..1877d9e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMSEncoder.java @@ -0,0 +1,735 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Bands; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltCommon; +import de.maxhenkel.voicechat.concentus.CeltMode; +import de.maxhenkel.voicechat.concentus.ChannelLayout; +import de.maxhenkel.voicechat.concentus.CodecHelpers; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.MDCT; +import de.maxhenkel.voicechat.concentus.OpusApplication; +import de.maxhenkel.voicechat.concentus.OpusBandwidth; +import de.maxhenkel.voicechat.concentus.OpusEncoder; +import de.maxhenkel.voicechat.concentus.OpusError; +import de.maxhenkel.voicechat.concentus.OpusException; +import de.maxhenkel.voicechat.concentus.OpusFramesize; +import de.maxhenkel.voicechat.concentus.OpusMode; +import de.maxhenkel.voicechat.concentus.OpusMultistream; +import de.maxhenkel.voicechat.concentus.OpusRepacketizer; +import de.maxhenkel.voicechat.concentus.OpusSignal; +import de.maxhenkel.voicechat.concentus.QuantizeBands; +import de.maxhenkel.voicechat.concentus.VorbisLayout; + +public class OpusMSEncoder { + final ChannelLayout layout = new ChannelLayout(); + int lfe_stream = 0; + OpusApplication application = OpusApplication.OPUS_APPLICATION_AUDIO; + OpusFramesize variable_duration = OpusFramesize.OPUS_FRAMESIZE_UNKNOWN; + int surround = 0; + int bitrate_bps = 0; + final float[] subframe_mem = new float[3]; + OpusEncoder[] encoders = null; + int[] window_mem = null; + int[] preemph_mem = null; + private static final int[] diff_table = new int[]{512, 300, 165, 87, 45, 23, 11, 6, 3, 0, 0, 0, 0, 0, 0, 0, 0}; + private final int MS_FRAME_TMP = 3832; + + private OpusMSEncoder(int nb_streams, int nb_coupled_streams) { + if (nb_streams < 1 || nb_coupled_streams > nb_streams || nb_coupled_streams < 0) { + throw new IllegalArgumentException("Invalid channel count in MS encoder"); + } + this.encoders = new OpusEncoder[nb_streams]; + for (int c = 0; c < nb_streams; ++c) { + this.encoders[c] = new OpusEncoder(); + } + int nb_channels = nb_coupled_streams * 2 + (nb_streams - nb_coupled_streams); + this.window_mem = new int[nb_channels * 120]; + this.preemph_mem = new int[nb_channels]; + } + + public void resetState() { + this.subframe_mem[2] = 0.0f; + this.subframe_mem[1] = 0.0f; + this.subframe_mem[0] = 0.0f; + if (this.surround != 0) { + Arrays.MemSet(this.preemph_mem, 0, this.layout.nb_channels); + Arrays.MemSet(this.window_mem, 0, this.layout.nb_channels * 120); + } + int encoder_ptr = 0; + for (int s = 0; s < this.layout.nb_streams; ++s) { + OpusEncoder enc = this.encoders[encoder_ptr++]; + enc.resetState(); + } + } + + static int validate_encoder_layout(ChannelLayout layout) { + for (int s = 0; s < layout.nb_streams; ++s) { + if (s < layout.nb_coupled_streams) { + if (OpusMultistream.get_left_channel(layout, s, -1) == -1) { + return 0; + } + if (OpusMultistream.get_right_channel(layout, s, -1) != -1) continue; + return 0; + } + if (OpusMultistream.get_mono_channel(layout, s, -1) != -1) continue; + return 0; + } + return 1; + } + + static void channel_pos(int channels, int[] pos) { + if (channels == 4) { + pos[0] = 1; + pos[1] = 3; + pos[2] = 1; + pos[3] = 3; + } else if (channels == 3 || channels == 5 || channels == 6) { + pos[0] = 1; + pos[1] = 2; + pos[2] = 3; + pos[3] = 1; + pos[4] = 3; + pos[5] = 0; + } else if (channels == 7) { + pos[0] = 1; + pos[1] = 2; + pos[2] = 3; + pos[3] = 1; + pos[4] = 3; + pos[5] = 2; + pos[6] = 0; + } else if (channels == 8) { + pos[0] = 1; + pos[1] = 2; + pos[2] = 3; + pos[3] = 1; + pos[4] = 3; + pos[5] = 1; + pos[6] = 3; + pos[7] = 0; + } + } + + static int logSum(int a, int b) { + int diff; + int max; + if (a > b) { + max = a; + diff = Inlines.SUB32(Inlines.EXTEND32(a), Inlines.EXTEND32(b)); + } else { + max = b; + diff = Inlines.SUB32(Inlines.EXTEND32(b), Inlines.EXTEND32(a)); + } + if (diff >= 8192) { + return max; + } + int low = Inlines.SHR32(diff, 9); + int frac = Inlines.SHL16(diff - Inlines.SHL16(low, 9), 6); + return max + diff_table[low] + Inlines.MULT16_16_Q15(frac, Inlines.SUB16(diff_table[low + 1], diff_table[low])); + } + + static void surround_analysis(CeltMode celt_mode, short[] pcm, int pcm_ptr, int[] bandLogE, int[] mem, int[] preemph_mem, int len, int overlap, int channels, int rate) { + int i; + int c; + int LM; + int[] pos = new int[]{0, 0, 0, 0, 0, 0, 0, 0}; + int[][] bandE = Arrays.InitTwoDimensionalArrayInt(1, 21); + int[][] maskLogE = Arrays.InitTwoDimensionalArrayInt(3, 21); + int upsample = CeltCommon.resampling_factor(rate); + int frame_size = len * upsample; + for (LM = 0; LM < celt_mode.maxLM && celt_mode.shortMdctSize << LM != frame_size; ++LM) { + } + int[] input = new int[frame_size + overlap]; + short[] x = new short[len]; + int[][] freq = Arrays.InitTwoDimensionalArrayInt(1, frame_size); + OpusMSEncoder.channel_pos(channels, pos); + for (c = 0; c < 3; ++c) { + for (i = 0; i < 21; ++i) { + maskLogE[c][i] = -28672; + } + } + for (c = 0; c < channels; ++c) { + System.arraycopy(mem, c * overlap, input, 0, overlap); + OpusMSEncoder.opus_copy_channel_in_short(x, 0, 1, pcm, pcm_ptr, channels, c, len); + BoxedValueInt boxed_preemph = new BoxedValueInt(preemph_mem[c]); + CeltCommon.celt_preemphasis(x, input, overlap, frame_size, 1, upsample, celt_mode.preemph, boxed_preemph, 0); + preemph_mem[c] = boxed_preemph.Val; + MDCT.clt_mdct_forward(celt_mode.mdct, input, 0, freq[0], 0, celt_mode.window, overlap, celt_mode.maxLM - LM, 1); + if (upsample != 1) { + int bound = len; + i = 0; + while (i < bound) { + int[] nArray = freq[0]; + int n = i++; + nArray[n] = nArray[n] * upsample; + } + while (i < frame_size) { + freq[0][i] = 0; + ++i; + } + } + Bands.compute_band_energies(celt_mode, freq, bandE, 21, 1, LM); + QuantizeBands.amp2Log2(celt_mode, 21, 21, bandE[0], bandLogE, 21 * c, 1); + for (i = 1; i < 21; ++i) { + bandLogE[21 * c + i] = Inlines.MAX16(bandLogE[21 * c + i], bandLogE[21 * c + i - 1] - 1024); + } + for (i = 19; i >= 0; --i) { + bandLogE[21 * c + i] = Inlines.MAX16(bandLogE[21 * c + i], bandLogE[21 * c + i + 1] - 2048); + } + if (pos[c] == 1) { + for (i = 0; i < 21; ++i) { + maskLogE[0][i] = OpusMSEncoder.logSum(maskLogE[0][i], bandLogE[21 * c + i]); + } + } else if (pos[c] == 3) { + for (i = 0; i < 21; ++i) { + maskLogE[2][i] = OpusMSEncoder.logSum(maskLogE[2][i], bandLogE[21 * c + i]); + } + } else if (pos[c] == 2) { + for (i = 0; i < 21; ++i) { + maskLogE[0][i] = OpusMSEncoder.logSum(maskLogE[0][i], bandLogE[21 * c + i] - 512); + maskLogE[2][i] = OpusMSEncoder.logSum(maskLogE[2][i], bandLogE[21 * c + i] - 512); + } + } + System.arraycopy(input, frame_size, mem, c * overlap, overlap); + } + for (i = 0; i < 21; ++i) { + maskLogE[1][i] = Inlines.MIN32(maskLogE[0][i], maskLogE[2][i]); + } + int channel_offset = Inlines.HALF16(Inlines.celt_log2(32768 / (channels - 1))); + for (c = 0; c < 3; ++c) { + i = 0; + while (i < 21) { + int[] nArray = maskLogE[c]; + int n = i++; + nArray[n] = nArray[n] + channel_offset; + } + } + for (c = 0; c < channels; ++c) { + if (pos[c] != 0) { + int[] mask = maskLogE[pos[c] - 1]; + for (i = 0; i < 21; ++i) { + bandLogE[21 * c + i] = bandLogE[21 * c + i] - mask[i]; + } + continue; + } + for (i = 0; i < 21; ++i) { + bandLogE[21 * c + i] = 0; + } + } + } + + int opus_multistream_encoder_init(int Fs, int channels, int streams, int coupled_streams, short[] mapping, OpusApplication application, int surround) { + int ret; + int i; + if (channels > 255 || channels < 1 || coupled_streams > streams || streams < 1 || coupled_streams < 0 || streams > 255 - coupled_streams) { + return OpusError.OPUS_BAD_ARG; + } + this.layout.nb_channels = channels; + this.layout.nb_streams = streams; + this.layout.nb_coupled_streams = coupled_streams; + this.subframe_mem[2] = 0.0f; + this.subframe_mem[1] = 0.0f; + this.subframe_mem[0] = 0.0f; + if (surround == 0) { + this.lfe_stream = -1; + } + this.bitrate_bps = -1000; + this.application = application; + this.variable_duration = OpusFramesize.OPUS_FRAMESIZE_ARG; + for (i = 0; i < this.layout.nb_channels; ++i) { + this.layout.mapping[i] = mapping[i]; + } + if (OpusMultistream.validate_layout(this.layout) == 0 || OpusMSEncoder.validate_encoder_layout(this.layout) == 0) { + return OpusError.OPUS_BAD_ARG; + } + int encoder_ptr = 0; + for (i = 0; i < this.layout.nb_coupled_streams; ++i) { + ret = this.encoders[encoder_ptr].opus_init_encoder(Fs, 2, application); + if (ret != OpusError.OPUS_OK) { + return ret; + } + if (i == this.lfe_stream) { + this.encoders[encoder_ptr].setIsLFE(true); + } + ++encoder_ptr; + } + while (i < this.layout.nb_streams) { + ret = this.encoders[encoder_ptr].opus_init_encoder(Fs, 1, application); + if (i == this.lfe_stream) { + this.encoders[encoder_ptr].setIsLFE(true); + } + if (ret != OpusError.OPUS_OK) { + return ret; + } + ++encoder_ptr; + ++i; + } + if (surround != 0) { + Arrays.MemSet(this.preemph_mem, 0, channels); + Arrays.MemSet(this.window_mem, 0, channels * 120); + } + this.surround = surround; + return OpusError.OPUS_OK; + } + + /* + * Enabled force condition propagation + * Lifted jumps to return sites + */ + int opus_multistream_surround_encoder_init(int Fs, int channels, int mapping_family, BoxedValueInt streams, BoxedValueInt coupled_streams, short[] mapping, OpusApplication application) { + streams.Val = 0; + coupled_streams.Val = 0; + if (channels > 255 || channels < 1) { + return OpusError.OPUS_BAD_ARG; + } + this.lfe_stream = -1; + if (mapping_family == 0) { + if (channels == 1) { + streams.Val = 1; + coupled_streams.Val = 0; + mapping[0] = 0; + return this.opus_multistream_encoder_init(Fs, channels, streams.Val, coupled_streams.Val, mapping, application, channels > 2 && mapping_family == 1 ? 1 : 0); + } else { + if (channels != 2) return OpusError.OPUS_UNIMPLEMENTED; + streams.Val = 1; + coupled_streams.Val = 1; + mapping[0] = 0; + mapping[1] = 1; + } + return this.opus_multistream_encoder_init(Fs, channels, streams.Val, coupled_streams.Val, mapping, application, channels > 2 && mapping_family == 1 ? 1 : 0); + } else if (mapping_family == 1 && channels <= 8 && channels >= 1) { + streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_streams; + coupled_streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_coupled_streams; + for (int i = 0; i < channels; ++i) { + mapping[i] = VorbisLayout.vorbis_mappings[channels - 1].mapping[i]; + } + if (channels < 6) return this.opus_multistream_encoder_init(Fs, channels, streams.Val, coupled_streams.Val, mapping, application, channels > 2 && mapping_family == 1 ? 1 : 0); + this.lfe_stream = streams.Val - 1; + return this.opus_multistream_encoder_init(Fs, channels, streams.Val, coupled_streams.Val, mapping, application, channels > 2 && mapping_family == 1 ? 1 : 0); + } else { + if (mapping_family != 255) return OpusError.OPUS_UNIMPLEMENTED; + streams.Val = channels; + coupled_streams.Val = 0; + for (int i = 0; i < channels; i = (int)((byte)(i + 1))) { + mapping[i] = (short)i; + } + } + return this.opus_multistream_encoder_init(Fs, channels, streams.Val, coupled_streams.Val, mapping, application, channels > 2 && mapping_family == 1 ? 1 : 0); + } + + public static OpusMSEncoder Create(int Fs, int channels, int streams, int coupled_streams, short[] mapping, OpusApplication application) throws OpusException { + if (channels > 255 || channels < 1 || coupled_streams > streams || streams < 1 || coupled_streams < 0 || streams > 255 - coupled_streams) { + throw new IllegalArgumentException("Invalid channel / stream configuration"); + } + OpusMSEncoder st = new OpusMSEncoder(streams, coupled_streams); + int ret = st.opus_multistream_encoder_init(Fs, channels, streams, coupled_streams, mapping, application, 0); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("OPUS_BAD_ARG when creating MS encoder"); + } + throw new OpusException("Could not create MS encoder", ret); + } + return st; + } + + /* + * Enabled force condition propagation + * Lifted jumps to return sites + */ + static void GetStreamCount(int channels, int mapping_family, BoxedValueInt nb_streams, BoxedValueInt nb_coupled_streams) { + if (mapping_family == 0) { + if (channels == 1) { + nb_streams.Val = 1; + nb_coupled_streams.Val = 0; + return; + } else { + if (channels != 2) throw new IllegalArgumentException("More than 2 channels requires custom mappings"); + nb_streams.Val = 1; + nb_coupled_streams.Val = 1; + } + return; + } else if (mapping_family == 1 && channels <= 8 && channels >= 1) { + nb_streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_streams; + nb_coupled_streams.Val = VorbisLayout.vorbis_mappings[channels - 1].nb_coupled_streams; + return; + } else { + if (mapping_family != 255) throw new IllegalArgumentException("Invalid mapping family"); + nb_streams.Val = channels; + nb_coupled_streams.Val = 0; + } + } + + public static OpusMSEncoder CreateSurround(int Fs, int channels, int mapping_family, BoxedValueInt streams, BoxedValueInt coupled_streams, short[] mapping, OpusApplication application) throws OpusException { + if (channels > 255 || channels < 1 || application == OpusApplication.OPUS_APPLICATION_UNIMPLEMENTED) { + throw new IllegalArgumentException("Invalid channel count or application"); + } + BoxedValueInt nb_streams = new BoxedValueInt(0); + BoxedValueInt nb_coupled_streams = new BoxedValueInt(0); + OpusMSEncoder.GetStreamCount(channels, mapping_family, nb_streams, nb_coupled_streams); + OpusMSEncoder st = new OpusMSEncoder(nb_streams.Val, nb_coupled_streams.Val); + int ret = st.opus_multistream_surround_encoder_init(Fs, channels, mapping_family, streams, coupled_streams, mapping, application); + if (ret != OpusError.OPUS_OK) { + if (ret == OpusError.OPUS_BAD_ARG) { + throw new IllegalArgumentException("Bad argument passed to CreateSurround"); + } + throw new OpusException("Could not create multistream encoder", ret); + } + return st; + } + + int surround_rate_allocation(int[] out_rates, int frame_size) { + int channel_rate; + int rate_sum = 0; + OpusEncoder ptr = this.encoders[0]; + int Fs = ptr.getSampleRate(); + int stream_offset = this.bitrate_bps > this.layout.nb_channels * 40000 ? 20000 : this.bitrate_bps / this.layout.nb_channels / 2; + stream_offset += 60 * (Fs / frame_size - 50); + int lfe_offset = 3500 + 60 * (Fs / frame_size - 50); + int coupled_ratio = 512; + int lfe_ratio = 32; + if (this.bitrate_bps == -1000) { + channel_rate = Fs + 60 * Fs / frame_size; + } else if (this.bitrate_bps == -1) { + channel_rate = 300000; + } else { + int nb_lfe = this.lfe_stream != -1 ? 1 : 0; + int nb_coupled = this.layout.nb_coupled_streams; + int nb_uncoupled = this.layout.nb_streams - nb_coupled - nb_lfe; + int total = (nb_uncoupled << 8) + coupled_ratio * nb_coupled + nb_lfe * lfe_ratio; + channel_rate = 256 * (this.bitrate_bps - lfe_offset * nb_lfe - stream_offset * (nb_coupled + nb_uncoupled)) / total; + } + for (int i = 0; i < this.layout.nb_streams; ++i) { + out_rates[i] = i < this.layout.nb_coupled_streams ? stream_offset + (channel_rate * coupled_ratio >> 8) : (i != this.lfe_stream ? stream_offset + channel_rate : lfe_offset + (channel_rate * lfe_ratio >> 8)); + out_rates[i] = Inlines.IMAX(out_rates[i], 500); + rate_sum += out_rates[i]; + } + return rate_sum; + } + + int opus_multistream_encode_native(short[] pcm, int pcm_ptr, int analysis_frame_size, byte[] data, int data_ptr, int max_data_bytes, int lsb_depth, int float_api) { + int s; + byte[] tmp_data = new byte[3832]; + OpusRepacketizer rp = new OpusRepacketizer(); + int[] bitrates = new int[256]; + int[] bandLogE = new int[42]; + int[] mem = null; + int[] preemph_mem = null; + if (this.surround != 0) { + preemph_mem = this.preemph_mem; + mem = this.window_mem; + } + int encoder_ptr = 0; + int Fs = this.encoders[encoder_ptr].getSampleRate(); + boolean vbr = this.encoders[encoder_ptr].getUseVBR(); + CeltMode celt_mode = this.encoders[encoder_ptr].GetCeltMode(); + int channels = this.layout.nb_streams + this.layout.nb_coupled_streams; + int delay_compensation = this.encoders[encoder_ptr].getLookahead(); + int frame_size = CodecHelpers.compute_frame_size(pcm, pcm_ptr, analysis_frame_size, this.variable_duration, channels, Fs, this.bitrate_bps, delay_compensation -= Fs / 400, this.subframe_mem, this.encoders[encoder_ptr].analysis.enabled); + if (400 * frame_size < Fs) { + return OpusError.OPUS_BAD_ARG; + } + if (400 * frame_size != Fs && 200 * frame_size != Fs && 100 * frame_size != Fs && 50 * frame_size != Fs && 25 * frame_size != Fs && 50 * frame_size != 3 * Fs) { + return OpusError.OPUS_BAD_ARG; + } + int smallest_packet = this.layout.nb_streams * 2 - 1; + if (max_data_bytes < smallest_packet) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + short[] buf = new short[2 * frame_size]; + int[] bandSMR = new int[21 * this.layout.nb_channels]; + if (this.surround != 0) { + OpusMSEncoder.surround_analysis(celt_mode, pcm, pcm_ptr, bandSMR, mem, preemph_mem, frame_size, 120, this.layout.nb_channels, Fs); + } + int rate_sum = this.surround_rate_allocation(bitrates, frame_size); + if (!vbr) { + if (this.bitrate_bps == -1000) { + max_data_bytes = Inlines.IMIN(max_data_bytes, 3 * rate_sum / (24 * Fs / frame_size)); + } else if (this.bitrate_bps != -1) { + max_data_bytes = Inlines.IMIN(max_data_bytes, Inlines.IMAX(smallest_packet, 3 * this.bitrate_bps / (24 * Fs / frame_size))); + } + } + for (s = 0; s < this.layout.nb_streams; ++s) { + OpusEncoder enc = this.encoders[encoder_ptr]; + ++encoder_ptr; + enc.setBitrate(bitrates[s]); + if (this.surround == 0) continue; + int equiv_rate = this.bitrate_bps; + if (frame_size * 50 < Fs) { + equiv_rate -= 60 * (Fs / frame_size - 50) * this.layout.nb_channels; + } + if (equiv_rate > 10000 * this.layout.nb_channels) { + enc.setBandwidth(OpusBandwidth.OPUS_BANDWIDTH_FULLBAND); + } else if (equiv_rate > 7000 * this.layout.nb_channels) { + enc.setBandwidth(OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND); + } else if (equiv_rate > 5000 * this.layout.nb_channels) { + enc.setBandwidth(OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND); + } else { + enc.setBandwidth(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND); + } + if (s >= this.layout.nb_coupled_streams) continue; + enc.setForceMode(OpusMode.MODE_CELT_ONLY); + enc.setForceChannels(2); + } + encoder_ptr = 0; + int tot_size = 0; + for (s = 0; s < this.layout.nb_streams; ++s) { + int len; + int c2; + int c1; + int i; + rp.Reset(); + OpusEncoder enc = this.encoders[encoder_ptr]; + if (s < this.layout.nb_coupled_streams) { + int left = OpusMultistream.get_left_channel(this.layout, s, -1); + int right = OpusMultistream.get_right_channel(this.layout, s, -1); + OpusMSEncoder.opus_copy_channel_in_short(buf, 0, 2, pcm, pcm_ptr, this.layout.nb_channels, left, frame_size); + OpusMSEncoder.opus_copy_channel_in_short(buf, 1, 2, pcm, pcm_ptr, this.layout.nb_channels, right, frame_size); + ++encoder_ptr; + if (this.surround != 0) { + for (i = 0; i < 21; ++i) { + bandLogE[i] = bandSMR[21 * left + i]; + bandLogE[21 + i] = bandSMR[21 * right + i]; + } + } + c1 = left; + c2 = right; + } else { + int chan = OpusMultistream.get_mono_channel(this.layout, s, -1); + OpusMSEncoder.opus_copy_channel_in_short(buf, 0, 1, pcm, pcm_ptr, this.layout.nb_channels, chan, frame_size); + ++encoder_ptr; + if (this.surround != 0) { + for (i = 0; i < 21; ++i) { + bandLogE[i] = bandSMR[21 * chan + i]; + } + } + c1 = chan; + c2 = -1; + } + if (this.surround != 0) { + enc.SetEnergyMask(bandLogE); + } + int curr_max = max_data_bytes - tot_size; + curr_max -= Inlines.IMAX(0, 2 * (this.layout.nb_streams - s - 1) - 1); + curr_max = Inlines.IMIN(curr_max, 3832); + if (s != this.layout.nb_streams - 1) { + curr_max -= curr_max > 253 ? 2 : 1; + } + if (!vbr && s == this.layout.nb_streams - 1) { + enc.setBitrate(curr_max * (8 * Fs / frame_size)); + } + if ((len = enc.opus_encode_native(buf, 0, frame_size, tmp_data, 0, curr_max, lsb_depth, pcm, pcm_ptr, analysis_frame_size, c1, c2, this.layout.nb_channels, float_api)) < 0) { + return len; + } + rp.addPacket(tmp_data, 0, len); + len = rp.opus_repacketizer_out_range_impl(0, rp.getNumFrames(), data, data_ptr, max_data_bytes - tot_size, s != this.layout.nb_streams - 1 ? 1 : 0, !vbr && s == this.layout.nb_streams - 1 ? 1 : 0); + data_ptr += len; + tot_size += len; + } + return tot_size; + } + + static void opus_copy_channel_in_short(short[] dst, int dst_ptr, int dst_stride, short[] src, int src_ptr, int src_stride, int src_channel, int frame_size) { + for (int i = 0; i < frame_size; ++i) { + dst[dst_ptr + i * dst_stride] = src[i * src_stride + src_channel + src_ptr]; + } + } + + public int encodeMultistream(short[] pcm, int pcm_offset, int frame_size, byte[] outputBuffer, int outputBuffer_offset, int max_data_bytes) { + return this.opus_multistream_encode_native(pcm, pcm_offset, frame_size, outputBuffer, outputBuffer_offset, max_data_bytes, 16, 0); + } + + public int getBitrate() { + int value = 0; + int encoder_ptr = 0; + for (int s = 0; s < this.layout.nb_streams; ++s) { + OpusEncoder enc = this.encoders[encoder_ptr++]; + value += enc.getBitrate(); + } + return value; + } + + public void setBitrate(int value) { + if (value < 0 && value != -1000 && value != -1) { + throw new IllegalArgumentException("Invalid bitrate"); + } + this.bitrate_bps = value; + } + + public OpusApplication getApplication() { + return this.encoders[0].getApplication(); + } + + public void setApplication(OpusApplication value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setApplication(value); + } + } + + public int getForceChannels() { + return this.encoders[0].getForceChannels(); + } + + public void setForceChannels(int value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setForceChannels(value); + } + } + + public OpusBandwidth getMaxBandwidth() { + return this.encoders[0].getMaxBandwidth(); + } + + public void setMaxBandwidth(OpusBandwidth value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setMaxBandwidth(value); + } + } + + public OpusBandwidth getBandwidth() { + return this.encoders[0].getBandwidth(); + } + + public void setBandwidth(OpusBandwidth value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setBandwidth(value); + } + } + + public boolean getUseDTX() { + return this.encoders[0].getUseDTX(); + } + + public void setUseDTX(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setUseDTX(value); + } + } + + public int getComplexity() { + return this.encoders[0].getComplexity(); + } + + public void setComplexity(int value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setComplexity(value); + } + } + + public OpusMode getForceMode() { + return this.encoders[0].getForceMode(); + } + + public void setForceMode(OpusMode value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setForceMode(value); + } + } + + public boolean getUseInbandFEC() { + return this.encoders[0].getUseInbandFEC(); + } + + public void setUseInbandFEC(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setUseInbandFEC(value); + } + } + + public int getPacketLossPercent() { + return this.encoders[0].getPacketLossPercent(); + } + + public void setPacketLossPercent(int value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setPacketLossPercent(value); + } + } + + public boolean getUseVBR() { + return this.encoders[0].getUseVBR(); + } + + public void setUseVBR(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setUseVBR(value); + } + } + + public boolean getUseConstrainedVBR() { + return this.encoders[0].getUseConstrainedVBR(); + } + + public void setUseConstrainedVBR(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setUseConstrainedVBR(value); + } + } + + public OpusSignal getSignalType() { + return this.encoders[0].getSignalType(); + } + + public void setSignalType(OpusSignal value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setSignalType(value); + } + } + + public int getLookahead() { + return this.encoders[0].getLookahead(); + } + + public int getSampleRate() { + return this.encoders[0].getSampleRate(); + } + + public int getFinalRange() { + int value = 0; + int encoder_ptr = 0; + for (int s = 0; s < this.layout.nb_streams; ++s) { + value ^= this.encoders[encoder_ptr++].getFinalRange(); + } + return value; + } + + public int getLSBDepth() { + return this.encoders[0].getLSBDepth(); + } + + public void setLSBDepth(int value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setLSBDepth(value); + } + } + + public boolean getPredictionDisabled() { + return this.encoders[0].getPredictionDisabled(); + } + + public void setPredictionDisabled(boolean value) { + for (int encoder_ptr = 0; encoder_ptr < this.layout.nb_streams; ++encoder_ptr) { + this.encoders[encoder_ptr].setPredictionDisabled(value); + } + } + + public OpusFramesize getExpertFrameDuration() { + return this.variable_duration; + } + + public void setExpertFrameDuration(OpusFramesize value) { + this.variable_duration = value; + } + + public OpusEncoder getMultistreamEncoderState(int streamId) { + if (streamId >= this.layout.nb_streams) { + throw new IllegalArgumentException("Requested stream doesn't exist"); + } + return this.encoders[streamId]; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMode.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMode.java new file mode 100644 index 0000000..0dfb454 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMode.java @@ -0,0 +1,14 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +public enum OpusMode { + MODE_UNKNOWN, + MODE_AUTO, + MODE_SILK_ONLY, + MODE_HYBRID, + MODE_CELT_ONLY; + +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMultistream.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMultistream.java new file mode 100644 index 0000000..993665f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusMultistream.java @@ -0,0 +1,60 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.ChannelLayout; + +class OpusMultistream { + OpusMultistream() { + } + + static int validate_layout(ChannelLayout layout) { + int max_channel = layout.nb_streams + layout.nb_coupled_streams; + if (max_channel > 255) { + return 0; + } + for (int i = 0; i < layout.nb_channels; ++i) { + if (layout.mapping[i] < max_channel || layout.mapping[i] == 255) continue; + return 0; + } + return 1; + } + + static int get_left_channel(ChannelLayout layout, int stream_id, int prev) { + int i; + int n = i = prev < 0 ? 0 : prev + 1; + while (i < layout.nb_channels) { + if (layout.mapping[i] == stream_id * 2) { + return i; + } + ++i; + } + return -1; + } + + static int get_right_channel(ChannelLayout layout, int stream_id, int prev) { + int i; + int n = i = prev < 0 ? 0 : prev + 1; + while (i < layout.nb_channels) { + if (layout.mapping[i] == stream_id * 2 + 1) { + return i; + } + ++i; + } + return -1; + } + + static int get_mono_channel(ChannelLayout layout, int stream_id, int prev) { + int i; + int n = i = prev < 0 ? 0 : prev + 1; + while (i < layout.nb_channels) { + if (layout.mapping[i] == stream_id + layout.nb_coupled_streams) { + return i; + } + ++i; + } + return -1; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusPacketInfo.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusPacketInfo.java new file mode 100644 index 0000000..6870920 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusPacketInfo.java @@ -0,0 +1,282 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.BoxedValueShort; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.OpusBandwidth; +import de.maxhenkel.voicechat.concentus.OpusBandwidthHelpers; +import de.maxhenkel.voicechat.concentus.OpusDecoder; +import de.maxhenkel.voicechat.concentus.OpusError; +import de.maxhenkel.voicechat.concentus.OpusException; +import de.maxhenkel.voicechat.concentus.OpusMode; +import java.util.ArrayList; +import java.util.List; + +public class OpusPacketInfo { + public byte TOCByte; + public List Frames; + public int PayloadOffset; + + private OpusPacketInfo(byte toc, List frames, int payloadOffset) { + this.TOCByte = toc; + this.Frames = frames; + this.PayloadOffset = payloadOffset; + } + + public static OpusPacketInfo parseOpusPacket(byte[] packet, int packet_offset, int len) throws OpusException { + BoxedValueInt packetOffset; + BoxedValueInt payload_offset; + short[] size; + BoxedValueByte out_toc = new BoxedValueByte(0); + int numFrames = OpusPacketInfo.getNumFrames(packet, packet_offset, len); + byte[][] frames = new byte[numFrames][]; + int error = OpusPacketInfo.opus_packet_parse_impl(packet, packet_offset, len, 0, out_toc, frames, 0, size = new short[numFrames], 0, payload_offset = new BoxedValueInt(0), packetOffset = new BoxedValueInt(0)); + if (error < 0) { + throw new OpusException("An error occurred while parsing the packet", error); + } + ArrayList copiedFrames = new ArrayList(); + for (int c = 0; c < frames.length; ++c) { + Byte[] convertedFrame = new Byte[frames[c].length]; + for (int d = 0; d < convertedFrame.length; ++d) { + convertedFrame[d] = frames[c][d]; + } + copiedFrames.add(convertedFrame); + } + return new OpusPacketInfo(out_toc.Val, copiedFrames, payload_offset.Val); + } + + public static int getNumSamplesPerFrame(byte[] packet, int packet_offset, int Fs) { + int audiosize; + if ((packet[packet_offset] & 0x80) != 0) { + audiosize = packet[packet_offset] >> 3 & 3; + audiosize = (Fs << audiosize) / 400; + } else { + audiosize = (packet[packet_offset] & 0x60) == 96 ? ((packet[packet_offset] & 8) != 0 ? Fs / 50 : Fs / 100) : ((audiosize = packet[packet_offset] >> 3 & 3) == 3 ? Fs * 60 / 1000 : (Fs << audiosize) / 100); + } + return audiosize; + } + + public static OpusBandwidth getBandwidth(byte[] packet, int packet_offset) { + OpusBandwidth bandwidth; + if ((packet[packet_offset] & 0x80) != 0) { + bandwidth = OpusBandwidthHelpers.GetBandwidth(OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) + (packet[packet_offset] >> 5 & 3)); + if (bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND) { + bandwidth = OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND; + } + } else { + bandwidth = (packet[packet_offset] & 0x60) == 96 ? ((packet[packet_offset] & 0x10) != 0 ? OpusBandwidth.OPUS_BANDWIDTH_FULLBAND : OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND) : OpusBandwidthHelpers.GetBandwidth(OpusBandwidthHelpers.GetOrdinal(OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND) + (packet[packet_offset] >> 5 & 3)); + } + return bandwidth; + } + + public static int getNumEncodedChannels(byte[] packet, int packet_offset) { + return (packet[packet_offset] & 4) != 0 ? 2 : 1; + } + + public static int getNumFrames(byte[] packet, int packet_offset, int len) { + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + int count = packet[packet_offset] & 3; + if (count == 0) { + return 1; + } + if (count != 3) { + return 2; + } + if (len < 2) { + return OpusError.OPUS_INVALID_PACKET; + } + return packet[packet_offset + 1] & 0x3F; + } + + public static int getNumSamples(byte[] packet, int packet_offset, int len, int Fs) { + int count = OpusPacketInfo.getNumFrames(packet, packet_offset, len); + if (count < 0) { + return count; + } + int samples = count * OpusPacketInfo.getNumSamplesPerFrame(packet, packet_offset, Fs); + if (samples * 25 > Fs * 3) { + return OpusError.OPUS_INVALID_PACKET; + } + return samples; + } + + public static int getNumSamples(OpusDecoder dec, byte[] packet, int packet_offset, int len) { + return OpusPacketInfo.getNumSamples(packet, packet_offset, len, dec.Fs); + } + + public static OpusMode getEncoderMode(byte[] packet, int packet_offset) { + OpusMode mode = (packet[packet_offset] & 0x80) != 0 ? OpusMode.MODE_CELT_ONLY : ((packet[packet_offset] & 0x60) == 96 ? OpusMode.MODE_HYBRID : OpusMode.MODE_SILK_ONLY); + return mode; + } + + static int encode_size(int size, byte[] data, int data_ptr) { + if (size < 252) { + data[data_ptr] = (byte)(size & 0xFF); + return 1; + } + int dp1 = 252 + (size & 3); + data[data_ptr] = (byte)(dp1 & 0xFF); + data[data_ptr + 1] = (byte)(size - dp1 >> 2); + return 2; + } + + static int parse_size(byte[] data, int data_ptr, int len, BoxedValueShort size) { + if (len < 1) { + size.Val = (short)-1; + return -1; + } + if (Inlines.SignedByteToUnsignedInt(data[data_ptr]) < 252) { + size.Val = (short)Inlines.SignedByteToUnsignedInt(data[data_ptr]); + return 1; + } + if (len < 2) { + size.Val = (short)-1; + return -1; + } + size.Val = (short)(4 * Inlines.SignedByteToUnsignedInt(data[data_ptr + 1]) + Inlines.SignedByteToUnsignedInt(data[data_ptr])); + return 2; + } + + static int opus_packet_parse_impl(byte[] data, int data_ptr, int len, int self_delimited, BoxedValueByte out_toc, byte[][] frames, int frames_ptr, short[] sizes, int sizes_ptr, BoxedValueInt payload_offset, BoxedValueInt packet_offset) { + int i; + int bytes; + BoxedValueShort boxed_size; + int count; + int pad = 0; + int data0 = data_ptr; + out_toc.Val = 0; + payload_offset.Val = 0; + packet_offset.Val = 0; + if (sizes == null || len < 0) { + return OpusError.OPUS_BAD_ARG; + } + if (len == 0) { + return OpusError.OPUS_INVALID_PACKET; + } + int framesize = OpusPacketInfo.getNumSamplesPerFrame(data, data_ptr, 48000); + boolean cbr = false; + byte toc = data[data_ptr++]; + int last_size = --len; + switch (toc & 3) { + case 0: { + count = 1; + break; + } + case 1: { + count = 2; + cbr = true; + if (self_delimited != 0) break; + if ((len & 1) != 0) { + return OpusError.OPUS_INVALID_PACKET; + } + last_size = len / 2; + sizes[sizes_ptr] = (short)last_size; + break; + } + case 2: { + count = 2; + boxed_size = new BoxedValueShort(sizes[sizes_ptr]); + bytes = OpusPacketInfo.parse_size(data, data_ptr, len, boxed_size); + sizes[sizes_ptr] = boxed_size.Val; + if (sizes[sizes_ptr] < 0 || sizes[sizes_ptr] > (len -= bytes)) { + return OpusError.OPUS_INVALID_PACKET; + } + data_ptr += bytes; + last_size = len - sizes[sizes_ptr]; + break; + } + default: { + int ch; + if (len < 1) { + return OpusError.OPUS_INVALID_PACKET; + } + if ((count = (ch = Inlines.SignedByteToUnsignedInt(data[data_ptr++])) & 0x3F) <= 0 || framesize * count > 5760) { + return OpusError.OPUS_INVALID_PACKET; + } + --len; + if ((ch & 0x40) != 0) { + int p; + do { + if (len <= 0) { + return OpusError.OPUS_INVALID_PACKET; + } + p = Inlines.SignedByteToUnsignedInt(data[data_ptr++]); + --len; + int tmp = p == 255 ? 254 : p; + len -= tmp; + pad += tmp; + } while (p == 255); + } + if (len < 0) { + return OpusError.OPUS_INVALID_PACKET; + } + boolean bl = cbr = (ch & 0x80) == 0; + if (!cbr) { + last_size = len; + for (i = 0; i < count - 1; ++i) { + boxed_size = new BoxedValueShort(sizes[sizes_ptr + i]); + bytes = OpusPacketInfo.parse_size(data, data_ptr, len, boxed_size); + sizes[sizes_ptr + i] = boxed_size.Val; + if (sizes[sizes_ptr + i] < 0 || sizes[sizes_ptr + i] > (len -= bytes)) { + return OpusError.OPUS_INVALID_PACKET; + } + data_ptr += bytes; + last_size -= bytes + sizes[sizes_ptr + i]; + } + if (last_size >= 0) break; + return OpusError.OPUS_INVALID_PACKET; + } + if (self_delimited != 0) break; + last_size = len / count; + if (last_size * count != len) { + return OpusError.OPUS_INVALID_PACKET; + } + for (i = 0; i < count - 1; ++i) { + sizes[sizes_ptr + i] = (short)last_size; + } + } + } + if (self_delimited != 0) { + boxed_size = new BoxedValueShort(sizes[sizes_ptr + count - 1]); + bytes = OpusPacketInfo.parse_size(data, data_ptr, len, boxed_size); + sizes[sizes_ptr + count - 1] = boxed_size.Val; + if (sizes[sizes_ptr + count - 1] < 0 || sizes[sizes_ptr + count - 1] > (len -= bytes)) { + return OpusError.OPUS_INVALID_PACKET; + } + data_ptr += bytes; + if (cbr) { + if (sizes[sizes_ptr + count - 1] * count > len) { + return OpusError.OPUS_INVALID_PACKET; + } + for (i = 0; i < count - 1; ++i) { + sizes[sizes_ptr + i] = sizes[sizes_ptr + count - 1]; + } + } else if (bytes + sizes[sizes_ptr + count - 1] > last_size) { + return OpusError.OPUS_INVALID_PACKET; + } + } else { + if (last_size > 1275) { + return OpusError.OPUS_INVALID_PACKET; + } + sizes[sizes_ptr + count - 1] = (short)last_size; + } + payload_offset.Val = data_ptr - data0; + for (i = 0; i < count; ++i) { + if (frames != null) { + frames[frames_ptr + i] = new byte[data.length - data_ptr]; + System.arraycopy(data, data_ptr, frames[frames_ptr + i], 0, data.length - data_ptr); + } + data_ptr += sizes[sizes_ptr + i]; + } + packet_offset.Val = pad + (data_ptr - data0); + out_toc.Val = toc; + return count; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusRepacketizer.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusRepacketizer.java new file mode 100644 index 0000000..90b91b8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusRepacketizer.java @@ -0,0 +1,261 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.OpusError; +import de.maxhenkel.voicechat.concentus.OpusPacketInfo; + +public class OpusRepacketizer { + byte toc = 0; + int nb_frames = 0; + final byte[][] frames = new byte[48][]; + final short[] len = new short[48]; + int framesize = 0; + + public void Reset() { + this.nb_frames = 0; + } + + public OpusRepacketizer() { + this.Reset(); + } + + int opus_repacketizer_cat_impl(byte[] data, int data_ptr, int len, int self_delimited) { + BoxedValueByte dummy_toc = new BoxedValueByte(0); + BoxedValueInt dummy_offset = new BoxedValueInt(0); + if (len < 1) { + return OpusError.OPUS_INVALID_PACKET; + } + if (this.nb_frames == 0) { + this.toc = data[data_ptr]; + this.framesize = OpusPacketInfo.getNumSamplesPerFrame(data, data_ptr, 8000); + } else if ((this.toc & 0xFC) != (data[data_ptr] & 0xFC)) { + return OpusError.OPUS_INVALID_PACKET; + } + int curr_nb_frames = OpusPacketInfo.getNumFrames(data, data_ptr, len); + if (curr_nb_frames < 1) { + return OpusError.OPUS_INVALID_PACKET; + } + if ((curr_nb_frames + this.nb_frames) * this.framesize > 960) { + return OpusError.OPUS_INVALID_PACKET; + } + int ret = OpusPacketInfo.opus_packet_parse_impl(data, data_ptr, len, self_delimited, dummy_toc, this.frames, this.nb_frames, this.len, this.nb_frames, dummy_offset, dummy_offset); + if (ret < 1) { + return ret; + } + this.nb_frames += curr_nb_frames; + return OpusError.OPUS_OK; + } + + public int addPacket(byte[] data, int data_offset, int len) { + return this.opus_repacketizer_cat_impl(data, data_offset, len, 0); + } + + public int getNumFrames() { + return this.nb_frames; + } + + int opus_repacketizer_out_range_impl(int begin, int end, byte[] data, int data_ptr, int maxlen, int self_delimited, int pad) { + int i; + if (begin < 0 || begin >= end || end > this.nb_frames) { + return OpusError.OPUS_BAD_ARG; + } + int count = end - begin; + int tot_size = self_delimited != 0 ? 1 + (this.len[count - 1] >= 252 ? 1 : 0) : 0; + int ptr = data_ptr; + if (count == 1) { + if ((tot_size += this.len[0] + 1) > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte)(this.toc & 0xFC); + } else if (count == 2) { + if (this.len[1] == this.len[0]) { + if ((tot_size += 2 * this.len[0] + 1) > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte)(this.toc & 0xFC | 1); + } else { + if ((tot_size += this.len[0] + this.len[1] + 2 + (this.len[0] >= 252 ? 1 : 0)) > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte)(this.toc & 0xFC | 2); + ptr += OpusPacketInfo.encode_size(this.len[0], data, ptr); + } + } + if (count > 2 || pad != 0 && tot_size < maxlen) { + int pad_amount = 0; + ptr = data_ptr; + tot_size = self_delimited != 0 ? 1 + (this.len[count - 1] >= 252 ? 1 : 0) : 0; + boolean vbr = false; + for (i = 1; i < count; ++i) { + if (this.len[i] == this.len[0]) continue; + vbr = true; + break; + } + if (vbr) { + tot_size += 2; + for (i = 0; i < count - 1; ++i) { + tot_size += 1 + (this.len[i] >= 252 ? 1 : 0) + this.len[i]; + } + if ((tot_size += this.len[count - 1]) > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte)(this.toc & 0xFC | 3); + data[ptr++] = (byte)(count | 0x80); + } else { + if ((tot_size += count * this.len[0] + 2) > maxlen) { + return OpusError.OPUS_BUFFER_TOO_SMALL; + } + data[ptr++] = (byte)(this.toc & 0xFC | 3); + data[ptr++] = (byte)count; + } + int n = pad_amount = pad != 0 ? maxlen - tot_size : 0; + if (pad_amount != 0) { + data[data_ptr + 1] = (byte)(data[data_ptr + 1] | 0x40); + int nb_255s = (pad_amount - 1) / 255; + for (i = 0; i < nb_255s; ++i) { + data[ptr++] = -1; + } + data[ptr++] = (byte)(pad_amount - 255 * nb_255s - 1); + tot_size += pad_amount; + } + if (vbr) { + for (i = 0; i < count - 1; ++i) { + ptr += OpusPacketInfo.encode_size(this.len[i], data, ptr); + } + } + } + if (self_delimited != 0) { + int sdlen = OpusPacketInfo.encode_size(this.len[count - 1], data, ptr); + ptr += sdlen; + } + for (i = begin; i < count + begin; ++i) { + if (data == this.frames[i]) { + Arrays.MemMove(data, 0, ptr, (int)this.len[i]); + } else { + System.arraycopy(this.frames[i], 0, data, ptr, this.len[i]); + } + ptr += this.len[i]; + } + if (pad != 0) { + Arrays.MemSetWithOffset(data, (byte)0, ptr, data_ptr + maxlen - ptr); + } + return tot_size; + } + + public int createPacket(int begin, int end, byte[] data, int data_offset, int maxlen) { + return this.opus_repacketizer_out_range_impl(begin, end, data, data_offset, maxlen, 0, 0); + } + + public int createPacket(byte[] data, int data_offset, int maxlen) { + return this.opus_repacketizer_out_range_impl(0, this.nb_frames, data, data_offset, maxlen, 0, 0); + } + + public static int padPacket(byte[] data, int data_offset, int len, int new_len) { + OpusRepacketizer rp = new OpusRepacketizer(); + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + if (len == new_len) { + return OpusError.OPUS_OK; + } + if (len > new_len) { + return OpusError.OPUS_BAD_ARG; + } + rp.Reset(); + Arrays.MemMove(data, data_offset, data_offset + new_len - len, len); + rp.addPacket(data, data_offset + new_len - len, len); + int ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, data_offset, new_len, 0, 1); + if (ret > 0) { + return OpusError.OPUS_OK; + } + return ret; + } + + public static int unpadPacket(byte[] data, int data_offset, int len) { + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + OpusRepacketizer rp = new OpusRepacketizer(); + rp.Reset(); + int ret = rp.addPacket(data, data_offset, len); + if (ret < 0) { + return ret; + } + ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, data_offset, len, 0, 0); + Inlines.OpusAssert(ret > 0 && ret <= len); + return ret; + } + + public static int padMultistreamPacket(byte[] data, int data_offset, int len, int new_len, int nb_streams) { + BoxedValueByte dummy_toc = new BoxedValueByte(0); + short[] size = new short[48]; + BoxedValueInt packet_offset = new BoxedValueInt(0); + BoxedValueInt dummy_offset = new BoxedValueInt(0); + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + if (len == new_len) { + return OpusError.OPUS_OK; + } + if (len > new_len) { + return OpusError.OPUS_BAD_ARG; + } + int amount = new_len - len; + for (int s = 0; s < nb_streams - 1; ++s) { + if (len <= 0) { + return OpusError.OPUS_INVALID_PACKET; + } + int count = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, 1, dummy_toc, null, 0, size, 0, dummy_offset, packet_offset); + if (count < 0) { + return count; + } + data_offset += packet_offset.Val; + len -= packet_offset.Val; + } + return OpusRepacketizer.padPacket(data, data_offset, len, len + amount); + } + + public static int unpadMultistreamPacket(byte[] data, int data_offset, int len, int nb_streams) { + BoxedValueByte dummy_toc = new BoxedValueByte(0); + short[] size = new short[48]; + BoxedValueInt packet_offset = new BoxedValueInt(0); + BoxedValueInt dummy_offset = new BoxedValueInt(0); + OpusRepacketizer rp = new OpusRepacketizer(); + if (len < 1) { + return OpusError.OPUS_BAD_ARG; + } + int dst = data_offset; + int dst_len = 0; + for (int s = 0; s < nb_streams; ++s) { + int self_delimited = (s != nb_streams ? 1 : 0) - 1; + if (len <= 0) { + return OpusError.OPUS_INVALID_PACKET; + } + rp.Reset(); + int ret = OpusPacketInfo.opus_packet_parse_impl(data, data_offset, len, self_delimited, dummy_toc, null, 0, size, 0, dummy_offset, packet_offset); + if (ret < 0) { + return ret; + } + ret = rp.opus_repacketizer_cat_impl(data, data_offset, packet_offset.Val, self_delimited); + if (ret < 0) { + return ret; + } + ret = rp.opus_repacketizer_out_range_impl(0, rp.nb_frames, data, dst, len, self_delimited, 0); + if (ret < 0) { + return ret; + } + dst_len += ret; + dst += ret; + data_offset += packet_offset.Val; + len -= packet_offset.Val; + } + return dst_len; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusSignal.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusSignal.java new file mode 100644 index 0000000..2c37415 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusSignal.java @@ -0,0 +1,13 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +public enum OpusSignal { + OPUS_SIGNAL_UNKNOWN, + OPUS_SIGNAL_AUTO, + OPUS_SIGNAL_VOICE, + OPUS_SIGNAL_MUSIC; + +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/OpusTables.java b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusTables.java new file mode 100644 index 0000000..a4cd60a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/OpusTables.java @@ -0,0 +1,40 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.MLPState; + +class OpusTables { + static final float[] dct_table = new float[]{0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.351851f, 0.33833f, 0.311806f, 0.2733f, 0.224292f, 0.166664f, 0.102631f, 0.034654f, -0.034654f, -0.102631f, -0.166664f, -0.224292f, -0.2733f, -0.311806f, -0.33833f, -0.351851f, 0.34676f, 0.293969f, 0.196424f, 0.068975f, -0.068975f, -0.196424f, -0.293969f, -0.34676f, -0.34676f, -0.293969f, -0.196424f, -0.068975f, 0.068975f, 0.196424f, 0.293969f, 0.34676f, 0.33833f, 0.224292f, 0.034654f, -0.166664f, -0.311806f, -0.351851f, -0.2733f, -0.102631f, 0.102631f, 0.2733f, 0.351851f, 0.311806f, 0.166664f, -0.034654f, -0.224292f, -0.33833f, 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f, 0.135299f, 0.326641f, 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f, 0.135299f, 0.326641f, 0.311806f, 0.034654f, -0.2733f, -0.33833f, -0.102631f, 0.224292f, 0.351851f, 0.166664f, -0.166664f, -0.351851f, -0.224292f, 0.102631f, 0.33833f, 0.2733f, -0.034654f, -0.311806f, 0.293969f, -0.068975f, -0.34676f, -0.196424f, 0.196424f, 0.34676f, 0.068975f, -0.293969f, -0.293969f, 0.068975f, 0.34676f, 0.196424f, -0.196424f, -0.34676f, -0.068975f, 0.293969f, 0.2733f, -0.166664f, -0.33833f, 0.034654f, 0.351851f, 0.102631f, -0.311806f, -0.224292f, 0.224292f, 0.311806f, -0.102631f, -0.351851f, -0.034654f, 0.33833f, 0.166664f, -0.2733f}; + static final float[] analysis_window = new float[]{4.3E-5f, 1.71E-4f, 3.85E-4f, 6.85E-4f, 0.001071f, 0.001541f, 0.002098f, 0.002739f, 0.003466f, 0.004278f, 0.005174f, 0.006156f, 0.007222f, 0.008373f, 0.009607f, 0.010926f, 0.012329f, 0.013815f, 0.015385f, 0.017037f, 0.018772f, 0.02059f, 0.02249f, 0.024472f, 0.026535f, 0.028679f, 0.030904f, 0.03321f, 0.035595f, 0.03806f, 0.040604f, 0.043227f, 0.045928f, 0.048707f, 0.051564f, 0.054497f, 0.057506f, 0.060591f, 0.063752f, 0.066987f, 0.070297f, 0.07368f, 0.077136f, 0.080665f, 0.084265f, 0.087937f, 0.091679f, 0.095492f, 0.099373f, 0.103323f, 0.107342f, 0.111427f, 0.115579f, 0.119797f, 0.12408f, 0.128428f, 0.132839f, 0.137313f, 0.141849f, 0.146447f, 0.151105f, 0.155823f, 0.1606f, 0.165435f, 0.170327f, 0.175276f, 0.18028f, 0.18534f, 0.190453f, 0.195619f, 0.200838f, 0.206107f, 0.211427f, 0.216797f, 0.222215f, 0.22768f, 0.233193f, 0.238751f, 0.244353f, 0.25f, 0.255689f, 0.261421f, 0.267193f, 0.273005f, 0.278856f, 0.284744f, 0.29067f, 0.296632f, 0.302628f, 0.308658f, 0.314721f, 0.320816f, 0.326941f, 0.333097f, 0.33928f, 0.345492f, 0.351729f, 0.357992f, 0.36428f, 0.37059f, 0.376923f, 0.383277f, 0.389651f, 0.396044f, 0.402455f, 0.408882f, 0.415325f, 0.421783f, 0.428254f, 0.434737f, 0.441231f, 0.447736f, 0.454249f, 0.46077f, 0.467298f, 0.473832f, 0.48037f, 0.486912f, 0.493455f, 0.5f, 0.506545f, 0.513088f, 0.51963f, 0.526168f, 0.532702f, 0.53923f, 0.545751f, 0.552264f, 0.558769f, 0.565263f, 0.571746f, 0.578217f, 0.584675f, 0.591118f, 0.597545f, 0.603956f, 0.610349f, 0.616723f, 0.623077f, 0.62941f, 0.63572f, 0.642008f, 0.648271f, 0.654508f, 0.66072f, 0.666903f, 0.673059f, 0.679184f, 0.685279f, 0.691342f, 0.697372f, 0.703368f, 0.70933f, 0.715256f, 0.721144f, 0.726995f, 0.732807f, 0.738579f, 0.744311f, 0.75f, 0.755647f, 0.761249f, 0.766807f, 0.77232f, 0.777785f, 0.783203f, 0.788573f, 0.793893f, 0.799162f, 0.804381f, 0.809547f, 0.81466f, 0.81972f, 0.824724f, 0.829673f, 0.834565f, 0.8394f, 0.844177f, 0.848895f, 0.853553f, 0.858151f, 0.862687f, 0.867161f, 0.871572f, 0.87592f, 0.880203f, 0.884421f, 0.888573f, 0.892658f, 0.896677f, 0.900627f, 0.904508f, 0.908321f, 0.912063f, 0.915735f, 0.919335f, 0.922864f, 0.92632f, 0.929703f, 0.933013f, 0.936248f, 0.939409f, 0.942494f, 0.945503f, 0.948436f, 0.951293f, 0.954072f, 0.956773f, 0.959396f, 0.96194f, 0.964405f, 0.96679f, 0.969096f, 0.971321f, 0.973465f, 0.975528f, 0.97751f, 0.97941f, 0.981228f, 0.982963f, 0.984615f, 0.986185f, 0.987671f, 0.989074f, 0.990393f, 0.991627f, 0.992778f, 0.993844f, 0.994826f, 0.995722f, 0.996534f, 0.997261f, 0.997902f, 0.998459f, 0.998929f, 0.999315f, 0.999615f, 0.999829f, 0.999957f, 1.0f}; + static final int[] tbands = new int[]{2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120}; + static final int[] extra_bands = new int[]{1, 2, 4, 6, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, 68, 80, 96, 120, 160, 200}; + static final float[] weights = new float[]{-0.0941125f, -0.302976f, -0.603555f, -0.19393f, -0.185983f, -0.601617f, -0.0465317f, -0.114563f, -0.103599f, -0.618938f, -0.317859f, -0.169949f, -0.0702885f, 0.148065f, 0.409524f, 0.548432f, 0.367649f, -0.494393f, 0.764306f, -1.83957f, 0.170849f, 12.786f, -1.08848f, -1.27284f, -16.2606f, 24.1773f, -5.57454f, -0.17276f, -0.163388f, -0.224421f, -0.0948944f, -0.0728695f, -0.26557f, -0.100283f, -0.0515459f, -0.146142f, -0.120674f, -0.180655f, 0.12857f, 0.442138f, -0.493735f, 0.167767f, 0.206699f, -0.197567f, 0.417999f, 1.50364f, -0.773341f, -10.0401f, 0.401872f, 2.97966f, 15.2165f, -1.88905f, -1.19254f, 0.0285397f, -0.00405139f, 0.0707565f, 0.00825699f, -0.0927269f, -0.010393f, -0.00428882f, -0.00489743f, -0.0709731f, -0.00255992f, 0.0395619f, 0.226424f, 0.0325231f, 0.162175f, -0.100118f, 0.485789f, 0.12697f, 0.285937f, 0.0155637f, 0.10546f, 3.05558f, 1.15059f, -1.00904f, -1.83088f, 3.31766f, -3.42516f, -0.119135f, -0.0405654f, 0.00690068f, 0.0179877f, -0.0382487f, 0.00597941f, -0.0183611f, 0.00190395f, -0.144322f, -0.0435671f, 9.90594E-4f, 0.221087f, 0.142405f, 0.484066f, 0.404395f, 0.511955f, -0.237255f, 0.241742f, 0.35045f, -0.699428f, 10.3993f, 2.6507f, -2.43459f, -4.18838f, 1.05928f, 1.71067f, 0.00667811f, -0.0721335f, -0.0397346f, 0.0362704f, -0.11496f, -0.0235776f, 0.0082161f, -0.0141741f, -0.0329699f, -0.0354253f, 0.00277404f, -0.290654f, -1.14767f, -0.319157f, -0.686544f, 0.36897f, 0.478899f, 0.182579f, -0.411069f, 0.881104f, -4.60683f, 1.4697f, 0.335845f, -1.81905f, -30.1699f, 5.55225f, 0.0019508f, -0.123576f, -0.0727332f, -0.0641597f, -0.0534458f, -0.108166f, -0.0937368f, -0.0697883f, -0.0275475f, -0.192309f, -0.110074f, 0.285375f, -0.405597f, 0.0926724f, -0.287881f, -0.851193f, -0.099493f, -0.233764f, -1.2852f, 1.13611f, 3.12168f, -0.0699f, -1.86216f, 2.65292f, -7.31036f, 2.44776f, -0.00111802f, -0.0632786f, -0.0376296f, -0.149851f, 0.142963f, 0.184368f, 0.123433f, 0.0756158f, 0.117312f, 0.0933395f, 0.0692163f, 0.0842592f, 0.0704683f, 0.0589963f, 0.0942205f, -0.448862f, 0.0262677f, 0.270352f, -0.262317f, 0.172586f, 2.00227f, -0.159216f, 0.038422f, 10.2073f, 4.15536f, -2.3407f, -0.0550265f, 0.00964792f, -0.141336f, 0.0274501f, 0.0343921f, -0.0487428f, 0.0950172f, -0.00775017f, -0.0372492f, -0.00548121f, -0.0663695f, 0.0960506f, -0.200008f, -0.0412827f, 0.58728f, 0.0515787f, 0.337254f, 0.855024f, 0.668371f, -0.114904f, -3.62962f, -0.467477f, -0.215472f, 2.61537f, 0.406117f, -1.36373f, 0.0425394f, 0.12208f, 0.0934502f, 0.123055f, 0.0340935f, -0.142466f, 0.035037f, -0.0490666f, 0.0733208f, 0.0576672f, 0.123984f, -0.0517194f, -0.253018f, 0.590565f, 0.145849f, 0.315185f, 0.221534f, -0.149081f, 0.216161f, -0.349575f, 24.5664f, -0.994196f, 0.614289f, -18.7905f, -2.83277f, -0.716801f, -0.347201f, 0.479515f, -0.246027f, 0.0758683f, 0.137293f, -0.17781f, 0.118751f, -0.00108329f, -0.237334f, 0.355732f, -0.12991f, -0.0547627f, -0.318576f, -0.325524f, 0.180494f, -0.0625604f, 0.141219f, 0.344064f, 0.37658f, -0.591772f, 5.8427f, -0.38075f, 0.221894f, -1.41934f, -1879430.0f, 1.34114f, 0.0283355f, -0.0447856f, -0.0211466f, -0.0256927f, 0.0139618f, 0.0207934f, -0.0107666f, 0.0110969f, 0.0586069f, -0.0253545f, -0.0328433f, 0.11872f, -0.216943f, 0.145748f, 0.119808f, -0.0915211f, -0.120647f, -0.0787719f, -0.143644f, -0.595116f, -1.152f, -1.25335f, -1.17092f, 4.34023f, -975268.0f, -1.37033f, -0.0401123f, 0.210602f, -0.136656f, 0.135962f, -0.0523293f, 0.0444604f, 0.0143928f, 0.00412666f, -0.0193003f, 0.218452f, -0.110204f, -2.02563f, 0.918238f, -2.45362f, 1.19542f, -0.061362f, -1.92243f, 0.308111f, 0.49764f, 0.912356f, 0.209272f, -2.34525f, 2.19326f, -6.47121f, 1.69771f, -0.725123f, 0.0118929f, 0.0377944f, 0.0554003f, 0.0226452f, -0.0704421f, -0.0300309f, 0.0122978f, -0.0041782f, -0.0686612f, 0.0313115f, 0.039111f, 0.364111f, -0.0945548f, 0.0229876f, -0.17414f, 0.329795f, 0.114714f, 0.30022f, 0.106997f, 0.132355f, 5.79932f, 0.908058f, -0.905324f, -3.3561f, 0.190647f, 0.184211f, -0.673648f, 0.231807f, -0.0586222f, 0.230752f, -0.438277f, 0.245857f, -0.17215f, 0.0876383f, -0.720512f, 0.162515f, 0.0170571f, 0.101781f, 0.388477f, 1.32931f, 1.08548f, -0.936301f, -2.36958f, -6.71988f, -3.44376f, 2.13818f, 14.2318f, 4.91459f, -3.09052f, -9.69191f, -0.768234f, 1.79604f, 0.0549653f, 0.163399f, 0.0797025f, 0.0343933f, -0.0555876f, -0.00505673f, 0.0187258f, 0.0326628f, 0.0231486f, 0.15573f, 0.0476223f, -0.254824f, 1.60155f, -0.801221f, 2.55496f, 0.737629f, -1.36249f, -0.695463f, -2.44301f, -1.73188f, 3.95279f, 1.89068f, 0.486087f, -11.3343f, 3941600.0f, -0.381439f, 0.12115f, -0.906927f, 2.93878f, 1.6388f, 0.882811f, 0.874344f, 1.21726f, -0.874545f, 0.321706f, 0.785055f, 0.946558f, -0.575066f, -3.46553f, 0.884905f, 0.0924047f, -9.90712f, 0.391338f, 0.160103f, -2.04954f, 4.1455f, 0.0684029f, -0.144761f, -0.285282f, 0.379244f, -1.1584f, -0.0277241f, -9.85f, -4.82386f, 3.71333f, 3.87308f, 3.52558f}; + static final int[] topo = new int[]{25, 15, 2}; + static final MLPState net = new MLPState(); + static final float[] tansig_table; + static final int[] mono_voice_bandwidth_thresholds; + static final int[] mono_music_bandwidth_thresholds; + static final int[] stereo_voice_bandwidth_thresholds; + static final int[] stereo_music_bandwidth_thresholds; + public static final int stereo_voice_threshold = 30000; + public static final int stereo_music_threshold = 30000; + static final int[][] mode_thresholds; + + OpusTables() { + } + + static { + OpusTables.net.layers = 3; + OpusTables.net.topo = topo; + OpusTables.net.weights = weights; + tansig_table = new float[]{0.0f, 0.039979f, 0.07983f, 0.119427f, 0.158649f, 0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f, 0.379949f, 0.413644f, 0.446244f, 0.4777f, 0.507977f, 0.53705f, 0.5649f, 0.591519f, 0.616909f, 0.641077f, 0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f, 0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.82104f, 0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f, 0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.91542f, 0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f, 0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.96109f, 0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f, 0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f, 0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f, 0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.99202f, 0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f, 0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f, 0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.99759f, 0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f, 0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f, 0.999f, 0.999076f, 0.999147f, 0.999213f, 0.999273f, 0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f, 0.99955f, 0.999585f, 0.999617f, 0.999646f, 0.999673f, 0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f, 0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f, 0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f, 0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f, 0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f, 0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.99997f, 0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.99998f, 0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f, 0.999988f, 0.999989f, 0.99999f, 0.99999f, 0.999991f, 0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f, 0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f, 0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f, 0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; + mono_voice_bandwidth_thresholds = new int[]{11000, 1000, 14000, 1000, 17000, 1000, 21000, 2000}; + mono_music_bandwidth_thresholds = new int[]{12000, 1000, 15000, 1000, 18000, 2000, 22000, 2000}; + stereo_voice_bandwidth_thresholds = new int[]{11000, 1000, 14000, 1000, 21000, 2000, 28000, 2000}; + stereo_music_bandwidth_thresholds = new int[]{12000, 1000, 18000, 2000, 21000, 2000, 30000, 2000}; + mode_thresholds = new int[][]{{64000, 16000}, {36000, 16000}}; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/PLC.java b/source/decompiled/de/maxhenkel/voicechat/concentus/PLC.java new file mode 100644 index 0000000..9ff9d1b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/PLC.java @@ -0,0 +1,240 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BWExpander; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.LPCInversePredGain; +import de.maxhenkel.voicechat.concentus.PLCStruct; +import de.maxhenkel.voicechat.concentus.SilkChannelDecoder; +import de.maxhenkel.voicechat.concentus.SilkDecoderControl; +import de.maxhenkel.voicechat.concentus.SumSqrShift; + +class PLC { + private static final int NB_ATT = 2; + private static final short[] HARM_ATT_Q15 = new short[]{32440, 31130}; + private static final short[] PLC_RAND_ATTENUATE_V_Q15 = new short[]{31130, 26214}; + private static final short[] PLC_RAND_ATTENUATE_UV_Q15 = new short[]{32440, 29491}; + + PLC() { + } + + static void silk_PLC_Reset(SilkChannelDecoder psDec) { + psDec.sPLC.pitchL_Q8 = Inlines.silk_LSHIFT(psDec.frame_length, 7); + psDec.sPLC.prevGain_Q16[0] = 65536; + psDec.sPLC.prevGain_Q16[1] = 65536; + psDec.sPLC.subfr_length = 20; + psDec.sPLC.nb_subfr = 2; + } + + static void silk_PLC(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, short[] frame, int frame_ptr, int lost) { + if (psDec.fs_kHz != psDec.sPLC.fs_kHz) { + PLC.silk_PLC_Reset(psDec); + psDec.sPLC.fs_kHz = psDec.fs_kHz; + } + if (lost != 0) { + PLC.silk_PLC_conceal(psDec, psDecCtrl, frame, frame_ptr); + ++psDec.lossCnt; + } else { + PLC.silk_PLC_update(psDec, psDecCtrl); + } + } + + static void silk_PLC_update(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl) { + PLCStruct psPLC = psDec.sPLC; + psDec.prevSignalType = psDec.indices.signalType; + int LTP_Gain_Q14 = 0; + if (psDec.indices.signalType == 2) { + int i; + for (int j = 0; j * psDec.subfr_length < psDecCtrl.pitchL[psDec.nb_subfr - 1] && j != psDec.nb_subfr; ++j) { + int temp_LTP_Gain_Q14 = 0; + for (i = 0; i < 5; ++i) { + temp_LTP_Gain_Q14 += psDecCtrl.LTPCoef_Q14[(psDec.nb_subfr - 1 - j) * 5 + i]; + } + if (temp_LTP_Gain_Q14 <= LTP_Gain_Q14) continue; + LTP_Gain_Q14 = temp_LTP_Gain_Q14; + System.arraycopy(psDecCtrl.LTPCoef_Q14, Inlines.silk_SMULBB(psDec.nb_subfr - 1 - j, 5), psPLC.LTPCoef_Q14, 0, 5); + psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(psDecCtrl.pitchL[psDec.nb_subfr - 1 - j], 8); + } + Arrays.MemSet(psPLC.LTPCoef_Q14, (short)0, 5); + psPLC.LTPCoef_Q14[2] = (short)LTP_Gain_Q14; + if (LTP_Gain_Q14 < 11469) { + int tmp = Inlines.silk_LSHIFT(11469, 10); + int scale_Q10 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1)); + for (i = 0; i < 5; ++i) { + psPLC.LTPCoef_Q14[i] = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q10), 10); + } + } else if (LTP_Gain_Q14 > 15565) { + int tmp = Inlines.silk_LSHIFT(15565, 14); + int scale_Q14 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1)); + for (i = 0; i < 5; ++i) { + psPLC.LTPCoef_Q14[i] = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q14), 14); + } + } + } else { + psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(Inlines.silk_SMULBB(psDec.fs_kHz, 18), 8); + Arrays.MemSet(psPLC.LTPCoef_Q14, (short)0, 5); + } + System.arraycopy(psDecCtrl.PredCoef_Q12[1], 0, psPLC.prevLPC_Q12, 0, psDec.LPC_order); + psPLC.prevLTP_scale_Q14 = (short)psDecCtrl.LTP_scale_Q14; + System.arraycopy(psDecCtrl.Gains_Q16, psDec.nb_subfr - 2, psPLC.prevGain_Q16, 0, 2); + psPLC.subfr_length = psDec.subfr_length; + psPLC.nb_subfr = psDec.nb_subfr; + } + + static void silk_PLC_energy(BoxedValueInt energy1, BoxedValueInt shift1, BoxedValueInt energy2, BoxedValueInt shift2, int[] exc_Q14, int[] prevGain_Q10, int subfr_length, int nb_subfr) { + int exc_buf_ptr = 0; + short[] exc_buf = new short[2 * subfr_length]; + for (int k = 0; k < 2; ++k) { + for (int i = 0; i < subfr_length; ++i) { + exc_buf[exc_buf_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(Inlines.silk_SMULWW(exc_Q14[i + (k + nb_subfr - 2) * subfr_length], prevGain_Q10[k]), 8)); + } + exc_buf_ptr += subfr_length; + } + SumSqrShift.silk_sum_sqr_shift(energy1, shift1, exc_buf, subfr_length); + SumSqrShift.silk_sum_sqr_shift(energy2, shift2, exc_buf, subfr_length, subfr_length); + } + + static void silk_PLC_conceal(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, short[] frame, int frame_ptr) { + int j; + int i; + BoxedValueInt energy1 = new BoxedValueInt(0); + BoxedValueInt energy2 = new BoxedValueInt(0); + BoxedValueInt shift1 = new BoxedValueInt(0); + BoxedValueInt shift2 = new BoxedValueInt(0); + short[] sLTP = new short[psDec.ltp_mem_length]; + int[] sLTP_Q14 = new int[psDec.ltp_mem_length + psDec.frame_length]; + PLCStruct psPLC = psDec.sPLC; + int[] prevGain_Q10 = new int[]{Inlines.silk_RSHIFT(psPLC.prevGain_Q16[0], 6), Inlines.silk_RSHIFT(psPLC.prevGain_Q16[1], 6)}; + if (psDec.first_frame_after_reset != 0) { + Arrays.MemSet(psPLC.prevLPC_Q12, (short)0, 16); + } + PLC.silk_PLC_energy(energy1, shift1, energy2, shift2, psDec.exc_Q14, prevGain_Q10, psDec.subfr_length, psDec.nb_subfr); + int rand_ptr = Inlines.silk_RSHIFT(energy1.Val, shift2.Val) < Inlines.silk_RSHIFT(energy2.Val, shift1.Val) ? Inlines.silk_max_int(0, (psPLC.nb_subfr - 1) * psPLC.subfr_length - 128) : Inlines.silk_max_int(0, psPLC.nb_subfr * psPLC.subfr_length - 128); + short[] B_Q14 = psPLC.LTPCoef_Q14; + short rand_scale_Q14 = psPLC.randScale_Q14; + short harm_Gain_Q15 = HARM_ATT_Q15[Inlines.silk_min_int(1, psDec.lossCnt)]; + int rand_Gain_Q15 = psDec.prevSignalType == 2 ? PLC_RAND_ATTENUATE_V_Q15[Inlines.silk_min_int(1, psDec.lossCnt)] : PLC_RAND_ATTENUATE_UV_Q15[Inlines.silk_min_int(1, psDec.lossCnt)]; + BWExpander.silk_bwexpander(psPLC.prevLPC_Q12, psDec.LPC_order, 64881); + if (psDec.lossCnt == 0) { + rand_scale_Q14 = 16384; + if (psDec.prevSignalType == 2) { + for (i = 0; i < 5; ++i) { + rand_scale_Q14 = (short)(rand_scale_Q14 - B_Q14[i]); + } + rand_scale_Q14 = Inlines.silk_max_16((short)3277, rand_scale_Q14); + rand_scale_Q14 = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, psPLC.prevLTP_scale_Q14), 14); + } else { + int invGain_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain(psPLC.prevLPC_Q12, psDec.LPC_order); + int down_scale_Q30 = Inlines.silk_min_32(Inlines.silk_RSHIFT(0x40000000, 3), invGain_Q30); + down_scale_Q30 = Inlines.silk_max_32(Inlines.silk_RSHIFT(0x40000000, 8), down_scale_Q30); + down_scale_Q30 = Inlines.silk_LSHIFT(down_scale_Q30, 3); + rand_Gain_Q15 = Inlines.silk_RSHIFT(Inlines.silk_SMULWB(down_scale_Q30, rand_Gain_Q15), 14); + } + } + int rand_seed = psPLC.rand_seed; + int lag = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8); + int sLTP_buf_idx = psDec.ltp_mem_length; + int idx = psDec.ltp_mem_length - lag - psDec.LPC_order - 2; + Inlines.OpusAssert(idx > 0); + Filters.silk_LPC_analysis_filter(sLTP, idx, psDec.outBuf, idx, psPLC.prevLPC_Q12, 0, psDec.ltp_mem_length - idx, psDec.LPC_order); + int inv_gain_Q30 = Inlines.silk_INVERSE32_varQ(psPLC.prevGain_Q16[1], 46); + inv_gain_Q30 = Inlines.silk_min(inv_gain_Q30, 0x3FFFFFFF); + for (i = idx + psDec.LPC_order; i < psDec.ltp_mem_length; ++i) { + sLTP_Q14[i] = Inlines.silk_SMULWB(inv_gain_Q30, sLTP[i]); + } + for (int k = 0; k < psDec.nb_subfr; ++k) { + int pred_lag_ptr = sLTP_buf_idx - lag + 2; + for (i = 0; i < psDec.subfr_length; ++i) { + int LTP_pred_Q12 = 2; + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr], B_Q14[0]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 1], B_Q14[1]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 2], B_Q14[2]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 3], B_Q14[3]); + LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 4], B_Q14[4]); + ++pred_lag_ptr; + rand_seed = Inlines.silk_RAND(rand_seed); + idx = Inlines.silk_RSHIFT(rand_seed, 25) & 0x7F; + sLTP_Q14[sLTP_buf_idx] = Inlines.silk_LSHIFT32(Inlines.silk_SMLAWB(LTP_pred_Q12, psDec.exc_Q14[rand_ptr + idx], rand_scale_Q14), 2); + ++sLTP_buf_idx; + } + for (j = 0; j < 5; ++j) { + B_Q14[j] = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(harm_Gain_Q15, B_Q14[j]), 15); + } + rand_scale_Q14 = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, rand_Gain_Q15), 15); + psPLC.pitchL_Q8 = Inlines.silk_SMLAWB(psPLC.pitchL_Q8, psPLC.pitchL_Q8, 655); + psPLC.pitchL_Q8 = Inlines.silk_min_32(psPLC.pitchL_Q8, Inlines.silk_LSHIFT(Inlines.silk_SMULBB(18, psDec.fs_kHz), 8)); + lag = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8); + } + int sLPC_Q14_ptr = psDec.ltp_mem_length - 16; + System.arraycopy(psDec.sLPC_Q14_buf, 0, sLTP_Q14, sLPC_Q14_ptr, 16); + Inlines.OpusAssert(psDec.LPC_order >= 10); + for (i = 0; i < psDec.frame_length; ++i) { + int sLPCmaxi = sLPC_Q14_ptr + 16 + i; + int LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 1], psPLC.prevLPC_Q12[0]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 2], psPLC.prevLPC_Q12[1]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 3], psPLC.prevLPC_Q12[2]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 4], psPLC.prevLPC_Q12[3]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 5], psPLC.prevLPC_Q12[4]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 6], psPLC.prevLPC_Q12[5]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 7], psPLC.prevLPC_Q12[6]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 8], psPLC.prevLPC_Q12[7]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 9], psPLC.prevLPC_Q12[8]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 10], psPLC.prevLPC_Q12[9]); + for (j = 10; j < psDec.LPC_order; ++j) { + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - j - 1], psPLC.prevLPC_Q12[j]); + } + sLTP_Q14[sLPCmaxi] = Inlines.silk_ADD_LSHIFT32(sLTP_Q14[sLPCmaxi], LPC_pred_Q10, 4); + frame[frame_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLTP_Q14[sLPCmaxi], prevGain_Q10[1]), 8))); + } + System.arraycopy(sLTP_Q14, sLPC_Q14_ptr + psDec.frame_length, psDec.sLPC_Q14_buf, 0, 16); + psPLC.rand_seed = rand_seed; + psPLC.randScale_Q14 = rand_scale_Q14; + for (i = 0; i < 4; ++i) { + psDecCtrl.pitchL[i] = lag; + } + } + + static void silk_PLC_glue_frames(SilkChannelDecoder psDec, short[] frame, int frame_ptr, int length) { + BoxedValueInt energy_shift = new BoxedValueInt(0); + BoxedValueInt energy = new BoxedValueInt(0); + PLCStruct psPLC = psDec.sPLC; + if (psDec.lossCnt != 0) { + BoxedValueInt boxed_conc_e = new BoxedValueInt(0); + BoxedValueInt boxed_conc_shift = new BoxedValueInt(0); + SumSqrShift.silk_sum_sqr_shift(boxed_conc_e, boxed_conc_shift, frame, frame_ptr, length); + psPLC.conc_energy = boxed_conc_e.Val; + psPLC.conc_energy_shift = boxed_conc_shift.Val; + psPLC.last_frame_lost = 1; + } else { + if (psDec.sPLC.last_frame_lost != 0) { + SumSqrShift.silk_sum_sqr_shift(energy, energy_shift, frame, frame_ptr, length); + if (energy_shift.Val > psPLC.conc_energy_shift) { + psPLC.conc_energy = Inlines.silk_RSHIFT(psPLC.conc_energy, energy_shift.Val - psPLC.conc_energy_shift); + } else if (energy_shift.Val < psPLC.conc_energy_shift) { + energy.Val = Inlines.silk_RSHIFT(energy.Val, psPLC.conc_energy_shift - energy_shift.Val); + } + if (energy.Val > psPLC.conc_energy) { + int LZ = Inlines.silk_CLZ32(psPLC.conc_energy); + psPLC.conc_energy = Inlines.silk_LSHIFT(psPLC.conc_energy, --LZ); + energy.Val = Inlines.silk_RSHIFT(energy.Val, Inlines.silk_max_32(24 - LZ, 0)); + int frac_Q24 = Inlines.silk_DIV32(psPLC.conc_energy, Inlines.silk_max(energy.Val, 1)); + int gain_Q16 = Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(frac_Q24), 4); + int slope_Q16 = Inlines.silk_DIV32_16(65536 - gain_Q16, length); + slope_Q16 = Inlines.silk_LSHIFT(slope_Q16, 2); + for (int i = frame_ptr; i < frame_ptr + length; ++i) { + frame[i] = (short)Inlines.silk_SMULWB(gain_Q16, frame[i]); + if ((gain_Q16 += slope_Q16) > 65536) break; + } + } + } + psPLC.last_frame_lost = 0; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/PLCStruct.java b/source/decompiled/de/maxhenkel/voicechat/concentus/PLCStruct.java new file mode 100644 index 0000000..91ec03b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/PLCStruct.java @@ -0,0 +1,42 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class PLCStruct { + int pitchL_Q8 = 0; + final short[] LTPCoef_Q14 = new short[5]; + final short[] prevLPC_Q12 = new short[16]; + int last_frame_lost = 0; + int rand_seed = 0; + short randScale_Q14 = 0; + int conc_energy = 0; + int conc_energy_shift = 0; + short prevLTP_scale_Q14 = 0; + final int[] prevGain_Q16 = new int[2]; + int fs_kHz = 0; + int nb_subfr = 0; + int subfr_length = 0; + + PLCStruct() { + } + + void Reset() { + this.pitchL_Q8 = 0; + Arrays.MemSet(this.LTPCoef_Q14, (short)0, 5); + Arrays.MemSet(this.prevLPC_Q12, (short)0, 16); + this.last_frame_lost = 0; + this.rand_seed = 0; + this.randScale_Q14 = 0; + this.conc_energy = 0; + this.conc_energy_shift = 0; + this.prevLTP_scale_Q14 = 0; + Arrays.MemSet(this.prevGain_Q16, 0, 2); + this.fs_kHz = 0; + this.nb_subfr = 0; + this.subfr_length = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Pitch.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Pitch.java new file mode 100644 index 0000000..b578c0b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Pitch.java @@ -0,0 +1,267 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Autocorrelation; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltLPC; +import de.maxhenkel.voicechat.concentus.CeltPitchXCorr; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Kernels; + +class Pitch { + private static final int[] second_check = new int[]{0, 0, 3, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 3, 2}; + + Pitch() { + } + + static void find_best_pitch(int[] xcorr, int[] y, int len, int max_pitch, int[] best_pitch, int yshift, int maxcorr) { + int Syy = 1; + int xshift = Inlines.celt_ilog2(maxcorr) - 14; + int best_num_0 = -1; + int best_num_1 = -1; + int best_den_0 = 0; + int best_den_1 = 0; + best_pitch[0] = 0; + best_pitch[1] = 1; + for (int j = 0; j < len; ++j) { + Syy = Inlines.ADD32(Syy, Inlines.SHR32(Inlines.MULT16_16(y[j], y[j]), yshift)); + } + for (int i = 0; i < max_pitch; ++i) { + short xcorr16; + int num; + if (xcorr[i] > 0 && Inlines.MULT16_32_Q15(num = Inlines.MULT16_16_Q15((int)(xcorr16 = Inlines.EXTRACT16(Inlines.VSHR32(xcorr[i], xshift))), (int)xcorr16), best_den_1) > Inlines.MULT16_32_Q15(best_num_1, Syy)) { + if (Inlines.MULT16_32_Q15(num, best_den_0) > Inlines.MULT16_32_Q15(best_num_0, Syy)) { + best_num_1 = best_num_0; + best_den_1 = best_den_0; + best_pitch[1] = best_pitch[0]; + best_num_0 = num; + best_den_0 = Syy; + best_pitch[0] = i; + } else { + best_num_1 = num; + best_den_1 = Syy; + best_pitch[1] = i; + } + } + Syy += Inlines.SHR32(Inlines.MULT16_16(y[i + len], y[i + len]), yshift) - Inlines.SHR32(Inlines.MULT16_16(y[i], y[i]), yshift); + Syy = Inlines.MAX32(1, Syy); + } + } + + static void celt_fir5(int[] x, int[] num, int[] y, int N, int[] mem) { + int num0 = num[0]; + int num1 = num[1]; + int num2 = num[2]; + int num3 = num[3]; + int num4 = num[4]; + int mem0 = mem[0]; + int mem1 = mem[1]; + int mem2 = mem[2]; + int mem3 = mem[3]; + int mem4 = mem[4]; + for (int i = 0; i < N; ++i) { + int sum = Inlines.SHL32(Inlines.EXTEND32(x[i]), 12); + sum = Inlines.MAC16_16(sum, num0, mem0); + sum = Inlines.MAC16_16(sum, num1, mem1); + sum = Inlines.MAC16_16(sum, num2, mem2); + sum = Inlines.MAC16_16(sum, num3, mem3); + sum = Inlines.MAC16_16(sum, num4, mem4); + mem4 = mem3; + mem3 = mem2; + mem2 = mem1; + mem1 = mem0; + mem0 = x[i]; + y[i] = Inlines.ROUND16(sum, 12); + } + mem[0] = mem0; + mem[1] = mem1; + mem[2] = mem2; + mem[3] = mem3; + mem[4] = mem4; + } + + static void pitch_downsample(int[][] x, int[] x_lp, int len, int C) { + int i; + int shift; + int[] ac = new int[5]; + int tmp = Short.MAX_VALUE; + int[] lpc = new int[4]; + int[] mem = new int[]{0, 0, 0, 0, 0}; + int[] lpc2 = new int[5]; + int c1 = 26214; + int maxabs = Inlines.celt_maxabs32(x[0], 0, len); + if (C == 2) { + int maxabs_1 = Inlines.celt_maxabs32(x[1], 0, len); + maxabs = Inlines.MAX32(maxabs, maxabs_1); + } + if (maxabs < 1) { + maxabs = 1; + } + if ((shift = Inlines.celt_ilog2(maxabs) - 10) < 0) { + shift = 0; + } + if (C == 2) { + ++shift; + } + int halflen = len >> 1; + for (i = 1; i < halflen; ++i) { + x_lp[i] = Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[0][2 * i - 1] + x[0][2 * i + 1]) + x[0][2 * i]), shift); + } + x_lp[0] = Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[0][1]) + x[0][0]), shift); + if (C == 2) { + for (i = 1; i < halflen; ++i) { + int n = i; + x_lp[n] = x_lp[n] + Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[1][2 * i - 1] + x[1][2 * i + 1]) + x[1][2 * i]), shift); + } + x_lp[0] = x_lp[0] + Inlines.SHR32(Inlines.HALF32(Inlines.HALF32(x[1][1]) + x[1][0]), shift); + } + Autocorrelation._celt_autocorr(x_lp, ac, null, 0, 4, halflen); + ac[0] = ac[0] + Inlines.SHR32(ac[0], 13); + for (i = 1; i <= 4; ++i) { + int n = i; + ac[n] = ac[n] - Inlines.MULT16_32_Q15(2 * i * i, ac[i]); + } + CeltLPC.celt_lpc(lpc, ac, 4); + for (i = 0; i < 4; ++i) { + tmp = Inlines.MULT16_16_Q15(29491, tmp); + lpc[i] = Inlines.MULT16_16_Q15(lpc[i], tmp); + } + lpc2[0] = lpc[0] + 3277; + lpc2[1] = lpc[1] + Inlines.MULT16_16_Q15(c1, lpc[0]); + lpc2[2] = lpc[2] + Inlines.MULT16_16_Q15(c1, lpc[1]); + lpc2[3] = lpc[3] + Inlines.MULT16_16_Q15(c1, lpc[2]); + lpc2[4] = Inlines.MULT16_16_Q15(c1, lpc[3]); + Pitch.celt_fir5(x_lp, lpc2, x_lp, halflen, mem); + } + + static void pitch_search(int[] x_lp, int x_lp_ptr, int[] y, int len, int max_pitch, BoxedValueInt pitch) { + int b; + int a; + int c; + int ymax; + int j; + int[] best_pitch = new int[]{0, 0}; + int shift = 0; + Inlines.OpusAssert(len > 0); + Inlines.OpusAssert(max_pitch > 0); + int lag = len + max_pitch; + int[] x_lp4 = new int[len >> 2]; + int[] y_lp4 = new int[lag >> 2]; + int[] xcorr = new int[max_pitch >> 1]; + for (j = 0; j < len >> 2; ++j) { + x_lp4[j] = x_lp[x_lp_ptr + 2 * j]; + } + for (j = 0; j < lag >> 2; ++j) { + y_lp4[j] = y[2 * j]; + } + int xmax = Inlines.celt_maxabs32(x_lp4, 0, len >> 2); + shift = Inlines.celt_ilog2(Inlines.MAX32(1, Inlines.MAX32(xmax, ymax = Inlines.celt_maxabs32(y_lp4, 0, lag >> 2)))) - 11; + if (shift > 0) { + for (j = 0; j < len >> 2; ++j) { + x_lp4[j] = Inlines.SHR16(x_lp4[j], shift); + } + for (j = 0; j < lag >> 2; ++j) { + y_lp4[j] = Inlines.SHR16(y_lp4[j], shift); + } + shift *= 2; + } else { + shift = 0; + } + int maxcorr = CeltPitchXCorr.pitch_xcorr(x_lp4, y_lp4, xcorr, len >> 2, max_pitch >> 2); + Pitch.find_best_pitch(xcorr, y_lp4, len >> 2, max_pitch >> 2, best_pitch, 0, maxcorr); + maxcorr = 1; + for (int i = 0; i < max_pitch >> 1; ++i) { + xcorr[i] = 0; + if (Inlines.abs(i - 2 * best_pitch[0]) > 2 && Inlines.abs(i - 2 * best_pitch[1]) > 2) continue; + int sum = 0; + for (j = 0; j < len >> 1; ++j) { + sum += Inlines.SHR32(Inlines.MULT16_16(x_lp[x_lp_ptr + j], y[i + j]), shift); + } + xcorr[i] = Inlines.MAX32(-1, sum); + maxcorr = Inlines.MAX32(maxcorr, sum); + } + Pitch.find_best_pitch(xcorr, y, len >> 1, max_pitch >> 1, best_pitch, shift + 1, maxcorr); + int offset = best_pitch[0] > 0 && best_pitch[0] < (max_pitch >> 1) - 1 ? ((c = xcorr[best_pitch[0] + 1]) - (a = xcorr[best_pitch[0] - 1]) > Inlines.MULT16_32_Q15((short)22938, (b = xcorr[best_pitch[0]]) - a) ? 1 : (a - c > Inlines.MULT16_32_Q15((short)22938, b - c) ? -1 : 0)) : 0; + pitch.Val = 2 * best_pitch[0] - offset; + } + + static int remove_doubling(int[] x, int maxperiod, int minperiod, int N, BoxedValueInt T0_, int prev_period, int prev_gain) { + int k; + int g; + int T0; + int[] xcorr = new int[3]; + int minperiod0 = minperiod; + minperiod /= 2; + T0_.Val /= 2; + prev_period /= 2; + N /= 2; + int x_ptr = maxperiod /= 2; + if (T0_.Val >= maxperiod) { + T0_.Val = maxperiod - 1; + } + int T = T0 = T0_.Val; + int[] yy_lookup = new int[maxperiod + 1]; + BoxedValueInt boxed_xx = new BoxedValueInt(0); + BoxedValueInt boxed_xy = new BoxedValueInt(0); + BoxedValueInt boxed_xy2 = new BoxedValueInt(0); + Kernels.dual_inner_prod(x, x_ptr, x, x_ptr, x, x_ptr - T0, N, boxed_xx, boxed_xy); + int xx = boxed_xx.Val; + int xy = boxed_xy.Val; + yy_lookup[0] = xx; + int yy = xx; + for (int i = 1; i <= maxperiod; ++i) { + int xi = x_ptr - i; + yy = yy + Inlines.MULT16_16(x[xi], x[xi]) - Inlines.MULT16_16(x[xi + N], x[xi + N]); + yy_lookup[i] = Inlines.MAX32(0, yy); + } + yy = yy_lookup[T0]; + int best_xy = xy; + int best_yy = yy; + int x2y2 = 1 + Inlines.HALF32(Inlines.MULT32_32_Q31(xx, yy)); + int sh = Inlines.celt_ilog2(x2y2) >> 1; + int t = Inlines.VSHR32(x2y2, 2 * (sh - 7)); + int g0 = g = Inlines.VSHR32(Inlines.MULT16_32_Q15(Inlines.celt_rsqrt_norm(t), xy), sh + 1); + for (k = 2; k <= 15; ++k) { + int cont = 0; + int T1 = Inlines.celt_udiv(2 * T0 + k, 2 * k); + if (T1 < minperiod) break; + int T1b = k == 2 ? (T1 + T0 > maxperiod ? T0 : T0 + T1) : Inlines.celt_udiv(2 * second_check[k] * T0 + k, 2 * k); + Kernels.dual_inner_prod(x, x_ptr, x, x_ptr - T1, x, x_ptr - T1b, N, boxed_xy, boxed_xy2); + xy = boxed_xy.Val; + int xy2 = boxed_xy2.Val; + yy = yy_lookup[T1] + yy_lookup[T1b]; + int x2y22 = 1 + Inlines.MULT32_32_Q31(xx, yy); + int sh2 = Inlines.celt_ilog2(x2y22) >> 1; + int t2 = Inlines.VSHR32(x2y22, 2 * (sh2 - 7)); + int g1 = Inlines.VSHR32(Inlines.MULT16_32_Q15(Inlines.celt_rsqrt_norm(t2), xy += xy2), sh2 + 1); + cont = Inlines.abs(T1 - prev_period) <= 1 ? prev_gain : (Inlines.abs(T1 - prev_period) <= 2 && 5 * k * k < T0 ? Inlines.HALF16(prev_gain) : 0); + int thresh = Inlines.MAX16(9830, Inlines.MULT16_16_Q15(22938, g0) - cont); + if (T1 < 3 * minperiod) { + thresh = Inlines.MAX16(13107, Inlines.MULT16_16_Q15(27853, g0) - cont); + } else if (T1 < 2 * minperiod) { + thresh = Inlines.MAX16(16384, Inlines.MULT16_16_Q15(29491, g0) - cont); + } + if (g1 <= thresh) continue; + best_xy = xy; + best_yy = yy; + T = T1; + g = g1; + } + int pg = best_yy <= (best_xy = Inlines.MAX32(0, best_xy)) ? Short.MAX_VALUE : Inlines.SHR32(Inlines.frac_div32(best_xy, best_yy + 1), 16); + for (k = 0; k < 3; ++k) { + xcorr[k] = Kernels.celt_inner_prod(x, x_ptr, x, x_ptr - (T + k - 1), N); + } + int offset = xcorr[2] - xcorr[0] > Inlines.MULT16_32_Q15((short)22938, xcorr[1] - xcorr[0]) ? 1 : (xcorr[0] - xcorr[2] > Inlines.MULT16_32_Q15((short)22938, xcorr[1] - xcorr[2]) ? -1 : 0); + if (pg > g) { + pg = g; + } + T0_.Val = 2 * T + offset; + if (T0_.Val < minperiod0) { + T0_.Val = minperiod0; + } + return pg; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/PitchAnalysisCore.java b/source/decompiled/de/maxhenkel/voicechat/concentus/PitchAnalysisCore.java new file mode 100644 index 0000000..b8658bf --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/PitchAnalysisCore.java @@ -0,0 +1,458 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.BoxedValueShort; +import de.maxhenkel.voicechat.concentus.CeltPitchXCorr; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Resampler; +import de.maxhenkel.voicechat.concentus.SilkTables; +import de.maxhenkel.voicechat.concentus.Sort; +import de.maxhenkel.voicechat.concentus.SumSqrShift; + +class PitchAnalysisCore { + private static final int SCRATCH_SIZE = 22; + private static final int SF_LENGTH_4KHZ = 20; + private static final int SF_LENGTH_8KHZ = 40; + private static final int MIN_LAG_4KHZ = 8; + private static final int MIN_LAG_8KHZ = 16; + private static final int MAX_LAG_4KHZ = 72; + private static final int MAX_LAG_8KHZ = 143; + private static final int CSTRIDE_4KHZ = 65; + private static final int CSTRIDE_8KHZ = 132; + private static final int D_COMP_MIN = 13; + private static final int D_COMP_MAX = 147; + private static final int D_COMP_STRIDE = 134; + + PitchAnalysisCore() { + } + + static int silk_pitch_analysis_core(short[] frame, int[] pitch_out, BoxedValueShort lagIndex, BoxedValueByte contourIndex, BoxedValueInt LTPCorr_Q15, int prevLag, int search_thres1_Q16, int search_thres2_Q13, int Fs_kHz, int complexity, int nb_subfr) { + int CCmax_new; + int nb_cbk_search; + byte[][] Lag_CB_ptr; + int prevLag_log2_Q7; + int j; + int energy_target; + int length_d_srch; + int sum; + int d; + int cross_corr; + int basis_ptr; + short[] basis; + int k; + int i; + int[] filt_state = new int[6]; + int[] d_srch = new int[24]; + int[] CC = new int[11]; + Inlines.OpusAssert(Fs_kHz == 8 || Fs_kHz == 12 || Fs_kHz == 16); + Inlines.OpusAssert(complexity >= 0); + Inlines.OpusAssert(complexity <= 2); + Inlines.OpusAssert(search_thres1_Q16 >= 0 && search_thres1_Q16 <= 65536); + Inlines.OpusAssert(search_thres2_Q13 >= 0 && search_thres2_Q13 <= 8192); + int frame_length = (20 + nb_subfr * 5) * Fs_kHz; + int frame_length_4kHz = (20 + nb_subfr * 5) * 4; + int frame_length_8kHz = (20 + nb_subfr * 5) * 8; + int sf_length = 5 * Fs_kHz; + int min_lag = 2 * Fs_kHz; + int max_lag = 18 * Fs_kHz - 1; + short[] frame_8kHz = new short[frame_length_8kHz]; + if (Fs_kHz == 16) { + Arrays.MemSet(filt_state, 0, 2); + Resampler.silk_resampler_down2(filt_state, frame_8kHz, frame, frame_length); + } else if (Fs_kHz == 12) { + Arrays.MemSet(filt_state, 0, 6); + Resampler.silk_resampler_down2_3(filt_state, frame_8kHz, frame, frame_length); + } else { + Inlines.OpusAssert(Fs_kHz == 8); + System.arraycopy(frame, 0, frame_8kHz, 0, frame_length_8kHz); + } + Arrays.MemSet(filt_state, 0, 2); + short[] frame_4kHz = new short[frame_length_4kHz]; + Resampler.silk_resampler_down2(filt_state, frame_4kHz, frame_8kHz, frame_length_8kHz); + for (i = frame_length_4kHz - 1; i > 0; --i) { + frame_4kHz[i] = Inlines.silk_ADD_SAT16(frame_4kHz[i], frame_4kHz[i - 1]); + } + BoxedValueInt boxed_energy = new BoxedValueInt(0); + BoxedValueInt boxed_shift = new BoxedValueInt(0); + SumSqrShift.silk_sum_sqr_shift(boxed_energy, boxed_shift, frame_4kHz, frame_length_4kHz); + int energy = boxed_energy.Val; + int shift = boxed_shift.Val; + if (shift > 0) { + shift = Inlines.silk_RSHIFT(shift, 1); + for (i = 0; i < frame_length_4kHz; ++i) { + frame_4kHz[i] = Inlines.silk_RSHIFT16(frame_4kHz[i], shift); + } + } + short[] C = new short[nb_subfr * 132]; + int[] xcorr32 = new int[65]; + Arrays.MemSet(C, (short)0, (nb_subfr >> 1) * 65); + short[] target = frame_4kHz; + int target_ptr = Inlines.silk_LSHIFT(20, 2); + for (k = 0; k < nb_subfr >> 1; ++k) { + basis = target; + basis_ptr = target_ptr - 8; + CeltPitchXCorr.pitch_xcorr(target, target_ptr, target, target_ptr - 72, xcorr32, 40, 65); + cross_corr = xcorr32[64]; + int normalizer = Inlines.silk_inner_prod_self(target, target_ptr, 40); + normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_inner_prod_self(basis, basis_ptr, 40)); + normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_SMULBB(40, 4000)); + Inlines.MatrixSet(C, k, 0, 65, (short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 14)); + for (d = 9; d <= 72; ++d) { + cross_corr = xcorr32[72 - d]; + normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_SMULBB(basis[--basis_ptr], basis[basis_ptr]) - Inlines.silk_SMULBB(basis[basis_ptr + 40], basis[basis_ptr + 40])); + Inlines.MatrixSet(C, k, d - 8, 65, (short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 14)); + } + target_ptr += 40; + } + if (nb_subfr == 4) { + for (i = 72; i >= 8; --i) { + sum = Inlines.MatrixGet(C, 0, i - 8, 65) + Inlines.MatrixGet(C, 1, i - 8, 65); + sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); + C[i - 8] = (short)sum; + } + } else { + for (i = 72; i >= 8; --i) { + sum = Inlines.silk_LSHIFT(C[i - 8], 1); + sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); + C[i - 8] = (short)sum; + } + } + Inlines.OpusAssert(3 * (length_d_srch = Inlines.silk_ADD_LSHIFT32(4, complexity, 1)) <= 24); + Sort.silk_insertion_sort_decreasing_int16(C, d_srch, 65, length_d_srch); + short Cmax = C[0]; + if (Cmax < 3277) { + Arrays.MemSet(pitch_out, 0, nb_subfr); + LTPCorr_Q15.Val = 0; + lagIndex.Val = 0; + contourIndex.Val = 0; + return 1; + } + int threshold = Inlines.silk_SMULWB(search_thres1_Q16, Cmax); + for (i = 0; i < length_d_srch; ++i) { + if (C[i] <= threshold) { + length_d_srch = i; + break; + } + d_srch[i] = Inlines.silk_LSHIFT(d_srch[i] + 8, 1); + } + Inlines.OpusAssert(length_d_srch > 0); + short[] d_comp = new short[134]; + for (i = 13; i < 147; ++i) { + d_comp[i - 13] = 0; + } + for (i = 0; i < length_d_srch; ++i) { + d_comp[d_srch[i] - 13] = 1; + } + for (i = 146; i >= 16; --i) { + int n = i - 13; + d_comp[n] = (short)(d_comp[n] + (short)(d_comp[i - 1 - 13] + d_comp[i - 2 - 13])); + } + length_d_srch = 0; + for (i = 16; i < 144; ++i) { + if (d_comp[i + 1 - 13] <= 0) continue; + d_srch[length_d_srch] = i; + ++length_d_srch; + } + for (i = 146; i >= 16; --i) { + int n = i - 13; + d_comp[n] = (short)(d_comp[n] + (short)(d_comp[i - 1 - 13] + d_comp[i - 2 - 13] + d_comp[i - 3 - 13])); + } + int length_d_comp = 0; + for (i = 16; i < 147; ++i) { + if (d_comp[i - 13] <= 0) continue; + d_comp[length_d_comp] = (short)(i - 2); + ++length_d_comp; + } + boxed_energy.Val = 0; + boxed_shift.Val = 0; + SumSqrShift.silk_sum_sqr_shift(boxed_energy, boxed_shift, frame_8kHz, frame_length_8kHz); + energy = boxed_energy.Val; + shift = boxed_shift.Val; + if (shift > 0) { + shift = Inlines.silk_RSHIFT(shift, 1); + for (i = 0; i < frame_length_8kHz; ++i) { + frame_8kHz[i] = Inlines.silk_RSHIFT16(frame_8kHz[i], shift); + } + } + Arrays.MemSet(C, (short)0, nb_subfr * 132); + target = frame_8kHz; + target_ptr = 160; + for (k = 0; k < nb_subfr; ++k) { + energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod(target, target_ptr, target, target_ptr, 40), 1); + for (j = 0; j < length_d_comp; ++j) { + basis = target; + d = d_comp[j]; + basis_ptr = target_ptr - d; + cross_corr = Inlines.silk_inner_prod(target, target_ptr, basis, basis_ptr, 40); + if (cross_corr > 0) { + int energy_basis = Inlines.silk_inner_prod_self(basis, basis_ptr, 40); + Inlines.MatrixSet(C, k, d - 14, 132, (short)Inlines.silk_DIV32_varQ(cross_corr, Inlines.silk_ADD32(energy_target, energy_basis), 14)); + continue; + } + Inlines.MatrixSet(C, k, d - 14, 132, (short)0); + } + target_ptr += 40; + } + int CCmax = Integer.MIN_VALUE; + int CCmax_b = Integer.MIN_VALUE; + int CBimax = 0; + int lag = -1; + if (prevLag > 0) { + if (Fs_kHz == 12) { + prevLag = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(prevLag, 1), 3); + } else if (Fs_kHz == 16) { + prevLag = Inlines.silk_RSHIFT(prevLag, 1); + } + prevLag_log2_Q7 = Inlines.silk_lin2log(prevLag); + } else { + prevLag_log2_Q7 = 0; + } + Inlines.OpusAssert(search_thres2_Q13 == Inlines.silk_SAT16(search_thres2_Q13)); + if (nb_subfr == 4) { + Lag_CB_ptr = SilkTables.silk_CB_lags_stage2; + nb_cbk_search = Fs_kHz == 8 && complexity > 0 ? 11 : 3; + } else { + Lag_CB_ptr = SilkTables.silk_CB_lags_stage2_10_ms; + nb_cbk_search = 3; + } + for (k = 0; k < length_d_srch; ++k) { + d = d_srch[k]; + for (j = 0; j < nb_cbk_search; ++j) { + CC[j] = 0; + for (i = 0; i < nb_subfr; ++i) { + int d_subfr = d + Lag_CB_ptr[i][j]; + CC[j] = CC[j] + Inlines.MatrixGet(C, i, d_subfr - 14, 132); + } + } + CCmax_new = Integer.MIN_VALUE; + int CBimax_new = 0; + for (i = 0; i < nb_cbk_search; ++i) { + if (CC[i] <= CCmax_new) continue; + CCmax_new = CC[i]; + CBimax_new = i; + } + int lag_log2_Q7 = Inlines.silk_lin2log(d); + Inlines.OpusAssert(lag_log2_Q7 == Inlines.silk_SAT16(lag_log2_Q7)); + Inlines.OpusAssert(nb_subfr * 1638 == Inlines.silk_SAT16(nb_subfr * 1638)); + int CCmax_new_b = CCmax_new - Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * 1638, lag_log2_Q7), 7); + Inlines.OpusAssert(nb_subfr * 1638 == Inlines.silk_SAT16(nb_subfr * 1638)); + if (prevLag > 0) { + int delta_lag_log2_sqr_Q7 = lag_log2_Q7 - prevLag_log2_Q7; + Inlines.OpusAssert(delta_lag_log2_sqr_Q7 == Inlines.silk_SAT16(delta_lag_log2_sqr_Q7)); + delta_lag_log2_sqr_Q7 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(delta_lag_log2_sqr_Q7, delta_lag_log2_sqr_Q7), 7); + int prev_lag_bias_Q13 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * 1638, LTPCorr_Q15.Val), 15); + prev_lag_bias_Q13 = Inlines.silk_DIV32(Inlines.silk_MUL(prev_lag_bias_Q13, delta_lag_log2_sqr_Q7), delta_lag_log2_sqr_Q7 + 64); + CCmax_new_b -= prev_lag_bias_Q13; + } + if (CCmax_new_b <= CCmax_b || CCmax_new <= Inlines.silk_SMULBB(nb_subfr, search_thres2_Q13) || SilkTables.silk_CB_lags_stage2[0][CBimax_new] > 16) continue; + CCmax_b = CCmax_new_b; + CCmax = CCmax_new; + lag = d; + CBimax = CBimax_new; + } + if (lag == -1) { + Arrays.MemSet(pitch_out, 0, nb_subfr); + LTPCorr_Q15.Val = 0; + lagIndex.Val = 0; + contourIndex.Val = 0; + return 1; + } + LTPCorr_Q15.Val = Inlines.silk_LSHIFT(Inlines.silk_DIV32_16(CCmax, nb_subfr), 2); + Inlines.OpusAssert(LTPCorr_Q15.Val >= 0); + if (Fs_kHz > 8) { + short[] input_frame_ptr; + boxed_energy.Val = 0; + boxed_shift.Val = 0; + SumSqrShift.silk_sum_sqr_shift(boxed_energy, boxed_shift, frame, frame_length); + energy = boxed_energy.Val; + shift = boxed_shift.Val; + if (shift > 0) { + short[] scratch_mem = new short[frame_length]; + shift = Inlines.silk_RSHIFT(shift, 1); + for (i = 0; i < frame_length; ++i) { + scratch_mem[i] = Inlines.silk_RSHIFT16(frame[i], shift); + } + input_frame_ptr = scratch_mem; + } else { + input_frame_ptr = frame; + } + int CBimax_old = CBimax; + Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag)); + lag = Fs_kHz == 12 ? Inlines.silk_RSHIFT(Inlines.silk_SMULBB(lag, 3), 1) : (Fs_kHz == 16 ? Inlines.silk_LSHIFT(lag, 1) : Inlines.silk_SMULBB(lag, 3)); + lag = Inlines.silk_LIMIT_int(lag, min_lag, max_lag); + int start_lag = Inlines.silk_max_int(lag - 2, min_lag); + int end_lag = Inlines.silk_min_int(lag + 2, max_lag); + int lag_new = lag; + CBimax = 0; + CCmax = Integer.MIN_VALUE; + for (k = 0; k < nb_subfr; ++k) { + pitch_out[k] = lag + 2 * SilkTables.silk_CB_lags_stage2[k][CBimax_old]; + } + if (nb_subfr == 4) { + nb_cbk_search = SilkTables.silk_nb_cbk_searchs_stage3[complexity]; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3; + } else { + nb_cbk_search = 12; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3_10_ms; + } + silk_pe_stage3_vals[] energies_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search]; + silk_pe_stage3_vals[] cross_corr_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search]; + for (int c = 0; c < nb_subfr * nb_cbk_search; ++c) { + energies_st3[c] = new silk_pe_stage3_vals(); + cross_corr_st3[c] = new silk_pe_stage3_vals(); + } + PitchAnalysisCore.silk_P_Ana_calc_corr_st3(cross_corr_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity); + PitchAnalysisCore.silk_P_Ana_calc_energy_st3(energies_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity); + int lag_counter = 0; + Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag)); + int contour_bias_Q15 = Inlines.silk_DIV32_16(1638, lag); + target = input_frame_ptr; + target_ptr = 20 * Fs_kHz; + energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod_self(target, target_ptr, nb_subfr * sf_length), 1); + for (d = start_lag; d <= end_lag; ++d) { + for (j = 0; j < nb_cbk_search; ++j) { + cross_corr = 0; + energy = energy_target; + for (k = 0; k < nb_subfr; ++k) { + cross_corr = Inlines.silk_ADD32(cross_corr, Inlines.MatrixGet((silk_pe_stage3_vals[])cross_corr_st3, (int)k, (int)j, (int)nb_cbk_search).Values[lag_counter]); + Inlines.OpusAssert((energy = Inlines.silk_ADD32(energy, Inlines.MatrixGet((silk_pe_stage3_vals[])energies_st3, (int)k, (int)j, (int)nb_cbk_search).Values[lag_counter])) >= 0); + } + if (cross_corr > 0) { + CCmax_new = Inlines.silk_DIV32_varQ(cross_corr, energy, 14); + int diff = Short.MAX_VALUE - Inlines.silk_MUL(contour_bias_Q15, j); + Inlines.OpusAssert(diff == Inlines.silk_SAT16(diff)); + CCmax_new = Inlines.silk_SMULWB(CCmax_new, diff); + } else { + CCmax_new = 0; + } + if (CCmax_new <= CCmax || d + SilkTables.silk_CB_lags_stage3[0][j] > max_lag) continue; + CCmax = CCmax_new; + lag_new = d; + CBimax = j; + } + ++lag_counter; + } + for (k = 0; k < nb_subfr; ++k) { + pitch_out[k] = lag_new + Lag_CB_ptr[k][CBimax]; + pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], min_lag, 18 * Fs_kHz); + } + lagIndex.Val = (short)(lag_new - min_lag); + contourIndex.Val = (byte)CBimax; + } else { + for (k = 0; k < nb_subfr; ++k) { + pitch_out[k] = lag + Lag_CB_ptr[k][CBimax]; + pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], 16, 144); + } + lagIndex.Val = (short)(lag - 16); + contourIndex.Val = (byte)CBimax; + } + Inlines.OpusAssert(lagIndex.Val >= 0); + return 0; + } + + private static void silk_P_Ana_calc_corr_st3(silk_pe_stage3_vals[] cross_corr_st3, short[] frame, int start_lag, int sf_length, int nb_subfr, int complexity) { + int nb_cbk_search; + byte[][] Lag_CB_ptr; + byte[][] Lag_range_ptr; + Inlines.OpusAssert(complexity >= 0); + Inlines.OpusAssert(complexity <= 2); + if (nb_subfr == 4) { + Lag_range_ptr = SilkTables.silk_Lag_range_stage3[complexity]; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3; + nb_cbk_search = SilkTables.silk_nb_cbk_searchs_stage3[complexity]; + } else { + Inlines.OpusAssert(nb_subfr == 2); + Lag_range_ptr = SilkTables.silk_Lag_range_stage3_10_ms; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3_10_ms; + nb_cbk_search = 12; + } + int[] scratch_mem = new int[22]; + int[] xcorr32 = new int[22]; + int target_ptr = Inlines.silk_LSHIFT(sf_length, 2); + for (int k = 0; k < nb_subfr; ++k) { + int j; + int lag_counter = 0; + byte lag_high = Lag_range_ptr[k][1]; + int lag_low = Lag_range_ptr[k][0]; + Inlines.OpusAssert(lag_high - lag_low + 1 <= 22); + CeltPitchXCorr.pitch_xcorr(frame, target_ptr, frame, target_ptr - start_lag - lag_high, xcorr32, sf_length, lag_high - lag_low + 1); + for (j = lag_low; j <= lag_high; ++j) { + Inlines.OpusAssert(lag_counter < 22); + scratch_mem[lag_counter] = xcorr32[lag_high - j]; + ++lag_counter; + } + byte delta = Lag_range_ptr[k][0]; + for (int i = 0; i < nb_cbk_search; ++i) { + int idx = Lag_CB_ptr[k][i] - delta; + for (j = 0; j < 5; ++j) { + Inlines.OpusAssert(idx + j < 22); + Inlines.OpusAssert(idx + j < lag_counter); + Inlines.MatrixGet((silk_pe_stage3_vals[])cross_corr_st3, (int)k, (int)i, (int)nb_cbk_search).Values[j] = scratch_mem[idx + j]; + } + } + target_ptr += sf_length; + } + } + + static void silk_P_Ana_calc_energy_st3(silk_pe_stage3_vals[] energies_st3, short[] frame, int start_lag, int sf_length, int nb_subfr, int complexity) { + int nb_cbk_search; + byte[][] Lag_CB_ptr; + byte[][] Lag_range_ptr; + Inlines.OpusAssert(complexity >= 0); + Inlines.OpusAssert(complexity <= 2); + if (nb_subfr == 4) { + Lag_range_ptr = SilkTables.silk_Lag_range_stage3[complexity]; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3; + nb_cbk_search = SilkTables.silk_nb_cbk_searchs_stage3[complexity]; + } else { + Inlines.OpusAssert(nb_subfr == 2); + Lag_range_ptr = SilkTables.silk_Lag_range_stage3_10_ms; + Lag_CB_ptr = SilkTables.silk_CB_lags_stage3_10_ms; + nb_cbk_search = 12; + } + int[] scratch_mem = new int[22]; + int target_ptr = Inlines.silk_LSHIFT(sf_length, 2); + for (int k = 0; k < nb_subfr; ++k) { + int i; + int lag_counter = 0; + int basis_ptr = target_ptr - (start_lag + Lag_range_ptr[k][0]); + int energy = Inlines.silk_inner_prod_self(frame, basis_ptr, sf_length); + Inlines.OpusAssert(energy >= 0); + scratch_mem[lag_counter] = energy; + ++lag_counter; + int lag_diff = Lag_range_ptr[k][1] - Lag_range_ptr[k][0] + 1; + for (i = 1; i < lag_diff; ++i) { + Inlines.OpusAssert((energy -= Inlines.silk_SMULBB(frame[basis_ptr + sf_length - i], frame[basis_ptr + sf_length - i])) >= 0); + energy = Inlines.silk_ADD_SAT32(energy, Inlines.silk_SMULBB(frame[basis_ptr - i], frame[basis_ptr - i])); + Inlines.OpusAssert(energy >= 0); + Inlines.OpusAssert(lag_counter < 22); + scratch_mem[lag_counter] = energy; + ++lag_counter; + } + byte delta = Lag_range_ptr[k][0]; + for (i = 0; i < nb_cbk_search; ++i) { + int idx = Lag_CB_ptr[k][i] - delta; + for (int j = 0; j < 5; ++j) { + Inlines.OpusAssert(idx + j < 22); + Inlines.OpusAssert(idx + j < lag_counter); + Inlines.MatrixGet((silk_pe_stage3_vals[])energies_st3, (int)k, (int)i, (int)nb_cbk_search).Values[j] = scratch_mem[idx + j]; + Inlines.OpusAssert(Inlines.MatrixGet((silk_pe_stage3_vals[])energies_st3, (int)k, (int)i, (int)nb_cbk_search).Values[j] >= 0); + } + } + target_ptr += sf_length; + } + } + + static class silk_pe_stage3_vals { + public final int[] Values = new int[5]; + + silk_pe_stage3_vals() { + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/ProcessGains.java b/source/decompiled/de/maxhenkel/voicechat/concentus/ProcessGains.java new file mode 100644 index 0000000..81f0500 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/ProcessGains.java @@ -0,0 +1,61 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.GainQuantization; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Sigmoid; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkEncoderControl; +import de.maxhenkel.voicechat.concentus.SilkShapeState; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class ProcessGains { + ProcessGains() { + } + + static void silk_process_gains(SilkChannelEncoder psEnc, SilkEncoderControl psEncCtrl, int condCoding) { + int k; + SilkShapeState psShapeSt = psEnc.sShape; + if (psEnc.indices.signalType == 2) { + int s_Q16 = 0 - Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(psEncCtrl.LTPredCodGain_Q7 - 1536, 4)); + for (k = 0; k < psEnc.nb_subfr; ++k) { + psEncCtrl.Gains_Q16[k] = Inlines.silk_SMLAWB(psEncCtrl.Gains_Q16[k], psEncCtrl.Gains_Q16[k], s_Q16); + } + } + int InvMaxSqrVal_Q16 = Inlines.silk_DIV32_16(Inlines.silk_log2lin(Inlines.silk_SMULWB(8894 - psEnc.SNR_dB_Q7, 21627)), psEnc.subfr_length); + for (k = 0; k < psEnc.nb_subfr; ++k) { + int gain; + int ResNrg = psEncCtrl.ResNrg[k]; + int ResNrgPart = Inlines.silk_SMULWW(ResNrg, InvMaxSqrVal_Q16); + ResNrgPart = psEncCtrl.ResNrgQ[k] > 0 ? Inlines.silk_RSHIFT_ROUND(ResNrgPart, psEncCtrl.ResNrgQ[k]) : (ResNrgPart >= Inlines.silk_RSHIFT(Integer.MAX_VALUE, -psEncCtrl.ResNrgQ[k]) ? Integer.MAX_VALUE : Inlines.silk_LSHIFT(ResNrgPart, -psEncCtrl.ResNrgQ[k])); + int gain_squared = Inlines.silk_ADD_SAT32(ResNrgPart, Inlines.silk_SMMUL(gain = psEncCtrl.Gains_Q16[k], gain)); + if (gain_squared < Short.MAX_VALUE) { + gain_squared = Inlines.silk_SMLAWW(Inlines.silk_LSHIFT(ResNrgPart, 16), gain, gain); + Inlines.OpusAssert(gain_squared > 0); + gain = Inlines.silk_SQRT_APPROX(gain_squared); + gain = Inlines.silk_min(gain, 0x7FFFFF); + psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(gain, 8); + continue; + } + gain = Inlines.silk_SQRT_APPROX(gain_squared); + gain = Inlines.silk_min(gain, Short.MAX_VALUE); + psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(gain, 16); + } + System.arraycopy(psEncCtrl.Gains_Q16, 0, psEncCtrl.GainsUnq_Q16, 0, psEnc.nb_subfr); + psEncCtrl.lastGainIndexPrev = psShapeSt.LastGainIndex; + BoxedValueByte boxed_lastGainIndex = new BoxedValueByte(psShapeSt.LastGainIndex); + GainQuantization.silk_gains_quant(psEnc.indices.GainsIndices, psEncCtrl.Gains_Q16, boxed_lastGainIndex, condCoding == 2 ? 1 : 0, psEnc.nb_subfr); + psShapeSt.LastGainIndex = boxed_lastGainIndex.Val; + if (psEnc.indices.signalType == 2) { + psEnc.indices.quantOffsetType = psEncCtrl.LTPredCodGain_Q7 + Inlines.silk_RSHIFT(psEnc.input_tilt_Q15, 8) > 128 ? (byte)0 : 1; + } + short quant_offset_Q10 = SilkTables.silk_Quantization_Offsets_Q10[psEnc.indices.signalType >> 1][psEnc.indices.quantOffsetType]; + psEncCtrl.Lambda_Q10 = 1229 + Inlines.silk_SMULBB(-50, psEnc.nStatesDelayedDecision) + Inlines.silk_SMULWB(-52428, psEnc.speech_activity_Q8) + Inlines.silk_SMULWB(-409, psEncCtrl.input_quality_Q14) + Inlines.silk_SMULWB(-818, psEncCtrl.coding_quality_Q14) + Inlines.silk_SMULWB(52429, quant_offset_Q10); + Inlines.OpusAssert(psEncCtrl.Lambda_Q10 > 0); + Inlines.OpusAssert(psEncCtrl.Lambda_Q10 < 2048); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/PulseCache.java b/source/decompiled/de/maxhenkel/voicechat/concentus/PulseCache.java new file mode 100644 index 0000000..52fff55 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/PulseCache.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class PulseCache { + int size = 0; + short[] index = null; + short[] bits = null; + short[] caps = null; + + PulseCache() { + } + + void Reset() { + this.size = 0; + this.index = null; + this.bits = null; + this.caps = null; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/QuantizeBands.java b/source/decompiled/de/maxhenkel/voicechat/concentus/QuantizeBands.java new file mode 100644 index 0000000..d35030c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/QuantizeBands.java @@ -0,0 +1,303 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltMode; +import de.maxhenkel.voicechat.concentus.CeltTables; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Laplace; + +class QuantizeBands { + private static final int[] pred_coef = new int[]{29440, 26112, 21248, 16384}; + private static final int[] beta_coef = new int[]{30147, 22282, 12124, 6554}; + private static final int beta_intra = 4915; + private static short[] small_energy_icdf = new short[]{2, 1, 0}; + + QuantizeBands() { + } + + static int loss_distortion(int[][] eBands, int[][] oldEBands, int start, int end, int len, int C) { + int dist = 0; + int c = 0; + do { + for (int i = start; i < end; ++i) { + int d = Inlines.SUB16(Inlines.SHR16(eBands[c][i], 3), Inlines.SHR16(oldEBands[c][i], 3)); + dist = Inlines.MAC16_16(dist, d, d); + } + } while (++c < C); + return Inlines.MIN32(200, Inlines.SHR32(dist, 14)); + } + + static int quant_coarse_energy_impl(CeltMode m, int start, int end, int[][] eBands, int[][] oldEBands, int budget, int tell, short[] prob_model, int[][] error, EntropyCoder enc, int C, int LM, int intra, int max_decay, int lfe) { + int beta; + int coef; + int badness = 0; + int[] prev = new int[]{0, 0}; + if (tell + 3 <= budget) { + enc.enc_bit_logp(intra, 3); + } + if (intra != 0) { + coef = 0; + beta = 4915; + } else { + beta = beta_coef[LM]; + coef = pred_coef[LM]; + } + for (int i = start; i < end; ++i) { + int c = 0; + do { + int x = eBands[c][i]; + int oldE = Inlines.MAX16(-9216, oldEBands[c][i]); + int f = Inlines.SHL32(Inlines.EXTEND32(x), 7) - Inlines.PSHR32(Inlines.MULT16_16(coef, oldE), 8) - prev[c]; + int qi = f + 65536 >> 17; + short decay_bound = Inlines.EXTRACT16(Inlines.MAX32(-28672, Inlines.SUB32(oldEBands[c][i], max_decay))); + if (qi < 0 && x < decay_bound && (qi += Inlines.SHR16(Inlines.SUB16((int)decay_bound, x), 10)) > 0) { + qi = 0; + } + int qi0 = qi; + tell = enc.tell(); + int bits_left = budget - tell - 3 * C * (end - i); + if (i != start && bits_left < 30) { + if (bits_left < 24) { + qi = Inlines.IMIN(1, qi); + } + if (bits_left < 16) { + qi = Inlines.IMAX(-1, qi); + } + } + if (lfe != 0 && i >= 2) { + qi = Inlines.IMIN(qi, 0); + } + if (budget - tell >= 15) { + int pi = 2 * Inlines.IMIN(i, 20); + BoxedValueInt boxed_qi = new BoxedValueInt(qi); + Laplace.ec_laplace_encode(enc, boxed_qi, prob_model[pi] << 7, prob_model[pi + 1] << 6); + qi = boxed_qi.Val; + } else if (budget - tell >= 2) { + enc.enc_icdf(2 * qi ^ 0 - ((qi = Inlines.IMAX(-1, Inlines.IMIN(qi, 1))) < 0 ? 1 : 0), small_energy_icdf, 2); + } else if (budget - tell >= 1) { + qi = Inlines.IMIN(0, qi); + enc.enc_bit_logp(-qi, 1); + } else { + qi = -1; + } + error[c][i] = Inlines.PSHR32(f, 7) - Inlines.SHL16(qi, 10); + badness += Inlines.abs(qi0 - qi); + int q = Inlines.SHL32(qi, 10); + int tmp = Inlines.PSHR32(Inlines.MULT16_16(coef, oldE), 8) + prev[c] + Inlines.SHL32(q, 7); + tmp = Inlines.MAX32(-3670016, tmp); + oldEBands[c][i] = Inlines.PSHR32(tmp, 7); + prev[c] = prev[c] + Inlines.SHL32(q, 7) - Inlines.MULT16_16(beta, Inlines.PSHR32(q, 8)); + } while (++c < C); + } + return lfe != 0 ? 0 : badness; + } + + static void quant_coarse_energy(CeltMode m, int start, int end, int effEnd, int[][] eBands, int[][] oldEBands, int budget, int[][] error, EntropyCoder enc, int C, int LM, int nbAvailableBytes, int force_intra, BoxedValueInt delayedIntra, int two_pass, int loss_rate, int lfe) { + EntropyCoder enc_start_state = new EntropyCoder(); + int badness1 = 0; + int intra = force_intra != 0 || two_pass == 0 && delayedIntra.Val > 2 * C * (end - start) && nbAvailableBytes > (end - start) * C ? 1 : 0; + int intra_bias = budget * delayedIntra.Val * loss_rate / (C * 512); + int new_distortion = QuantizeBands.loss_distortion(eBands, oldEBands, start, effEnd, m.nbEBands, C); + int tell = enc.tell(); + if (tell + 3 > budget) { + intra = 0; + two_pass = 0; + } + int max_decay = 16384; + if (end - start > 10) { + max_decay = Inlines.MIN32(max_decay, Inlines.SHL32(nbAvailableBytes, 7)); + } + if (lfe != 0) { + max_decay = 3072; + } + enc_start_state.Assign(enc); + int[][] oldEBands_intra = Arrays.InitTwoDimensionalArrayInt(C, m.nbEBands); + int[][] error_intra = Arrays.InitTwoDimensionalArrayInt(C, m.nbEBands); + System.arraycopy(oldEBands[0], 0, oldEBands_intra[0], 0, m.nbEBands); + if (C == 2) { + System.arraycopy(oldEBands[1], 0, oldEBands_intra[1], 0, m.nbEBands); + } + if (two_pass != 0 || intra != 0) { + badness1 = QuantizeBands.quant_coarse_energy_impl(m, start, end, eBands, oldEBands_intra, budget, tell, CeltTables.e_prob_model[LM][1], error_intra, enc, C, LM, 1, max_decay, lfe); + } + if (intra == 0) { + EntropyCoder enc_intra_state = new EntropyCoder(); + byte[] intra_bits = null; + int tell_intra = enc.tell_frac(); + enc_intra_state.Assign(enc); + int nstart_bytes = enc_start_state.range_bytes(); + int nintra_bytes = enc_intra_state.range_bytes(); + int intra_buf = nstart_bytes; + int save_bytes = nintra_bytes - nstart_bytes; + if (save_bytes != 0) { + intra_bits = new byte[save_bytes]; + System.arraycopy(enc_intra_state.get_buffer(), intra_buf, intra_bits, 0, save_bytes); + } + enc.Assign(enc_start_state); + int badness2 = QuantizeBands.quant_coarse_energy_impl(m, start, end, eBands, oldEBands, budget, tell, CeltTables.e_prob_model[LM][intra], error, enc, C, LM, 0, max_decay, lfe); + if (two_pass != 0 && (badness1 < badness2 || badness1 == badness2 && enc.tell_frac() + intra_bias > tell_intra)) { + enc.Assign(enc_intra_state); + if (intra_bits != null) { + enc_intra_state.write_buffer(intra_bits, 0, intra_buf, nintra_bytes - nstart_bytes); + } + System.arraycopy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands); + System.arraycopy(error_intra[0], 0, error[0], 0, m.nbEBands); + if (C == 2) { + System.arraycopy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands); + System.arraycopy(error_intra[1], 0, error[1], 0, m.nbEBands); + } + intra = 1; + } + } else { + System.arraycopy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands); + System.arraycopy(error_intra[0], 0, error[0], 0, m.nbEBands); + if (C == 2) { + System.arraycopy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands); + System.arraycopy(error_intra[1], 0, error[1], 0, m.nbEBands); + } + } + delayedIntra.Val = intra != 0 ? new_distortion : Inlines.ADD32(Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(pred_coef[LM], pred_coef[LM]), delayedIntra.Val), new_distortion); + } + + static void quant_fine_energy(CeltMode m, int start, int end, int[][] oldEBands, int[][] error, int[] fine_quant, EntropyCoder enc, int C) { + for (int i = start; i < end; ++i) { + int frac = 1 << fine_quant[i]; + if (fine_quant[i] <= 0) continue; + int c = 0; + do { + int q2; + if ((q2 = error[c][i] + 512 >> 10 - fine_quant[i]) > frac - 1) { + q2 = frac - 1; + } + if (q2 < 0) { + q2 = 0; + } + enc.enc_bits(q2, fine_quant[i]); + int offset = Inlines.SUB16(Inlines.SHR32(Inlines.SHL32(q2, 10) + 512, fine_quant[i]), 512); + int[] nArray = oldEBands[c]; + int n = i; + nArray[n] = nArray[n] + offset; + int[] nArray2 = error[c]; + int n2 = i; + nArray2[n2] = nArray2[n2] - offset; + } while (++c < C); + } + } + + static void quant_energy_finalise(CeltMode m, int start, int end, int[][] oldEBands, int[][] error, int[] fine_quant, int[] fine_priority, int bits_left, EntropyCoder enc, int C) { + for (int prio = 0; prio < 2; ++prio) { + for (int i = start; i < end && bits_left >= C; ++i) { + if (fine_quant[i] >= 8 || fine_priority[i] != prio) continue; + int c = 0; + do { + int q2 = error[c][i] < 0 ? 0 : 1; + enc.enc_bits(q2, 1); + int offset = Inlines.SHR16(Inlines.SHL16(q2, 10) - 512, fine_quant[i] + 1); + int[] nArray = oldEBands[c]; + int n = i; + nArray[n] = nArray[n] + offset; + --bits_left; + } while (++c < C); + } + } + } + + static void unquant_coarse_energy(CeltMode m, int start, int end, int[] oldEBands, int intra, EntropyCoder dec, int C, int LM) { + int beta; + int coef; + short[] prob_model = CeltTables.e_prob_model[LM][intra]; + int[] prev = new int[]{0, 0}; + if (intra != 0) { + coef = 0; + beta = 4915; + } else { + beta = beta_coef[LM]; + coef = pred_coef[LM]; + } + int budget = dec.storage * 8; + for (int i = start; i < end; ++i) { + int c = 0; + do { + int qi; + Inlines.OpusAssert(c < 2); + int tell = dec.tell(); + if (budget - tell >= 15) { + int pi = 2 * Inlines.IMIN(i, 20); + qi = Laplace.ec_laplace_decode(dec, prob_model[pi] << 7, prob_model[pi + 1] << 6); + } else if (budget - tell >= 2) { + qi = dec.dec_icdf(small_energy_icdf, 2); + qi = qi >> 1 ^ -(qi & 1); + } else { + qi = budget - tell >= 1 ? 0 - dec.dec_bit_logp(1L) : -1; + } + int q = Inlines.SHL32(qi, 10); + oldEBands[i + c * m.nbEBands] = Inlines.MAX16(-9216, oldEBands[i + c * m.nbEBands]); + int tmp = Inlines.PSHR32(Inlines.MULT16_16(coef, oldEBands[i + c * m.nbEBands]), 8) + prev[c] + Inlines.SHL32(q, 7); + tmp = Inlines.MAX32(-3670016, tmp); + oldEBands[i + c * m.nbEBands] = Inlines.PSHR32(tmp, 7); + prev[c] = prev[c] + Inlines.SHL32(q, 7) - Inlines.MULT16_16(beta, Inlines.PSHR32(q, 8)); + } while (++c < C); + } + } + + static void unquant_fine_energy(CeltMode m, int start, int end, int[] oldEBands, int[] fine_quant, EntropyCoder dec, int C) { + for (int i = start; i < end; ++i) { + if (fine_quant[i] <= 0) continue; + int c = 0; + do { + int q2 = dec.dec_bits(fine_quant[i]); + int offset = Inlines.SUB16(Inlines.SHR32(Inlines.SHL32(q2, 10) + 512, fine_quant[i]), 512); + int n = i + c * m.nbEBands; + oldEBands[n] = oldEBands[n] + offset; + } while (++c < C); + } + } + + static void unquant_energy_finalise(CeltMode m, int start, int end, int[] oldEBands, int[] fine_quant, int[] fine_priority, int bits_left, EntropyCoder dec, int C) { + for (int prio = 0; prio < 2; ++prio) { + for (int i = start; i < end && bits_left >= C; ++i) { + if (fine_quant[i] >= 8 || fine_priority[i] != prio) continue; + int c = 0; + do { + int q2 = dec.dec_bits(1); + int offset = Inlines.SHR16(Inlines.SHL16(q2, 10) - 512, fine_quant[i] + 1); + int n = i + c * m.nbEBands; + oldEBands[n] = oldEBands[n] + offset; + --bits_left; + } while (++c < C); + } + } + } + + static void amp2Log2(CeltMode m, int effEnd, int end, int[][] bandE, int[][] bandLogE, int C) { + int c = 0; + do { + int i; + for (i = 0; i < effEnd; ++i) { + bandLogE[c][i] = Inlines.celt_log2(Inlines.SHL32(bandE[c][i], 2)) - Inlines.SHL16((int)CeltTables.eMeans[i], 6); + } + for (i = effEnd; i < end; ++i) { + bandLogE[c][i] = -14336; + } + } while (++c < C); + } + + static void amp2Log2(CeltMode m, int effEnd, int end, int[] bandE, int[] bandLogE, int bandLogE_ptr, int C) { + int c = 0; + do { + int i; + for (i = 0; i < effEnd; ++i) { + bandLogE[bandLogE_ptr + c * m.nbEBands + i] = Inlines.celt_log2(Inlines.SHL32(bandE[i + c * m.nbEBands], 2)) - Inlines.SHL16((int)CeltTables.eMeans[i], 6); + } + for (i = effEnd; i < end; ++i) { + bandLogE[bandLogE_ptr + c * m.nbEBands + i] = -14336; + } + } while (++c < C); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/QuantizeLTPGains.java b/source/decompiled/de/maxhenkel/voicechat/concentus/QuantizeLTPGains.java new file mode 100644 index 0000000..0c76fd2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/QuantizeLTPGains.java @@ -0,0 +1,64 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkTables; +import de.maxhenkel.voicechat.concentus.VQ_WMat_EC; + +class QuantizeLTPGains { + QuantizeLTPGains() { + } + + static void silk_quant_LTP_gains(short[] B_Q14, byte[] cbk_index, BoxedValueByte periodicity_index, BoxedValueInt sum_log_gain_Q7, int[] W_Q18, int mu_Q9, int lowComplexity, int nb_subfr) { + int j; + byte[][] cbk_ptr_Q7; + int k; + byte[] temp_idx = new byte[4]; + int min_rate_dist_Q14 = Integer.MAX_VALUE; + int best_sum_log_gain_Q7 = 0; + for (k = 0; k < 3; ++k) { + int gain_safety = 51; + short[] cl_ptr_Q5 = SilkTables.silk_LTP_gain_BITS_Q5_ptrs[k]; + cbk_ptr_Q7 = SilkTables.silk_LTP_vq_ptrs_Q7[k]; + short[] cbk_gain_ptr_Q7 = SilkTables.silk_LTP_vq_gain_ptrs_Q7[k]; + byte cbk_size = SilkTables.silk_LTP_vq_sizes[k]; + int W_Q18_ptr = 0; + int b_Q14_ptr = 0; + int rate_dist_Q14 = 0; + int sum_log_gain_tmp_Q7 = sum_log_gain_Q7.Val; + for (j = 0; j < nb_subfr; ++j) { + int max_gain_Q7 = Inlines.silk_log2lin(5333 - sum_log_gain_tmp_Q7 + 896) - gain_safety; + BoxedValueByte temp_idx_box = new BoxedValueByte(temp_idx[j]); + BoxedValueInt rate_dist_Q14_subfr_box = new BoxedValueInt(0); + BoxedValueInt gain_Q7_box = new BoxedValueInt(0); + VQ_WMat_EC.silk_VQ_WMat_EC(temp_idx_box, rate_dist_Q14_subfr_box, gain_Q7_box, B_Q14, b_Q14_ptr, W_Q18, W_Q18_ptr, cbk_ptr_Q7, cbk_gain_ptr_Q7, cl_ptr_Q5, mu_Q9, max_gain_Q7, cbk_size); + int rate_dist_Q14_subfr = rate_dist_Q14_subfr_box.Val; + int gain_Q7 = gain_Q7_box.Val; + temp_idx[j] = temp_idx_box.Val; + rate_dist_Q14 = Inlines.silk_ADD_POS_SAT32(rate_dist_Q14, rate_dist_Q14_subfr); + sum_log_gain_tmp_Q7 = Inlines.silk_max(0, sum_log_gain_tmp_Q7 + Inlines.silk_lin2log(gain_safety + gain_Q7) - 896); + b_Q14_ptr += 5; + W_Q18_ptr += 25; + } + if ((rate_dist_Q14 = Inlines.silk_min(0x7FFFFFFE, rate_dist_Q14)) < min_rate_dist_Q14) { + min_rate_dist_Q14 = rate_dist_Q14; + periodicity_index.Val = (byte)k; + System.arraycopy(temp_idx, 0, cbk_index, 0, nb_subfr); + best_sum_log_gain_Q7 = sum_log_gain_tmp_Q7; + } + if (lowComplexity != 0 && rate_dist_Q14 < 12304) break; + } + cbk_ptr_Q7 = SilkTables.silk_LTP_vq_ptrs_Q7[periodicity_index.Val]; + for (j = 0; j < nb_subfr; ++j) { + for (k = 0; k < 5; ++k) { + B_Q14[j * 5 + k] = (short)Inlines.silk_LSHIFT(cbk_ptr_Q7[cbk_index[j]][k], 7); + } + } + sum_log_gain_Q7.Val = best_sum_log_gain_Q7; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Rate.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Rate.java new file mode 100644 index 0000000..ff2327d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Rate.java @@ -0,0 +1,308 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CeltMode; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; + +class Rate { + private static final byte[] LOG2_FRAC_TABLE = new byte[]{0, 8, 13, 16, 19, 21, 23, 24, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 34, 35, 36, 36, 37, 37}; + private static final int ALLOC_STEPS = 6; + + Rate() { + } + + static int get_pulses(int i) { + return i < 8 ? i : 8 + (i & 7) << (i >> 3) - 1; + } + + static int bits2pulses(CeltMode m, int band, int LM, int bits) { + short[] cache = m.cache.bits; + short cache_ptr = m.cache.index[++LM * m.nbEBands + band]; + int lo = 0; + int hi = cache[cache_ptr]; + --bits; + for (int i = 0; i < 6; ++i) { + int mid = lo + hi + 1 >> 1; + if (cache[cache_ptr + mid] >= bits) { + hi = mid; + continue; + } + lo = mid; + } + if (bits - (lo == 0 ? -1 : cache[cache_ptr + lo]) <= cache[cache_ptr + hi] - bits) { + return lo; + } + return hi; + } + + static int pulses2bits(CeltMode m, int band, int LM, int pulses) { + return pulses == 0 ? 0 : m.cache.bits[m.cache.index[++LM * m.nbEBands + band] + pulses] + 1; + } + + static int interp_bits2pulses(CeltMode m, int start, int end, int skip_start, int[] bits1, int[] bits2, int[] thresh, int[] cap, int total, BoxedValueInt _balance, int skip_rsv, BoxedValueInt intensity, int intensity_rsv, BoxedValueInt dual_stereo, int dual_stereo_rsv, int[] bits, int[] ebits, int[] fine_priority, int C, int LM, EntropyCoder ec, int encode, int prev, int signalBandwidth) { + int percoeff; + int left; + int tmp; + int j; + boolean done; + int psum; + int codedBands = -1; + int alloc_floor = C << 3; + int stereo = C > 1 ? 1 : 0; + int logM = LM << 3; + int lo = 0; + int hi = 64; + for (int i = 0; i < 6; ++i) { + int mid = lo + hi >> 1; + psum = 0; + done = false; + j = end; + while (j-- > start) { + int tmp2 = bits1[j] + (mid * bits2[j] >> 6); + if (tmp2 >= thresh[j] || done) { + done = true; + psum += Inlines.IMIN(tmp2, cap[j]); + continue; + } + if (tmp2 < alloc_floor) continue; + psum += alloc_floor; + } + if (psum > total) { + hi = mid; + continue; + } + lo = mid; + } + psum = 0; + done = false; + j = end; + while (j-- > start) { + tmp = bits1[j] + (lo * bits2[j] >> 6); + if (tmp < thresh[j] && !done) { + tmp = tmp >= alloc_floor ? alloc_floor : 0; + } else { + done = true; + } + bits[j] = tmp = Inlines.IMIN(tmp, cap[j]); + psum += tmp; + } + codedBands = end; + while (true) { + int rem; + int band_width; + if ((j = codedBands - 1) <= skip_start) { + total += skip_rsv; + break; + } + left = total - psum; + percoeff = Inlines.celt_udiv(left, m.eBands[codedBands] - m.eBands[start]); + int band_bits = bits[j] + percoeff * (band_width = m.eBands[codedBands] - m.eBands[j]) + (rem = Inlines.IMAX((left -= (m.eBands[codedBands] - m.eBands[start]) * percoeff) - (m.eBands[j] - m.eBands[start]), 0)); + if (band_bits >= Inlines.IMAX(thresh[j], alloc_floor + 8)) { + if (encode != 0) { + if (codedBands <= start + 2 || band_bits > (j < prev ? 7 : 9) * band_width << LM << 3 >> 4 && j <= signalBandwidth) { + ec.enc_bit_logp(1, 1); + break; + } + ec.enc_bit_logp(0, 1); + } else if (ec.dec_bit_logp(1L) != 0) break; + psum += 8; + band_bits -= 8; + } + psum -= bits[j] + intensity_rsv; + if (intensity_rsv > 0) { + intensity_rsv = LOG2_FRAC_TABLE[j - start]; + } + psum += intensity_rsv; + if (band_bits >= alloc_floor) { + psum += alloc_floor; + bits[j] = alloc_floor; + } else { + bits[j] = 0; + } + --codedBands; + } + Inlines.OpusAssert(codedBands > start); + if (intensity_rsv > 0) { + if (encode != 0) { + intensity.Val = Inlines.IMIN(intensity.Val, codedBands); + ec.enc_uint(intensity.Val - start, codedBands + 1 - start); + } else { + intensity.Val = start + (int)ec.dec_uint(codedBands + 1 - start); + } + } else { + intensity.Val = 0; + } + if (intensity.Val <= start) { + total += dual_stereo_rsv; + dual_stereo_rsv = 0; + } + if (dual_stereo_rsv > 0) { + if (encode != 0) { + ec.enc_bit_logp(dual_stereo.Val, 1); + } else { + dual_stereo.Val = ec.dec_bit_logp(1L); + } + } else { + dual_stereo.Val = 0; + } + left = total - psum; + percoeff = Inlines.celt_udiv(left, m.eBands[codedBands] - m.eBands[start]); + left -= (m.eBands[codedBands] - m.eBands[start]) * percoeff; + for (j = start; j < codedBands; ++j) { + int n = j; + bits[n] = bits[n] + percoeff * (m.eBands[j + 1] - m.eBands[j]); + } + j = start; + while (j < codedBands) { + tmp = Inlines.IMIN(left, m.eBands[j + 1] - m.eBands[j]); + int n = j++; + bits[n] = bits[n] + tmp; + left -= tmp; + } + int balance = 0; + for (j = start; j < codedBands; ++j) { + int excess; + Inlines.OpusAssert(bits[j] >= 0); + int N0 = m.eBands[j + 1] - m.eBands[j]; + int N = N0 << LM; + int bit = bits[j] + balance; + if (N > 1) { + excess = Inlines.MAX32(bit - cap[j], 0); + bits[j] = bit - excess; + int den = C * N + (C == 2 && N > 2 && dual_stereo.Val == 0 && j < intensity.Val ? 1 : 0); + int NClogN = den * (m.logN[j] + logM); + int offset = (NClogN >> 1) - den * 21; + if (N == 2) { + offset += den << 3 >> 2; + } + if (bits[j] + offset < den * 2 << 3) { + offset += NClogN >> 2; + } else if (bits[j] + offset < den * 3 << 3) { + offset += NClogN >> 3; + } + ebits[j] = Inlines.IMAX(0, bits[j] + offset + (den << 2)); + ebits[j] = Inlines.celt_udiv(ebits[j], den) >> 3; + if (C * ebits[j] > bits[j] >> 3) { + ebits[j] = bits[j] >> stereo >> 3; + } + ebits[j] = Inlines.IMIN(ebits[j], 8); + fine_priority[j] = ebits[j] * (den << 3) >= bits[j] + offset ? 1 : 0; + int n = j; + bits[n] = bits[n] - (C * ebits[j] << 3); + } else { + excess = Inlines.MAX32(0, bit - (C << 3)); + bits[j] = bit - excess; + ebits[j] = 0; + fine_priority[j] = 1; + } + if (excess > 0) { + int extra_fine = Inlines.IMIN(excess >> stereo + 3, 8 - ebits[j]); + int n = j; + ebits[n] = ebits[n] + extra_fine; + int extra_bits = extra_fine * C << 3; + fine_priority[j] = extra_bits >= excess - balance ? 1 : 0; + excess -= extra_bits; + } + balance = excess; + Inlines.OpusAssert(bits[j] >= 0); + Inlines.OpusAssert(ebits[j] >= 0); + } + _balance.Val = balance; + while (j < end) { + ebits[j] = bits[j] >> stereo >> 3; + Inlines.OpusAssert(C * ebits[j] << 3 == bits[j]); + bits[j] = 0; + fine_priority[j] = ebits[j] < 1 ? 1 : 0; + ++j; + } + return codedBands; + } + + static int compute_allocation(CeltMode m, int start, int end, int[] offsets, int[] cap, int alloc_trim, BoxedValueInt intensity, BoxedValueInt dual_stereo, int total, BoxedValueInt balance, int[] pulses, int[] ebits, int[] fine_priority, int C, int LM, EntropyCoder ec, int encode, int prev, int signalBandwidth) { + int j; + total = Inlines.IMAX(total, 0); + int len = m.nbEBands; + int skip_start = start; + int skip_rsv = total >= 8 ? 8 : 0; + total -= skip_rsv; + int dual_stereo_rsv = 0; + int intensity_rsv = 0; + if (C == 2) { + intensity_rsv = LOG2_FRAC_TABLE[end - start]; + if (intensity_rsv > total) { + intensity_rsv = 0; + } else { + dual_stereo_rsv = (total -= intensity_rsv) >= 8 ? 8 : 0; + total -= dual_stereo_rsv; + } + } + int[] bits1 = new int[len]; + int[] bits2 = new int[len]; + int[] thresh = new int[len]; + int[] trim_offset = new int[len]; + for (j = start; j < end; ++j) { + thresh[j] = Inlines.IMAX(C << 3, 3 * (m.eBands[j + 1] - m.eBands[j]) << LM << 3 >> 4); + trim_offset[j] = C * (m.eBands[j + 1] - m.eBands[j]) * (alloc_trim - 5 - LM) * (end - j - 1) * (1 << LM + 3) >> 6; + if (m.eBands[j + 1] - m.eBands[j] << LM != 1) continue; + int n = j; + trim_offset[n] = trim_offset[n] - (C << 3); + } + int lo = 1; + int hi = m.nbAllocVectors - 1; + do { + boolean done = false; + int psum = 0; + int mid = lo + hi >> 1; + j = end; + while (j-- > start) { + int N = m.eBands[j + 1] - m.eBands[j]; + int bitsj = C * N * m.allocVectors[mid * len + j] << LM >> 2; + if (bitsj > 0) { + bitsj = Inlines.IMAX(0, bitsj + trim_offset[j]); + } + if ((bitsj += offsets[j]) >= thresh[j] || done) { + done = true; + psum += Inlines.IMIN(bitsj, cap[j]); + continue; + } + if (bitsj < C << 3) continue; + psum += C << 3; + } + if (psum > total) { + hi = mid - 1; + continue; + } + lo = mid + 1; + } while (lo <= hi); + hi = lo--; + for (j = start; j < end; ++j) { + int bits2j; + int N = m.eBands[j + 1] - m.eBands[j]; + int bits1j = C * N * m.allocVectors[lo * len + j] << LM >> 2; + int n = bits2j = hi >= m.nbAllocVectors ? cap[j] : C * N * m.allocVectors[hi * len + j] << LM >> 2; + if (bits1j > 0) { + bits1j = Inlines.IMAX(0, bits1j + trim_offset[j]); + } + if (bits2j > 0) { + bits2j = Inlines.IMAX(0, bits2j + trim_offset[j]); + } + if (lo > 0) { + bits1j += offsets[j]; + } + bits2j += offsets[j]; + if (offsets[j] > 0) { + skip_start = j; + } + bits2j = Inlines.IMAX(0, bits2j - bits1j); + bits1[j] = bits1j; + bits2[j] = bits2j; + } + int codedBands = Rate.interp_bits2pulses(m, start, end, skip_start, bits1, bits2, thresh, cap, total, balance, skip_rsv, intensity, intensity_rsv, dual_stereo, dual_stereo_rsv, pulses, ebits, fine_priority, C, LM, ec, encode, prev, signalBandwidth); + return codedBands; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/RegularizeCorrelations.java b/source/decompiled/de/maxhenkel/voicechat/concentus/RegularizeCorrelations.java new file mode 100644 index 0000000..b67b303 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/RegularizeCorrelations.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; + +class RegularizeCorrelations { + RegularizeCorrelations() { + } + + static void silk_regularize_correlations(int[] XX, int XX_ptr, int[] xx, int xx_ptr, int noise, int D) { + for (int i = 0; i < D; ++i) { + Inlines.MatrixSet(XX, XX_ptr, i, i, D, Inlines.silk_ADD32(Inlines.MatrixGet(XX, XX_ptr, i, i, D), noise)); + } + int n = xx_ptr; + xx[n] = xx[n] + noise; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Resampler.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Resampler.java new file mode 100644 index 0000000..beb6dca --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Resampler.java @@ -0,0 +1,375 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkError; +import de.maxhenkel.voicechat.concentus.SilkResamplerState; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class Resampler { + private static final int USE_silk_resampler_copy = 0; + private static final int USE_silk_resampler_private_up2_HQ_wrapper = 1; + private static final int USE_silk_resampler_private_IIR_FIR = 2; + private static final int USE_silk_resampler_private_down_FIR = 3; + private static final int ORDER_FIR = 4; + + Resampler() { + } + + private static int rateID(int R) { + return ((R >> 12) - (R > 16000 ? 1 : 0) >> (R > 24000 ? 1 : 0)) - 1; + } + + /* + * Enabled aggressive block sorting + */ + static int silk_resampler_init(SilkResamplerState S, int Fs_Hz_in, int Fs_Hz_out, int forEnc) { + int up2x; + block13: { + block15: { + block18: { + block17: { + block16: { + block14: { + S.Reset(); + if (forEnc != 0) { + if (Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000 && Fs_Hz_in != 24000 && Fs_Hz_in != 48000 || Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000) { + Inlines.OpusAssert(false); + return -1; + } + S.inputDelay = SilkTables.delay_matrix_enc[Resampler.rateID(Fs_Hz_in)][Resampler.rateID(Fs_Hz_out)]; + } else { + if (Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000 || Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000 && Fs_Hz_out != 24000 && Fs_Hz_out != 48000) { + Inlines.OpusAssert(false); + return -1; + } + S.inputDelay = SilkTables.delay_matrix_dec[Resampler.rateID(Fs_Hz_in)][Resampler.rateID(Fs_Hz_out)]; + } + S.Fs_in_kHz = Inlines.silk_DIV32_16(Fs_Hz_in, 1000); + S.Fs_out_kHz = Inlines.silk_DIV32_16(Fs_Hz_out, 1000); + S.batchSize = S.Fs_in_kHz * 10; + up2x = 0; + if (Fs_Hz_out <= Fs_Hz_in) break block14; + if (Fs_Hz_out == Inlines.silk_MUL(Fs_Hz_in, 2)) { + S.resampler_function = 1; + break block13; + } else { + S.resampler_function = 2; + up2x = 1; + } + break block13; + } + if (Fs_Hz_out >= Fs_Hz_in) break block15; + S.resampler_function = 3; + if (Inlines.silk_MUL(Fs_Hz_out, 4) != Inlines.silk_MUL(Fs_Hz_in, 3)) break block16; + S.FIR_Fracs = 3; + S.FIR_Order = 18; + S.Coefs = SilkTables.silk_Resampler_3_4_COEFS; + break block13; + } + if (Inlines.silk_MUL(Fs_Hz_out, 3) != Inlines.silk_MUL(Fs_Hz_in, 2)) break block17; + S.FIR_Fracs = 2; + S.FIR_Order = 18; + S.Coefs = SilkTables.silk_Resampler_2_3_COEFS; + break block13; + } + if (Inlines.silk_MUL(Fs_Hz_out, 2) != Fs_Hz_in) break block18; + S.FIR_Fracs = 1; + S.FIR_Order = 24; + S.Coefs = SilkTables.silk_Resampler_1_2_COEFS; + break block13; + } + if (Inlines.silk_MUL(Fs_Hz_out, 3) == Fs_Hz_in) { + S.FIR_Fracs = 1; + S.FIR_Order = 36; + S.Coefs = SilkTables.silk_Resampler_1_3_COEFS; + break block13; + } else if (Inlines.silk_MUL(Fs_Hz_out, 4) == Fs_Hz_in) { + S.FIR_Fracs = 1; + S.FIR_Order = 36; + S.Coefs = SilkTables.silk_Resampler_1_4_COEFS; + break block13; + } else { + if (Inlines.silk_MUL(Fs_Hz_out, 6) != Fs_Hz_in) { + Inlines.OpusAssert(false); + return -1; + } + S.FIR_Fracs = 1; + S.FIR_Order = 36; + S.Coefs = SilkTables.silk_Resampler_1_6_COEFS; + } + break block13; + } + S.resampler_function = 0; + } + S.invRatio_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_DIV32(Inlines.silk_LSHIFT32(Fs_Hz_in, 14 + up2x), Fs_Hz_out), 2); + while (Inlines.silk_SMULWW(S.invRatio_Q16, Fs_Hz_out) < Inlines.silk_LSHIFT32(Fs_Hz_in, up2x)) { + ++S.invRatio_Q16; + } + return 0; + } + + static int silk_resampler(SilkResamplerState S, short[] output, int output_ptr, short[] input, int input_ptr, int inLen) { + Inlines.OpusAssert(inLen >= S.Fs_in_kHz); + Inlines.OpusAssert(S.inputDelay <= S.Fs_in_kHz); + int nSamples = S.Fs_in_kHz - S.inputDelay; + short[] delayBufPtr = S.delayBuf; + System.arraycopy(input, input_ptr, delayBufPtr, S.inputDelay, nSamples); + switch (S.resampler_function) { + case 1: { + Resampler.silk_resampler_private_up2_HQ(S.sIIR, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz); + Resampler.silk_resampler_private_up2_HQ(S.sIIR, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz); + break; + } + case 2: { + Resampler.silk_resampler_private_IIR_FIR(S, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz); + Resampler.silk_resampler_private_IIR_FIR(S, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz); + break; + } + case 3: { + Resampler.silk_resampler_private_down_FIR(S, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz); + Resampler.silk_resampler_private_down_FIR(S, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz); + break; + } + default: { + System.arraycopy(delayBufPtr, 0, output, output_ptr, S.Fs_in_kHz); + System.arraycopy(input, input_ptr + nSamples, output, output_ptr + S.Fs_out_kHz, inLen - S.Fs_in_kHz); + } + } + System.arraycopy(input, input_ptr + inLen - S.inputDelay, delayBufPtr, 0, S.inputDelay); + return SilkError.SILK_NO_ERROR; + } + + static void silk_resampler_down2(int[] S, short[] output, short[] input, int inLen) { + int len2 = Inlines.silk_RSHIFT32(inLen, 1); + Inlines.OpusAssert(true); + Inlines.OpusAssert(true); + for (int k = 0; k < len2; ++k) { + int in32 = Inlines.silk_LSHIFT(input[2 * k], 10); + int Y = Inlines.silk_SUB32(in32, S[0]); + int X = Inlines.silk_SMLAWB(Y, Y, -25727); + int out32 = Inlines.silk_ADD32(S[0], X); + S[0] = Inlines.silk_ADD32(in32, X); + in32 = Inlines.silk_LSHIFT(input[2 * k + 1], 10); + Y = Inlines.silk_SUB32(in32, S[1]); + X = Inlines.silk_SMULWB(Y, 9872); + out32 = Inlines.silk_ADD32(out32, S[1]); + out32 = Inlines.silk_ADD32(out32, X); + S[1] = Inlines.silk_ADD32(in32, X); + output[k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32, 11)); + } + } + + static void silk_resampler_down2_3(int[] S, short[] output, short[] input, int inLen) { + int nSamplesIn; + int[] buf = new int[484]; + int input_ptr = 0; + int output_ptr = 0; + System.arraycopy(S, 0, buf, 0, 4); + while (true) { + nSamplesIn = Inlines.silk_min(inLen, 480); + Resampler.silk_resampler_private_AR2(S, 4, buf, 4, input, input_ptr, SilkTables.silk_Resampler_2_3_COEFS_LQ, nSamplesIn); + int buf_ptr = 0; + for (int counter = nSamplesIn; counter > 2; counter -= 3) { + int res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr], SilkTables.silk_Resampler_2_3_COEFS_LQ[2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 1], SilkTables.silk_Resampler_2_3_COEFS_LQ[3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], SilkTables.silk_Resampler_2_3_COEFS_LQ[5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], SilkTables.silk_Resampler_2_3_COEFS_LQ[4]); + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr + 1], SilkTables.silk_Resampler_2_3_COEFS_LQ[4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], SilkTables.silk_Resampler_2_3_COEFS_LQ[5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], SilkTables.silk_Resampler_2_3_COEFS_LQ[3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 4], SilkTables.silk_Resampler_2_3_COEFS_LQ[2]); + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + buf_ptr += 3; + } + input_ptr += nSamplesIn; + if ((inLen -= nSamplesIn) <= 0) break; + System.arraycopy(buf, nSamplesIn, buf, 0, 4); + } + System.arraycopy(buf, nSamplesIn, S, 0, 4); + } + + static void silk_resampler_private_AR2(int[] S, int S_ptr, int[] out_Q8, int out_Q8_ptr, short[] input, int input_ptr, short[] A_Q14, int len) { + for (int k = 0; k < len; ++k) { + int out32; + out_Q8[out_Q8_ptr + k] = out32 = Inlines.silk_ADD_LSHIFT32(S[S_ptr], input[input_ptr + k], 8); + out32 = Inlines.silk_LSHIFT(out32, 2); + S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr + 1], out32, A_Q14[0]); + S[S_ptr + 1] = Inlines.silk_SMULWB(out32, A_Q14[1]); + } + } + + static int silk_resampler_private_down_FIR_INTERPOL(short[] output, int output_ptr, int[] buf, short[] FIR_Coefs, int FIR_Coefs_ptr, int FIR_Order, int FIR_Fracs, int max_index_Q16, int index_increment_Q16) { + switch (FIR_Order) { + case 18: { + for (int index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) { + int buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16); + int interpol_ind = Inlines.silk_SMULWB(index_Q16 & 0xFFFF, FIR_Fracs); + int interpol_ptr = FIR_Coefs_ptr + 9 * interpol_ind; + int res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr + 0], FIR_Coefs[interpol_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 1], FIR_Coefs[interpol_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], FIR_Coefs[interpol_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], FIR_Coefs[interpol_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 4], FIR_Coefs[interpol_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 5], FIR_Coefs[interpol_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 6], FIR_Coefs[interpol_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 7], FIR_Coefs[interpol_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 8], FIR_Coefs[interpol_ptr + 8]); + interpol_ptr = FIR_Coefs_ptr + 9 * (FIR_Fracs - 1 - interpol_ind); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 17], FIR_Coefs[interpol_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 16], FIR_Coefs[interpol_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 15], FIR_Coefs[interpol_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 14], FIR_Coefs[interpol_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 13], FIR_Coefs[interpol_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 12], FIR_Coefs[interpol_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 11], FIR_Coefs[interpol_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 10], FIR_Coefs[interpol_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 9], FIR_Coefs[interpol_ptr + 8]); + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + } + break; + } + case 24: { + for (int index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) { + int buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16); + int res_Q6 = Inlines.silk_SMULWB(Inlines.silk_ADD32(buf[buf_ptr + 0], buf[buf_ptr + 23]), FIR_Coefs[FIR_Coefs_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 1], buf[buf_ptr + 22]), FIR_Coefs[FIR_Coefs_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 2], buf[buf_ptr + 21]), FIR_Coefs[FIR_Coefs_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 3], buf[buf_ptr + 20]), FIR_Coefs[FIR_Coefs_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 4], buf[buf_ptr + 19]), FIR_Coefs[FIR_Coefs_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 5], buf[buf_ptr + 18]), FIR_Coefs[FIR_Coefs_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 6], buf[buf_ptr + 17]), FIR_Coefs[FIR_Coefs_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 7], buf[buf_ptr + 16]), FIR_Coefs[FIR_Coefs_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 8], buf[buf_ptr + 15]), FIR_Coefs[FIR_Coefs_ptr + 8]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 9], buf[buf_ptr + 14]), FIR_Coefs[FIR_Coefs_ptr + 9]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 10], buf[buf_ptr + 13]), FIR_Coefs[FIR_Coefs_ptr + 10]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 11], buf[buf_ptr + 12]), FIR_Coefs[FIR_Coefs_ptr + 11]); + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + } + break; + } + case 36: { + for (int index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) { + int buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16); + int res_Q6 = Inlines.silk_SMULWB(Inlines.silk_ADD32(buf[buf_ptr + 0], buf[buf_ptr + 35]), FIR_Coefs[FIR_Coefs_ptr + 0]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 1], buf[buf_ptr + 34]), FIR_Coefs[FIR_Coefs_ptr + 1]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 2], buf[buf_ptr + 33]), FIR_Coefs[FIR_Coefs_ptr + 2]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 3], buf[buf_ptr + 32]), FIR_Coefs[FIR_Coefs_ptr + 3]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 4], buf[buf_ptr + 31]), FIR_Coefs[FIR_Coefs_ptr + 4]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 5], buf[buf_ptr + 30]), FIR_Coefs[FIR_Coefs_ptr + 5]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 6], buf[buf_ptr + 29]), FIR_Coefs[FIR_Coefs_ptr + 6]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 7], buf[buf_ptr + 28]), FIR_Coefs[FIR_Coefs_ptr + 7]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 8], buf[buf_ptr + 27]), FIR_Coefs[FIR_Coefs_ptr + 8]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 9], buf[buf_ptr + 26]), FIR_Coefs[FIR_Coefs_ptr + 9]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 10], buf[buf_ptr + 25]), FIR_Coefs[FIR_Coefs_ptr + 10]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 11], buf[buf_ptr + 24]), FIR_Coefs[FIR_Coefs_ptr + 11]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 12], buf[buf_ptr + 23]), FIR_Coefs[FIR_Coefs_ptr + 12]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 13], buf[buf_ptr + 22]), FIR_Coefs[FIR_Coefs_ptr + 13]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 14], buf[buf_ptr + 21]), FIR_Coefs[FIR_Coefs_ptr + 14]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 15], buf[buf_ptr + 20]), FIR_Coefs[FIR_Coefs_ptr + 15]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 16], buf[buf_ptr + 19]), FIR_Coefs[FIR_Coefs_ptr + 16]); + res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 17], buf[buf_ptr + 18]), FIR_Coefs[FIR_Coefs_ptr + 17]); + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6)); + } + break; + } + default: { + Inlines.OpusAssert(false); + } + } + return output_ptr; + } + + static void silk_resampler_private_down_FIR(SilkResamplerState S, short[] output, int output_ptr, short[] input, int input_ptr, int inLen) { + int nSamplesIn; + int[] buf = new int[S.batchSize + S.FIR_Order]; + System.arraycopy(S.sFIR_i32, 0, buf, 0, S.FIR_Order); + int index_increment_Q16 = S.invRatio_Q16; + while (true) { + nSamplesIn = Inlines.silk_min(inLen, S.batchSize); + Resampler.silk_resampler_private_AR2(S.sIIR, 0, buf, S.FIR_Order, input, input_ptr, S.Coefs, nSamplesIn); + int max_index_Q16 = Inlines.silk_LSHIFT32(nSamplesIn, 16); + output_ptr = Resampler.silk_resampler_private_down_FIR_INTERPOL(output, output_ptr, buf, S.Coefs, 2, S.FIR_Order, S.FIR_Fracs, max_index_Q16, index_increment_Q16); + input_ptr += nSamplesIn; + if ((inLen -= nSamplesIn) <= 1) break; + System.arraycopy(buf, nSamplesIn, buf, 0, S.FIR_Order); + } + System.arraycopy(buf, nSamplesIn, S.sFIR_i32, 0, S.FIR_Order); + } + + static int silk_resampler_private_IIR_FIR_INTERPOL(short[] output, int output_ptr, short[] buf, int max_index_Q16, int index_increment_Q16) { + for (int index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16) { + int table_index = Inlines.silk_SMULWB(index_Q16 & 0xFFFF, 12); + int buf_ptr = index_Q16 >> 16; + int res_Q15 = Inlines.silk_SMULBB(buf[buf_ptr], SilkTables.silk_resampler_frac_FIR_12[table_index][0]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 1], SilkTables.silk_resampler_frac_FIR_12[table_index][1]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 2], SilkTables.silk_resampler_frac_FIR_12[table_index][2]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 3], SilkTables.silk_resampler_frac_FIR_12[table_index][3]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 4], SilkTables.silk_resampler_frac_FIR_12[11 - table_index][3]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 5], SilkTables.silk_resampler_frac_FIR_12[11 - table_index][2]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 6], SilkTables.silk_resampler_frac_FIR_12[11 - table_index][1]); + res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 7], SilkTables.silk_resampler_frac_FIR_12[11 - table_index][0]); + output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q15, 15)); + } + return output_ptr; + } + + static void silk_resampler_private_IIR_FIR(SilkResamplerState S, short[] output, int output_ptr, short[] input, int input_ptr, int inLen) { + int nSamplesIn; + short[] buf = new short[2 * S.batchSize + 8]; + System.arraycopy(S.sFIR_i16, 0, buf, 0, 8); + int index_increment_Q16 = S.invRatio_Q16; + while (true) { + nSamplesIn = Inlines.silk_min(inLen, S.batchSize); + Resampler.silk_resampler_private_up2_HQ(S.sIIR, buf, 8, input, input_ptr, nSamplesIn); + int max_index_Q16 = Inlines.silk_LSHIFT32(nSamplesIn, 17); + output_ptr = Resampler.silk_resampler_private_IIR_FIR_INTERPOL(output, output_ptr, buf, max_index_Q16, index_increment_Q16); + input_ptr += nSamplesIn; + if ((inLen -= nSamplesIn) <= 0) break; + System.arraycopy(buf, nSamplesIn << 1, buf, 0, 8); + } + System.arraycopy(buf, nSamplesIn << 1, S.sFIR_i16, 0, 8); + } + + static void silk_resampler_private_up2_HQ(int[] S, short[] output, int output_ptr, short[] input, int input_ptr, int len) { + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_0[0] > 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_0[1] > 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_0[2] < 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_1[0] > 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_1[1] > 0); + Inlines.OpusAssert(SilkTables.silk_resampler_up2_hq_1[2] < 0); + for (int k = 0; k < len; ++k) { + int in32 = Inlines.silk_LSHIFT(input[input_ptr + k], 10); + int Y = Inlines.silk_SUB32(in32, S[0]); + int X = Inlines.silk_SMULWB(Y, SilkTables.silk_resampler_up2_hq_0[0]); + int out32_1 = Inlines.silk_ADD32(S[0], X); + S[0] = Inlines.silk_ADD32(in32, X); + Y = Inlines.silk_SUB32(out32_1, S[1]); + X = Inlines.silk_SMULWB(Y, SilkTables.silk_resampler_up2_hq_0[1]); + int out32_2 = Inlines.silk_ADD32(S[1], X); + S[1] = Inlines.silk_ADD32(out32_1, X); + Y = Inlines.silk_SUB32(out32_2, S[2]); + X = Inlines.silk_SMLAWB(Y, Y, SilkTables.silk_resampler_up2_hq_0[2]); + out32_1 = Inlines.silk_ADD32(S[2], X); + S[2] = Inlines.silk_ADD32(out32_2, X); + output[output_ptr + 2 * k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32_1, 10)); + Y = Inlines.silk_SUB32(in32, S[3]); + X = Inlines.silk_SMULWB(Y, SilkTables.silk_resampler_up2_hq_1[0]); + out32_1 = Inlines.silk_ADD32(S[3], X); + S[3] = Inlines.silk_ADD32(in32, X); + Y = Inlines.silk_SUB32(out32_1, S[4]); + X = Inlines.silk_SMULWB(Y, SilkTables.silk_resampler_up2_hq_1[1]); + out32_2 = Inlines.silk_ADD32(S[4], X); + S[4] = Inlines.silk_ADD32(out32_1, X); + Y = Inlines.silk_SUB32(out32_2, S[5]); + X = Inlines.silk_SMLAWB(Y, Y, SilkTables.silk_resampler_up2_hq_1[2]); + out32_1 = Inlines.silk_ADD32(S[5], X); + S[5] = Inlines.silk_ADD32(out32_2, X); + output[output_ptr + 2 * k + 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32_1, 10)); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/ResidualEnergy.java b/source/decompiled/de/maxhenkel/voicechat/concentus/ResidualEnergy.java new file mode 100644 index 0000000..459a5f2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/ResidualEnergy.java @@ -0,0 +1,87 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SumSqrShift; + +class ResidualEnergy { + ResidualEnergy() { + } + + static void silk_residual_energy(int[] nrgs, int[] nrgsQ, short[] x, short[][] a_Q12, int[] gains, int subfr_length, int nb_subfr, int LPC_order) { + int i; + BoxedValueInt rshift = new BoxedValueInt(0); + BoxedValueInt energy = new BoxedValueInt(0); + int x_ptr = 0; + int offset = LPC_order + subfr_length; + short[] LPC_res = new short[2 * offset]; + Inlines.OpusAssert((nb_subfr >> 1) * 2 == nb_subfr); + for (i = 0; i < nb_subfr >> 1; ++i) { + Filters.silk_LPC_analysis_filter(LPC_res, 0, x, x_ptr, a_Q12[i], 0, 2 * offset, LPC_order); + int LPC_res_ptr = LPC_order; + for (int j = 0; j < 2; ++j) { + SumSqrShift.silk_sum_sqr_shift(energy, rshift, LPC_res, LPC_res_ptr, subfr_length); + nrgs[i * 2 + j] = energy.Val; + nrgsQ[i * 2 + j] = 0 - rshift.Val; + LPC_res_ptr += offset; + } + x_ptr += 2 * offset; + } + i = 0; + while (i < nb_subfr) { + int lz1 = Inlines.silk_CLZ32(nrgs[i]) - 1; + int lz2 = Inlines.silk_CLZ32(gains[i]) - 1; + int tmp32 = Inlines.silk_LSHIFT32(gains[i], lz2); + tmp32 = Inlines.silk_SMMUL(tmp32, tmp32); + nrgs[i] = Inlines.silk_SMMUL(tmp32, Inlines.silk_LSHIFT32(nrgs[i], lz1)); + int n = i++; + nrgsQ[n] = nrgsQ[n] + (lz1 + 2 * lz2 - 32 - 32); + } + } + + static int silk_residual_energy16_covar(short[] c, int c_ptr, int[] wXX, int wXX_ptr, int[] wXx, int wxx, int D, int cQ) { + int i; + int lshifts; + int[] cn = new int[D]; + Inlines.OpusAssert(D >= 0); + Inlines.OpusAssert(D <= 16); + Inlines.OpusAssert(cQ > 0); + Inlines.OpusAssert(cQ < 16); + int Qxtra = lshifts = 16 - cQ; + int c_max = 0; + for (i = c_ptr; i < c_ptr + D; ++i) { + c_max = Inlines.silk_max_32(c_max, Inlines.silk_abs(c[i])); + } + Qxtra = Inlines.silk_min_int(Qxtra, Inlines.silk_CLZ32(c_max) - 17); + int w_max = Inlines.silk_max_32(wXX[wXX_ptr], wXX[wXX_ptr + D * D - 1]); + Qxtra = Inlines.silk_min_int(Qxtra, Inlines.silk_CLZ32(Inlines.silk_MUL(D, Inlines.silk_RSHIFT(Inlines.silk_SMULWB(w_max, c_max), 4))) - 5); + Qxtra = Inlines.silk_max_int(Qxtra, 0); + for (i = 0; i < D; ++i) { + cn[i] = Inlines.silk_LSHIFT(c[c_ptr + i], Qxtra); + Inlines.OpusAssert(Inlines.silk_abs(cn[i]) <= 32768); + } + lshifts -= Qxtra; + int tmp = 0; + for (i = 0; i < D; ++i) { + tmp = Inlines.silk_SMLAWB(tmp, wXx[i], cn[i]); + } + int nrg = Inlines.silk_RSHIFT(wxx, 1 + lshifts) - tmp; + int tmp2 = 0; + for (i = 0; i < D; ++i) { + tmp = 0; + int pRow = wXX_ptr + i * D; + for (int j = i + 1; j < D; ++j) { + tmp = Inlines.silk_SMLAWB(tmp, wXX[pRow + j], cn[j]); + } + tmp = Inlines.silk_SMLAWB(tmp, Inlines.silk_RSHIFT(wXX[pRow + i], 1), cn[i]); + tmp2 = Inlines.silk_SMLAWB(tmp2, tmp, cn[i]); + } + nrg = (nrg = Inlines.silk_ADD_LSHIFT32(nrg, tmp2, lshifts)) < 1 ? 1 : (nrg > Inlines.silk_RSHIFT(Integer.MAX_VALUE, lshifts + 2) ? 0x3FFFFFFF : Inlines.silk_LSHIFT(nrg, lshifts + 1)); + return nrg; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Schur.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Schur.java new file mode 100644 index 0000000..a7135cd --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Schur.java @@ -0,0 +1,96 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.Inlines; + +class Schur { + Schur() { + } + + static int silk_schur(short[] rc_Q15, int[] c, int order) { + int k; + int[][] C = Arrays.InitTwoDimensionalArrayInt(17, 2); + Inlines.OpusAssert(order == 6 || order == 8 || order == 10 || order == 12 || order == 14 || order == 16); + int lz = Inlines.silk_CLZ32(c[0]); + if (lz < 2) { + for (k = 0; k < order + 1; ++k) { + int n = Inlines.silk_RSHIFT(c[k], 1); + C[k][1] = n; + C[k][0] = n; + } + } else if (lz > 2) { + lz -= 2; + for (k = 0; k < order + 1; ++k) { + int n = Inlines.silk_LSHIFT(c[k], lz); + C[k][1] = n; + C[k][0] = n; + } + } else { + for (k = 0; k < order + 1; ++k) { + int n = c[k]; + C[k][1] = n; + C[k][0] = n; + } + } + for (k = 0; k < order; ++k) { + if (Inlines.silk_abs_int32(C[k + 1][0]) >= C[0][1]) { + rc_Q15[k] = C[k + 1][0] > 0 ? -32440 : 32440; + ++k; + break; + } + int rc_tmp_Q15 = 0 - Inlines.silk_DIV32_16(C[k + 1][0], Inlines.silk_max_32(Inlines.silk_RSHIFT(C[0][1], 15), 1)); + rc_tmp_Q15 = Inlines.silk_SAT16(rc_tmp_Q15); + rc_Q15[k] = (short)rc_tmp_Q15; + for (int n = 0; n < order - k; ++n) { + int Ctmp1 = C[n + k + 1][0]; + int Ctmp2 = C[n][1]; + C[n + k + 1][0] = Inlines.silk_SMLAWB(Ctmp1, Inlines.silk_LSHIFT(Ctmp2, 1), rc_tmp_Q15); + C[n][1] = Inlines.silk_SMLAWB(Ctmp2, Inlines.silk_LSHIFT(Ctmp1, 1), rc_tmp_Q15); + } + } + while (k < order) { + rc_Q15[k] = 0; + ++k; + } + return Inlines.silk_max_32(1, C[0][1]); + } + + static int silk_schur64(int[] rc_Q16, int[] c, int order) { + int k; + int[][] C = Arrays.InitTwoDimensionalArrayInt(17, 2); + Inlines.OpusAssert(order == 6 || order == 8 || order == 10 || order == 12 || order == 14 || order == 16); + if (c[0] <= 0) { + Arrays.MemSet(rc_Q16, 0, order); + return 0; + } + for (k = 0; k < order + 1; ++k) { + int n = c[k]; + C[k][1] = n; + C[k][0] = n; + } + for (k = 0; k < order; ++k) { + if (Inlines.silk_abs_int32(C[k + 1][0]) >= C[0][1]) { + rc_Q16[k] = C[k + 1][0] > 0 ? -64881 : 64881; + ++k; + break; + } + int rc_tmp_Q31 = Inlines.silk_DIV32_varQ(-C[k + 1][0], C[0][1], 31); + rc_Q16[k] = Inlines.silk_RSHIFT_ROUND(rc_tmp_Q31, 15); + for (int n = 0; n < order - k; ++n) { + int Ctmp1_Q30 = C[n + k + 1][0]; + int Ctmp2_Q30 = C[n][1]; + C[n + k + 1][0] = Ctmp1_Q30 + Inlines.silk_SMMUL(Inlines.silk_LSHIFT(Ctmp2_Q30, 1), rc_tmp_Q31); + C[n][1] = Ctmp2_Q30 + Inlines.silk_SMMUL(Inlines.silk_LSHIFT(Ctmp1_Q30, 1), rc_tmp_Q31); + } + } + while (k < order) { + rc_Q16[k] = 0; + ++k; + } + return Inlines.silk_max_32(1, C[0][1]); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/ShellCoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/ShellCoder.java new file mode 100644 index 0000000..9c92bed --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/ShellCoder.java @@ -0,0 +1,91 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class ShellCoder { + ShellCoder() { + } + + static void combine_pulses(int[] output, int[] input, int input_ptr, int len) { + for (int k = 0; k < len; ++k) { + output[k] = input[input_ptr + 2 * k] + input[input_ptr + 2 * k + 1]; + } + } + + static void combine_pulses(int[] output, int[] input, int len) { + for (int k = 0; k < len; ++k) { + output[k] = input[2 * k] + input[2 * k + 1]; + } + } + + static void encode_split(EntropyCoder psRangeEnc, int p_child1, int p, short[] shell_table) { + if (p > 0) { + psRangeEnc.enc_icdf(p_child1, shell_table, SilkTables.silk_shell_code_table_offsets[p], 8); + } + } + + static void decode_split(short[] p_child1, int child1_ptr, short[] p_child2, int p_child2_ptr, EntropyCoder psRangeDec, int p, short[] shell_table) { + if (p > 0) { + p_child1[child1_ptr] = (short)psRangeDec.dec_icdf(shell_table, SilkTables.silk_shell_code_table_offsets[p], 8); + p_child2[p_child2_ptr] = (short)(p - p_child1[child1_ptr]); + } else { + p_child1[child1_ptr] = 0; + p_child2[p_child2_ptr] = 0; + } + } + + static void silk_shell_encoder(EntropyCoder psRangeEnc, int[] pulses0, int pulses0_ptr) { + int[] pulses1 = new int[8]; + int[] pulses2 = new int[4]; + int[] pulses3 = new int[2]; + int[] pulses4 = new int[1]; + Inlines.OpusAssert(true); + ShellCoder.combine_pulses(pulses1, pulses0, pulses0_ptr, 8); + ShellCoder.combine_pulses(pulses2, pulses1, 4); + ShellCoder.combine_pulses(pulses3, pulses2, 2); + ShellCoder.combine_pulses(pulses4, pulses3, 1); + ShellCoder.encode_split(psRangeEnc, pulses3[0], pulses4[0], SilkTables.silk_shell_code_table3); + ShellCoder.encode_split(psRangeEnc, pulses2[0], pulses3[0], SilkTables.silk_shell_code_table2); + ShellCoder.encode_split(psRangeEnc, pulses1[0], pulses2[0], SilkTables.silk_shell_code_table1); + ShellCoder.encode_split(psRangeEnc, pulses0[pulses0_ptr], pulses1[0], SilkTables.silk_shell_code_table0); + ShellCoder.encode_split(psRangeEnc, pulses0[pulses0_ptr + 2], pulses1[1], SilkTables.silk_shell_code_table0); + ShellCoder.encode_split(psRangeEnc, pulses1[2], pulses2[1], SilkTables.silk_shell_code_table1); + ShellCoder.encode_split(psRangeEnc, pulses0[pulses0_ptr + 4], pulses1[2], SilkTables.silk_shell_code_table0); + ShellCoder.encode_split(psRangeEnc, pulses0[pulses0_ptr + 6], pulses1[3], SilkTables.silk_shell_code_table0); + ShellCoder.encode_split(psRangeEnc, pulses2[2], pulses3[1], SilkTables.silk_shell_code_table2); + ShellCoder.encode_split(psRangeEnc, pulses1[4], pulses2[2], SilkTables.silk_shell_code_table1); + ShellCoder.encode_split(psRangeEnc, pulses0[pulses0_ptr + 8], pulses1[4], SilkTables.silk_shell_code_table0); + ShellCoder.encode_split(psRangeEnc, pulses0[pulses0_ptr + 10], pulses1[5], SilkTables.silk_shell_code_table0); + ShellCoder.encode_split(psRangeEnc, pulses1[6], pulses2[3], SilkTables.silk_shell_code_table1); + ShellCoder.encode_split(psRangeEnc, pulses0[pulses0_ptr + 12], pulses1[6], SilkTables.silk_shell_code_table0); + ShellCoder.encode_split(psRangeEnc, pulses0[pulses0_ptr + 14], pulses1[7], SilkTables.silk_shell_code_table0); + } + + static void silk_shell_decoder(short[] pulses0, int pulses0_ptr, EntropyCoder psRangeDec, int pulses4) { + short[] pulses1 = new short[8]; + short[] pulses2 = new short[4]; + short[] pulses3 = new short[2]; + Inlines.OpusAssert(true); + ShellCoder.decode_split(pulses3, 0, pulses3, 1, psRangeDec, pulses4, SilkTables.silk_shell_code_table3); + ShellCoder.decode_split(pulses2, 0, pulses2, 1, psRangeDec, pulses3[0], SilkTables.silk_shell_code_table2); + ShellCoder.decode_split(pulses1, 0, pulses1, 1, psRangeDec, pulses2[0], SilkTables.silk_shell_code_table1); + ShellCoder.decode_split(pulses0, pulses0_ptr, pulses0, pulses0_ptr + 1, psRangeDec, pulses1[0], SilkTables.silk_shell_code_table0); + ShellCoder.decode_split(pulses0, pulses0_ptr + 2, pulses0, pulses0_ptr + 3, psRangeDec, pulses1[1], SilkTables.silk_shell_code_table0); + ShellCoder.decode_split(pulses1, 2, pulses1, 3, psRangeDec, pulses2[1], SilkTables.silk_shell_code_table1); + ShellCoder.decode_split(pulses0, pulses0_ptr + 4, pulses0, pulses0_ptr + 5, psRangeDec, pulses1[2], SilkTables.silk_shell_code_table0); + ShellCoder.decode_split(pulses0, pulses0_ptr + 6, pulses0, pulses0_ptr + 7, psRangeDec, pulses1[3], SilkTables.silk_shell_code_table0); + ShellCoder.decode_split(pulses2, 2, pulses2, 3, psRangeDec, pulses3[1], SilkTables.silk_shell_code_table2); + ShellCoder.decode_split(pulses1, 4, pulses1, 5, psRangeDec, pulses2[2], SilkTables.silk_shell_code_table1); + ShellCoder.decode_split(pulses0, pulses0_ptr + 8, pulses0, pulses0_ptr + 9, psRangeDec, pulses1[4], SilkTables.silk_shell_code_table0); + ShellCoder.decode_split(pulses0, pulses0_ptr + 10, pulses0, pulses0_ptr + 11, psRangeDec, pulses1[5], SilkTables.silk_shell_code_table0); + ShellCoder.decode_split(pulses1, 6, pulses1, 7, psRangeDec, pulses2[3], SilkTables.silk_shell_code_table1); + ShellCoder.decode_split(pulses0, pulses0_ptr + 12, pulses0, pulses0_ptr + 13, psRangeDec, pulses1[6], SilkTables.silk_shell_code_table0); + ShellCoder.decode_split(pulses0, pulses0_ptr + 14, pulses0, pulses0_ptr + 15, psRangeDec, pulses1[7], SilkTables.silk_shell_code_table0); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SideInfoIndices.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SideInfoIndices.java new file mode 100644 index 0000000..0549f79 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SideInfoIndices.java @@ -0,0 +1,52 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class SideInfoIndices { + final byte[] GainsIndices = new byte[4]; + final byte[] LTPIndex = new byte[4]; + final byte[] NLSFIndices = new byte[17]; + short lagIndex = 0; + byte contourIndex = 0; + byte signalType = 0; + byte quantOffsetType = 0; + byte NLSFInterpCoef_Q2 = 0; + byte PERIndex = 0; + byte LTP_scaleIndex = 0; + byte Seed = 0; + + SideInfoIndices() { + } + + void Reset() { + Arrays.MemSet(this.GainsIndices, (byte)0, 4); + Arrays.MemSet(this.LTPIndex, (byte)0, 4); + Arrays.MemSet(this.NLSFIndices, (byte)0, 17); + this.lagIndex = 0; + this.contourIndex = 0; + this.signalType = 0; + this.quantOffsetType = 0; + this.NLSFInterpCoef_Q2 = 0; + this.PERIndex = 0; + this.LTP_scaleIndex = 0; + this.Seed = 0; + } + + void Assign(SideInfoIndices other) { + System.arraycopy(other.GainsIndices, 0, this.GainsIndices, 0, 4); + System.arraycopy(other.LTPIndex, 0, this.LTPIndex, 0, 4); + System.arraycopy(other.NLSFIndices, 0, this.NLSFIndices, 0, 17); + this.lagIndex = other.lagIndex; + this.contourIndex = other.contourIndex; + this.signalType = other.signalType; + this.quantOffsetType = other.quantOffsetType; + this.NLSFInterpCoef_Q2 = other.NLSFInterpCoef_Q2; + this.PERIndex = other.PERIndex; + this.LTP_scaleIndex = other.LTP_scaleIndex; + this.Seed = other.Seed; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Sigmoid.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Sigmoid.java new file mode 100644 index 0000000..3aa0ad2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Sigmoid.java @@ -0,0 +1,31 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; + +class Sigmoid { + private static final int[] sigm_LUT_slope_Q10 = new int[]{237, 153, 73, 30, 12, 7}; + private static final int[] sigm_LUT_pos_Q15 = new int[]{16384, 23955, 28861, 31213, 32178, 32548}; + private static final int[] sigm_LUT_neg_Q15 = new int[]{16384, 8812, 3906, 1554, 589, 219}; + + Sigmoid() { + } + + static int silk_sigm_Q15(int in_Q5) { + if (in_Q5 < 0) { + if ((in_Q5 = -in_Q5) >= 192) { + return 0; + } + int ind = Inlines.silk_RSHIFT(in_Q5, 5); + return sigm_LUT_neg_Q15[ind] - Inlines.silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F); + } + if (in_Q5 >= 192) { + return Short.MAX_VALUE; + } + int ind = Inlines.silk_RSHIFT(in_Q5, 5); + return sigm_LUT_pos_Q15[ind] + Inlines.silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkChannelDecoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkChannelDecoder.java new file mode 100644 index 0000000..05b0ce3 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkChannelDecoder.java @@ -0,0 +1,197 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.CNG; +import de.maxhenkel.voicechat.concentus.CNGState; +import de.maxhenkel.voicechat.concentus.DecodeCore; +import de.maxhenkel.voicechat.concentus.DecodeIndices; +import de.maxhenkel.voicechat.concentus.DecodeParameters; +import de.maxhenkel.voicechat.concentus.DecodePulses; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.NLSFCodebook; +import de.maxhenkel.voicechat.concentus.PLC; +import de.maxhenkel.voicechat.concentus.PLCStruct; +import de.maxhenkel.voicechat.concentus.Resampler; +import de.maxhenkel.voicechat.concentus.SideInfoIndices; +import de.maxhenkel.voicechat.concentus.SilkDecoderControl; +import de.maxhenkel.voicechat.concentus.SilkResamplerState; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class SilkChannelDecoder { + int prev_gain_Q16 = 0; + final int[] exc_Q14 = new int[320]; + final int[] sLPC_Q14_buf = new int[16]; + final short[] outBuf = new short[480]; + int lagPrev = 0; + byte LastGainIndex = 0; + int fs_kHz = 0; + int fs_API_hz = 0; + int nb_subfr = 0; + int frame_length = 0; + int subfr_length = 0; + int ltp_mem_length = 0; + int LPC_order = 0; + final short[] prevNLSF_Q15 = new short[16]; + int first_frame_after_reset = 0; + short[] pitch_lag_low_bits_iCDF; + short[] pitch_contour_iCDF; + int nFramesDecoded = 0; + int nFramesPerPacket = 0; + int ec_prevSignalType = 0; + short ec_prevLagIndex = 0; + final int[] VAD_flags = new int[3]; + int LBRR_flag = 0; + final int[] LBRR_flags = new int[3]; + final SilkResamplerState resampler_state = new SilkResamplerState(); + NLSFCodebook psNLSF_CB = null; + final SideInfoIndices indices = new SideInfoIndices(); + final CNGState sCNG = new CNGState(); + int lossCnt = 0; + int prevSignalType = 0; + final PLCStruct sPLC = new PLCStruct(); + + SilkChannelDecoder() { + } + + void Reset() { + this.prev_gain_Q16 = 0; + Arrays.MemSet(this.exc_Q14, 0, 320); + Arrays.MemSet(this.sLPC_Q14_buf, 0, 16); + Arrays.MemSet(this.outBuf, (short)0, 480); + this.lagPrev = 0; + this.LastGainIndex = 0; + this.fs_kHz = 0; + this.fs_API_hz = 0; + this.nb_subfr = 0; + this.frame_length = 0; + this.subfr_length = 0; + this.ltp_mem_length = 0; + this.LPC_order = 0; + Arrays.MemSet(this.prevNLSF_Q15, (short)0, 16); + this.first_frame_after_reset = 0; + this.pitch_lag_low_bits_iCDF = null; + this.pitch_contour_iCDF = null; + this.nFramesDecoded = 0; + this.nFramesPerPacket = 0; + this.ec_prevSignalType = 0; + this.ec_prevLagIndex = 0; + Arrays.MemSet(this.VAD_flags, 0, 3); + this.LBRR_flag = 0; + Arrays.MemSet(this.LBRR_flags, 0, 3); + this.resampler_state.Reset(); + this.psNLSF_CB = null; + this.indices.Reset(); + this.sCNG.Reset(); + this.lossCnt = 0; + this.prevSignalType = 0; + this.sPLC.Reset(); + } + + int silk_init_decoder() { + this.Reset(); + this.first_frame_after_reset = 1; + this.prev_gain_Q16 = 65536; + this.silk_CNG_Reset(); + this.silk_PLC_Reset(); + return 0; + } + + private void silk_CNG_Reset() { + int NLSF_step_Q15 = Inlines.silk_DIV32_16(Short.MAX_VALUE, this.LPC_order + 1); + int NLSF_acc_Q15 = 0; + for (int i = 0; i < this.LPC_order; ++i) { + this.sCNG.CNG_smth_NLSF_Q15[i] = (short)(NLSF_acc_Q15 += NLSF_step_Q15); + } + this.sCNG.CNG_smth_Gain_Q16 = 0; + this.sCNG.rand_seed = 3176576; + } + + private void silk_PLC_Reset() { + this.sPLC.pitchL_Q8 = Inlines.silk_LSHIFT(this.frame_length, 7); + this.sPLC.prevGain_Q16[0] = 65536; + this.sPLC.prevGain_Q16[1] = 65536; + this.sPLC.subfr_length = 20; + this.sPLC.nb_subfr = 2; + } + + int silk_decoder_set_fs(int fs_kHz, int fs_API_Hz) { + int ret = 0; + Inlines.OpusAssert(fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16); + Inlines.OpusAssert(this.nb_subfr == 4 || this.nb_subfr == 2); + this.subfr_length = Inlines.silk_SMULBB(5, fs_kHz); + int frame_length = Inlines.silk_SMULBB(this.nb_subfr, this.subfr_length); + if (this.fs_kHz != fs_kHz || this.fs_API_hz != fs_API_Hz) { + ret += Resampler.silk_resampler_init(this.resampler_state, Inlines.silk_SMULBB(fs_kHz, 1000), fs_API_Hz, 0); + this.fs_API_hz = fs_API_Hz; + } + if (this.fs_kHz != fs_kHz || frame_length != this.frame_length) { + this.pitch_contour_iCDF = fs_kHz == 8 ? (this.nb_subfr == 4 ? SilkTables.silk_pitch_contour_NB_iCDF : SilkTables.silk_pitch_contour_10_ms_NB_iCDF) : (this.nb_subfr == 4 ? SilkTables.silk_pitch_contour_iCDF : SilkTables.silk_pitch_contour_10_ms_iCDF); + if (this.fs_kHz != fs_kHz) { + this.ltp_mem_length = Inlines.silk_SMULBB(20, fs_kHz); + if (fs_kHz == 8 || fs_kHz == 12) { + this.LPC_order = 10; + this.psNLSF_CB = SilkTables.silk_NLSF_CB_NB_MB; + } else { + this.LPC_order = 16; + this.psNLSF_CB = SilkTables.silk_NLSF_CB_WB; + } + if (fs_kHz == 16) { + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform8_iCDF; + } else if (fs_kHz == 12) { + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform6_iCDF; + } else if (fs_kHz == 8) { + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform4_iCDF; + } else { + Inlines.OpusAssert(false); + } + this.first_frame_after_reset = 1; + this.lagPrev = 100; + this.LastGainIndex = (byte)10; + this.prevSignalType = 0; + Arrays.MemSet(this.outBuf, (short)0, 480); + Arrays.MemSet(this.sLPC_Q14_buf, 0, 16); + } + this.fs_kHz = fs_kHz; + this.frame_length = frame_length; + } + Inlines.OpusAssert(this.frame_length > 0 && this.frame_length <= 320); + return ret; + } + + int silk_decode_frame(EntropyCoder psRangeDec, short[] pOut, int pOut_ptr, BoxedValueInt pN, int lostFlag, int condCoding) { + SilkDecoderControl thisCtrl = new SilkDecoderControl(); + int ret = 0; + int L = this.frame_length; + thisCtrl.LTP_scale_Q14 = 0; + Inlines.OpusAssert(L > 0 && L <= 320); + if (lostFlag == 0 || lostFlag == 2 && this.LBRR_flags[this.nFramesDecoded] == 1) { + short[] pulses = new short[L + 16 - 1 & 0xFFFFFFF0]; + DecodeIndices.silk_decode_indices(this, psRangeDec, this.nFramesDecoded, lostFlag, condCoding); + DecodePulses.silk_decode_pulses(psRangeDec, pulses, this.indices.signalType, this.indices.quantOffsetType, this.frame_length); + DecodeParameters.silk_decode_parameters(this, thisCtrl, condCoding); + DecodeCore.silk_decode_core(this, thisCtrl, pOut, pOut_ptr, pulses); + PLC.silk_PLC(this, thisCtrl, pOut, pOut_ptr, 0); + this.lossCnt = 0; + this.prevSignalType = this.indices.signalType; + Inlines.OpusAssert(this.prevSignalType >= 0 && this.prevSignalType <= 2); + this.first_frame_after_reset = 0; + } else { + PLC.silk_PLC(this, thisCtrl, pOut, pOut_ptr, 1); + } + Inlines.OpusAssert(this.ltp_mem_length >= this.frame_length); + int mv_len = this.ltp_mem_length - this.frame_length; + Arrays.MemMove(this.outBuf, this.frame_length, 0, mv_len); + System.arraycopy(pOut, pOut_ptr, this.outBuf, mv_len, this.frame_length); + CNG.silk_CNG(this, thisCtrl, pOut, pOut_ptr, L); + PLC.silk_PLC_glue_frames(this, pOut, pOut_ptr, L); + this.lagPrev = thisCtrl.pitchL[this.nb_subfr - 1]; + pN.Val = L; + return ret; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkChannelEncoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkChannelEncoder.java new file mode 100644 index 0000000..166084a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkChannelEncoder.java @@ -0,0 +1,671 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.EncControlState; +import de.maxhenkel.voicechat.concentus.EncodeIndices; +import de.maxhenkel.voicechat.concentus.EncodePulses; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.FindPitchLags; +import de.maxhenkel.voicechat.concentus.FindPredCoefs; +import de.maxhenkel.voicechat.concentus.GainQuantization; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.NLSFCodebook; +import de.maxhenkel.voicechat.concentus.NoiseShapeAnalysis; +import de.maxhenkel.voicechat.concentus.ProcessGains; +import de.maxhenkel.voicechat.concentus.Resampler; +import de.maxhenkel.voicechat.concentus.SideInfoIndices; +import de.maxhenkel.voicechat.concentus.SilkEncoderControl; +import de.maxhenkel.voicechat.concentus.SilkError; +import de.maxhenkel.voicechat.concentus.SilkLPState; +import de.maxhenkel.voicechat.concentus.SilkNSQState; +import de.maxhenkel.voicechat.concentus.SilkPrefilterState; +import de.maxhenkel.voicechat.concentus.SilkResamplerState; +import de.maxhenkel.voicechat.concentus.SilkShapeState; +import de.maxhenkel.voicechat.concentus.SilkTables; +import de.maxhenkel.voicechat.concentus.SilkVADState; +import de.maxhenkel.voicechat.concentus.VoiceActivityDetection; + +class SilkChannelEncoder { + final int[] In_HP_State = new int[2]; + int variable_HP_smth1_Q15 = 0; + int variable_HP_smth2_Q15 = 0; + final SilkLPState sLP = new SilkLPState(); + final SilkVADState sVAD = new SilkVADState(); + final SilkNSQState sNSQ = new SilkNSQState(); + final short[] prev_NLSFq_Q15 = new short[16]; + int speech_activity_Q8 = 0; + int allow_bandwidth_switch = 0; + byte LBRRprevLastGainIndex = 0; + byte prevSignalType = 0; + int prevLag = 0; + int pitch_LPC_win_length = 0; + int max_pitch_lag = 0; + int API_fs_Hz = 0; + int prev_API_fs_Hz = 0; + int maxInternal_fs_Hz = 0; + int minInternal_fs_Hz = 0; + int desiredInternal_fs_Hz = 0; + int fs_kHz = 0; + int nb_subfr = 0; + int frame_length = 0; + int subfr_length = 0; + int ltp_mem_length = 0; + int la_pitch = 0; + int la_shape = 0; + int shapeWinLength = 0; + int TargetRate_bps = 0; + int PacketSize_ms = 0; + int PacketLoss_perc = 0; + int frameCounter = 0; + int Complexity = 0; + int nStatesDelayedDecision = 0; + int useInterpolatedNLSFs = 0; + int shapingLPCOrder = 0; + int predictLPCOrder = 0; + int pitchEstimationComplexity = 0; + int pitchEstimationLPCOrder = 0; + int pitchEstimationThreshold_Q16 = 0; + int LTPQuantLowComplexity = 0; + int mu_LTP_Q9 = 0; + int sum_log_gain_Q7 = 0; + int NLSF_MSVQ_Survivors = 0; + int first_frame_after_reset = 0; + int controlled_since_last_payload = 0; + int warping_Q16 = 0; + int useCBR = 0; + int prefillFlag = 0; + short[] pitch_lag_low_bits_iCDF = null; + short[] pitch_contour_iCDF = null; + NLSFCodebook psNLSF_CB = null; + final int[] input_quality_bands_Q15 = new int[4]; + int input_tilt_Q15 = 0; + int SNR_dB_Q7 = 0; + final byte[] VAD_flags = new byte[3]; + byte LBRR_flag = 0; + final int[] LBRR_flags = new int[3]; + final SideInfoIndices indices = new SideInfoIndices(); + final byte[] pulses = new byte[320]; + final short[] inputBuf = new short[322]; + int inputBufIx = 0; + int nFramesPerPacket = 0; + int nFramesEncoded = 0; + int nChannelsAPI = 0; + int nChannelsInternal = 0; + int channelNb = 0; + int frames_since_onset = 0; + int ec_prevSignalType = 0; + short ec_prevLagIndex = 0; + final SilkResamplerState resampler_state = new SilkResamplerState(); + int useDTX = 0; + int inDTX = 0; + int noSpeechCounter = 0; + int useInBandFEC = 0; + int LBRR_enabled = 0; + int LBRR_GainIncreases = 0; + final SideInfoIndices[] indices_LBRR = new SideInfoIndices[3]; + final byte[][] pulses_LBRR = Arrays.InitTwoDimensionalArrayByte(3, 320); + final SilkShapeState sShape = new SilkShapeState(); + final SilkPrefilterState sPrefilt = new SilkPrefilterState(); + final short[] x_buf = new short[720]; + int LTPCorr_Q15 = 0; + + SilkChannelEncoder() { + for (int c = 0; c < 3; ++c) { + this.indices_LBRR[c] = new SideInfoIndices(); + } + } + + void Reset() { + Arrays.MemSet(this.In_HP_State, 0, 2); + this.variable_HP_smth1_Q15 = 0; + this.variable_HP_smth2_Q15 = 0; + this.sLP.Reset(); + this.sVAD.Reset(); + this.sNSQ.Reset(); + Arrays.MemSet(this.prev_NLSFq_Q15, (short)0, 16); + this.speech_activity_Q8 = 0; + this.allow_bandwidth_switch = 0; + this.LBRRprevLastGainIndex = 0; + this.prevSignalType = 0; + this.prevLag = 0; + this.pitch_LPC_win_length = 0; + this.max_pitch_lag = 0; + this.API_fs_Hz = 0; + this.prev_API_fs_Hz = 0; + this.maxInternal_fs_Hz = 0; + this.minInternal_fs_Hz = 0; + this.desiredInternal_fs_Hz = 0; + this.fs_kHz = 0; + this.nb_subfr = 0; + this.frame_length = 0; + this.subfr_length = 0; + this.ltp_mem_length = 0; + this.la_pitch = 0; + this.la_shape = 0; + this.shapeWinLength = 0; + this.TargetRate_bps = 0; + this.PacketSize_ms = 0; + this.PacketLoss_perc = 0; + this.frameCounter = 0; + this.Complexity = 0; + this.nStatesDelayedDecision = 0; + this.useInterpolatedNLSFs = 0; + this.shapingLPCOrder = 0; + this.predictLPCOrder = 0; + this.pitchEstimationComplexity = 0; + this.pitchEstimationLPCOrder = 0; + this.pitchEstimationThreshold_Q16 = 0; + this.LTPQuantLowComplexity = 0; + this.mu_LTP_Q9 = 0; + this.sum_log_gain_Q7 = 0; + this.NLSF_MSVQ_Survivors = 0; + this.first_frame_after_reset = 0; + this.controlled_since_last_payload = 0; + this.warping_Q16 = 0; + this.useCBR = 0; + this.prefillFlag = 0; + this.pitch_lag_low_bits_iCDF = null; + this.pitch_contour_iCDF = null; + this.psNLSF_CB = null; + Arrays.MemSet(this.input_quality_bands_Q15, 0, 4); + this.input_tilt_Q15 = 0; + this.SNR_dB_Q7 = 0; + Arrays.MemSet(this.VAD_flags, (byte)0, 3); + this.LBRR_flag = 0; + Arrays.MemSet(this.LBRR_flags, 0, 3); + this.indices.Reset(); + Arrays.MemSet(this.pulses, (byte)0, 320); + Arrays.MemSet(this.inputBuf, (short)0, 322); + this.inputBufIx = 0; + this.nFramesPerPacket = 0; + this.nFramesEncoded = 0; + this.nChannelsAPI = 0; + this.nChannelsInternal = 0; + this.channelNb = 0; + this.frames_since_onset = 0; + this.ec_prevSignalType = 0; + this.ec_prevLagIndex = 0; + this.resampler_state.Reset(); + this.useDTX = 0; + this.inDTX = 0; + this.noSpeechCounter = 0; + this.useInBandFEC = 0; + this.LBRR_enabled = 0; + this.LBRR_GainIncreases = 0; + for (int c = 0; c < 3; ++c) { + this.indices_LBRR[c].Reset(); + Arrays.MemSet(this.pulses_LBRR[c], (byte)0, 320); + } + this.sShape.Reset(); + this.sPrefilt.Reset(); + Arrays.MemSet(this.x_buf, (short)0, 720); + this.LTPCorr_Q15 = 0; + } + + int silk_control_encoder(EncControlState encControl, int TargetRate_bps, int allow_bw_switch, int channelNb, int force_fs_kHz) { + int ret = SilkError.SILK_NO_ERROR; + this.useDTX = encControl.useDTX; + this.useCBR = encControl.useCBR; + this.API_fs_Hz = encControl.API_sampleRate; + this.maxInternal_fs_Hz = encControl.maxInternalSampleRate; + this.minInternal_fs_Hz = encControl.minInternalSampleRate; + this.desiredInternal_fs_Hz = encControl.desiredInternalSampleRate; + this.useInBandFEC = encControl.useInBandFEC; + this.nChannelsAPI = encControl.nChannelsAPI; + this.nChannelsInternal = encControl.nChannelsInternal; + this.allow_bandwidth_switch = allow_bw_switch; + this.channelNb = channelNb; + if (this.controlled_since_last_payload != 0 && this.prefillFlag == 0) { + if (this.API_fs_Hz != this.prev_API_fs_Hz && this.fs_kHz > 0) { + ret = this.silk_setup_resamplers(this.fs_kHz); + } + return ret; + } + int fs_kHz = this.silk_control_audio_bandwidth(encControl); + if (force_fs_kHz != 0) { + fs_kHz = force_fs_kHz; + } + ret = this.silk_setup_resamplers(fs_kHz); + ret = this.silk_setup_fs(fs_kHz, encControl.payloadSize_ms); + ret = this.silk_setup_complexity(encControl.complexity); + this.PacketLoss_perc = encControl.packetLossPercentage; + ret = this.silk_setup_LBRR(TargetRate_bps); + this.controlled_since_last_payload = 1; + return ret; + } + + private int silk_setup_resamplers(int fs_kHz) { + int ret = 0; + if (this.fs_kHz != fs_kHz || this.prev_API_fs_Hz != this.API_fs_Hz) { + if (this.fs_kHz == 0) { + ret += Resampler.silk_resampler_init(this.resampler_state, this.API_fs_Hz, fs_kHz * 1000, 1); + } else { + SilkResamplerState temp_resampler_state = null; + int buf_length_ms = Inlines.silk_LSHIFT(this.nb_subfr * 5, 1) + 5; + int old_buf_samples = buf_length_ms * this.fs_kHz; + temp_resampler_state = new SilkResamplerState(); + ret += Resampler.silk_resampler_init(temp_resampler_state, Inlines.silk_SMULBB(this.fs_kHz, 1000), this.API_fs_Hz, 0); + int api_buf_samples = buf_length_ms * Inlines.silk_DIV32_16(this.API_fs_Hz, 1000); + short[] x_buf_API_fs_Hz = new short[api_buf_samples]; + ret += Resampler.silk_resampler(temp_resampler_state, x_buf_API_fs_Hz, 0, this.x_buf, 0, old_buf_samples); + ret += Resampler.silk_resampler_init(this.resampler_state, this.API_fs_Hz, Inlines.silk_SMULBB(fs_kHz, 1000), 1); + ret += Resampler.silk_resampler(this.resampler_state, this.x_buf, 0, x_buf_API_fs_Hz, 0, api_buf_samples); + } + } + this.prev_API_fs_Hz = this.API_fs_Hz; + return ret; + } + + private int silk_setup_fs(int fs_kHz, int PacketSize_ms) { + int ret = SilkError.SILK_NO_ERROR; + if (PacketSize_ms != this.PacketSize_ms) { + if (PacketSize_ms != 10 && PacketSize_ms != 20 && PacketSize_ms != 40 && PacketSize_ms != 60) { + ret = SilkError.SILK_ENC_PACKET_SIZE_NOT_SUPPORTED; + } + if (PacketSize_ms <= 10) { + this.nFramesPerPacket = 1; + this.nb_subfr = PacketSize_ms == 10 ? 2 : 1; + this.frame_length = Inlines.silk_SMULBB(PacketSize_ms, fs_kHz); + this.pitch_LPC_win_length = Inlines.silk_SMULBB(14, fs_kHz); + this.pitch_contour_iCDF = this.fs_kHz == 8 ? SilkTables.silk_pitch_contour_10_ms_NB_iCDF : SilkTables.silk_pitch_contour_10_ms_iCDF; + } else { + this.nFramesPerPacket = Inlines.silk_DIV32_16(PacketSize_ms, 20); + this.nb_subfr = 4; + this.frame_length = Inlines.silk_SMULBB(20, fs_kHz); + this.pitch_LPC_win_length = Inlines.silk_SMULBB(24, fs_kHz); + this.pitch_contour_iCDF = this.fs_kHz == 8 ? SilkTables.silk_pitch_contour_NB_iCDF : SilkTables.silk_pitch_contour_iCDF; + } + this.PacketSize_ms = PacketSize_ms; + this.TargetRate_bps = 0; + } + Inlines.OpusAssert(fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16); + Inlines.OpusAssert(this.nb_subfr == 2 || this.nb_subfr == 4); + if (this.fs_kHz != fs_kHz) { + this.sShape.Reset(); + this.sPrefilt.Reset(); + this.sNSQ.Reset(); + Arrays.MemSet(this.prev_NLSFq_Q15, (short)0, 16); + Arrays.MemSet(this.sLP.In_LP_State, 0, 2); + this.inputBufIx = 0; + this.nFramesEncoded = 0; + this.TargetRate_bps = 0; + this.prevLag = 100; + this.first_frame_after_reset = 1; + this.sPrefilt.lagPrev = 100; + this.sShape.LastGainIndex = (byte)10; + this.sNSQ.lagPrev = 100; + this.sNSQ.prev_gain_Q16 = 65536; + this.prevSignalType = 0; + this.fs_kHz = fs_kHz; + this.pitch_contour_iCDF = this.fs_kHz == 8 ? (this.nb_subfr == 4 ? SilkTables.silk_pitch_contour_NB_iCDF : SilkTables.silk_pitch_contour_10_ms_NB_iCDF) : (this.nb_subfr == 4 ? SilkTables.silk_pitch_contour_iCDF : SilkTables.silk_pitch_contour_10_ms_iCDF); + if (this.fs_kHz == 8 || this.fs_kHz == 12) { + this.predictLPCOrder = 10; + this.psNLSF_CB = SilkTables.silk_NLSF_CB_NB_MB; + } else { + this.predictLPCOrder = 16; + this.psNLSF_CB = SilkTables.silk_NLSF_CB_WB; + } + this.subfr_length = 5 * fs_kHz; + this.frame_length = Inlines.silk_SMULBB(this.subfr_length, this.nb_subfr); + this.ltp_mem_length = Inlines.silk_SMULBB(20, fs_kHz); + this.la_pitch = Inlines.silk_SMULBB(2, fs_kHz); + this.max_pitch_lag = Inlines.silk_SMULBB(18, fs_kHz); + this.pitch_LPC_win_length = this.nb_subfr == 4 ? Inlines.silk_SMULBB(24, fs_kHz) : Inlines.silk_SMULBB(14, fs_kHz); + if (this.fs_kHz == 16) { + this.mu_LTP_Q9 = 10; + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform8_iCDF; + } else if (this.fs_kHz == 12) { + this.mu_LTP_Q9 = 13; + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform6_iCDF; + } else { + this.mu_LTP_Q9 = 15; + this.pitch_lag_low_bits_iCDF = SilkTables.silk_uniform4_iCDF; + } + } + Inlines.OpusAssert(this.subfr_length * this.nb_subfr == this.frame_length); + return ret; + } + + private int silk_setup_complexity(int Complexity) { + int ret = 0; + Inlines.OpusAssert(Complexity >= 0 && Complexity <= 10); + if (Complexity < 2) { + this.pitchEstimationComplexity = 0; + this.pitchEstimationThreshold_Q16 = 52429; + this.pitchEstimationLPCOrder = 6; + this.shapingLPCOrder = 8; + this.la_shape = 3 * this.fs_kHz; + this.nStatesDelayedDecision = 1; + this.useInterpolatedNLSFs = 0; + this.LTPQuantLowComplexity = 1; + this.NLSF_MSVQ_Survivors = 2; + this.warping_Q16 = 0; + } else if (Complexity < 4) { + this.pitchEstimationComplexity = 1; + this.pitchEstimationThreshold_Q16 = 49807; + this.pitchEstimationLPCOrder = 8; + this.shapingLPCOrder = 10; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 1; + this.useInterpolatedNLSFs = 0; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 4; + this.warping_Q16 = 0; + } else if (Complexity < 6) { + this.pitchEstimationComplexity = 1; + this.pitchEstimationThreshold_Q16 = 48497; + this.pitchEstimationLPCOrder = 10; + this.shapingLPCOrder = 12; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 2; + this.useInterpolatedNLSFs = 1; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 8; + this.warping_Q16 = this.fs_kHz * 983; + } else if (Complexity < 8) { + this.pitchEstimationComplexity = 1; + this.pitchEstimationThreshold_Q16 = 47186; + this.pitchEstimationLPCOrder = 12; + this.shapingLPCOrder = 14; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 3; + this.useInterpolatedNLSFs = 1; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 16; + this.warping_Q16 = this.fs_kHz * 983; + } else { + this.pitchEstimationComplexity = 2; + this.pitchEstimationThreshold_Q16 = 45875; + this.pitchEstimationLPCOrder = 16; + this.shapingLPCOrder = 16; + this.la_shape = 5 * this.fs_kHz; + this.nStatesDelayedDecision = 4; + this.useInterpolatedNLSFs = 1; + this.LTPQuantLowComplexity = 0; + this.NLSF_MSVQ_Survivors = 32; + this.warping_Q16 = this.fs_kHz * 983; + } + this.pitchEstimationLPCOrder = Inlines.silk_min_int(this.pitchEstimationLPCOrder, this.predictLPCOrder); + this.shapeWinLength = 5 * this.fs_kHz + 2 * this.la_shape; + this.Complexity = Complexity; + Inlines.OpusAssert(this.pitchEstimationLPCOrder <= 16); + Inlines.OpusAssert(this.shapingLPCOrder <= 16); + Inlines.OpusAssert(this.nStatesDelayedDecision <= 4); + Inlines.OpusAssert(this.warping_Q16 <= Short.MAX_VALUE); + Inlines.OpusAssert(this.la_shape <= 80); + Inlines.OpusAssert(this.shapeWinLength <= 240); + Inlines.OpusAssert(this.NLSF_MSVQ_Survivors <= 32); + return ret; + } + + private int silk_setup_LBRR(int TargetRate_bps) { + int ret = SilkError.SILK_NO_ERROR; + int LBRR_in_previous_packet = this.LBRR_enabled; + this.LBRR_enabled = 0; + if (this.useInBandFEC != 0 && this.PacketLoss_perc > 0) { + int LBRR_rate_thres_bps = this.fs_kHz == 8 ? 12000 : (this.fs_kHz == 12 ? 14000 : 16000); + if (TargetRate_bps > (LBRR_rate_thres_bps = Inlines.silk_SMULWB(Inlines.silk_MUL(LBRR_rate_thres_bps, 125 - Inlines.silk_min(this.PacketLoss_perc, 25)), 655))) { + this.LBRR_GainIncreases = LBRR_in_previous_packet == 0 ? 7 : Inlines.silk_max_int(7 - Inlines.silk_SMULWB(this.PacketLoss_perc, 26214), 2); + this.LBRR_enabled = 1; + } + } + return ret; + } + + int silk_control_audio_bandwidth(EncControlState encControl) { + int fs_kHz = this.fs_kHz; + int fs_Hz = Inlines.silk_SMULBB(fs_kHz, 1000); + if (fs_Hz == 0) { + fs_Hz = Inlines.silk_min(this.desiredInternal_fs_Hz, this.API_fs_Hz); + fs_kHz = Inlines.silk_DIV32_16(fs_Hz, 1000); + } else if (fs_Hz > this.API_fs_Hz || fs_Hz > this.maxInternal_fs_Hz || fs_Hz < this.minInternal_fs_Hz) { + fs_Hz = this.API_fs_Hz; + fs_Hz = Inlines.silk_min(fs_Hz, this.maxInternal_fs_Hz); + fs_Hz = Inlines.silk_max(fs_Hz, this.minInternal_fs_Hz); + fs_kHz = Inlines.silk_DIV32_16(fs_Hz, 1000); + } else { + if (this.sLP.transition_frame_no >= 256) { + this.sLP.mode = 0; + } + if (this.allow_bandwidth_switch != 0 || encControl.opusCanSwitch != 0) { + if (Inlines.silk_SMULBB(this.fs_kHz, 1000) > this.desiredInternal_fs_Hz) { + if (this.sLP.mode == 0) { + this.sLP.transition_frame_no = 256; + Arrays.MemSet(this.sLP.In_LP_State, 0, 2); + } + if (encControl.opusCanSwitch != 0) { + this.sLP.mode = 0; + fs_kHz = this.fs_kHz == 16 ? 12 : 8; + } else if (this.sLP.transition_frame_no <= 0) { + encControl.switchReady = 1; + encControl.maxBits -= encControl.maxBits * 5 / (encControl.payloadSize_ms + 5); + } else { + this.sLP.mode = -2; + } + } else if (Inlines.silk_SMULBB(this.fs_kHz, 1000) < this.desiredInternal_fs_Hz) { + if (encControl.opusCanSwitch != 0) { + fs_kHz = this.fs_kHz == 8 ? 12 : 16; + this.sLP.transition_frame_no = 0; + Arrays.MemSet(this.sLP.In_LP_State, 0, 2); + this.sLP.mode = 1; + } else if (this.sLP.mode == 0) { + encControl.switchReady = 1; + encControl.maxBits -= encControl.maxBits * 5 / (encControl.payloadSize_ms + 5); + } else { + this.sLP.mode = 1; + } + } else if (this.sLP.mode < 0) { + this.sLP.mode = 1; + } + } + } + return fs_kHz; + } + + int silk_control_SNR(int TargetRate_bps) { + int ret = SilkError.SILK_NO_ERROR; + if ((TargetRate_bps = Inlines.silk_LIMIT(TargetRate_bps, 5000, 80000)) != this.TargetRate_bps) { + this.TargetRate_bps = TargetRate_bps; + int[] rateTable = this.fs_kHz == 8 ? SilkTables.silk_TargetRate_table_NB : (this.fs_kHz == 12 ? SilkTables.silk_TargetRate_table_MB : SilkTables.silk_TargetRate_table_WB); + if (this.nb_subfr == 2) { + TargetRate_bps -= 2200; + } + for (int k = 1; k < 8; ++k) { + if (TargetRate_bps > rateTable[k]) continue; + int frac_Q6 = Inlines.silk_DIV32(Inlines.silk_LSHIFT(TargetRate_bps - rateTable[k - 1], 6), rateTable[k] - rateTable[k - 1]); + this.SNR_dB_Q7 = Inlines.silk_LSHIFT(SilkTables.silk_SNR_table_Q1[k - 1], 6) + Inlines.silk_MUL(frac_Q6, SilkTables.silk_SNR_table_Q1[k] - SilkTables.silk_SNR_table_Q1[k - 1]); + break; + } + } + return ret; + } + + void silk_encode_do_VAD() { + VoiceActivityDetection.silk_VAD_GetSA_Q8(this, this.inputBuf, 1); + if (this.speech_activity_Q8 < 13) { + this.indices.signalType = 0; + ++this.noSpeechCounter; + if (this.noSpeechCounter < 10) { + this.inDTX = 0; + } else if (this.noSpeechCounter > 30) { + this.noSpeechCounter = 10; + this.inDTX = 0; + } + this.VAD_flags[this.nFramesEncoded] = 0; + } else { + this.noSpeechCounter = 0; + this.inDTX = 0; + this.indices.signalType = 1; + this.VAD_flags[this.nFramesEncoded] = 1; + } + } + + int silk_encode_frame(BoxedValueInt pnBytesOut, EntropyCoder psRangeEnc, int condCoding, int maxBits, int useCBR) { + SilkEncoderControl sEncCtrl = new SilkEncoderControl(); + int ret = 0; + EntropyCoder sRangeEnc_copy = new EntropyCoder(); + EntropyCoder sRangeEnc_copy2 = new EntropyCoder(); + SilkNSQState sNSQ_copy = new SilkNSQState(); + SilkNSQState sNSQ_copy2 = new SilkNSQState(); + byte LastGainIndex_copy2 = 0; + int gainMult_upper = 0; + int gainMult_lower = 0; + int nBits_upper = 0; + int nBits_lower = 0; + this.indices.Seed = (byte)(this.frameCounter++ & 3); + int x_frame = this.ltp_mem_length; + this.sLP.silk_LP_variable_cutoff(this.inputBuf, 1, this.frame_length); + System.arraycopy(this.inputBuf, 1, this.x_buf, x_frame + 5 * this.fs_kHz, this.frame_length); + if (this.prefillFlag == 0) { + short[] res_pitch = new short[this.la_pitch + this.frame_length + this.ltp_mem_length]; + int res_pitch_frame = this.ltp_mem_length; + FindPitchLags.silk_find_pitch_lags(this, sEncCtrl, res_pitch, this.x_buf, x_frame); + NoiseShapeAnalysis.silk_noise_shape_analysis(this, sEncCtrl, res_pitch, res_pitch_frame, this.x_buf, x_frame); + FindPredCoefs.silk_find_pred_coefs(this, sEncCtrl, res_pitch, this.x_buf, x_frame, condCoding); + ProcessGains.silk_process_gains(this, sEncCtrl, condCoding); + int[] xfw_Q3 = new int[this.frame_length]; + Filters.silk_prefilter(this, sEncCtrl, xfw_Q3, this.x_buf, x_frame); + this.silk_LBRR_encode(sEncCtrl, xfw_Q3, condCoding); + int maxIter = 6; + int gainMult_Q8 = 256; + boolean found_lower = false; + boolean found_upper = false; + int gainsID = GainQuantization.silk_gains_ID(this.indices.GainsIndices, this.nb_subfr); + int gainsID_lower = -1; + int gainsID_upper = -1; + sRangeEnc_copy.Assign(psRangeEnc); + sNSQ_copy.Assign(this.sNSQ); + byte seed_copy = this.indices.Seed; + short ec_prevLagIndex_copy = this.ec_prevLagIndex; + int ec_prevSignalType_copy = this.ec_prevSignalType; + byte[] ec_buf_copy = new byte[1275]; + int iter = 0; + while (true) { + int nBits; + if (gainsID == gainsID_lower) { + nBits = nBits_lower; + } else if (gainsID == gainsID_upper) { + nBits = nBits_upper; + } else { + if (iter > 0) { + psRangeEnc.Assign(sRangeEnc_copy); + this.sNSQ.Assign(sNSQ_copy); + this.indices.Seed = seed_copy; + this.ec_prevLagIndex = ec_prevLagIndex_copy; + this.ec_prevSignalType = ec_prevSignalType_copy; + } + if (this.nStatesDelayedDecision > 1 || this.warping_Q16 > 0) { + this.sNSQ.silk_NSQ_del_dec(this, this.indices, xfw_Q3, this.pulses, sEncCtrl.PredCoef_Q12, sEncCtrl.LTPCoef_Q14, sEncCtrl.AR2_Q13, sEncCtrl.HarmShapeGain_Q14, sEncCtrl.Tilt_Q14, sEncCtrl.LF_shp_Q14, sEncCtrl.Gains_Q16, sEncCtrl.pitchL, sEncCtrl.Lambda_Q10, sEncCtrl.LTP_scale_Q14); + } else { + this.sNSQ.silk_NSQ(this, this.indices, xfw_Q3, this.pulses, sEncCtrl.PredCoef_Q12, sEncCtrl.LTPCoef_Q14, sEncCtrl.AR2_Q13, sEncCtrl.HarmShapeGain_Q14, sEncCtrl.Tilt_Q14, sEncCtrl.LF_shp_Q14, sEncCtrl.Gains_Q16, sEncCtrl.pitchL, sEncCtrl.Lambda_Q10, sEncCtrl.LTP_scale_Q14); + } + EncodeIndices.silk_encode_indices(this, psRangeEnc, this.nFramesEncoded, 0, condCoding); + EncodePulses.silk_encode_pulses(psRangeEnc, this.indices.signalType, this.indices.quantOffsetType, this.pulses, this.frame_length); + nBits = psRangeEnc.tell(); + if (useCBR == 0 && iter == 0 && nBits <= maxBits) break; + } + if (iter == maxIter) { + if (!found_lower || gainsID != gainsID_lower && nBits <= maxBits) break; + psRangeEnc.Assign(sRangeEnc_copy2); + Inlines.OpusAssert(sRangeEnc_copy2.offs <= 1275); + psRangeEnc.write_buffer(ec_buf_copy, 0, 0, sRangeEnc_copy2.offs); + this.sNSQ.Assign(sNSQ_copy2); + this.sShape.LastGainIndex = LastGainIndex_copy2; + break; + } + if (nBits > maxBits) { + if (!found_lower && iter >= 2) { + sEncCtrl.Lambda_Q10 = Inlines.silk_ADD_RSHIFT32(sEncCtrl.Lambda_Q10, sEncCtrl.Lambda_Q10, 1); + found_upper = false; + gainsID_upper = -1; + } else { + found_upper = true; + nBits_upper = nBits; + gainMult_upper = gainMult_Q8; + gainsID_upper = gainsID; + } + } else { + if (nBits >= maxBits - 5) break; + found_lower = true; + nBits_lower = nBits; + gainMult_lower = gainMult_Q8; + if (gainsID != gainsID_lower) { + gainsID_lower = gainsID; + sRangeEnc_copy2.Assign(psRangeEnc); + Inlines.OpusAssert(psRangeEnc.offs <= 1275); + System.arraycopy(psRangeEnc.get_buffer(), 0, ec_buf_copy, 0, psRangeEnc.offs); + sNSQ_copy2.Assign(this.sNSQ); + LastGainIndex_copy2 = this.sShape.LastGainIndex; + } + } + if (!(found_lower & found_upper)) { + int gain_factor_Q16 = Inlines.silk_log2lin(Inlines.silk_LSHIFT(nBits - maxBits, 7) / this.frame_length + 2048); + gain_factor_Q16 = Inlines.silk_min_32(gain_factor_Q16, 131072); + if (nBits > maxBits) { + gain_factor_Q16 = Inlines.silk_max_32(gain_factor_Q16, 85197); + } + gainMult_Q8 = (short)Inlines.silk_SMULWB(gain_factor_Q16, gainMult_Q8); + } else { + gainMult_Q8 = (short)(gainMult_lower + Inlines.silk_DIV32_16(Inlines.silk_MUL(gainMult_upper - gainMult_lower, maxBits - nBits_lower), nBits_upper - nBits_lower)); + if (gainMult_Q8 > Inlines.silk_ADD_RSHIFT32(gainMult_lower, gainMult_upper - gainMult_lower, 2)) { + gainMult_Q8 = (short)Inlines.silk_ADD_RSHIFT32(gainMult_lower, gainMult_upper - gainMult_lower, 2); + } else if (gainMult_Q8 < Inlines.silk_SUB_RSHIFT32(gainMult_upper, gainMult_upper - gainMult_lower, 2)) { + gainMult_Q8 = (short)Inlines.silk_SUB_RSHIFT32(gainMult_upper, gainMult_upper - gainMult_lower, 2); + } + } + for (int i = 0; i < this.nb_subfr; ++i) { + sEncCtrl.Gains_Q16[i] = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(sEncCtrl.GainsUnq_Q16[i], gainMult_Q8), 8); + } + this.sShape.LastGainIndex = sEncCtrl.lastGainIndexPrev; + BoxedValueByte boxed_gainIndex = new BoxedValueByte(this.sShape.LastGainIndex); + GainQuantization.silk_gains_quant(this.indices.GainsIndices, sEncCtrl.Gains_Q16, boxed_gainIndex, condCoding == 2 ? 1 : 0, this.nb_subfr); + this.sShape.LastGainIndex = boxed_gainIndex.Val; + gainsID = GainQuantization.silk_gains_ID(this.indices.GainsIndices, this.nb_subfr); + ++iter; + } + } + Arrays.MemMove(this.x_buf, this.frame_length, 0, this.ltp_mem_length + 5 * this.fs_kHz); + if (this.prefillFlag != 0) { + pnBytesOut.Val = 0; + return ret; + } + this.prevLag = sEncCtrl.pitchL[this.nb_subfr - 1]; + this.prevSignalType = this.indices.signalType; + this.first_frame_after_reset = 0; + pnBytesOut.Val = Inlines.silk_RSHIFT(psRangeEnc.tell() + 7, 3); + return ret; + } + + void silk_LBRR_encode(SilkEncoderControl thisCtrl, int[] xfw_Q3, int condCoding) { + int[] TempGains_Q16 = new int[this.nb_subfr]; + SideInfoIndices psIndices_LBRR = this.indices_LBRR[this.nFramesEncoded]; + SilkNSQState sNSQ_LBRR = new SilkNSQState(); + if (this.LBRR_enabled != 0 && this.speech_activity_Q8 > 77) { + this.LBRR_flags[this.nFramesEncoded] = 1; + sNSQ_LBRR.Assign(this.sNSQ); + psIndices_LBRR.Assign(this.indices); + System.arraycopy(thisCtrl.Gains_Q16, 0, TempGains_Q16, 0, this.nb_subfr); + if (this.nFramesEncoded == 0 || this.LBRR_flags[this.nFramesEncoded - 1] == 0) { + this.LBRRprevLastGainIndex = this.sShape.LastGainIndex; + psIndices_LBRR.GainsIndices[0] = (byte)(psIndices_LBRR.GainsIndices[0] + this.LBRR_GainIncreases); + psIndices_LBRR.GainsIndices[0] = (byte)Inlines.silk_min_int(psIndices_LBRR.GainsIndices[0], 63); + } + BoxedValueByte boxed_gainIndex = new BoxedValueByte(this.LBRRprevLastGainIndex); + GainQuantization.silk_gains_dequant(thisCtrl.Gains_Q16, psIndices_LBRR.GainsIndices, boxed_gainIndex, condCoding == 2 ? 1 : 0, this.nb_subfr); + this.LBRRprevLastGainIndex = boxed_gainIndex.Val; + if (this.nStatesDelayedDecision > 1 || this.warping_Q16 > 0) { + sNSQ_LBRR.silk_NSQ_del_dec(this, psIndices_LBRR, xfw_Q3, this.pulses_LBRR[this.nFramesEncoded], thisCtrl.PredCoef_Q12, thisCtrl.LTPCoef_Q14, thisCtrl.AR2_Q13, thisCtrl.HarmShapeGain_Q14, thisCtrl.Tilt_Q14, thisCtrl.LF_shp_Q14, thisCtrl.Gains_Q16, thisCtrl.pitchL, thisCtrl.Lambda_Q10, thisCtrl.LTP_scale_Q14); + } else { + sNSQ_LBRR.silk_NSQ(this, psIndices_LBRR, xfw_Q3, this.pulses_LBRR[this.nFramesEncoded], thisCtrl.PredCoef_Q12, thisCtrl.LTPCoef_Q14, thisCtrl.AR2_Q13, thisCtrl.HarmShapeGain_Q14, thisCtrl.Tilt_Q14, thisCtrl.LF_shp_Q14, thisCtrl.Gains_Q16, thisCtrl.pitchL, thisCtrl.Lambda_Q10, thisCtrl.LTP_scale_Q14); + } + System.arraycopy(TempGains_Q16, 0, thisCtrl.Gains_Q16, 0, this.nb_subfr); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkConstants.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkConstants.java new file mode 100644 index 0000000..0d28397 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkConstants.java @@ -0,0 +1,157 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class SilkConstants { + public static final int ENCODER_NUM_CHANNELS = 2; + public static final int DECODER_NUM_CHANNELS = 2; + public static final int MAX_FRAMES_PER_PACKET = 3; + public static final int MIN_TARGET_RATE_BPS = 5000; + public static final int MAX_TARGET_RATE_BPS = 80000; + public static final int TARGET_RATE_TAB_SZ = 8; + public static final int LBRR_NB_MIN_RATE_BPS = 12000; + public static final int LBRR_MB_MIN_RATE_BPS = 14000; + public static final int LBRR_WB_MIN_RATE_BPS = 16000; + public static final int NB_SPEECH_FRAMES_BEFORE_DTX = 10; + public static final int MAX_CONSECUTIVE_DTX = 20; + public static final int MAX_FS_KHZ = 16; + public static final int MAX_API_FS_KHZ = 48; + public static final int TYPE_NO_VOICE_ACTIVITY = 0; + public static final int TYPE_UNVOICED = 1; + public static final int TYPE_VOICED = 2; + public static final int CODE_INDEPENDENTLY = 0; + public static final int CODE_INDEPENDENTLY_NO_LTP_SCALING = 1; + public static final int CODE_CONDITIONALLY = 2; + public static final int STEREO_QUANT_TAB_SIZE = 16; + public static final int STEREO_QUANT_SUB_STEPS = 5; + public static final int STEREO_INTERP_LEN_MS = 8; + public static final float STEREO_RATIO_SMOOTH_COEF = 0.01f; + public static final int PITCH_EST_MIN_LAG_MS = 2; + public static final int PITCH_EST_MAX_LAG_MS = 18; + public static final int MAX_NB_SUBFR = 4; + public static final int LTP_MEM_LENGTH_MS = 20; + public static final int SUB_FRAME_LENGTH_MS = 5; + public static final int MAX_SUB_FRAME_LENGTH = 80; + public static final int MAX_FRAME_LENGTH_MS = 20; + public static final int MAX_FRAME_LENGTH = 320; + public static final int LA_PITCH_MS = 2; + public static final int LA_PITCH_MAX = 32; + public static final int MAX_FIND_PITCH_LPC_ORDER = 16; + public static final int FIND_PITCH_LPC_WIN_MS = 24; + public static final int FIND_PITCH_LPC_WIN_MS_2_SF = 14; + public static final int FIND_PITCH_LPC_WIN_MAX = 384; + public static final int LA_SHAPE_MS = 5; + public static final int LA_SHAPE_MAX = 80; + public static final int SHAPE_LPC_WIN_MAX = 240; + public static final int MIN_QGAIN_DB = 2; + public static final int MAX_QGAIN_DB = 88; + public static final int N_LEVELS_QGAIN = 64; + public static final int MAX_DELTA_GAIN_QUANT = 36; + public static final int MIN_DELTA_GAIN_QUANT = -4; + public static final short OFFSET_VL_Q10 = 32; + public static final short OFFSET_VH_Q10 = 100; + public static final short OFFSET_UVL_Q10 = 100; + public static final short OFFSET_UVH_Q10 = 240; + public static final int QUANT_LEVEL_ADJUST_Q10 = 80; + public static final int MAX_LPC_STABILIZE_ITERATIONS = 16; + public static final float MAX_PREDICTION_POWER_GAIN = 10000.0f; + public static final float MAX_PREDICTION_POWER_GAIN_AFTER_RESET = 100.0f; + public static final int SILK_MAX_ORDER_LPC = 16; + public static final int MAX_LPC_ORDER = 16; + public static final int MIN_LPC_ORDER = 10; + public static final int LTP_ORDER = 5; + public static final int NB_LTP_CBKS = 3; + public static final int USE_HARM_SHAPING = 1; + public static final int MAX_SHAPE_LPC_ORDER = 16; + public static final int HARM_SHAPE_FIR_TAPS = 3; + public static final int MAX_DEL_DEC_STATES = 4; + public static final int LTP_BUF_LENGTH = 512; + public static final int LTP_MASK = 511; + public static final int DECISION_DELAY = 32; + public static final int DECISION_DELAY_MASK = 31; + public static final int SHELL_CODEC_FRAME_LENGTH = 16; + public static final int LOG2_SHELL_CODEC_FRAME_LENGTH = 4; + public static final int MAX_NB_SHELL_BLOCKS = 20; + public static final int N_RATE_LEVELS = 10; + public static final int SILK_MAX_PULSES = 16; + public static final int MAX_MATRIX_SIZE = 16; + static final int NSQ_LPC_BUF_LENGTH = Math.max(16, 32); + public static final int VAD_N_BANDS = 4; + public static final int VAD_INTERNAL_SUBFRAMES_LOG2 = 2; + public static final int VAD_INTERNAL_SUBFRAMES = 4; + public static final int VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 = 1024; + public static final int VAD_NOISE_LEVELS_BIAS = 50; + public static final int VAD_NEGATIVE_OFFSET_Q5 = 128; + public static final int VAD_SNR_FACTOR_Q16 = 45000; + public static final int VAD_SNR_SMOOTH_COEF_Q18 = 4096; + public static final int LSF_COS_TAB_SZ = 128; + public static final int NLSF_W_Q = 2; + public static final int NLSF_VQ_MAX_VECTORS = 32; + public static final int NLSF_VQ_MAX_SURVIVORS = 32; + public static final int NLSF_QUANT_MAX_AMPLITUDE = 4; + public static final int NLSF_QUANT_MAX_AMPLITUDE_EXT = 10; + public static final float NLSF_QUANT_LEVEL_ADJ = 0.1f; + public static final int NLSF_QUANT_DEL_DEC_STATES_LOG2 = 2; + public static final int NLSF_QUANT_DEL_DEC_STATES = 4; + public static final int TRANSITION_TIME_MS = 5120; + public static final int TRANSITION_NB = 3; + public static final int TRANSITION_NA = 2; + public static final int TRANSITION_INT_NUM = 5; + public static final int TRANSITION_FRAMES = 256; + public static final int TRANSITION_INT_STEPS = 64; + public static final int BWE_AFTER_LOSS_Q16 = 63570; + public static final int CNG_BUF_MASK_MAX = 255; + public static final int CNG_GAIN_SMTH_Q16 = 4634; + public static final int CNG_NLSF_SMTH_Q16 = 16348; + public static final int PE_MAX_FS_KHZ = 16; + public static final int PE_MAX_NB_SUBFR = 4; + public static final int PE_SUBFR_LENGTH_MS = 5; + public static final int PE_LTP_MEM_LENGTH_MS = 20; + public static final int PE_MAX_FRAME_LENGTH_MS = 40; + public static final int PE_MAX_FRAME_LENGTH = 640; + public static final int PE_MAX_FRAME_LENGTH_ST_1 = 160; + public static final int PE_MAX_FRAME_LENGTH_ST_2 = 320; + public static final int PE_MAX_LAG_MS = 18; + public static final int PE_MIN_LAG_MS = 2; + public static final int PE_MAX_LAG = 288; + public static final int PE_MIN_LAG = 32; + public static final int PE_D_SRCH_LENGTH = 24; + public static final int PE_NB_STAGE3_LAGS = 5; + public static final int PE_NB_CBKS_STAGE2 = 3; + public static final int PE_NB_CBKS_STAGE2_EXT = 11; + public static final int PE_NB_CBKS_STAGE3_MAX = 34; + public static final int PE_NB_CBKS_STAGE3_MID = 24; + public static final int PE_NB_CBKS_STAGE3_MIN = 16; + public static final int PE_NB_CBKS_STAGE3_10MS = 12; + public static final int PE_NB_CBKS_STAGE2_10MS = 3; + public static final float PE_SHORTLAG_BIAS = 0.2f; + public static final float PE_PREVLAG_BIAS = 0.2f; + public static final float PE_FLATCONTOUR_BIAS = 0.05f; + public static final int SILK_PE_MIN_COMPLEX = 0; + public static final int SILK_PE_MID_COMPLEX = 1; + public static final int SILK_PE_MAX_COMPLEX = 2; + public static final float BWE_COEF = 0.99f; + public static final int V_PITCH_GAIN_START_MIN_Q14 = 11469; + public static final int V_PITCH_GAIN_START_MAX_Q14 = 15565; + public static final int MAX_PITCH_LAG_MS = 18; + public static final int RAND_BUF_SIZE = 128; + public static final int RAND_BUF_MASK = 127; + public static final int LOG2_INV_LPC_GAIN_HIGH_THRES = 3; + public static final int LOG2_INV_LPC_GAIN_LOW_THRES = 8; + public static final int PITCH_DRIFT_FAC_Q16 = 655; + public static final int SILK_RESAMPLER_MAX_FIR_ORDER = 36; + public static final int SILK_RESAMPLER_MAX_IIR_ORDER = 6; + public static final int RESAMPLER_DOWN_ORDER_FIR0 = 18; + public static final int RESAMPLER_DOWN_ORDER_FIR1 = 24; + public static final int RESAMPLER_DOWN_ORDER_FIR2 = 36; + public static final int RESAMPLER_ORDER_FIR_12 = 8; + public static final int RESAMPLER_MAX_BATCH_SIZE_MS = 10; + public static final int RESAMPLER_MAX_FS_KHZ = 48; + public static final int RESAMPLER_MAX_BATCH_SIZE_IN = 480; + public static final int SILK_MAX_FRAMES_PER_PACKET = 3; + + SilkConstants() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkDecoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkDecoder.java new file mode 100644 index 0000000..af6bbc4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkDecoder.java @@ -0,0 +1,32 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.SilkChannelDecoder; +import de.maxhenkel.voicechat.concentus.StereoDecodeState; + +class SilkDecoder { + final SilkChannelDecoder[] channel_state = new SilkChannelDecoder[2]; + final StereoDecodeState sStereo = new StereoDecodeState(); + int nChannelsAPI = 0; + int nChannelsInternal = 0; + int prev_decode_only_middle = 0; + + SilkDecoder() { + for (int c = 0; c < 2; ++c) { + this.channel_state[c] = new SilkChannelDecoder(); + } + } + + void Reset() { + for (int c = 0; c < 2; ++c) { + this.channel_state[c].Reset(); + } + this.sStereo.Reset(); + this.nChannelsAPI = 0; + this.nChannelsInternal = 0; + this.prev_decode_only_middle = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkDecoderControl.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkDecoderControl.java new file mode 100644 index 0000000..1f0f1ff --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkDecoderControl.java @@ -0,0 +1,27 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class SilkDecoderControl { + final int[] pitchL = new int[4]; + final int[] Gains_Q16 = new int[4]; + final short[][] PredCoef_Q12 = Arrays.InitTwoDimensionalArrayShort(2, 16); + final short[] LTPCoef_Q14 = new short[20]; + int LTP_scale_Q14 = 0; + + SilkDecoderControl() { + } + + void Reset() { + Arrays.MemSet(this.pitchL, 0, 4); + Arrays.MemSet(this.Gains_Q16, 0, 4); + Arrays.MemSet(this.PredCoef_Q12[0], (short)0, 16); + Arrays.MemSet(this.PredCoef_Q12[1], (short)0, 16); + Arrays.MemSet(this.LTPCoef_Q14, (short)0, 20); + this.LTP_scale_Q14 = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkEncoder.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkEncoder.java new file mode 100644 index 0000000..831490b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkEncoder.java @@ -0,0 +1,52 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.StereoEncodeState; +import de.maxhenkel.voicechat.concentus.VoiceActivityDetection; + +class SilkEncoder { + final SilkChannelEncoder[] state_Fxx = new SilkChannelEncoder[2]; + final StereoEncodeState sStereo = new StereoEncodeState(); + int nBitsUsedLBRR = 0; + int nBitsExceeded = 0; + int nChannelsAPI = 0; + int nChannelsInternal = 0; + int nPrevChannelsInternal = 0; + int timeSinceSwitchAllowed_ms = 0; + int allowBandwidthSwitch = 0; + int prev_decode_only_middle = 0; + + SilkEncoder() { + for (int c = 0; c < 2; ++c) { + this.state_Fxx[c] = new SilkChannelEncoder(); + } + } + + void Reset() { + for (int c = 0; c < 2; ++c) { + this.state_Fxx[c].Reset(); + } + this.sStereo.Reset(); + this.nBitsUsedLBRR = 0; + this.nBitsExceeded = 0; + this.nChannelsAPI = 0; + this.nChannelsInternal = 0; + this.nPrevChannelsInternal = 0; + this.timeSinceSwitchAllowed_ms = 0; + this.allowBandwidthSwitch = 0; + this.prev_decode_only_middle = 0; + } + + static int silk_init_encoder(SilkChannelEncoder psEnc) { + int ret = 0; + psEnc.Reset(); + psEnc.variable_HP_smth2_Q15 = psEnc.variable_HP_smth1_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(0x3C0000) - 2048, 8); + psEnc.first_frame_after_reset = 1; + return ret += VoiceActivityDetection.silk_VAD_Init(psEnc.sVAD); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkEncoderControl.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkEncoderControl.java new file mode 100644 index 0000000..8a61578 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkEncoderControl.java @@ -0,0 +1,61 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class SilkEncoderControl { + final int[] Gains_Q16 = new int[4]; + final short[][] PredCoef_Q12 = Arrays.InitTwoDimensionalArrayShort(2, 16); + final short[] LTPCoef_Q14 = new short[20]; + int LTP_scale_Q14 = 0; + final int[] pitchL = new int[4]; + final short[] AR1_Q13 = new short[64]; + final short[] AR2_Q13 = new short[64]; + final int[] LF_shp_Q14 = new int[4]; + final int[] GainsPre_Q14 = new int[4]; + final int[] HarmBoost_Q14 = new int[4]; + final int[] Tilt_Q14 = new int[4]; + final int[] HarmShapeGain_Q14 = new int[4]; + int Lambda_Q10 = 0; + int input_quality_Q14 = 0; + int coding_quality_Q14 = 0; + int sparseness_Q8 = 0; + int predGain_Q16 = 0; + int LTPredCodGain_Q7 = 0; + final int[] ResNrg = new int[4]; + final int[] ResNrgQ = new int[4]; + final int[] GainsUnq_Q16 = new int[4]; + byte lastGainIndexPrev = 0; + + SilkEncoderControl() { + } + + void Reset() { + Arrays.MemSet(this.Gains_Q16, 0, 4); + Arrays.MemSet(this.PredCoef_Q12[0], (short)0, 16); + Arrays.MemSet(this.PredCoef_Q12[1], (short)0, 16); + Arrays.MemSet(this.LTPCoef_Q14, (short)0, 20); + this.LTP_scale_Q14 = 0; + Arrays.MemSet(this.pitchL, 0, 4); + Arrays.MemSet(this.AR1_Q13, (short)0, 64); + Arrays.MemSet(this.AR2_Q13, (short)0, 64); + Arrays.MemSet(this.LF_shp_Q14, 0, 4); + Arrays.MemSet(this.GainsPre_Q14, 0, 4); + Arrays.MemSet(this.HarmBoost_Q14, 0, 4); + Arrays.MemSet(this.Tilt_Q14, 0, 4); + Arrays.MemSet(this.HarmShapeGain_Q14, 0, 4); + this.Lambda_Q10 = 0; + this.input_quality_Q14 = 0; + this.coding_quality_Q14 = 0; + this.sparseness_Q8 = 0; + this.predGain_Q16 = 0; + this.LTPredCodGain_Q7 = 0; + Arrays.MemSet(this.ResNrg, 0, 4); + Arrays.MemSet(this.ResNrgQ, 0, 4); + Arrays.MemSet(this.GainsUnq_Q16, 0, 4); + this.lastGainIndexPrev = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkError.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkError.java new file mode 100644 index 0000000..ae5bbc4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkError.java @@ -0,0 +1,27 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class SilkError { + static int SILK_NO_ERROR = 0; + static int SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES = -101; + static int SILK_ENC_FS_NOT_SUPPORTED = -102; + static int SILK_ENC_PACKET_SIZE_NOT_SUPPORTED = -103; + static int SILK_ENC_PAYLOAD_BUF_TOO_SHORT = -104; + static int SILK_ENC_INVALID_LOSS_RATE = -105; + static int SILK_ENC_INVALID_COMPLEXITY_SETTING = -106; + static int SILK_ENC_INVALID_INBAND_FEC_SETTING = -107; + static int SILK_ENC_INVALID_DTX_SETTING = -108; + static int SILK_ENC_INVALID_CBR_SETTING = -109; + static int SILK_ENC_INTERNAL_ERROR = -110; + static int SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR = -111; + static int SILK_DEC_INVALID_SAMPLING_FREQUENCY = -200; + static int SILK_DEC_PAYLOAD_TOO_LARGE = -201; + static int SILK_DEC_PAYLOAD_ERROR = -202; + static int SILK_DEC_INVALID_FRAME_SIZE = -203; + + SilkError() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkLPState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkLPState.java new file mode 100644 index 0000000..3d60a80 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkLPState.java @@ -0,0 +1,42 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; + +class SilkLPState { + final int[] In_LP_State = new int[2]; + int transition_frame_no = 0; + int mode = 0; + + SilkLPState() { + } + + void Reset() { + this.In_LP_State[0] = 0; + this.In_LP_State[1] = 0; + this.transition_frame_no = 0; + this.mode = 0; + } + + void silk_LP_variable_cutoff(short[] frame, int frame_ptr, int frame_length) { + int[] B_Q28 = new int[3]; + int[] A_Q28 = new int[2]; + int fac_Q16 = 0; + int ind = 0; + Inlines.OpusAssert(this.transition_frame_no >= 0 && this.transition_frame_no <= 256); + if (this.mode != 0) { + fac_Q16 = Inlines.silk_LSHIFT(256 - this.transition_frame_no, 10); + ind = Inlines.silk_RSHIFT(fac_Q16, 16); + Inlines.OpusAssert(ind >= 0); + Inlines.OpusAssert(ind < 5); + Filters.silk_LP_interpolate_filter_taps(B_Q28, A_Q28, ind, fac_Q16 -= Inlines.silk_LSHIFT(ind, 16)); + this.transition_frame_no = Inlines.silk_LIMIT(this.transition_frame_no + this.mode, 0, 256); + Inlines.OpusAssert(true); + Filters.silk_biquad_alt(frame, frame_ptr, B_Q28, A_Q28, this.In_LP_State, frame, frame_ptr, frame_length, 1); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkNSQState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkNSQState.java new file mode 100644 index 0000000..8b9bb62 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkNSQState.java @@ -0,0 +1,715 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SideInfoIndices; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkConstants; +import de.maxhenkel.voicechat.concentus.SilkTables; + +class SilkNSQState { + final short[] xq = new short[640]; + final int[] sLTP_shp_Q14 = new int[640]; + final int[] sLPC_Q14 = new int[80 + SilkConstants.NSQ_LPC_BUF_LENGTH]; + final int[] sAR2_Q14 = new int[16]; + int sLF_AR_shp_Q14 = 0; + int lagPrev = 0; + int sLTP_buf_idx = 0; + int sLTP_shp_buf_idx = 0; + int rand_seed = 0; + int prev_gain_Q16 = 0; + int rewhite_flag = 0; + + SilkNSQState() { + } + + void Reset() { + Arrays.MemSet(this.xq, (short)0, 640); + Arrays.MemSet(this.sLTP_shp_Q14, 0, 640); + Arrays.MemSet(this.sLPC_Q14, 0, 80 + SilkConstants.NSQ_LPC_BUF_LENGTH); + Arrays.MemSet(this.sAR2_Q14, 0, 16); + this.sLF_AR_shp_Q14 = 0; + this.lagPrev = 0; + this.sLTP_buf_idx = 0; + this.sLTP_shp_buf_idx = 0; + this.rand_seed = 0; + this.prev_gain_Q16 = 0; + this.rewhite_flag = 0; + } + + void Assign(SilkNSQState other) { + this.sLF_AR_shp_Q14 = other.sLF_AR_shp_Q14; + this.lagPrev = other.lagPrev; + this.sLTP_buf_idx = other.sLTP_buf_idx; + this.sLTP_shp_buf_idx = other.sLTP_shp_buf_idx; + this.rand_seed = other.rand_seed; + this.prev_gain_Q16 = other.prev_gain_Q16; + this.rewhite_flag = other.rewhite_flag; + System.arraycopy(other.xq, 0, this.xq, 0, 640); + System.arraycopy(other.sLTP_shp_Q14, 0, this.sLTP_shp_Q14, 0, 640); + System.arraycopy(other.sLPC_Q14, 0, this.sLPC_Q14, 0, 80 + SilkConstants.NSQ_LPC_BUF_LENGTH); + System.arraycopy(other.sAR2_Q14, 0, this.sAR2_Q14, 0, 16); + } + + void silk_NSQ(SilkChannelEncoder psEncC, SideInfoIndices psIndices, int[] x_Q3, byte[] pulses, short[][] PredCoef_Q12, short[] LTPCoef_Q14, short[] AR2_Q13, int[] HarmShapeGain_Q14, int[] Tilt_Q14, int[] LF_shp_Q14, int[] Gains_Q16, int[] pitchL, int Lambda_Q10, int LTP_scale_Q14) { + int pulses_ptr = 0; + int x_Q3_ptr = 0; + this.rand_seed = psIndices.Seed; + int lag = this.lagPrev; + Inlines.OpusAssert(this.prev_gain_Q16 != 0); + short offset_Q10 = SilkTables.silk_Quantization_Offsets_Q10[psIndices.signalType >> 1][psIndices.quantOffsetType]; + int LSF_interpolation_flag = psIndices.NLSFInterpCoef_Q2 == 4 ? 0 : 1; + int[] sLTP_Q15 = new int[psEncC.ltp_mem_length + psEncC.frame_length]; + short[] sLTP = new short[psEncC.ltp_mem_length + psEncC.frame_length]; + int[] x_sc_Q10 = new int[psEncC.subfr_length]; + this.sLTP_shp_buf_idx = psEncC.ltp_mem_length; + this.sLTP_buf_idx = psEncC.ltp_mem_length; + int pxq = psEncC.ltp_mem_length; + for (int k = 0; k < psEncC.nb_subfr; ++k) { + int A_Q12 = k >> 1 | 1 - LSF_interpolation_flag; + int B_Q14 = k * 5; + int AR_shp_Q13 = k * 16; + Inlines.OpusAssert(HarmShapeGain_Q14[k] >= 0); + int HarmShapeFIRPacked_Q14 = Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 2); + HarmShapeFIRPacked_Q14 |= Inlines.silk_LSHIFT(Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 1), 16); + this.rewhite_flag = 0; + if (psIndices.signalType == 2) { + lag = pitchL[k]; + if ((k & 3 - Inlines.silk_LSHIFT(LSF_interpolation_flag, 1)) == 0) { + int start_idx = psEncC.ltp_mem_length - lag - psEncC.predictLPCOrder - 2; + Inlines.OpusAssert(start_idx > 0); + Filters.silk_LPC_analysis_filter(sLTP, start_idx, this.xq, start_idx + k * psEncC.subfr_length, PredCoef_Q12[A_Q12], 0, psEncC.ltp_mem_length - start_idx, psEncC.predictLPCOrder); + this.rewhite_flag = 1; + this.sLTP_buf_idx = psEncC.ltp_mem_length; + } + } + this.silk_nsq_scale_states(psEncC, x_Q3, x_Q3_ptr, x_sc_Q10, sLTP, sLTP_Q15, k, LTP_scale_Q14, Gains_Q16, pitchL, psIndices.signalType); + this.silk_noise_shape_quantizer(psIndices.signalType, x_sc_Q10, pulses, pulses_ptr, this.xq, pxq, sLTP_Q15, PredCoef_Q12[A_Q12], LTPCoef_Q14, B_Q14, AR2_Q13, AR_shp_Q13, lag, HarmShapeFIRPacked_Q14, Tilt_Q14[k], LF_shp_Q14[k], Gains_Q16[k], Lambda_Q10, offset_Q10, psEncC.subfr_length, psEncC.shapingLPCOrder, psEncC.predictLPCOrder); + x_Q3_ptr += psEncC.subfr_length; + pulses_ptr += psEncC.subfr_length; + pxq += psEncC.subfr_length; + } + this.lagPrev = pitchL[psEncC.nb_subfr - 1]; + Arrays.MemMove(this.xq, psEncC.frame_length, 0, psEncC.ltp_mem_length); + Arrays.MemMove(this.sLTP_shp_Q14, psEncC.frame_length, 0, psEncC.ltp_mem_length); + } + + private void silk_noise_shape_quantizer(int signalType, int[] x_sc_Q10, byte[] pulses, int pulses_ptr, short[] xq, int xq_ptr, int[] sLTP_Q15, short[] a_Q12, short[] b_Q14, int b_Q14_ptr, short[] AR_shp_Q13, int AR_shp_Q13_ptr, int lag, int HarmShapeFIRPacked_Q14, int Tilt_Q14, int LF_shp_Q14, int Gain_Q16, int Lambda_Q10, int offset_Q10, int length, int shapingLPCOrder, int predictLPCOrder) { + int shp_lag_ptr = this.sLTP_shp_buf_idx - lag + 1; + int pred_lag_ptr = this.sLTP_buf_idx - lag + 2; + int Gain_Q10 = Inlines.silk_RSHIFT(Gain_Q16, 6); + int psLPC_Q14 = SilkConstants.NSQ_LPC_BUF_LENGTH - 1; + for (int i = 0; i < length; ++i) { + int sLF_AR_shp_Q14; + int rd2_Q20; + int rd1_Q20; + int q2_Q10; + int q1_Q10; + int q1_Q0; + int LTP_pred_Q13; + this.rand_seed = Inlines.silk_RAND(this.rand_seed); + Inlines.OpusAssert(predictLPCOrder == 10 || predictLPCOrder == 16); + int LPC_pred_Q10 = Inlines.silk_RSHIFT(predictLPCOrder, 1); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 0], a_Q12[0]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 1], a_Q12[1]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 2], a_Q12[2]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 3], a_Q12[3]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 4], a_Q12[4]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 5], a_Q12[5]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 6], a_Q12[6]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 7], a_Q12[7]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 8], a_Q12[8]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 9], a_Q12[9]); + if (predictLPCOrder == 16) { + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 10], a_Q12[10]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 11], a_Q12[11]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 12], a_Q12[12]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 13], a_Q12[13]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 14], a_Q12[14]); + LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, this.sLPC_Q14[psLPC_Q14 - 15], a_Q12[15]); + } + if (signalType == 2) { + LTP_pred_Q13 = 2; + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr], b_Q14[b_Q14_ptr]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 1], b_Q14[b_Q14_ptr + 1]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 2], b_Q14[b_Q14_ptr + 2]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 3], b_Q14[b_Q14_ptr + 3]); + LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 4], b_Q14[b_Q14_ptr + 4]); + ++pred_lag_ptr; + } else { + LTP_pred_Q13 = 0; + } + Inlines.OpusAssert((shapingLPCOrder & 1) == 0); + int tmp2 = this.sLPC_Q14[psLPC_Q14]; + int tmp1 = this.sAR2_Q14[0]; + this.sAR2_Q14[0] = tmp2; + int n_AR_Q12 = Inlines.silk_RSHIFT(shapingLPCOrder, 1); + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp2, AR_shp_Q13[AR_shp_Q13_ptr]); + for (int j = 2; j < shapingLPCOrder; j += 2) { + tmp2 = this.sAR2_Q14[j - 1]; + this.sAR2_Q14[j - 1] = tmp1; + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + j - 1]); + tmp1 = this.sAR2_Q14[j + 0]; + this.sAR2_Q14[j + 0] = tmp2; + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp2, AR_shp_Q13[AR_shp_Q13_ptr + j]); + } + this.sAR2_Q14[shapingLPCOrder - 1] = tmp1; + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + shapingLPCOrder - 1]); + n_AR_Q12 = Inlines.silk_LSHIFT32(n_AR_Q12, 1); + n_AR_Q12 = Inlines.silk_SMLAWB(n_AR_Q12, this.sLF_AR_shp_Q14, Tilt_Q14); + int n_LF_Q12 = Inlines.silk_SMULWB(this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - 1], LF_shp_Q14); + n_LF_Q12 = Inlines.silk_SMLAWT(n_LF_Q12, this.sLF_AR_shp_Q14, LF_shp_Q14); + Inlines.OpusAssert(lag > 0 || signalType != 2); + tmp1 = Inlines.silk_SUB32(Inlines.silk_LSHIFT32(LPC_pred_Q10, 2), n_AR_Q12); + tmp1 = Inlines.silk_SUB32(tmp1, n_LF_Q12); + if (lag > 0) { + int n_LTP_Q13 = Inlines.silk_SMULWB(Inlines.silk_ADD32(this.sLTP_shp_Q14[shp_lag_ptr], this.sLTP_shp_Q14[shp_lag_ptr - 2]), HarmShapeFIRPacked_Q14); + n_LTP_Q13 = Inlines.silk_SMLAWT(n_LTP_Q13, this.sLTP_shp_Q14[shp_lag_ptr - 1], HarmShapeFIRPacked_Q14); + n_LTP_Q13 = Inlines.silk_LSHIFT(n_LTP_Q13, 1); + ++shp_lag_ptr; + tmp2 = Inlines.silk_SUB32(LTP_pred_Q13, n_LTP_Q13); + tmp1 = Inlines.silk_ADD_LSHIFT32(tmp2, tmp1, 1); + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 3); + } else { + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 2); + } + int r_Q10 = Inlines.silk_SUB32(x_sc_Q10[i], tmp1); + if (this.rand_seed < 0) { + r_Q10 = -r_Q10; + } + if ((q1_Q0 = Inlines.silk_RSHIFT(q1_Q10 = Inlines.silk_SUB32(r_Q10 = Inlines.silk_LIMIT_32(r_Q10, -31744, 30720), offset_Q10), 10)) > 0) { + q1_Q10 = Inlines.silk_SUB32(Inlines.silk_LSHIFT(q1_Q0, 10), 80); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q20 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else if (q1_Q0 == 0) { + q1_Q10 = offset_Q10; + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 944); + rd1_Q20 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else if (q1_Q0 == -1) { + q2_Q10 = offset_Q10; + q1_Q10 = Inlines.silk_SUB32(q2_Q10, 944); + rd1_Q20 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else { + q1_Q10 = Inlines.silk_ADD32(Inlines.silk_LSHIFT(q1_Q0, 10), 80); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q20 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q20 = Inlines.silk_SMULBB(-q2_Q10, Lambda_Q10); + } + int rr_Q10 = Inlines.silk_SUB32(r_Q10, q1_Q10); + rd1_Q20 = Inlines.silk_SMLABB(rd1_Q20, rr_Q10, rr_Q10); + rr_Q10 = Inlines.silk_SUB32(r_Q10, q2_Q10); + rd2_Q20 = Inlines.silk_SMLABB(rd2_Q20, rr_Q10, rr_Q10); + if (rd2_Q20 < rd1_Q20) { + q1_Q10 = q2_Q10; + } + pulses[pulses_ptr + i] = (byte)Inlines.silk_RSHIFT_ROUND(q1_Q10, 10); + int exc_Q14 = Inlines.silk_LSHIFT(q1_Q10, 4); + if (this.rand_seed < 0) { + exc_Q14 = -exc_Q14; + } + int LPC_exc_Q14 = Inlines.silk_ADD_LSHIFT32(exc_Q14, LTP_pred_Q13, 1); + int xq_Q14 = Inlines.silk_ADD_LSHIFT32(LPC_exc_Q14, LPC_pred_Q10, 4); + xq[xq_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(xq_Q14, Gain_Q10), 8)); + this.sLPC_Q14[++psLPC_Q14] = xq_Q14; + this.sLF_AR_shp_Q14 = sLF_AR_shp_Q14 = Inlines.silk_SUB_LSHIFT32(xq_Q14, n_AR_Q12, 2); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx] = Inlines.silk_SUB_LSHIFT32(sLF_AR_shp_Q14, n_LF_Q12, 2); + sLTP_Q15[this.sLTP_buf_idx] = Inlines.silk_LSHIFT(LPC_exc_Q14, 1); + ++this.sLTP_shp_buf_idx; + ++this.sLTP_buf_idx; + this.rand_seed = Inlines.silk_ADD32_ovflw(this.rand_seed, pulses[pulses_ptr + i]); + } + System.arraycopy(this.sLPC_Q14, length, this.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + } + + private void silk_nsq_scale_states(SilkChannelEncoder psEncC, int[] x_Q3, int x_Q3_ptr, int[] x_sc_Q10, short[] sLTP, int[] sLTP_Q15, int subfr, int LTP_scale_Q14, int[] Gains_Q16, int[] pitchL, int signal_type) { + int i; + int lag = pitchL[subfr]; + int inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(Inlines.silk_max(Gains_Q16[subfr], 1), 47); + Inlines.OpusAssert(inv_gain_Q31 != 0); + int gain_adj_Q16 = Gains_Q16[subfr] != this.prev_gain_Q16 ? Inlines.silk_DIV32_varQ(this.prev_gain_Q16, Gains_Q16[subfr], 16) : 65536; + int inv_gain_Q23 = Inlines.silk_RSHIFT_ROUND(inv_gain_Q31, 8); + for (i = 0; i < psEncC.subfr_length; ++i) { + x_sc_Q10[i] = Inlines.silk_SMULWW(x_Q3[x_Q3_ptr + i], inv_gain_Q23); + } + this.prev_gain_Q16 = Gains_Q16[subfr]; + if (this.rewhite_flag != 0) { + if (subfr == 0) { + inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, LTP_scale_Q14), 2); + } + for (i = this.sLTP_buf_idx - lag - 2; i < this.sLTP_buf_idx; ++i) { + Inlines.OpusAssert(i < 320); + sLTP_Q15[i] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[i]); + } + } + if (gain_adj_Q16 != 65536) { + for (i = this.sLTP_shp_buf_idx - psEncC.ltp_mem_length; i < this.sLTP_shp_buf_idx; ++i) { + this.sLTP_shp_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sLTP_shp_Q14[i]); + } + if (signal_type == 2 && this.rewhite_flag == 0) { + for (i = this.sLTP_buf_idx - lag - 2; i < this.sLTP_buf_idx; ++i) { + sLTP_Q15[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[i]); + } + } + this.sLF_AR_shp_Q14 = Inlines.silk_SMULWW(gain_adj_Q16, this.sLF_AR_shp_Q14); + for (i = 0; i < SilkConstants.NSQ_LPC_BUF_LENGTH; ++i) { + this.sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sLPC_Q14[i]); + } + for (i = 0; i < 16; ++i) { + this.sAR2_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sAR2_Q14[i]); + } + } + } + + void silk_NSQ_del_dec(SilkChannelEncoder psEncC, SideInfoIndices psIndices, int[] x_Q3, byte[] pulses, short[][] PredCoef_Q12, short[] LTPCoef_Q14, short[] AR2_Q13, int[] HarmShapeGain_Q14, int[] Tilt_Q14, int[] LF_shp_Q14, int[] Gains_Q16, int[] pitchL, int Lambda_Q10, int LTP_scale_Q14) { + int last_smple_idx; + int i; + int Winner_ind; + int RDmin_Q10; + NSQ_del_dec_struct psDD; + int k; + int pulses_ptr = 0; + int x_Q3_ptr = 0; + int lag = this.lagPrev; + Inlines.OpusAssert(this.prev_gain_Q16 != 0); + NSQ_del_dec_struct[] psDelDec = new NSQ_del_dec_struct[psEncC.nStatesDelayedDecision]; + for (int c = 0; c < psEncC.nStatesDelayedDecision; ++c) { + psDelDec[c] = new NSQ_del_dec_struct(psEncC.shapingLPCOrder); + } + for (k = 0; k < psEncC.nStatesDelayedDecision; ++k) { + psDD = psDelDec[k]; + psDD.SeedInit = psDD.Seed = k + psIndices.Seed & 3; + psDD.RD_Q10 = 0; + psDD.LF_AR_Q14 = this.sLF_AR_shp_Q14; + psDD.Shape_Q14[0] = this.sLTP_shp_Q14[psEncC.ltp_mem_length - 1]; + System.arraycopy(this.sLPC_Q14, 0, psDD.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + System.arraycopy(this.sAR2_Q14, 0, psDD.sAR2_Q14, 0, psEncC.shapingLPCOrder); + } + short offset_Q10 = SilkTables.silk_Quantization_Offsets_Q10[psIndices.signalType >> 1][psIndices.quantOffsetType]; + int smpl_buf_idx = 0; + int decisionDelay = Inlines.silk_min_int(32, psEncC.subfr_length); + if (psIndices.signalType == 2) { + for (k = 0; k < psEncC.nb_subfr; ++k) { + decisionDelay = Inlines.silk_min_int(decisionDelay, pitchL[k] - 2 - 1); + } + } else if (lag > 0) { + decisionDelay = Inlines.silk_min_int(decisionDelay, lag - 2 - 1); + } + int LSF_interpolation_flag = psIndices.NLSFInterpCoef_Q2 == 4 ? 0 : 1; + int[] sLTP_Q15 = new int[psEncC.ltp_mem_length + psEncC.frame_length]; + short[] sLTP = new short[psEncC.ltp_mem_length + psEncC.frame_length]; + int[] x_sc_Q10 = new int[psEncC.subfr_length]; + int[] delayedGain_Q10 = new int[32]; + int pxq = psEncC.ltp_mem_length; + this.sLTP_shp_buf_idx = psEncC.ltp_mem_length; + this.sLTP_buf_idx = psEncC.ltp_mem_length; + int subfr = 0; + for (k = 0; k < psEncC.nb_subfr; ++k) { + int A_Q12 = k >> 1 | 1 - LSF_interpolation_flag; + Inlines.OpusAssert(HarmShapeGain_Q14[k] >= 0); + int HarmShapeFIRPacked_Q14 = Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 2); + HarmShapeFIRPacked_Q14 |= Inlines.silk_LSHIFT(Inlines.silk_RSHIFT(HarmShapeGain_Q14[k], 1), 16); + this.rewhite_flag = 0; + if (psIndices.signalType == 2) { + lag = pitchL[k]; + if ((k & 3 - Inlines.silk_LSHIFT(LSF_interpolation_flag, 1)) == 0) { + int start_idx; + if (k == 2) { + RDmin_Q10 = psDelDec[0].RD_Q10; + Winner_ind = 0; + for (i = 1; i < psEncC.nStatesDelayedDecision; ++i) { + if (psDelDec[i].RD_Q10 >= RDmin_Q10) continue; + RDmin_Q10 = psDelDec[i].RD_Q10; + Winner_ind = i; + } + for (i = 0; i < psEncC.nStatesDelayedDecision; ++i) { + if (i == Winner_ind) continue; + psDelDec[i].RD_Q10 += 0x7FFFFFF; + Inlines.OpusAssert(psDelDec[i].RD_Q10 >= 0); + } + psDD = psDelDec[Winner_ind]; + last_smple_idx = smpl_buf_idx + decisionDelay; + for (i = 0; i < decisionDelay; ++i) { + last_smple_idx = last_smple_idx - 1 & 0x1F; + pulses[pulses_ptr + i - decisionDelay] = (byte)Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + this.xq[pxq + i - decisionDelay] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], Gains_Q16[1]), 14)); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay + i] = psDD.Shape_Q14[last_smple_idx]; + } + subfr = 0; + } + Inlines.OpusAssert((start_idx = psEncC.ltp_mem_length - lag - psEncC.predictLPCOrder - 2) > 0); + Filters.silk_LPC_analysis_filter(sLTP, start_idx, this.xq, start_idx + k * psEncC.subfr_length, PredCoef_Q12[A_Q12], 0, psEncC.ltp_mem_length - start_idx, psEncC.predictLPCOrder); + this.sLTP_buf_idx = psEncC.ltp_mem_length; + this.rewhite_flag = 1; + } + } + this.silk_nsq_del_dec_scale_states(psEncC, psDelDec, x_Q3, x_Q3_ptr, x_sc_Q10, sLTP, sLTP_Q15, k, psEncC.nStatesDelayedDecision, LTP_scale_Q14, Gains_Q16, pitchL, psIndices.signalType, decisionDelay); + BoxedValueInt smpl_buf_idx_boxed = new BoxedValueInt(smpl_buf_idx); + this.silk_noise_shape_quantizer_del_dec(psDelDec, psIndices.signalType, x_sc_Q10, pulses, pulses_ptr, this.xq, pxq, sLTP_Q15, delayedGain_Q10, PredCoef_Q12[A_Q12], LTPCoef_Q14, k * 5, AR2_Q13, k * 16, lag, HarmShapeFIRPacked_Q14, Tilt_Q14[k], LF_shp_Q14[k], Gains_Q16[k], Lambda_Q10, offset_Q10, psEncC.subfr_length, subfr++, psEncC.shapingLPCOrder, psEncC.predictLPCOrder, psEncC.warping_Q16, psEncC.nStatesDelayedDecision, smpl_buf_idx_boxed, decisionDelay); + smpl_buf_idx = smpl_buf_idx_boxed.Val; + x_Q3_ptr += psEncC.subfr_length; + pulses_ptr += psEncC.subfr_length; + pxq += psEncC.subfr_length; + } + RDmin_Q10 = psDelDec[0].RD_Q10; + Winner_ind = 0; + for (k = 1; k < psEncC.nStatesDelayedDecision; ++k) { + if (psDelDec[k].RD_Q10 >= RDmin_Q10) continue; + RDmin_Q10 = psDelDec[k].RD_Q10; + Winner_ind = k; + } + psDD = psDelDec[Winner_ind]; + psIndices.Seed = (byte)psDD.SeedInit; + last_smple_idx = smpl_buf_idx + decisionDelay; + int Gain_Q10 = Inlines.silk_RSHIFT32(Gains_Q16[psEncC.nb_subfr - 1], 6); + for (i = 0; i < decisionDelay; ++i) { + last_smple_idx = last_smple_idx - 1 & 0x1F; + pulses[pulses_ptr + i - decisionDelay] = (byte)Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + this.xq[pxq + i - decisionDelay] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], Gain_Q10), 8)); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay + i] = psDD.Shape_Q14[last_smple_idx]; + } + System.arraycopy(psDD.sLPC_Q14, psEncC.subfr_length, this.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + System.arraycopy(psDD.sAR2_Q14, 0, this.sAR2_Q14, 0, psEncC.shapingLPCOrder); + this.sLF_AR_shp_Q14 = psDD.LF_AR_Q14; + this.lagPrev = pitchL[psEncC.nb_subfr - 1]; + Arrays.MemMove(this.xq, psEncC.frame_length, 0, psEncC.ltp_mem_length); + Arrays.MemMove(this.sLTP_shp_Q14, psEncC.frame_length, 0, psEncC.ltp_mem_length); + } + + private void silk_noise_shape_quantizer_del_dec(NSQ_del_dec_struct[] psDelDec, int signalType, int[] x_Q10, byte[] pulses, int pulses_ptr, short[] xq, int xq_ptr, int[] sLTP_Q15, int[] delayedGain_Q10, short[] a_Q12, short[] b_Q14, int b_Q14_ptr, short[] AR_shp_Q13, int AR_shp_Q13_ptr, int lag, int HarmShapeFIRPacked_Q14, int Tilt_Q14, int LF_shp_Q14, int Gain_Q16, int Lambda_Q10, int offset_Q10, int length, int subfr, int shapingLPCOrder, int predictLPCOrder, int warping_Q16, int nStatesDelayedDecision, BoxedValueInt smpl_buf_idx, int decisionDelay) { + NSQ_del_dec_struct psDD; + int k; + Inlines.OpusAssert(nStatesDelayedDecision > 0); + NSQ_sample_struct[] sampleStates = new NSQ_sample_struct[2 * nStatesDelayedDecision]; + for (int c = 0; c < 2 * nStatesDelayedDecision; ++c) { + sampleStates[c] = new NSQ_sample_struct(); + } + int shp_lag_ptr = this.sLTP_shp_buf_idx - lag + 1; + int pred_lag_ptr = this.sLTP_buf_idx - lag + 2; + int Gain_Q10 = Inlines.silk_RSHIFT(Gain_Q16, 6); + for (int i = 0; i < length; ++i) { + int k2; + int SS_left; + int n_LTP_Q14; + int LTP_pred_Q14; + if (signalType == 2) { + LTP_pred_Q14 = 2; + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr], b_Q14[b_Q14_ptr + 0]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 1], b_Q14[b_Q14_ptr + 1]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 2], b_Q14[b_Q14_ptr + 2]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 3], b_Q14[b_Q14_ptr + 3]); + LTP_pred_Q14 = Inlines.silk_SMLAWB(LTP_pred_Q14, sLTP_Q15[pred_lag_ptr - 4], b_Q14[b_Q14_ptr + 4]); + LTP_pred_Q14 = Inlines.silk_LSHIFT(LTP_pred_Q14, 1); + ++pred_lag_ptr; + } else { + LTP_pred_Q14 = 0; + } + if (lag > 0) { + n_LTP_Q14 = Inlines.silk_SMULWB(Inlines.silk_ADD32(this.sLTP_shp_Q14[shp_lag_ptr], this.sLTP_shp_Q14[shp_lag_ptr - 2]), HarmShapeFIRPacked_Q14); + n_LTP_Q14 = Inlines.silk_SMLAWT(n_LTP_Q14, this.sLTP_shp_Q14[shp_lag_ptr - 1], HarmShapeFIRPacked_Q14); + n_LTP_Q14 = Inlines.silk_SUB_LSHIFT32(LTP_pred_Q14, n_LTP_Q14, 2); + ++shp_lag_ptr; + } else { + n_LTP_Q14 = 0; + } + for (k = 0; k < nStatesDelayedDecision; ++k) { + int rd2_Q10; + int rd1_Q10; + int q2_Q10; + int q1_Q10; + int q1_Q0; + psDD = psDelDec[k]; + int[] psDD_sAR2 = psDD.sAR2_Q14; + SS_left = 2 * k; + int SS_right = SS_left + 1; + psDD.Seed = Inlines.silk_RAND(psDD.Seed); + int psLPC_Q14 = SilkConstants.NSQ_LPC_BUF_LENGTH - 1 + i; + Inlines.OpusAssert(predictLPCOrder == 10 || predictLPCOrder == 16); + int LPC_pred_Q14 = Inlines.silk_RSHIFT(predictLPCOrder, 1); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14], a_Q12[0]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 1], a_Q12[1]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 2], a_Q12[2]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 3], a_Q12[3]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 4], a_Q12[4]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 5], a_Q12[5]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 6], a_Q12[6]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 7], a_Q12[7]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 8], a_Q12[8]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 9], a_Q12[9]); + if (predictLPCOrder == 16) { + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 10], a_Q12[10]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 11], a_Q12[11]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 12], a_Q12[12]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 13], a_Q12[13]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 14], a_Q12[14]); + LPC_pred_Q14 = Inlines.silk_SMLAWB(LPC_pred_Q14, psDD.sLPC_Q14[psLPC_Q14 - 15], a_Q12[15]); + } + LPC_pred_Q14 = Inlines.silk_LSHIFT(LPC_pred_Q14, 4); + Inlines.OpusAssert((shapingLPCOrder & 1) == 0); + int tmp2 = Inlines.silk_SMLAWB(psDD.sLPC_Q14[psLPC_Q14], psDD_sAR2[0], warping_Q16); + int tmp1 = Inlines.silk_SMLAWB(psDD_sAR2[0], psDD_sAR2[1] - tmp2, warping_Q16); + psDD_sAR2[0] = tmp2; + int n_AR_Q14 = Inlines.silk_RSHIFT(shapingLPCOrder, 1); + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp2, AR_shp_Q13[AR_shp_Q13_ptr]); + for (int j = 2; j < shapingLPCOrder; j += 2) { + tmp2 = Inlines.silk_SMLAWB(psDD_sAR2[j - 1], psDD_sAR2[j + 0] - tmp1, warping_Q16); + psDD_sAR2[j - 1] = tmp1; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + j - 1]); + tmp1 = Inlines.silk_SMLAWB(psDD_sAR2[j + 0], psDD_sAR2[j + 1] - tmp2, warping_Q16); + psDD_sAR2[j + 0] = tmp2; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp2, AR_shp_Q13[AR_shp_Q13_ptr + j]); + } + psDD_sAR2[shapingLPCOrder - 1] = tmp1; + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, tmp1, AR_shp_Q13[AR_shp_Q13_ptr + shapingLPCOrder - 1]); + n_AR_Q14 = Inlines.silk_LSHIFT(n_AR_Q14, 1); + n_AR_Q14 = Inlines.silk_SMLAWB(n_AR_Q14, psDD.LF_AR_Q14, Tilt_Q14); + n_AR_Q14 = Inlines.silk_LSHIFT(n_AR_Q14, 2); + int n_LF_Q14 = Inlines.silk_SMULWB(psDD.Shape_Q14[smpl_buf_idx.Val], LF_shp_Q14); + n_LF_Q14 = Inlines.silk_SMLAWT(n_LF_Q14, psDD.LF_AR_Q14, LF_shp_Q14); + n_LF_Q14 = Inlines.silk_LSHIFT(n_LF_Q14, 2); + tmp1 = Inlines.silk_ADD32(n_AR_Q14, n_LF_Q14); + tmp2 = Inlines.silk_ADD32(n_LTP_Q14, LPC_pred_Q14); + tmp1 = Inlines.silk_SUB32(tmp2, tmp1); + tmp1 = Inlines.silk_RSHIFT_ROUND(tmp1, 4); + int r_Q10 = Inlines.silk_SUB32(x_Q10[i], tmp1); + if (psDD.Seed < 0) { + r_Q10 = -r_Q10; + } + if ((q1_Q0 = Inlines.silk_RSHIFT(q1_Q10 = Inlines.silk_SUB32(r_Q10 = Inlines.silk_LIMIT_32(r_Q10, -31744, 30720), offset_Q10), 10)) > 0) { + q1_Q10 = Inlines.silk_SUB32(Inlines.silk_LSHIFT(q1_Q0, 10), 80); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q10 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else if (q1_Q0 == 0) { + q1_Q10 = offset_Q10; + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 944); + rd1_Q10 = Inlines.silk_SMULBB(q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else if (q1_Q0 == -1) { + q2_Q10 = offset_Q10; + q1_Q10 = Inlines.silk_SUB32(q2_Q10, 944); + rd1_Q10 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(q2_Q10, Lambda_Q10); + } else { + q1_Q10 = Inlines.silk_ADD32(Inlines.silk_LSHIFT(q1_Q0, 10), 80); + q1_Q10 = Inlines.silk_ADD32(q1_Q10, offset_Q10); + q2_Q10 = Inlines.silk_ADD32(q1_Q10, 1024); + rd1_Q10 = Inlines.silk_SMULBB(-q1_Q10, Lambda_Q10); + rd2_Q10 = Inlines.silk_SMULBB(-q2_Q10, Lambda_Q10); + } + int rr_Q10 = Inlines.silk_SUB32(r_Q10, q1_Q10); + rd1_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMLABB(rd1_Q10, rr_Q10, rr_Q10), 10); + rr_Q10 = Inlines.silk_SUB32(r_Q10, q2_Q10); + rd2_Q10 = Inlines.silk_RSHIFT(Inlines.silk_SMLABB(rd2_Q10, rr_Q10, rr_Q10), 10); + if (rd1_Q10 < rd2_Q10) { + sampleStates[SS_left].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd1_Q10); + sampleStates[SS_right].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd2_Q10); + sampleStates[SS_left].Q_Q10 = q1_Q10; + sampleStates[SS_right].Q_Q10 = q2_Q10; + } else { + sampleStates[SS_left].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd2_Q10); + sampleStates[SS_right].RD_Q10 = Inlines.silk_ADD32(psDD.RD_Q10, rd1_Q10); + sampleStates[SS_left].Q_Q10 = q2_Q10; + sampleStates[SS_right].Q_Q10 = q1_Q10; + } + int exc_Q14 = Inlines.silk_LSHIFT32(sampleStates[SS_left].Q_Q10, 4); + if (psDD.Seed < 0) { + exc_Q14 = -exc_Q14; + } + int LPC_exc_Q14 = Inlines.silk_ADD32(exc_Q14, LTP_pred_Q14); + int xq_Q14 = Inlines.silk_ADD32(LPC_exc_Q14, LPC_pred_Q14); + int sLF_AR_shp_Q14 = Inlines.silk_SUB32(xq_Q14, n_AR_Q14); + sampleStates[SS_left].sLTP_shp_Q14 = Inlines.silk_SUB32(sLF_AR_shp_Q14, n_LF_Q14); + sampleStates[SS_left].LF_AR_Q14 = sLF_AR_shp_Q14; + sampleStates[SS_left].LPC_exc_Q14 = LPC_exc_Q14; + sampleStates[SS_left].xq_Q14 = xq_Q14; + exc_Q14 = Inlines.silk_LSHIFT32(sampleStates[SS_right].Q_Q10, 4); + if (psDD.Seed < 0) { + exc_Q14 = -exc_Q14; + } + LPC_exc_Q14 = Inlines.silk_ADD32(exc_Q14, LTP_pred_Q14); + xq_Q14 = Inlines.silk_ADD32(LPC_exc_Q14, LPC_pred_Q14); + sLF_AR_shp_Q14 = Inlines.silk_SUB32(xq_Q14, n_AR_Q14); + sampleStates[SS_right].sLTP_shp_Q14 = Inlines.silk_SUB32(sLF_AR_shp_Q14, n_LF_Q14); + sampleStates[SS_right].LF_AR_Q14 = sLF_AR_shp_Q14; + sampleStates[SS_right].LPC_exc_Q14 = LPC_exc_Q14; + sampleStates[SS_right].xq_Q14 = xq_Q14; + } + smpl_buf_idx.Val = smpl_buf_idx.Val - 1 & 0x1F; + int last_smple_idx = smpl_buf_idx.Val + decisionDelay & 0x1F; + int RDmin_Q10 = sampleStates[0].RD_Q10; + int Winner_ind = 0; + for (k = 1; k < nStatesDelayedDecision; ++k) { + if (sampleStates[k * 2].RD_Q10 >= RDmin_Q10) continue; + RDmin_Q10 = sampleStates[k * 2].RD_Q10; + Winner_ind = k; + } + int Winner_rand_state = psDelDec[Winner_ind].RandState[last_smple_idx]; + for (k = 0; k < nStatesDelayedDecision; ++k) { + if (psDelDec[k].RandState[last_smple_idx] == Winner_rand_state) continue; + k2 = k * 2; + sampleStates[k2].RD_Q10 = Inlines.silk_ADD32(sampleStates[k2].RD_Q10, 0x7FFFFFF); + sampleStates[k2 + 1].RD_Q10 = Inlines.silk_ADD32(sampleStates[k2 + 1].RD_Q10, 0x7FFFFFF); + Inlines.OpusAssert(sampleStates[k2].RD_Q10 >= 0); + } + int RDmax_Q10 = sampleStates[0].RD_Q10; + RDmin_Q10 = sampleStates[1].RD_Q10; + int RDmax_ind = 0; + int RDmin_ind = 0; + for (k = 1; k < nStatesDelayedDecision; ++k) { + k2 = k * 2; + if (sampleStates[k2].RD_Q10 > RDmax_Q10) { + RDmax_Q10 = sampleStates[k2].RD_Q10; + RDmax_ind = k; + } + if (sampleStates[k2 + 1].RD_Q10 >= RDmin_Q10) continue; + RDmin_Q10 = sampleStates[k2 + 1].RD_Q10; + RDmin_ind = k; + } + if (RDmin_Q10 < RDmax_Q10) { + psDelDec[RDmax_ind].PartialCopyFrom(psDelDec[RDmin_ind], i); + sampleStates[RDmax_ind * 2].Assign(sampleStates[RDmin_ind * 2 + 1]); + } + psDD = psDelDec[Winner_ind]; + if (subfr > 0 || i >= decisionDelay) { + pulses[pulses_ptr + i - decisionDelay] = (byte)Inlines.silk_RSHIFT_ROUND(psDD.Q_Q10[last_smple_idx], 10); + xq[xq_ptr + i - decisionDelay] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(psDD.Xq_Q14[last_smple_idx], delayedGain_Q10[last_smple_idx]), 8)); + this.sLTP_shp_Q14[this.sLTP_shp_buf_idx - decisionDelay] = psDD.Shape_Q14[last_smple_idx]; + sLTP_Q15[this.sLTP_buf_idx - decisionDelay] = psDD.Pred_Q15[last_smple_idx]; + } + ++this.sLTP_shp_buf_idx; + ++this.sLTP_buf_idx; + for (k = 0; k < nStatesDelayedDecision; ++k) { + psDD = psDelDec[k]; + SS_left = k * 2; + psDD.LF_AR_Q14 = sampleStates[SS_left].LF_AR_Q14; + psDD.sLPC_Q14[SilkConstants.NSQ_LPC_BUF_LENGTH + i] = sampleStates[SS_left].xq_Q14; + psDD.Xq_Q14[smpl_buf_idx.Val] = sampleStates[SS_left].xq_Q14; + psDD.Q_Q10[smpl_buf_idx.Val] = sampleStates[SS_left].Q_Q10; + psDD.Pred_Q15[smpl_buf_idx.Val] = Inlines.silk_LSHIFT32(sampleStates[SS_left].LPC_exc_Q14, 1); + psDD.Shape_Q14[smpl_buf_idx.Val] = sampleStates[SS_left].sLTP_shp_Q14; + psDD.RandState[smpl_buf_idx.Val] = psDD.Seed = Inlines.silk_ADD32_ovflw(psDD.Seed, Inlines.silk_RSHIFT_ROUND(sampleStates[SS_left].Q_Q10, 10)); + psDD.RD_Q10 = sampleStates[SS_left].RD_Q10; + } + delayedGain_Q10[smpl_buf_idx.Val] = Gain_Q10; + } + for (k = 0; k < nStatesDelayedDecision; ++k) { + psDD = psDelDec[k]; + System.arraycopy(psDD.sLPC_Q14, length, psDD.sLPC_Q14, 0, SilkConstants.NSQ_LPC_BUF_LENGTH); + } + } + + private void silk_nsq_del_dec_scale_states(SilkChannelEncoder psEncC, NSQ_del_dec_struct[] psDelDec, int[] x_Q3, int x_Q3_ptr, int[] x_sc_Q10, short[] sLTP, int[] sLTP_Q15, int subfr, int nStatesDelayedDecision, int LTP_scale_Q14, int[] Gains_Q16, int[] pitchL, int signal_type, int decisionDelay) { + int i; + int lag = pitchL[subfr]; + int inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(Inlines.silk_max(Gains_Q16[subfr], 1), 47); + Inlines.OpusAssert(inv_gain_Q31 != 0); + int gain_adj_Q16 = Gains_Q16[subfr] != this.prev_gain_Q16 ? Inlines.silk_DIV32_varQ(this.prev_gain_Q16, Gains_Q16[subfr], 16) : 65536; + int inv_gain_Q23 = Inlines.silk_RSHIFT_ROUND(inv_gain_Q31, 8); + for (i = 0; i < psEncC.subfr_length; ++i) { + x_sc_Q10[i] = Inlines.silk_SMULWW(x_Q3[x_Q3_ptr + i], inv_gain_Q23); + } + this.prev_gain_Q16 = Gains_Q16[subfr]; + if (this.rewhite_flag != 0) { + if (subfr == 0) { + inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, LTP_scale_Q14), 2); + } + for (i = this.sLTP_buf_idx - lag - 2; i < this.sLTP_buf_idx; ++i) { + Inlines.OpusAssert(i < 320); + sLTP_Q15[i] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[i]); + } + } + if (gain_adj_Q16 != 65536) { + for (i = this.sLTP_shp_buf_idx - psEncC.ltp_mem_length; i < this.sLTP_shp_buf_idx; ++i) { + this.sLTP_shp_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, this.sLTP_shp_Q14[i]); + } + if (signal_type == 2 && this.rewhite_flag == 0) { + for (i = this.sLTP_buf_idx - lag - 2; i < this.sLTP_buf_idx - decisionDelay; ++i) { + sLTP_Q15[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[i]); + } + } + for (int k = 0; k < nStatesDelayedDecision; ++k) { + NSQ_del_dec_struct psDD = psDelDec[k]; + psDD.LF_AR_Q14 = Inlines.silk_SMULWW(gain_adj_Q16, psDD.LF_AR_Q14); + for (i = 0; i < SilkConstants.NSQ_LPC_BUF_LENGTH; ++i) { + psDD.sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.sLPC_Q14[i]); + } + for (i = 0; i < psEncC.shapingLPCOrder; ++i) { + psDD.sAR2_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.sAR2_Q14[i]); + } + for (i = 0; i < 32; ++i) { + psDD.Pred_Q15[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.Pred_Q15[i]); + psDD.Shape_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, psDD.Shape_Q14[i]); + } + } + } + } + + private class NSQ_del_dec_struct { + final int[] sLPC_Q14 = new int[80 + SilkConstants.NSQ_LPC_BUF_LENGTH]; + final int[] RandState = new int[32]; + final int[] Q_Q10 = new int[32]; + final int[] Xq_Q14 = new int[32]; + final int[] Pred_Q15 = new int[32]; + final int[] Shape_Q14 = new int[32]; + int[] sAR2_Q14; + int LF_AR_Q14 = 0; + int Seed = 0; + int SeedInit = 0; + int RD_Q10 = 0; + + NSQ_del_dec_struct(int shapingOrder) { + this.sAR2_Q14 = new int[shapingOrder]; + } + + void PartialCopyFrom(NSQ_del_dec_struct other, int q14Offset) { + System.arraycopy(other.sLPC_Q14, q14Offset, this.sLPC_Q14, q14Offset, 80 + SilkConstants.NSQ_LPC_BUF_LENGTH - q14Offset); + System.arraycopy(other.RandState, 0, this.RandState, 0, 32); + System.arraycopy(other.Q_Q10, 0, this.Q_Q10, 0, 32); + System.arraycopy(other.Xq_Q14, 0, this.Xq_Q14, 0, 32); + System.arraycopy(other.Pred_Q15, 0, this.Pred_Q15, 0, 32); + System.arraycopy(other.Shape_Q14, 0, this.Shape_Q14, 0, 32); + System.arraycopy(other.sAR2_Q14, 0, this.sAR2_Q14, 0, this.sAR2_Q14.length); + this.LF_AR_Q14 = other.LF_AR_Q14; + this.Seed = other.Seed; + this.SeedInit = other.SeedInit; + this.RD_Q10 = other.RD_Q10; + } + + void Assign(NSQ_del_dec_struct other) { + this.PartialCopyFrom(other, 0); + } + } + + private class NSQ_sample_struct { + int Q_Q10; + int RD_Q10; + int xq_Q14; + int LF_AR_Q14; + int sLTP_shp_Q14; + int LPC_exc_Q14; + + private NSQ_sample_struct() { + } + + void Assign(NSQ_sample_struct other) { + this.Q_Q10 = other.Q_Q10; + this.RD_Q10 = other.RD_Q10; + this.xq_Q14 = other.xq_Q14; + this.LF_AR_Q14 = other.LF_AR_Q14; + this.sLTP_shp_Q14 = other.sLTP_shp_Q14; + this.LPC_exc_Q14 = other.LPC_exc_Q14; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkPrefilterState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkPrefilterState.java new file mode 100644 index 0000000..917db81 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkPrefilterState.java @@ -0,0 +1,32 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class SilkPrefilterState { + final short[] sLTP_shp = new short[512]; + final int[] sAR_shp = new int[17]; + int sLTP_shp_buf_idx = 0; + int sLF_AR_shp_Q12 = 0; + int sLF_MA_shp_Q12 = 0; + int sHarmHP_Q2 = 0; + int rand_seed = 0; + int lagPrev = 0; + + SilkPrefilterState() { + } + + void Reset() { + Arrays.MemSet(this.sLTP_shp, (short)0, 512); + Arrays.MemSet(this.sAR_shp, 0, 17); + this.sLTP_shp_buf_idx = 0; + this.sLF_AR_shp_Q12 = 0; + this.sLF_MA_shp_Q12 = 0; + this.sHarmHP_Q2 = 0; + this.rand_seed = 0; + this.lagPrev = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkResamplerState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkResamplerState.java new file mode 100644 index 0000000..e82a127 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkResamplerState.java @@ -0,0 +1,58 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class SilkResamplerState { + final int[] sIIR = new int[6]; + final int[] sFIR_i32 = new int[36]; + final short[] sFIR_i16 = new short[36]; + final short[] delayBuf = new short[48]; + int resampler_function = 0; + int batchSize = 0; + int invRatio_Q16 = 0; + int FIR_Order = 0; + int FIR_Fracs = 0; + int Fs_in_kHz = 0; + int Fs_out_kHz = 0; + int inputDelay = 0; + short[] Coefs = null; + + SilkResamplerState() { + } + + void Reset() { + Arrays.MemSet(this.sIIR, 0, 6); + Arrays.MemSet(this.sFIR_i32, 0, 36); + Arrays.MemSet(this.sFIR_i16, (short)0, 36); + Arrays.MemSet(this.delayBuf, (short)0, 48); + this.resampler_function = 0; + this.batchSize = 0; + this.invRatio_Q16 = 0; + this.FIR_Order = 0; + this.FIR_Fracs = 0; + this.Fs_in_kHz = 0; + this.Fs_out_kHz = 0; + this.inputDelay = 0; + this.Coefs = null; + } + + void Assign(SilkResamplerState other) { + this.resampler_function = other.resampler_function; + this.batchSize = other.batchSize; + this.invRatio_Q16 = other.invRatio_Q16; + this.FIR_Order = other.FIR_Order; + this.FIR_Fracs = other.FIR_Fracs; + this.Fs_in_kHz = other.Fs_in_kHz; + this.Fs_out_kHz = other.Fs_out_kHz; + this.inputDelay = other.inputDelay; + this.Coefs = other.Coefs; + System.arraycopy(other.sIIR, 0, this.sIIR, 0, 6); + System.arraycopy(other.sFIR_i32, 0, this.sFIR_i32, 0, 36); + System.arraycopy(other.sFIR_i16, 0, this.sFIR_i16, 0, 36); + System.arraycopy(other.delayBuf, 0, this.delayBuf, 0, 48); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkShapeState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkShapeState.java new file mode 100644 index 0000000..90bccab --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkShapeState.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class SilkShapeState { + byte LastGainIndex = 0; + int HarmBoost_smth_Q16 = 0; + int HarmShapeGain_smth_Q16 = 0; + int Tilt_smth_Q16 = 0; + + SilkShapeState() { + } + + void Reset() { + this.LastGainIndex = 0; + this.HarmBoost_smth_Q16 = 0; + this.HarmShapeGain_smth_Q16 = 0; + this.Tilt_smth_Q16 = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkTables.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkTables.java new file mode 100644 index 0000000..8a67825 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkTables.java @@ -0,0 +1,211 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.NLSFCodebook; + +class SilkTables { + static final short[] silk_LSFCosTab_Q12 = new short[]{8192, 8190, 8182, 8170, 8152, 8130, 8104, 8072, 8034, 7994, 7946, 7896, 7840, 7778, 7714, 7644, 7568, 7490, 7406, 7318, 7226, 7128, 7026, 6922, 6812, 6698, 6580, 6458, 6332, 6204, 6070, 5934, 5792, 5648, 5502, 5352, 5198, 5040, 4880, 4718, 4552, 4382, 4212, 4038, 3862, 3684, 3502, 3320, 3136, 2948, 2760, 2570, 2378, 2186, 1990, 1794, 1598, 1400, 1202, 1002, 802, 602, 402, 202, 0, -202, -402, -602, -802, -1002, -1202, -1400, -1598, -1794, -1990, -2186, -2378, -2570, -2760, -2948, -3136, -3320, -3502, -3684, -3862, -4038, -4212, -4382, -4552, -4718, -4880, -5040, -5198, -5352, -5502, -5648, -5792, -5934, -6070, -6204, -6332, -6458, -6580, -6698, -6812, -6922, -7026, -7128, -7226, -7318, -7406, -7490, -7568, -7644, -7714, -7778, -7840, -7896, -7946, -7994, -8034, -8072, -8104, -8130, -8152, -8170, -8182, -8190, -8192}; + static final short[][] silk_gain_iCDF = new short[][]{{224, 112, 44, 15, 3, 2, 1, 0}, {254, 237, 192, 132, 70, 23, 4, 0}, {255, 252, 226, 155, 61, 11, 2, 0}}; + static final short[] silk_delta_gain_iCDF = new short[]{250, 245, 234, 203, 71, 50, 42, 38, 35, 33, 31, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + static final short[] silk_LTP_per_index_iCDF = new short[]{179, 99, 0}; + static final short[] silk_LTP_gain_iCDF_0 = new short[]{71, 56, 43, 30, 21, 12, 6, 0}; + static final short[] silk_LTP_gain_iCDF_1 = new short[]{199, 165, 144, 124, 109, 96, 84, 71, 61, 51, 42, 32, 23, 15, 8, 0}; + static final short[] silk_LTP_gain_iCDF_2 = new short[]{241, 225, 211, 199, 187, 175, 164, 153, 142, 132, 123, 114, 105, 96, 88, 80, 72, 64, 57, 50, 44, 38, 33, 29, 24, 20, 16, 12, 9, 5, 2, 0}; + static final short silk_LTP_gain_middle_avg_RD_Q14 = 12304; + static final short[] silk_LTP_gain_BITS_Q5_0 = new short[]{15, 131, 138, 138, 155, 155, 173, 173}; + static final short[] silk_LTP_gain_BITS_Q5_1 = new short[]{69, 93, 115, 118, 131, 138, 141, 138, 150, 150, 155, 150, 155, 160, 166, 160}; + static final short[] silk_LTP_gain_BITS_Q5_2 = new short[]{131, 128, 134, 141, 141, 141, 145, 145, 145, 150, 155, 155, 155, 155, 160, 160, 160, 160, 166, 166, 173, 173, 182, 192, 182, 192, 192, 192, 205, 192, 205, 224}; + static final short[][] silk_LTP_gain_iCDF_ptrs = new short[][]{silk_LTP_gain_iCDF_0, silk_LTP_gain_iCDF_1, silk_LTP_gain_iCDF_2}; + static final short[][] silk_LTP_gain_BITS_Q5_ptrs = new short[][]{silk_LTP_gain_BITS_Q5_0, silk_LTP_gain_BITS_Q5_1, silk_LTP_gain_BITS_Q5_2}; + static final byte[][] silk_LTP_gain_vq_0 = new byte[][]{{4, 6, 24, 7, 5}, {0, 0, 2, 0, 0}, {12, 28, 41, 13, -4}, {-9, 15, 42, 25, 14}, {1, -2, 62, 41, -9}, {-10, 37, 65, -4, 3}, {-6, 4, 66, 7, -8}, {16, 14, 38, -3, 33}}; + static final byte[][] silk_LTP_gain_vq_1 = new byte[][]{{13, 22, 39, 23, 12}, {-1, 36, 64, 27, -6}, {-7, 10, 55, 43, 17}, {1, 1, 8, 1, 1}, {6, -11, 74, 53, -9}, {-12, 55, 76, -12, 8}, {-3, 3, 93, 27, -4}, {26, 39, 59, 3, -8}, {2, 0, 77, 11, 9}, {-8, 22, 44, -6, 7}, {40, 9, 26, 3, 9}, {-7, 20, 101, -7, 4}, {3, -8, 42, 26, 0}, {-15, 33, 68, 2, 23}, {-2, 55, 46, -2, 15}, {3, -1, 21, 16, 41}}; + static final byte[][] silk_LTP_gain_vq_2 = new byte[][]{{-6, 27, 61, 39, 5}, {-11, 42, 88, 4, 1}, {-2, 60, 65, 6, -4}, {-1, -5, 73, 56, 1}, {-9, 19, 94, 29, -9}, {0, 12, 99, 6, 4}, {8, -19, 102, 46, -13}, {3, 2, 13, 3, 2}, {9, -21, 84, 72, -18}, {-11, 46, 104, -22, 8}, {18, 38, 48, 23, 0}, {-16, 70, 83, -21, 11}, {5, -11, 117, 22, -8}, {-6, 23, 117, -12, 3}, {3, -8, 95, 28, 4}, {-10, 15, 77, 60, -15}, {-1, 4, 124, 2, -4}, {3, 38, 84, 24, -25}, {2, 13, 42, 13, 31}, {21, -4, 56, 46, -1}, {-1, 35, 79, -13, 19}, {-7, 65, 88, -9, -14}, {20, 4, 81, 49, -29}, {20, 0, 75, 3, -17}, {5, -9, 44, 92, -8}, {1, -3, 22, 69, 31}, {-6, 95, 41, -12, 5}, {39, 67, 16, -4, 1}, {0, -6, 120, 55, -36}, {-13, 44, 122, 4, -24}, {81, 5, 11, 3, 7}, {2, 0, 9, 10, 88}}; + static final byte[][][] silk_LTP_vq_ptrs_Q7 = new byte[][][]{silk_LTP_gain_vq_0, silk_LTP_gain_vq_1, silk_LTP_gain_vq_2}; + static final short[] silk_LTP_gain_vq_0_gain = new short[]{46, 2, 90, 87, 93, 91, 82, 98}; + static final short[] silk_LTP_gain_vq_1_gain = new short[]{109, 120, 118, 12, 113, 115, 117, 119, 99, 59, 87, 111, 63, 111, 112, 80}; + static final short[] silk_LTP_gain_vq_2_gain = new short[]{126, 124, 125, 124, 129, 121, 126, 23, 132, 127, 127, 127, 126, 127, 122, 133, 130, 134, 101, 118, 119, 145, 126, 86, 124, 120, 123, 119, 170, 173, 107, 109}; + static final short[][] silk_LTP_vq_gain_ptrs_Q7 = new short[][]{silk_LTP_gain_vq_0_gain, silk_LTP_gain_vq_1_gain, silk_LTP_gain_vq_2_gain}; + static final byte[] silk_LTP_vq_sizes = new byte[]{8, 16, 32}; + static final short[] silk_NLSF_CB1_NB_MB_Q8 = new short[]{12, 35, 60, 83, 108, 132, 157, 180, 206, 228, 15, 32, 55, 77, 101, 125, 151, 175, 201, 225, 19, 42, 66, 89, 114, 137, 162, 184, 209, 230, 12, 25, 50, 72, 97, 120, 147, 172, 200, 223, 26, 44, 69, 90, 114, 135, 159, 180, 205, 225, 13, 22, 53, 80, 106, 130, 156, 180, 205, 228, 15, 25, 44, 64, 90, 115, 142, 168, 196, 222, 19, 24, 62, 82, 100, 120, 145, 168, 190, 214, 22, 31, 50, 79, 103, 120, 151, 170, 203, 227, 21, 29, 45, 65, 106, 124, 150, 171, 196, 224, 30, 49, 75, 97, 121, 142, 165, 186, 209, 229, 19, 25, 52, 70, 93, 116, 143, 166, 192, 219, 26, 34, 62, 75, 97, 118, 145, 167, 194, 217, 25, 33, 56, 70, 91, 113, 143, 165, 196, 223, 21, 34, 51, 72, 97, 117, 145, 171, 196, 222, 20, 29, 50, 67, 90, 117, 144, 168, 197, 221, 22, 31, 48, 66, 95, 117, 146, 168, 196, 222, 24, 33, 51, 77, 116, 134, 158, 180, 200, 224, 21, 28, 70, 87, 106, 124, 149, 170, 194, 217, 26, 33, 53, 64, 83, 117, 152, 173, 204, 225, 27, 34, 65, 95, 108, 129, 155, 174, 210, 225, 20, 26, 72, 99, 113, 131, 154, 176, 200, 219, 34, 43, 61, 78, 93, 114, 155, 177, 205, 229, 23, 29, 54, 97, 124, 138, 163, 179, 209, 229, 30, 38, 56, 89, 118, 129, 158, 178, 200, 231, 21, 29, 49, 63, 85, 111, 142, 163, 193, 222, 27, 48, 77, 103, 133, 158, 179, 196, 215, 232, 29, 47, 74, 99, 124, 151, 176, 198, 220, 237, 33, 42, 61, 76, 93, 121, 155, 174, 207, 225, 29, 53, 87, 112, 136, 154, 170, 188, 208, 227, 24, 30, 52, 84, 131, 150, 166, 186, 203, 229, 37, 48, 64, 84, 104, 118, 156, 177, 201, 230}; + static final short[] silk_NLSF_CB1_iCDF_NB_MB = new short[]{212, 178, 148, 129, 108, 96, 85, 82, 79, 77, 61, 59, 57, 56, 51, 49, 48, 45, 42, 41, 40, 38, 36, 34, 31, 30, 21, 12, 10, 3, 1, 0, 255, 245, 244, 236, 233, 225, 217, 203, 190, 176, 175, 161, 149, 136, 125, 114, 102, 91, 81, 71, 60, 52, 43, 35, 28, 20, 19, 18, 12, 11, 5, 0}; + static final short[] silk_NLSF_CB2_SELECT_NB_MB = new short[]{16, 0, 0, 0, 0, 99, 66, 36, 36, 34, 36, 34, 34, 34, 34, 83, 69, 36, 52, 34, 116, 102, 70, 68, 68, 176, 102, 68, 68, 34, 65, 85, 68, 84, 36, 116, 141, 152, 139, 170, 132, 187, 184, 216, 137, 132, 249, 168, 185, 139, 104, 102, 100, 68, 68, 178, 218, 185, 185, 170, 244, 216, 187, 187, 170, 244, 187, 187, 219, 138, 103, 155, 184, 185, 137, 116, 183, 155, 152, 136, 132, 217, 184, 184, 170, 164, 217, 171, 155, 139, 244, 169, 184, 185, 170, 164, 216, 223, 218, 138, 214, 143, 188, 218, 168, 244, 141, 136, 155, 170, 168, 138, 220, 219, 139, 164, 219, 202, 216, 137, 168, 186, 246, 185, 139, 116, 185, 219, 185, 138, 100, 100, 134, 100, 102, 34, 68, 68, 100, 68, 168, 203, 221, 218, 168, 167, 154, 136, 104, 70, 164, 246, 171, 137, 139, 137, 155, 218, 219, 139}; + static final short[] silk_NLSF_CB2_iCDF_NB_MB = new short[]{255, 254, 253, 238, 14, 3, 2, 1, 0, 255, 254, 252, 218, 35, 3, 2, 1, 0, 255, 254, 250, 208, 59, 4, 2, 1, 0, 255, 254, 246, 194, 71, 10, 2, 1, 0, 255, 252, 236, 183, 82, 8, 2, 1, 0, 255, 252, 235, 180, 90, 17, 2, 1, 0, 255, 248, 224, 171, 97, 30, 4, 1, 0, 255, 254, 236, 173, 95, 37, 7, 1, 0}; + static final short[] silk_NLSF_CB2_BITS_NB_MB_Q5 = new short[]{255, 255, 255, 131, 6, 145, 255, 255, 255, 255, 255, 236, 93, 15, 96, 255, 255, 255, 255, 255, 194, 83, 25, 71, 221, 255, 255, 255, 255, 162, 73, 34, 66, 162, 255, 255, 255, 210, 126, 73, 43, 57, 173, 255, 255, 255, 201, 125, 71, 48, 58, 130, 255, 255, 255, 166, 110, 73, 57, 62, 104, 210, 255, 255, 251, 123, 65, 55, 68, 100, 171, 255}; + static final short[] silk_NLSF_PRED_NB_MB_Q8 = new short[]{179, 138, 140, 148, 151, 149, 153, 151, 163, 116, 67, 82, 59, 92, 72, 100, 89, 92}; + static final short[] silk_NLSF_DELTA_MIN_NB_MB_Q15 = new short[]{250, 3, 6, 3, 3, 3, 4, 3, 3, 3, 461}; + static final NLSFCodebook silk_NLSF_CB_NB_MB = new NLSFCodebook(); + static final short[] silk_NLSF_CB1_WB_Q8; + static final short[] silk_NLSF_CB1_iCDF_WB; + static final short[] silk_NLSF_CB2_SELECT_WB; + static final short[] silk_NLSF_CB2_iCDF_WB; + static final short[] silk_NLSF_CB2_BITS_WB_Q5; + static final short[] silk_NLSF_PRED_WB_Q8; + static final short[] silk_NLSF_DELTA_MIN_WB_Q15; + static final NLSFCodebook silk_NLSF_CB_WB; + static final int[] silk_TargetRate_table_NB; + static final int[] silk_TargetRate_table_MB; + static final int[] silk_TargetRate_table_WB; + static final short[] silk_SNR_table_Q1; + static final short[] silk_stereo_pred_quant_Q13; + static final short[] silk_stereo_pred_joint_iCDF; + static final short[] silk_stereo_only_code_mid_iCDF; + static final short[] silk_LBRR_flags_2_iCDF; + static final short[] silk_LBRR_flags_3_iCDF; + static final short[][] silk_LBRR_flags_iCDF_ptr; + static final short[] silk_lsb_iCDF; + static final short[] silk_LTPscale_iCDF; + static final short[] silk_type_offset_VAD_iCDF; + static final short[] silk_type_offset_no_VAD_iCDF; + static final short[] silk_NLSF_interpolation_factor_iCDF; + static final short[][] silk_Quantization_Offsets_Q10; + static final short[] silk_LTPScales_table_Q14; + static final short[] silk_uniform3_iCDF; + static final short[] silk_uniform4_iCDF; + static final short[] silk_uniform5_iCDF; + static final short[] silk_uniform6_iCDF; + static final short[] silk_uniform8_iCDF; + static final short[] silk_NLSF_EXT_iCDF; + static final int[][] silk_Transition_LP_B_Q28; + static final int[][] silk_Transition_LP_A_Q28; + static final short[] silk_pitch_lag_iCDF; + static final short[] silk_pitch_delta_iCDF; + static final short[] silk_pitch_contour_iCDF; + static final short[] silk_pitch_contour_NB_iCDF; + static final short[] silk_pitch_contour_10_ms_iCDF; + static final short[] silk_pitch_contour_10_ms_NB_iCDF; + static final byte[] silk_max_pulses_table; + static final short[][] silk_pulses_per_block_iCDF; + static final short[][] silk_pulses_per_block_BITS_Q5; + static final short[][] silk_rate_levels_iCDF; + static final short[][] silk_rate_levels_BITS_Q5; + static final short[] silk_shell_code_table0; + static final short[] silk_shell_code_table1; + static final short[] silk_shell_code_table2; + static final short[] silk_shell_code_table3; + static final short[] silk_shell_code_table_offsets; + static final short[] silk_sign_iCDF; + static final byte[][] delay_matrix_enc; + static final byte[][] delay_matrix_dec; + static final short[] silk_Resampler_3_4_COEFS; + static final short[] silk_Resampler_2_3_COEFS; + static final short[] silk_Resampler_1_2_COEFS; + static final short[] silk_Resampler_1_3_COEFS; + static final short[] silk_Resampler_1_4_COEFS; + static final short[] silk_Resampler_1_6_COEFS; + static final short[] silk_Resampler_2_3_COEFS_LQ; + static final short[][] silk_resampler_frac_FIR_12; + public static final short silk_resampler_down2_0 = 9872; + public static final short silk_resampler_down2_1 = -25727; + static final short[] silk_resampler_up2_hq_0; + static final short[] silk_resampler_up2_hq_1; + static final byte[][] silk_CB_lags_stage2_10_ms; + static final byte[][] silk_CB_lags_stage3_10_ms; + static final byte[][] silk_CB_lags_stage2; + static final byte[][] silk_CB_lags_stage3; + static final byte[][] silk_Lag_range_stage3_10_ms; + static final byte[][][] silk_Lag_range_stage3; + static final byte[] silk_nb_cbk_searchs_stage3; + + SilkTables() { + } + + /* + * Opcode count of 17815 triggered aggressive code reduction. Override with --aggressivesizethreshold. + */ + static { + SilkTables.silk_NLSF_CB_NB_MB.nVectors = (short)32; + SilkTables.silk_NLSF_CB_NB_MB.order = (short)10; + SilkTables.silk_NLSF_CB_NB_MB.quantStepSize_Q16 = (short)11796; + SilkTables.silk_NLSF_CB_NB_MB.invQuantStepSize_Q6 = (short)356; + SilkTables.silk_NLSF_CB_NB_MB.CB1_NLSF_Q8 = silk_NLSF_CB1_NB_MB_Q8; + SilkTables.silk_NLSF_CB_NB_MB.CB1_iCDF = silk_NLSF_CB1_iCDF_NB_MB; + SilkTables.silk_NLSF_CB_NB_MB.pred_Q8 = silk_NLSF_PRED_NB_MB_Q8; + SilkTables.silk_NLSF_CB_NB_MB.ec_sel = silk_NLSF_CB2_SELECT_NB_MB; + SilkTables.silk_NLSF_CB_NB_MB.ec_iCDF = silk_NLSF_CB2_iCDF_NB_MB; + SilkTables.silk_NLSF_CB_NB_MB.ec_Rates_Q5 = silk_NLSF_CB2_BITS_NB_MB_Q5; + SilkTables.silk_NLSF_CB_NB_MB.deltaMin_Q15 = silk_NLSF_DELTA_MIN_NB_MB_Q15; + silk_NLSF_CB1_WB_Q8 = new short[]{7, 23, 38, 54, 69, 85, 100, 116, 131, 147, 162, 178, 193, 208, 223, 239, 13, 25, 41, 55, 69, 83, 98, 112, 127, 142, 157, 171, 187, 203, 220, 236, 15, 21, 34, 51, 61, 78, 92, 106, 126, 136, 152, 167, 185, 205, 225, 240, 10, 21, 36, 50, 63, 79, 95, 110, 126, 141, 157, 173, 189, 205, 221, 237, 17, 20, 37, 51, 59, 78, 89, 107, 123, 134, 150, 164, 184, 205, 224, 240, 10, 15, 32, 51, 67, 81, 96, 112, 129, 142, 158, 173, 189, 204, 220, 236, 8, 21, 37, 51, 65, 79, 98, 113, 126, 138, 155, 168, 179, 192, 209, 218, 12, 15, 34, 55, 63, 78, 87, 108, 118, 131, 148, 167, 185, 203, 219, 236, 16, 19, 32, 36, 56, 79, 91, 108, 118, 136, 154, 171, 186, 204, 220, 237, 11, 28, 43, 58, 74, 89, 105, 120, 135, 150, 165, 180, 196, 211, 226, 241, 6, 16, 33, 46, 60, 75, 92, 107, 123, 137, 156, 169, 185, 199, 214, 225, 11, 19, 30, 44, 57, 74, 89, 105, 121, 135, 152, 169, 186, 202, 218, 234, 12, 19, 29, 46, 57, 71, 88, 100, 120, 132, 148, 165, 182, 199, 216, 233, 17, 23, 35, 46, 56, 77, 92, 106, 123, 134, 152, 167, 185, 204, 222, 237, 14, 17, 45, 53, 63, 75, 89, 107, 115, 132, 151, 171, 188, 206, 221, 240, 9, 16, 29, 40, 56, 71, 88, 103, 119, 137, 154, 171, 189, 205, 222, 237, 16, 19, 36, 48, 57, 76, 87, 105, 118, 132, 150, 167, 185, 202, 218, 236, 12, 17, 29, 54, 71, 81, 94, 104, 126, 136, 149, 164, 182, 201, 221, 237, 15, 28, 47, 62, 79, 97, 115, 129, 142, 155, 168, 180, 194, 208, 223, 238, 8, 14, 30, 45, 62, 78, 94, 111, 127, 143, 159, 175, 192, 207, 223, 239, 17, 30, 49, 62, 79, 92, 107, 119, 132, 145, 160, 174, 190, 204, 220, 235, 14, 19, 36, 45, 61, 76, 91, 108, 121, 138, 154, 172, 189, 205, 222, 238, 12, 18, 31, 45, 60, 76, 91, 107, 123, 138, 154, 171, 187, 204, 221, 236, 13, 17, 31, 43, 53, 70, 83, 103, 114, 131, 149, 167, 185, 203, 220, 237, 17, 22, 35, 42, 58, 78, 93, 110, 125, 139, 155, 170, 188, 206, 224, 240, 8, 15, 34, 50, 67, 83, 99, 115, 131, 146, 162, 178, 193, 209, 224, 239, 13, 16, 41, 66, 73, 86, 95, 111, 128, 137, 150, 163, 183, 206, 225, 241, 17, 25, 37, 52, 63, 75, 92, 102, 119, 132, 144, 160, 175, 191, 212, 231, 19, 31, 49, 65, 83, 100, 117, 133, 147, 161, 174, 187, 200, 213, 227, 242, 18, 31, 52, 68, 88, 103, 117, 126, 138, 149, 163, 177, 192, 207, 223, 239, 16, 29, 47, 61, 76, 90, 106, 119, 133, 147, 161, 176, 193, 209, 224, 240, 15, 21, 35, 50, 61, 73, 86, 97, 110, 119, 129, 141, 175, 198, 218, 237}; + silk_NLSF_CB1_iCDF_WB = new short[]{225, 204, 201, 184, 183, 175, 158, 154, 153, 135, 119, 115, 113, 110, 109, 99, 98, 95, 79, 68, 52, 50, 48, 45, 43, 32, 31, 27, 18, 10, 3, 0, 255, 251, 235, 230, 212, 201, 196, 182, 167, 166, 163, 151, 138, 124, 110, 104, 90, 78, 76, 70, 69, 57, 45, 34, 24, 21, 11, 6, 5, 4, 3, 0}; + silk_NLSF_CB2_SELECT_WB = new short[]{0, 0, 0, 0, 0, 0, 0, 1, 100, 102, 102, 68, 68, 36, 34, 96, 164, 107, 158, 185, 180, 185, 139, 102, 64, 66, 36, 34, 34, 0, 1, 32, 208, 139, 141, 191, 152, 185, 155, 104, 96, 171, 104, 166, 102, 102, 102, 132, 1, 0, 0, 0, 0, 16, 16, 0, 80, 109, 78, 107, 185, 139, 103, 101, 208, 212, 141, 139, 173, 153, 123, 103, 36, 0, 0, 0, 0, 0, 0, 1, 48, 0, 0, 0, 0, 0, 0, 32, 68, 135, 123, 119, 119, 103, 69, 98, 68, 103, 120, 118, 118, 102, 71, 98, 134, 136, 157, 184, 182, 153, 139, 134, 208, 168, 248, 75, 189, 143, 121, 107, 32, 49, 34, 34, 34, 0, 17, 2, 210, 235, 139, 123, 185, 137, 105, 134, 98, 135, 104, 182, 100, 183, 171, 134, 100, 70, 68, 70, 66, 66, 34, 131, 64, 166, 102, 68, 36, 2, 1, 0, 134, 166, 102, 68, 34, 34, 66, 132, 212, 246, 158, 139, 107, 107, 87, 102, 100, 219, 125, 122, 137, 118, 103, 132, 114, 135, 137, 105, 171, 106, 50, 34, 164, 214, 141, 143, 185, 151, 121, 103, 192, 34, 0, 0, 0, 0, 0, 1, 208, 109, 74, 187, 134, 249, 159, 137, 102, 110, 154, 118, 87, 101, 119, 101, 0, 2, 0, 36, 36, 66, 68, 35, 96, 164, 102, 100, 36, 0, 2, 33, 167, 138, 174, 102, 100, 84, 2, 2, 100, 107, 120, 119, 36, 197, 24, 0}; + silk_NLSF_CB2_iCDF_WB = new short[]{255, 254, 253, 244, 12, 3, 2, 1, 0, 255, 254, 252, 224, 38, 3, 2, 1, 0, 255, 254, 251, 209, 57, 4, 2, 1, 0, 255, 254, 244, 195, 69, 4, 2, 1, 0, 255, 251, 232, 184, 84, 7, 2, 1, 0, 255, 254, 240, 186, 86, 14, 2, 1, 0, 255, 254, 239, 178, 91, 30, 5, 1, 0, 255, 248, 227, 177, 100, 19, 2, 1, 0}; + silk_NLSF_CB2_BITS_WB_Q5 = new short[]{255, 255, 255, 156, 4, 154, 255, 255, 255, 255, 255, 227, 102, 15, 92, 255, 255, 255, 255, 255, 213, 83, 24, 72, 236, 255, 255, 255, 255, 150, 76, 33, 63, 214, 255, 255, 255, 190, 121, 77, 43, 55, 185, 255, 255, 255, 245, 137, 71, 43, 59, 139, 255, 255, 255, 255, 131, 66, 50, 66, 107, 194, 255, 255, 166, 116, 76, 55, 53, 125, 255, 255}; + silk_NLSF_PRED_WB_Q8 = new short[]{175, 148, 160, 176, 178, 173, 174, 164, 177, 174, 196, 182, 198, 192, 182, 68, 62, 66, 60, 72, 117, 85, 90, 118, 136, 151, 142, 160, 142, 155}; + silk_NLSF_DELTA_MIN_WB_Q15 = new short[]{100, 3, 40, 3, 3, 3, 5, 14, 14, 10, 11, 3, 8, 9, 7, 3, 347}; + silk_NLSF_CB_WB = new NLSFCodebook(); + SilkTables.silk_NLSF_CB_WB.nVectors = (short)32; + SilkTables.silk_NLSF_CB_WB.order = (short)16; + SilkTables.silk_NLSF_CB_WB.quantStepSize_Q16 = (short)9830; + SilkTables.silk_NLSF_CB_WB.invQuantStepSize_Q6 = (short)427; + SilkTables.silk_NLSF_CB_WB.CB1_NLSF_Q8 = silk_NLSF_CB1_WB_Q8; + SilkTables.silk_NLSF_CB_WB.CB1_iCDF = silk_NLSF_CB1_iCDF_WB; + SilkTables.silk_NLSF_CB_WB.pred_Q8 = silk_NLSF_PRED_WB_Q8; + SilkTables.silk_NLSF_CB_WB.ec_sel = silk_NLSF_CB2_SELECT_WB; + SilkTables.silk_NLSF_CB_WB.ec_iCDF = silk_NLSF_CB2_iCDF_WB; + SilkTables.silk_NLSF_CB_WB.ec_Rates_Q5 = silk_NLSF_CB2_BITS_WB_Q5; + SilkTables.silk_NLSF_CB_WB.deltaMin_Q15 = silk_NLSF_DELTA_MIN_WB_Q15; + silk_TargetRate_table_NB = new int[]{0, 8000, 9400, 11500, 13500, 17500, 25000, 80000}; + silk_TargetRate_table_MB = new int[]{0, 9000, 12000, 14500, 18500, 24500, 35500, 80000}; + silk_TargetRate_table_WB = new int[]{0, 10500, 14000, 17000, 21500, 28500, 42000, 80000}; + silk_SNR_table_Q1 = new short[]{18, 29, 38, 40, 46, 52, 62, 84}; + silk_stereo_pred_quant_Q13 = new short[]{-13732, -10050, -8266, -7526, -6500, -5000, -2950, -820, 820, 2950, 5000, 6500, 7526, 8266, 10050, 13732}; + silk_stereo_pred_joint_iCDF = new short[]{249, 247, 246, 245, 244, 234, 210, 202, 201, 200, 197, 174, 82, 59, 56, 55, 54, 46, 22, 12, 11, 10, 9, 7, 0}; + silk_stereo_only_code_mid_iCDF = new short[]{64, 0}; + silk_LBRR_flags_2_iCDF = new short[]{203, 150, 0}; + silk_LBRR_flags_3_iCDF = new short[]{215, 195, 166, 125, 110, 82, 0}; + silk_LBRR_flags_iCDF_ptr = new short[][]{silk_LBRR_flags_2_iCDF, silk_LBRR_flags_3_iCDF}; + silk_lsb_iCDF = new short[]{120, 0}; + silk_LTPscale_iCDF = new short[]{128, 64, 0}; + silk_type_offset_VAD_iCDF = new short[]{232, 158, 10, 0}; + silk_type_offset_no_VAD_iCDF = new short[]{230, 0}; + silk_NLSF_interpolation_factor_iCDF = new short[]{243, 221, 192, 181, 0}; + silk_Quantization_Offsets_Q10 = new short[][]{{100, 240}, {32, 100}}; + silk_LTPScales_table_Q14 = new short[]{15565, 12288, 8192}; + silk_uniform3_iCDF = new short[]{171, 85, 0}; + silk_uniform4_iCDF = new short[]{192, 128, 64, 0}; + silk_uniform5_iCDF = new short[]{205, 154, 102, 51, 0}; + silk_uniform6_iCDF = new short[]{213, 171, 128, 85, 43, 0}; + silk_uniform8_iCDF = new short[]{224, 192, 160, 128, 96, 64, 32, 0}; + silk_NLSF_EXT_iCDF = new short[]{100, 40, 16, 7, 3, 1, 0}; + silk_Transition_LP_B_Q28 = new int[][]{{250767114, 501534038, 250767114}, {209867381, 419732057, 209867381}, {170987846, 341967853, 170987846}, {131531482, 263046905, 131531482}, {89306658, 178584282, 89306658}}; + silk_Transition_LP_A_Q28 = new int[][]{{506393414, 239854379}, {411067935, 169683996}, {306733530, 116694253}, {185807084, 77959395}, {35497197, 57401098}}; + silk_pitch_lag_iCDF = new short[]{253, 250, 244, 233, 212, 182, 150, 131, 120, 110, 98, 85, 72, 60, 49, 40, 32, 25, 19, 15, 13, 11, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; + silk_pitch_delta_iCDF = new short[]{210, 208, 206, 203, 199, 193, 183, 168, 142, 104, 74, 52, 37, 27, 20, 14, 10, 6, 4, 2, 0}; + silk_pitch_contour_iCDF = new short[]{223, 201, 183, 167, 152, 138, 124, 111, 98, 88, 79, 70, 62, 56, 50, 44, 39, 35, 31, 27, 24, 21, 18, 16, 14, 12, 10, 8, 6, 4, 3, 2, 1, 0}; + silk_pitch_contour_NB_iCDF = new short[]{188, 176, 155, 138, 119, 97, 67, 43, 26, 10, 0}; + silk_pitch_contour_10_ms_iCDF = new short[]{165, 119, 80, 61, 47, 35, 27, 20, 14, 9, 4, 0}; + silk_pitch_contour_10_ms_NB_iCDF = new short[]{113, 63, 0}; + silk_max_pulses_table = new byte[]{8, 10, 12, 16}; + silk_pulses_per_block_iCDF = new short[][]{{125, 51, 26, 18, 15, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, {198, 105, 45, 22, 15, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, {213, 162, 116, 83, 59, 43, 32, 24, 18, 15, 12, 9, 7, 6, 5, 3, 2, 0}, {239, 187, 116, 59, 28, 16, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}, {250, 229, 188, 135, 86, 51, 30, 19, 13, 10, 8, 6, 5, 4, 3, 2, 1, 0}, {249, 235, 213, 185, 156, 128, 103, 83, 66, 53, 42, 33, 26, 21, 17, 13, 10, 0}, {254, 249, 235, 206, 164, 118, 77, 46, 27, 16, 10, 7, 5, 4, 3, 2, 1, 0}, {255, 253, 249, 239, 220, 191, 156, 119, 85, 57, 37, 23, 15, 10, 6, 4, 2, 0}, {255, 253, 251, 246, 237, 223, 203, 179, 152, 124, 98, 75, 55, 40, 29, 21, 15, 0}, {255, 254, 253, 247, 220, 162, 106, 67, 42, 28, 18, 12, 9, 6, 4, 3, 2, 0}}; + silk_pulses_per_block_BITS_Q5 = new short[][]{{31, 57, 107, 160, 205, 205, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, {69, 47, 67, 111, 166, 205, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, {82, 74, 79, 95, 109, 128, 145, 160, 173, 205, 205, 205, 224, 255, 255, 224, 255, 224}, {125, 74, 59, 69, 97, 141, 182, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}, {173, 115, 85, 73, 76, 92, 115, 145, 173, 205, 224, 224, 255, 255, 255, 255, 255, 255}, {166, 134, 113, 102, 101, 102, 107, 118, 125, 138, 145, 155, 166, 182, 192, 192, 205, 150}, {224, 182, 134, 101, 83, 79, 85, 97, 120, 145, 173, 205, 224, 255, 255, 255, 255, 255}, {255, 224, 192, 150, 120, 101, 92, 89, 93, 102, 118, 134, 160, 182, 192, 224, 224, 224}, {255, 224, 224, 182, 155, 134, 118, 109, 104, 102, 106, 111, 118, 131, 145, 160, 173, 131}}; + silk_rate_levels_iCDF = new short[][]{{241, 190, 178, 132, 87, 74, 41, 14, 0}, {223, 193, 157, 140, 106, 57, 39, 18, 0}}; + silk_rate_levels_BITS_Q5 = new short[][]{{131, 74, 141, 79, 80, 138, 95, 104, 134}, {95, 99, 91, 125, 93, 76, 123, 115, 123}}; + silk_shell_code_table0 = new short[]{128, 0, 214, 42, 0, 235, 128, 21, 0, 244, 184, 72, 11, 0, 248, 214, 128, 42, 7, 0, 248, 225, 170, 80, 25, 5, 0, 251, 236, 198, 126, 54, 18, 3, 0, 250, 238, 211, 159, 82, 35, 15, 5, 0, 250, 231, 203, 168, 128, 88, 53, 25, 6, 0, 252, 238, 216, 185, 148, 108, 71, 40, 18, 4, 0, 253, 243, 225, 199, 166, 128, 90, 57, 31, 13, 3, 0, 254, 246, 233, 212, 183, 147, 109, 73, 44, 23, 10, 2, 0, 255, 250, 240, 223, 198, 166, 128, 90, 58, 33, 16, 6, 1, 0, 255, 251, 244, 231, 210, 181, 146, 110, 75, 46, 25, 12, 5, 1, 0, 255, 253, 248, 238, 221, 196, 164, 128, 92, 60, 35, 18, 8, 3, 1, 0, 255, 253, 249, 242, 229, 208, 180, 146, 110, 76, 48, 27, 14, 7, 3, 1, 0}; + silk_shell_code_table1 = new short[]{129, 0, 207, 50, 0, 236, 129, 20, 0, 245, 185, 72, 10, 0, 249, 213, 129, 42, 6, 0, 250, 226, 169, 87, 27, 4, 0, 251, 233, 194, 130, 62, 20, 4, 0, 250, 236, 207, 160, 99, 47, 17, 3, 0, 255, 240, 217, 182, 131, 81, 41, 11, 1, 0, 255, 254, 233, 201, 159, 107, 61, 20, 2, 1, 0, 255, 249, 233, 206, 170, 128, 86, 50, 23, 7, 1, 0, 255, 250, 238, 217, 186, 148, 108, 70, 39, 18, 6, 1, 0, 255, 252, 243, 226, 200, 166, 128, 90, 56, 30, 13, 4, 1, 0, 255, 252, 245, 231, 209, 180, 146, 110, 76, 47, 25, 11, 4, 1, 0, 255, 253, 248, 237, 219, 194, 163, 128, 93, 62, 37, 19, 8, 3, 1, 0, 255, 254, 250, 241, 226, 205, 177, 145, 111, 79, 51, 30, 15, 6, 2, 1, 0}; + silk_shell_code_table2 = new short[]{129, 0, 203, 54, 0, 234, 129, 23, 0, 245, 184, 73, 10, 0, 250, 215, 129, 41, 5, 0, 252, 232, 173, 86, 24, 3, 0, 253, 240, 200, 129, 56, 15, 2, 0, 253, 244, 217, 164, 94, 38, 10, 1, 0, 253, 245, 226, 189, 132, 71, 27, 7, 1, 0, 253, 246, 231, 203, 159, 105, 56, 23, 6, 1, 0, 255, 248, 235, 213, 179, 133, 85, 47, 19, 5, 1, 0, 255, 254, 243, 221, 194, 159, 117, 70, 37, 12, 2, 1, 0, 255, 254, 248, 234, 208, 171, 128, 85, 48, 22, 8, 2, 1, 0, 255, 254, 250, 240, 220, 189, 149, 107, 67, 36, 16, 6, 2, 1, 0, 255, 254, 251, 243, 227, 201, 166, 128, 90, 55, 29, 13, 5, 2, 1, 0, 255, 254, 252, 246, 234, 213, 183, 147, 109, 73, 43, 22, 10, 4, 2, 1, 0}; + silk_shell_code_table3 = new short[]{130, 0, 200, 58, 0, 231, 130, 26, 0, 244, 184, 76, 12, 0, 249, 214, 130, 43, 6, 0, 252, 232, 173, 87, 24, 3, 0, 253, 241, 203, 131, 56, 14, 2, 0, 254, 246, 221, 167, 94, 35, 8, 1, 0, 254, 249, 232, 193, 130, 65, 23, 5, 1, 0, 255, 251, 239, 211, 162, 99, 45, 15, 4, 1, 0, 255, 251, 243, 223, 186, 131, 74, 33, 11, 3, 1, 0, 255, 252, 245, 230, 202, 158, 105, 57, 24, 8, 2, 1, 0, 255, 253, 247, 235, 214, 179, 132, 84, 44, 19, 7, 2, 1, 0, 255, 254, 250, 240, 223, 196, 159, 112, 69, 36, 15, 6, 2, 1, 0, 255, 254, 253, 245, 231, 209, 176, 136, 93, 55, 27, 11, 3, 2, 1, 0, 255, 254, 253, 252, 239, 221, 194, 158, 117, 76, 42, 18, 4, 3, 2, 1, 0}; + silk_shell_code_table_offsets = new short[]{0, 0, 2, 5, 9, 14, 20, 27, 35, 44, 54, 65, 77, 90, 104, 119, 135}; + silk_sign_iCDF = new short[]{254, 49, 67, 77, 82, 93, 99, 198, 11, 18, 24, 31, 36, 45, 255, 46, 66, 78, 87, 94, 104, 208, 14, 21, 32, 42, 51, 66, 255, 94, 104, 109, 112, 115, 118, 248, 53, 69, 80, 88, 95, 102}; + delay_matrix_enc = new byte[][]{{6, 0, 3}, {0, 7, 3}, {0, 1, 10}, {0, 2, 6}, {18, 10, 12}}; + delay_matrix_dec = new byte[][]{{4, 0, 2, 0, 0}, {0, 9, 4, 7, 4}, {0, 3, 12, 7, 7}}; + silk_Resampler_3_4_COEFS = new short[]{-20694, -13867, -49, 64, 17, -157, 353, -496, 163, 11047, 22205, -39, 6, 91, -170, 186, 23, -896, 6336, 19928, -19, -36, 102, -89, -24, 328, -951, 2568, 15909}; + silk_Resampler_2_3_COEFS = new short[]{-14457, -14019, 64, 128, -122, 36, 310, -768, 584, 9267, 17733, 12, 128, 18, -142, 288, -117, -865, 4123, 14459}; + silk_Resampler_1_2_COEFS = new short[]{616, -14323, -10, 39, 58, -46, -84, 120, 184, -315, -541, 1284, 5380, 9024}; + silk_Resampler_1_3_COEFS = new short[]{16102, -15162, -13, 0, 20, 26, 5, -31, -43, -4, 65, 90, 7, -157, -248, -44, 593, 1583, 2612, 3271}; + silk_Resampler_1_4_COEFS = new short[]{22500, -15099, 3, -14, -20, -15, 2, 25, 37, 25, -16, -71, -107, -79, 50, 292, 623, 982, 1288, 1464}; + silk_Resampler_1_6_COEFS = new short[]{27540, -15257, 17, 12, 8, 1, -10, -22, -30, -32, -22, 3, 44, 100, 168, 243, 317, 381, 429, 455}; + silk_Resampler_2_3_COEFS_LQ = new short[]{-2797, -6507, 4697, 10739, 1567, 8276}; + silk_resampler_frac_FIR_12 = new short[][]{{189, -600, 617, 30567}, {117, -159, -1070, 29704}, {52, 221, -2392, 28276}, {-4, 529, -3350, 26341}, {-48, 758, -3956, 23973}, {-80, 905, -4235, 21254}, {-99, 972, -4222, 18278}, {-107, 967, -3957, 15143}, {-103, 896, -3487, 11950}, {-91, 773, -2865, 8798}, {-71, 611, -2143, 5784}, {-46, 425, -1375, 2996}}; + silk_resampler_up2_hq_0 = new short[]{1746, 14986, -26453}; + silk_resampler_up2_hq_1 = new short[]{6854, 25769, -9994}; + silk_CB_lags_stage2_10_ms = new byte[][]{{0, 1, 0}, {0, 0, 1}}; + silk_CB_lags_stage3_10_ms = new byte[][]{{0, 0, 1, -1, 1, -1, 2, -2, 2, -2, 3, -3}, {0, 1, 0, 1, -1, 2, -1, 2, -2, 3, -2, 3}}; + silk_CB_lags_stage2 = new byte[][]{{0, 2, -1, -1, -1, 0, 0, 1, 1, 0, 1}, {0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0}, {0, -1, 2, 1, 0, 1, 1, 0, 0, -1, -1}}; + silk_CB_lags_stage3 = new byte[][]{{0, 0, 1, -1, 0, 1, -1, 0, -1, 1, -2, 2, -2, -2, 2, -3, 2, 3, -3, -4, 3, -4, 4, 4, -5, 5, -6, -5, 6, -7, 6, 5, 8, -9}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 1, -1, 0, 1, -1, -1, 1, -1, 2, 1, -1, 2, -2, -2, 2, -2, 2, 2, 3, -3}, {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, -1, 1, 0, 0, 2, 1, -1, 2, -1, -1, 2, -1, 2, 2, -1, 3, -2, -2, -2, 3}, {0, 1, 0, 0, 1, 0, 1, -1, 2, -1, 2, -1, 2, 3, -2, 3, -2, -2, 4, 4, -3, 5, -3, -4, 6, -4, 6, 5, -5, 8, -6, -5, -7, 9}}; + silk_Lag_range_stage3_10_ms = new byte[][]{{-3, 7}, {-2, 7}}; + silk_Lag_range_stage3 = new byte[][][]{new byte[][]{{-5, 8}, {-1, 6}, {-1, 6}, {-4, 10}}, new byte[][]{{-6, 10}, {-2, 6}, {-1, 6}, {-5, 10}}, new byte[][]{{-9, 12}, {-3, 7}, {-2, 7}, {-7, 13}}}; + silk_nb_cbk_searchs_stage3 = new byte[]{16, 24, 34}; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SilkVADState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkVADState.java new file mode 100644 index 0000000..fe6a773 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SilkVADState.java @@ -0,0 +1,36 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class SilkVADState { + final int[] AnaState = new int[2]; + final int[] AnaState1 = new int[2]; + final int[] AnaState2 = new int[2]; + final int[] XnrgSubfr = new int[4]; + final int[] NrgRatioSmth_Q8 = new int[4]; + short HPstate = 0; + final int[] NL = new int[4]; + final int[] inv_NL = new int[4]; + final int[] NoiseLevelBias = new int[4]; + int counter = 0; + + SilkVADState() { + } + + void Reset() { + Arrays.MemSet(this.AnaState, 0, 2); + Arrays.MemSet(this.AnaState1, 0, 2); + Arrays.MemSet(this.AnaState2, 0, 2); + Arrays.MemSet(this.XnrgSubfr, 0, 4); + Arrays.MemSet(this.NrgRatioSmth_Q8, 0, 4); + this.HPstate = 0; + Arrays.MemSet(this.NL, 0, 4); + Arrays.MemSet(this.inv_NL, 0, 4); + Arrays.MemSet(this.NoiseLevelBias, 0, 4); + this.counter = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Sort.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Sort.java new file mode 100644 index 0000000..6c0de8a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Sort.java @@ -0,0 +1,87 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Inlines; + +class Sort { + Sort() { + } + + static void silk_insertion_sort_increasing(int[] a, int[] idx, int L, int K) { + int j; + int value; + int i; + Inlines.OpusAssert(K > 0); + Inlines.OpusAssert(L > 0); + Inlines.OpusAssert(L >= K); + for (i = 0; i < K; ++i) { + idx[i] = i; + } + i = 1; + while (i < K) { + value = a[i]; + for (j = i - 1; j >= 0 && value < a[j]; --j) { + a[j + 1] = a[j]; + idx[j + 1] = idx[j]; + } + a[j + 1] = value; + idx[j + 1] = i++; + } + for (i = K; i < L; ++i) { + value = a[i]; + if (value >= a[K - 1]) continue; + for (j = K - 2; j >= 0 && value < a[j]; --j) { + a[j + 1] = a[j]; + idx[j + 1] = idx[j]; + } + a[j + 1] = value; + idx[j + 1] = i; + } + } + + static void silk_insertion_sort_increasing_all_values_int16(short[] a, int L) { + Inlines.OpusAssert(L > 0); + for (int i = 1; i < L; ++i) { + short value = a[i]; + for (int j = i - 1; j >= 0 && value < a[j]; --j) { + a[j + 1] = a[j]; + } + a[j + 1] = value; + } + } + + static void silk_insertion_sort_decreasing_int16(short[] a, int[] idx, int L, int K) { + int j; + short value; + int i; + Inlines.OpusAssert(K > 0); + Inlines.OpusAssert(L > 0); + Inlines.OpusAssert(L >= K); + for (i = 0; i < K; ++i) { + idx[i] = i; + } + i = 1; + while (i < K) { + value = a[i]; + for (j = i - 1; j >= 0 && value > a[j]; --j) { + a[j + 1] = a[j]; + idx[j + 1] = idx[j]; + } + a[j + 1] = value; + idx[j + 1] = i++; + } + for (i = K; i < L; ++i) { + value = a[i]; + if (value <= a[K - 1]) continue; + for (j = K - 2; j >= 0 && value > a[j]; --j) { + a[j + 1] = a[j]; + idx[j + 1] = idx[j]; + } + a[j + 1] = value; + idx[j + 1] = i; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Spread.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Spread.java new file mode 100644 index 0000000..c1ea5e2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Spread.java @@ -0,0 +1,15 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class Spread { + public static final int SPREAD_NONE = 0; + public static final int SPREAD_LIGHT = 1; + public static final int SPREAD_NORMAL = 2; + public static final int SPREAD_AGGRESSIVE = 3; + + Spread() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/Stereo.java b/source/decompiled/de/maxhenkel/voicechat/concentus/Stereo.java new file mode 100644 index 0000000..7dc0203 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/Stereo.java @@ -0,0 +1,283 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.SilkTables; +import de.maxhenkel.voicechat.concentus.StereoDecodeState; +import de.maxhenkel.voicechat.concentus.StereoEncodeState; +import de.maxhenkel.voicechat.concentus.SumSqrShift; + +class Stereo { + Stereo() { + } + + static void silk_stereo_decode_pred(EntropyCoder psRangeDec, int[] pred_Q13) { + int[][] ix = Arrays.InitTwoDimensionalArrayInt(2, 3); + int n = psRangeDec.dec_icdf(SilkTables.silk_stereo_pred_joint_iCDF, 8); + ix[0][2] = Inlines.silk_DIV32_16(n, 5); + ix[1][2] = n - 5 * ix[0][2]; + for (n = 0; n < 2; ++n) { + ix[n][0] = psRangeDec.dec_icdf(SilkTables.silk_uniform3_iCDF, 8); + ix[n][1] = psRangeDec.dec_icdf(SilkTables.silk_uniform5_iCDF, 8); + } + for (n = 0; n < 2; ++n) { + int[] nArray = ix[n]; + nArray[0] = nArray[0] + 3 * ix[n][2]; + short low_Q13 = SilkTables.silk_stereo_pred_quant_Q13[ix[n][0]]; + int step_Q13 = Inlines.silk_SMULWB(SilkTables.silk_stereo_pred_quant_Q13[ix[n][0] + 1] - low_Q13, 6554); + pred_Q13[n] = Inlines.silk_SMLABB(low_Q13, step_Q13, 2 * ix[n][1] + 1); + } + pred_Q13[0] = pred_Q13[0] - pred_Q13[1]; + } + + static void silk_stereo_decode_mid_only(EntropyCoder psRangeDec, BoxedValueInt decode_only_mid) { + decode_only_mid.Val = psRangeDec.dec_icdf(SilkTables.silk_stereo_only_code_mid_iCDF, 8); + } + + static void silk_stereo_encode_pred(EntropyCoder psRangeEnc, byte[][] ix) { + int n = 5 * ix[0][2] + ix[1][2]; + Inlines.OpusAssert(n < 25); + psRangeEnc.enc_icdf(n, SilkTables.silk_stereo_pred_joint_iCDF, 8); + for (n = 0; n < 2; ++n) { + Inlines.OpusAssert(ix[n][0] < 3); + Inlines.OpusAssert(ix[n][1] < 5); + psRangeEnc.enc_icdf(ix[n][0], SilkTables.silk_uniform3_iCDF, 8); + psRangeEnc.enc_icdf(ix[n][1], SilkTables.silk_uniform5_iCDF, 8); + } + } + + static void silk_stereo_encode_mid_only(EntropyCoder psRangeEnc, byte mid_only_flag) { + psRangeEnc.enc_icdf(mid_only_flag, SilkTables.silk_stereo_only_code_mid_iCDF, 8); + } + + static int silk_stereo_find_predictor(BoxedValueInt ratio_Q14, short[] x, short[] y, int[] mid_res_amp_Q0, int mid_res_amp_Q0_ptr, int length, int smooth_coef_Q16) { + BoxedValueInt nrgx = new BoxedValueInt(0); + BoxedValueInt nrgy = new BoxedValueInt(0); + BoxedValueInt scale1 = new BoxedValueInt(0); + BoxedValueInt scale2 = new BoxedValueInt(0); + SumSqrShift.silk_sum_sqr_shift(nrgx, scale1, x, length); + SumSqrShift.silk_sum_sqr_shift(nrgy, scale2, y, length); + int scale = Inlines.silk_max_int(scale1.Val, scale2.Val); + scale += scale & 1; + nrgy.Val = Inlines.silk_RSHIFT32(nrgy.Val, scale - scale2.Val); + nrgx.Val = Inlines.silk_RSHIFT32(nrgx.Val, scale - scale1.Val); + nrgx.Val = Inlines.silk_max_int(nrgx.Val, 1); + int corr = Inlines.silk_inner_prod_aligned_scale(x, y, scale, length); + int pred_Q13 = Inlines.silk_DIV32_varQ(corr, nrgx.Val, 13); + pred_Q13 = Inlines.silk_LIMIT(pred_Q13, -16384, 16384); + int pred2_Q10 = Inlines.silk_SMULWB(pred_Q13, pred_Q13); + smooth_coef_Q16 = Inlines.silk_max_int(smooth_coef_Q16, Inlines.silk_abs(pred2_Q10)); + Inlines.OpusAssert(smooth_coef_Q16 < 32768); + scale = Inlines.silk_RSHIFT(scale, 1); + mid_res_amp_Q0[mid_res_amp_Q0_ptr] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr], Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgx.Val), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr], smooth_coef_Q16); + nrgy.Val = Inlines.silk_SUB_LSHIFT32(nrgy.Val, Inlines.silk_SMULWB(corr, pred_Q13), 4); + nrgy.Val = Inlines.silk_ADD_LSHIFT32(nrgy.Val, Inlines.silk_SMULWB(nrgx.Val, pred2_Q10), 6); + mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgy.Val), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], smooth_coef_Q16); + ratio_Q14.Val = Inlines.silk_DIV32_varQ(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], Inlines.silk_max(mid_res_amp_Q0[mid_res_amp_Q0_ptr], 1), 14); + ratio_Q14.Val = Inlines.silk_LIMIT(ratio_Q14.Val, 0, Short.MAX_VALUE); + return pred_Q13; + } + + static void silk_stereo_LR_to_MS(StereoEncodeState state, short[] x1, int x1_ptr, short[] x2, int x2_ptr, byte[][] ix, BoxedValueByte mid_only_flag, int[] mid_side_rates_bps, int total_rate_bps, int prev_speech_act_Q8, int toMono, int fs_kHz, int frame_length) { + int width_Q14; + int min_mid_rate_bps; + int sum; + int n; + int[] pred_Q13 = new int[2]; + BoxedValueInt LP_ratio_Q14 = new BoxedValueInt(0); + BoxedValueInt HP_ratio_Q14 = new BoxedValueInt(0); + int mid = x1_ptr - 2; + short[] side = new short[frame_length + 2]; + for (n = 0; n < frame_length + 2; ++n) { + sum = x1[x1_ptr + n - 2] + x2[x2_ptr + n - 2]; + int diff = x1[x1_ptr + n - 2] - x2[x2_ptr + n - 2]; + x1[mid + n] = (short)Inlines.silk_RSHIFT_ROUND(sum, 1); + side[n] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(diff, 1)); + } + System.arraycopy(state.sMid, 0, x1, mid, 2); + System.arraycopy(state.sSide, 0, side, 0, 2); + System.arraycopy(x1, mid + frame_length, state.sMid, 0, 2); + System.arraycopy(side, frame_length, state.sSide, 0, 2); + short[] LP_mid = new short[frame_length]; + short[] HP_mid = new short[frame_length]; + for (n = 0; n < frame_length; ++n) { + sum = Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD_LSHIFT32(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 2); + LP_mid[n] = (short)sum; + HP_mid[n] = (short)(x1[mid + n + 1] - sum); + } + short[] LP_side = new short[frame_length]; + short[] HP_side = new short[frame_length]; + for (n = 0; n < frame_length; ++n) { + sum = Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD_LSHIFT32(side[n] + side[n + 2], side[n + 1], 1), 2); + LP_side[n] = (short)sum; + HP_side[n] = (short)(side[n + 1] - sum); + } + boolean is10msFrame = frame_length == 10 * fs_kHz; + int smooth_coef_Q16 = is10msFrame ? 328 : 655; + smooth_coef_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULBB(prev_speech_act_Q8, prev_speech_act_Q8), smooth_coef_Q16); + pred_Q13[0] = Stereo.silk_stereo_find_predictor(LP_ratio_Q14, LP_mid, LP_side, state.mid_side_amp_Q0, 0, frame_length, smooth_coef_Q16); + pred_Q13[1] = Stereo.silk_stereo_find_predictor(HP_ratio_Q14, HP_mid, HP_side, state.mid_side_amp_Q0, 2, frame_length, smooth_coef_Q16); + int frac_Q16 = Inlines.silk_SMLABB(HP_ratio_Q14.Val, LP_ratio_Q14.Val, 3); + frac_Q16 = Inlines.silk_min(frac_Q16, 65536); + if ((total_rate_bps -= is10msFrame ? 1200 : 600) < 1) { + total_rate_bps = 1; + } + Inlines.OpusAssert((min_mid_rate_bps = Inlines.silk_SMLABB(2000, fs_kHz, 900)) < Short.MAX_VALUE); + int frac_3_Q16 = Inlines.silk_MUL(3, frac_Q16); + mid_side_rates_bps[0] = Inlines.silk_DIV32_varQ(total_rate_bps, 851968 + frac_3_Q16, 19); + if (mid_side_rates_bps[0] < min_mid_rate_bps) { + mid_side_rates_bps[0] = min_mid_rate_bps; + mid_side_rates_bps[1] = total_rate_bps - mid_side_rates_bps[0]; + width_Q14 = Inlines.silk_DIV32_varQ(Inlines.silk_LSHIFT(mid_side_rates_bps[1], 1) - min_mid_rate_bps, Inlines.silk_SMULWB(65536 + frac_3_Q16, min_mid_rate_bps), 16); + width_Q14 = Inlines.silk_LIMIT(width_Q14, 0, 16384); + } else { + mid_side_rates_bps[1] = total_rate_bps - mid_side_rates_bps[0]; + width_Q14 = 16384; + } + state.smth_width_Q14 = (short)Inlines.silk_SMLAWB(state.smth_width_Q14, width_Q14 - state.smth_width_Q14, smooth_coef_Q16); + mid_only_flag.Val = 0; + if (toMono != 0) { + width_Q14 = 0; + pred_Q13[0] = 0; + pred_Q13[1] = 0; + Stereo.silk_stereo_quant_pred(pred_Q13, ix); + } else if (state.width_prev_Q14 == 0 && (8 * total_rate_bps < 13 * min_mid_rate_bps || Inlines.silk_SMULWB(frac_Q16, state.smth_width_Q14) < 819)) { + pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14); + pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14); + Stereo.silk_stereo_quant_pred(pred_Q13, ix); + width_Q14 = 0; + pred_Q13[0] = 0; + pred_Q13[1] = 0; + mid_side_rates_bps[0] = total_rate_bps; + mid_side_rates_bps[1] = 0; + mid_only_flag.Val = 1; + } else if (state.width_prev_Q14 != 0 && (8 * total_rate_bps < 11 * min_mid_rate_bps || Inlines.silk_SMULWB(frac_Q16, state.smth_width_Q14) < 328)) { + pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14); + pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14); + Stereo.silk_stereo_quant_pred(pred_Q13, ix); + width_Q14 = 0; + pred_Q13[0] = 0; + pred_Q13[1] = 0; + } else if (state.smth_width_Q14 > 15565) { + Stereo.silk_stereo_quant_pred(pred_Q13, ix); + width_Q14 = 16384; + } else { + pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14); + pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14); + Stereo.silk_stereo_quant_pred(pred_Q13, ix); + width_Q14 = state.smth_width_Q14; + } + if (mid_only_flag.Val == 1) { + state.silent_side_len = (short)(state.silent_side_len + (short)(frame_length - 8 * fs_kHz)); + if (state.silent_side_len < 5 * fs_kHz) { + mid_only_flag.Val = 0; + } else { + state.silent_side_len = (short)10000; + } + } else { + state.silent_side_len = 0; + } + if (mid_only_flag.Val == 0 && mid_side_rates_bps[1] < 1) { + mid_side_rates_bps[1] = 1; + mid_side_rates_bps[0] = Inlines.silk_max_int(1, total_rate_bps - mid_side_rates_bps[1]); + } + int pred0_Q13 = -state.pred_prev_Q13[0]; + int pred1_Q13 = -state.pred_prev_Q13[1]; + int w_Q24 = Inlines.silk_LSHIFT(state.width_prev_Q14, 10); + int denom_Q16 = Inlines.silk_DIV32_16(65536, 8 * fs_kHz); + int delta0_Q13 = 0 - Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[0] - state.pred_prev_Q13[0], denom_Q16), 16); + int delta1_Q13 = 0 - Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[1] - state.pred_prev_Q13[1], denom_Q16), 16); + int deltaw_Q24 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(width_Q14 - state.width_prev_Q14, denom_Q16), 10); + for (n = 0; n < 8 * fs_kHz; ++n) { + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 9); + sum = Inlines.silk_SMLAWB(Inlines.silk_SMULWB(w_Q24 += deltaw_Q24, side[n + 1]), sum, pred0_Q13 += delta0_Q13); + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT(x1[mid + n + 1], 11), pred1_Q13 += delta1_Q13); + x2[x2_ptr + n - 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + pred0_Q13 = 0 - pred_Q13[0]; + pred1_Q13 = 0 - pred_Q13[1]; + w_Q24 = Inlines.silk_LSHIFT(width_Q14, 10); + for (n = 8 * fs_kHz; n < frame_length; ++n) { + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 9); + sum = Inlines.silk_SMLAWB(Inlines.silk_SMULWB(w_Q24, side[n + 1]), sum, pred0_Q13); + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT(x1[mid + n + 1], 11), pred1_Q13); + x2[x2_ptr + n - 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + state.pred_prev_Q13[0] = (short)pred_Q13[0]; + state.pred_prev_Q13[1] = (short)pred_Q13[1]; + state.width_prev_Q14 = (short)width_Q14; + } + + static void silk_stereo_MS_to_LR(StereoDecodeState state, short[] x1, int x1_ptr, short[] x2, int x2_ptr, int[] pred_Q13, int fs_kHz, int frame_length) { + int sum; + int n; + System.arraycopy(state.sMid, 0, x1, x1_ptr, 2); + System.arraycopy(state.sSide, 0, x2, x2_ptr, 2); + System.arraycopy(x1, x1_ptr + frame_length, state.sMid, 0, 2); + System.arraycopy(x2, x2_ptr + frame_length, state.sSide, 0, 2); + int pred0_Q13 = state.pred_prev_Q13[0]; + int pred1_Q13 = state.pred_prev_Q13[1]; + int denom_Q16 = Inlines.silk_DIV32_16(65536, 8 * fs_kHz); + int delta0_Q13 = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[0] - state.pred_prev_Q13[0], denom_Q16), 16); + int delta1_Q13 = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[1] - state.pred_prev_Q13[1], denom_Q16), 16); + for (n = 0; n < 8 * fs_kHz; ++n) { + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[x1_ptr + n] + x1[x1_ptr + n + 2], x1[x1_ptr + n + 1], 1), 9); + sum = Inlines.silk_SMLAWB(Inlines.silk_LSHIFT(x2[x2_ptr + n + 1], 8), sum, pred0_Q13 += delta0_Q13); + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT(x1[x1_ptr + n + 1], 11), pred1_Q13 += delta1_Q13); + x2[x2_ptr + n + 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + pred0_Q13 = pred_Q13[0]; + pred1_Q13 = pred_Q13[1]; + for (n = 8 * fs_kHz; n < frame_length; ++n) { + sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[x1_ptr + n] + x1[x1_ptr + n + 2], x1[x1_ptr + n + 1], 1), 9); + sum = Inlines.silk_SMLAWB(Inlines.silk_LSHIFT(x2[x2_ptr + n + 1], 8), sum, pred0_Q13); + sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT(x1[x1_ptr + n + 1], 11), pred1_Q13); + x2[x2_ptr + n + 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8)); + } + state.pred_prev_Q13[0] = (short)pred_Q13[0]; + state.pred_prev_Q13[1] = (short)pred_Q13[1]; + for (n = 0; n < frame_length; ++n) { + sum = x1[x1_ptr + n + 1] + x2[x2_ptr + n + 1]; + int diff = x1[x1_ptr + n + 1] - x2[x2_ptr + n + 1]; + x1[x1_ptr + n + 1] = (short)Inlines.silk_SAT16(sum); + x2[x2_ptr + n + 1] = (short)Inlines.silk_SAT16(diff); + } + } + + static void silk_stereo_quant_pred(int[] pred_Q13, byte[][] ix) { + int quant_pred_Q13 = 0; + Arrays.MemSet(ix[0], (byte)0, 3); + Arrays.MemSet(ix[1], (byte)0, 3); + for (int n = 0; n < 2; ++n) { + boolean done = false; + int err_min_Q13 = Integer.MAX_VALUE; + for (int i = 0; !done && i < 15; i = (int)((byte)(i + 1))) { + short low_Q13 = SilkTables.silk_stereo_pred_quant_Q13[i]; + int step_Q13 = Inlines.silk_SMULWB(SilkTables.silk_stereo_pred_quant_Q13[i + 1] - low_Q13, 6554); + for (int j = 0; !done && j < 5; j = (int)((byte)(j + 1))) { + int lvl_Q13 = Inlines.silk_SMLABB(low_Q13, step_Q13, 2 * j + 1); + int err_Q13 = Inlines.silk_abs(pred_Q13[n] - lvl_Q13); + if (err_Q13 < err_min_Q13) { + err_min_Q13 = err_Q13; + quant_pred_Q13 = lvl_Q13; + ix[n][0] = i; + ix[n][1] = j; + continue; + } + done = true; + } + } + ix[n][2] = (byte)Inlines.silk_DIV32_16(ix[n][0], 3); + ix[n][0] = (byte)(ix[n][0] - (byte)(ix[n][2] * 3)); + pred_Q13[n] = quant_pred_Q13; + } + pred_Q13[0] = pred_Q13[0] - pred_Q13[1]; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/StereoDecodeState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/StereoDecodeState.java new file mode 100644 index 0000000..2f9a1d3 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/StereoDecodeState.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class StereoDecodeState { + final short[] pred_prev_Q13 = new short[2]; + final short[] sMid = new short[2]; + final short[] sSide = new short[2]; + + StereoDecodeState() { + } + + void Reset() { + Arrays.MemSet(this.pred_prev_Q13, (short)0, 2); + Arrays.MemSet(this.sMid, (short)0, 2); + Arrays.MemSet(this.sSide, (short)0, 2); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/StereoEncodeState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/StereoEncodeState.java new file mode 100644 index 0000000..8822ba0 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/StereoEncodeState.java @@ -0,0 +1,38 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class StereoEncodeState { + final short[] pred_prev_Q13 = new short[2]; + final short[] sMid = new short[2]; + final short[] sSide = new short[2]; + final int[] mid_side_amp_Q0 = new int[4]; + short smth_width_Q14 = 0; + short width_prev_Q14 = 0; + short silent_side_len = 0; + final byte[][][] predIx = Arrays.InitThreeDimensionalArrayByte(3, 2, 3); + final byte[] mid_only_flags = new byte[3]; + + StereoEncodeState() { + } + + void Reset() { + Arrays.MemSet(this.pred_prev_Q13, (short)0, 2); + Arrays.MemSet(this.sMid, (short)0, 2); + Arrays.MemSet(this.sSide, (short)0, 2); + Arrays.MemSet(this.mid_side_amp_Q0, 0, 4); + this.smth_width_Q14 = 0; + this.width_prev_Q14 = 0; + this.silent_side_len = 0; + for (int x = 0; x < 3; ++x) { + for (int y = 0; y < 2; ++y) { + Arrays.MemSet(this.predIx[x][y], (byte)0, 3); + } + } + Arrays.MemSet(this.mid_only_flags, (byte)0, 3); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/StereoWidthState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/StereoWidthState.java new file mode 100644 index 0000000..f81c623 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/StereoWidthState.java @@ -0,0 +1,24 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class StereoWidthState { + int XX; + int XY; + int YY; + int smoothed_width; + int max_follower; + + StereoWidthState() { + } + + void Reset() { + this.XX = 0; + this.XY = 0; + this.YY = 0; + this.smoothed_width = 0; + this.max_follower = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/SumSqrShift.java b/source/decompiled/de/maxhenkel/voicechat/concentus/SumSqrShift.java new file mode 100644 index 0000000..72eeb6c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/SumSqrShift.java @@ -0,0 +1,81 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Inlines; + +class SumSqrShift { + SumSqrShift() { + } + + static void silk_sum_sqr_shift(BoxedValueInt energy, BoxedValueInt shift, short[] x, int x_ptr, int len) { + int nrg_tmp; + int i; + int nrg = 0; + int shft = 0; + --len; + for (i = 0; i < len; i += 2) { + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[x_ptr + i], x[x_ptr + i]); + if ((nrg = Inlines.silk_SMLABB_ovflw(nrg, x[x_ptr + i + 1], x[x_ptr + i + 1])) >= 0) continue; + nrg = (int)Inlines.silk_RSHIFT_uint(nrg, 2); + shft = 2; + i += 2; + break; + } + while (i < len) { + nrg_tmp = Inlines.silk_SMULBB(x[x_ptr + i], x[x_ptr + i]); + if ((nrg = (int)Inlines.silk_ADD_RSHIFT_uint(nrg, nrg_tmp = Inlines.silk_SMLABB_ovflw(nrg_tmp, x[x_ptr + i + 1], x[x_ptr + i + 1]), shft)) < 0) { + nrg = (int)Inlines.silk_RSHIFT_uint(nrg, 2); + shft += 2; + } + i += 2; + } + if (i == len) { + nrg_tmp = Inlines.silk_SMULBB(x[x_ptr + i], x[x_ptr + i]); + nrg = (int)Inlines.silk_ADD_RSHIFT_uint(nrg, nrg_tmp, shft); + } + if ((nrg & 0xC0000000) != 0) { + nrg = (int)Inlines.silk_RSHIFT_uint(nrg, 2); + shft += 2; + } + shift.Val = shft; + energy.Val = nrg; + } + + static void silk_sum_sqr_shift(BoxedValueInt energy, BoxedValueInt shift, short[] x, int len) { + int nrg_tmp; + int i; + int nrg = 0; + int shft = 0; + --len; + for (i = 0; i < len; i += 2) { + nrg = Inlines.silk_SMLABB_ovflw(nrg, x[i], x[i]); + if ((nrg = Inlines.silk_SMLABB_ovflw(nrg, x[i + 1], x[i + 1])) >= 0) continue; + nrg = (int)Inlines.silk_RSHIFT_uint(nrg, 2); + shft = 2; + i += 2; + break; + } + while (i < len) { + nrg_tmp = Inlines.silk_SMULBB(x[i], x[i]); + if ((nrg = (int)Inlines.silk_ADD_RSHIFT_uint(nrg, nrg_tmp = Inlines.silk_SMLABB_ovflw(nrg_tmp, x[i + 1], x[i + 1]), shft)) < 0) { + nrg = (int)Inlines.silk_RSHIFT_uint(nrg, 2); + shft += 2; + } + i += 2; + } + if (i == len) { + nrg_tmp = Inlines.silk_SMULBB(x[i], x[i]); + nrg = (int)Inlines.silk_ADD_RSHIFT_uint(nrg, nrg_tmp, shft); + } + if ((nrg & 0xC0000000) != 0) { + nrg = (int)Inlines.silk_RSHIFT_uint(nrg, 2); + shft += 2; + } + shift.Val = shft; + energy.Val = nrg; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/TOCStruct.java b/source/decompiled/de/maxhenkel/voicechat/concentus/TOCStruct.java new file mode 100644 index 0000000..bb301ff --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/TOCStruct.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Arrays; + +class TOCStruct { + int VADFlag = 0; + final int[] VADFlags = new int[3]; + int inbandFECFlag = 0; + + TOCStruct() { + } + + void Reset() { + this.VADFlag = 0; + Arrays.MemSet(this.VADFlags, 0, 3); + this.inbandFECFlag = 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/TonalityAnalysisState.java b/source/decompiled/de/maxhenkel/voicechat/concentus/TonalityAnalysisState.java new file mode 100644 index 0000000..27dd45c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/TonalityAnalysisState.java @@ -0,0 +1,92 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.AnalysisInfo; +import de.maxhenkel.voicechat.concentus.Arrays; + +class TonalityAnalysisState { + boolean enabled = false; + final float[] angle = new float[240]; + final float[] d_angle = new float[240]; + final float[] d2_angle = new float[240]; + final int[] inmem = new int[720]; + int mem_fill; + final float[] prev_band_tonality = new float[18]; + float prev_tonality; + final float[][] E = Arrays.InitTwoDimensionalArrayFloat(8, 18); + final float[] lowE = new float[18]; + final float[] highE = new float[18]; + final float[] meanE = new float[21]; + final float[] mem = new float[32]; + final float[] cmean = new float[8]; + final float[] std = new float[9]; + float music_prob; + float Etracker; + float lowECount; + int E_count; + int last_music; + int last_transition; + int count; + final float[] subframe_mem = new float[3]; + int analysis_offset; + final float[] pspeech = new float[200]; + final float[] pmusic = new float[200]; + float speech_confidence; + float music_confidence; + int speech_confidence_count; + int music_confidence_count; + int write_pos; + int read_pos; + int read_subframe; + final AnalysisInfo[] info = new AnalysisInfo[200]; + + TonalityAnalysisState() { + for (int c = 0; c < 200; ++c) { + this.info[c] = new AnalysisInfo(); + } + } + + void Reset() { + int c; + Arrays.MemSet(this.angle, 0.0f, 240); + Arrays.MemSet(this.d_angle, 0.0f, 240); + Arrays.MemSet(this.d2_angle, 0.0f, 240); + Arrays.MemSet(this.inmem, 0, 720); + this.mem_fill = 0; + Arrays.MemSet(this.prev_band_tonality, 0.0f, 18); + this.prev_tonality = 0.0f; + for (c = 0; c < 8; ++c) { + Arrays.MemSet(this.E[c], 0.0f, 18); + } + Arrays.MemSet(this.lowE, 0.0f, 18); + Arrays.MemSet(this.highE, 0.0f, 18); + Arrays.MemSet(this.meanE, 0.0f, 21); + Arrays.MemSet(this.mem, 0.0f, 32); + Arrays.MemSet(this.cmean, 0.0f, 8); + Arrays.MemSet(this.std, 0.0f, 9); + this.music_prob = 0.0f; + this.Etracker = 0.0f; + this.lowECount = 0.0f; + this.E_count = 0; + this.last_music = 0; + this.last_transition = 0; + this.count = 0; + Arrays.MemSet(this.subframe_mem, 0.0f, 3); + this.analysis_offset = 0; + Arrays.MemSet(this.pspeech, 0.0f, 200); + Arrays.MemSet(this.pmusic, 0.0f, 200); + this.speech_confidence = 0.0f; + this.music_confidence = 0.0f; + this.speech_confidence_count = 0; + this.music_confidence_count = 0; + this.write_pos = 0; + this.read_pos = 0; + this.read_subframe = 0; + for (c = 0; c < 200; ++c) { + this.info[c].Reset(); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/TuningParameters.java b/source/decompiled/de/maxhenkel/voicechat/concentus/TuningParameters.java new file mode 100644 index 0000000..a0ffd2d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/TuningParameters.java @@ -0,0 +1,56 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class TuningParameters { + static final int BITRESERVOIR_DECAY_TIME_MS = 500; + static final float FIND_PITCH_WHITE_NOISE_FRACTION = 0.001f; + static final float FIND_PITCH_BANDWIDTH_EXPANSION = 0.99f; + static final float FIND_LPC_COND_FAC = 1.0E-5f; + static final float FIND_LTP_COND_FAC = 1.0E-5f; + static final float LTP_DAMPING = 0.05f; + static final float LTP_SMOOTHING = 0.1f; + static final float MU_LTP_QUANT_NB = 0.03f; + static final float MU_LTP_QUANT_MB = 0.025f; + static final float MU_LTP_QUANT_WB = 0.02f; + static final float MAX_SUM_LOG_GAIN_DB = 250.0f; + static final float VARIABLE_HP_SMTH_COEF1 = 0.1f; + static final float VARIABLE_HP_SMTH_COEF2 = 0.015f; + static final float VARIABLE_HP_MAX_DELTA_FREQ = 0.4f; + static final int VARIABLE_HP_MIN_CUTOFF_HZ = 60; + static final int VARIABLE_HP_MAX_CUTOFF_HZ = 100; + static final float SPEECH_ACTIVITY_DTX_THRES = 0.05f; + static final float LBRR_SPEECH_ACTIVITY_THRES = 0.3f; + static final float BG_SNR_DECR_dB = 2.0f; + static final float HARM_SNR_INCR_dB = 2.0f; + static final float SPARSE_SNR_INCR_dB = 2.0f; + static final float SPARSENESS_THRESHOLD_QNT_OFFSET = 0.75f; + static final float WARPING_MULTIPLIER = 0.015f; + static final float SHAPE_WHITE_NOISE_FRACTION = 5.0E-5f; + static final float BANDWIDTH_EXPANSION = 0.95f; + static final float LOW_RATE_BANDWIDTH_EXPANSION_DELTA = 0.01f; + static final float LOW_RATE_HARMONIC_BOOST = 0.1f; + static final float LOW_INPUT_QUALITY_HARMONIC_BOOST = 0.1f; + static final float HARMONIC_SHAPING = 0.3f; + static final float HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING = 0.2f; + static final float HP_NOISE_COEF = 0.25f; + static final float HARM_HP_NOISE_COEF = 0.35f; + static final float INPUT_TILT = 0.05f; + static final float HIGH_RATE_INPUT_TILT = 0.1f; + static final float LOW_FREQ_SHAPING = 4.0f; + static final float LOW_QUALITY_LOW_FREQ_SHAPING_DECR = 0.5f; + static final float SUBFR_SMTH_COEF = 0.4f; + static final float LAMBDA_OFFSET = 1.2f; + static final float LAMBDA_SPEECH_ACT = -0.2f; + static final float LAMBDA_DELAYED_DECISIONS = -0.05f; + static final float LAMBDA_INPUT_QUALITY = -0.1f; + static final float LAMBDA_CODING_QUALITY = -0.2f; + static final float LAMBDA_QUANT_OFFSET = 0.8f; + static final int REDUCE_BITRATE_10_MS_BPS = 2200; + static final int MAX_BANDWIDTH_SWITCH_DELAY_MS = 5000; + + TuningParameters() { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/VQ.java b/source/decompiled/de/maxhenkel/voicechat/concentus/VQ.java new file mode 100644 index 0000000..c609a11 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/VQ.java @@ -0,0 +1,230 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.CWRS; +import de.maxhenkel.voicechat.concentus.EntropyCoder; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Kernels; + +class VQ { + private static int[] SPREAD_FACTOR = new int[]{15, 10, 5}; + + VQ() { + } + + static void exp_rotation1(int[] X, int X_ptr, int len, int stride, int c, int s) { + int x2; + int x1; + int i; + int Xptr = X_ptr; + int ms = Inlines.NEG16(s); + for (i = 0; i < len - stride; ++i) { + x1 = X[Xptr]; + x2 = X[Xptr + stride]; + X[Xptr + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x2), s, x1), 15)); + X[Xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x1), ms, x2), 15)); + ++Xptr; + } + Xptr = X_ptr + (len - 2 * stride - 1); + for (i = len - 2 * stride - 1; i >= 0; --i) { + x1 = X[Xptr]; + x2 = X[Xptr + stride]; + X[Xptr + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x2), s, x1), 15)); + X[Xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MAC16_16(Inlines.MULT16_16(c, x1), ms, x2), 15)); + --Xptr; + } + } + + static void exp_rotation(int[] X, int X_ptr, int len, int dir, int stride, int K, int spread) { + int stride2 = 0; + if (2 * K >= len || spread == 0) { + return; + } + int factor = SPREAD_FACTOR[spread - 1]; + int gain = Inlines.celt_div(Inlines.MULT16_16(Short.MAX_VALUE, len), len + factor * K); + int theta = Inlines.HALF16(Inlines.MULT16_16_Q15(gain, gain)); + int c = Inlines.celt_cos_norm(Inlines.EXTEND32(theta)); + int s = Inlines.celt_cos_norm(Inlines.EXTEND32(Inlines.SUB16(Short.MAX_VALUE, theta))); + if (len >= 8 * stride) { + stride2 = 1; + while ((stride2 * stride2 + stride2) * stride + (stride >> 2) < len) { + ++stride2; + } + } + len = Inlines.celt_udiv(len, stride); + for (int i = 0; i < stride; ++i) { + if (dir < 0) { + if (stride2 != 0) { + VQ.exp_rotation1(X, X_ptr + i * len, len, stride2, s, c); + } + VQ.exp_rotation1(X, X_ptr + i * len, len, 1, c, s); + continue; + } + VQ.exp_rotation1(X, X_ptr + i * len, len, 1, c, (short)(0 - s)); + if (stride2 == 0) continue; + VQ.exp_rotation1(X, X_ptr + i * len, len, stride2, s, (short)(0 - c)); + } + } + + static void normalise_residual(int[] iy, int[] X, int X_ptr, int N, int Ryy, int gain) { + int k = Inlines.celt_ilog2(Ryy) >> 1; + int t = Inlines.VSHR32(Ryy, 2 * (k - 7)); + int g = Inlines.MULT16_16_P15(Inlines.celt_rsqrt_norm(t), gain); + int i = 0; + do { + X[X_ptr + i] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(g, iy[i]), k + 1)); + } while (++i < N); + } + + static int extract_collapse_mask(int[] iy, int N, int B) { + if (B <= 1) { + return 1; + } + int N0 = Inlines.celt_udiv(N, B); + int collapse_mask = 0; + int i = 0; + do { + int tmp = 0; + int j = 0; + do { + tmp |= iy[i * N0 + j]; + } while (++j < N0); + collapse_mask |= (tmp != 0 ? 1 : 0) << i; + } while (++i < B); + return collapse_mask; + } + + static int alg_quant(int[] X, int X_ptr, int N, int K, int spread, int B, EntropyCoder enc) { + int[] y = new int[N]; + int[] iy = new int[N]; + int[] signx = new int[N]; + Inlines.OpusAssert(K > 0, "alg_quant() needs at least one pulse"); + Inlines.OpusAssert(N > 1, "alg_quant() needs at least two dimensions"); + VQ.exp_rotation(X, X_ptr, N, 1, B, K, spread); + int sum = 0; + int j = 0; + do { + if (X[X_ptr + j] > 0) { + signx[j] = 1; + } else { + signx[j] = -1; + X[X_ptr + j] = 0 - X[X_ptr + j]; + } + iy[j] = 0; + y[j] = 0; + } while (++j < N); + int yy = 0; + int xy = 0; + int pulsesLeft = K; + if (K > N >> 1) { + j = 0; + do { + sum += X[X_ptr + j]; + } while (++j < N); + if (sum <= K) { + X[X_ptr] = 16384; + j = X_ptr + 1; + do { + X[j] = 0; + } while (++j < N + X_ptr); + sum = 16384; + } + short rcp = Inlines.EXTRACT16(Inlines.MULT16_32_Q16(K - 1, Inlines.celt_rcp(sum))); + j = 0; + do { + iy[j] = Inlines.MULT16_16_Q15(X[X_ptr + j], (int)rcp); + y[j] = iy[j]; + yy = Inlines.MAC16_16(yy, y[j], y[j]); + xy = Inlines.MAC16_16(xy, X[X_ptr + j], y[j]); + int n = j; + y[n] = y[n] * 2; + pulsesLeft -= iy[j]; + } while (++j < N); + } + Inlines.OpusAssert(pulsesLeft >= 1, "Allocated too many pulses in the quick pass"); + if (pulsesLeft > N + 3) { + int tmp = pulsesLeft; + yy = Inlines.MAC16_16(yy, tmp, tmp); + yy = Inlines.MAC16_16(yy, tmp, y[0]); + iy[0] = iy[0] + pulsesLeft; + pulsesLeft = 0; + } + int s = 1; + for (int i = 0; i < pulsesLeft; ++i) { + int best_num = -32767; + int best_den = 0; + int rshift = 1 + Inlines.celt_ilog2(K - pulsesLeft + i + 1); + int best_id = 0; + yy = Inlines.ADD16(yy, 1); + j = 0; + do { + int Rxy = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + j])), rshift)); + int Ryy = Inlines.ADD16(yy, y[j]); + if (Inlines.MULT16_16(best_den, Rxy = Inlines.MULT16_16_Q15(Rxy, Rxy)) <= Inlines.MULT16_16(Ryy, best_num)) continue; + best_den = Ryy; + best_num = Rxy; + best_id = j; + } while (++j < N); + xy = Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + best_id])); + yy = Inlines.ADD16(yy, y[best_id]); + y[best_id] = y[best_id] + 2 * s; + int n = best_id; + iy[n] = iy[n] + 1; + } + j = 0; + do { + X[X_ptr + j] = Inlines.MULT16_16(signx[j], X[X_ptr + j]); + if (signx[j] >= 0) continue; + iy[j] = -iy[j]; + } while (++j < N); + CWRS.encode_pulses(iy, N, K, enc); + int collapse_mask = VQ.extract_collapse_mask(iy, N, B); + return collapse_mask; + } + + static int alg_unquant(int[] X, int X_ptr, int N, int K, int spread, int B, EntropyCoder dec, int gain) { + int[] iy = new int[N]; + Inlines.OpusAssert(K > 0, "alg_unquant() needs at least one pulse"); + Inlines.OpusAssert(N > 1, "alg_unquant() needs at least two dimensions"); + int Ryy = CWRS.decode_pulses(iy, N, K, dec); + VQ.normalise_residual(iy, X, X_ptr, N, Ryy, gain); + VQ.exp_rotation(X, X_ptr, N, -1, B, K, spread); + int collapse_mask = VQ.extract_collapse_mask(iy, N, B); + return collapse_mask; + } + + static void renormalise_vector(int[] X, int X_ptr, int N, int gain) { + int E = 1 + Kernels.celt_inner_prod(X, X_ptr, X, X_ptr, N); + int k = Inlines.celt_ilog2(E) >> 1; + int t = Inlines.VSHR32(E, 2 * (k - 7)); + int g = Inlines.MULT16_16_P15(Inlines.celt_rsqrt_norm(t), gain); + int xptr = X_ptr; + for (int i = 0; i < N; ++i) { + X[xptr] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(g, X[xptr]), k + 1)); + ++xptr; + } + } + + static int stereo_itheta(int[] X, int X_ptr, int[] Y, int Y_ptr, int stereo, int N) { + int Eside = 1; + int Emid = 1; + if (stereo != 0) { + for (int i = 0; i < N; ++i) { + int m = Inlines.ADD16(Inlines.SHR16(X[X_ptr + i], 1), Inlines.SHR16(Y[Y_ptr + i], 1)); + int s = Inlines.SUB16(Inlines.SHR16(X[X_ptr + i], 1), Inlines.SHR16(Y[Y_ptr + i], 1)); + Emid = Inlines.MAC16_16(Emid, m, m); + Eside = Inlines.MAC16_16(Eside, s, s); + } + } else { + Emid += Kernels.celt_inner_prod(X, X_ptr, X, X_ptr, N); + Eside += Kernels.celt_inner_prod(Y, Y_ptr, Y, Y_ptr, N); + } + int mid = Inlines.celt_sqrt(Emid); + int side = Inlines.celt_sqrt(Eside); + int itheta = Inlines.MULT16_16_Q15(20861, Inlines.celt_atan2p(side, mid)); + return itheta; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/VQ_WMat_EC.java b/source/decompiled/de/maxhenkel/voicechat/concentus/VQ_WMat_EC.java new file mode 100644 index 0000000..82ce4a6 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/VQ_WMat_EC.java @@ -0,0 +1,60 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.BoxedValueByte; +import de.maxhenkel.voicechat.concentus.BoxedValueInt; +import de.maxhenkel.voicechat.concentus.Inlines; + +class VQ_WMat_EC { + VQ_WMat_EC() { + } + + static void silk_VQ_WMat_EC(BoxedValueByte ind, BoxedValueInt rate_dist_Q14, BoxedValueInt gain_Q7, short[] in_Q14, int in_Q14_ptr, int[] W_Q18, int W_Q18_ptr, byte[][] cb_Q7, short[] cb_gain_Q7, short[] cl_Q5, int mu_Q9, int max_gain_Q7, int L) { + int cb_row_Q7_ptr = 0; + short[] diff_Q14 = new short[5]; + rate_dist_Q14.Val = Integer.MAX_VALUE; + for (int k = 0; k < L; ++k) { + byte[] cb_row_Q7 = cb_Q7[cb_row_Q7_ptr++]; + int gain_tmp_Q7 = cb_gain_Q7[k]; + diff_Q14[0] = (short)(in_Q14[in_Q14_ptr] - Inlines.silk_LSHIFT(cb_row_Q7[0], 7)); + diff_Q14[1] = (short)(in_Q14[in_Q14_ptr + 1] - Inlines.silk_LSHIFT(cb_row_Q7[1], 7)); + diff_Q14[2] = (short)(in_Q14[in_Q14_ptr + 2] - Inlines.silk_LSHIFT(cb_row_Q7[2], 7)); + diff_Q14[3] = (short)(in_Q14[in_Q14_ptr + 3] - Inlines.silk_LSHIFT(cb_row_Q7[3], 7)); + diff_Q14[4] = (short)(in_Q14[in_Q14_ptr + 4] - Inlines.silk_LSHIFT(cb_row_Q7[4], 7)); + int sum1_Q14 = Inlines.silk_SMULBB(mu_Q9, cl_Q5[k]); + Inlines.OpusAssert((sum1_Q14 = Inlines.silk_ADD_LSHIFT32(sum1_Q14, Inlines.silk_max(Inlines.silk_SUB32(gain_tmp_Q7, max_gain_Q7), 0), 10)) >= 0); + int sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 1], diff_Q14[1]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 2], diff_Q14[2]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 3], diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 4], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr], diff_Q14[0]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[0]); + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 7], diff_Q14[2]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 8], diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 9], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 6], diff_Q14[1]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[1]); + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 13], diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 14], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 12], diff_Q14[2]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[2]); + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 19], diff_Q14[4]); + sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1); + sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 18], diff_Q14[3]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[3]); + sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 24], diff_Q14[4]); + sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[4]); + Inlines.OpusAssert(sum1_Q14 >= 0); + if (sum1_Q14 >= rate_dist_Q14.Val) continue; + rate_dist_Q14.Val = sum1_Q14; + ind.Val = (byte)k; + gain_Q7.Val = gain_tmp_Q7; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/VoiceActivityDetection.java b/source/decompiled/de/maxhenkel/voicechat/concentus/VoiceActivityDetection.java new file mode 100644 index 0000000..8813a75 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/VoiceActivityDetection.java @@ -0,0 +1,152 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +import de.maxhenkel.voicechat.concentus.Filters; +import de.maxhenkel.voicechat.concentus.Inlines; +import de.maxhenkel.voicechat.concentus.Sigmoid; +import de.maxhenkel.voicechat.concentus.SilkChannelEncoder; +import de.maxhenkel.voicechat.concentus.SilkVADState; + +class VoiceActivityDetection { + private static final int[] tiltWeights = new int[]{30000, 6000, -12000, -12000}; + + VoiceActivityDetection() { + } + + static int silk_VAD_Init(SilkVADState psSilk_VAD) { + int b; + int ret = 0; + psSilk_VAD.Reset(); + for (b = 0; b < 4; ++b) { + psSilk_VAD.NoiseLevelBias[b] = Inlines.silk_max_32(Inlines.silk_DIV32_16(50, (short)(b + 1)), 1); + } + for (b = 0; b < 4; ++b) { + psSilk_VAD.NL[b] = Inlines.silk_MUL(100, psSilk_VAD.NoiseLevelBias[b]); + psSilk_VAD.inv_NL[b] = Inlines.silk_DIV32(Integer.MAX_VALUE, psSilk_VAD.NL[b]); + } + psSilk_VAD.counter = 15; + for (b = 0; b < 4; ++b) { + psSilk_VAD.NrgRatioSmth_Q8[b] = 25600; + } + return ret; + } + + static int silk_VAD_GetSA_Q8(SilkChannelEncoder psEncC, short[] pIn, int pIn_ptr) { + int SNR_Q7; + int speech_nrg; + int b; + int i; + int sumSquared = 0; + int[] Xnrg = new int[4]; + int[] NrgToNoiseRatio_Q8 = new int[4]; + int[] X_offset = new int[4]; + int ret = 0; + SilkVADState psSilk_VAD = psEncC.sVAD; + Inlines.OpusAssert(true); + Inlines.OpusAssert(320 >= psEncC.frame_length); + Inlines.OpusAssert(psEncC.frame_length <= 512); + Inlines.OpusAssert(psEncC.frame_length == 8 * Inlines.silk_RSHIFT(psEncC.frame_length, 3)); + int decimated_framelength1 = Inlines.silk_RSHIFT(psEncC.frame_length, 1); + int decimated_framelength2 = Inlines.silk_RSHIFT(psEncC.frame_length, 2); + int decimated_framelength = Inlines.silk_RSHIFT(psEncC.frame_length, 3); + X_offset[0] = 0; + X_offset[1] = decimated_framelength + decimated_framelength2; + X_offset[2] = X_offset[1] + decimated_framelength; + X_offset[3] = X_offset[2] + decimated_framelength2; + short[] X = new short[X_offset[3] + decimated_framelength1]; + Filters.silk_ana_filt_bank_1(pIn, pIn_ptr, psSilk_VAD.AnaState, X, X, X_offset[3], psEncC.frame_length); + Filters.silk_ana_filt_bank_1(X, 0, psSilk_VAD.AnaState1, X, X, X_offset[2], decimated_framelength1); + Filters.silk_ana_filt_bank_1(X, 0, psSilk_VAD.AnaState2, X, X, X_offset[1], decimated_framelength2); + X[decimated_framelength - 1] = (short)Inlines.silk_RSHIFT(X[decimated_framelength - 1], 1); + short HPstateTmp = X[decimated_framelength - 1]; + for (i = decimated_framelength - 1; i > 0; --i) { + X[i - 1] = (short)Inlines.silk_RSHIFT(X[i - 1], 1); + int n = i; + X[n] = (short)(X[n] - X[i - 1]); + } + X[0] = (short)(X[0] - psSilk_VAD.HPstate); + psSilk_VAD.HPstate = HPstateTmp; + for (b = 0; b < 4; ++b) { + decimated_framelength = Inlines.silk_RSHIFT(psEncC.frame_length, Inlines.silk_min_int(4 - b, 3)); + int dec_subframe_length = Inlines.silk_RSHIFT(decimated_framelength, 2); + int dec_subframe_offset = 0; + Xnrg[b] = psSilk_VAD.XnrgSubfr[b]; + for (int s = 0; s < 4; ++s) { + sumSquared = 0; + for (i = 0; i < dec_subframe_length; ++i) { + int x_tmp = Inlines.silk_RSHIFT(X[X_offset[b] + i + dec_subframe_offset], 3); + Inlines.OpusAssert((sumSquared = Inlines.silk_SMLABB(sumSquared, x_tmp, x_tmp)) >= 0); + } + Xnrg[b] = s < 3 ? Inlines.silk_ADD_POS_SAT32(Xnrg[b], sumSquared) : Inlines.silk_ADD_POS_SAT32(Xnrg[b], Inlines.silk_RSHIFT(sumSquared, 1)); + dec_subframe_offset += dec_subframe_length; + } + psSilk_VAD.XnrgSubfr[b] = sumSquared; + } + VoiceActivityDetection.silk_VAD_GetNoiseLevels(Xnrg, psSilk_VAD); + sumSquared = 0; + int input_tilt = 0; + for (b = 0; b < 4; ++b) { + speech_nrg = Xnrg[b] - psSilk_VAD.NL[b]; + if (speech_nrg > 0) { + NrgToNoiseRatio_Q8[b] = (Xnrg[b] & 0xFF800000) == 0 ? Inlines.silk_DIV32(Inlines.silk_LSHIFT(Xnrg[b], 8), psSilk_VAD.NL[b] + 1) : Inlines.silk_DIV32(Xnrg[b], Inlines.silk_RSHIFT(psSilk_VAD.NL[b], 8) + 1); + SNR_Q7 = Inlines.silk_lin2log(NrgToNoiseRatio_Q8[b]) - 1024; + sumSquared = Inlines.silk_SMLABB(sumSquared, SNR_Q7, SNR_Q7); + if (speech_nrg < 0x100000) { + SNR_Q7 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(speech_nrg), 6), SNR_Q7); + } + input_tilt = Inlines.silk_SMLAWB(input_tilt, tiltWeights[b], SNR_Q7); + continue; + } + NrgToNoiseRatio_Q8[b] = 256; + } + sumSquared = Inlines.silk_DIV32_16(sumSquared, 4); + short pSNR_dB_Q7 = (short)(3 * Inlines.silk_SQRT_APPROX(sumSquared)); + int SA_Q15 = Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(45000, pSNR_dB_Q7) - 128); + psEncC.input_tilt_Q15 = Inlines.silk_LSHIFT(Sigmoid.silk_sigm_Q15(input_tilt) - 16384, 1); + speech_nrg = 0; + for (b = 0; b < 4; ++b) { + speech_nrg += (b + 1) * Inlines.silk_RSHIFT(Xnrg[b] - psSilk_VAD.NL[b], 4); + } + if (speech_nrg <= 0) { + SA_Q15 = Inlines.silk_RSHIFT(SA_Q15, 1); + } else if (speech_nrg < 32768) { + speech_nrg = psEncC.frame_length == 10 * psEncC.fs_kHz ? Inlines.silk_LSHIFT_SAT32(speech_nrg, 16) : Inlines.silk_LSHIFT_SAT32(speech_nrg, 15); + speech_nrg = Inlines.silk_SQRT_APPROX(speech_nrg); + SA_Q15 = Inlines.silk_SMULWB(32768 + speech_nrg, SA_Q15); + } + psEncC.speech_activity_Q8 = Inlines.silk_min_int(Inlines.silk_RSHIFT(SA_Q15, 7), 255); + int smooth_coef_Q16 = Inlines.silk_SMULWB(4096, Inlines.silk_SMULWB(SA_Q15, SA_Q15)); + if (psEncC.frame_length == 10 * psEncC.fs_kHz) { + smooth_coef_Q16 >>= 1; + } + for (b = 0; b < 4; ++b) { + psSilk_VAD.NrgRatioSmth_Q8[b] = Inlines.silk_SMLAWB(psSilk_VAD.NrgRatioSmth_Q8[b], NrgToNoiseRatio_Q8[b] - psSilk_VAD.NrgRatioSmth_Q8[b], smooth_coef_Q16); + SNR_Q7 = 3 * (Inlines.silk_lin2log(psSilk_VAD.NrgRatioSmth_Q8[b]) - 1024); + psEncC.input_quality_bands_Q15[b] = Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT(SNR_Q7 - 2048, 4)); + } + return ret; + } + + static void silk_VAD_GetNoiseLevels(int[] pX, SilkVADState psSilk_VAD) { + int min_coef = psSilk_VAD.counter < 1000 ? Inlines.silk_DIV32_16(Short.MAX_VALUE, (short)(Inlines.silk_RSHIFT(psSilk_VAD.counter, 4) + 1)) : 0; + for (int k = 0; k < 4; ++k) { + int nl = psSilk_VAD.NL[k]; + Inlines.OpusAssert(nl >= 0); + int nrg = Inlines.silk_ADD_POS_SAT32(pX[k], psSilk_VAD.NoiseLevelBias[k]); + Inlines.OpusAssert(nrg > 0); + int inv_nrg = Inlines.silk_DIV32(Integer.MAX_VALUE, nrg); + Inlines.OpusAssert(inv_nrg >= 0); + int coef = nrg > Inlines.silk_LSHIFT(nl, 3) ? 128 : (nrg < nl ? 1024 : Inlines.silk_SMULWB(Inlines.silk_SMULWW(inv_nrg, nl), 2048)); + coef = Inlines.silk_max_int(coef, min_coef); + psSilk_VAD.inv_NL[k] = Inlines.silk_SMLAWB(psSilk_VAD.inv_NL[k], inv_nrg - psSilk_VAD.inv_NL[k], coef); + Inlines.OpusAssert(psSilk_VAD.inv_NL[k] >= 0); + nl = Inlines.silk_DIV32(Integer.MAX_VALUE, psSilk_VAD.inv_NL[k]); + Inlines.OpusAssert(nl >= 0); + psSilk_VAD.NL[k] = nl = Inlines.silk_min(nl, 0xFFFFFF); + } + ++psSilk_VAD.counter; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/concentus/VorbisLayout.java b/source/decompiled/de/maxhenkel/voicechat/concentus/VorbisLayout.java new file mode 100644 index 0000000..e0cb934 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/concentus/VorbisLayout.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.concentus; + +class VorbisLayout { + int nb_streams; + int nb_coupled_streams; + short[] mapping; + static final VorbisLayout[] vorbis_mappings = new VorbisLayout[]{new VorbisLayout(1, 0, new short[]{0}), new VorbisLayout(1, 1, new short[]{0, 1}), new VorbisLayout(2, 1, new short[]{0, 2, 1}), new VorbisLayout(2, 2, new short[]{0, 1, 2, 3}), new VorbisLayout(3, 2, new short[]{0, 4, 1, 2, 3}), new VorbisLayout(4, 2, new short[]{0, 4, 1, 2, 3, 5}), new VorbisLayout(4, 3, new short[]{0, 4, 1, 2, 3, 5, 6}), new VorbisLayout(5, 3, new short[]{0, 6, 1, 2, 3, 4, 5, 7})}; + + VorbisLayout(int streams, int coupled_streams, short[] map) { + this.nb_streams = streams; + this.nb_coupled_streams = coupled_streams; + this.mapping = map; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/config/ServerConfig.java b/source/decompiled/de/maxhenkel/voicechat/config/ServerConfig.java new file mode 100644 index 0000000..d467657 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/config/ServerConfig.java @@ -0,0 +1,59 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.config; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.configbuilder.ConfigBuilder; +import de.maxhenkel.voicechat.configbuilder.entry.ConfigEntry; + +public class ServerConfig { + public final ConfigEntry voiceChatPort; + public final ConfigEntry voiceChatBindAddress; + public final ConfigEntry voiceChatDistance; + public ConfigEntry whisperDistance; + public final ConfigEntry voiceChatCodec; + public final ConfigEntry voiceChatMtuSize; + public final ConfigEntry tcpRateLimit; + public final ConfigEntry keepAlive; + public final ConfigEntry groupsEnabled; + public final ConfigEntry voiceHost; + public final ConfigEntry allowRecording; + public final ConfigEntry spectatorInteraction; + public ConfigEntry spectatorPlayerPossession; + public ConfigEntry forceVoiceChat; + public ConfigEntry loginTimeout; + public ConfigEntry broadcastRange; + public ConfigEntry allowPings; + public ConfigEntry threadedServerSupport; + + public ServerConfig(ConfigBuilder builder) { + builder.header(String.format("%s server config v%s", "Simple Voice Chat", Voicechat.INSTANCE.getDescription().getVersion())); + this.voiceChatPort = builder.integerEntry("port", (Integer)24454, -1, 65535, "The port number to use for the voice chat communication.", "Audio packets are always transmitted via the UDP protocol on the port number", "specified here, independently of other networking used for the game server.", "Set this to '-1' to use the same port number that is used by the Minecraft server.", "However, it is strongly recommended NOT to use the same port number because UDP on", "it is also used by default for the server query. Doing so may crash the server!"); + this.voiceChatBindAddress = builder.stringEntry("bind_address", "", "The server IP address to bind the voice chat to", "Leave blank to use the 'server-ip' property from the 'server.properties' config file", "To bind to the wildcard IP address, use '*'"); + this.voiceChatDistance = builder.doubleEntry("max_voice_distance", (Double)48.0, 1.0, 1000000.0, "The distance to which the voice can be heard"); + this.whisperDistance = builder.doubleEntry("whisper_distance", (Double)24.0, 1.0, 1000000.0, "The distance to which the voice can be heard when whispering"); + this.voiceChatCodec = builder.enumEntry("codec", Codec.VOIP, "The Opus codec", "Valid values are 'VOIP', 'AUDIO', and 'RESTRICTED_LOWDELAY'"); + this.voiceChatMtuSize = builder.integerEntry("mtu_size", (Integer)1275, 512, 2048, "The maximum size that audio packets are allowed to have (in bytes)", "Setting this to lower values might cause issues"); + this.tcpRateLimit = builder.integerEntry("tcp_rate_limit", (Integer)16, -1, 1024, "The maximum number of packets a player can send per second", "Set this to -1 to disable the rate limit - This must be greater than 0 in all other cases", "This only applies to voice chat packets that are sent through Minecrafts networking", "This affects actions like opening/joining/leaving voice chat groups or general state changes like disabling/enabling the voice chat"); + this.keepAlive = builder.integerEntry("keep_alive", (Integer)1000, 1000, Integer.MAX_VALUE, "The frequency at which keep-alive packets are sent (in milliseconds)", "Setting this to a higher value may result in timeouts"); + this.groupsEnabled = builder.booleanEntry("enable_groups", true, "If group chats are allowed"); + this.voiceHost = builder.stringEntry("voice_host", "", "The hostname that clients should use to connect to the voice chat", "This may also include a port, e.g. 'example.com:24454'", "Do NOT change this value if you don't know what you're doing"); + this.allowRecording = builder.booleanEntry("allow_recording", true, "If players are allowed to record the voice chat audio"); + this.spectatorInteraction = builder.booleanEntry("spectator_interaction", false, "If spectators are allowed to talk to other players"); + this.spectatorPlayerPossession = builder.booleanEntry("spectator_player_possession", false, "If spectators can talk to players they are spectating"); + this.forceVoiceChat = builder.booleanEntry("force_voice_chat", false, "If players without the voice chat mod should be kicked from the server"); + this.loginTimeout = builder.integerEntry("login_timeout", (Integer)10000, 100, Integer.MAX_VALUE, "The amount of time the server should wait to check if a player has the mod installed (in milliseconds)", "Only relevant when 'force_voice_chat' is set to 'true'"); + this.broadcastRange = builder.doubleEntry("broadcast_range", (Double)-1.0, -1.0, (Double)Double.MAX_VALUE, "The range in which the voice chat should broadcast audio", "A value less than 0 means 'max_voice_distance'"); + this.allowPings = builder.booleanEntry("allow_pings", true, "If the voice chat server should reply to external pings"); + this.threadedServerSupport = builder.booleanEntry("threaded_server_support", true, "If the voice chat should support servers that run multiple threads, like Folia", "Disabling this may slightly improve performance, but can cause issues on servers that don't run everything on a single thread"); + } + + public static enum Codec { + VOIP, + AUDIO, + RESTRICTED_LOWDELAY; + + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/config/Translations.java b/source/decompiled/de/maxhenkel/voicechat/config/Translations.java new file mode 100644 index 0000000..27207ef --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/config/Translations.java @@ -0,0 +1,31 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.config; + +import de.maxhenkel.voicechat.configbuilder.ConfigBuilder; +import de.maxhenkel.voicechat.configbuilder.entry.ConfigEntry; + +public class Translations { + public final ConfigEntry forceVoicechatKickMessage; + public final ConfigEntry rateLimitKickMessage; + public final ConfigEntry voicechatNotCompatibleMessage; + public final ConfigEntry voicechatNeededForCommandMessage; + public final ConfigEntry playerCommandMessage; + public final ConfigEntry placeholderVoicechatInstalled; + public final ConfigEntry placeholderVoicechatNotInstalled; + public final ConfigEntry placeholderVoicechatDisabled; + + public Translations(ConfigBuilder builder) { + builder.header("Simple Voice Chat translations", "This file contains all server-side translations for the Simple Voice Chat plugin"); + this.forceVoicechatKickMessage = builder.stringEntry("force_voicechat_kick_message", "You need %s %s to play on this server", "The message a player gets when kicked for not having voice chat installed and the server has force_voicechat enabled", "The first parameter is the mod/plugin name and the second parameter is the mod/plugin version"); + this.rateLimitKickMessage = builder.stringEntry("rate_limit_kick_message", "Kicked for exceeding packet rate limit", "The message a player gets when kicked for sending too many voice chat packets"); + this.voicechatNotCompatibleMessage = builder.stringEntry("voicechat_not_compatible_message", "Your voice chat client version is not compatible with the server-side version.\\nPlease install version %s of %s.", "The message a player gets when joining a server with an incompatible voice chat version", "The first parameter is the mod/plugin version and the second parameter is the mod/plugin name"); + this.voicechatNeededForCommandMessage = builder.stringEntry("voicechat_needed_for_command_message", "You need to have %s installed on your client to use this command", "The message a player gets when trying to execute a command that requires the voice chat mod installed on the client side.", "The first parameter is the mod/plugin name"); + this.playerCommandMessage = builder.stringEntry("player_command_message", "This command can only be executed as a player", "The message a player gets when trying to execute a command that can only be executed as a player"); + this.placeholderVoicechatInstalled = builder.stringEntry("placeholder_api_voicechat_installed", "", "The Placeholder API string for '%voicechat_installed%'"); + this.placeholderVoicechatNotInstalled = builder.stringEntry("placeholder_api_voicechat_not_installed", "", "The Placeholder API string for '%voicechat_not_installed%'"); + this.placeholderVoicechatDisabled = builder.stringEntry("placeholder_api_voicechat_disabled", "", "The Placeholder API string for '%voicechat_disabled%'"); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/CommentedProperties.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/CommentedProperties.java new file mode 100644 index 0000000..c118ff1 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/CommentedProperties.java @@ -0,0 +1,454 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder; + +import java.io.BufferedReader; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringReader; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +public class CommentedProperties +implements Map { + private final boolean strict; + private final List headerComments; + private final Map properties; + + public CommentedProperties(boolean strict) { + this.strict = strict; + this.headerComments = new ArrayList(); + this.properties = new LinkedHashMap(); + } + + public CommentedProperties() { + this(true); + } + + public CommentedProperties addHeaderComment(String comment) { + this.headerComments.add(comment); + return this; + } + + public CommentedProperties setHeaderComments(List headerComments) { + this.headerComments.clear(); + this.headerComments.addAll(headerComments); + return this; + } + + void sort(Comparator comparator) { + ArrayList> list = new ArrayList>(this.properties.entrySet()); + list.sort((o1, o2) -> comparator.compare((String)o1.getKey(), (String)o2.getKey())); + this.properties.clear(); + for (Map.Entry entry : list) { + this.properties.put((String)entry.getKey(), (Property)entry.getValue()); + } + } + + @Nullable + public String get(String key) { + Objects.requireNonNull(key); + Property property = this.properties.get(key); + if (property == null) { + return null; + } + return property.value; + } + + @Nullable + public List getComments(String key) { + Objects.requireNonNull(key); + Property property = this.properties.get(key); + if (property == null) { + return null; + } + return property.comments; + } + + public CommentedProperties setComments(String key, List comments) { + Objects.requireNonNull(key); + Property property = this.properties.get(key); + if (property == null) { + this.properties.put(key, new Property(comments, "")); + return this; + } + property.comments = comments; + return this; + } + + public CommentedProperties set(String key, String value, String ... comments) { + Objects.requireNonNull(key); + Objects.requireNonNull(value); + this.properties.put(key, new Property(Arrays.asList(comments), value)); + return this; + } + + public CommentedProperties load(InputStream inputStream) throws IOException { + ArrayList headerComments = new ArrayList(); + LinkedHashMap properties = new LinkedHashMap(); + try (LineReader reader = LineReader.fromInputStream(inputStream);){ + String line; + boolean header = true; + ArrayList previousComments = new ArrayList(); + while ((line = reader.nextLine()) != null) { + if (line.trim().isEmpty()) { + if (!header) continue; + headerComments.addAll(previousComments); + previousComments.clear(); + header = false; + continue; + } + Pair pair = CommentedProperties.readLine(line); + if (pair.key == null) { + previousComments.add(pair.value); + continue; + } + Property property = new Property(pair.value); + property.comments.addAll(previousComments); + previousComments.clear(); + properties.put(pair.key, property); + header = false; + } + } + this.setHeaderComments(headerComments); + this.properties.clear(); + this.properties.putAll(properties); + return this; + } + + protected static Pair readLine(String line) throws IOException { + int c; + StringBuilder key = new StringBuilder(); + StringBuilder value = new StringBuilder(); + StringReader reader = new StringReader(line); + boolean isKey = true; + boolean isComment = false; + boolean isPrecedingBackslash = false; + boolean onlyHadWhitespace = true; + boolean isStartOfValue = false; + while ((c = reader.read()) != -1) { + boolean isWhitespace = CommentedProperties.isWhitespace(c); + if (isComment) { + if (onlyHadWhitespace && isWhitespace) continue; + onlyHadWhitespace = false; + value.append((char)c); + continue; + } + if (isPrecedingBackslash) { + if (isKey) { + key.append((char)CommentedProperties.readEscapedCharacter(c, reader)); + } else { + value.append((char)CommentedProperties.readEscapedCharacter(c, reader)); + } + isPrecedingBackslash = false; + isStartOfValue = false; + continue; + } + if (c == 92) { + isPrecedingBackslash = true; + continue; + } + if ((c == 35 || c == 33) && onlyHadWhitespace) { + isComment = true; + continue; + } + if (isKey) { + if (key.length() <= 0 && CommentedProperties.isWhitespace(c)) continue; + if (CommentedProperties.isSeparator(c)) { + isKey = false; + isStartOfValue = true; + onlyHadWhitespace = false; + continue; + } + if (CommentedProperties.isWhitespace(c)) continue; + key.append((char)c); + } else { + if (isStartOfValue && (CommentedProperties.isWhitespace(c) || CommentedProperties.isSeparator(c))) continue; + value.append((char)c); + isStartOfValue = false; + } + if (!onlyHadWhitespace || isWhitespace) continue; + onlyHadWhitespace = false; + } + return new Pair(isComment ? null : key.toString(), value.toString()); + } + + private static int readEscapedCharacter(int c, StringReader reader) throws IOException { + if (c == 117) { + int u = 0; + for (int i = 0; i < 4; ++i) { + int uc = reader.read(); + if (uc == -1) { + throw new IOException("Invalid unicode escape sequence"); + } + u <<= 4; + if (uc >= 48 && uc <= 57) { + u += uc - 48; + continue; + } + if (uc >= 97 && uc <= 102) { + u += uc - 97 + 10; + continue; + } + if (uc >= 65 && uc <= 70) { + u += uc - 65 + 10; + continue; + } + throw new IOException("Invalid unicode escape sequence"); + } + return u; + } + if (c == 116) { + return 9; + } + if (c == 114) { + return 13; + } + if (c == 110) { + return 10; + } + if (c == 102) { + return 12; + } + return c; + } + + private static boolean isWhitespace(int c) { + return c == 32 || c == 9 || c == 13 || c == 12 || Character.isWhitespace(c); + } + + private static boolean isSeparator(int c) { + return c == 61 || c == 58 || c == 32 || c == 9 || c == 12; + } + + public CommentedProperties save(OutputStream outputStream) { + try (PrintWriter writer = new PrintWriter(outputStream);){ + for (String string : CommentedProperties.removeNewLines(this.headerComments)) { + writer.print("# "); + writer.println(string); + } + if (this.headerComments.size() > 0) { + writer.println(); + } + for (Map.Entry entry : this.properties.entrySet()) { + for (String comment : CommentedProperties.removeNewLines(((Property)entry.getValue()).comments)) { + writer.print("# "); + writer.println(comment); + } + writer.print(this.escapeKey((String)entry.getKey())); + writer.print("="); + writer.println(this.escapeValue(((Property)entry.getValue()).value)); + } + writer.flush(); + } + return this; + } + + private static List removeNewLines(List comments) { + ArrayList newComments = new ArrayList(); + for (String comment : comments) { + newComments.addAll(Arrays.asList(comment.split("\\r?\\n"))); + } + return newComments; + } + + private String escapeKey(String str) { + str = this.escape(str); + str = str.replace(" ", "\\ "); + str = str.replace("=", "\\="); + str = str.replace(":", "\\:"); + return str; + } + + private String escapeValue(String str) { + str = this.escape(str); + if (this.strict) { + str = str.replace("=", "\\="); + str = str.replace(":", "\\:"); + } + if (str.startsWith(" ")) { + str = String.format("\\%s", str); + } + return str; + } + + private String escape(String str) { + str = str.replace("\\", "\\\\"); + str = str.replace("\n", "\\n"); + str = str.replace("\r", "\\n"); + str = str.replace("\t", "\\t"); + str = str.replace("#", "\\#"); + str = str.replace("!", "\\!"); + for (int i = 0; i < str.length(); ++i) { + char c = str.charAt(i); + if (Character.UnicodeBlock.of(c) == Character.UnicodeBlock.BASIC_LATIN) continue; + str = String.format("%s\\u%04X%s", str.substring(0, i), (int)c, str.substring(i + 1)); + } + return str; + } + + @Override + public int size() { + return this.properties.size(); + } + + @Override + public boolean isEmpty() { + return this.properties.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return this.properties.containsKey(key); + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + for (Property property : this.properties.values()) { + if (!property.value.equals(value)) continue; + return true; + } + return false; + } + + @Override + @Nullable + @Deprecated + public String get(Object key) { + if (!(key instanceof String)) { + return null; + } + return this.get((String)key); + } + + @Override + @Deprecated + public String put(String key, String value) { + Objects.requireNonNull(key); + Objects.requireNonNull(value); + Property put = this.properties.put(key, new Property(value)); + if (put == null) { + return null; + } + return put.value; + } + + @Override + public String remove(Object key) { + Property removed = this.properties.remove(key); + if (removed == null) { + return null; + } + return removed.value; + } + + @Override + @Deprecated + public void putAll(Map map) { + for (Map.Entry entry : map.entrySet()) { + this.put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void clear() { + this.headerComments.clear(); + this.properties.clear(); + } + + @Override + public Set keySet() { + return this.properties.keySet(); + } + + @Override + @Deprecated + public Collection values() { + return this.properties.values().stream().map(property -> ((Property)property).value).collect(Collectors.toList()); + } + + @Override + @Deprecated + public Set> entrySet() { + return this.properties.entrySet().stream().map(entry -> new AbstractMap.SimpleEntry((String)entry.getKey(), ((Property)entry.getValue()).value)).collect(Collectors.toSet()); + } + + protected static class Property { + private List comments; + private String value; + + public Property(List comments, String value) { + this.comments = comments; + this.value = value; + } + + public Property(String value) { + this(new ArrayList(), value); + } + } + + protected static class LineReader + implements Closeable { + private final BufferedReader reader; + + public LineReader(BufferedReader reader) { + this.reader = reader; + } + + public static LineReader fromInputStream(InputStream inputStream) { + return new LineReader(new BufferedReader(new InputStreamReader(inputStream))); + } + + @Nullable + public String nextLine() throws IOException { + String line = this.reader.readLine(); + if (line == null) { + return null; + } + if (line.endsWith("\\")) { + line = line.substring(0, line.length() - 1); + String nextLine = this.nextLine(); + if (nextLine == null) { + return line; + } + return line + nextLine; + } + return line; + } + + @Override + public void close() throws IOException { + this.reader.close(); + } + } + + protected static class Pair { + private String key; + private String value; + + public Pair(String key, String value) { + this.key = key; + this.value = value; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/CommentedPropertyConfig.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/CommentedPropertyConfig.java new file mode 100644 index 0000000..b26328c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/CommentedPropertyConfig.java @@ -0,0 +1,144 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder; + +import de.maxhenkel.voicechat.configbuilder.CommentedProperties; +import de.maxhenkel.voicechat.configbuilder.Config; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.OpenOption; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.FileAttribute; +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.annotation.Nullable; + +public class CommentedPropertyConfig +implements Config { + private static final Logger LOGGER = Logger.getLogger(CommentedPropertyConfig.class.getName()); + private static final ExecutorService SAVE_EXECUTOR_SERVICE = Executors.newSingleThreadExecutor(runnable -> { + Thread thread = new Thread(runnable); + thread.setName("ConfigSaver"); + thread.setDaemon(true); + return thread; + }); + protected CommentedProperties properties; + @Nullable + protected Path path; + + protected CommentedPropertyConfig(CommentedProperties properties) { + this.properties = properties; + } + + public static Builder builder() { + return new Builder(); + } + + public String get(String key) { + return this.properties.get(key); + } + + public boolean has(String key) { + return this.properties.containsKey(key); + } + + public void set(String key, String value, String ... comments) { + this.properties.set(key, value, comments); + } + + public CommentedProperties getProperties() { + return this.properties; + } + + public void load() throws IOException { + if (this.path == null) { + return; + } + if (Files.exists(this.path, new LinkOption[0])) { + try (InputStream inputStream = Files.newInputStream(this.path, new OpenOption[0]);){ + this.properties.load(inputStream); + } + } + } + + public void reload() { + this.properties.clear(); + try { + this.load(); + } + catch (IOException e) { + LOGGER.log(Level.SEVERE, "Failed to reload config", e); + } + } + + public synchronized void saveSync() { + if (this.path == null) { + return; + } + try { + Files.createDirectories(this.path.getParent(), new FileAttribute[0]); + } + catch (Exception e) { + LOGGER.log(Level.SEVERE, "Failed to create parent directories of config", e); + } + try (OutputStream stream = Files.newOutputStream(this.path, StandardOpenOption.CREATE, StandardOpenOption.SYNC, StandardOpenOption.TRUNCATE_EXISTING);){ + this.properties.save(stream); + } + catch (Exception e) { + LOGGER.log(Level.SEVERE, "Failed to save config", e); + } + } + + public void save() { + if (this.path == null) { + return; + } + SAVE_EXECUTOR_SERVICE.execute(this::saveSync); + } + + @Override + public Map getEntries() { + return Collections.unmodifiableMap(this.properties); + } + + public static class Builder { + @Nullable + private Path path; + private boolean strict = true; + + private Builder() { + } + + public Builder path(Path path) { + this.path = path; + return this; + } + + public Builder strict(boolean strict) { + this.strict = strict; + return this; + } + + public CommentedPropertyConfig build() { + CommentedPropertyConfig config = new CommentedPropertyConfig(new CommentedProperties(this.strict)); + if (this.path != null) { + config.path = this.path.toAbsolutePath(); + } + config.reload(); + return config; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/Config.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/Config.java new file mode 100644 index 0000000..22e9990 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/Config.java @@ -0,0 +1,11 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder; + +import java.util.Map; + +public interface Config { + public Map getEntries(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/ConfigBuilder.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/ConfigBuilder.java new file mode 100644 index 0000000..1d6329f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/ConfigBuilder.java @@ -0,0 +1,167 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nonnull + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.ConfigBuilderImpl; +import de.maxhenkel.voicechat.configbuilder.MigratableConfig; +import de.maxhenkel.voicechat.configbuilder.MigratableConfigImpl; +import de.maxhenkel.voicechat.configbuilder.entry.BooleanConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.ConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.DoubleConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.EnumConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.FloatConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.IntegerConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.LongConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.StringConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public interface ConfigBuilder { + public ConfigBuilder header(String ... var1); + + public BooleanConfigEntry booleanEntry(String var1, Boolean var2, String ... var3); + + public IntegerConfigEntry integerEntry(String var1, Integer var2, Integer var3, Integer var4, String ... var5); + + default public IntegerConfigEntry integerEntry(String key, Integer def, String ... comments) { + return this.integerEntry(key, def, (Integer)null, (Integer)null, comments); + } + + public LongConfigEntry longEntry(String var1, Long var2, Long var3, Long var4, String ... var5); + + default public LongConfigEntry longEntry(String key, Long def, String ... comments) { + return this.longEntry(key, def, (Long)null, (Long)null, comments); + } + + public DoubleConfigEntry doubleEntry(String var1, Double var2, Double var3, Double var4, String ... var5); + + default public DoubleConfigEntry doubleEntry(String key, Double def, String ... comments) { + return this.doubleEntry(key, def, (Double)null, (Double)null, comments); + } + + public FloatConfigEntry floatEntry(String var1, Float var2, Float var3, Float var4, String ... var5); + + default public FloatConfigEntry floatEntry(String key, Float def, String ... comments) { + return this.floatEntry(key, def, (Float)null, (Float)null, comments); + } + + public StringConfigEntry stringEntry(String var1, String var2, String ... var3); + + public > EnumConfigEntry enumEntry(String var1, E var2, String ... var3); + + public ConfigEntry entry(String var1, T var2, String ... var3); + + public static Builder builder(@Nonnull Function builderConsumer) { + return new Builder(builderConsumer); + } + + public static class Builder { + @Nonnull + private final Function builderConsumer; + @Nullable + private Path path; + private final Map, ValueSerializer> valueSerializers; + private boolean removeUnused; + private boolean strict; + private boolean keepOrder; + private boolean saveAfterBuild; + private boolean saveSyncAfterBuild; + @Nullable + private Consumer migration; + + private Builder(@Nonnull Function builderConsumer) { + this.builderConsumer = builderConsumer; + this.valueSerializers = new HashMap(); + this.removeUnused = true; + this.strict = false; + this.keepOrder = true; + this.saveAfterBuild = true; + this.saveSyncAfterBuild = false; + } + + public Builder path(Path path) { + this.path = path; + return this; + } + + public Builder addValueSerializer(Class type, ValueSerializer serializer) { + this.valueSerializers.put(type, serializer); + return this; + } + + public Builder removeUnused(boolean removeUnused) { + this.removeUnused = removeUnused; + return this; + } + + public Builder strict(boolean strict) { + this.strict = strict; + return this; + } + + public Builder keepOrder(boolean keepOrder) { + this.keepOrder = keepOrder; + return this; + } + + public Builder saveAfterBuild(boolean saveAfterBuild) { + this.saveAfterBuild = saveAfterBuild; + if (saveAfterBuild) { + this.saveSyncAfterBuild = false; + } + return this; + } + + public Builder saveSyncAfterBuild(boolean saveSyncAfterBuild) { + this.saveSyncAfterBuild = saveSyncAfterBuild; + if (saveSyncAfterBuild) { + this.saveAfterBuild = false; + } + return this; + } + + public Builder migration(Consumer migration) { + this.migration = migration; + return this; + } + + public C build() { + CommentedPropertyConfig cpc = CommentedPropertyConfig.builder().path(this.path).strict(this.strict).build(); + if (this.migration != null && this.path != null && Files.exists(this.path, new LinkOption[0])) { + MigratableConfigImpl migratableConfig = new MigratableConfigImpl(cpc); + this.migration.accept(migratableConfig); + migratableConfig.freeze(); + } + ConfigBuilderImpl builder = new ConfigBuilderImpl(cpc, this.valueSerializers); + C config = this.builderConsumer.apply(builder); + builder.freeze(); + if (this.removeUnused) { + builder.removeUnused(); + } + if (this.keepOrder) { + builder.sortEntries(); + } + if (this.saveAfterBuild) { + builder.config.save(); + } else if (this.saveSyncAfterBuild) { + builder.config.saveSync(); + } + return config; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/ConfigBuilderImpl.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/ConfigBuilderImpl.java new file mode 100644 index 0000000..f9c7158 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/ConfigBuilderImpl.java @@ -0,0 +1,211 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.ConfigBuilder; +import de.maxhenkel.voicechat.configbuilder.custom.IntegerList; +import de.maxhenkel.voicechat.configbuilder.custom.StringList; +import de.maxhenkel.voicechat.configbuilder.custom.StringMap; +import de.maxhenkel.voicechat.configbuilder.custom.serializer.IntegerListValueSerializer; +import de.maxhenkel.voicechat.configbuilder.custom.serializer.StringListValueSerializer; +import de.maxhenkel.voicechat.configbuilder.custom.serializer.StringMapValueSerializer; +import de.maxhenkel.voicechat.configbuilder.custom.serializer.UUIDSerializer; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.BooleanConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.ConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.DoubleConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.EnumConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.FloatConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.GenericConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.IntegerConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.LongConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.StringConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.BooleanSerializer; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.DoubleSerializer; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.EnumSerializer; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.FloatSerializer; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.IntegerSerializer; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.LongSerializer; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.StringSerializer; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializable; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; +import javax.annotation.Nullable; + +public class ConfigBuilderImpl +implements ConfigBuilder { + protected final CommentedPropertyConfig config; + protected final Map, ValueSerializer> valueSerializers; + protected List> entries; + protected boolean frozen; + + public ConfigBuilderImpl(CommentedPropertyConfig config) { + this(config, null); + } + + public ConfigBuilderImpl(CommentedPropertyConfig config, @Nullable Map, ValueSerializer> customValueSerializers) { + this.config = config; + this.valueSerializers = ConfigBuilderImpl.getDefaultValueSerializers(); + if (customValueSerializers != null) { + this.valueSerializers.putAll(customValueSerializers); + } + this.entries = new ArrayList(); + } + + void removeUnused() { + List existingKeys = this.entries.stream().map(AbstractConfigEntry::getKey).collect(Collectors.toList()); + List toRemove = this.config.getProperties().keySet().stream().filter(s -> !existingKeys.contains(s)).collect(Collectors.toList()); + for (String key : toRemove) { + this.config.getProperties().remove(key); + } + } + + void sortEntries() { + HashMap map = new HashMap(); + for (int i = 0; i < this.entries.size(); ++i) { + map.put(this.entries.get(i).getKey(), i); + } + this.config.getProperties().sort(Comparator.comparingInt(o -> map.getOrDefault(o, Integer.MAX_VALUE))); + } + + void reloadFromDisk() { + this.config.reload(); + this.entries.forEach(AbstractConfigEntry::reload); + } + + void freeze() { + this.frozen = true; + } + + void checkFrozen() { + if (this.frozen) { + throw new IllegalStateException("ConfigBuilder is frozen"); + } + } + + @Override + public ConfigBuilderImpl header(String ... header) { + this.checkFrozen(); + this.config.getProperties().setHeaderComments(Arrays.asList(header)); + return this; + } + + @Override + public BooleanConfigEntry booleanEntry(String key, Boolean def, String ... comments) { + this.checkFrozen(); + BooleanConfigEntry entry = new BooleanConfigEntry(this.config, BooleanSerializer.INSTANCE, comments, key, def); + this.entries.add(entry); + return entry; + } + + @Override + public IntegerConfigEntry integerEntry(String key, Integer def, Integer min, Integer max, String ... comments) { + this.checkFrozen(); + IntegerConfigEntry entry = new IntegerConfigEntry(this.config, IntegerSerializer.INSTANCE, comments, key, def, min, max); + this.entries.add(entry); + return entry; + } + + @Override + public LongConfigEntry longEntry(String key, Long def, Long min, Long max, String ... comments) { + this.checkFrozen(); + LongConfigEntry entry = new LongConfigEntry(this.config, LongSerializer.INSTANCE, comments, key, def, min, max); + this.entries.add(entry); + return entry; + } + + @Override + public DoubleConfigEntry doubleEntry(String key, Double def, Double min, Double max, String ... comments) { + this.checkFrozen(); + DoubleConfigEntry entry = new DoubleConfigEntry(this.config, DoubleSerializer.INSTANCE, comments, key, def, min, max); + this.entries.add(entry); + return entry; + } + + @Override + public FloatConfigEntry floatEntry(String key, Float def, Float min, Float max, String ... comments) { + this.checkFrozen(); + FloatConfigEntry entry = new FloatConfigEntry(this.config, FloatSerializer.INSTANCE, comments, key, def, min, max); + this.entries.add(entry); + return entry; + } + + @Override + public StringConfigEntry stringEntry(String key, String def, String ... comments) { + this.checkFrozen(); + StringConfigEntry entry = new StringConfigEntry(this.config, StringSerializer.INSTANCE, comments, key, def); + this.entries.add(entry); + return entry; + } + + @Override + public > EnumConfigEntry enumEntry(String key, E def, String ... comments) { + this.checkFrozen(); + EnumConfigEntry entry = new EnumConfigEntry(this.config, new EnumSerializer(def.getClass()), comments, key, def); + this.entries.add(entry); + return entry; + } + + @Override + public ConfigEntry entry(String key, T def, String ... comments) { + this.checkFrozen(); + AbstractConfigEntry entry = this.entryInternal(key, def, comments); + this.entries.add(entry); + return entry; + } + + private AbstractConfigEntry entryInternal(String key, T def, String ... comments) { + ValueSerializer valueSerializer = this.valueSerializers.get(def.getClass()); + if (valueSerializer != null) { + return new GenericConfigEntry(this.config, valueSerializer, comments, key, def); + } + if (def instanceof Enum) { + return this.enumEntry(key, (Enum)def, comments); + } + try { + ValueSerializable annotation = def.getClass().getDeclaredAnnotation(ValueSerializable.class); + if (annotation == null) { + throw new IllegalArgumentException(String.format("Unsupported data type: %s", def.getClass().getName())); + } + Class> entryConverterClass = annotation.value(); + Constructor> constructor = entryConverterClass.getDeclaredConstructor(new Class[0]); + constructor.setAccessible(true); + constructor.newInstance(new Object[0]); + ValueSerializer converter = constructor.newInstance(new Object[0]); + return new GenericConfigEntry(this.config, converter, comments, key, def); + } + catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { + throw new IllegalArgumentException("Could not instantiate value serializer", e); + } + } + + protected static Map, ValueSerializer> getDefaultValueSerializers() { + HashMap valueSerializers = new HashMap(); + valueSerializers.put(Boolean.class, new BooleanSerializer()); + valueSerializers.put(Integer.class, new IntegerSerializer()); + valueSerializers.put(Long.class, new LongSerializer()); + valueSerializers.put(Float.class, new FloatSerializer()); + valueSerializers.put(Double.class, new DoubleSerializer()); + valueSerializers.put(String.class, new StringSerializer()); + valueSerializers.put(UUID.class, UUIDSerializer.INSTANCE); + valueSerializers.put(StringList.class, StringListValueSerializer.INSTANCE); + valueSerializers.put(IntegerList.class, IntegerListValueSerializer.INSTANCE); + valueSerializers.put(StringMap.class, StringMapValueSerializer.INSTANCE); + return valueSerializers; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/MigratableConfig.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/MigratableConfig.java new file mode 100644 index 0000000..ecaf694 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/MigratableConfig.java @@ -0,0 +1,21 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder; + +import de.maxhenkel.voicechat.configbuilder.Config; +import javax.annotation.Nullable; + +public interface MigratableConfig +extends Config { + @Nullable + public String get(String var1); + + public boolean has(String var1); + + public void set(String var1, String var2); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/MigratableConfigImpl.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/MigratableConfigImpl.java new file mode 100644 index 0000000..d5a8f4f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/MigratableConfigImpl.java @@ -0,0 +1,58 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.MigratableConfig; +import java.util.Map; +import javax.annotation.Nullable; + +class MigratableConfigImpl +implements MigratableConfig { + private final CommentedPropertyConfig config; + private boolean frozen; + + public MigratableConfigImpl(CommentedPropertyConfig config) { + this.config = config; + } + + @Override + @Nullable + public String get(String key) { + this.checkFrozen(); + return this.config.get(key); + } + + @Override + public boolean has(String key) { + this.checkFrozen(); + return this.config.has(key); + } + + @Override + public void set(String key, String value) { + this.checkFrozen(); + this.config.set(key, value, new String[0]); + } + + @Override + public Map getEntries() { + this.checkFrozen(); + return this.config.getEntries(); + } + + public void freeze() { + this.frozen = true; + } + + private void checkFrozen() { + if (this.frozen) { + throw new IllegalStateException("ConfigBuilder is frozen"); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/AbstractValueList.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/AbstractValueList.java new file mode 100644 index 0000000..97f8bc3 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/AbstractValueList.java @@ -0,0 +1,162 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.custom; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Objects; + +public abstract class AbstractValueList +implements List { + protected final List list; + + protected AbstractValueList(T ... values) { + this(Arrays.asList(values)); + } + + protected AbstractValueList(List values) { + this.list = Collections.unmodifiableList(values); + } + + @Override + public int size() { + return this.list.size(); + } + + @Override + public boolean isEmpty() { + return this.list.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return this.list.contains(o); + } + + @Override + public Iterator iterator() { + return this.list.iterator(); + } + + @Override + public Object[] toArray() { + return this.list.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return this.list.toArray(a); + } + + @Override + public boolean add(T s) { + return (Boolean)AbstractValueList.throwException(); + } + + @Override + public boolean remove(Object o) { + return (Boolean)AbstractValueList.throwException(); + } + + @Override + public boolean containsAll(Collection c) { + return this.list.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + return (Boolean)AbstractValueList.throwException(); + } + + @Override + public boolean addAll(int index, Collection c) { + return (Boolean)AbstractValueList.throwException(); + } + + @Override + public boolean removeAll(Collection c) { + return (Boolean)AbstractValueList.throwException(); + } + + @Override + public boolean retainAll(Collection c) { + return (Boolean)AbstractValueList.throwException(); + } + + @Override + public void clear() { + AbstractValueList.throwException(); + } + + @Override + public T get(int index) { + return this.list.get(index); + } + + @Override + public T set(int index, T element) { + return AbstractValueList.throwException(); + } + + @Override + public void add(int index, T element) { + AbstractValueList.throwException(); + } + + @Override + public T remove(int index) { + return AbstractValueList.throwException(); + } + + @Override + public int indexOf(Object o) { + return this.list.indexOf(o); + } + + @Override + public int lastIndexOf(Object o) { + return this.list.lastIndexOf(o); + } + + @Override + public ListIterator listIterator() { + return this.list.listIterator(); + } + + @Override + public ListIterator listIterator(int index) { + return this.list.listIterator(index); + } + + @Override + public List subList(int fromIndex, int toIndex) { + return this.list.subList(fromIndex, toIndex); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + AbstractValueList that = (AbstractValueList)o; + return Objects.equals(this.list, that.list); + } + + @Override + public int hashCode() { + return this.list.hashCode(); + } + + private static T throwException() { + throw new UnsupportedOperationException("Can't modify config entries"); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/AbstractValueMap.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/AbstractValueMap.java new file mode 100644 index 0000000..df4022c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/AbstractValueMap.java @@ -0,0 +1,120 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.custom; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +public class AbstractValueMap +implements Map { + protected final Map map; + + protected AbstractValueMap(Map map) { + this.map = Collections.unmodifiableMap(new LinkedHashMap(map)); + } + + @Override + public int size() { + return this.map.size(); + } + + @Override + public boolean isEmpty() { + return this.map.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return this.map.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return this.map.containsValue(value); + } + + @Override + public V get(Object key) { + return this.map.get(key); + } + + @Override + public V put(K key, V value) { + return (V)AbstractValueMap.throwException(); + } + + @Override + public V remove(Object key) { + return (V)AbstractValueMap.throwException(); + } + + @Override + public void putAll(Map m) { + AbstractValueMap.throwException(); + } + + @Override + public void clear() { + AbstractValueMap.throwException(); + } + + @Override + public Set keySet() { + return this.map.keySet(); + } + + @Override + public Collection values() { + return this.map.values(); + } + + @Override + public Set> entrySet() { + return this.map.entrySet(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + AbstractValueMap that = (AbstractValueMap)o; + return this.map.equals(that.map); + } + + @Override + public int hashCode() { + return this.map.hashCode(); + } + + private static T throwException() { + throw new UnsupportedOperationException("Can't modify config entries"); + } + + public static abstract class Builder> { + protected final Map map = new LinkedHashMap(); + + protected Builder() { + } + + public Builder put(K key, V value) { + this.map.put(key, value); + return this; + } + + public Builder putAll(Map map) { + this.map.putAll(map); + return this; + } + + public abstract M build(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/IntegerList.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/IntegerList.java new file mode 100644 index 0000000..3421cab --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/IntegerList.java @@ -0,0 +1,27 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.custom; + +import de.maxhenkel.voicechat.configbuilder.custom.AbstractValueList; +import java.util.List; + +public class IntegerList +extends AbstractValueList { + protected IntegerList(Integer ... values) { + super(values); + } + + protected IntegerList(List values) { + super(values); + } + + public static IntegerList of(Integer ... values) { + return new IntegerList(values); + } + + public static IntegerList of(List values) { + return new IntegerList(values); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/StringList.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/StringList.java new file mode 100644 index 0000000..f279aab --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/StringList.java @@ -0,0 +1,27 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.custom; + +import de.maxhenkel.voicechat.configbuilder.custom.AbstractValueList; +import java.util.List; + +public class StringList +extends AbstractValueList { + protected StringList(String ... values) { + super(values); + } + + protected StringList(List values) { + super(values); + } + + public static StringList of(String ... values) { + return new StringList(values); + } + + public static StringList of(List values) { + return new StringList(values); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/StringMap.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/StringMap.java new file mode 100644 index 0000000..de730d0 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/StringMap.java @@ -0,0 +1,36 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.custom; + +import de.maxhenkel.voicechat.configbuilder.custom.AbstractValueMap; +import java.util.Collections; +import java.util.Map; + +public class StringMap +extends AbstractValueMap { + protected StringMap(Map map) { + super(map); + } + + public static StringMap of(Map map) { + return new StringMap(map); + } + + public static StringMap of() { + return new StringMap(Collections.emptyMap()); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder + extends AbstractValueMap.Builder { + @Override + public StringMap build() { + return new StringMap(this.map); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/IntegerListValueSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/IntegerListValueSerializer.java new file mode 100644 index 0000000..e132604 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/IntegerListValueSerializer.java @@ -0,0 +1,42 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.custom.serializer; + +import de.maxhenkel.voicechat.configbuilder.custom.IntegerList; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import java.util.ArrayList; +import javax.annotation.Nullable; + +public class IntegerListValueSerializer +implements ValueSerializer { + public static final IntegerListValueSerializer INSTANCE = new IntegerListValueSerializer(); + + @Override + @Nullable + public IntegerList deserialize(String str) { + ArrayList resultList = new ArrayList(); + for (String s : str.split(",")) { + try { + resultList.add(Integer.valueOf(s)); + } + catch (NumberFormatException e) { + return null; + } + } + return IntegerList.of(resultList); + } + + @Override + public String serialize(IntegerList val) { + ArrayList resultList = new ArrayList(val.size()); + for (Integer i : val) { + resultList.add(String.valueOf(i)); + } + return String.join((CharSequence)",", resultList); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/StringListValueSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/StringListValueSerializer.java new file mode 100644 index 0000000..793b41a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/StringListValueSerializer.java @@ -0,0 +1,32 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.custom.serializer; + +import de.maxhenkel.voicechat.configbuilder.custom.StringList; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import java.util.ArrayList; + +public class StringListValueSerializer +implements ValueSerializer { + public static final StringListValueSerializer INSTANCE = new StringListValueSerializer(); + + @Override + public StringList deserialize(String str) { + ArrayList resultList = new ArrayList(); + for (String s : str.split("(? resultList = new ArrayList(val.size()); + for (String str : val) { + resultList.add(str.replace(",", "\\,")); + } + return String.join((CharSequence)",", resultList); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/StringMapValueSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/StringMapValueSerializer.java new file mode 100644 index 0000000..e3822a0 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/StringMapValueSerializer.java @@ -0,0 +1,55 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.custom.serializer; + +import de.maxhenkel.voicechat.configbuilder.custom.StringMap; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +public class StringMapValueSerializer +implements ValueSerializer { + public static final StringMapValueSerializer INSTANCE = new StringMapValueSerializer(); + public static final Pattern QUOTE_ESCAPE_PATTERN = Pattern.compile("\"((?:(?![\"\\\\]).|\\\\.)*)\"\\s*=\\s*\"((?:(?![\"\\\\]).|\\\\.)*)\""); + + @Override + @Nullable + public StringMap deserialize(String str) { + boolean matches = QUOTE_ESCAPE_PATTERN.splitAsStream(str).allMatch(s -> s.trim().isEmpty() || s.trim().equals(",")); + if (!matches) { + return null; + } + LinkedHashMap map = new LinkedHashMap(); + Matcher matcher = QUOTE_ESCAPE_PATTERN.matcher(str); + while (matcher.find()) { + map.put(StringMapValueSerializer.unescape(matcher.group(1)), StringMapValueSerializer.unescape(matcher.group(2))); + } + return StringMap.of(map); + } + + @Override + public String serialize(StringMap val) { + ArrayList resultList = new ArrayList(val.size()); + for (Map.Entry entry : val.entrySet()) { + resultList.add("\"" + StringMapValueSerializer.escape((String)entry.getKey()) + "\"=\"" + StringMapValueSerializer.escape((String)entry.getValue()) + "\""); + } + return String.join((CharSequence)",", resultList); + } + + private static String escape(String input) { + return input.replace("\\", "\\\\").replace("\"", "\\\""); + } + + private static String unescape(String input) { + return input.replace("\\\"", "\"").replace("\\\\", "\\"); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/UUIDSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/UUIDSerializer.java new file mode 100644 index 0000000..a0d4593 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/custom/serializer/UUIDSerializer.java @@ -0,0 +1,23 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.custom.serializer; + +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import java.util.UUID; + +public class UUIDSerializer +implements ValueSerializer { + public static final UUIDSerializer INSTANCE = new UUIDSerializer(); + + @Override + public UUID deserialize(String str) { + return UUID.fromString(str); + } + + @Override + public String serialize(UUID val) { + return val.toString(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/AbstractConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/AbstractConfigEntry.java new file mode 100644 index 0000000..8ed87e8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/AbstractConfigEntry.java @@ -0,0 +1,134 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.Config; +import de.maxhenkel.voicechat.configbuilder.entry.ConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import java.util.Objects; +import javax.annotation.Nullable; + +public abstract class AbstractConfigEntry +implements ConfigEntry { + protected final CommentedPropertyConfig config; + protected final ValueSerializer serializer; + protected String[] comments; + protected String key; + protected T def; + @Nullable + protected T value; + + public AbstractConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, T def) { + Objects.requireNonNull(config); + Objects.requireNonNull(serializer); + Objects.requireNonNull(comments); + Objects.requireNonNull(key); + Objects.requireNonNull(def); + this.config = config; + this.serializer = serializer; + this.comments = comments; + this.key = key; + this.def = def; + } + + public void reload() { + if (this.config.getProperties().containsKey(this.key)) { + T val = this.serializer.deserialize(this.config.getProperties().get(this.key)); + if (val == null) { + this.reset(); + } else { + this.value = this.fixValue(val); + this.syncEntryToProperties(); + } + } else { + this.reset(); + } + } + + @Override + public T get() { + return this.value; + } + + @Override + public AbstractConfigEntry set(T value) { + if (this.value != null && this.value.equals(value)) { + return this; + } + this.value = this.fixValue(value); + this.syncEntryToProperties(); + return this; + } + + @Override + public String getKey() { + return this.key; + } + + @Override + public AbstractConfigEntry comment(String ... comments) { + this.comments = comments; + this.syncEntryToProperties(); + return this; + } + + @Override + public String[] getComments() { + return this.comments; + } + + @Override + public ConfigEntry reset() { + this.value = this.def; + this.syncEntryToProperties(); + return this; + } + + private void syncEntryToProperties() { + String serialized = this.serializer.serialize(this.value); + if (serialized == null) { + if (this.value == this.def) { + throw new IllegalStateException("Failed to serialize default value"); + } + this.reset(); + return; + } + this.config.getProperties().set(this.key, serialized, this.comments); + } + + @Override + public ConfigEntry save() { + this.config.save(); + return this; + } + + @Override + public ConfigEntry saveSync() { + this.config.saveSync(); + return this; + } + + T fixValue(T value) { + return value; + } + + @Override + public T getDefault() { + return this.def; + } + + @Override + public Config getConfig() { + return this.config; + } + + public ValueSerializer getSerializer() { + return this.serializer; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/AbstractRangedConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/AbstractRangedConfigEntry.java new file mode 100644 index 0000000..5f2a066 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/AbstractRangedConfigEntry.java @@ -0,0 +1,49 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nonnull + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.RangedConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public abstract class AbstractRangedConfigEntry +extends AbstractConfigEntry +implements RangedConfigEntry { + @Nonnull + protected final T min; + @Nonnull + protected final T max; + + public AbstractRangedConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, T def, @Nullable T min, @Nullable T max) { + super(config, serializer, comments, key, def); + this.min = min != null ? min : this.minimumPossibleValue(); + this.max = max != null ? max : this.maximumPossibleValue(); + } + + @Override + @Nonnull + public T getMin() { + return this.min; + } + + @Override + @Nonnull + public T getMax() { + return this.max; + } + + @Nonnull + abstract T minimumPossibleValue(); + + @Nonnull + abstract T maximumPossibleValue(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/BooleanConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/BooleanConfigEntry.java new file mode 100644 index 0000000..6e432b1 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/BooleanConfigEntry.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; + +public class BooleanConfigEntry +extends AbstractConfigEntry { + public BooleanConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, Boolean def) { + super(config, serializer, comments, key, def); + this.reload(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/ConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/ConfigEntry.java new file mode 100644 index 0000000..1da2262 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/ConfigEntry.java @@ -0,0 +1,29 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.Config; + +public interface ConfigEntry { + public ConfigEntry comment(String ... var1); + + public String[] getComments(); + + public T get(); + + public ConfigEntry set(T var1); + + public String getKey(); + + public ConfigEntry reset(); + + public ConfigEntry save(); + + public ConfigEntry saveSync(); + + public T getDefault(); + + public Config getConfig(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/DoubleConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/DoubleConfigEntry.java new file mode 100644 index 0000000..da2d53f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/DoubleConfigEntry.java @@ -0,0 +1,40 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nonnull + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractRangedConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class DoubleConfigEntry +extends AbstractRangedConfigEntry { + public DoubleConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, Double def, @Nullable Double min, @Nullable Double max) { + super(config, serializer, comments, key, def, min, max); + this.reload(); + } + + @Override + @Nonnull + Double minimumPossibleValue() { + return Double.MIN_VALUE; + } + + @Override + @Nonnull + Double maximumPossibleValue() { + return Double.MAX_VALUE; + } + + @Override + Double fixValue(Double value) { + return Math.max(Math.min(value, (Double)this.max), (Double)this.min); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/EnumConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/EnumConfigEntry.java new file mode 100644 index 0000000..c3981e8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/EnumConfigEntry.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; + +public class EnumConfigEntry> +extends AbstractConfigEntry { + public EnumConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, E def) { + super(config, serializer, comments, key, def); + this.reload(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/FloatConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/FloatConfigEntry.java new file mode 100644 index 0000000..53442b8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/FloatConfigEntry.java @@ -0,0 +1,40 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nonnull + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractRangedConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class FloatConfigEntry +extends AbstractRangedConfigEntry { + public FloatConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, Float def, @Nullable Float min, @Nullable Float max) { + super(config, serializer, comments, key, def, min, max); + this.reload(); + } + + @Override + @Nonnull + Float minimumPossibleValue() { + return Float.valueOf(Float.MIN_VALUE); + } + + @Override + @Nonnull + Float maximumPossibleValue() { + return Float.valueOf(Float.MAX_VALUE); + } + + @Override + Float fixValue(Float value) { + return Float.valueOf(Math.max(Math.min(value.floatValue(), ((Float)this.max).floatValue()), ((Float)this.min).floatValue())); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/GenericConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/GenericConfigEntry.java new file mode 100644 index 0000000..9abe5b6 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/GenericConfigEntry.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; + +public class GenericConfigEntry +extends AbstractConfigEntry { + public GenericConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, T def) { + super(config, serializer, comments, key, def); + this.reload(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/IntegerConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/IntegerConfigEntry.java new file mode 100644 index 0000000..2fc1cfe --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/IntegerConfigEntry.java @@ -0,0 +1,40 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nonnull + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractRangedConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class IntegerConfigEntry +extends AbstractRangedConfigEntry { + public IntegerConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, Integer def, @Nullable Integer min, @Nullable Integer max) { + super(config, serializer, comments, key, def, min, max); + this.reload(); + } + + @Override + @Nonnull + Integer minimumPossibleValue() { + return Integer.MIN_VALUE; + } + + @Override + @Nonnull + Integer maximumPossibleValue() { + return Integer.MAX_VALUE; + } + + @Override + Integer fixValue(Integer value) { + return Math.max(Math.min(value, (Integer)this.max), (Integer)this.min); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/LongConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/LongConfigEntry.java new file mode 100644 index 0000000..868ef97 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/LongConfigEntry.java @@ -0,0 +1,40 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nonnull + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractRangedConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class LongConfigEntry +extends AbstractRangedConfigEntry { + public LongConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, Long def, @Nullable Long min, @Nullable Long max) { + super(config, serializer, comments, key, def, min, max); + this.reload(); + } + + @Override + @Nonnull + Long minimumPossibleValue() { + return Long.MIN_VALUE; + } + + @Override + @Nonnull + Long maximumPossibleValue() { + return Long.MAX_VALUE; + } + + @Override + Long fixValue(Long value) { + return Math.max(Math.min(value, (Long)this.max), (Long)this.min); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/RangedConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/RangedConfigEntry.java new file mode 100644 index 0000000..ebe99ed --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/RangedConfigEntry.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nonnull + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.entry.ConfigEntry; +import javax.annotation.Nonnull; + +public interface RangedConfigEntry +extends ConfigEntry { + @Nonnull + public T getMin(); + + @Nonnull + public T getMax(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/StringConfigEntry.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/StringConfigEntry.java new file mode 100644 index 0000000..e975311 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/StringConfigEntry.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.entry; + +import de.maxhenkel.voicechat.configbuilder.CommentedPropertyConfig; +import de.maxhenkel.voicechat.configbuilder.entry.AbstractConfigEntry; +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; + +public class StringConfigEntry +extends AbstractConfigEntry { + public StringConfigEntry(CommentedPropertyConfig config, ValueSerializer serializer, String[] comments, String key, String def) { + super(config, serializer, comments, key, def); + this.reload(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/BooleanSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/BooleanSerializer.java new file mode 100644 index 0000000..66b42e7 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/BooleanSerializer.java @@ -0,0 +1,28 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry.serializer; + +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nullable; + +public class BooleanSerializer +implements ValueSerializer { + public static final BooleanSerializer INSTANCE = new BooleanSerializer(); + + @Override + @Nullable + public Boolean deserialize(String str) { + return Boolean.valueOf(str); + } + + @Override + @Nullable + public String serialize(Boolean val) { + return String.valueOf(val); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/DoubleSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/DoubleSerializer.java new file mode 100644 index 0000000..a6322f7 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/DoubleSerializer.java @@ -0,0 +1,33 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry.serializer; + +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nullable; + +public class DoubleSerializer +implements ValueSerializer { + public static final DoubleSerializer INSTANCE = new DoubleSerializer(); + + @Override + @Nullable + public Double deserialize(String str) { + try { + return Double.parseDouble(str); + } + catch (NumberFormatException e) { + return null; + } + } + + @Override + @Nullable + public String serialize(Double val) { + return String.valueOf(val); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/EnumSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/EnumSerializer.java new file mode 100644 index 0000000..d2c0dfe --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/EnumSerializer.java @@ -0,0 +1,37 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry.serializer; + +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nullable; + +public class EnumSerializer> +implements ValueSerializer { + protected Class enumClass; + + public EnumSerializer(Class enumClass) { + this.enumClass = enumClass; + } + + @Override + @Nullable + public E deserialize(String str) { + try { + return Enum.valueOf(this.enumClass, str); + } + catch (Exception e) { + return null; + } + } + + @Override + @Nullable + public String serialize(E val) { + return ((Enum)val).name(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/FloatSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/FloatSerializer.java new file mode 100644 index 0000000..bb6c715 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/FloatSerializer.java @@ -0,0 +1,33 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry.serializer; + +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nullable; + +public class FloatSerializer +implements ValueSerializer { + public static final FloatSerializer INSTANCE = new FloatSerializer(); + + @Override + @Nullable + public Float deserialize(String str) { + try { + return Float.valueOf(Float.parseFloat(str)); + } + catch (NumberFormatException e) { + return null; + } + } + + @Override + @Nullable + public String serialize(Float val) { + return String.valueOf(val); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/IntegerSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/IntegerSerializer.java new file mode 100644 index 0000000..af1e84f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/IntegerSerializer.java @@ -0,0 +1,33 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry.serializer; + +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nullable; + +public class IntegerSerializer +implements ValueSerializer { + public static final IntegerSerializer INSTANCE = new IntegerSerializer(); + + @Override + @Nullable + public Integer deserialize(String str) { + try { + return Integer.parseInt(str); + } + catch (NumberFormatException e) { + return null; + } + } + + @Override + @Nullable + public String serialize(Integer val) { + return String.valueOf(val); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/LongSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/LongSerializer.java new file mode 100644 index 0000000..7c46a98 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/LongSerializer.java @@ -0,0 +1,33 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry.serializer; + +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nullable; + +public class LongSerializer +implements ValueSerializer { + public static final LongSerializer INSTANCE = new LongSerializer(); + + @Override + @Nullable + public Long deserialize(String str) { + try { + return Long.parseLong(str); + } + catch (NumberFormatException e) { + return null; + } + } + + @Override + @Nullable + public String serialize(Long val) { + return String.valueOf(val); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/StringSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/StringSerializer.java new file mode 100644 index 0000000..b96a62e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/StringSerializer.java @@ -0,0 +1,28 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry.serializer; + +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import javax.annotation.Nullable; + +public class StringSerializer +implements ValueSerializer { + public static final StringSerializer INSTANCE = new StringSerializer(); + + @Override + @Nullable + public String deserialize(String str) { + return str; + } + + @Override + @Nullable + public String serialize(String val) { + return val; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/ValueSerializable.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/ValueSerializable.java new file mode 100644 index 0000000..5b3bbe3 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/ValueSerializable.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.configbuilder.entry.serializer; + +import de.maxhenkel.voicechat.configbuilder.entry.serializer.ValueSerializer; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(value=RetentionPolicy.RUNTIME) +@Target(value={ElementType.TYPE}) +public @interface ValueSerializable { + public Class> value(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/ValueSerializer.java b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/ValueSerializer.java new file mode 100644 index 0000000..d7550e1 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/configbuilder/entry/serializer/ValueSerializer.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.configbuilder.entry.serializer; + +import javax.annotation.Nullable; + +public interface ValueSerializer { + @Nullable + public T deserialize(String var1); + + @Nullable + public String serialize(T var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/debug/CooldownTimer.java b/source/decompiled/de/maxhenkel/voicechat/debug/CooldownTimer.java new file mode 100644 index 0000000..6fb3052 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/debug/CooldownTimer.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.debug; + +import java.util.concurrent.ConcurrentHashMap; + +public class CooldownTimer { + private static ConcurrentHashMap cooldowns = new ConcurrentHashMap(); + + public static void run(String id, long time, Runnable runnable) { + if (System.currentTimeMillis() - cooldowns.getOrDefault(id, 0L) > time) { + cooldowns.put(id, System.currentTimeMillis()); + runnable.run(); + } + } + + public static void run(String id, Runnable runnable) { + CooldownTimer.run(id, 10000L, runnable); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/debug/PingHandler.java b/source/decompiled/de/maxhenkel/voicechat/debug/PingHandler.java new file mode 100644 index 0000000..6ca7263 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/debug/PingHandler.java @@ -0,0 +1,46 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * io.netty.buffer.Unpooled + */ +package de.maxhenkel.voicechat.debug; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.server.Server; +import io.netty.buffer.Unpooled; +import java.net.SocketAddress; +import java.util.UUID; + +public class PingHandler { + public static final UUID PING_V1 = UUID.fromString("58bc9ae9-c7a8-45e4-a11c-efbb67199425"); + private static final int PAYLOAD_SIZE = 24; + + public static boolean onPacket(Server server, SocketAddress socketAddress, UUID playerID, FriendlyByteBuf buf) { + if (!Voicechat.SERVER_CONFIG.allowPings.get().booleanValue()) { + return false; + } + if (!PING_V1.equals(playerID)) { + return false; + } + try { + byte[] payload = buf.readByteArray(24); + FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.wrappedBuffer((byte[])payload)); + UUID id = buffer.readUUID(); + long timestamp = buffer.readLong(); + Voicechat.LOGGER.debug("Received ping {} from {}", id, socketAddress); + FriendlyByteBuf responseBuffer = new FriendlyByteBuf(Unpooled.buffer((int)24)); + responseBuffer.writeUUID(id); + responseBuffer.writeLong(timestamp); + byte[] response = new byte[responseBuffer.readableBytes()]; + responseBuffer.readBytes(response); + server.getSocket().send(response, socketAddress); + } + catch (Exception e) { + Voicechat.LOGGER.debug("Failed to send ping to {}: {}", socketAddress, e.getMessage()); + } + return true; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/debug/VoicechatUncaughtExceptionHandler.java b/source/decompiled/de/maxhenkel/voicechat/debug/VoicechatUncaughtExceptionHandler.java new file mode 100644 index 0000000..398af6f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/debug/VoicechatUncaughtExceptionHandler.java @@ -0,0 +1,15 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.debug; + +import de.maxhenkel.voicechat.Voicechat; + +public class VoicechatUncaughtExceptionHandler +implements Thread.UncaughtExceptionHandler { + @Override + public void uncaughtException(Thread t, Throwable e) { + Voicechat.LOGGER.error("Uncaught exception in thread {}", t.getName(), e); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/integration/commodore/CommodoreCommands.java b/source/decompiled/de/maxhenkel/voicechat/integration/commodore/CommodoreCommands.java new file mode 100644 index 0000000..7748660 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/integration/commodore/CommodoreCommands.java @@ -0,0 +1,44 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.arguments.ArgumentType + * com.mojang.brigadier.arguments.StringArgumentType + * com.mojang.brigadier.builder.ArgumentBuilder + * com.mojang.brigadier.builder.LiteralArgumentBuilder + * com.mojang.brigadier.builder.RequiredArgumentBuilder + * com.mojang.brigadier.suggestion.SuggestionProvider + */ +package de.maxhenkel.voicechat.integration.commodore; + +import com.mojang.brigadier.arguments.ArgumentType; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.builder.RequiredArgumentBuilder; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.commodore.Commodore; +import de.maxhenkel.voicechat.integration.commodore.GroupNameSuggestionProvider; +import de.maxhenkel.voicechat.integration.commodore.MissingArgumentTypeException; + +public class CommodoreCommands { + public static void registerCompletions(Commodore commodore) throws MissingArgumentTypeException { + ArgumentType playerArgument = CommodoreCommands.playerArgument(); + ArgumentType uuidArgument = CommodoreCommands.uuidArgument(); + if (playerArgument == null || uuidArgument == null) { + throw new MissingArgumentTypeException("Could not acquire argument types"); + } + LiteralArgumentBuilder literalBuilder = (LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)((LiteralArgumentBuilder)LiteralArgumentBuilder.literal((String)"voicechat").then((ArgumentBuilder)LiteralArgumentBuilder.literal((String)"help"))).then(LiteralArgumentBuilder.literal((String)"test").then((ArgumentBuilder)RequiredArgumentBuilder.argument((String)"target", playerArgument)))).then(LiteralArgumentBuilder.literal((String)"invite").then((ArgumentBuilder)RequiredArgumentBuilder.argument((String)"target", playerArgument)))).then(LiteralArgumentBuilder.literal((String)"join").then((ArgumentBuilder)RequiredArgumentBuilder.argument((String)"group_id", uuidArgument)))).then(LiteralArgumentBuilder.literal((String)"join").then(RequiredArgumentBuilder.argument((String)"group_id", uuidArgument).then((ArgumentBuilder)RequiredArgumentBuilder.argument((String)"password", (ArgumentType)StringArgumentType.string()))))).then(LiteralArgumentBuilder.literal((String)"join").then((ArgumentBuilder)RequiredArgumentBuilder.argument((String)"group_name", (ArgumentType)StringArgumentType.string()).suggests((SuggestionProvider)GroupNameSuggestionProvider.INSTANCE)))).then(LiteralArgumentBuilder.literal((String)"join").then(RequiredArgumentBuilder.argument((String)"group_name", (ArgumentType)StringArgumentType.string()).suggests((SuggestionProvider)GroupNameSuggestionProvider.INSTANCE).then((ArgumentBuilder)RequiredArgumentBuilder.argument((String)"password", (ArgumentType)StringArgumentType.string()))))).then((ArgumentBuilder)LiteralArgumentBuilder.literal((String)"leave")); + commodore.register(literalBuilder); + } + + private static ArgumentType playerArgument() { + return Voicechat.compatibility.playerArgument(); + } + + private static ArgumentType uuidArgument() { + return Voicechat.compatibility.uuidArgument(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/integration/commodore/GroupNameSuggestionProvider.java b/source/decompiled/de/maxhenkel/voicechat/integration/commodore/GroupNameSuggestionProvider.java new file mode 100644 index 0000000..b7c6f07 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/integration/commodore/GroupNameSuggestionProvider.java @@ -0,0 +1,39 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * com.mojang.brigadier.context.CommandContext + * com.mojang.brigadier.suggestion.SuggestionProvider + * com.mojang.brigadier.suggestion.Suggestions + * com.mojang.brigadier.suggestion.SuggestionsBuilder + */ +package de.maxhenkel.voicechat.integration.commodore; + +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionProvider; +import com.mojang.brigadier.suggestion.Suggestions; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.voice.server.Group; +import de.maxhenkel.voicechat.voice.server.Server; +import java.util.concurrent.CompletableFuture; + +public class GroupNameSuggestionProvider +implements SuggestionProvider { + public static final GroupNameSuggestionProvider INSTANCE = new GroupNameSuggestionProvider(); + + public CompletableFuture getSuggestions(CommandContext context, SuggestionsBuilder builder) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return builder.buildFuture(); + } + server.getGroupManager().getGroups().values().stream().map(Group::getName).distinct().map(s -> { + if (s.contains(" ")) { + return String.format("\"%s\"", s); + } + return s; + }).forEach(arg_0 -> ((SuggestionsBuilder)builder).suggest(arg_0)); + return builder.buildFuture(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/integration/commodore/MissingArgumentTypeException.java b/source/decompiled/de/maxhenkel/voicechat/integration/commodore/MissingArgumentTypeException.java new file mode 100644 index 0000000..fda7735 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/integration/commodore/MissingArgumentTypeException.java @@ -0,0 +1,12 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.integration.commodore; + +public class MissingArgumentTypeException +extends Exception { + public MissingArgumentTypeException(String message) { + super(message); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/integration/placeholderapi/VoicechatExpansion.java b/source/decompiled/de/maxhenkel/voicechat/integration/placeholderapi/VoicechatExpansion.java new file mode 100644 index 0000000..6f7e6b5 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/integration/placeholderapi/VoicechatExpansion.java @@ -0,0 +1,64 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * me.clip.placeholderapi.expansion.PlaceholderExpansion + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.integration.placeholderapi; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import de.maxhenkel.voicechat.voice.server.Server; +import javax.annotation.Nullable; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import org.bukkit.entity.Player; + +public class VoicechatExpansion +extends PlaceholderExpansion { + public String getIdentifier() { + return "voicechat"; + } + + public String getAuthor() { + return String.join((CharSequence)", ", Voicechat.INSTANCE.getDescription().getAuthors()); + } + + public String getVersion() { + return Voicechat.INSTANCE.getDescription().getVersion(); + } + + public boolean persist() { + return true; + } + + @Nullable + public String onPlaceholderRequest(Player player, String params) { + if (player == null) { + return super.onPlaceholderRequest(player, params); + } + if (params.equalsIgnoreCase("installed")) { + return Voicechat.SERVER.isCompatible(player) ? Voicechat.TRANSLATIONS.placeholderVoicechatInstalled.get() : ""; + } + if (params.equalsIgnoreCase("not_installed")) { + return !Voicechat.SERVER.isCompatible(player) ? Voicechat.TRANSLATIONS.placeholderVoicechatNotInstalled.get() : ""; + } + if (params.equalsIgnoreCase("disabled")) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return ""; + } + PlayerState state = server.getPlayerStateManager().getState(player.getUniqueId()); + if (state == null) { + return ""; + } + if (!state.isDisabled()) { + return ""; + } + return Voicechat.TRANSLATIONS.placeholderVoicechatDisabled.get(); + } + return super.onPlaceholderRequest(player, params); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/integration/viaversion/ViaVersionCompatibility.java b/source/decompiled/de/maxhenkel/voicechat/integration/viaversion/ViaVersionCompatibility.java new file mode 100644 index 0000000..24aaef4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/integration/viaversion/ViaVersionCompatibility.java @@ -0,0 +1,51 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.integration.viaversion; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.util.Key; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import javax.annotation.Nullable; + +public class ViaVersionCompatibility { + public static void register() throws Exception { + Class protocol113to1122 = ViaVersionCompatibility.getClass("com.viaversion.viaversion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2"); + if (protocol113to1122 != null) { + ViaVersionCompatibility.registerPackets(protocol113to1122); + return; + } + Class protocol1122to113 = ViaVersionCompatibility.getClass("com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13"); + if (protocol1122to113 != null) { + ViaVersionCompatibility.registerPackets(protocol1122to113); + return; + } + } + + private static void registerPackets(Class protocolClass) throws Exception { + Field mappingsField = protocolClass.getDeclaredField("MAPPINGS"); + Object mappings = mappingsField.get(null); + Method getChannelMappings = mappings.getClass().getDeclaredMethod("getChannelMappings", new Class[0]); + Object mappingMap = getChannelMappings.invoke(mappings, new Object[0]); + Method put = mappingMap.getClass().getDeclaredMethod("put", Object.class, Object.class); + for (String id : Voicechat.netManager.getPackets()) { + Key key = Key.parse(id); + put.invoke(mappingMap, String.format("%s:%s", "vc", key.getValue()), String.format("%s:%s", "voicechat", key.getValue())); + } + } + + @Nullable + private static Class getClass(String name) { + try { + return Class.forName(name); + } + catch (ClassNotFoundException e) { + return null; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/logging/JavaLoggingLogger.java b/source/decompiled/de/maxhenkel/voicechat/logging/JavaLoggingLogger.java new file mode 100644 index 0000000..21a31ff --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/logging/JavaLoggingLogger.java @@ -0,0 +1,94 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.logging; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.logging.LogLevel; +import de.maxhenkel.voicechat.logging.VoicechatLogger; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class JavaLoggingLogger +implements VoicechatLogger { + private final boolean debugMode; + private final Logger logger; + private static final String PLACEHOLDER = "{}"; + + public JavaLoggingLogger(Logger logger) { + this.logger = logger; + this.debugMode = Voicechat.debugMode(); + } + + @Override + public void log(LogLevel level, String message, Object ... args) { + if (!this.isEnabled(level)) { + return; + } + Throwable throwable = null; + if (args.length > 0 && args[args.length - 1] instanceof Throwable) { + throwable = (Throwable)args[args.length - 1]; + Object[] newArgs = new Object[args.length - 1]; + System.arraycopy(args, 0, newArgs, 0, newArgs.length); + args = newArgs; + } + this.logger.log(this.fromLogLevel(level), this.replacePlaceholders(message, args), throwable); + } + + private String replacePlaceholders(String message, Object ... args) { + StringBuilder formattedMessage = new StringBuilder(); + int placeholderStart = message.indexOf(PLACEHOLDER); + for (int argIndex = 0; placeholderStart >= 0 && argIndex < args.length; ++argIndex) { + formattedMessage.append(message, 0, placeholderStart); + formattedMessage.append(args[argIndex]); + message = message.substring(placeholderStart + PLACEHOLDER.length()); + placeholderStart = message.indexOf(PLACEHOLDER); + } + formattedMessage.append(message); + return formattedMessage.toString(); + } + + @Override + public boolean isEnabled(LogLevel level) { + return this.logger.isLoggable(this.fromLogLevel(level)); + } + + private Level fromLogLevel(LogLevel level) { + if (this.debugMode) { + switch (level) { + case TRACE: { + return Level.ALL; + } + case WARN: { + return Level.WARNING; + } + case ERROR: + case FATAL: { + return Level.SEVERE; + } + } + return Level.INFO; + } + switch (level) { + case TRACE: { + return Level.ALL; + } + case DEBUG: { + return Level.CONFIG; + } + case WARN: { + return Level.WARNING; + } + case ERROR: + case FATAL: { + return Level.SEVERE; + } + } + return Level.INFO; + } + + public Logger getLogger() { + return this.logger; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/logging/LogLevel.java b/source/decompiled/de/maxhenkel/voicechat/logging/LogLevel.java new file mode 100644 index 0000000..c671af6 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/logging/LogLevel.java @@ -0,0 +1,15 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.logging; + +public enum LogLevel { + TRACE, + DEBUG, + INFO, + WARN, + ERROR, + FATAL; + +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/logging/VoicechatLogger.java b/source/decompiled/de/maxhenkel/voicechat/logging/VoicechatLogger.java new file mode 100644 index 0000000..097510b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/logging/VoicechatLogger.java @@ -0,0 +1,37 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.logging; + +import de.maxhenkel.voicechat.logging.LogLevel; + +public interface VoicechatLogger { + public void log(LogLevel var1, String var2, Object ... var3); + + public boolean isEnabled(LogLevel var1); + + default public void trace(String message, Object ... args) { + this.log(LogLevel.TRACE, message, args); + } + + default public void debug(String message, Object ... args) { + this.log(LogLevel.DEBUG, message, args); + } + + default public void info(String message, Object ... args) { + this.log(LogLevel.INFO, message, args); + } + + default public void warn(String message, Object ... args) { + this.log(LogLevel.WARN, message, args); + } + + default public void error(String message, Object ... args) { + this.log(LogLevel.ERROR, message, args); + } + + default public void fatal(String message, Object ... args) { + this.log(LogLevel.FATAL, message, args); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/AddCategoryPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/AddCategoryPacket.java new file mode 100644 index 0000000..08f6954 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/AddCategoryPacket.java @@ -0,0 +1,44 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.plugins.impl.VolumeCategoryImpl; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; + +public class AddCategoryPacket +implements Packet { + public static final Key ADD_CATEGORY = Voicechat.compatibility.createNamespacedKey("add_category"); + private VolumeCategoryImpl category; + + public AddCategoryPacket() { + } + + public AddCategoryPacket(VolumeCategoryImpl category) { + this.category = category; + } + + public VolumeCategoryImpl getCategory() { + return this.category; + } + + @Override + public Key getID() { + return ADD_CATEGORY; + } + + @Override + public AddCategoryPacket fromBytes(FriendlyByteBuf buf) { + this.category = VolumeCategoryImpl.fromBytes(buf); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + this.category.toBytes(buf); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/AddGroupPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/AddGroupPacket.java new file mode 100644 index 0000000..7980f57 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/AddGroupPacket.java @@ -0,0 +1,44 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import de.maxhenkel.voicechat.voice.common.ClientGroup; + +public class AddGroupPacket +implements Packet { + public static final Key ADD_ADD_GROUP = Voicechat.compatibility.createNamespacedKey("add_group"); + private ClientGroup group; + + public AddGroupPacket() { + } + + public AddGroupPacket(ClientGroup group) { + this.group = group; + } + + public ClientGroup getGroup() { + return this.group; + } + + @Override + public Key getID() { + return ADD_ADD_GROUP; + } + + @Override + public AddGroupPacket fromBytes(FriendlyByteBuf buf) { + this.group = ClientGroup.fromBytes(buf); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + this.group.toBytes(buf); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/CreateGroupPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/CreateGroupPacket.java new file mode 100644 index 0000000..392e20b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/CreateGroupPacket.java @@ -0,0 +1,80 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.plugins.impl.GroupImpl; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public class CreateGroupPacket +implements Packet { + public static final Key CREATE_GROUP = Voicechat.compatibility.createNamespacedKey("create_group"); + private String name; + @Nullable + private String password; + private Group.Type type; + + public CreateGroupPacket() { + } + + public CreateGroupPacket(String name, @Nullable String password, Group.Type type) { + this.name = name; + this.password = password; + this.type = type; + } + + public String getName() { + return this.name; + } + + @Nullable + public String getPassword() { + return this.password; + } + + public Group.Type getType() { + return this.type; + } + + @Override + public Key getID() { + return CREATE_GROUP; + } + + @Override + public void onPacket(Player player) { + Voicechat.SERVER.getServer().getGroupManager().onCreateGroupPacket(player, this); + } + + @Override + public CreateGroupPacket fromBytes(FriendlyByteBuf buf) { + this.name = buf.readUtf(24); + this.password = null; + if (buf.readBoolean()) { + this.password = buf.readUtf(24); + } + this.type = GroupImpl.TypeImpl.fromInt(buf.readShort()); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUtf(this.name, 24); + buf.writeBoolean(this.password != null); + if (this.password != null) { + buf.writeUtf(this.password, 24); + } + buf.writeShort(GroupImpl.TypeImpl.toInt(this.type)); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/JoinGroupPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/JoinGroupPacket.java new file mode 100644 index 0000000..53df9b9 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/JoinGroupPacket.java @@ -0,0 +1,70 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import java.util.UUID; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public class JoinGroupPacket +implements Packet { + public static final Key SET_GROUP = Voicechat.compatibility.createNamespacedKey("set_group"); + private UUID group; + @Nullable + private String password; + + public JoinGroupPacket() { + } + + public JoinGroupPacket(UUID group, @Nullable String password) { + this.group = group; + this.password = password; + } + + public UUID getGroup() { + return this.group; + } + + @Nullable + public String getPassword() { + return this.password; + } + + @Override + public Key getID() { + return SET_GROUP; + } + + @Override + public void onPacket(Player player) { + Voicechat.SERVER.getServer().getGroupManager().onJoinGroupPacket(player, this); + } + + @Override + public JoinGroupPacket fromBytes(FriendlyByteBuf buf) { + this.group = buf.readUUID(); + if (buf.readBoolean()) { + this.password = buf.readUtf(512); + } + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(this.group); + buf.writeBoolean(this.password != null); + if (this.password != null) { + buf.writeUtf(this.password, 512); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/JoinedGroupPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/JoinedGroupPacket.java new file mode 100644 index 0000000..aa4ebda --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/JoinedGroupPacket.java @@ -0,0 +1,63 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import java.util.UUID; +import javax.annotation.Nullable; + +public class JoinedGroupPacket +implements Packet { + public static final Key JOINED_GROUP = Voicechat.compatibility.createNamespacedKey("joined_group"); + @Nullable + private UUID group; + private boolean wrongPassword; + + public JoinedGroupPacket() { + } + + public JoinedGroupPacket(@Nullable UUID group, boolean wrongPassword) { + this.group = group; + this.wrongPassword = wrongPassword; + } + + @Nullable + public UUID getGroup() { + return this.group; + } + + public boolean isWrongPassword() { + return this.wrongPassword; + } + + @Override + public Key getID() { + return JOINED_GROUP; + } + + @Override + public JoinedGroupPacket fromBytes(FriendlyByteBuf buf) { + if (buf.readBoolean()) { + this.group = buf.readUUID(); + } + this.wrongPassword = buf.readBoolean(); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeBoolean(this.group != null); + if (this.group != null) { + buf.writeUUID(this.group); + } + buf.writeBoolean(this.wrongPassword); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/LeaveGroupPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/LeaveGroupPacket.java new file mode 100644 index 0000000..f7f8fdb --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/LeaveGroupPacket.java @@ -0,0 +1,38 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import org.bukkit.entity.Player; + +public class LeaveGroupPacket +implements Packet { + public static final Key LEAVE_GROUP = Voicechat.compatibility.createNamespacedKey("leave_group"); + + @Override + public Key getID() { + return LEAVE_GROUP; + } + + @Override + public void onPacket(Player player) { + Voicechat.SERVER.getServer().getGroupManager().onLeaveGroupPacket(player, this); + } + + @Override + public LeaveGroupPacket fromBytes(FriendlyByteBuf buf) { + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/NetManager.java b/source/decompiled/de/maxhenkel/voicechat/net/NetManager.java new file mode 100644 index 0000000..d02c681 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/NetManager.java @@ -0,0 +1,162 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * io.netty.buffer.Unpooled + * org.bukkit.Bukkit + * org.bukkit.entity.Player + * org.bukkit.event.EventHandler + * org.bukkit.event.Listener + * org.bukkit.event.player.PlayerJoinEvent + * org.bukkit.event.player.PlayerQuitEvent + * org.bukkit.plugin.Plugin + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.AddCategoryPacket; +import de.maxhenkel.voicechat.net.AddGroupPacket; +import de.maxhenkel.voicechat.net.CreateGroupPacket; +import de.maxhenkel.voicechat.net.JoinGroupPacket; +import de.maxhenkel.voicechat.net.JoinedGroupPacket; +import de.maxhenkel.voicechat.net.LeaveGroupPacket; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.net.PlayerStatePacket; +import de.maxhenkel.voicechat.net.PlayerStatesPacket; +import de.maxhenkel.voicechat.net.RemoveCategoryPacket; +import de.maxhenkel.voicechat.net.RemoveGroupPacket; +import de.maxhenkel.voicechat.net.RemovePlayerStatePacket; +import de.maxhenkel.voicechat.net.RequestSecretPacket; +import de.maxhenkel.voicechat.net.SecretPacket; +import de.maxhenkel.voicechat.net.UpdateStatePacket; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import io.netty.buffer.Unpooled; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.HashSet; +import java.util.Set; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.Plugin; + +public class NetManager +implements Listener { + private final Set packets = new HashSet(); + + public void onEnable() { + this.packets.clear(); + Bukkit.getPluginManager().registerEvents((Listener)this, (Plugin)Voicechat.INSTANCE); + try { + this.registerIncomingPacket(UpdateStatePacket.class); + this.registerIncomingPacket(RequestSecretPacket.class); + this.registerIncomingPacket(CreateGroupPacket.class); + this.registerIncomingPacket(JoinGroupPacket.class); + this.registerIncomingPacket(LeaveGroupPacket.class); + this.registerOutgoingPacket(SecretPacket.class); + this.registerOutgoingPacket(PlayerStatesPacket.class); + this.registerOutgoingPacket(PlayerStatePacket.class); + this.registerOutgoingPacket(RemovePlayerStatePacket.class); + this.registerOutgoingPacket(AddGroupPacket.class); + this.registerOutgoingPacket(RemoveGroupPacket.class); + this.registerOutgoingPacket(JoinedGroupPacket.class); + this.registerOutgoingPacket(AddCategoryPacket.class); + this.registerOutgoingPacket(RemoveCategoryPacket.class); + } + catch (Exception e) { + throw new IllegalStateException("Failed to register voice chat packets", e); + } + } + + public void onDisable() { + Set incomingChannels = Bukkit.getMessenger().getIncomingChannels((Plugin)Voicechat.INSTANCE); + Set outgoingChannels = Bukkit.getMessenger().getOutgoingChannels((Plugin)Voicechat.INSTANCE); + for (String channel : incomingChannels) { + Bukkit.getMessenger().unregisterIncomingPluginChannel((Plugin)Voicechat.INSTANCE, channel); + } + for (String channel : outgoingChannels) { + Bukkit.getMessenger().unregisterOutgoingPluginChannel((Plugin)Voicechat.INSTANCE, channel); + } + this.packets.clear(); + } + + public Set getPackets() { + return this.packets; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + Set outgoingChannels = Bukkit.getMessenger().getOutgoingChannels((Plugin)Voicechat.INSTANCE); + for (String channel : outgoingChannels) { + Voicechat.compatibility.addChannel(event.getPlayer(), channel); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Set outgoingChannels = Bukkit.getMessenger().getOutgoingChannels((Plugin)Voicechat.INSTANCE); + for (String channel : outgoingChannels) { + Voicechat.compatibility.removeChannel(event.getPlayer(), channel); + } + } + + private > void registerIncomingPacket(Class packetClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + Constructor c = packetClass.getDeclaredConstructor(new Class[0]); + String id = ((Packet)c.newInstance(new Object[0])).getID().toString(); + this.packets.add(id); + Bukkit.getMessenger().registerIncomingPluginChannel((Plugin)Voicechat.INSTANCE, id, (s, player, bytes) -> { + Packet packet; + if (!Voicechat.SERVER.getRateLimiter().allow(player.getUniqueId())) { + Voicechat.LOGGER.warn("Player {} exceeded packet rate limit", player.getDisplayName()); + player.kickPlayer(Voicechat.TRANSLATIONS.rateLimitKickMessage.get()); + return; + } + try { + packet = (Packet)c.newInstance(new Object[0]); + } + catch (Exception e) { + Voicechat.LOGGER.error("Failed to create new packet instance of {}", packetClass.getSimpleName(), e); + return; + } + FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.wrappedBuffer((byte[])bytes)); + packet.fromBytes(buffer); + packet.onPacket(player); + }); + } + + private > void registerOutgoingPacket(Class packetClass) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + String id = ((Packet)packetClass.getDeclaredConstructor(new Class[0]).newInstance(new Object[0])).getID().toString(); + this.packets.add(id); + Bukkit.getMessenger().registerOutgoingPluginChannel((Plugin)Voicechat.INSTANCE, id); + } + + public static void sendToClient(Player player, Packet p) { + Voicechat.compatibility.runTask(() -> { + if (!Voicechat.SERVER.isCompatible(player)) { + return; + } + try { + FriendlyByteBuf buf = new FriendlyByteBuf(); + p.toBytes(buf); + byte[] bytes = new byte[buf.readableBytes()]; + buf.readBytes(bytes); + player.sendPluginMessage((Plugin)Voicechat.INSTANCE, p.getID().toString(), bytes); + } + catch (Exception e) { + Voicechat.LOGGER.error("Failed to send packet to '{}'", player.getName(), e); + } + }); + } + + public static void sendMessage(Player player, String key, String ... args) { + Voicechat.compatibility.sendTranslationMessage(player, key, args); + } + + public static void sendStatusMessage(Player player, String key, String ... args) { + Voicechat.compatibility.sendStatusMessage(player, key, args); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/Packet.java b/source/decompiled/de/maxhenkel/voicechat/net/Packet.java new file mode 100644 index 0000000..97233ee --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/Packet.java @@ -0,0 +1,23 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import org.bukkit.entity.Player; + +public interface Packet { + public Key getID(); + + public T fromBytes(FriendlyByteBuf var1); + + public void toBytes(FriendlyByteBuf var1); + + default public void onPacket(Player player) { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/PacketRateLimiter.java b/source/decompiled/de/maxhenkel/voicechat/net/PacketRateLimiter.java new file mode 100644 index 0000000..eed6ec2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/PacketRateLimiter.java @@ -0,0 +1,66 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.net; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.entity.Player; + +public class PacketRateLimiter { + private final ConcurrentHashMap rateLimiters = new ConcurrentHashMap(); + private final int maxPacketsPerSecond; + private final int timeWindowSeconds = 5; + + public PacketRateLimiter(int maxPacketsPerSecond) { + this.maxPacketsPerSecond = maxPacketsPerSecond; + } + + public boolean allow(UUID player) { + if (this.maxPacketsPerSecond <= 0) { + return true; + } + RateLimiter limiter = this.rateLimiters.computeIfAbsent(player, id -> new RateLimiter(this.maxPacketsPerSecond * 5, 5000L)); + return limiter.tryAcquire(); + } + + public void onPlayerLoggedOut(Player player) { + this.rateLimiters.remove(player.getUniqueId()); + } + + private static class RateLimiter { + private final int threshold; + private final long timePerTokenNanos; + private long lastLeakNanos; + private long amount; + + public RateLimiter(int threshold, long windowMillis) { + this.threshold = threshold; + this.timePerTokenNanos = windowMillis * 1000000L / (long)threshold; + this.lastLeakNanos = System.nanoTime(); + this.amount = 0L; + } + + public boolean tryAcquire() { + long currentTime = System.nanoTime(); + long elapsed = currentTime - this.lastLeakNanos; + long leakedTokens = elapsed / this.timePerTokenNanos; + if (leakedTokens > 0L) { + this.amount -= leakedTokens; + if (this.amount < 0L) { + this.amount = 0L; + } + this.lastLeakNanos += leakedTokens * this.timePerTokenNanos; + } + if (this.amount >= (long)this.threshold) { + return false; + } + ++this.amount; + return true; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/PlayerStatePacket.java b/source/decompiled/de/maxhenkel/voicechat/net/PlayerStatePacket.java new file mode 100644 index 0000000..fb93cbc --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/PlayerStatePacket.java @@ -0,0 +1,44 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import de.maxhenkel.voicechat.voice.common.PlayerState; + +public class PlayerStatePacket +implements Packet { + public static final Key PLAYER_STATE = Voicechat.compatibility.createNamespacedKey("state"); + private PlayerState playerState; + + public PlayerStatePacket() { + } + + public PlayerStatePacket(PlayerState playerState) { + this.playerState = playerState; + } + + public PlayerState getPlayerState() { + return this.playerState; + } + + @Override + public Key getID() { + return PLAYER_STATE; + } + + @Override + public PlayerStatePacket fromBytes(FriendlyByteBuf buf) { + this.playerState = PlayerState.fromBytes(buf); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + this.playerState.toBytes(buf); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/PlayerStatesPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/PlayerStatesPacket.java new file mode 100644 index 0000000..5a4b730 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/PlayerStatesPacket.java @@ -0,0 +1,54 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import java.util.ArrayList; +import java.util.Collection; + +public class PlayerStatesPacket +implements Packet { + public static final Key PLAYER_STATES = Voicechat.compatibility.createNamespacedKey("states"); + private Collection playerStates; + + public PlayerStatesPacket() { + } + + public PlayerStatesPacket(Collection playerStates) { + this.playerStates = playerStates; + } + + public Collection getPlayerStates() { + return this.playerStates; + } + + @Override + public Key getID() { + return PLAYER_STATES; + } + + @Override + public PlayerStatesPacket fromBytes(FriendlyByteBuf buf) { + int count = buf.readInt(); + this.playerStates = new ArrayList(count); + for (int i = 0; i < count; ++i) { + PlayerState playerState = PlayerState.fromBytes(buf); + this.playerStates.add(playerState); + } + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeInt(this.playerStates.size()); + for (PlayerState state : this.playerStates) { + state.toBytes(buf); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/RemoveCategoryPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/RemoveCategoryPacket.java new file mode 100644 index 0000000..e4e7c6d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/RemoveCategoryPacket.java @@ -0,0 +1,43 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; + +public class RemoveCategoryPacket +implements Packet { + public static final Key REMOVE_CATEGORY = Voicechat.compatibility.createNamespacedKey("remove_category"); + private String categoryId; + + public RemoveCategoryPacket() { + } + + public RemoveCategoryPacket(String categoryId) { + this.categoryId = categoryId; + } + + public String getCategoryId() { + return this.categoryId; + } + + @Override + public Key getID() { + return REMOVE_CATEGORY; + } + + @Override + public RemoveCategoryPacket fromBytes(FriendlyByteBuf buf) { + this.categoryId = buf.readUtf(16); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUtf(this.categoryId, 16); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/RemoveGroupPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/RemoveGroupPacket.java new file mode 100644 index 0000000..cdbd61d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/RemoveGroupPacket.java @@ -0,0 +1,44 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import java.util.UUID; + +public class RemoveGroupPacket +implements Packet { + public static final Key REMOVE_GROUP = Voicechat.compatibility.createNamespacedKey("remove_group"); + private UUID groupId; + + public RemoveGroupPacket() { + } + + public RemoveGroupPacket(UUID groupId) { + this.groupId = groupId; + } + + public UUID getGroupId() { + return this.groupId; + } + + @Override + public Key getID() { + return REMOVE_GROUP; + } + + @Override + public RemoveGroupPacket fromBytes(FriendlyByteBuf buf) { + this.groupId = buf.readUUID(); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(this.groupId); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/RemovePlayerStatePacket.java b/source/decompiled/de/maxhenkel/voicechat/net/RemovePlayerStatePacket.java new file mode 100644 index 0000000..4a5d4b0 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/RemovePlayerStatePacket.java @@ -0,0 +1,44 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import java.util.UUID; + +public class RemovePlayerStatePacket +implements Packet { + public static final Key REMOVE_PLAYER_STATE = Voicechat.compatibility.createNamespacedKey("remove_state"); + private UUID id; + + public RemovePlayerStatePacket() { + } + + public RemovePlayerStatePacket(UUID id) { + this.id = id; + } + + public UUID getId() { + return this.id; + } + + @Override + public Key getID() { + return REMOVE_PLAYER_STATE; + } + + @Override + public RemovePlayerStatePacket fromBytes(FriendlyByteBuf buf) { + this.id = buf.readUUID(); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(this.id); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/RequestSecretPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/RequestSecretPacket.java new file mode 100644 index 0000000..d59f0e0 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/RequestSecretPacket.java @@ -0,0 +1,52 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import org.bukkit.entity.Player; + +public class RequestSecretPacket +implements Packet { + public static final Key REQUEST_SECRET = Voicechat.compatibility.createNamespacedKey("request_secret"); + private int compatibilityVersion; + + public RequestSecretPacket() { + } + + public RequestSecretPacket(int compatibilityVersion) { + this.compatibilityVersion = compatibilityVersion; + } + + public int getCompatibilityVersion() { + return this.compatibilityVersion; + } + + @Override + public Key getID() { + return REQUEST_SECRET; + } + + @Override + public void onPacket(Player player) { + Voicechat.SERVER.onRequestSecretPacket(player, this); + } + + @Override + public RequestSecretPacket fromBytes(FriendlyByteBuf buf) { + this.compatibilityVersion = buf.readInt(); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeInt(this.compatibilityVersion); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/SecretPacket.java b/source/decompiled/de/maxhenkel/voicechat/net/SecretPacket.java new file mode 100644 index 0000000..93f0071 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/SecretPacket.java @@ -0,0 +1,122 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.config.ServerConfig; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import de.maxhenkel.voicechat.voice.common.Secret; +import java.util.UUID; +import org.bukkit.entity.Player; + +public class SecretPacket +implements Packet { + public static final Key SECRET = Voicechat.compatibility.createNamespacedKey("secret"); + private Secret secret; + private int serverPort; + private UUID playerUUID; + private ServerConfig.Codec codec; + private int mtuSize; + private double voiceChatDistance; + private int keepAlive; + private boolean groupsEnabled; + private String voiceHost; + private boolean allowRecording; + + public SecretPacket() { + } + + public SecretPacket(Player player, Secret secret, int port, ServerConfig serverConfig, String voiceHost) { + this.secret = secret; + this.serverPort = port; + this.playerUUID = player.getUniqueId(); + this.codec = serverConfig.voiceChatCodec.get(); + this.mtuSize = serverConfig.voiceChatMtuSize.get(); + this.voiceChatDistance = serverConfig.voiceChatDistance.get(); + this.keepAlive = serverConfig.keepAlive.get(); + this.groupsEnabled = serverConfig.groupsEnabled.get(); + this.voiceHost = voiceHost; + this.allowRecording = serverConfig.allowRecording.get(); + } + + public Secret getSecret() { + return this.secret; + } + + public int getServerPort() { + return this.serverPort; + } + + public UUID getPlayerUUID() { + return this.playerUUID; + } + + public ServerConfig.Codec getCodec() { + return this.codec; + } + + public int getMtuSize() { + return this.mtuSize; + } + + public double getVoiceChatDistance() { + return this.voiceChatDistance; + } + + public int getKeepAlive() { + return this.keepAlive; + } + + public boolean groupsEnabled() { + return this.groupsEnabled; + } + + public String getVoiceHost() { + return this.voiceHost; + } + + @Override + public Key getID() { + return SECRET; + } + + public boolean allowRecording() { + return this.allowRecording; + } + + @Override + public SecretPacket fromBytes(FriendlyByteBuf buf) { + this.secret = Secret.fromBytes(buf); + this.serverPort = buf.readInt(); + this.playerUUID = buf.readUUID(); + this.codec = ServerConfig.Codec.values()[buf.readByte()]; + this.mtuSize = buf.readInt(); + this.voiceChatDistance = buf.readDouble(); + this.keepAlive = buf.readInt(); + this.groupsEnabled = buf.readBoolean(); + this.voiceHost = buf.readUtf(); + this.allowRecording = buf.readBoolean(); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + this.secret.toBytes(buf); + buf.writeInt(this.serverPort); + buf.writeUUID(this.playerUUID); + buf.writeByte(this.codec.ordinal()); + buf.writeInt(this.mtuSize); + buf.writeDouble(this.voiceChatDistance); + buf.writeInt(this.keepAlive); + buf.writeBoolean(this.groupsEnabled); + buf.writeUtf(this.voiceHost); + buf.writeBoolean(this.allowRecording); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/net/UpdateStatePacket.java b/source/decompiled/de/maxhenkel/voicechat/net/UpdateStatePacket.java new file mode 100644 index 0000000..0f467c6 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/net/UpdateStatePacket.java @@ -0,0 +1,52 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.net; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.Packet; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.util.Key; +import org.bukkit.entity.Player; + +public class UpdateStatePacket +implements Packet { + public static final Key PLAYER_STATE = Voicechat.compatibility.createNamespacedKey("update_state"); + private boolean disabled; + + public UpdateStatePacket() { + } + + public UpdateStatePacket(boolean disabled) { + this.disabled = disabled; + } + + public boolean isDisabled() { + return this.disabled; + } + + @Override + public Key getID() { + return PLAYER_STATE; + } + + @Override + public void onPacket(Player player) { + Voicechat.SERVER.getServer().getPlayerStateManager().onUpdateStatePacket(player, this); + } + + @Override + public UpdateStatePacket fromBytes(FriendlyByteBuf buf) { + this.disabled = buf.readBoolean(); + return this; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeBoolean(this.disabled); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/CDL.java b/source/decompiled/de/maxhenkel/voicechat/org/json/CDL.java new file mode 100644 index 0000000..ee27067 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/CDL.java @@ -0,0 +1,196 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONArray; +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.org.json.JSONTokener; + +public class CDL { + @Deprecated + public CDL() { + } + + private static String getValue(JSONTokener x, char delimiter) throws JSONException { + char c; + while ((c = x.next()) == ' ' || c == '\t') { + } + if (c == '\u0000') { + return null; + } + if (c == '\"' || c == '\'') { + char q = c; + StringBuilder sb = new StringBuilder(); + while (true) { + char nextC; + if ((c = x.next()) == q && (nextC = x.next()) != '\"') { + if (nextC <= '\u0000') break; + x.back(); + break; + } + if (c == '\u0000' || c == '\n' || c == '\r') { + throw x.syntaxError("Missing close quote '" + q + "'."); + } + sb.append(c); + } + return sb.toString(); + } + if (c == delimiter) { + x.back(); + return ""; + } + x.back(); + return x.nextTo(delimiter); + } + + public static JSONArray rowToJSONArray(JSONTokener x) throws JSONException { + return CDL.rowToJSONArray(x, ','); + } + + public static JSONArray rowToJSONArray(JSONTokener x, char delimiter) throws JSONException { + JSONArray ja = new JSONArray(); + block0: while (true) { + String value = CDL.getValue(x, delimiter); + char c = x.next(); + if (value != null) { + ja.put(value); + } else { + if (ja.length() == 0 && c != delimiter) { + return null; + } + ja.put(""); + } + while (true) { + if (c == delimiter) continue block0; + if (c != ' ') { + if (c == '\n' || c == '\r' || c == '\u0000') { + return ja; + } + throw x.syntaxError("Bad character '" + c + "' (" + c + ")."); + } + c = x.next(); + } + break; + } + } + + public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x) throws JSONException { + return CDL.rowToJSONObject(names, x, ','); + } + + public static JSONObject rowToJSONObject(JSONArray names, JSONTokener x, char delimiter) throws JSONException { + JSONArray ja = CDL.rowToJSONArray(x, delimiter); + return ja != null ? ja.toJSONObject(names) : null; + } + + public static String rowToString(JSONArray ja) { + return CDL.rowToString(ja, ','); + } + + public static String rowToString(JSONArray ja, char delimiter) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < ja.length(); ++i) { + Object object; + if (i > 0) { + sb.append(delimiter); + } + if ((object = ja.opt(i)) == null) continue; + String string = object.toString(); + if (!(string.isEmpty() || string.indexOf(delimiter) < 0 && string.indexOf(10) < 0 && string.indexOf(13) < 0 && string.indexOf(0) < 0 && string.charAt(0) != '\"')) { + sb.append('\"'); + int length = string.length(); + for (int j = 0; j < length; ++j) { + char c = string.charAt(j); + if (c < ' ' || c == '\"') continue; + sb.append(c); + } + sb.append('\"'); + continue; + } + sb.append(string); + } + sb.append('\n'); + return sb.toString(); + } + + public static JSONArray toJSONArray(String string) throws JSONException { + return CDL.toJSONArray(string, ','); + } + + public static JSONArray toJSONArray(String string, char delimiter) throws JSONException { + return CDL.toJSONArray(new JSONTokener(string), delimiter); + } + + public static JSONArray toJSONArray(JSONTokener x) throws JSONException { + return CDL.toJSONArray(x, ','); + } + + public static JSONArray toJSONArray(JSONTokener x, char delimiter) throws JSONException { + return CDL.toJSONArray(CDL.rowToJSONArray(x, delimiter), x, delimiter); + } + + public static JSONArray toJSONArray(JSONArray names, String string) throws JSONException { + return CDL.toJSONArray(names, string, ','); + } + + public static JSONArray toJSONArray(JSONArray names, String string, char delimiter) throws JSONException { + return CDL.toJSONArray(names, new JSONTokener(string), delimiter); + } + + public static JSONArray toJSONArray(JSONArray names, JSONTokener x) throws JSONException { + return CDL.toJSONArray(names, x, ','); + } + + public static JSONArray toJSONArray(JSONArray names, JSONTokener x, char delimiter) throws JSONException { + String key; + JSONObject j; + JSONObject jo; + if (names == null || names.length() == 0) { + return null; + } + JSONArray ja = new JSONArray(); + while ((jo = CDL.rowToJSONObject(names, x, delimiter)) != null) { + ja.put(jo); + } + if (ja.length() == 0) { + return null; + } + if (ja.length() == 1 && (j = ja.getJSONObject(0)).length() == 1 && "".equals(key = j.keys().next()) && "".equals(j.get(key))) { + return null; + } + return ja; + } + + public static String toString(JSONArray ja) throws JSONException { + return CDL.toString(ja, ','); + } + + public static String toString(JSONArray ja, char delimiter) throws JSONException { + JSONArray names; + JSONObject jo = ja.optJSONObject(0); + if (jo != null && (names = jo.names()) != null) { + return CDL.rowToString(names, delimiter) + CDL.toString(names, ja, delimiter); + } + return null; + } + + public static String toString(JSONArray names, JSONArray ja) throws JSONException { + return CDL.toString(names, ja, ','); + } + + public static String toString(JSONArray names, JSONArray ja, char delimiter) throws JSONException { + if (names == null || names.length() == 0) { + return null; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < ja.length(); ++i) { + JSONObject jo = ja.optJSONObject(i); + if (jo == null) continue; + sb.append(CDL.rowToString(jo.toJSONArray(names), delimiter)); + } + return sb.toString(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/Cookie.java b/source/decompiled/de/maxhenkel/voicechat/org/json/Cookie.java new file mode 100644 index 0000000..c45c429 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/Cookie.java @@ -0,0 +1,121 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.org.json.JSONTokener; +import java.util.Locale; + +public class Cookie { + @Deprecated + public Cookie() { + } + + public static String escape(String string) { + String s = string.trim(); + int length = s.length(); + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; ++i) { + char c = s.charAt(i); + if (c < ' ' || c == '+' || c == '%' || c == '=' || c == ';') { + sb.append('%'); + sb.append(Character.forDigit((char)(c >>> 4 & 0xF), 16)); + sb.append(Character.forDigit((char)(c & 0xF), 16)); + continue; + } + sb.append(c); + } + return sb.toString(); + } + + public static JSONObject toJSONObject(String string) { + JSONObject jo = new JSONObject(); + JSONTokener x = new JSONTokener(string); + String name = Cookie.unescape(x.nextTo('=').trim()); + if ("".equals(name)) { + throw new JSONException("Cookies must have a 'name'"); + } + jo.put("name", name); + x.next('='); + jo.put("value", Cookie.unescape(x.nextTo(';')).trim()); + x.next(); + while (x.more()) { + Object value; + name = Cookie.unescape(x.nextTo("=;")).trim().toLowerCase(Locale.ROOT); + if ("name".equalsIgnoreCase(name)) { + throw new JSONException("Illegal attribute name: 'name'"); + } + if ("value".equalsIgnoreCase(name)) { + throw new JSONException("Illegal attribute name: 'value'"); + } + if (x.next() != '=') { + value = Boolean.TRUE; + } else { + value = Cookie.unescape(x.nextTo(';')).trim(); + x.next(); + } + if ("".equals(name) || "".equals(value)) continue; + jo.put(name, value); + } + return jo; + } + + public static String toString(JSONObject jo) throws JSONException { + StringBuilder sb = new StringBuilder(); + String name = null; + Object value = null; + for (String key : jo.keySet()) { + if ("name".equalsIgnoreCase(key)) { + name = jo.getString(key).trim(); + } + if ("value".equalsIgnoreCase(key)) { + value = jo.getString(key).trim(); + } + if (name == null || value == null) continue; + break; + } + if (name == null || "".equals(name.trim())) { + throw new JSONException("Cookie does not have a name"); + } + if (value == null) { + value = ""; + } + sb.append(Cookie.escape(name)); + sb.append("="); + sb.append(Cookie.escape((String)value)); + for (String key : jo.keySet()) { + if ("name".equalsIgnoreCase(key) || "value".equalsIgnoreCase(key)) continue; + value = jo.opt(key); + if (value instanceof Boolean) { + if (!Boolean.TRUE.equals(value)) continue; + sb.append(';').append(Cookie.escape(key)); + continue; + } + sb.append(';').append(Cookie.escape(key)).append('=').append(Cookie.escape(value.toString())); + } + return sb.toString(); + } + + public static String unescape(String string) { + int length = string.length(); + StringBuilder sb = new StringBuilder(length); + for (int i = 0; i < length; ++i) { + char c = string.charAt(i); + if (c == '+') { + c = ' '; + } else if (c == '%' && i + 2 < length) { + int d = JSONTokener.dehexchar(string.charAt(i + 1)); + int e = JSONTokener.dehexchar(string.charAt(i + 2)); + if (d >= 0 && e >= 0) { + c = (char)(d * 16 + e); + i += 2; + } + } + sb.append(c); + } + return sb.toString(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/CookieList.java b/source/decompiled/de/maxhenkel/voicechat/org/json/CookieList.java new file mode 100644 index 0000000..5aed590 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/CookieList.java @@ -0,0 +1,45 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.Cookie; +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.org.json.JSONTokener; + +public class CookieList { + @Deprecated + public CookieList() { + } + + public static JSONObject toJSONObject(String string) throws JSONException { + JSONObject jo = new JSONObject(); + JSONTokener x = new JSONTokener(string); + while (x.more()) { + String name = Cookie.unescape(x.nextTo('=')); + x.next('='); + jo.put(name, Cookie.unescape(x.nextTo(';'))); + x.next(); + } + return jo; + } + + public static String toString(JSONObject jo) throws JSONException { + boolean isEndOfPair = false; + StringBuilder sb = new StringBuilder(); + for (String key : jo.keySet()) { + Object value = jo.opt(key); + if (JSONObject.NULL.equals(value)) continue; + if (isEndOfPair) { + sb.append(';'); + } + sb.append(Cookie.escape(key)); + sb.append("="); + sb.append(Cookie.escape(value.toString())); + isEndOfPair = true; + } + return sb.toString(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/HTTP.java b/source/decompiled/de/maxhenkel/voicechat/org/json/HTTP.java new file mode 100644 index 0000000..386067d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/HTTP.java @@ -0,0 +1,69 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.HTTPTokener; +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import java.util.Locale; + +public class HTTP { + public static final String CRLF = "\r\n"; + + public static JSONObject toJSONObject(String string) throws JSONException { + JSONObject jo = new JSONObject(); + HTTPTokener x = new HTTPTokener(string); + String token = x.nextToken(); + if (token.toUpperCase(Locale.ROOT).startsWith("HTTP")) { + jo.put("HTTP-Version", token); + jo.put("Status-Code", x.nextToken()); + jo.put("Reason-Phrase", x.nextTo('\u0000')); + x.next(); + } else { + jo.put("Method", token); + jo.put("Request-URI", x.nextToken()); + jo.put("HTTP-Version", x.nextToken()); + } + while (x.more()) { + String name = x.nextTo(':'); + x.next(':'); + jo.put(name, x.nextTo('\u0000')); + x.next(); + } + return jo; + } + + public static String toString(JSONObject jo) throws JSONException { + StringBuilder sb = new StringBuilder(); + if (jo.has("Status-Code") && jo.has("Reason-Phrase")) { + sb.append(jo.getString("HTTP-Version")); + sb.append(' '); + sb.append(jo.getString("Status-Code")); + sb.append(' '); + sb.append(jo.getString("Reason-Phrase")); + } else if (jo.has("Method") && jo.has("Request-URI")) { + sb.append(jo.getString("Method")); + sb.append(' '); + sb.append('\"'); + sb.append(jo.getString("Request-URI")); + sb.append('\"'); + sb.append(' '); + sb.append(jo.getString("HTTP-Version")); + } else { + throw new JSONException("Not enough material for an HTTP header."); + } + sb.append(CRLF); + for (String key : jo.keySet()) { + String value = jo.optString(key); + if ("HTTP-Version".equals(key) || "Status-Code".equals(key) || "Reason-Phrase".equals(key) || "Method".equals(key) || "Request-URI".equals(key) || JSONObject.NULL.equals(value)) continue; + sb.append(key); + sb.append(": "); + sb.append(jo.optString(key)); + sb.append(CRLF); + } + sb.append(CRLF); + return sb.toString(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/HTTPTokener.java b/source/decompiled/de/maxhenkel/voicechat/org/json/HTTPTokener.java new file mode 100644 index 0000000..0ffb14d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/HTTPTokener.java @@ -0,0 +1,39 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONTokener; + +public class HTTPTokener +extends JSONTokener { + public HTTPTokener(String string) { + super(string); + } + + public String nextToken() throws JSONException { + char c; + StringBuilder sb = new StringBuilder(); + while (Character.isWhitespace(c = this.next())) { + } + if (c == '\"' || c == '\'') { + char q = c; + while (true) { + if ((c = this.next()) < ' ') { + throw this.syntaxError("Unterminated string."); + } + if (c == q) { + return sb.toString(); + } + sb.append(c); + } + } + while (c != '\u0000' && !Character.isWhitespace(c)) { + sb.append(c); + c = this.next(); + } + return sb.toString(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONArray.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONArray.java new file mode 100644 index 0000000..66706ec --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONArray.java @@ -0,0 +1,855 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.org.json.JSONParserConfiguration; +import de.maxhenkel.voicechat.org.json.JSONPointer; +import de.maxhenkel.voicechat.org.json.JSONPointerException; +import de.maxhenkel.voicechat.org.json.JSONString; +import de.maxhenkel.voicechat.org.json.JSONTokener; +import de.maxhenkel.voicechat.org.json.StringBuilderWriter; +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class JSONArray +implements Iterable { + private final ArrayList myArrayList; + + public JSONArray() { + this.myArrayList = new ArrayList(); + } + + public JSONArray(JSONTokener x) throws JSONException { + this(x, x.getJsonParserConfiguration()); + } + + public JSONArray(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException { + this(); + boolean isInitial; + boolean bl = isInitial = x.getPrevious() == '\u0000'; + if (x.nextClean() != '[') { + throw x.syntaxError("A JSONArray text must start with '['"); + } + char nextChar = x.nextClean(); + if (nextChar == '\u0000') { + throw x.syntaxError("Expected a ',' or ']'"); + } + if (nextChar == ',' && jsonParserConfiguration.isStrictMode()) { + throw x.syntaxError("Array content starts with a ','"); + } + if (nextChar != ']') { + x.back(); + do { + if (x.nextClean() == ',') { + x.back(); + this.myArrayList.add(JSONObject.NULL); + continue; + } + x.back(); + this.myArrayList.add(x.nextValue()); + } while (!JSONArray.checkForSyntaxError(x, jsonParserConfiguration, isInitial)); + return; + } + if (isInitial && jsonParserConfiguration.isStrictMode() && x.nextClean() != '\u0000') { + throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text"); + } + } + + private static boolean checkForSyntaxError(JSONTokener x, JSONParserConfiguration jsonParserConfiguration, boolean isInitial) { + switch (x.nextClean()) { + case '\u0000': { + throw x.syntaxError("Expected a ',' or ']'"); + } + case ',': { + char nextChar = x.nextClean(); + if (nextChar == '\u0000') { + throw x.syntaxError("Expected a ',' or ']'"); + } + if (nextChar == ']') { + if (jsonParserConfiguration.isStrictMode()) { + throw x.syntaxError("Strict mode error: Expected another array element"); + } + return true; + } + if (nextChar == ',') { + if (jsonParserConfiguration.isStrictMode()) { + throw x.syntaxError("Strict mode error: Expected a valid array element"); + } + return true; + } + x.back(); + break; + } + case ']': { + if (isInitial && jsonParserConfiguration.isStrictMode() && x.nextClean() != '\u0000') { + throw x.syntaxError("Strict mode error: Unparsed characters found at end of input text"); + } + return true; + } + default: { + throw x.syntaxError("Expected a ',' or ']'"); + } + } + return false; + } + + public JSONArray(String source) throws JSONException { + this(source, new JSONParserConfiguration()); + } + + public JSONArray(String source, JSONParserConfiguration jsonParserConfiguration) throws JSONException { + this(new JSONTokener(source, jsonParserConfiguration), jsonParserConfiguration); + } + + public JSONArray(Collection collection) { + this(collection, 0, new JSONParserConfiguration()); + } + + public JSONArray(Collection collection, JSONParserConfiguration jsonParserConfiguration) { + this(collection, 0, jsonParserConfiguration); + } + + JSONArray(Collection collection, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) { + throw new JSONException("JSONArray has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth()); + } + if (collection == null) { + this.myArrayList = new ArrayList(); + } else { + this.myArrayList = new ArrayList(collection.size()); + this.addAll(collection, true, recursionDepth, jsonParserConfiguration); + } + } + + public JSONArray(Iterable iter) { + this(); + if (iter == null) { + return; + } + this.addAll(iter, true); + } + + public JSONArray(JSONArray array) { + this.myArrayList = array == null ? new ArrayList() : new ArrayList(array.myArrayList); + } + + public JSONArray(Object array) throws JSONException { + this(); + if (!array.getClass().isArray()) { + throw new JSONException("JSONArray initial value should be a string or collection or array."); + } + this.addAll(array, true, 0); + } + + public JSONArray(int initialCapacity) throws JSONException { + if (initialCapacity < 0) { + throw new JSONException("JSONArray initial capacity cannot be negative."); + } + this.myArrayList = new ArrayList(initialCapacity); + } + + @Override + public Iterator iterator() { + return this.myArrayList.iterator(); + } + + public Object get(int index) throws JSONException { + Object object = this.opt(index); + if (object == null) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + return object; + } + + public boolean getBoolean(int index) throws JSONException { + Object object = this.get(index); + if (Boolean.FALSE.equals(object) || object instanceof String && "false".equalsIgnoreCase((String)object)) { + return false; + } + if (Boolean.TRUE.equals(object) || object instanceof String && "true".equalsIgnoreCase((String)object)) { + return true; + } + throw JSONArray.wrongValueFormatException(index, "boolean", object, null); + } + + public double getDouble(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof Number) { + return ((Number)object).doubleValue(); + } + try { + return Double.parseDouble(object.toString()); + } + catch (Exception e) { + throw JSONArray.wrongValueFormatException(index, "double", object, e); + } + } + + public float getFloat(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof Number) { + return ((Number)object).floatValue(); + } + try { + return Float.parseFloat(object.toString()); + } + catch (Exception e) { + throw JSONArray.wrongValueFormatException(index, "float", object, e); + } + } + + public Number getNumber(int index) throws JSONException { + Object object = this.get(index); + try { + if (object instanceof Number) { + return (Number)object; + } + return JSONObject.stringToNumber(object.toString()); + } + catch (Exception e) { + throw JSONArray.wrongValueFormatException(index, "number", object, e); + } + } + + public > E getEnum(Class clazz, int index) throws JSONException { + E val = this.optEnum(clazz, index); + if (val == null) { + throw JSONArray.wrongValueFormatException(index, "enum of type " + JSONObject.quote(clazz.getSimpleName()), this.opt(index), null); + } + return val; + } + + public BigDecimal getBigDecimal(int index) throws JSONException { + Object object = this.get(index); + BigDecimal val = JSONObject.objectToBigDecimal(object, null); + if (val == null) { + throw JSONArray.wrongValueFormatException(index, "BigDecimal", object, null); + } + return val; + } + + public BigInteger getBigInteger(int index) throws JSONException { + Object object = this.get(index); + BigInteger val = JSONObject.objectToBigInteger(object, null); + if (val == null) { + throw JSONArray.wrongValueFormatException(index, "BigInteger", object, null); + } + return val; + } + + public int getInt(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof Number) { + return ((Number)object).intValue(); + } + try { + return Integer.parseInt(object.toString()); + } + catch (Exception e) { + throw JSONArray.wrongValueFormatException(index, "int", object, e); + } + } + + public JSONArray getJSONArray(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONArray) { + return (JSONArray)object; + } + throw JSONArray.wrongValueFormatException(index, "JSONArray", object, null); + } + + public JSONObject getJSONObject(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof JSONObject) { + return (JSONObject)object; + } + throw JSONArray.wrongValueFormatException(index, "JSONObject", object, null); + } + + public long getLong(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof Number) { + return ((Number)object).longValue(); + } + try { + return Long.parseLong(object.toString()); + } + catch (Exception e) { + throw JSONArray.wrongValueFormatException(index, "long", object, e); + } + } + + public String getString(int index) throws JSONException { + Object object = this.get(index); + if (object instanceof String) { + return (String)object; + } + throw JSONArray.wrongValueFormatException(index, "String", object, null); + } + + public boolean isNull(int index) { + return JSONObject.NULL.equals(this.opt(index)); + } + + public String join(String separator) throws JSONException { + int len = this.length(); + if (len == 0) { + return ""; + } + StringBuilder sb = new StringBuilder(JSONObject.valueToString(this.myArrayList.get(0))); + for (int i = 1; i < len; ++i) { + sb.append(separator).append(JSONObject.valueToString(this.myArrayList.get(i))); + } + return sb.toString(); + } + + public int length() { + return this.myArrayList.size(); + } + + public void clear() { + this.myArrayList.clear(); + } + + public Object opt(int index) { + return index < 0 || index >= this.length() ? null : this.myArrayList.get(index); + } + + public boolean optBoolean(int index) { + return this.optBoolean(index, false); + } + + public boolean optBoolean(int index, boolean defaultValue) { + try { + return this.getBoolean(index); + } + catch (Exception e) { + return defaultValue; + } + } + + public Boolean optBooleanObject(int index) { + return this.optBooleanObject(index, false); + } + + public Boolean optBooleanObject(int index, Boolean defaultValue) { + try { + return this.getBoolean(index); + } + catch (Exception e) { + return defaultValue; + } + } + + public double optDouble(int index) { + return this.optDouble(index, Double.NaN); + } + + public double optDouble(int index, double defaultValue) { + Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.doubleValue(); + } + + public Double optDoubleObject(int index) { + return this.optDoubleObject(index, Double.NaN); + } + + public Double optDoubleObject(int index, Double defaultValue) { + Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.doubleValue(); + } + + public float optFloat(int index) { + return this.optFloat(index, Float.NaN); + } + + public float optFloat(int index, float defaultValue) { + Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.floatValue(); + } + + public Float optFloatObject(int index) { + return this.optFloatObject(index, Float.valueOf(Float.NaN)); + } + + public Float optFloatObject(int index, Float defaultValue) { + Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return Float.valueOf(val.floatValue()); + } + + public int optInt(int index) { + return this.optInt(index, 0); + } + + public int optInt(int index, int defaultValue) { + Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + public Integer optIntegerObject(int index) { + return this.optIntegerObject(index, 0); + } + + public Integer optIntegerObject(int index, Integer defaultValue) { + Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + public > E optEnum(Class clazz, int index) { + return this.optEnum(clazz, index, null); + } + + public > E optEnum(Class clazz, int index, E defaultValue) { + try { + Object val = this.opt(index); + if (JSONObject.NULL.equals(val)) { + return defaultValue; + } + if (clazz.isAssignableFrom(val.getClass())) { + Enum myE = (Enum)val; + return (E)myE; + } + return Enum.valueOf(clazz, val.toString()); + } + catch (IllegalArgumentException e) { + return defaultValue; + } + catch (NullPointerException e) { + return defaultValue; + } + } + + public BigInteger optBigInteger(int index, BigInteger defaultValue) { + Object val = this.opt(index); + return JSONObject.objectToBigInteger(val, defaultValue); + } + + public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) { + Object val = this.opt(index); + return JSONObject.objectToBigDecimal(val, defaultValue); + } + + public JSONArray optJSONArray(int index) { + return this.optJSONArray(index, null); + } + + public JSONArray optJSONArray(int index, JSONArray defaultValue) { + Object object = this.opt(index); + return object instanceof JSONArray ? (JSONArray)object : defaultValue; + } + + public JSONObject optJSONObject(int index) { + return this.optJSONObject(index, null); + } + + public JSONObject optJSONObject(int index, JSONObject defaultValue) { + Object object = this.opt(index); + return object instanceof JSONObject ? (JSONObject)object : defaultValue; + } + + public long optLong(int index) { + return this.optLong(index, 0L); + } + + public long optLong(int index, long defaultValue) { + Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.longValue(); + } + + public Long optLongObject(int index) { + return this.optLongObject(index, 0L); + } + + public Long optLongObject(int index, Long defaultValue) { + Number val = this.optNumber(index, null); + if (val == null) { + return defaultValue; + } + return val.longValue(); + } + + public Number optNumber(int index) { + return this.optNumber(index, null); + } + + public Number optNumber(int index, Number defaultValue) { + Object val = this.opt(index); + if (JSONObject.NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Number) { + return (Number)val; + } + if (val instanceof String) { + try { + return JSONObject.stringToNumber((String)val); + } + catch (Exception e) { + return defaultValue; + } + } + return defaultValue; + } + + public String optString(int index) { + return this.optString(index, ""); + } + + public String optString(int index, String defaultValue) { + Object object = this.opt(index); + return JSONObject.NULL.equals(object) ? defaultValue : object.toString(); + } + + public JSONArray put(boolean value) { + return this.put(value ? Boolean.TRUE : Boolean.FALSE); + } + + public JSONArray put(Collection value) { + return this.put(new JSONArray(value)); + } + + public JSONArray put(double value) throws JSONException { + return this.put((Object)value); + } + + public JSONArray put(float value) throws JSONException { + return this.put(Float.valueOf(value)); + } + + public JSONArray put(int value) { + return this.put((Object)value); + } + + public JSONArray put(long value) { + return this.put((Object)value); + } + + public JSONArray put(Map value) { + return this.put(new JSONObject(value)); + } + + public JSONArray put(Object value) { + JSONObject.testValidity(value); + this.myArrayList.add(value); + return this; + } + + public JSONArray put(int index, boolean value) throws JSONException { + return this.put(index, value ? Boolean.TRUE : Boolean.FALSE); + } + + public JSONArray put(int index, Collection value) throws JSONException { + return this.put(index, new JSONArray(value)); + } + + public JSONArray put(int index, double value) throws JSONException { + return this.put(index, (Object)value); + } + + public JSONArray put(int index, float value) throws JSONException { + return this.put(index, Float.valueOf(value)); + } + + public JSONArray put(int index, int value) throws JSONException { + return this.put(index, (Object)value); + } + + public JSONArray put(int index, long value) throws JSONException { + return this.put(index, (Object)value); + } + + public JSONArray put(int index, Map value) throws JSONException { + this.put(index, new JSONObject(value, new JSONParserConfiguration())); + return this; + } + + public JSONArray put(int index, Map value, JSONParserConfiguration jsonParserConfiguration) throws JSONException { + this.put(index, new JSONObject(value, jsonParserConfiguration)); + return this; + } + + public JSONArray put(int index, Object value) throws JSONException { + if (index < 0) { + throw new JSONException("JSONArray[" + index + "] not found."); + } + if (index < this.length()) { + JSONObject.testValidity(value); + this.myArrayList.set(index, value); + return this; + } + if (index == this.length()) { + return this.put(value); + } + this.myArrayList.ensureCapacity(index + 1); + while (index != this.length()) { + this.myArrayList.add(JSONObject.NULL); + } + return this.put(value); + } + + public JSONArray putAll(Collection collection) { + this.addAll(collection, false); + return this; + } + + public JSONArray putAll(Iterable iter) { + this.addAll(iter, false); + return this; + } + + public JSONArray putAll(JSONArray array) { + this.myArrayList.addAll(array.myArrayList); + return this; + } + + public JSONArray putAll(Object array) throws JSONException { + this.addAll(array, false); + return this; + } + + public Object query(String jsonPointer) { + return this.query(new JSONPointer(jsonPointer)); + } + + public Object query(JSONPointer jsonPointer) { + return jsonPointer.queryFrom(this); + } + + public Object optQuery(String jsonPointer) { + return this.optQuery(new JSONPointer(jsonPointer)); + } + + public Object optQuery(JSONPointer jsonPointer) { + try { + return jsonPointer.queryFrom(this); + } + catch (JSONPointerException e) { + return null; + } + } + + public Object remove(int index) { + return index >= 0 && index < this.length() ? this.myArrayList.remove(index) : null; + } + + public boolean similar(Object other) { + if (!(other instanceof JSONArray)) { + return false; + } + int len = this.length(); + if (len != ((JSONArray)other).length()) { + return false; + } + for (int i = 0; i < len; ++i) { + Object valueOther; + Object valueThis = this.myArrayList.get(i); + if (valueThis == (valueOther = ((JSONArray)other).myArrayList.get(i))) continue; + if (valueThis == null) { + return false; + } + if (this.isSimilar(valueThis, valueOther)) continue; + return false; + } + return true; + } + + private boolean isSimilar(Object valueThis, Object valueOther) { + return !(valueThis instanceof JSONObject ? !((JSONObject)valueThis).similar(valueOther) : (valueThis instanceof JSONArray ? !((JSONArray)valueThis).similar(valueOther) : (valueThis instanceof Number && valueOther instanceof Number ? !JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther) : (valueThis instanceof JSONString && valueOther instanceof JSONString ? !((JSONString)valueThis).toJSONString().equals(((JSONString)valueOther).toJSONString()) : !valueThis.equals(valueOther))))); + } + + public JSONObject toJSONObject(JSONArray names) throws JSONException { + if (names == null || names.isEmpty() || this.isEmpty()) { + return null; + } + JSONObject jo = new JSONObject(names.length()); + for (int i = 0; i < names.length(); ++i) { + jo.put(names.getString(i), this.opt(i)); + } + return jo; + } + + public String toString() { + try { + return this.toString(0); + } + catch (Exception e) { + return null; + } + } + + public String toString(int indentFactor) throws JSONException { + int initialSize = this.myArrayList.size() * 2; + StringBuilderWriter sw = new StringBuilderWriter(Math.max(initialSize, 16)); + return this.write(sw, indentFactor, 0).toString(); + } + + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + public Writer write(Writer writer, int indentFactor, int indent) throws JSONException { + try { + boolean needsComma = false; + int length = this.length(); + writer.write(91); + if (length == 1) { + this.writeArrayAttempt(writer, indentFactor, indent, 0); + } else if (length != 0) { + int newIndent = indent + indentFactor; + for (int i = 0; i < length; ++i) { + if (needsComma) { + writer.write(44); + } + if (indentFactor > 0) { + writer.write(10); + } + JSONObject.indent(writer, newIndent); + this.writeArrayAttempt(writer, indentFactor, newIndent, i); + needsComma = true; + } + if (indentFactor > 0) { + writer.write(10); + } + JSONObject.indent(writer, indent); + } + writer.write(93); + return writer; + } + catch (IOException e) { + throw new JSONException(e); + } + } + + private void writeArrayAttempt(Writer writer, int indentFactor, int indent, int i) { + try { + JSONObject.writeValue(writer, this.myArrayList.get(i), indentFactor, indent); + } + catch (Exception e) { + throw new JSONException("Unable to write JSONArray value at index: " + i, e); + } + } + + public List toList() { + ArrayList results = new ArrayList(this.myArrayList.size()); + for (Object element : this.myArrayList) { + if (element == null || JSONObject.NULL.equals(element)) { + results.add(null); + continue; + } + if (element instanceof JSONArray) { + results.add(((JSONArray)element).toList()); + continue; + } + if (element instanceof JSONObject) { + results.add(((JSONObject)element).toMap()); + continue; + } + results.add(element); + } + return results; + } + + public boolean isEmpty() { + return this.myArrayList.isEmpty(); + } + + private void addAll(Collection collection, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + this.myArrayList.ensureCapacity(this.myArrayList.size() + collection.size()); + if (wrap) { + for (Object o : collection) { + this.put(JSONObject.wrap(o, recursionDepth + 1, jsonParserConfiguration)); + } + } else { + for (Object o : collection) { + this.put(o); + } + } + } + + private void addAll(Iterable iter, boolean wrap) { + if (wrap) { + for (Object o : iter) { + this.put(JSONObject.wrap(o)); + } + } else { + for (Object o : iter) { + this.put(o); + } + } + } + + private void addAll(Object array, boolean wrap) throws JSONException { + this.addAll(array, wrap, 0); + } + + private void addAll(Object array, boolean wrap, int recursionDepth) { + this.addAll(array, wrap, recursionDepth, new JSONParserConfiguration()); + } + + private void addAll(Object array, boolean wrap, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) throws JSONException { + if (array.getClass().isArray()) { + int length = Array.getLength(array); + this.myArrayList.ensureCapacity(this.myArrayList.size() + length); + if (wrap) { + for (int i = 0; i < length; ++i) { + this.put(JSONObject.wrap(Array.get(array, i), recursionDepth + 1, jsonParserConfiguration)); + } + } else { + for (int i = 0; i < length; ++i) { + this.put(Array.get(array, i)); + } + } + } else if (array instanceof JSONArray) { + this.myArrayList.addAll(((JSONArray)array).myArrayList); + } else if (array instanceof Collection) { + this.addAll((Collection)array, wrap, recursionDepth, jsonParserConfiguration); + } else if (array instanceof Iterable) { + this.addAll((Iterable)array, wrap); + } else { + throw new JSONException("JSONArray initial value should be a string or collection or array."); + } + } + + private static JSONException wrongValueFormatException(int idx, String valueType, Object value, Throwable cause) { + if (value == null) { + return new JSONException("JSONArray[" + idx + "] is not a " + valueType + " (null).", cause); + } + if (value instanceof Map || value instanceof Iterable || value instanceof JSONObject) { + return new JSONException("JSONArray[" + idx + "] is not a " + valueType + " (" + value.getClass() + ").", cause); + } + return new JSONException("JSONArray[" + idx + "] is not a " + valueType + " (" + value.getClass() + " : " + value + ").", cause); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONException.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONException.java new file mode 100644 index 0000000..1afc7e7 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONException.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +public class JSONException +extends RuntimeException { + private static final long serialVersionUID = 0L; + + public JSONException(String message) { + super(message); + } + + public JSONException(String message, Throwable cause) { + super(message, cause); + } + + public JSONException(Throwable cause) { + super(cause.getMessage(), cause); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONML.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONML.java new file mode 100644 index 0000000..516bcbb --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONML.java @@ -0,0 +1,332 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONArray; +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONMLParserConfiguration; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.org.json.XML; +import de.maxhenkel.voicechat.org.json.XMLTokener; + +public class JSONML { + @Deprecated + public JSONML() { + } + + private static Object parse(XMLTokener x, boolean arrayForm, JSONArray ja, boolean keepStrings, int currentNestingDepth) throws JSONException { + return JSONML.parse(x, arrayForm, ja, keepStrings ? JSONMLParserConfiguration.KEEP_STRINGS : JSONMLParserConfiguration.ORIGINAL, currentNestingDepth); + } + + private static Object parse(XMLTokener x, boolean arrayForm, JSONArray ja, JSONMLParserConfiguration config, int currentNestingDepth) throws JSONException { + String closeTag = null; + JSONArray newja = null; + JSONObject newjo = null; + String tagName = null; + while (true) { + Object value; + if (!x.more()) { + throw x.syntaxError("Bad XML"); + } + Object token = x.nextContent(); + if (token == XML.LT) { + token = x.nextToken(); + if (token instanceof Character) { + if (token == XML.SLASH) { + token = x.nextToken(); + if (!(token instanceof String)) { + throw new JSONException("Expected a closing name instead of '" + token + "'."); + } + if (x.nextToken() != XML.GT) { + throw x.syntaxError("Misshaped close tag"); + } + return token; + } + if (token == XML.BANG) { + char c = x.next(); + if (c == '-') { + if (x.next() == '-') { + x.skipPast("-->"); + continue; + } + x.back(); + continue; + } + if (c == '[') { + token = x.nextToken(); + if ("CDATA".equals(token) && x.next() == '[') { + if (ja == null) continue; + ja.put(x.nextCDATA()); + continue; + } + throw x.syntaxError("Expected 'CDATA['"); + } + int i = 1; + do { + if ((token = x.nextMeta()) == null) { + throw x.syntaxError("Missing '>' after ' 0); + continue; + } + if (token == XML.QUEST) { + x.skipPast("?>"); + continue; + } + throw x.syntaxError("Misshaped tag"); + } + if (!(token instanceof String)) { + throw x.syntaxError("Bad tagName '" + token + "'."); + } + tagName = (String)token; + newja = new JSONArray(); + newjo = new JSONObject(); + if (arrayForm) { + newja.put(tagName); + if (ja != null) { + ja.put(newja); + } + } else { + newjo.put("tagName", tagName); + if (ja != null) { + ja.put(newjo); + } + } + token = null; + while (true) { + if (token == null) { + token = x.nextToken(); + } + if (token == null) { + throw x.syntaxError("Misshaped tag"); + } + if (!(token instanceof String)) break; + String attribute = (String)token; + if (!arrayForm && ("tagName".equals(attribute) || "childNode".equals(attribute))) { + throw x.syntaxError("Reserved attribute."); + } + token = x.nextToken(); + if (token == XML.EQ) { + token = x.nextToken(); + if (!(token instanceof String)) { + throw x.syntaxError("Missing value"); + } + newjo.accumulate(attribute, config.isKeepStrings() ? (String)token : XML.stringToValue((String)token)); + token = null; + continue; + } + newjo.accumulate(attribute, ""); + } + if (arrayForm && newjo.length() > 0) { + newja.put(newjo); + } + if (token == XML.SLASH) { + if (x.nextToken() != XML.GT) { + throw x.syntaxError("Misshaped tag"); + } + if (ja != null) continue; + if (arrayForm) { + return newja; + } + return newjo; + } + if (token != XML.GT) { + throw x.syntaxError("Misshaped tag"); + } + if (currentNestingDepth == config.getMaxNestingDepth()) { + throw x.syntaxError("Maximum nesting depth of " + config.getMaxNestingDepth() + " reached"); + } + closeTag = (String)JSONML.parse(x, arrayForm, newja, config, currentNestingDepth + 1); + if (closeTag == null) continue; + if (!closeTag.equals(tagName)) { + throw x.syntaxError("Mismatched '" + tagName + "' and '" + closeTag + "'"); + } + tagName = null; + if (!arrayForm && newja.length() > 0) { + newjo.put("childNodes", newja); + } + if (ja != null) continue; + if (arrayForm) { + return newja; + } + return newjo; + } + if (ja == null) continue; + if (token instanceof String) { + String strToken = (String)token; + value = config.isKeepStrings() ? XML.unescape(strToken) : XML.stringToValue(strToken); + } else { + value = token; + } + ja.put(value); + } + } + + public static JSONArray toJSONArray(String string) throws JSONException { + return (JSONArray)JSONML.parse(new XMLTokener(string), true, null, JSONMLParserConfiguration.ORIGINAL, 0); + } + + public static JSONArray toJSONArray(String string, boolean keepStrings) throws JSONException { + return (JSONArray)JSONML.parse(new XMLTokener(string), true, null, keepStrings, 0); + } + + public static JSONArray toJSONArray(String string, JSONMLParserConfiguration config) throws JSONException { + return (JSONArray)JSONML.parse(new XMLTokener(string), true, null, config, 0); + } + + public static JSONArray toJSONArray(XMLTokener x, JSONMLParserConfiguration config) throws JSONException { + return (JSONArray)JSONML.parse(x, true, null, config, 0); + } + + public static JSONArray toJSONArray(XMLTokener x, boolean keepStrings) throws JSONException { + return (JSONArray)JSONML.parse(x, true, null, keepStrings, 0); + } + + public static JSONArray toJSONArray(XMLTokener x) throws JSONException { + return (JSONArray)JSONML.parse(x, true, null, false, 0); + } + + public static JSONObject toJSONObject(String string) throws JSONException { + return (JSONObject)JSONML.parse(new XMLTokener(string), false, null, false, 0); + } + + public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException { + return (JSONObject)JSONML.parse(new XMLTokener(string), false, null, keepStrings, 0); + } + + public static JSONObject toJSONObject(String string, JSONMLParserConfiguration config) throws JSONException { + return (JSONObject)JSONML.parse(new XMLTokener(string), false, null, config, 0); + } + + public static JSONObject toJSONObject(XMLTokener x) throws JSONException { + return (JSONObject)JSONML.parse(x, false, null, false, 0); + } + + public static JSONObject toJSONObject(XMLTokener x, boolean keepStrings) throws JSONException { + return (JSONObject)JSONML.parse(x, false, null, keepStrings, 0); + } + + public static JSONObject toJSONObject(XMLTokener x, JSONMLParserConfiguration config) throws JSONException { + return (JSONObject)JSONML.parse(x, false, null, config, 0); + } + + public static String toString(JSONArray ja) throws JSONException { + int length; + int i; + StringBuilder sb = new StringBuilder(); + String tagName = ja.getString(0); + XML.noSpace(tagName); + tagName = XML.escape(tagName); + sb.append('<'); + sb.append(tagName); + Object object = ja.opt(1); + if (object instanceof JSONObject) { + i = 2; + JSONObject jo = (JSONObject)object; + for (String key : jo.keySet()) { + Object value = jo.opt(key); + XML.noSpace(key); + if (value == null) continue; + sb.append(' '); + sb.append(XML.escape(key)); + sb.append('='); + sb.append('\"'); + sb.append(XML.escape(value.toString())); + sb.append('\"'); + } + } else { + i = 1; + } + if (i >= (length = ja.length())) { + sb.append('/'); + sb.append('>'); + } else { + sb.append('>'); + do { + object = ja.get(i); + ++i; + if (object == null) continue; + if (object instanceof String) { + sb.append(XML.escape(object.toString())); + continue; + } + if (object instanceof JSONObject) { + sb.append(JSONML.toString((JSONObject)object)); + continue; + } + if (object instanceof JSONArray) { + sb.append(JSONML.toString((JSONArray)object)); + continue; + } + sb.append(object.toString()); + } while (i < length); + sb.append('<'); + sb.append('/'); + sb.append(tagName); + sb.append('>'); + } + return sb.toString(); + } + + public static String toString(JSONObject jo) throws JSONException { + StringBuilder sb = new StringBuilder(); + String tagName = jo.optString("tagName"); + if (tagName == null) { + return XML.escape(jo.toString()); + } + XML.noSpace(tagName); + tagName = XML.escape(tagName); + sb.append('<'); + sb.append(tagName); + for (String key : jo.keySet()) { + if ("tagName".equals(key) || "childNodes".equals(key)) continue; + XML.noSpace(key); + Object value = jo.opt(key); + if (value == null) continue; + sb.append(' '); + sb.append(XML.escape(key)); + sb.append('='); + sb.append('\"'); + sb.append(XML.escape(value.toString())); + sb.append('\"'); + } + JSONArray ja = jo.optJSONArray("childNodes"); + if (ja == null) { + sb.append('/'); + sb.append('>'); + } else { + sb.append('>'); + int length = ja.length(); + for (int i = 0; i < length; ++i) { + Object object = ja.get(i); + if (object == null) continue; + if (object instanceof String) { + sb.append(XML.escape(object.toString())); + continue; + } + if (object instanceof JSONObject) { + sb.append(JSONML.toString((JSONObject)object)); + continue; + } + if (object instanceof JSONArray) { + sb.append(JSONML.toString((JSONArray)object)); + continue; + } + sb.append(object.toString()); + } + sb.append('<'); + sb.append('/'); + sb.append(tagName); + sb.append('>'); + } + return sb.toString(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONMLParserConfiguration.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONMLParserConfiguration.java new file mode 100644 index 0000000..6c65751 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONMLParserConfiguration.java @@ -0,0 +1,35 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.ParserConfiguration; + +public class JSONMLParserConfiguration +extends ParserConfiguration { + public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; + public static final JSONMLParserConfiguration ORIGINAL = new JSONMLParserConfiguration(); + public static final JSONMLParserConfiguration KEEP_STRINGS = new JSONMLParserConfiguration().withKeepStrings(true); + + public JSONMLParserConfiguration() { + this.maxNestingDepth = 512; + } + + protected JSONMLParserConfiguration(boolean keepStrings, int maxNestingDepth) { + super(keepStrings, maxNestingDepth); + } + + @Override + protected JSONMLParserConfiguration clone() { + return new JSONMLParserConfiguration(this.keepStrings, this.maxNestingDepth); + } + + public JSONMLParserConfiguration withKeepStrings(boolean newVal) { + return (JSONMLParserConfiguration)super.withKeepStrings(newVal); + } + + public JSONMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) { + return (JSONMLParserConfiguration)super.withMaxNestingDepth(maxNestingDepth); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONObject.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONObject.java new file mode 100644 index 0000000..aff7e1d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONObject.java @@ -0,0 +1,1771 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONArray; +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONParserConfiguration; +import de.maxhenkel.voicechat.org.json.JSONPointer; +import de.maxhenkel.voicechat.org.json.JSONPointerException; +import de.maxhenkel.voicechat.org.json.JSONPropertyIgnore; +import de.maxhenkel.voicechat.org.json.JSONPropertyName; +import de.maxhenkel.voicechat.org.json.JSONString; +import de.maxhenkel.voicechat.org.json.JSONTokener; +import de.maxhenkel.voicechat.org.json.JSONWriter; +import de.maxhenkel.voicechat.org.json.StringBuilderWriter; +import java.io.Closeable; +import java.io.IOException; +import java.io.Writer; +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.regex.Pattern; + +public class JSONObject { + static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?"); + private final Map map; + public static final Object NULL = new Null(); + private static final Set EXCLUDED_RECORD_METHOD_NAMES = Collections.unmodifiableSet(new HashSet(Arrays.asList("get", "is", "set", "toString", "hashCode", "equals", "clone", "notify", "notifyAll", "wait"))); + + public Class getMapType() { + return this.map.getClass(); + } + + public JSONObject() { + this.map = new HashMap(); + } + + public JSONObject(JSONObject jo, String ... names) { + this(names.length); + for (int i = 0; i < names.length; ++i) { + try { + this.putOnce(names[i], jo.opt(names[i])); + continue; + } + catch (Exception exception) { + // empty catch block + } + } + } + + public JSONObject(JSONTokener x) throws JSONException { + this(x, x.getJsonParserConfiguration()); + } + + public JSONObject(JSONTokener x, JSONParserConfiguration jsonParserConfiguration) throws JSONException { + this(); + boolean isInitial; + boolean bl = isInitial = x.getPrevious() == '\u0000'; + if (x.nextClean() != '{') { + throw x.syntaxError("A JSONObject text must begin with '{'"); + } + while (!this.parseJSONObject(x, jsonParserConfiguration, isInitial)) { + } + } + + private boolean parseJSONObject(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration, boolean isInitial) { + boolean doneParsing = false; + char c = jsonTokener.nextClean(); + switch (c) { + case '\u0000': { + throw jsonTokener.syntaxError("A JSONObject text must end with '}'"); + } + case '}': { + if (isInitial && jsonParserConfiguration.isStrictMode() && jsonTokener.nextClean() != '\u0000') { + throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text"); + } + return true; + } + } + Object obj = jsonTokener.nextSimpleValue(c); + String key = obj.toString(); + JSONObject.checkKeyForStrictMode(jsonTokener, jsonParserConfiguration, obj); + c = jsonTokener.nextClean(); + if (c != ':') { + throw jsonTokener.syntaxError("Expected a ':' after a key"); + } + if (key != null) { + boolean keyExists; + boolean bl = keyExists = this.opt(key) != null; + if (keyExists && !jsonParserConfiguration.isOverwriteDuplicateKey()) { + throw jsonTokener.syntaxError("Duplicate key \"" + key + "\""); + } + Object value = jsonTokener.nextValue(); + if (value != null) { + this.put(key, value); + } + } + if (JSONObject.parseEndOfKeyValuePair(jsonTokener, jsonParserConfiguration, isInitial)) { + doneParsing = true; + } + return doneParsing; + } + + private static boolean parseEndOfKeyValuePair(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration, boolean isInitial) { + switch (jsonTokener.nextClean()) { + case ';': { + if (!jsonParserConfiguration.isStrictMode()) break; + throw jsonTokener.syntaxError("Strict mode error: Invalid character ';' found"); + } + case ',': { + if (jsonTokener.nextClean() == '}') { + if (jsonParserConfiguration.isStrictMode()) { + throw jsonTokener.syntaxError("Strict mode error: Expected another object element"); + } + return true; + } + if (jsonTokener.end()) { + throw jsonTokener.syntaxError("A JSONObject text must end with '}'"); + } + jsonTokener.back(); + break; + } + case '}': { + if (isInitial && jsonParserConfiguration.isStrictMode() && jsonTokener.nextClean() != '\u0000') { + throw jsonTokener.syntaxError("Strict mode error: Unparsed characters found at end of input text"); + } + return true; + } + default: { + throw jsonTokener.syntaxError("Expected a ',' or '}'"); + } + } + return false; + } + + private static void checkKeyForStrictMode(JSONTokener jsonTokener, JSONParserConfiguration jsonParserConfiguration, Object obj) { + if (jsonParserConfiguration != null && jsonParserConfiguration.isStrictMode()) { + if (obj instanceof Boolean) { + throw jsonTokener.syntaxError(String.format("Strict mode error: key '%s' cannot be boolean", obj.toString())); + } + if (obj == NULL) { + throw jsonTokener.syntaxError(String.format("Strict mode error: key '%s' cannot be null", obj.toString())); + } + if (obj instanceof Number) { + throw jsonTokener.syntaxError(String.format("Strict mode error: key '%s' cannot be number", obj.toString())); + } + } + } + + public JSONObject(Map m) { + this(m, 0, new JSONParserConfiguration()); + } + + public JSONObject(Map m, JSONParserConfiguration jsonParserConfiguration) { + this(m, 0, jsonParserConfiguration); + } + + private JSONObject(Map m, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + if (recursionDepth > jsonParserConfiguration.getMaxNestingDepth()) { + throw new JSONException("JSONObject has reached recursion depth limit of " + jsonParserConfiguration.getMaxNestingDepth()); + } + if (m == null) { + this.map = new HashMap(); + } else { + this.map = new HashMap(m.size()); + for (Map.Entry e : m.entrySet()) { + if (e.getKey() == null) { + throw new NullPointerException("Null key."); + } + Object value = e.getValue(); + if (value == null && !jsonParserConfiguration.isUseNativeNulls()) continue; + JSONObject.testValidity(value); + this.map.put(String.valueOf(e.getKey()), JSONObject.wrap(value, recursionDepth + 1, jsonParserConfiguration)); + } + } + } + + public JSONObject(Object bean) { + this(); + this.populateMap(bean, new JSONParserConfiguration()); + } + + public JSONObject(Object bean, JSONParserConfiguration jsonParserConfiguration) { + this(); + this.populateMap(bean, jsonParserConfiguration); + } + + private JSONObject(Object bean, Set objectsRecord) { + this(); + this.populateMap(bean, objectsRecord, new JSONParserConfiguration()); + } + + public JSONObject(Object object, String ... names) { + this(names.length); + Class c = object.getClass(); + for (int i = 0; i < names.length; ++i) { + String name = names[i]; + try { + this.putOpt(name, c.getField(name).get(object)); + continue; + } + catch (Exception exception) { + // empty catch block + } + } + } + + public JSONObject(String source) throws JSONException { + this(source, new JSONParserConfiguration()); + } + + public JSONObject(String source, JSONParserConfiguration jsonParserConfiguration) throws JSONException { + this(new JSONTokener(source, jsonParserConfiguration), jsonParserConfiguration); + } + + public JSONObject(String baseName, Locale locale) throws JSONException { + this(); + ResourceBundle bundle = ResourceBundle.getBundle(baseName, locale, Thread.currentThread().getContextClassLoader()); + Enumeration keys = bundle.getKeys(); + while (keys.hasMoreElements()) { + String key = keys.nextElement(); + if (key == null) continue; + String[] path = key.split("\\."); + int last = path.length - 1; + JSONObject target = this; + for (int i = 0; i < last; ++i) { + String segment = path[i]; + JSONObject nextTarget = target.optJSONObject(segment); + if (nextTarget == null) { + nextTarget = new JSONObject(); + target.put(segment, nextTarget); + } + target = nextTarget; + } + target.put(path[last], bundle.getString(key)); + } + } + + protected JSONObject(int initialCapacity) { + this.map = new HashMap(initialCapacity); + } + + public JSONObject accumulate(String key, Object value) throws JSONException { + JSONObject.testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, value instanceof JSONArray ? new JSONArray().put(value) : value); + } else if (object instanceof JSONArray) { + ((JSONArray)object).put(value); + } else { + this.put(key, new JSONArray().put(object).put(value)); + } + return this; + } + + public JSONObject append(String key, Object value) throws JSONException { + JSONObject.testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, new JSONArray().put(value)); + } else if (object instanceof JSONArray) { + this.put(key, ((JSONArray)object).put(value)); + } else { + throw JSONObject.wrongValueFormatException(key, "JSONArray", null, null); + } + return this; + } + + public static String doubleToString(double d) { + if (Double.isInfinite(d) || Double.isNaN(d)) { + return "null"; + } + String string = Double.toString(d); + if (string.indexOf(46) > 0 && string.indexOf(101) < 0 && string.indexOf(69) < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + public Object get(String key) throws JSONException { + if (key == null) { + throw new JSONException("Null key."); + } + Object object = this.opt(key); + if (object == null) { + throw new JSONException("JSONObject[" + JSONObject.quote(key) + "] not found."); + } + return object; + } + + public > E getEnum(Class clazz, String key) throws JSONException { + E val = this.optEnum(clazz, key); + if (val == null) { + throw JSONObject.wrongValueFormatException(key, "enum of type " + JSONObject.quote(clazz.getSimpleName()), this.opt(key), null); + } + return val; + } + + public boolean getBoolean(String key) throws JSONException { + Object object = this.get(key); + if (Boolean.FALSE.equals(object) || object instanceof String && "false".equalsIgnoreCase((String)object)) { + return false; + } + if (Boolean.TRUE.equals(object) || object instanceof String && "true".equalsIgnoreCase((String)object)) { + return true; + } + throw JSONObject.wrongValueFormatException(key, "Boolean", object, null); + } + + public BigInteger getBigInteger(String key) throws JSONException { + Object object = this.get(key); + BigInteger ret = JSONObject.objectToBigInteger(object, null); + if (ret != null) { + return ret; + } + throw JSONObject.wrongValueFormatException(key, "BigInteger", object, null); + } + + public BigDecimal getBigDecimal(String key) throws JSONException { + Object object = this.get(key); + BigDecimal ret = JSONObject.objectToBigDecimal(object, null); + if (ret != null) { + return ret; + } + throw JSONObject.wrongValueFormatException(key, "BigDecimal", object, null); + } + + public double getDouble(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof Number) { + return ((Number)object).doubleValue(); + } + try { + return Double.parseDouble(object.toString()); + } + catch (Exception e) { + throw JSONObject.wrongValueFormatException(key, "double", object, e); + } + } + + public float getFloat(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof Number) { + return ((Number)object).floatValue(); + } + try { + return Float.parseFloat(object.toString()); + } + catch (Exception e) { + throw JSONObject.wrongValueFormatException(key, "float", object, e); + } + } + + public Number getNumber(String key) throws JSONException { + Object object = this.get(key); + try { + if (object instanceof Number) { + return (Number)object; + } + return JSONObject.stringToNumber(object.toString()); + } + catch (Exception e) { + throw JSONObject.wrongValueFormatException(key, "number", object, e); + } + } + + public int getInt(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof Number) { + return ((Number)object).intValue(); + } + try { + return Integer.parseInt(object.toString()); + } + catch (Exception e) { + throw JSONObject.wrongValueFormatException(key, "int", object, e); + } + } + + public JSONArray getJSONArray(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONArray) { + return (JSONArray)object; + } + throw JSONObject.wrongValueFormatException(key, "JSONArray", object, null); + } + + public JSONObject getJSONObject(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONObject) { + return (JSONObject)object; + } + throw JSONObject.wrongValueFormatException(key, "JSONObject", object, null); + } + + public long getLong(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof Number) { + return ((Number)object).longValue(); + } + try { + return Long.parseLong(object.toString()); + } + catch (Exception e) { + throw JSONObject.wrongValueFormatException(key, "long", object, e); + } + } + + public static String[] getNames(JSONObject jo) { + if (jo.isEmpty()) { + return null; + } + return jo.keySet().toArray(new String[jo.length()]); + } + + public static String[] getNames(Object object) { + if (object == null) { + return null; + } + Class klass = object.getClass(); + Field[] fields = klass.getFields(); + int length = fields.length; + if (length == 0) { + return null; + } + String[] names = new String[length]; + for (int i = 0; i < length; ++i) { + names[i] = fields[i].getName(); + } + return names; + } + + public String getString(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof String) { + return (String)object; + } + throw JSONObject.wrongValueFormatException(key, "string", object, null); + } + + public boolean has(String key) { + return this.map.containsKey(key); + } + + public JSONObject increment(String key) throws JSONException { + Object value = this.opt(key); + if (value == null) { + this.put(key, 1); + } else if (value instanceof Integer) { + this.put(key, (Integer)value + 1); + } else if (value instanceof Long) { + this.put(key, (Long)value + 1L); + } else if (value instanceof BigInteger) { + this.put(key, ((BigInteger)value).add(BigInteger.ONE)); + } else if (value instanceof Float) { + this.put(key, ((Float)value).floatValue() + 1.0f); + } else if (value instanceof Double) { + this.put(key, (Double)value + 1.0); + } else if (value instanceof BigDecimal) { + this.put(key, ((BigDecimal)value).add(BigDecimal.ONE)); + } else { + throw new JSONException("Unable to increment [" + JSONObject.quote(key) + "]."); + } + return this; + } + + public boolean isNull(String key) { + return NULL.equals(this.opt(key)); + } + + public Iterator keys() { + return this.keySet().iterator(); + } + + public Set keySet() { + return this.map.keySet(); + } + + protected Set> entrySet() { + return this.map.entrySet(); + } + + public int length() { + return this.map.size(); + } + + public void clear() { + this.map.clear(); + } + + public boolean isEmpty() { + return this.map.isEmpty(); + } + + public JSONArray names() { + if (this.map.isEmpty()) { + return null; + } + return new JSONArray((Collection)this.map.keySet()); + } + + public static String numberToString(Number number) throws JSONException { + if (number == null) { + throw new JSONException("Null pointer"); + } + JSONObject.testValidity(number); + String string = number.toString(); + if (string.indexOf(46) > 0 && string.indexOf(101) < 0 && string.indexOf(69) < 0) { + while (string.endsWith("0")) { + string = string.substring(0, string.length() - 1); + } + if (string.endsWith(".")) { + string = string.substring(0, string.length() - 1); + } + } + return string; + } + + public Object opt(String key) { + return key == null ? null : this.map.get(key); + } + + public > E optEnum(Class clazz, String key) { + return this.optEnum(clazz, key, null); + } + + public > E optEnum(Class clazz, String key, E defaultValue) { + try { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (clazz.isAssignableFrom(val.getClass())) { + Enum myE = (Enum)val; + return (E)myE; + } + return Enum.valueOf(clazz, val.toString()); + } + catch (IllegalArgumentException e) { + return defaultValue; + } + catch (NullPointerException e) { + return defaultValue; + } + } + + public boolean optBoolean(String key) { + return this.optBoolean(key, false); + } + + public boolean optBoolean(String key, boolean defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Boolean) { + return (Boolean)val; + } + try { + return this.getBoolean(key); + } + catch (Exception e) { + return defaultValue; + } + } + + public Boolean optBooleanObject(String key) { + return this.optBooleanObject(key, false); + } + + public Boolean optBooleanObject(String key, Boolean defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Boolean) { + return (boolean)((Boolean)val); + } + try { + return this.getBoolean(key); + } + catch (Exception e) { + return defaultValue; + } + } + + public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { + Object val = this.opt(key); + return JSONObject.objectToBigDecimal(val, defaultValue); + } + + static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { + return JSONObject.objectToBigDecimal(val, defaultValue, true); + } + + static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue, boolean exact) { + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof BigDecimal) { + return (BigDecimal)val; + } + if (val instanceof BigInteger) { + return new BigDecimal((BigInteger)val); + } + if (val instanceof Double || val instanceof Float) { + if (!JSONObject.numberIsFinite((Number)val)) { + return defaultValue; + } + if (exact) { + return new BigDecimal(((Number)val).doubleValue()); + } + return new BigDecimal(val.toString()); + } + if (val instanceof Long || val instanceof Integer || val instanceof Short || val instanceof Byte) { + return new BigDecimal(((Number)val).longValue()); + } + try { + return new BigDecimal(val.toString()); + } + catch (Exception e) { + return defaultValue; + } + } + + public BigInteger optBigInteger(String key, BigInteger defaultValue) { + Object val = this.opt(key); + return JSONObject.objectToBigInteger(val, defaultValue); + } + + static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) { + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof BigInteger) { + return (BigInteger)val; + } + if (val instanceof BigDecimal) { + return ((BigDecimal)val).toBigInteger(); + } + if (val instanceof Double || val instanceof Float) { + if (!JSONObject.numberIsFinite((Number)val)) { + return defaultValue; + } + return BigDecimal.valueOf(((Number)val).doubleValue()).toBigInteger(); + } + if (val instanceof Long || val instanceof Integer || val instanceof Short || val instanceof Byte) { + return BigInteger.valueOf(((Number)val).longValue()); + } + try { + String valStr = val.toString(); + if (JSONObject.isDecimalNotation(valStr)) { + return new BigDecimal(valStr).toBigInteger(); + } + return new BigInteger(valStr); + } + catch (Exception e) { + return defaultValue; + } + } + + public double optDouble(String key) { + return this.optDouble(key, Double.NaN); + } + + public double optDouble(String key, double defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + return val.doubleValue(); + } + + public Double optDoubleObject(String key) { + return this.optDoubleObject(key, Double.NaN); + } + + public Double optDoubleObject(String key, Double defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + return val.doubleValue(); + } + + public float optFloat(String key) { + return this.optFloat(key, Float.NaN); + } + + public float optFloat(String key, float defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + return val.floatValue(); + } + + public Float optFloatObject(String key) { + return this.optFloatObject(key, Float.valueOf(Float.NaN)); + } + + public Float optFloatObject(String key, Float defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + return Float.valueOf(val.floatValue()); + } + + public int optInt(String key) { + return this.optInt(key, 0); + } + + public int optInt(String key, int defaultValue) { + Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + public Integer optIntegerObject(String key) { + return this.optIntegerObject(key, 0); + } + + public Integer optIntegerObject(String key, Integer defaultValue) { + Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + public JSONArray optJSONArray(String key) { + return this.optJSONArray(key, null); + } + + public JSONArray optJSONArray(String key, JSONArray defaultValue) { + Object object = this.opt(key); + return object instanceof JSONArray ? (JSONArray)object : defaultValue; + } + + public JSONObject optJSONObject(String key) { + return this.optJSONObject(key, null); + } + + public JSONObject optJSONObject(String key, JSONObject defaultValue) { + Object object = this.opt(key); + return object instanceof JSONObject ? (JSONObject)object : defaultValue; + } + + public long optLong(String key) { + return this.optLong(key, 0L); + } + + public long optLong(String key, long defaultValue) { + Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + return val.longValue(); + } + + public Long optLongObject(String key) { + return this.optLongObject(key, 0L); + } + + public Long optLongObject(String key, Long defaultValue) { + Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + return val.longValue(); + } + + public Number optNumber(String key) { + return this.optNumber(key, null); + } + + public Number optNumber(String key, Number defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Number) { + return (Number)val; + } + try { + return JSONObject.stringToNumber(val.toString()); + } + catch (Exception e) { + return defaultValue; + } + } + + public String optString(String key) { + return this.optString(key, ""); + } + + public String optString(String key, String defaultValue) { + Object object = this.opt(key); + return NULL.equals(object) ? defaultValue : object.toString(); + } + + private void populateMap(Object bean, JSONParserConfiguration jsonParserConfiguration) { + this.populateMap(bean, Collections.newSetFromMap(new IdentityHashMap()), jsonParserConfiguration); + } + + private void populateMap(Object bean, Set objectsRecord, JSONParserConfiguration jsonParserConfiguration) { + Method[] methods; + Class klass = bean.getClass(); + boolean isRecord = JSONObject.isRecordType(klass); + for (Method method : methods = JSONObject.getMethods(klass)) { + String key; + if (!JSONObject.isValidMethod(method) || (key = JSONObject.getKeyNameFromMethod(method, isRecord)) == null || key.isEmpty()) continue; + this.processMethod(bean, objectsRecord, jsonParserConfiguration, method, key); + } + } + + private void processMethod(Object bean, Set objectsRecord, JSONParserConfiguration jsonParserConfiguration, Method method, String key) { + try { + Object result = method.invoke(bean, new Object[0]); + if (result != null || jsonParserConfiguration.isUseNativeNulls()) { + if (objectsRecord.contains(result)) { + throw JSONObject.recursivelyDefinedObjectException(key); + } + objectsRecord.add(result); + JSONObject.testValidity(result); + this.map.put(key, JSONObject.wrap(result, objectsRecord)); + objectsRecord.remove(result); + JSONObject.closeClosable(result); + } + } + catch (IllegalAccessException illegalAccessException) { + } + catch (IllegalArgumentException illegalArgumentException) { + } + catch (InvocationTargetException invocationTargetException) { + // empty catch block + } + } + + private static boolean isRecordType(Class klass) { + try { + Method isRecordMethod = Class.class.getMethod("isRecord", new Class[0]); + return (Boolean)isRecordMethod.invoke(klass, new Object[0]); + } + catch (NoSuchMethodException e) { + return false; + } + catch (Exception e) { + return false; + } + } + + private static Method[] getMethods(Class klass) { + boolean includeSuperClass = klass.getClassLoader() != null; + return includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods(); + } + + private static boolean isValidMethodName(String name) { + return !"getClass".equals(name) && !"getDeclaringClass".equals(name); + } + + private static String getKeyNameFromMethod(Method method, boolean isRecordType) { + String key; + int forcedNameDepth; + int ignoreDepth = JSONObject.getAnnotationDepth(method, JSONPropertyIgnore.class); + if (ignoreDepth > 0 && ((forcedNameDepth = JSONObject.getAnnotationDepth(method, JSONPropertyName.class)) < 0 || ignoreDepth <= forcedNameDepth)) { + return null; + } + JSONPropertyName annotation = JSONObject.getAnnotation(method, JSONPropertyName.class); + if (JSONObject.annotationValueNotEmpty(annotation)) { + return annotation.value(); + } + String name = method.getName(); + if (name.startsWith("get") && name.length() > 3) { + key = name.substring(3); + } else if (name.startsWith("is") && name.length() > 2) { + key = name.substring(2); + } else { + if (isRecordType && JSONObject.isRecordStyleAccessor(name, method)) { + return name; + } + return null; + } + if (key.isEmpty() || Character.isLowerCase(key.charAt(0))) { + return null; + } + if (key.length() == 1) { + key = key.toLowerCase(Locale.ROOT); + } else if (!Character.isUpperCase(key.charAt(1))) { + key = key.substring(0, 1).toLowerCase(Locale.ROOT) + key.substring(1); + } + return key; + } + + private static boolean isRecordStyleAccessor(String methodName, Method method) { + if (methodName.isEmpty() || !Character.isLowerCase(methodName.charAt(0))) { + return false; + } + if (EXCLUDED_RECORD_METHOD_NAMES.contains(methodName)) { + return false; + } + Class declaringClass = method.getDeclaringClass(); + if (declaringClass == null || declaringClass == Object.class) { + return false; + } + if (Enum.class.isAssignableFrom(declaringClass) || Number.class.isAssignableFrom(declaringClass)) { + return false; + } + String className = declaringClass.getName(); + return !className.startsWith("java.") && !className.startsWith("javax."); + } + + private static boolean annotationValueNotEmpty(JSONPropertyName annotation) { + return annotation != null && annotation.value() != null && !annotation.value().isEmpty(); + } + + private static boolean isValidMethod(Method method) { + int modifiers = method.getModifiers(); + return Modifier.isPublic(modifiers) && !Modifier.isStatic(modifiers) && method.getParameterTypes().length == 0 && !method.isBridge() && method.getReturnType() != Void.TYPE && JSONObject.isValidMethodName(method.getName()); + } + + private static void closeClosable(Object input) { + if (input instanceof Closeable) { + try { + ((Closeable)input).close(); + } + catch (IOException iOException) { + // empty catch block + } + } + } + + private static A getAnnotation(Method m, Class annotationClass) { + if (m == null || annotationClass == null) { + return null; + } + if (m.isAnnotationPresent(annotationClass)) { + return m.getAnnotation(annotationClass); + } + Class c = m.getDeclaringClass(); + if (c.getSuperclass() == null) { + return null; + } + for (Class i : c.getInterfaces()) { + try { + Method im = i.getMethod(m.getName(), m.getParameterTypes()); + return JSONObject.getAnnotation(im, annotationClass); + } + catch (SecurityException securityException) { + } + catch (NoSuchMethodException noSuchMethodException) { + // empty catch block + } + } + if (Object.class.equals(c.getSuperclass())) { + return null; + } + try { + return JSONObject.getAnnotation(c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), annotationClass); + } + catch (SecurityException ex) { + return null; + } + catch (NoSuchMethodException ex) { + return null; + } + } + + private static int getAnnotationDepth(Method m, Class annotationClass) { + if (m == null || annotationClass == null) { + return -1; + } + if (m.isAnnotationPresent(annotationClass)) { + return 1; + } + Class c = m.getDeclaringClass(); + if (c.getSuperclass() == null) { + return -1; + } + for (Class i : c.getInterfaces()) { + try { + Method im = i.getMethod(m.getName(), m.getParameterTypes()); + int d = JSONObject.getAnnotationDepth(im, annotationClass); + if (d <= 0) continue; + return d + 1; + } + catch (SecurityException securityException) { + } + catch (NoSuchMethodException noSuchMethodException) { + // empty catch block + } + } + if (Object.class.equals(c.getSuperclass())) { + return -1; + } + try { + int d = JSONObject.getAnnotationDepth(c.getSuperclass().getMethod(m.getName(), m.getParameterTypes()), annotationClass); + if (d > 0) { + return d + 1; + } + return -1; + } + catch (SecurityException ex) { + return -1; + } + catch (NoSuchMethodException ex) { + return -1; + } + } + + public JSONObject put(String key, boolean value) throws JSONException { + return this.put(key, value ? Boolean.TRUE : Boolean.FALSE); + } + + public JSONObject put(String key, Collection value) throws JSONException { + return this.put(key, new JSONArray(value)); + } + + public JSONObject put(String key, double value) throws JSONException { + return this.put(key, (Object)value); + } + + public JSONObject put(String key, float value) throws JSONException { + return this.put(key, Float.valueOf(value)); + } + + public JSONObject put(String key, int value) throws JSONException { + return this.put(key, (Object)value); + } + + public JSONObject put(String key, long value) throws JSONException { + return this.put(key, (Object)value); + } + + public JSONObject put(String key, Map value) throws JSONException { + return this.put(key, new JSONObject(value)); + } + + public JSONObject put(String key, Object value) throws JSONException { + if (key == null) { + throw new NullPointerException("Null key."); + } + if (value != null) { + JSONObject.testValidity(value); + this.map.put(key, value); + } else { + this.remove(key); + } + return this; + } + + public JSONObject putOnce(String key, Object value) throws JSONException { + if (key != null && value != null) { + if (this.opt(key) != null) { + throw new JSONException("Duplicate key \"" + key + "\""); + } + return this.put(key, value); + } + return this; + } + + public JSONObject putOpt(String key, Object value) throws JSONException { + if (key != null && value != null) { + return this.put(key, value); + } + return this; + } + + public Object query(String jsonPointer) { + return this.query(new JSONPointer(jsonPointer)); + } + + public Object query(JSONPointer jsonPointer) { + return jsonPointer.queryFrom(this); + } + + public Object optQuery(String jsonPointer) { + return this.optQuery(new JSONPointer(jsonPointer)); + } + + public Object optQuery(JSONPointer jsonPointer) { + try { + return jsonPointer.queryFrom(this); + } + catch (JSONPointerException e) { + return null; + } + } + + public static String quote(String string) { + if (string == null || string.isEmpty()) { + return "\"\""; + } + StringBuilderWriter sw = new StringBuilderWriter(string.length() + 2); + try { + return JSONObject.quote(string, sw).toString(); + } + catch (IOException ignored) { + return ""; + } + } + + public static Writer quote(String string, Writer w) throws IOException { + if (string == null || string.isEmpty()) { + w.write("\"\""); + return w; + } + char c = '\u0000'; + int len = string.length(); + w.write(34); + block9: for (int i = 0; i < len; ++i) { + char b = c; + c = string.charAt(i); + switch (c) { + case '\"': + case '\\': { + w.write(92); + w.write(c); + continue block9; + } + case '/': { + if (b == '<') { + w.write(92); + } + w.write(c); + continue block9; + } + case '\b': { + w.write("\\b"); + continue block9; + } + case '\t': { + w.write("\\t"); + continue block9; + } + case '\n': { + w.write("\\n"); + continue block9; + } + case '\f': { + w.write("\\f"); + continue block9; + } + case '\r': { + w.write("\\r"); + continue block9; + } + default: { + JSONObject.writeAsHex(w, c); + } + } + } + w.write(34); + return w; + } + + private static void writeAsHex(Writer w, char c) throws IOException { + if (c < ' ' || c >= '\u0080' && c < '\u00a0' || c >= '\u2000' && c < '\u2100') { + w.write("\\u"); + String hhhh = Integer.toHexString(c); + w.write("0000", 0, 4 - hhhh.length()); + w.write(hhhh); + } else { + w.write(c); + } + } + + public Object remove(String key) { + return this.map.remove(key); + } + + public boolean similar(Object other) { + try { + if (!(other instanceof JSONObject)) { + return false; + } + if (!this.keySet().equals(((JSONObject)other).keySet())) { + return false; + } + return this.checkSimilarEntries(other); + } + catch (Exception e) { + return false; + } + } + + private boolean checkSimilarEntries(Object other) { + for (Map.Entry entry : this.entrySet()) { + Object valueOther; + String name = entry.getKey(); + Object valueThis = entry.getValue(); + if (valueThis == (valueOther = ((JSONObject)other).get(name))) continue; + if (valueThis == null) { + return false; + } + if (this.checkObjectType(valueThis, valueOther)) continue; + return false; + } + return true; + } + + private boolean checkObjectType(Object valueThis, Object valueOther) { + if (valueThis instanceof JSONObject) { + return ((JSONObject)valueThis).similar(valueOther); + } + if (valueThis instanceof JSONArray) { + return ((JSONArray)valueThis).similar(valueOther); + } + if (valueThis instanceof Number && valueOther instanceof Number) { + return JSONObject.isNumberSimilar((Number)valueThis, (Number)valueOther); + } + if (valueThis instanceof JSONString && valueOther instanceof JSONString) { + return ((JSONString)valueThis).toJSONString().equals(((JSONString)valueOther).toJSONString()); + } + return valueThis.equals(valueOther); + } + + static boolean isNumberSimilar(Number l, Number r) { + if (!JSONObject.numberIsFinite(l) || !JSONObject.numberIsFinite(r)) { + return false; + } + if (l.getClass().equals(r.getClass()) && l instanceof Comparable) { + int compareTo = ((Comparable)((Object)l)).compareTo(r); + return compareTo == 0; + } + BigDecimal lBigDecimal = JSONObject.objectToBigDecimal(l, null, false); + BigDecimal rBigDecimal = JSONObject.objectToBigDecimal(r, null, false); + if (lBigDecimal == null || rBigDecimal == null) { + return false; + } + return lBigDecimal.compareTo(rBigDecimal) == 0; + } + + private static boolean numberIsFinite(Number n) { + if (n instanceof Double && (((Double)n).isInfinite() || ((Double)n).isNaN())) { + return false; + } + return !(n instanceof Float) || !((Float)n).isInfinite() && !((Float)n).isNaN(); + } + + protected static boolean isDecimalNotation(String val) { + return val.indexOf(46) > -1 || val.indexOf(101) > -1 || val.indexOf(69) > -1 || "-0".equals(val); + } + + public static Object stringToValue(String string) { + if ("".equals(string)) { + return string; + } + if ("true".equalsIgnoreCase(string)) { + return Boolean.TRUE; + } + if ("false".equalsIgnoreCase(string)) { + return Boolean.FALSE; + } + if ("null".equalsIgnoreCase(string)) { + return NULL; + } + char initial = string.charAt(0); + if (initial >= '0' && initial <= '9' || initial == '-') { + try { + return JSONObject.stringToNumber(string); + } + catch (Exception exception) { + // empty catch block + } + } + return string; + } + + protected static Number stringToNumber(String val) throws NumberFormatException { + char initial = val.charAt(0); + if (initial >= '0' && initial <= '9' || initial == '-') { + if (JSONObject.isDecimalNotation(val)) { + return JSONObject.getNumber(val, initial); + } + JSONObject.checkForInvalidNumberFormat(val, initial); + BigInteger bi = new BigInteger(val); + if (bi.bitLength() <= 31) { + return bi.intValue(); + } + if (bi.bitLength() <= 63) { + return bi.longValue(); + } + return bi; + } + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + + private static void checkForInvalidNumberFormat(String val, char initial) { + if (initial == '0' && val.length() > 1) { + char at1 = val.charAt(1); + if (at1 >= '0' && at1 <= '9') { + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + } else if (initial == '-' && val.length() > 2) { + char at1 = val.charAt(1); + char at2 = val.charAt(2); + if (at1 == '0' && at2 >= '0' && at2 <= '9') { + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + } + } + + private static Number getNumber(String val, char initial) { + try { + BigDecimal bd = new BigDecimal(val); + if (initial == '-' && BigDecimal.ZERO.compareTo(bd) == 0) { + return -0.0; + } + return bd; + } + catch (NumberFormatException retryAsDouble) { + try { + Double d = Double.valueOf(val); + if (d.isNaN() || d.isInfinite()) { + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + return d; + } + catch (NumberFormatException ignore) { + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + } + } + + public static void testValidity(Object o) throws JSONException { + if (o instanceof Number && !JSONObject.numberIsFinite((Number)o)) { + throw new JSONException("JSON does not allow non-finite numbers."); + } + } + + public JSONArray toJSONArray(JSONArray names) throws JSONException { + if (names == null || names.isEmpty()) { + return null; + } + JSONArray ja = new JSONArray(); + for (int i = 0; i < names.length(); ++i) { + ja.put(this.opt(names.getString(i))); + } + return ja; + } + + public String toString() { + try { + return this.toString(0); + } + catch (Exception e) { + return null; + } + } + + public String toString(int indentFactor) throws JSONException { + int initialSize = this.map.size() * 6; + StringBuilderWriter w = new StringBuilderWriter(Math.max(initialSize, 16)); + return this.write(w, indentFactor, 0).toString(); + } + + public static String valueToString(Object value) throws JSONException { + return JSONWriter.valueToString(value); + } + + public static Object wrap(Object object) { + return JSONObject.wrap(object, null); + } + + static Object wrap(Object object, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + return JSONObject.wrap(object, null, recursionDepth, jsonParserConfiguration); + } + + private static Object wrap(Object object, Set objectsRecord) { + return JSONObject.wrap(object, objectsRecord, 0, new JSONParserConfiguration()); + } + + private static Object wrap(Object object, Set objectsRecord, int recursionDepth, JSONParserConfiguration jsonParserConfiguration) { + try { + String objectPackageName; + if (NULL.equals(object)) { + return NULL; + } + if (object instanceof JSONObject || object instanceof JSONArray || object instanceof JSONString || object instanceof String || object instanceof Byte || object instanceof Character || object instanceof Short || object instanceof Integer || object instanceof Long || object instanceof Boolean || object instanceof Float || object instanceof Double || object instanceof BigInteger || object instanceof BigDecimal || object instanceof Enum) { + return object; + } + if (object instanceof Collection) { + Collection coll = (Collection)object; + return new JSONArray(coll, recursionDepth, jsonParserConfiguration); + } + if (object.getClass().isArray()) { + return new JSONArray(object); + } + if (object instanceof Map) { + Map map = (Map)object; + return new JSONObject(map, recursionDepth, jsonParserConfiguration); + } + Package objectPackage = object.getClass().getPackage(); + String string = objectPackageName = objectPackage != null ? objectPackage.getName() : ""; + if (objectPackageName.startsWith("java.") || objectPackageName.startsWith("javax.") || object.getClass().getClassLoader() == null) { + return object.toString(); + } + if (objectsRecord != null) { + return new JSONObject(object, objectsRecord); + } + return new JSONObject(object); + } + catch (JSONException exception) { + throw exception; + } + catch (Exception exception) { + return null; + } + } + + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + static final Writer writeValue(Writer writer, Object value, int indentFactor, int indent) throws JSONException, IOException { + if (value == null || value.equals(null)) { + writer.write("null"); + } else if (value instanceof JSONString) { + JSONObject.processJsonStringToWriteValue(writer, value); + } else { + if (value instanceof String) { + JSONObject.quote(value.toString(), writer); + return writer; + } + if (value instanceof Number) { + JSONObject.processNumberToWriteValue(writer, (Number)value); + } else if (value instanceof Boolean) { + writer.write(value.toString()); + } else if (value instanceof Enum) { + writer.write(JSONObject.quote(((Enum)value).name())); + } else if (value instanceof JSONObject) { + ((JSONObject)value).write(writer, indentFactor, indent); + } else if (value instanceof JSONArray) { + ((JSONArray)value).write(writer, indentFactor, indent); + } else if (value instanceof Map) { + Map map = (Map)value; + new JSONObject(map).write(writer, indentFactor, indent); + } else if (value instanceof Collection) { + Collection coll = (Collection)value; + new JSONArray(coll).write(writer, indentFactor, indent); + } else if (value.getClass().isArray()) { + new JSONArray(value).write(writer, indentFactor, indent); + } else { + JSONObject.quote(value.toString(), writer); + } + } + return writer; + } + + private static void processJsonStringToWriteValue(Writer writer, Object value) throws IOException { + String o; + try { + o = ((JSONString)value).toJSONString(); + } + catch (Exception e) { + throw new JSONException(e); + } + writer.write(o != null ? o.toString() : JSONObject.quote(value.toString())); + } + + private static void processNumberToWriteValue(Writer writer, Number value) throws IOException { + String numberAsString = JSONObject.numberToString(value); + if (NUMBER_PATTERN.matcher(numberAsString).matches()) { + writer.write(numberAsString); + } else { + JSONObject.quote(numberAsString, writer); + } + } + + static final void indent(Writer writer, int indent) throws IOException { + for (int i = 0; i < indent; ++i) { + writer.write(32); + } + } + + public Writer write(Writer writer, int indentFactor, int indent) throws JSONException { + try { + boolean needsComma = false; + int length = this.length(); + writer.write(123); + if (length == 1) { + Map.Entry entry = this.entrySet().iterator().next(); + String key = entry.getKey(); + writer.write(JSONObject.quote(key)); + writer.write(58); + if (indentFactor > 0) { + writer.write(32); + } + JSONObject.attemptWriteValue(writer, indentFactor, indent, entry, key); + } else if (length != 0) { + this.writeContent(writer, indentFactor, indent, needsComma); + } + writer.write(125); + return writer; + } + catch (IOException exception) { + throw new JSONException(exception); + } + } + + private void writeContent(Writer writer, int indentFactor, int indent, boolean needsComma) throws IOException { + int newIndent = indent + indentFactor; + for (Map.Entry entry : this.entrySet()) { + if (needsComma) { + writer.write(44); + } + if (indentFactor > 0) { + writer.write(10); + } + JSONObject.indent(writer, newIndent); + String key = entry.getKey(); + writer.write(JSONObject.quote(key)); + writer.write(58); + if (indentFactor > 0) { + writer.write(32); + } + JSONObject.attemptWriteValue(writer, indentFactor, newIndent, entry, key); + needsComma = true; + } + if (indentFactor > 0) { + writer.write(10); + } + JSONObject.indent(writer, indent); + } + + private static void attemptWriteValue(Writer writer, int indentFactor, int indent, Map.Entry entry, String key) { + try { + JSONObject.writeValue(writer, entry.getValue(), indentFactor, indent); + } + catch (Exception e) { + throw new JSONException("Unable to write JSONObject value for key: " + key, e); + } + } + + public Map toMap() { + HashMap results = new HashMap(); + for (Map.Entry entry : this.entrySet()) { + Object value = entry.getValue() == null || NULL.equals(entry.getValue()) ? null : (entry.getValue() instanceof JSONObject ? ((JSONObject)entry.getValue()).toMap() : (entry.getValue() instanceof JSONArray ? ((JSONArray)entry.getValue()).toList() : entry.getValue())); + results.put(entry.getKey(), value); + } + return results; + } + + private static JSONException wrongValueFormatException(String key, String valueType, Object value, Throwable cause) { + if (value == null) { + return new JSONException("JSONObject[" + JSONObject.quote(key) + "] is not a " + valueType + " (null).", cause); + } + if (value instanceof Map || value instanceof Iterable || value instanceof JSONObject) { + return new JSONException("JSONObject[" + JSONObject.quote(key) + "] is not a " + valueType + " (" + value.getClass() + ").", cause); + } + return new JSONException("JSONObject[" + JSONObject.quote(key) + "] is not a " + valueType + " (" + value.getClass() + " : " + value + ").", cause); + } + + private static JSONException recursivelyDefinedObjectException(String key) { + return new JSONException("JavaBean object contains recursively defined member variable of key " + JSONObject.quote(key)); + } + + private Class getRawType(Type type) { + if (type instanceof Class) { + return (Class)type; + } + if (type instanceof ParameterizedType) { + return (Class)((ParameterizedType)type).getRawType(); + } + if (type instanceof GenericArrayType) { + return Object[].class; + } + return Object.class; + } + + private Type getElementType(Type type) { + if (type instanceof ParameterizedType) { + Type[] args = ((ParameterizedType)type).getActualTypeArguments(); + return args.length > 0 ? args[0] : Object.class; + } + return Object.class; + } + + private Type[] getMapTypes(Type type) { + Type[] args; + if (type instanceof ParameterizedType && (args = ((ParameterizedType)type).getActualTypeArguments()).length == 2) { + return args; + } + return new Type[]{Object.class, Object.class}; + } + + public static T fromJson(String jsonString, Class clazz) { + JSONObject jsonObject = new JSONObject(jsonString); + return jsonObject.fromJson(clazz); + } + + public T fromJson(Class clazz) { + try { + T obj = clazz.getDeclaredConstructor(new Class[0]).newInstance(new Object[0]); + for (Field field : clazz.getDeclaredFields()) { + field.setAccessible(true); + String fieldName = field.getName(); + if (!this.has(fieldName)) continue; + Object value = this.get(fieldName); + Type fieldType = field.getGenericType(); + Object convertedValue = this.convertValue(value, fieldType); + field.set(obj, convertedValue); + } + return obj; + } + catch (NoSuchMethodException e) { + throw new JSONException("No no-arg constructor for class: " + clazz.getName(), e); + } + catch (Exception e) { + throw new JSONException("Failed to instantiate or set field for class: " + clazz.getName(), e); + } + } + + private Object convertValue(Object value, Type targetType) throws JSONException { + if (value == null) { + return null; + } + Class rawType = this.getRawType(targetType); + if (rawType.isAssignableFrom(value.getClass())) { + return value; + } + if (rawType == Integer.TYPE || rawType == Integer.class) { + return ((Number)value).intValue(); + } + if (rawType == Double.TYPE || rawType == Double.class) { + return ((Number)value).doubleValue(); + } + if (rawType == Float.TYPE || rawType == Float.class) { + return Float.valueOf(((Number)value).floatValue()); + } + if (rawType == Long.TYPE || rawType == Long.class) { + return ((Number)value).longValue(); + } + if (rawType == Boolean.TYPE || rawType == Boolean.class) { + return value; + } + if (rawType == String.class) { + return value; + } + if (rawType == BigDecimal.class) { + return new BigDecimal((String)value); + } + if (rawType == BigInteger.class) { + return new BigInteger((String)value); + } + if (rawType.isEnum() && value instanceof String) { + return this.stringToEnum(rawType, (String)value); + } + if (Collection.class.isAssignableFrom(rawType)) { + if (value instanceof JSONArray) { + Type elementType = this.getElementType(targetType); + return this.fromJsonArray((JSONArray)value, rawType, elementType); + } + } else { + if (Map.class.isAssignableFrom(rawType) && value instanceof JSONObject) { + Type[] mapTypes = this.getMapTypes(targetType); + Type keyType = mapTypes[0]; + Type valueType = mapTypes[1]; + return this.convertToMap((JSONObject)value, keyType, valueType, rawType); + } + if (!rawType.isPrimitive() && !rawType.isEnum() && value instanceof JSONObject) { + return ((JSONObject)value).fromJson(rawType); + } + } + return value.toString(); + } + + private Map convertToMap(JSONObject jsonMap, Type keyType, Type valueType, Class mapType) throws JSONException { + try { + HashMap createdMap = new HashMap(); + Iterator iterator = jsonMap.keySet().iterator(); + while (iterator.hasNext()) { + String keyObj; + String keyStr = keyObj = iterator.next(); + Object mapValue = jsonMap.get(keyStr); + Object convertedKey = this.convertValue(keyStr, keyType); + Object convertedValue = this.convertValue(mapValue, valueType); + createdMap.put(convertedKey, convertedValue); + } + return createdMap; + } + catch (Exception e) { + throw new JSONException("Failed to convert JSONObject to Map: " + mapType.getName(), e); + } + } + + private E stringToEnum(Class enumClass, String value) throws JSONException { + try { + Class enumType = enumClass; + Method valueOfMethod = enumType.getMethod("valueOf", String.class); + return (E)valueOfMethod.invoke(null, value); + } + catch (Exception e) { + throw new JSONException("Failed to convert string to enum: " + value + " for " + enumClass.getName(), e); + } + } + + private Collection fromJsonArray(JSONArray jsonArray, Class collectionType, Type elementType) throws JSONException { + try { + Collection collection = this.getCollection(collectionType); + for (int i = 0; i < jsonArray.length(); ++i) { + Object jsonElement = jsonArray.get(i); + Object convertedValue = this.convertValue(jsonElement, elementType); + collection.add(convertedValue); + } + return collection; + } + catch (Exception e) { + throw new JSONException("Failed to convert JSONArray to Collection: " + collectionType.getName(), e); + } + } + + private Collection getCollection(Class collectionType) throws JSONException { + if (collectionType == List.class || collectionType == ArrayList.class) { + return new ArrayList(); + } + if (collectionType == Set.class || collectionType == HashSet.class) { + return new HashSet(); + } + throw new JSONException("Unsupported Collection type: " + collectionType.getName()); + } + + private static final class Null { + private Null() { + } + + public boolean equals(Object object) { + return object == null || object == this; + } + + public int hashCode() { + return 0; + } + + public String toString() { + return "null"; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONParserConfiguration.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONParserConfiguration.java new file mode 100644 index 0000000..933c56f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONParserConfiguration.java @@ -0,0 +1,65 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.ParserConfiguration; + +public class JSONParserConfiguration +extends ParserConfiguration { + private boolean overwriteDuplicateKey = false; + private boolean useNativeNulls; + private boolean strictMode; + + @Override + protected JSONParserConfiguration clone() { + JSONParserConfiguration clone = new JSONParserConfiguration(); + clone.overwriteDuplicateKey = this.overwriteDuplicateKey; + clone.strictMode = this.strictMode; + clone.maxNestingDepth = this.maxNestingDepth; + clone.keepStrings = this.keepStrings; + clone.useNativeNulls = this.useNativeNulls; + return clone; + } + + public JSONParserConfiguration withMaxNestingDepth(int maxNestingDepth) { + JSONParserConfiguration clone = this.clone(); + clone.maxNestingDepth = maxNestingDepth; + return clone; + } + + public JSONParserConfiguration withOverwriteDuplicateKey(boolean overwriteDuplicateKey) { + JSONParserConfiguration clone = this.clone(); + clone.overwriteDuplicateKey = overwriteDuplicateKey; + return clone; + } + + public JSONParserConfiguration withUseNativeNulls(boolean useNativeNulls) { + JSONParserConfiguration clone = this.clone(); + clone.useNativeNulls = useNativeNulls; + return clone; + } + + public JSONParserConfiguration withStrictMode() { + return this.withStrictMode(true); + } + + public JSONParserConfiguration withStrictMode(boolean mode) { + JSONParserConfiguration clone = this.clone(); + clone.strictMode = mode; + return clone; + } + + public boolean isOverwriteDuplicateKey() { + return this.overwriteDuplicateKey; + } + + public boolean isUseNativeNulls() { + return this.useNativeNulls; + } + + public boolean isStrictMode() { + return this.strictMode; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPointer.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPointer.java new file mode 100644 index 0000000..2c4e415 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPointer.java @@ -0,0 +1,158 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONArray; +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.org.json.JSONPointerException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class JSONPointer { + private static final String ENCODING = "utf-8"; + private final List refTokens; + + public static Builder builder() { + return new Builder(); + } + + public JSONPointer(String pointer) { + String refs; + if (pointer == null) { + throw new NullPointerException("pointer cannot be null"); + } + if (pointer.isEmpty() || "#".equals(pointer)) { + this.refTokens = Collections.emptyList(); + return; + } + if (pointer.startsWith("#/")) { + refs = pointer.substring(2); + try { + refs = URLDecoder.decode(refs, ENCODING); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } else if (pointer.startsWith("/")) { + refs = pointer.substring(1); + } else { + throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'"); + } + this.refTokens = new ArrayList(); + int slashIdx = -1; + int prevSlashIdx = 0; + do { + String token; + if ((prevSlashIdx = slashIdx + 1) == (slashIdx = refs.indexOf(47, prevSlashIdx)) || prevSlashIdx == refs.length()) { + this.refTokens.add(""); + continue; + } + if (slashIdx >= 0) { + token = refs.substring(prevSlashIdx, slashIdx); + this.refTokens.add(JSONPointer.unescape(token)); + continue; + } + token = refs.substring(prevSlashIdx); + this.refTokens.add(JSONPointer.unescape(token)); + } while (slashIdx >= 0); + } + + public JSONPointer(List refTokens) { + this.refTokens = new ArrayList(refTokens); + } + + private static String unescape(String token) { + return token.replace("~1", "/").replace("~0", "~"); + } + + public Object queryFrom(Object document) throws JSONPointerException { + if (this.refTokens.isEmpty()) { + return document; + } + Object current = document; + for (String token : this.refTokens) { + if (current instanceof JSONObject) { + current = ((JSONObject)current).opt(JSONPointer.unescape(token)); + continue; + } + if (current instanceof JSONArray) { + current = JSONPointer.readByIndexToken(current, token); + continue; + } + throw new JSONPointerException(String.format("value [%s] is not an array or object therefore its key %s cannot be resolved", current, token)); + } + return current; + } + + private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException { + try { + int index = Integer.parseInt(indexToken); + JSONArray currentArr = (JSONArray)current; + if (index >= currentArr.length()) { + throw new JSONPointerException(String.format("index %s is out of bounds - the array has %d elements", indexToken, currentArr.length())); + } + try { + return currentArr.get(index); + } + catch (JSONException e) { + throw new JSONPointerException("Error reading value at index position " + index, e); + } + } + catch (NumberFormatException e) { + throw new JSONPointerException(String.format("%s is not an array index", indexToken), e); + } + } + + public String toString() { + StringBuilder rval = new StringBuilder(); + for (String token : this.refTokens) { + rval.append('/').append(JSONPointer.escape(token)); + } + return rval.toString(); + } + + private static String escape(String token) { + return token.replace("~", "~0").replace("/", "~1"); + } + + public String toURIFragment() { + try { + StringBuilder rval = new StringBuilder("#"); + for (String token : this.refTokens) { + rval.append('/').append(URLEncoder.encode(token, ENCODING)); + } + return rval.toString(); + } + catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + public static class Builder { + private final List refTokens = new ArrayList(); + + public JSONPointer build() { + return new JSONPointer(this.refTokens); + } + + public Builder append(String token) { + if (token == null) { + throw new NullPointerException("token cannot be null"); + } + this.refTokens.add(token); + return this; + } + + public Builder append(int arrayIndex) { + this.refTokens.add(String.valueOf(arrayIndex)); + return this; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPointerException.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPointerException.java new file mode 100644 index 0000000..7b3138b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPointerException.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONException; + +public class JSONPointerException +extends JSONException { + private static final long serialVersionUID = 8872944667561856751L; + + public JSONPointerException(String message) { + super(message); + } + + public JSONPointerException(String message, Throwable cause) { + super(message, cause); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPropertyIgnore.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPropertyIgnore.java new file mode 100644 index 0000000..458c9ac --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPropertyIgnore.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(value=RetentionPolicy.RUNTIME) +@Target(value={ElementType.METHOD}) +public @interface JSONPropertyIgnore { +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPropertyName.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPropertyName.java new file mode 100644 index 0000000..a5f0270 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONPropertyName.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(value=RetentionPolicy.RUNTIME) +@Target(value={ElementType.METHOD}) +public @interface JSONPropertyName { + public String value(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONString.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONString.java new file mode 100644 index 0000000..6549811 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONString.java @@ -0,0 +1,9 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +public interface JSONString { + public String toJSONString(); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONStringer.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONStringer.java new file mode 100644 index 0000000..ca8241a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONStringer.java @@ -0,0 +1,19 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONWriter; +import java.io.StringWriter; + +public class JSONStringer +extends JSONWriter { + public JSONStringer() { + super(new StringWriter()); + } + + public String toString() { + return this.mode == 'd' ? this.writer.toString() : null; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONTokener.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONTokener.java new file mode 100644 index 0000000..c6d13bb --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONTokener.java @@ -0,0 +1,401 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONArray; +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.org.json.JSONParserConfiguration; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.nio.charset.Charset; + +public class JSONTokener { + private long character; + private boolean eof; + private long index; + private long line; + private char previous; + private final Reader reader; + private boolean usePrevious; + private long characterPreviousLine; + private JSONParserConfiguration jsonParserConfiguration; + + public JSONTokener(Reader reader) { + this(reader, new JSONParserConfiguration()); + } + + public JSONTokener(Reader reader, JSONParserConfiguration jsonParserConfiguration) { + this.jsonParserConfiguration = jsonParserConfiguration; + this.reader = reader.markSupported() ? reader : new BufferedReader(reader); + this.eof = false; + this.usePrevious = false; + this.previous = '\u0000'; + this.index = 0L; + this.character = 1L; + this.characterPreviousLine = 0L; + this.line = 1L; + } + + public JSONTokener(InputStream inputStream) { + this(inputStream, new JSONParserConfiguration()); + } + + public JSONTokener(InputStream inputStream, JSONParserConfiguration jsonParserConfiguration) { + this(new InputStreamReader(inputStream, Charset.forName("UTF-8")), jsonParserConfiguration); + } + + public JSONTokener(String source) { + this(new StringReader(source)); + } + + public JSONTokener(String source, JSONParserConfiguration jsonParserConfiguration) { + this(new StringReader(source), jsonParserConfiguration); + } + + public JSONParserConfiguration getJsonParserConfiguration() { + return this.jsonParserConfiguration; + } + + @Deprecated + public void setJsonParserConfiguration(JSONParserConfiguration jsonParserConfiguration) { + this.jsonParserConfiguration = jsonParserConfiguration; + } + + public void back() throws JSONException { + if (this.usePrevious || this.index <= 0L) { + throw new JSONException("Stepping back two steps is not supported"); + } + this.decrementIndexes(); + this.usePrevious = true; + this.eof = false; + } + + private void decrementIndexes() { + --this.index; + if (this.previous == '\r' || this.previous == '\n') { + --this.line; + this.character = this.characterPreviousLine; + } else if (this.character > 0L) { + --this.character; + } + } + + public static int dehexchar(char c) { + if (c >= '0' && c <= '9') { + return c - 48; + } + if (c >= 'A' && c <= 'F') { + return c - 55; + } + if (c >= 'a' && c <= 'f') { + return c - 87; + } + return -1; + } + + public boolean end() { + return this.eof && !this.usePrevious; + } + + public boolean more() throws JSONException { + if (this.usePrevious) { + return true; + } + try { + this.reader.mark(1); + } + catch (IOException e) { + throw new JSONException("Unable to preserve stream position", e); + } + try { + if (this.reader.read() <= 0) { + this.eof = true; + return false; + } + this.reader.reset(); + } + catch (IOException e) { + throw new JSONException("Unable to read the next character from the stream", e); + } + return true; + } + + public char next() throws JSONException { + int c; + if (this.usePrevious) { + this.usePrevious = false; + c = this.previous; + } else { + try { + c = this.reader.read(); + } + catch (IOException exception) { + throw new JSONException(exception); + } + } + if (c <= 0) { + this.eof = true; + return '\u0000'; + } + this.incrementIndexes(c); + this.previous = (char)c; + return this.previous; + } + + protected char getPrevious() { + return this.previous; + } + + private void incrementIndexes(int c) { + if (c > 0) { + ++this.index; + if (c == 13) { + ++this.line; + this.characterPreviousLine = this.character; + this.character = 0L; + } else if (c == 10) { + if (this.previous != '\r') { + ++this.line; + this.characterPreviousLine = this.character; + } + this.character = 0L; + } else { + ++this.character; + } + } + } + + public char next(char c) throws JSONException { + char n = this.next(); + if (n != c) { + if (n > '\u0000') { + throw this.syntaxError("Expected '" + c + "' and instead saw '" + n + "'"); + } + throw this.syntaxError("Expected '" + c + "' and instead saw ''"); + } + return n; + } + + public String next(int n) throws JSONException { + if (n == 0) { + return ""; + } + char[] chars = new char[n]; + for (int pos = 0; pos < n; ++pos) { + chars[pos] = this.next(); + if (!this.end()) continue; + throw this.syntaxError("Substring bounds error"); + } + return new String(chars); + } + + public char nextClean() throws JSONException { + char c; + while ((c = this.next()) != '\u0000' && c <= ' ') { + } + return c; + } + + public String nextString(char quote) throws JSONException { + StringBuilder sb = new StringBuilder(); + block15: while (true) { + char c = this.next(); + switch (c) { + case '\u0000': + case '\n': + case '\r': { + throw this.syntaxError("Unterminated string. Character with int code " + c + " is not allowed within a quoted string."); + } + case '\\': { + c = this.next(); + switch (c) { + case 'b': { + sb.append('\b'); + continue block15; + } + case 't': { + sb.append('\t'); + continue block15; + } + case 'n': { + sb.append('\n'); + continue block15; + } + case 'f': { + sb.append('\f'); + continue block15; + } + case 'r': { + sb.append('\r'); + continue block15; + } + case 'u': { + String next = this.next(4); + try { + sb.append((char)Integer.parseInt(next, 16)); + continue block15; + } + catch (NumberFormatException e) { + throw this.syntaxError("Illegal escape. \\u must be followed by a 4 digit hexadecimal number. \\" + next + " is not valid.", e); + } + } + case '\"': + case '\'': + case '/': + case '\\': { + sb.append(c); + continue block15; + } + } + throw this.syntaxError("Illegal escape. Escape sequence \\" + c + " is not valid."); + } + } + if (c == quote) { + return sb.toString(); + } + sb.append(c); + } + } + + public String nextTo(char delimiter) throws JSONException { + StringBuilder sb = new StringBuilder(); + while (true) { + char c; + if ((c = this.next()) == delimiter || c == '\u0000' || c == '\n' || c == '\r') { + if (c != '\u0000') { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + public String nextTo(String delimiters) throws JSONException { + StringBuilder sb = new StringBuilder(); + while (true) { + char c; + if (delimiters.indexOf(c = this.next()) >= 0 || c == '\u0000' || c == '\n' || c == '\r') { + if (c != '\u0000') { + this.back(); + } + return sb.toString().trim(); + } + sb.append(c); + } + } + + public Object nextValue() throws JSONException { + char c = this.nextClean(); + switch (c) { + case '{': { + this.back(); + try { + return new JSONObject(this, this.jsonParserConfiguration); + } + catch (StackOverflowError e) { + throw new JSONException("JSON Array or Object depth too large to process.", e); + } + } + case '[': { + this.back(); + try { + return new JSONArray(this, this.jsonParserConfiguration); + } + catch (StackOverflowError e) { + throw new JSONException("JSON Array or Object depth too large to process.", e); + } + } + } + return this.nextSimpleValue(c); + } + + Object nextSimpleValue(char c) { + String string; + if (this.jsonParserConfiguration != null && this.jsonParserConfiguration.isStrictMode() && c == '\'') { + throw this.syntaxError("Strict mode error: Single quoted strings are not allowed"); + } + switch (c) { + case '\"': + case '\'': { + return this.nextString(c); + } + } + StringBuilder sb = new StringBuilder(); + while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) { + sb.append(c); + c = this.next(); + } + if (!this.eof) { + this.back(); + } + if ("".equals(string = sb.toString().trim())) { + throw this.syntaxError("Missing value"); + } + if (this.jsonParserConfiguration != null && this.jsonParserConfiguration.isStrictMode() && string.endsWith(".")) { + throw this.syntaxError(String.format("Strict mode error: Value '%s' ends with dot", string)); + } + Object obj = JSONObject.stringToValue(string); + if (this.jsonParserConfiguration != null && this.jsonParserConfiguration.isStrictMode()) { + if (obj instanceof Boolean && !"true".equals(string) && !"false".equals(string)) { + throw this.syntaxError(String.format("Strict mode error: Value '%s' is not lowercase boolean", obj)); + } + if (obj == JSONObject.NULL && !"null".equals(string)) { + throw this.syntaxError(String.format("Strict mode error: Value '%s' is not lowercase null", obj)); + } + if (obj instanceof String) { + throw this.syntaxError(String.format("Strict mode error: Value '%s' is not surrounded by quotes", obj)); + } + } + return obj; + } + + public char skipTo(char to) throws JSONException { + char c; + try { + long startIndex = this.index; + long startCharacter = this.character; + long startLine = this.line; + this.reader.mark(1000000); + do { + if ((c = this.next()) != '\u0000') continue; + this.reader.reset(); + this.index = startIndex; + this.character = startCharacter; + this.line = startLine; + return '\u0000'; + } while (c != to); + this.reader.mark(1); + } + catch (IOException exception) { + throw new JSONException(exception); + } + this.back(); + return c; + } + + public JSONException syntaxError(String message) { + return new JSONException(message + this.toString()); + } + + public JSONException syntaxError(String message, Throwable causedBy) { + return new JSONException(message + this.toString(), causedBy); + } + + public String toString() { + return " at " + this.index + " [character " + this.character + " line " + this.line + "]"; + } + + public void close() throws IOException { + if (this.reader != null) { + this.reader.close(); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/JSONWriter.java b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONWriter.java new file mode 100644 index 0000000..d4db3e1 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/JSONWriter.java @@ -0,0 +1,204 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONArray; +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.org.json.JSONString; +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +public class JSONWriter { + private static final int maxdepth = 200; + private boolean comma = false; + protected char mode = (char)105; + private final JSONObject[] stack = new JSONObject[200]; + private int top = 0; + protected Appendable writer; + + public JSONWriter(Appendable w) { + this.writer = w; + } + + private JSONWriter append(String string) throws JSONException { + if (string == null) { + throw new JSONException("Null pointer"); + } + if (this.mode == 'o' || this.mode == 'a') { + try { + if (this.comma && this.mode == 'a') { + this.writer.append(','); + } + this.writer.append(string); + } + catch (IOException e) { + throw new JSONException(e); + } + if (this.mode == 'o') { + this.mode = (char)107; + } + this.comma = true; + return this; + } + throw new JSONException("Value out of sequence."); + } + + public JSONWriter array() throws JSONException { + if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { + this.push(null); + this.append("["); + this.comma = false; + return this; + } + throw new JSONException("Misplaced array."); + } + + private JSONWriter end(char m, char c) throws JSONException { + if (this.mode != m) { + throw new JSONException(m == 'a' ? "Misplaced endArray." : "Misplaced endObject."); + } + this.pop(m); + try { + this.writer.append(c); + } + catch (IOException e) { + throw new JSONException(e); + } + this.comma = true; + return this; + } + + public JSONWriter endArray() throws JSONException { + return this.end('a', ']'); + } + + public JSONWriter endObject() throws JSONException { + return this.end('k', '}'); + } + + public JSONWriter key(String string) throws JSONException { + if (string == null) { + throw new JSONException("Null key."); + } + if (this.mode == 'k') { + try { + JSONObject topObject = this.stack[this.top - 1]; + if (topObject.has(string)) { + throw new JSONException("Duplicate key \"" + string + "\""); + } + topObject.put(string, true); + if (this.comma) { + this.writer.append(','); + } + this.writer.append(JSONObject.quote(string)); + this.writer.append(':'); + this.comma = false; + this.mode = (char)111; + return this; + } + catch (IOException e) { + throw new JSONException(e); + } + } + throw new JSONException("Misplaced key."); + } + + public JSONWriter object() throws JSONException { + if (this.mode == 'i') { + this.mode = (char)111; + } + if (this.mode == 'o' || this.mode == 'a') { + this.append("{"); + this.push(new JSONObject()); + this.comma = false; + return this; + } + throw new JSONException("Misplaced object."); + } + + private void pop(char c) throws JSONException { + char m; + if (this.top <= 0) { + throw new JSONException("Nesting error."); + } + char c2 = m = this.stack[this.top - 1] == null ? (char)'a' : 'k'; + if (m != c) { + throw new JSONException("Nesting error."); + } + --this.top; + this.mode = (char)(this.top == 0 ? 100 : (this.stack[this.top - 1] == null ? 97 : 107)); + } + + private void push(JSONObject jo) throws JSONException { + if (this.top >= 200) { + throw new JSONException("Nesting too deep."); + } + this.stack[this.top] = jo; + this.mode = (char)(jo == null ? 97 : 107); + ++this.top; + } + + public static String valueToString(Object value) throws JSONException { + if (value == null || value.equals(null)) { + return "null"; + } + if (value instanceof JSONString) { + String object; + try { + object = ((JSONString)value).toJSONString(); + } + catch (Exception e) { + throw new JSONException(e); + } + if (object != null) { + return object; + } + throw new JSONException("Bad value from toJSONString: " + object); + } + if (value instanceof Number) { + String numberAsString = JSONObject.numberToString((Number)value); + if (JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) { + return numberAsString; + } + return JSONObject.quote(numberAsString); + } + if (value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray) { + return value.toString(); + } + if (value instanceof Map) { + Map map = (Map)value; + return new JSONObject(map).toString(); + } + if (value instanceof Collection) { + Collection coll = (Collection)value; + return new JSONArray(coll).toString(); + } + if (value.getClass().isArray()) { + return new JSONArray(value).toString(); + } + if (value instanceof Enum) { + return JSONObject.quote(((Enum)value).name()); + } + return JSONObject.quote(value.toString()); + } + + public JSONWriter value(boolean b) throws JSONException { + return this.append(b ? "true" : "false"); + } + + public JSONWriter value(double d) throws JSONException { + return this.value((Object)d); + } + + public JSONWriter value(long l) throws JSONException { + return this.append(Long.toString(l)); + } + + public JSONWriter value(Object object) throws JSONException { + return this.append(JSONWriter.valueToString(object)); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/ParserConfiguration.java b/source/decompiled/de/maxhenkel/voicechat/org/json/ParserConfiguration.java new file mode 100644 index 0000000..0255516 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/ParserConfiguration.java @@ -0,0 +1,46 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +public class ParserConfiguration { + public static final int UNDEFINED_MAXIMUM_NESTING_DEPTH = -1; + public static final int DEFAULT_MAXIMUM_NESTING_DEPTH = 512; + protected boolean keepStrings; + protected int maxNestingDepth; + + public ParserConfiguration() { + this.keepStrings = false; + this.maxNestingDepth = 512; + } + + protected ParserConfiguration(boolean keepStrings, int maxNestingDepth) { + this.keepStrings = keepStrings; + this.maxNestingDepth = maxNestingDepth; + } + + protected ParserConfiguration clone() { + return new ParserConfiguration(this.keepStrings, this.maxNestingDepth); + } + + public boolean isKeepStrings() { + return this.keepStrings; + } + + public T withKeepStrings(boolean newVal) { + ParserConfiguration newConfig = this.clone(); + newConfig.keepStrings = newVal; + return (T)newConfig; + } + + public int getMaxNestingDepth() { + return this.maxNestingDepth; + } + + public T withMaxNestingDepth(int maxNestingDepth) { + ParserConfiguration newConfig = this.clone(); + newConfig.maxNestingDepth = maxNestingDepth > -1 ? maxNestingDepth : -1; + return (T)newConfig; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/Property.java b/source/decompiled/de/maxhenkel/voicechat/org/json/Property.java new file mode 100644 index 0000000..fbe5b0d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/Property.java @@ -0,0 +1,36 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import java.util.Enumeration; +import java.util.Properties; + +public class Property { + public static JSONObject toJSONObject(Properties properties) throws JSONException { + JSONObject jo = new JSONObject(); + if (properties != null && !properties.isEmpty()) { + Enumeration enumProperties = properties.propertyNames(); + while (enumProperties.hasMoreElements()) { + String name = (String)enumProperties.nextElement(); + jo.put(name, properties.getProperty(name)); + } + } + return jo; + } + + public static Properties toProperties(JSONObject jo) throws JSONException { + Properties properties = new Properties(); + if (jo != null) { + for (String key : jo.keySet()) { + Object value = jo.opt(key); + if (JSONObject.NULL.equals(value)) continue; + properties.put(key, value.toString()); + } + } + return properties; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/StringBuilderWriter.java b/source/decompiled/de/maxhenkel/voicechat/org/json/StringBuilderWriter.java new file mode 100644 index 0000000..d094217 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/StringBuilderWriter.java @@ -0,0 +1,81 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import java.io.IOException; +import java.io.Writer; + +public class StringBuilderWriter +extends Writer { + private final StringBuilder builder; + + public StringBuilderWriter() { + this.builder = new StringBuilder(); + this.lock = this.builder; + } + + public StringBuilderWriter(int initialSize) { + this.builder = new StringBuilder(initialSize); + this.lock = this.builder; + } + + @Override + public void write(int c) { + this.builder.append((char)c); + } + + @Override + public void write(char[] cbuf, int offset, int length) { + if (offset < 0 || offset > cbuf.length || length < 0 || offset + length > cbuf.length || offset + length < 0) { + throw new IndexOutOfBoundsException(); + } + if (length == 0) { + return; + } + this.builder.append(cbuf, offset, length); + } + + @Override + public void write(String str) { + this.builder.append(str); + } + + @Override + public void write(String str, int offset, int length) { + this.builder.append(str, offset, offset + length); + } + + @Override + public StringBuilderWriter append(CharSequence csq) { + this.write(String.valueOf(csq)); + return this; + } + + @Override + public StringBuilderWriter append(CharSequence csq, int start, int end) { + if (csq == null) { + csq = "null"; + } + return this.append(csq.subSequence(start, end)); + } + + @Override + public StringBuilderWriter append(char c) { + this.write(c); + return this; + } + + public String toString() { + return this.builder.toString(); + } + + @Override + public void flush() { + } + + @Override + public void close() throws IOException { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/XML.java b/source/decompiled/de/maxhenkel/voicechat/org/json/XML.java new file mode 100644 index 0000000..7583461 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/XML.java @@ -0,0 +1,623 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONArray; +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONObject; +import de.maxhenkel.voicechat.org.json.XMLParserConfiguration; +import de.maxhenkel.voicechat.org.json.XMLTokener; +import de.maxhenkel.voicechat.org.json.XMLXsiTypeConverter; +import java.io.Reader; +import java.io.StringReader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Iterator; + +public class XML { + public static final Character AMP = Character.valueOf('&'); + public static final Character APOS = Character.valueOf('\''); + public static final Character BANG = Character.valueOf('!'); + public static final Character EQ = Character.valueOf('='); + public static final Character GT = Character.valueOf('>'); + public static final Character LT = Character.valueOf('<'); + public static final Character QUEST = Character.valueOf('?'); + public static final Character QUOT = Character.valueOf('\"'); + public static final Character SLASH = Character.valueOf('/'); + public static final String NULL_ATTR = "xsi:nil"; + public static final String TYPE_ATTR = "xsi:type"; + + private static Iterable codePointIterator(final String string) { + return new Iterable(){ + + @Override + public Iterator iterator() { + return new Iterator(){ + private int nextIndex = 0; + private int length; + { + this.length = string.length(); + } + + @Override + public boolean hasNext() { + return this.nextIndex < this.length; + } + + @Override + public Integer next() { + int result = string.codePointAt(this.nextIndex); + this.nextIndex += Character.charCount(result); + return result; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + public static String escape(String string) { + StringBuilder sb = new StringBuilder(string.length()); + block7: for (int cp : XML.codePointIterator(string)) { + switch (cp) { + case 38: { + sb.append("&"); + continue block7; + } + case 60: { + sb.append("<"); + continue block7; + } + case 62: { + sb.append(">"); + continue block7; + } + case 34: { + sb.append("""); + continue block7; + } + case 39: { + sb.append("'"); + continue block7; + } + } + if (XML.mustEscape(cp)) { + sb.append("&#x"); + sb.append(Integer.toHexString(cp)); + sb.append(';'); + continue; + } + sb.appendCodePoint(cp); + } + return sb.toString(); + } + + private static boolean mustEscape(int cp) { + return Character.isISOControl(cp) && cp != 9 && cp != 10 && cp != 13 || (cp < 32 || cp > 55295) && (cp < 57344 || cp > 65533) && (cp < 65536 || cp > 0x10FFFF); + } + + public static String unescape(String string) { + StringBuilder sb = new StringBuilder(string.length()); + int length = string.length(); + for (int i = 0; i < length; ++i) { + char c = string.charAt(i); + if (c == '&') { + int semic = string.indexOf(59, i); + if (semic > i) { + String entity = string.substring(i + 1, semic); + sb.append(XMLTokener.unescapeEntity(entity)); + i += entity.length() + 1; + continue; + } + sb.append(c); + continue; + } + sb.append(c); + } + return sb.toString(); + } + + public static void noSpace(String string) throws JSONException { + int length = string.length(); + if (length == 0) { + throw new JSONException("Empty string."); + } + for (int i = 0; i < length; ++i) { + if (!Character.isWhitespace(string.charAt(i))) continue; + throw new JSONException("'" + string + "' contains a space character."); + } + } + + private static boolean parse(XMLTokener x, JSONObject context, String name, XMLParserConfiguration config, int currentNestingDepth) throws JSONException { + Object obj; + String string; + JSONObject jsonObject = null; + Object token = x.nextToken(); + if (token == BANG) { + char c = x.next(); + if (c == '-') { + if (x.next() == '-') { + x.skipPast("-->"); + return false; + } + x.back(); + } else if (c == '[') { + token = x.nextToken(); + if ("CDATA".equals(token) && x.next() == '[') { + String string2 = x.nextCDATA(); + if (string2.length() > 0) { + context.accumulate(config.getcDataTagName(), string2); + } + return false; + } + throw x.syntaxError("Expected 'CDATA['"); + } + int i = 1; + do { + if ((token = x.nextMeta()) == null) { + throw x.syntaxError("Missing '>' after ' 0); + return false; + } + if (token == QUEST) { + x.skipPast("?>"); + return false; + } + if (token == SLASH) { + token = x.nextToken(); + if (name == null) { + throw x.syntaxError("Mismatched close tag " + token); + } + if (!token.equals(name)) { + throw x.syntaxError("Mismatched " + name + " and " + token); + } + if (x.nextToken() != GT) { + throw x.syntaxError("Misshaped close tag"); + } + return true; + } + if (token instanceof Character) { + throw x.syntaxError("Misshaped tag"); + } + String tagName = (String)token; + token = null; + jsonObject = new JSONObject(); + boolean nilAttributeFound = false; + XMLXsiTypeConverter xmlXsiTypeConverter = null; + while (true) { + if (token == null) { + token = x.nextToken(); + } + if (!(token instanceof String)) break; + string = (String)token; + token = x.nextToken(); + if (token == EQ) { + token = x.nextToken(); + if (!(token instanceof String)) { + throw x.syntaxError("Missing value"); + } + if (config.isConvertNilAttributeToNull() && NULL_ATTR.equals(string) && Boolean.parseBoolean((String)token)) { + nilAttributeFound = true; + } else if (config.getXsiTypeMap() != null && !config.getXsiTypeMap().isEmpty() && TYPE_ATTR.equals(string)) { + xmlXsiTypeConverter = config.getXsiTypeMap().get(token); + } else if (!nilAttributeFound) { + obj = XML.stringToValue((String)token); + if (obj instanceof Boolean) { + jsonObject.accumulate(string, config.isKeepBooleanAsString() ? (String)token : obj); + } else if (obj instanceof Number) { + jsonObject.accumulate(string, config.isKeepNumberAsString() ? (String)token : obj); + } else { + jsonObject.accumulate(string, XML.stringToValue((String)token)); + } + } + token = null; + continue; + } + jsonObject.accumulate(string, ""); + } + if (token == SLASH) { + if (x.nextToken() != GT) { + throw x.syntaxError("Misshaped tag"); + } + if (config.getForceList().contains(tagName)) { + if (nilAttributeFound) { + context.append(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.append(tagName, jsonObject); + } else { + context.put(tagName, new JSONArray()); + } + } else if (nilAttributeFound) { + context.accumulate(tagName, JSONObject.NULL); + } else if (jsonObject.length() > 0) { + context.accumulate(tagName, jsonObject); + } else { + context.accumulate(tagName, ""); + } + return false; + } + if (token == GT) { + while (true) { + if ((token = x.nextContent()) == null) { + if (tagName != null) { + throw x.syntaxError("Unclosed tag " + tagName); + } + return false; + } + if (token instanceof String) { + string = (String)token; + if (string.length() <= 0) continue; + if (xmlXsiTypeConverter != null) { + jsonObject.accumulate(config.getcDataTagName(), XML.stringToValue(string, xmlXsiTypeConverter)); + continue; + } + obj = XML.stringToValue((String)token); + if (obj instanceof Boolean) { + jsonObject.accumulate(config.getcDataTagName(), config.isKeepBooleanAsString() ? (String)token : obj); + continue; + } + if (obj instanceof Number) { + jsonObject.accumulate(config.getcDataTagName(), config.isKeepNumberAsString() ? (String)token : obj); + continue; + } + if (obj == JSONObject.NULL) { + jsonObject.accumulate(config.getcDataTagName(), config.isKeepStrings() ? (String)token : obj); + continue; + } + jsonObject.accumulate(config.getcDataTagName(), XML.stringToValue((String)token)); + continue; + } + if (token != LT) continue; + if (currentNestingDepth == config.getMaxNestingDepth()) { + throw x.syntaxError("Maximum nesting depth of " + config.getMaxNestingDepth() + " reached"); + } + if (XML.parse(x, jsonObject, tagName, config, currentNestingDepth + 1)) break; + } + if (config.getForceList().contains(tagName)) { + if (jsonObject.length() == 0) { + context.put(tagName, new JSONArray()); + } else if (jsonObject.length() == 1 && jsonObject.opt(config.getcDataTagName()) != null) { + context.append(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + context.append(tagName, jsonObject); + } + } else if (jsonObject.length() == 0) { + context.accumulate(tagName, ""); + } else if (jsonObject.length() == 1 && jsonObject.opt(config.getcDataTagName()) != null) { + context.accumulate(tagName, jsonObject.opt(config.getcDataTagName())); + } else { + if (!config.shouldTrimWhiteSpace()) { + XML.removeEmpty(jsonObject, config); + } + context.accumulate(tagName, jsonObject); + } + return false; + } + throw x.syntaxError("Misshaped tag"); + } + + private static void removeEmpty(JSONObject jsonObject, XMLParserConfiguration config) { + if (jsonObject.has(config.getcDataTagName())) { + Object s = jsonObject.get(config.getcDataTagName()); + if (s instanceof String) { + if (XML.isStringAllWhiteSpace(s.toString())) { + jsonObject.remove(config.getcDataTagName()); + } + } else if (s instanceof JSONArray) { + JSONArray sArray = (JSONArray)s; + for (int k = sArray.length() - 1; k >= 0; --k) { + String s1; + Object eachString = sArray.get(k); + if (!(eachString instanceof String) || !XML.isStringAllWhiteSpace(s1 = (String)eachString)) continue; + sArray.remove(k); + } + if (sArray.isEmpty()) { + jsonObject.remove(config.getcDataTagName()); + } + } + } + } + + private static boolean isStringAllWhiteSpace(String s) { + for (int k = 0; k < s.length(); ++k) { + char eachChar = s.charAt(k); + if (Character.isWhitespace(eachChar)) continue; + return false; + } + return true; + } + + private static Number stringToNumber(String val) throws NumberFormatException { + char initial = val.charAt(0); + if (initial >= '0' && initial <= '9' || initial == '-') { + BigInteger bi; + char at1; + if (XML.isDecimalNotation(val)) { + try { + BigDecimal bd = new BigDecimal(val); + if (initial == '-' && BigDecimal.ZERO.compareTo(bd) == 0) { + return -0.0; + } + return bd; + } + catch (NumberFormatException retryAsDouble) { + try { + Double d = Double.valueOf(val); + if (d.isNaN() || d.isInfinite()) { + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + return d; + } + catch (NumberFormatException ignore) { + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + } + } + if (initial == '0' && val.length() > 1) { + at1 = val.charAt(1); + if (at1 >= '0' && at1 <= '9') { + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + } else if (initial == '-' && val.length() > 2) { + at1 = val.charAt(1); + char at2 = val.charAt(2); + if (at1 == '0' && at2 >= '0' && at2 <= '9') { + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + } + if ((bi = new BigInteger(val)).bitLength() <= 31) { + return bi.intValue(); + } + if (bi.bitLength() <= 63) { + return bi.longValue(); + } + return bi; + } + throw new NumberFormatException("val [" + val + "] is not a valid number."); + } + + private static boolean isDecimalNotation(String val) { + return val.indexOf(46) > -1 || val.indexOf(101) > -1 || val.indexOf(69) > -1 || "-0".equals(val); + } + + public static Object stringToValue(String string, XMLXsiTypeConverter typeConverter) { + if (typeConverter != null) { + return typeConverter.convert(string); + } + return XML.stringToValue(string); + } + + public static Object stringToValue(String string) { + if ("".equals(string)) { + return string; + } + if ("true".equalsIgnoreCase(string)) { + return Boolean.TRUE; + } + if ("false".equalsIgnoreCase(string)) { + return Boolean.FALSE; + } + if ("null".equalsIgnoreCase(string)) { + return JSONObject.NULL; + } + char initial = string.charAt(0); + if (initial >= '0' && initial <= '9' || initial == '-') { + try { + return XML.stringToNumber(string); + } + catch (Exception exception) { + // empty catch block + } + } + return string; + } + + public static JSONObject toJSONObject(String string) throws JSONException { + return XML.toJSONObject(string, XMLParserConfiguration.ORIGINAL); + } + + public static JSONObject toJSONObject(Reader reader) throws JSONException { + return XML.toJSONObject(reader, XMLParserConfiguration.ORIGINAL); + } + + public static JSONObject toJSONObject(Reader reader, boolean keepStrings) throws JSONException { + if (keepStrings) { + return XML.toJSONObject(reader, XMLParserConfiguration.KEEP_STRINGS); + } + return XML.toJSONObject(reader, XMLParserConfiguration.ORIGINAL); + } + + public static JSONObject toJSONObject(Reader reader, boolean keepNumberAsString, boolean keepBooleanAsString) throws JSONException { + XMLParserConfiguration xmlParserConfiguration = new XMLParserConfiguration(); + if (keepNumberAsString) { + xmlParserConfiguration = xmlParserConfiguration.withKeepNumberAsString(keepNumberAsString); + } + if (keepBooleanAsString) { + xmlParserConfiguration = xmlParserConfiguration.withKeepBooleanAsString(keepBooleanAsString); + } + return XML.toJSONObject(reader, xmlParserConfiguration); + } + + public static JSONObject toJSONObject(Reader reader, XMLParserConfiguration config) throws JSONException { + JSONObject jo = new JSONObject(); + XMLTokener x = new XMLTokener(reader, config); + while (x.more()) { + x.skipPast("<"); + if (!x.more()) continue; + XML.parse(x, jo, null, config, 0); + } + return jo; + } + + public static JSONObject toJSONObject(String string, boolean keepStrings) throws JSONException { + return XML.toJSONObject((Reader)new StringReader(string), keepStrings); + } + + public static JSONObject toJSONObject(String string, boolean keepNumberAsString, boolean keepBooleanAsString) throws JSONException { + return XML.toJSONObject(new StringReader(string), keepNumberAsString, keepBooleanAsString); + } + + public static JSONObject toJSONObject(String string, XMLParserConfiguration config) throws JSONException { + return XML.toJSONObject((Reader)new StringReader(string), config); + } + + public static String toString(Object object) throws JSONException { + return XML.toString(object, null, XMLParserConfiguration.ORIGINAL); + } + + public static String toString(Object object, String tagName) { + return XML.toString(object, tagName, XMLParserConfiguration.ORIGINAL); + } + + public static String toString(Object object, String tagName, XMLParserConfiguration config) throws JSONException { + return XML.toString(object, tagName, config, 0, 0); + } + + private static String toString(Object object, String tagName, XMLParserConfiguration config, int indentFactor, int indent) throws JSONException { + String indentationSuffix; + StringBuilder sb = new StringBuilder(); + if (object instanceof JSONObject) { + if (tagName != null) { + sb.append(XML.indent(indent)); + sb.append('<'); + sb.append(tagName); + sb.append('>'); + if (indentFactor > 0) { + sb.append("\n"); + indent += indentFactor; + } + } + JSONObject jo = (JSONObject)object; + for (String key : jo.keySet()) { + Object val; + int i; + int jaLength; + JSONArray ja; + Object value = jo.opt(key); + if (value == null) { + value = ""; + } else if (value.getClass().isArray()) { + value = new JSONArray(value); + } + if (key.equals(config.getcDataTagName())) { + if (value instanceof JSONArray) { + ja = (JSONArray)value; + jaLength = ja.length(); + for (i = 0; i < jaLength; ++i) { + if (i > 0) { + sb.append('\n'); + } + val = ja.opt(i); + sb.append(XML.escape(val.toString())); + } + continue; + } + sb.append(XML.escape(value.toString())); + continue; + } + if (value instanceof JSONArray) { + ja = (JSONArray)value; + jaLength = ja.length(); + for (i = 0; i < jaLength; ++i) { + val = ja.opt(i); + if (val instanceof JSONArray) { + sb.append('<'); + sb.append(key); + sb.append('>'); + sb.append(XML.toString(val, null, config, indentFactor, indent)); + sb.append("'); + continue; + } + sb.append(XML.toString(val, key, config, indentFactor, indent)); + } + continue; + } + if ("".equals(value)) { + if (config.isCloseEmptyTag()) { + sb.append(XML.indent(indent)); + sb.append('<'); + sb.append(key); + sb.append(">"); + sb.append(""); + if (indentFactor <= 0) continue; + sb.append("\n"); + continue; + } + sb.append(XML.indent(indent)); + sb.append('<'); + sb.append(key); + sb.append("/>"); + if (indentFactor <= 0) continue; + sb.append("\n"); + continue; + } + sb.append(XML.toString(value, key, config, indentFactor, indent)); + } + if (tagName != null) { + sb.append(XML.indent(indent - indentFactor)); + sb.append("'); + if (indentFactor > 0) { + sb.append("\n"); + } + } + return sb.toString(); + } + if (object != null && (object instanceof JSONArray || object.getClass().isArray())) { + JSONArray ja = object.getClass().isArray() ? new JSONArray(object) : (JSONArray)object; + int jaLength = ja.length(); + for (int i = 0; i < jaLength; ++i) { + Object val = ja.opt(i); + sb.append(XML.toString(val, tagName == null ? "array" : tagName, config, indentFactor, indent)); + } + return sb.toString(); + } + String string = object == null ? "null" : XML.escape(object.toString()); + String string2 = indentationSuffix = indentFactor > 0 ? "\n" : ""; + if (tagName == null) { + return XML.indent(indent) + "\"" + string + "\"" + indentationSuffix; + } + if (string.length() == 0) { + return XML.indent(indent) + "<" + tagName + "/>" + indentationSuffix; + } + return XML.indent(indent) + "<" + tagName + ">" + string + "" + indentationSuffix; + } + + public static String toString(Object object, int indentFactor) { + return XML.toString(object, null, XMLParserConfiguration.ORIGINAL, indentFactor); + } + + public static String toString(Object object, String tagName, int indentFactor) { + return XML.toString(object, tagName, XMLParserConfiguration.ORIGINAL, indentFactor); + } + + public static String toString(Object object, String tagName, XMLParserConfiguration config, int indentFactor) throws JSONException { + return XML.toString(object, tagName, config, indentFactor, 0); + } + + private static final String indent(int indent) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < indent; ++i) { + sb.append(' '); + } + return sb.toString(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/XMLParserConfiguration.java b/source/decompiled/de/maxhenkel/voicechat/org/json/XMLParserConfiguration.java new file mode 100644 index 0000000..8a552e3 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/XMLParserConfiguration.java @@ -0,0 +1,175 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.ParserConfiguration; +import de.maxhenkel.voicechat.org.json.XMLXsiTypeConverter; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class XMLParserConfiguration +extends ParserConfiguration { + private boolean keepNumberAsString; + private boolean keepBooleanAsString; + public static final XMLParserConfiguration ORIGINAL = new XMLParserConfiguration(); + public static final XMLParserConfiguration KEEP_STRINGS = new XMLParserConfiguration().withKeepStrings(true); + private String cDataTagName; + private boolean convertNilAttributeToNull; + private boolean closeEmptyTag; + private Map> xsiTypeMap; + private Set forceList; + private boolean shouldTrimWhiteSpace; + + public XMLParserConfiguration() { + this.cDataTagName = "content"; + this.convertNilAttributeToNull = false; + this.xsiTypeMap = Collections.emptyMap(); + this.forceList = Collections.emptySet(); + this.shouldTrimWhiteSpace = true; + } + + @Deprecated + public XMLParserConfiguration(boolean keepStrings) { + this(keepStrings, "content", false); + } + + @Deprecated + public XMLParserConfiguration(String cDataTagName) { + this(false, cDataTagName, false); + } + + @Deprecated + public XMLParserConfiguration(boolean keepStrings, String cDataTagName) { + super(keepStrings, 512); + this.cDataTagName = cDataTagName; + this.convertNilAttributeToNull = false; + } + + @Deprecated + public XMLParserConfiguration(boolean keepStrings, String cDataTagName, boolean convertNilAttributeToNull) { + super(false, 512); + this.keepNumberAsString = keepStrings; + this.keepBooleanAsString = keepStrings; + this.cDataTagName = cDataTagName; + this.convertNilAttributeToNull = convertNilAttributeToNull; + } + + private XMLParserConfiguration(boolean keepStrings, String cDataTagName, boolean convertNilAttributeToNull, Map> xsiTypeMap, Set forceList, int maxNestingDepth, boolean closeEmptyTag, boolean keepNumberAsString, boolean keepBooleanAsString) { + super(false, maxNestingDepth); + this.keepNumberAsString = keepNumberAsString; + this.keepBooleanAsString = keepBooleanAsString; + this.cDataTagName = cDataTagName; + this.convertNilAttributeToNull = convertNilAttributeToNull; + this.xsiTypeMap = Collections.unmodifiableMap(xsiTypeMap); + this.forceList = Collections.unmodifiableSet(forceList); + this.closeEmptyTag = closeEmptyTag; + } + + @Override + protected XMLParserConfiguration clone() { + XMLParserConfiguration config = new XMLParserConfiguration(this.keepStrings, this.cDataTagName, this.convertNilAttributeToNull, this.xsiTypeMap, this.forceList, this.maxNestingDepth, this.closeEmptyTag, this.keepNumberAsString, this.keepBooleanAsString); + config.shouldTrimWhiteSpace = this.shouldTrimWhiteSpace; + return config; + } + + public XMLParserConfiguration withKeepStrings(boolean newVal) { + XMLParserConfiguration newConfig = this.clone(); + newConfig.keepStrings = newVal; + newConfig.keepNumberAsString = newVal; + newConfig.keepBooleanAsString = newVal; + return newConfig; + } + + public XMLParserConfiguration withKeepNumberAsString(boolean newVal) { + XMLParserConfiguration newConfig = this.clone(); + newConfig.keepNumberAsString = newVal; + newConfig.keepStrings = newConfig.keepBooleanAsString && newConfig.keepNumberAsString; + return newConfig; + } + + public XMLParserConfiguration withKeepBooleanAsString(boolean newVal) { + XMLParserConfiguration newConfig = this.clone(); + newConfig.keepBooleanAsString = newVal; + newConfig.keepStrings = newConfig.keepBooleanAsString && newConfig.keepNumberAsString; + return newConfig; + } + + public String getcDataTagName() { + return this.cDataTagName; + } + + public boolean isKeepNumberAsString() { + return this.keepNumberAsString; + } + + public boolean isKeepBooleanAsString() { + return this.keepBooleanAsString; + } + + public XMLParserConfiguration withcDataTagName(String newVal) { + XMLParserConfiguration newConfig = this.clone(); + newConfig.cDataTagName = newVal; + return newConfig; + } + + public boolean isConvertNilAttributeToNull() { + return this.convertNilAttributeToNull; + } + + public XMLParserConfiguration withConvertNilAttributeToNull(boolean newVal) { + XMLParserConfiguration newConfig = this.clone(); + newConfig.convertNilAttributeToNull = newVal; + return newConfig; + } + + public Map> getXsiTypeMap() { + return this.xsiTypeMap; + } + + public XMLParserConfiguration withXsiTypeMap(Map> xsiTypeMap) { + XMLParserConfiguration newConfig = this.clone(); + HashMap cloneXsiTypeMap = new HashMap(xsiTypeMap); + newConfig.xsiTypeMap = Collections.unmodifiableMap(cloneXsiTypeMap); + return newConfig; + } + + public Set getForceList() { + return this.forceList; + } + + public XMLParserConfiguration withForceList(Set forceList) { + XMLParserConfiguration newConfig = this.clone(); + HashSet cloneForceList = new HashSet(forceList); + newConfig.forceList = Collections.unmodifiableSet(cloneForceList); + return newConfig; + } + + public XMLParserConfiguration withMaxNestingDepth(int maxNestingDepth) { + return (XMLParserConfiguration)super.withMaxNestingDepth(maxNestingDepth); + } + + public XMLParserConfiguration withCloseEmptyTag(boolean closeEmptyTag) { + XMLParserConfiguration clonedConfiguration = this.clone(); + clonedConfiguration.closeEmptyTag = closeEmptyTag; + return clonedConfiguration; + } + + public XMLParserConfiguration withShouldTrimWhitespace(boolean shouldTrimWhiteSpace) { + XMLParserConfiguration clonedConfiguration = this.clone(); + clonedConfiguration.shouldTrimWhiteSpace = shouldTrimWhiteSpace; + return clonedConfiguration; + } + + public boolean isCloseEmptyTag() { + return this.closeEmptyTag; + } + + public boolean shouldTrimWhiteSpace() { + return this.shouldTrimWhiteSpace; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/XMLTokener.java b/source/decompiled/de/maxhenkel/voicechat/org/json/XMLTokener.java new file mode 100644 index 0000000..b1fb94e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/XMLTokener.java @@ -0,0 +1,278 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +import de.maxhenkel.voicechat.org.json.JSONException; +import de.maxhenkel.voicechat.org.json.JSONTokener; +import de.maxhenkel.voicechat.org.json.XML; +import de.maxhenkel.voicechat.org.json.XMLParserConfiguration; +import java.io.Reader; +import java.util.HashMap; + +public class XMLTokener +extends JSONTokener { + public static final HashMap entity = new HashMap(8); + private XMLParserConfiguration configuration = XMLParserConfiguration.ORIGINAL; + + public XMLTokener(Reader r) { + super(r); + } + + public XMLTokener(String s) { + super(s); + } + + public XMLTokener(Reader r, XMLParserConfiguration configuration) { + super(r); + this.configuration = configuration; + } + + public String nextCDATA() throws JSONException { + StringBuilder sb = new StringBuilder(); + while (this.more()) { + char c = this.next(); + sb.append(c); + int i = sb.length() - 3; + if (i < 0 || sb.charAt(i) != ']' || sb.charAt(i + 1) != ']' || sb.charAt(i + 2) != '>') continue; + sb.setLength(i); + return sb.toString(); + } + throw this.syntaxError("Unclosed CDATA"); + } + + public Object nextContent() throws JSONException { + char c; + while (Character.isWhitespace(c = this.next()) && this.configuration.shouldTrimWhiteSpace()) { + } + if (c == '\u0000') { + return null; + } + if (c == '<') { + return XML.LT; + } + StringBuilder sb = new StringBuilder(); + while (c != '\u0000') { + if (c == '<') { + this.back(); + if (this.configuration.shouldTrimWhiteSpace()) { + return sb.toString().trim(); + } + return sb.toString(); + } + if (c == '&') { + sb.append(this.nextEntity(c)); + } else { + sb.append(c); + } + c = this.next(); + } + return sb.toString().trim(); + } + + public Object nextEntity(char ampersand) throws JSONException { + char c; + StringBuilder sb = new StringBuilder(); + while (Character.isLetterOrDigit(c = this.next()) || c == '#') { + sb.append(Character.toLowerCase(c)); + } + if (c != ';') { + throw this.syntaxError("Missing ';' in XML entity: &" + sb); + } + String string = sb.toString(); + return XMLTokener.unescapeEntity(string); + } + + static String unescapeEntity(String e) { + if (e == null || e.isEmpty()) { + return ""; + } + if (e.charAt(0) == '#') { + int cp = e.charAt(1) == 'x' || e.charAt(1) == 'X' ? Integer.parseInt(e.substring(2), 16) : Integer.parseInt(e.substring(1)); + return new String(new int[]{cp}, 0, 1); + } + Character knownEntity = entity.get(e); + if (knownEntity == null) { + return '&' + e + ';'; + } + return knownEntity.toString(); + } + + public Object nextMeta() throws JSONException { + char c; + while (Character.isWhitespace(c = this.next())) { + } + switch (c) { + case '\u0000': { + throw this.syntaxError("Misshaped meta tag"); + } + case '<': { + return XML.LT; + } + case '>': { + return XML.GT; + } + case '/': { + return XML.SLASH; + } + case '=': { + return XML.EQ; + } + case '!': { + return XML.BANG; + } + case '?': { + return XML.QUEST; + } + case '\"': + case '\'': { + char q = c; + do { + if ((c = this.next()) != '\u0000') continue; + throw this.syntaxError("Unterminated string"); + } while (c != q); + return Boolean.TRUE; + } + } + while (!Character.isWhitespace(c = this.next())) { + switch (c) { + case '\u0000': { + throw this.syntaxError("Unterminated string"); + } + case '!': + case '\"': + case '\'': + case '/': + case '<': + case '=': + case '>': + case '?': { + this.back(); + return Boolean.TRUE; + } + } + } + return Boolean.TRUE; + } + + public Object nextToken() throws JSONException { + char c; + while (Character.isWhitespace(c = this.next())) { + } + switch (c) { + case '\u0000': { + throw this.syntaxError("Misshaped element"); + } + case '<': { + throw this.syntaxError("Misplaced '<'"); + } + case '>': { + return XML.GT; + } + case '/': { + return XML.SLASH; + } + case '=': { + return XML.EQ; + } + case '!': { + return XML.BANG; + } + case '?': { + return XML.QUEST; + } + case '\"': + case '\'': { + char q = c; + StringBuilder sb = new StringBuilder(); + while (true) { + if ((c = this.next()) == '\u0000') { + throw this.syntaxError("Unterminated string"); + } + if (c == q) { + return sb.toString(); + } + if (c == '&') { + sb.append(this.nextEntity(c)); + continue; + } + sb.append(c); + } + } + } + StringBuilder sb = new StringBuilder(); + while (true) { + sb.append(c); + c = this.next(); + if (Character.isWhitespace(c)) { + return sb.toString(); + } + switch (c) { + case '\u0000': { + return sb.toString(); + } + case '!': + case '/': + case '=': + case '>': + case '?': + case '[': + case ']': { + this.back(); + return sb.toString(); + } + case '\"': + case '\'': + case '<': { + throw this.syntaxError("Bad character in a name"); + } + } + } + } + + public void skipPast(String to) { + char c; + int i; + int offset = 0; + int length = to.length(); + char[] circle = new char[length]; + for (i = 0; i < length; ++i) { + c = this.next(); + if (c == '\u0000') { + return; + } + circle[i] = c; + } + while (true) { + int j = offset; + boolean b = true; + for (i = 0; i < length; ++i) { + if (circle[j] != to.charAt(i)) { + b = false; + break; + } + if (++j < length) continue; + j -= length; + } + if (b) { + return; + } + c = this.next(); + if (c == '\u0000') { + return; + } + circle[offset] = c; + if (++offset < length) continue; + offset -= length; + } + } + + static { + entity.put("amp", XML.AMP); + entity.put("apos", XML.APOS); + entity.put("gt", XML.GT); + entity.put("lt", XML.LT); + entity.put("quot", XML.QUOT); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/org/json/XMLXsiTypeConverter.java b/source/decompiled/de/maxhenkel/voicechat/org/json/XMLXsiTypeConverter.java new file mode 100644 index 0000000..c6f2dcb --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/org/json/XMLXsiTypeConverter.java @@ -0,0 +1,9 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.org.json; + +public interface XMLXsiTypeConverter { + public T convert(String var1); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/permission/PermissionManager.java b/source/decompiled/de/maxhenkel/voicechat/permission/PermissionManager.java new file mode 100644 index 0000000..e12de96 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/permission/PermissionManager.java @@ -0,0 +1,19 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.permissions.Permission + * org.bukkit.permissions.PermissionDefault + */ +package de.maxhenkel.voicechat.permission; + +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; + +public class PermissionManager { + public static Permission LISTEN_PERMISSION = new Permission("voicechat.listen", PermissionDefault.TRUE); + public static Permission SPEAK_PERMISSION = new Permission("voicechat.speak", PermissionDefault.TRUE); + public static Permission GROUPS_PERMISSION = new Permission("voicechat.groups", PermissionDefault.TRUE); + public static Permission ADMIN_PERMISSION = new Permission("voicechat.admin", PermissionDefault.OP); +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/CategoryManager.java b/source/decompiled/de/maxhenkel/voicechat/plugins/CategoryManager.java new file mode 100644 index 0000000..11a1939 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/CategoryManager.java @@ -0,0 +1,31 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins; + +import de.maxhenkel.voicechat.plugins.impl.VolumeCategoryImpl; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nullable; + +public class CategoryManager { + protected final Map categories = new ConcurrentHashMap(); + + public void addCategory(VolumeCategoryImpl category) { + this.categories.put(category.getId(), category); + } + + @Nullable + public VolumeCategoryImpl removeCategory(String categoryId) { + return this.categories.remove(categoryId); + } + + public Collection getCategories() { + return this.categories.values(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/EventBuilder.java b/source/decompiled/de/maxhenkel/voicechat/plugins/EventBuilder.java new file mode 100644 index 0000000..bfaf2a4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/EventBuilder.java @@ -0,0 +1,57 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins; + +import de.maxhenkel.voicechat.api.events.Event; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +public class EventBuilder { + private final Map, List>>> events = new HashMap, List>>>(); + + private EventBuilder() { + } + + public EventBuilder addEvent(Class eventClass, Consumer event, int priority) { + List eventList = this.events.getOrDefault(eventClass, new ArrayList()); + eventList.add(new Tuple>(priority, event)); + this.events.put(eventClass, eventList); + return this; + } + + public Map, List>> build() { + HashMap, List>> result = new HashMap, List>>(); + for (Map.Entry, List>>> entry : this.events.entrySet()) { + result.put(entry.getKey(), entry.getValue().stream().sorted((o1, o2) -> Integer.compare((Integer)o2.getA(), (Integer)o1.getA())).map(Tuple::getB).collect(Collectors.toList())); + } + return result; + } + + public static EventBuilder create() { + return new EventBuilder(); + } + + private static class Tuple { + private final A a; + private final B b; + + public Tuple(A a, B b) { + this.a = a; + this.b = b; + } + + public A getA() { + return this.a; + } + + public B getB() { + return this.b; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/PluginManager.java b/source/decompiled/de/maxhenkel/voicechat/plugins/PluginManager.java new file mode 100644 index 0000000..a3fbed8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/PluginManager.java @@ -0,0 +1,308 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.VoicechatPlugin; +import de.maxhenkel.voicechat.api.VoicechatSocket; +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.audiolistener.AudioListener; +import de.maxhenkel.voicechat.api.audiolistener.PlayerAudioListener; +import de.maxhenkel.voicechat.api.events.CreateGroupEvent; +import de.maxhenkel.voicechat.api.events.EntitySoundPacketEvent; +import de.maxhenkel.voicechat.api.events.Event; +import de.maxhenkel.voicechat.api.events.EventRegistration; +import de.maxhenkel.voicechat.api.events.JoinGroupEvent; +import de.maxhenkel.voicechat.api.events.LeaveGroupEvent; +import de.maxhenkel.voicechat.api.events.LocationalSoundPacketEvent; +import de.maxhenkel.voicechat.api.events.MicrophonePacketEvent; +import de.maxhenkel.voicechat.api.events.PlayerConnectedEvent; +import de.maxhenkel.voicechat.api.events.PlayerDisconnectedEvent; +import de.maxhenkel.voicechat.api.events.PlayerStateChangedEvent; +import de.maxhenkel.voicechat.api.events.RegisterVolumeCategoryEvent; +import de.maxhenkel.voicechat.api.events.RemoveGroupEvent; +import de.maxhenkel.voicechat.api.events.StaticSoundPacketEvent; +import de.maxhenkel.voicechat.api.events.UnregisterVolumeCategoryEvent; +import de.maxhenkel.voicechat.api.events.VoiceDistanceEvent; +import de.maxhenkel.voicechat.api.events.VoiceHostEvent; +import de.maxhenkel.voicechat.api.events.VoicechatServerStartedEvent; +import de.maxhenkel.voicechat.api.events.VoicechatServerStartingEvent; +import de.maxhenkel.voicechat.api.events.VoicechatServerStoppedEvent; +import de.maxhenkel.voicechat.plugins.EventBuilder; +import de.maxhenkel.voicechat.plugins.impl.GroupImpl; +import de.maxhenkel.voicechat.plugins.impl.VoicechatConnectionImpl; +import de.maxhenkel.voicechat.plugins.impl.VoicechatServerApiImpl; +import de.maxhenkel.voicechat.plugins.impl.VoicechatSocketImpl; +import de.maxhenkel.voicechat.plugins.impl.audiolistener.PlayerAudioListenerImpl; +import de.maxhenkel.voicechat.plugins.impl.events.CreateGroupEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.EntitySoundPacketEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.JoinGroupEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.LeaveGroupEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.LocationalSoundPacketEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.MicrophonePacketEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.PlayerConnectedEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.PlayerDisconnectedEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.PlayerStateChangedEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.RegisterVolumeCategoryEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.RemoveGroupEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.StaticSoundPacketEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.UnregisterVolumeCategoryEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.VoiceDistanceEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.VoiceHostEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.VoicechatServerStartedEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.VoicechatServerStartingEventImpl; +import de.maxhenkel.voicechat.plugins.impl.events.VoicechatServerStoppedEventImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.EntitySoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.LocationalSoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.MicrophonePacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.SoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.StaticSoundPacketImpl; +import de.maxhenkel.voicechat.voice.common.GroupSoundPacket; +import de.maxhenkel.voicechat.voice.common.LocationSoundPacket; +import de.maxhenkel.voicechat.voice.common.MicPacket; +import de.maxhenkel.voicechat.voice.common.PlayerSoundPacket; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import de.maxhenkel.voicechat.voice.common.SoundPacket; +import de.maxhenkel.voicechat.voice.server.Group; +import de.maxhenkel.voicechat.voice.server.Server; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public class PluginManager { + private List plugins; + private Map, List>> events; + private final Map> playerAudioListeners = new ConcurrentHashMap>(); + private static PluginManager instance; + + public void init() { + Voicechat.LOGGER.info("Loading plugins", new Object[0]); + this.plugins = Voicechat.apiService.getPlugins(); + Voicechat.LOGGER.info("Loaded {} plugin(s)", this.plugins.size()); + Voicechat.LOGGER.info("Initializing plugins", new Object[0]); + for (VoicechatPlugin plugin : this.plugins) { + try { + plugin.initialize(VoicechatServerApiImpl.instance()); + } + catch (Exception e) { + Voicechat.LOGGER.error("Failed to initialize plugin '{}'", plugin.getPluginId(), e); + } + } + Voicechat.LOGGER.info("Initialized {} plugin(s)", this.plugins.size()); + this.gatherEvents(); + } + + private void gatherEvents() { + EventBuilder eventBuilder = EventBuilder.create(); + EventRegistration registration = eventBuilder::addEvent; + for (VoicechatPlugin plugin : this.plugins) { + Voicechat.LOGGER.info("Registering events for '{}'", plugin.getPluginId()); + try { + plugin.registerEvents(registration); + } + catch (Throwable t) { + Voicechat.LOGGER.error("Failed to register events for '{}'", plugin.getPluginId(), t); + } + } + this.events = eventBuilder.build(); + } + + public synchronized boolean registerAudioListener(AudioListener l) { + if (!(l instanceof PlayerAudioListener)) { + return false; + } + PlayerAudioListener listener = (PlayerAudioListener)l; + boolean exists = this.playerAudioListeners.values().stream().anyMatch(listeners -> listeners.stream().anyMatch(playerAudioListener -> playerAudioListener.getListenerId().equals(listener.getListenerId()))); + if (exists) { + return false; + } + this.playerAudioListeners.computeIfAbsent(listener.getPlayerUuid(), k -> new CopyOnWriteArrayList()).add(listener); + return true; + } + + public synchronized boolean unregisterAudioListener(UUID listenerId) { + boolean removed = this.playerAudioListeners.values().stream().anyMatch(listeners -> listeners.removeIf(listener -> listener.getListenerId().equals(listenerId))); + if (!removed) { + return false; + } + this.playerAudioListeners.values().removeIf(List::isEmpty); + return true; + } + + public List getPlayerAudioListeners(UUID playerUuid) { + return this.playerAudioListeners.getOrDefault(playerUuid, Collections.emptyList()); + } + + public void onListenerAudio(UUID playerUuid, SoundPacket packet) { + if (playerUuid.equals(packet.getSender())) { + return; + } + List listeners = this.getPlayerAudioListeners(playerUuid); + if (listeners.isEmpty()) { + return; + } + SoundPacketImpl soundPacket = packet instanceof GroupSoundPacket ? new StaticSoundPacketImpl((GroupSoundPacket)packet) : (packet instanceof PlayerSoundPacket ? new EntitySoundPacketImpl((PlayerSoundPacket)packet) : (packet instanceof LocationSoundPacket ? new LocationalSoundPacketImpl((LocationSoundPacket)packet) : new SoundPacketImpl(packet))); + for (PlayerAudioListener l : listeners) { + if (!(l instanceof PlayerAudioListenerImpl)) continue; + try { + ((PlayerAudioListenerImpl)l).getListener().accept(soundPacket); + } + catch (Throwable t) { + Voicechat.LOGGER.error("Failed to process audio listener", t); + } + } + } + + public boolean dispatchEvent(Class eventClass, T event) { + List> events = this.events.get(eventClass); + if (events == null) { + return false; + } + for (Consumer evt : events) { + try { + Consumer e = evt; + e.accept(event); + if (!event.isCancelled()) continue; + break; + } + catch (Throwable t) { + Voicechat.LOGGER.error("Failed to dispatch event '{}'", eventClass.getSimpleName(), t); + } + } + return event.isCancelled(); + } + + public VoicechatSocket getSocketImplementation() { + VoicechatServerStartingEventImpl event = new VoicechatServerStartingEventImpl(); + this.dispatchEvent(VoicechatServerStartingEvent.class, event); + VoicechatSocket socket = event.getSocketImplementation(); + if (socket == null) { + socket = new VoicechatSocketImpl(); + Voicechat.LOGGER.debug("Using default voicechat socket implementation", new Object[0]); + } else { + Voicechat.LOGGER.info("Using custom voicechat socket implementation: {}", socket.getClass().getName()); + } + return socket; + } + + public String getVoiceHost(Player player, String voiceHost) { + VoiceHostEventImpl event = new VoiceHostEventImpl(player, voiceHost); + this.dispatchEvent(VoiceHostEvent.class, event); + return event.getVoiceHost(); + } + + public void onRegisterVolumeCategory(VolumeCategory category) { + this.dispatchEvent(RegisterVolumeCategoryEvent.class, new RegisterVolumeCategoryEventImpl(category)); + } + + public void onUnregisterVolumeCategory(VolumeCategory category) { + this.dispatchEvent(UnregisterVolumeCategoryEvent.class, new UnregisterVolumeCategoryEventImpl(category)); + } + + public void onServerStarted() { + this.dispatchEvent(VoicechatServerStartedEvent.class, new VoicechatServerStartedEventImpl()); + } + + public void onServerStopped() { + this.dispatchEvent(VoicechatServerStoppedEvent.class, new VoicechatServerStoppedEventImpl()); + } + + public void onPlayerConnected(@Nullable Player player) { + if (player == null) { + return; + } + this.dispatchEvent(PlayerConnectedEvent.class, new PlayerConnectedEventImpl(VoicechatConnectionImpl.fromPlayer(player))); + } + + public void onPlayerDisconnected(UUID player) { + this.dispatchEvent(PlayerDisconnectedEvent.class, new PlayerDisconnectedEventImpl(player)); + } + + public void onPlayerStateChanged(PlayerState state) { + this.dispatchEvent(PlayerStateChangedEvent.class, new PlayerStateChangedEventImpl(state)); + } + + public boolean onJoinGroup(Player player, @Nullable Group group) { + if (group == null) { + return this.onLeaveGroup(player); + } + return this.dispatchEvent(JoinGroupEvent.class, new JoinGroupEventImpl(new GroupImpl(group), VoicechatConnectionImpl.fromPlayer(player))); + } + + public boolean onCreateGroup(@Nullable Player player, @Nullable Group group) { + if (group == null) { + if (player == null) { + return false; + } + return this.onLeaveGroup(player); + } + return this.dispatchEvent(CreateGroupEvent.class, new CreateGroupEventImpl(new GroupImpl(group), VoicechatConnectionImpl.fromPlayer(player))); + } + + public boolean onLeaveGroup(Player player) { + Group g; + UUID groupUUID; + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return false; + } + GroupImpl group = null; + PlayerState state = server.getPlayerStateManager().getState(player.getUniqueId()); + if (state != null && (groupUUID = state.getGroup()) != null && (g = server.getGroupManager().getGroup(groupUUID)) != null) { + group = new GroupImpl(g); + } + return this.dispatchEvent(LeaveGroupEvent.class, new LeaveGroupEventImpl(group, VoicechatConnectionImpl.fromPlayer(player))); + } + + public boolean onRemoveGroup(Group group) { + return this.dispatchEvent(RemoveGroupEvent.class, new RemoveGroupEventImpl(new GroupImpl(group))); + } + + public boolean onMicPacket(Player sender, PlayerState senderState, MicPacket packet) { + return this.dispatchEvent(MicrophonePacketEvent.class, new MicrophonePacketEventImpl(new MicrophonePacketImpl(packet, sender.getUniqueId()), new VoicechatConnectionImpl(sender, senderState))); + } + + public float getDistance(Player sender, PlayerState senderState, MicPacket packet, float originalDistance) { + VoiceDistanceEventImpl event = new VoiceDistanceEventImpl(new MicrophonePacketImpl(packet, sender.getUniqueId()), new VoicechatConnectionImpl(sender, senderState), originalDistance); + this.dispatchEvent(VoiceDistanceEvent.class, event); + return event.getDistance(); + } + + public boolean onSoundPacket(@Nullable Player sender, @Nullable PlayerState senderState, Player receiver, PlayerState receiverState, SoundPacket p, String source) { + VoicechatConnectionImpl senderConnection = null; + if (sender != null && senderState != null) { + senderConnection = new VoicechatConnectionImpl(sender, senderState); + } + VoicechatConnectionImpl receiverConnection = new VoicechatConnectionImpl(receiver, receiverState); + if (p instanceof LocationSoundPacket) { + return this.dispatchEvent(LocationalSoundPacketEvent.class, new LocationalSoundPacketEventImpl(new LocationalSoundPacketImpl((LocationSoundPacket)p), (VoicechatConnection)senderConnection, (VoicechatConnection)receiverConnection, source)); + } + if (p instanceof PlayerSoundPacket) { + return this.dispatchEvent(EntitySoundPacketEvent.class, new EntitySoundPacketEventImpl(new EntitySoundPacketImpl((PlayerSoundPacket)p), (VoicechatConnection)senderConnection, (VoicechatConnection)receiverConnection, source)); + } + if (p instanceof GroupSoundPacket) { + return this.dispatchEvent(StaticSoundPacketEvent.class, new StaticSoundPacketEventImpl(new StaticSoundPacketImpl((GroupSoundPacket)p), (VoicechatConnection)senderConnection, (VoicechatConnection)receiverConnection, source)); + } + return false; + } + + public static synchronized PluginManager instance() { + if (instance == null) { + instance = new PluginManager(); + } + return instance; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/BukkitVoicechatServiceImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/BukkitVoicechatServiceImpl.java new file mode 100644 index 0000000..69e22ba --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/BukkitVoicechatServiceImpl.java @@ -0,0 +1,24 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.api.BukkitVoicechatService; +import de.maxhenkel.voicechat.api.VoicechatPlugin; +import java.util.ArrayList; +import java.util.List; + +public class BukkitVoicechatServiceImpl +implements BukkitVoicechatService { + private final List plugins = new ArrayList(); + + @Override + public void registerPlugin(VoicechatPlugin plugin) { + this.plugins.add(plugin); + } + + public List getPlugins() { + return this.plugins; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/EntityImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/EntityImpl.java new file mode 100644 index 0000000..371815c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/EntityImpl.java @@ -0,0 +1,57 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Entity + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.api.Entity; +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.plugins.impl.PositionImpl; +import java.util.Objects; +import java.util.UUID; + +public class EntityImpl +implements Entity { + protected org.bukkit.entity.Entity entity; + + public EntityImpl(org.bukkit.entity.Entity entity) { + this.entity = entity; + } + + @Override + public UUID getUuid() { + return this.entity.getUniqueId(); + } + + @Override + public Object getEntity() { + return this.entity; + } + + @Override + public Position getPosition() { + return new PositionImpl(this.entity.getLocation()); + } + + public org.bukkit.entity.Entity getRealEntity() { + return this.entity; + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || this.getClass() != object.getClass()) { + return false; + } + EntityImpl entity1 = (EntityImpl)object; + return Objects.equals(this.entity, entity1.entity); + } + + public int hashCode() { + return this.entity != null ? this.entity.hashCode() : 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/GroupImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/GroupImpl.java new file mode 100644 index 0000000..27f02da --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/GroupImpl.java @@ -0,0 +1,184 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import de.maxhenkel.voicechat.voice.server.Group; +import de.maxhenkel.voicechat.voice.server.Server; +import java.util.Objects; +import java.util.UUID; +import javax.annotation.Nullable; + +public class GroupImpl +implements de.maxhenkel.voicechat.api.Group { + private final Group group; + + public GroupImpl(Group group) { + this.group = group; + } + + @Override + public String getName() { + return this.group.getName(); + } + + @Override + public boolean hasPassword() { + return this.group.getPassword() != null; + } + + @Override + public UUID getId() { + return this.group.getId(); + } + + @Override + public boolean isPersistent() { + return this.group.isPersistent(); + } + + @Override + public boolean isHidden() { + return this.group.isHidden(); + } + + @Override + public Group.Type getType() { + return this.group.getType(); + } + + public Group getGroup() { + return this.group; + } + + @Nullable + public static GroupImpl create(PlayerState state) { + Group g; + UUID groupId = state.getGroup(); + Server server = Voicechat.SERVER.getServer(); + if (server != null && groupId != null && (g = server.getGroupManager().getGroup(groupId)) != null) { + return new GroupImpl(g); + } + return null; + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || this.getClass() != object.getClass()) { + return false; + } + GroupImpl group1 = (GroupImpl)object; + return Objects.equals(this.group.getId(), group1.group.getId()); + } + + public int hashCode() { + return this.group != null ? this.group.getId().hashCode() : 0; + } + + public static class TypeImpl + implements Group.Type { + public static short toInt(Group.Type type) { + if (type == OPEN) { + return 1; + } + if (type == ISOLATED) { + return 2; + } + return 0; + } + + public static Group.Type fromInt(short i) { + if (i == 1) { + return OPEN; + } + if (i == 2) { + return ISOLATED; + } + return NORMAL; + } + } + + public static class BuilderImpl + implements Group.Builder { + @Nullable + private UUID id; + private String name; + @Nullable + private String password; + private boolean persistent; + private boolean hidden; + private Group.Type type = Group.Type.NORMAL; + + @Override + public Group.Builder setId(@Nullable UUID id) { + this.id = id; + return this; + } + + @Override + public Group.Builder setName(String name) { + this.name = name; + this.name = BuilderImpl.convertGroupName(name); + return this; + } + + private static String convertGroupName(String name) { + if ((name = name.replaceAll("[\\n\\r\\t]", "")).matches("^\\s.*")) { + name = name.replaceFirst("^\\s+", ""); + } + if (name.length() > 16) { + return name.substring(0, 16); + } + return name; + } + + @Override + public Group.Builder setPassword(String password) { + this.password = password; + return this; + } + + @Override + public Group.Builder setPersistent(boolean persistent) { + this.persistent = persistent; + return this; + } + + @Override + public Group.Builder setHidden(boolean hidden) { + this.hidden = hidden; + return this; + } + + @Override + public Group.Builder setType(Group.Type type) { + this.type = type; + return this; + } + + @Override + public de.maxhenkel.voicechat.api.Group build() { + if (this.name == null) { + throw new IllegalStateException("Group is missing a name"); + } + if (!Voicechat.GROUP_REGEX.matcher(this.name).matches()) { + throw new IllegalStateException(String.format("Invalid group name: %s", this.name)); + } + GroupImpl group = new GroupImpl(new Group(this.id == null ? UUID.randomUUID() : this.id, this.name, this.password, this.persistent, this.hidden, this.type)); + Server server = Voicechat.SERVER.getServer(); + if (server != null && this.persistent) { + server.getGroupManager().addGroup(group.getGroup(), null); + } + return group; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/PlayerImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/PlayerImpl.java new file mode 100644 index 0000000..82b027f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/PlayerImpl.java @@ -0,0 +1,30 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Entity + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.plugins.impl.EntityImpl; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class PlayerImpl +extends EntityImpl +implements de.maxhenkel.voicechat.api.Player { + public PlayerImpl(Player entity) { + super((Entity)entity); + } + + @Override + public Object getPlayer() { + return this.entity; + } + + public Player getRealPlayer() { + return (Player)this.entity; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/PositionImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/PositionImpl.java new file mode 100644 index 0000000..2dc6695 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/PositionImpl.java @@ -0,0 +1,59 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.Location + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.api.Position; +import java.util.Objects; +import org.bukkit.Location; + +public class PositionImpl +implements Position { + private final Location position; + + public PositionImpl(Location position) { + this.position = position; + } + + public PositionImpl(double x, double y, double z) { + this.position = new Location(null, x, y, z); + } + + @Override + public double getX() { + return this.position.getX(); + } + + @Override + public double getY() { + return this.position.getY(); + } + + @Override + public double getZ() { + return this.position.getZ(); + } + + public Location getPosition() { + return this.position; + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || this.getClass() != object.getClass()) { + return false; + } + PositionImpl position1 = (PositionImpl)object; + return Objects.equals(this.position, position1.position); + } + + public int hashCode() { + return this.position != null ? this.position.hashCode() : 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/RawUdpPacketImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/RawUdpPacketImpl.java new file mode 100644 index 0000000..7454bfe --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/RawUdpPacketImpl.java @@ -0,0 +1,37 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.api.RawUdpPacket; +import java.net.SocketAddress; + +public class RawUdpPacketImpl +implements RawUdpPacket { + private static final byte[] BUFFER = new byte[4096]; + private final byte[] data; + private final SocketAddress socketAddress; + private final long timestamp; + + public RawUdpPacketImpl(byte[] data, SocketAddress socketAddress, long timestamp) { + this.data = data; + this.socketAddress = socketAddress; + this.timestamp = timestamp; + } + + @Override + public byte[] getData() { + return this.data; + } + + @Override + public long getTimestamp() { + return this.timestamp; + } + + @Override + public SocketAddress getSocketAddress() { + return this.socketAddress; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/ServerLevelImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/ServerLevelImpl.java new file mode 100644 index 0000000..8300659 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/ServerLevelImpl.java @@ -0,0 +1,41 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.World + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.api.ServerLevel; +import java.util.Objects; +import org.bukkit.World; + +public class ServerLevelImpl +implements ServerLevel { + private final World serverLevel; + + public ServerLevelImpl(World serverLevel) { + this.serverLevel = serverLevel; + } + + @Override + public Object getServerLevel() { + return this.serverLevel; + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || this.getClass() != object.getClass()) { + return false; + } + ServerLevelImpl that = (ServerLevelImpl)object; + return Objects.equals(this.serverLevel, that.serverLevel); + } + + public int hashCode() { + return this.serverLevel != null ? this.serverLevel.hashCode() : 0; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/ServerPlayerImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/ServerPlayerImpl.java new file mode 100644 index 0000000..e9e72ff --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/ServerPlayerImpl.java @@ -0,0 +1,31 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.api.ServerLevel; +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.plugins.impl.PlayerImpl; +import de.maxhenkel.voicechat.plugins.impl.ServerLevelImpl; +import org.bukkit.entity.Player; + +public class ServerPlayerImpl +extends PlayerImpl +implements ServerPlayer { + public ServerPlayerImpl(Player entity) { + super(entity); + } + + public Player getRealServerPlayer() { + return (Player)this.entity; + } + + @Override + public ServerLevel getServerLevel() { + return new ServerLevelImpl(this.entity.getWorld()); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatApiImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatApiImpl.java new file mode 100644 index 0000000..392892e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatApiImpl.java @@ -0,0 +1,114 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.World + * org.bukkit.entity.Entity + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.api.Entity; +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.ServerLevel; +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.VoicechatApi; +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.audio.AudioConverter; +import de.maxhenkel.voicechat.api.mp3.Mp3Decoder; +import de.maxhenkel.voicechat.api.mp3.Mp3Encoder; +import de.maxhenkel.voicechat.api.opus.OpusDecoder; +import de.maxhenkel.voicechat.api.opus.OpusEncoder; +import de.maxhenkel.voicechat.api.opus.OpusEncoderMode; +import de.maxhenkel.voicechat.plugins.impl.EntityImpl; +import de.maxhenkel.voicechat.plugins.impl.PositionImpl; +import de.maxhenkel.voicechat.plugins.impl.ServerLevelImpl; +import de.maxhenkel.voicechat.plugins.impl.ServerPlayerImpl; +import de.maxhenkel.voicechat.plugins.impl.VolumeCategoryImpl; +import de.maxhenkel.voicechat.plugins.impl.audio.AudioConverterImpl; +import de.maxhenkel.voicechat.plugins.impl.opus.OpusManager; +import de.maxhenkel.voicechat.voice.common.Utils; +import java.io.InputStream; +import java.io.OutputStream; +import javax.annotation.Nullable; +import javax.sound.sampled.AudioFormat; +import org.bukkit.World; +import org.bukkit.entity.Player; + +public abstract class VoicechatApiImpl +implements VoicechatApi { + private static final AudioConverter AUDIO_CONVERTER = new AudioConverterImpl(); + + @Override + public OpusEncoder createEncoder() { + return OpusManager.createEncoder(null); + } + + @Override + public OpusEncoder createEncoder(OpusEncoderMode mode) { + return OpusManager.createEncoder(mode); + } + + @Override + @Nullable + public Mp3Encoder createMp3Encoder(AudioFormat audioFormat, int bitrate, int quality, OutputStream outputStream) { + return null; + } + + @Override + @Nullable + public Mp3Decoder createMp3Decoder(InputStream inputStream) { + return null; + } + + @Override + public OpusDecoder createDecoder() { + return OpusManager.createDecoder(); + } + + @Override + public AudioConverter getAudioConverter() { + return AUDIO_CONVERTER; + } + + @Override + public Entity fromEntity(Object entity) { + if (entity instanceof org.bukkit.entity.Entity) { + return new EntityImpl((org.bukkit.entity.Entity)entity); + } + throw new IllegalArgumentException("entity is not an instance of Entity"); + } + + @Override + public ServerLevel fromServerLevel(Object serverLevel) { + if (serverLevel instanceof World) { + return new ServerLevelImpl((World)serverLevel); + } + throw new IllegalArgumentException("serverLevel is not an instance of World"); + } + + @Override + public ServerPlayer fromServerPlayer(Object serverPlayer) { + if (serverPlayer instanceof Player) { + return new ServerPlayerImpl((Player)serverPlayer); + } + throw new IllegalArgumentException("serverPlayer is not an instance of Player"); + } + + @Override + public Position createPosition(double x, double y, double z) { + return new PositionImpl(x, y, z); + } + + @Override + public VolumeCategory.Builder volumeCategoryBuilder() { + return new VolumeCategoryImpl.BuilderImpl(); + } + + @Override + public double getVoiceChatDistance() { + return Utils.getDefaultDistance(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatConnectionImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatConnectionImpl.java new file mode 100644 index 0000000..a564bcd --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatConnectionImpl.java @@ -0,0 +1,147 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.plugins.impl.GroupImpl; +import de.maxhenkel.voicechat.plugins.impl.ServerPlayerImpl; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import de.maxhenkel.voicechat.voice.server.Group; +import de.maxhenkel.voicechat.voice.server.PlayerStateManager; +import de.maxhenkel.voicechat.voice.server.Server; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public class VoicechatConnectionImpl +implements VoicechatConnection { + private final ServerPlayer player; + private final Player serverPlayer; + private final PlayerState state; + @Nullable + private final de.maxhenkel.voicechat.api.Group group; + + public VoicechatConnectionImpl(Player player, PlayerState state) { + this.serverPlayer = player; + this.player = new ServerPlayerImpl(player); + this.state = state; + this.group = GroupImpl.create(state); + } + + @Nullable + public static VoicechatConnectionImpl fromPlayer(@Nullable Player player) { + if (player == null) { + return null; + } + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return null; + } + PlayerState state = server.getPlayerStateManager().getState(player.getUniqueId()); + if (state == null) { + return null; + } + return new VoicechatConnectionImpl(player, state); + } + + @Override + @Nullable + public de.maxhenkel.voicechat.api.Group getGroup() { + return this.group; + } + + @Override + public boolean isInGroup() { + return this.group != null; + } + + @Override + public void setGroup(@Nullable de.maxhenkel.voicechat.api.Group group) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return; + } + if (group == null) { + server.getGroupManager().leaveGroup((Player)this.player.getPlayer()); + return; + } + if (group instanceof GroupImpl) { + GroupImpl g = (GroupImpl)group; + Group actualGroup = server.getGroupManager().getGroup(g.getGroup().getId()); + if (actualGroup == null) { + server.getGroupManager().addGroup(g.getGroup(), this.serverPlayer); + actualGroup = g.getGroup(); + } + server.getGroupManager().joinGroup(actualGroup, this.serverPlayer, g.getGroup().getPassword()); + } + } + + @Override + public boolean isConnected() { + return !this.state.isDisconnected(); + } + + @Override + public void setConnected(boolean connected) { + if (this.isInstalled()) { + return; + } + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return; + } + PlayerStateManager manager = server.getPlayerStateManager(); + PlayerState actualState = manager.getState(this.state.getUuid()); + if (actualState == null) { + return; + } + if (actualState.isDisconnected() != connected) { + return; + } + actualState.setDisconnected(!connected); + manager.broadcastState(null, actualState); + } + + @Override + public boolean isDisabled() { + return this.state.isDisabled(); + } + + @Override + public void setDisabled(boolean disabled) { + if (this.isInstalled()) { + return; + } + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return; + } + PlayerStateManager manager = server.getPlayerStateManager(); + PlayerState actualState = manager.getState(this.state.getUuid()); + if (actualState == null) { + return; + } + if (actualState.isDisabled() == disabled) { + return; + } + actualState.setDisabled(disabled); + manager.broadcastState(null, actualState); + } + + @Override + public boolean isInstalled() { + return Voicechat.SERVER.isCompatible(this.serverPlayer); + } + + @Override + public ServerPlayer getPlayer() { + return this.player; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatServerApiImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatServerApiImpl.java new file mode 100644 index 0000000..871551c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatServerApiImpl.java @@ -0,0 +1,336 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.Bukkit + * org.bukkit.World + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.Entity; +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.ServerLevel; +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.VoicechatServerApi; +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.audiochannel.AudioChannel; +import de.maxhenkel.voicechat.api.audiochannel.AudioPlayer; +import de.maxhenkel.voicechat.api.audiochannel.EntityAudioChannel; +import de.maxhenkel.voicechat.api.audiochannel.LocationalAudioChannel; +import de.maxhenkel.voicechat.api.audiochannel.StaticAudioChannel; +import de.maxhenkel.voicechat.api.audiolistener.AudioListener; +import de.maxhenkel.voicechat.api.audiolistener.PlayerAudioListener; +import de.maxhenkel.voicechat.api.audiosender.AudioSender; +import de.maxhenkel.voicechat.api.config.ConfigAccessor; +import de.maxhenkel.voicechat.api.opus.OpusEncoder; +import de.maxhenkel.voicechat.api.packets.ConvertablePacket; +import de.maxhenkel.voicechat.api.packets.EntitySoundPacket; +import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket; +import de.maxhenkel.voicechat.api.packets.StaticSoundPacket; +import de.maxhenkel.voicechat.plugins.PluginManager; +import de.maxhenkel.voicechat.plugins.impl.GroupImpl; +import de.maxhenkel.voicechat.plugins.impl.PositionImpl; +import de.maxhenkel.voicechat.plugins.impl.ServerPlayerImpl; +import de.maxhenkel.voicechat.plugins.impl.VoicechatApiImpl; +import de.maxhenkel.voicechat.plugins.impl.VoicechatConnectionImpl; +import de.maxhenkel.voicechat.plugins.impl.VolumeCategoryImpl; +import de.maxhenkel.voicechat.plugins.impl.audiochannel.AudioPlayerImpl; +import de.maxhenkel.voicechat.plugins.impl.audiochannel.AudioSupplier; +import de.maxhenkel.voicechat.plugins.impl.audiochannel.EntityAudioChannelImpl; +import de.maxhenkel.voicechat.plugins.impl.audiochannel.LocationalAudioChannelImpl; +import de.maxhenkel.voicechat.plugins.impl.audiochannel.StaticAudioChannelImpl; +import de.maxhenkel.voicechat.plugins.impl.audiolistener.PlayerAudioListenerImpl; +import de.maxhenkel.voicechat.plugins.impl.audiosender.AudioSenderImpl; +import de.maxhenkel.voicechat.plugins.impl.config.ConfigAccessorImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.ConvertablePacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.EntitySoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.LocationalSoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.StaticSoundPacketImpl; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import de.maxhenkel.voicechat.voice.common.SoundPacket; +import de.maxhenkel.voicechat.voice.server.ClientConnection; +import de.maxhenkel.voicechat.voice.server.Group; +import de.maxhenkel.voicechat.voice.server.Server; +import de.maxhenkel.voicechat.voice.server.ServerPlayerManager; +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.entity.Player; + +public class VoicechatServerApiImpl +extends VoicechatApiImpl +implements VoicechatServerApi { + private static final VoicechatServerApiImpl INSTANCE = new VoicechatServerApiImpl(); + + private VoicechatServerApiImpl() { + } + + public static VoicechatServerApiImpl instance() { + return INSTANCE; + } + + @Override + public void sendEntitySoundPacketTo(VoicechatConnection connection, EntitySoundPacket p) { + if (p instanceof EntitySoundPacketImpl) { + VoicechatServerApiImpl.sendPacket(connection, ((EntitySoundPacketImpl)p).getPacket()); + } + } + + @Override + public void sendLocationalSoundPacketTo(VoicechatConnection connection, LocationalSoundPacket p) { + if (p instanceof LocationalSoundPacketImpl) { + VoicechatServerApiImpl.sendPacket(connection, ((LocationalSoundPacketImpl)p).getPacket()); + } + } + + @Override + public void sendStaticSoundPacketTo(VoicechatConnection connection, StaticSoundPacket p) { + if (p instanceof StaticSoundPacketImpl) { + VoicechatServerApiImpl.sendPacket(connection, ((StaticSoundPacketImpl)p).getPacket()); + } + } + + @Override + public ConvertablePacket createPacket() { + return new ConvertablePacketImpl(); + } + + @Override + @Nullable + public EntityAudioChannel createEntityAudioChannel(UUID channelId, Entity entity) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return null; + } + return new EntityAudioChannelImpl(channelId, server, entity); + } + + @Override + @Nullable + public LocationalAudioChannel createLocationalAudioChannel(UUID channelId, ServerLevel level, Position initialPosition) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return null; + } + if (initialPosition instanceof PositionImpl) { + return new LocationalAudioChannelImpl(channelId, server, level, (PositionImpl)initialPosition); + } + throw new IllegalArgumentException("initialPosition is not an instance of PositionImpl"); + } + + @Override + @Nullable + public StaticAudioChannel createStaticAudioChannel(UUID channelId, ServerLevel level, VoicechatConnection connection) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return null; + } + StaticAudioChannelImpl staticAudioChannel = new StaticAudioChannelImpl(channelId, server); + staticAudioChannel.addTarget(connection); + return staticAudioChannel; + } + + @Override + @Nullable + public StaticAudioChannel createStaticAudioChannel(UUID channelId) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return null; + } + return new StaticAudioChannelImpl(channelId, server); + } + + @Override + public AudioPlayer createAudioPlayer(AudioChannel audioChannel, OpusEncoder encoder, Supplier audioSupplier) { + return new AudioPlayerImpl(audioChannel, encoder, audioSupplier); + } + + @Override + public AudioPlayer createAudioPlayer(AudioChannel audioChannel, OpusEncoder encoder, short[] audio) { + return new AudioPlayerImpl(audioChannel, encoder, new AudioSupplier(audio)); + } + + @Override + public AudioSender createAudioSender(VoicechatConnection connection) { + return new AudioSenderImpl(connection.getPlayer().getUuid()); + } + + @Override + public boolean registerAudioSender(AudioSender sender) { + if (!(sender instanceof AudioSenderImpl)) { + return false; + } + return AudioSenderImpl.registerAudioSender((AudioSenderImpl)sender); + } + + @Override + public boolean unregisterAudioSender(AudioSender sender) { + if (!(sender instanceof AudioSenderImpl)) { + return false; + } + return AudioSenderImpl.unregisterAudioSender((AudioSenderImpl)sender); + } + + @Override + public PlayerAudioListener.Builder playerAudioListenerBuilder() { + return new PlayerAudioListenerImpl.BuilderImpl(); + } + + @Override + public boolean registerAudioListener(AudioListener listener) { + return PluginManager.instance().registerAudioListener(listener); + } + + @Override + public boolean unregisterAudioListener(AudioListener listener) { + return this.unregisterAudioListener(listener.getListenerId()); + } + + @Override + public boolean unregisterAudioListener(UUID listenerId) { + return PluginManager.instance().unregisterAudioListener(listenerId); + } + + public static void sendPacket(VoicechatConnection receiver, SoundPacket soundPacket) { + if (!(receiver.getPlayer() instanceof ServerPlayerImpl)) { + throw new IllegalArgumentException("ServerPlayer is not an instance of ServerPlayerImpl"); + } + VoicechatServerApiImpl.sendPacket(((ServerPlayerImpl)receiver.getPlayer()).getRealServerPlayer(), soundPacket); + } + + public static void sendPacket(Player receiver, SoundPacket soundPacket) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return; + } + PlayerState state = server.getPlayerStateManager().getState(receiver.getUniqueId()); + if (state == null) { + return; + } + ClientConnection c = server.getConnections().get(receiver.getUniqueId()); + server.sendSoundPacket(null, null, receiver, state, c, soundPacket, "plugin"); + } + + @Override + @Nullable + public VoicechatConnection getConnectionOf(UUID playerUuid) { + Player player = Bukkit.getPlayer((UUID)playerUuid); + if (player == null) { + return null; + } + return VoicechatConnectionImpl.fromPlayer(player); + } + + @Override + public de.maxhenkel.voicechat.api.Group createGroup(String name, @Nullable String password) { + return this.createGroup(name, password, false); + } + + @Override + public de.maxhenkel.voicechat.api.Group createGroup(String name, @Nullable String password, boolean persistent) { + return this.groupBuilder().setName(name).setPassword(password).setPersistent(persistent).build(); + } + + @Override + public Group.Builder groupBuilder() { + return new GroupImpl.BuilderImpl(); + } + + @Override + public boolean removeGroup(UUID groupId) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return false; + } + return server.getGroupManager().removeGroup(groupId); + } + + @Override + @Nullable + public de.maxhenkel.voicechat.api.Group getGroup(UUID groupId) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return null; + } + return new GroupImpl(server.getGroupManager().getGroup(groupId)); + } + + @Override + public Collection getGroups() { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return Collections.emptyList(); + } + return server.getGroupManager().getGroups().values().stream().map(group -> new GroupImpl((Group)group)).collect(Collectors.toList()); + } + + @Override + @Nullable + public UUID getSecret(UUID userId) { + return null; + } + + @Override + public Collection getPlayersInRange(ServerLevel level, Position pos, double range, @Nullable Predicate filter) { + if (pos instanceof PositionImpl) { + return ServerPlayerManager.getPlayersInRange((World)level.getServerLevel(), ((PositionImpl)pos).getPosition(), range, filter == null ? null : player -> filter.test(new ServerPlayerImpl((Player)player))).stream().map(ServerPlayerImpl::new).collect(Collectors.toList()); + } + throw new IllegalArgumentException("Position is not an instance of PositionImpl"); + } + + @Override + public double getBroadcastRange() { + return Math.max(Voicechat.SERVER_CONFIG.voiceChatDistance.get(), Voicechat.SERVER_CONFIG.broadcastRange.get()); + } + + @Override + public void registerVolumeCategory(VolumeCategory category) { + if (!(category instanceof VolumeCategoryImpl)) { + throw new IllegalArgumentException("VolumeCategory is not an instance of VolumeCategoryImpl"); + } + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return; + } + server.getCategoryManager().addCategory((VolumeCategoryImpl)category); + PluginManager.instance().onRegisterVolumeCategory(category); + } + + @Override + public void unregisterVolumeCategory(String categoryId) { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return; + } + VolumeCategoryImpl category = server.getCategoryManager().removeCategory(categoryId); + if (category != null) { + PluginManager.instance().onUnregisterVolumeCategory(category); + } + } + + @Override + public Collection getVolumeCategories() { + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return Collections.emptyList(); + } + return server.getCategoryManager().getCategories().stream().map(VolumeCategory.class::cast).collect(Collectors.toList()); + } + + @Override + public ConfigAccessor getServerConfig() { + return new ConfigAccessorImpl(Voicechat.SERVER_CONFIG.voiceChatDistance.getConfig()); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatSocketBase.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatSocketBase.java new file mode 100644 index 0000000..f19dd3a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatSocketBase.java @@ -0,0 +1,29 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.debug.CooldownTimer; +import de.maxhenkel.voicechat.plugins.impl.RawUdpPacketImpl; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; + +public class VoicechatSocketBase { + private final byte[] BUFFER = new byte[4096]; + + public RawUdpPacketImpl read(DatagramSocket socket) throws IOException { + DatagramPacket packet = new DatagramPacket(this.BUFFER, this.BUFFER.length); + socket.receive(packet); + if (packet.getLength() >= this.BUFFER.length) { + CooldownTimer.run("udp_packet_too_large", () -> Voicechat.LOGGER.warn("Packet from {} is too large", packet.getSocketAddress())); + throw new IOException(String.format("Packet from %s is too large", packet.getSocketAddress())); + } + long timestamp = System.currentTimeMillis(); + byte[] data = new byte[packet.getLength()]; + System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength()); + return new RawUdpPacketImpl(data, packet.getSocketAddress(), timestamp); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatSocketImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatSocketImpl.java new file mode 100644 index 0000000..9ba0b85 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VoicechatSocketImpl.java @@ -0,0 +1,138 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.Bukkit + * org.bukkit.plugin.Plugin + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.RawUdpPacket; +import de.maxhenkel.voicechat.api.VoicechatSocket; +import de.maxhenkel.voicechat.plugins.impl.VoicechatSocketBase; +import java.net.BindException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.URI; +import java.net.URISyntaxException; +import org.bukkit.Bukkit; +import org.bukkit.plugin.Plugin; + +public class VoicechatSocketImpl +extends VoicechatSocketBase +implements VoicechatSocket { + private DatagramSocket socket; + + @Override + public void open(int port, String bindAddress) throws Exception { + if (this.socket != null) { + throw new IllegalStateException("Socket already opened"); + } + this.checkCorrectHost(); + InetAddress address = null; + try { + if (!bindAddress.isEmpty()) { + address = InetAddress.getByName(bindAddress); + } + } + catch (Exception e) { + bindAddress = ""; + Voicechat.LOGGER.error("Failed to parse bind IP address '{}'", bindAddress, e); + } + try { + try { + this.socket = new DatagramSocket(port, address); + } + catch (BindException e) { + if (address == null || bindAddress.equals("0.0.0.0")) { + throw e; + } + Voicechat.LOGGER.error("Failed to bind to address '{}', binding to wildcard IP instead", bindAddress); + this.socket = new DatagramSocket(port); + } + this.socket.setTrafficClass(4); + } + catch (BindException e) { + Voicechat.LOGGER.fatal("Failed to run voice chat at UDP port {}, make sure no other application is running at that port", port); + Bukkit.getScheduler().runTask((Plugin)Voicechat.INSTANCE, () -> { + Voicechat.LOGGER.fatal("Disabling {}", "Simple Voice Chat"); + Bukkit.getPluginManager().disablePlugin((Plugin)Voicechat.INSTANCE); + }); + throw e; + } + } + + private void checkCorrectHost() throws Exception { + String host = Voicechat.SERVER_CONFIG.voiceHost.get(); + if (host.isEmpty()) { + return; + } + try { + int port = Integer.parseInt(host); + if (port <= 0 || port > 65535) { + Voicechat.LOGGER.warn("Invalid voice host port: {}", port); + } else { + Voicechat.LOGGER.info("Voice host port is {}", port); + } + } + catch (NumberFormatException ignored) { + try { + new URI("voicechat://" + host); + Voicechat.LOGGER.info("Voice host is '{}'", host); + } + catch (URISyntaxException e) { + Voicechat.LOGGER.warn("Failed to parse voice host", e); + Bukkit.shutdown(); + throw e; + } + } + } + + @Override + public RawUdpPacket read() throws Exception { + if (this.socket == null) { + throw new IllegalStateException("Socket not opened yet"); + } + return this.read(this.socket); + } + + @Override + public void send(byte[] data, SocketAddress address) throws Exception { + if (this.socket == null || this.socket.isClosed()) { + return; + } + this.socket.send(new DatagramPacket(data, data.length, address)); + } + + @Override + public int getLocalPort() { + if (this.socket == null) { + return -1; + } + return this.socket.getLocalPort(); + } + + @Override + public void close() { + if (this.socket != null) { + try { + this.socket.close(); + } + catch (Throwable throwable) { + // empty catch block + } + } + } + + @Override + public boolean isClosed() { + if (this.socket == null) { + return true; + } + return this.socket.isClosed(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VolumeCategoryImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VolumeCategoryImpl.java new file mode 100644 index 0000000..5d3ab99 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/VolumeCategoryImpl.java @@ -0,0 +1,205 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl; + +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import java.util.regex.Pattern; +import javax.annotation.Nullable; + +public class VolumeCategoryImpl +implements VolumeCategory { + public static final Pattern ID_REGEX = Pattern.compile("^[a-z_]{1,16}$"); + private final String id; + private final String name; + @Nullable + private final String nameTranslationKey; + @Nullable + private final String description; + @Nullable + private final String descriptionTranslationKey; + @Nullable + private final int[][] icon; + + public VolumeCategoryImpl(String id, String name, @Nullable String nameTranslationKey, @Nullable String description, @Nullable String descriptionTranslationKey, @Nullable int[][] icon) { + if (!ID_REGEX.matcher(id).matches()) { + throw new IllegalArgumentException("Volume category ID can only contain a-z and _ with a maximum amount of 16 characters"); + } + this.id = id; + this.name = name; + this.nameTranslationKey = nameTranslationKey; + this.description = description; + this.descriptionTranslationKey = descriptionTranslationKey; + this.icon = icon; + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String getName() { + return this.name; + } + + @Override + @Nullable + public String getNameTranslationKey() { + return this.nameTranslationKey; + } + + @Override + @Nullable + public String getDescription() { + return this.description; + } + + @Override + @Nullable + public String getDescriptionTranslationKey() { + return this.descriptionTranslationKey; + } + + @Override + @Nullable + public int[][] getIcon() { + return this.icon; + } + + public static VolumeCategoryImpl fromBytes(FriendlyByteBuf buf) { + String id = buf.readUtf(16); + String name = buf.readUtf(16); + String nameTranslationKey = VolumeCategoryImpl.readOptionalString(buf); + String description = VolumeCategoryImpl.readOptionalString(buf); + String descriptionTranslationKey = VolumeCategoryImpl.readOptionalString(buf); + int[][] icon = null; + if (buf.readBoolean()) { + icon = new int[16][16]; + for (int x = 0; x < icon.length; ++x) { + for (int y = 0; y < icon.length; ++y) { + icon[x][y] = buf.readInt(); + } + } + } + return new VolumeCategoryImpl(id, name, nameTranslationKey, description, descriptionTranslationKey, icon); + } + + @Nullable + private static String readOptionalString(FriendlyByteBuf buf) { + if (buf.readBoolean()) { + return buf.readUtf(Short.MAX_VALUE); + } + return null; + } + + public void toBytes(FriendlyByteBuf buf) { + buf.writeUtf(this.id, 16); + buf.writeUtf(this.name, 16); + VolumeCategoryImpl.writeOptionalString(buf, this.nameTranslationKey); + VolumeCategoryImpl.writeOptionalString(buf, this.description); + VolumeCategoryImpl.writeOptionalString(buf, this.descriptionTranslationKey); + buf.writeBoolean(this.icon != null); + if (this.icon != null) { + if (this.icon.length != 16) { + throw new IllegalStateException("Icon is not 16x16"); + } + for (int x = 0; x < this.icon.length; ++x) { + if (this.icon[x].length != 16) { + throw new IllegalStateException("Icon is not 16x16"); + } + for (int y = 0; y < this.icon.length; ++y) { + buf.writeInt(this.icon[x][y]); + } + } + } + } + + private static void writeOptionalString(FriendlyByteBuf buf, @Nullable String string) { + buf.writeBoolean(string != null); + if (string != null) { + buf.writeUtf(string, Short.MAX_VALUE); + } + } + + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || this.getClass() != object.getClass()) { + return false; + } + VolumeCategoryImpl that = (VolumeCategoryImpl)object; + return this.id.equals(that.id); + } + + public int hashCode() { + return this.id.hashCode(); + } + + public static class BuilderImpl + implements VolumeCategory.Builder { + private String id; + private String name; + @Nullable + private String nameTranslationKey; + @Nullable + private String description; + @Nullable + private String descriptionTranslationKey; + @Nullable + private int[][] icon; + + @Override + public VolumeCategory.Builder setId(String id) { + this.id = id; + return this; + } + + @Override + public VolumeCategory.Builder setName(String name) { + this.name = name; + return this; + } + + @Override + public VolumeCategory.Builder setNameTranslationKey(@Nullable String translationKey) { + this.nameTranslationKey = translationKey; + return this; + } + + @Override + public VolumeCategory.Builder setDescription(@Nullable String description) { + this.description = description; + return this; + } + + @Override + public VolumeCategory.Builder setDescriptionTranslationKey(@Nullable String translationKey) { + this.descriptionTranslationKey = translationKey; + return this; + } + + @Override + public VolumeCategory.Builder setIcon(@Nullable int[][] icon) { + this.icon = icon; + return this; + } + + @Override + public VolumeCategory build() { + if (this.id == null) { + throw new IllegalStateException("id missing"); + } + if (this.name == null) { + throw new IllegalStateException("name missing"); + } + return new VolumeCategoryImpl(this.id, this.name, this.nameTranslationKey, this.description, this.descriptionTranslationKey, this.icon); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audio/AudioConverterImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audio/AudioConverterImpl.java new file mode 100644 index 0000000..3cfdd10 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audio/AudioConverterImpl.java @@ -0,0 +1,41 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.audio; + +import de.maxhenkel.voicechat.api.audio.AudioConverter; +import de.maxhenkel.voicechat.voice.common.AudioUtils; + +public class AudioConverterImpl +implements AudioConverter { + @Override + public short[] bytesToShorts(byte[] bytes) { + return AudioUtils.bytesToShorts(bytes); + } + + @Override + public byte[] shortsToBytes(short[] shorts) { + return AudioUtils.shortsToBytes(shorts); + } + + @Override + public short[] floatsToShorts(float[] floats) { + return AudioUtils.floatsToShorts(floats); + } + + @Override + public float[] shortsToFloats(short[] shorts) { + return AudioUtils.shortsToFloats(shorts); + } + + @Override + public byte[] floatsToBytes(float[] floats) { + return AudioUtils.floatsToBytes(floats); + } + + @Override + public float[] bytesToFloats(byte[] bytes) { + return AudioUtils.bytesToFloats(bytes); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioChannelImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioChannelImpl.java new file mode 100644 index 0000000..3a32f19 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioChannelImpl.java @@ -0,0 +1,59 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.audiochannel; + +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.audiochannel.AudioChannel; +import de.maxhenkel.voicechat.voice.server.Server; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Predicate; +import javax.annotation.Nullable; + +public abstract class AudioChannelImpl +implements AudioChannel { + protected UUID channelId; + protected Server server; + protected AtomicLong sequenceNumber; + @Nullable + protected Predicate filter; + @Nullable + protected String category; + + public AudioChannelImpl(UUID channelId, Server server) { + this.channelId = channelId; + this.server = server; + this.sequenceNumber = new AtomicLong(); + } + + @Override + public void setFilter(Predicate filter) { + this.filter = filter; + } + + @Override + public UUID getId() { + return this.channelId; + } + + @Override + public boolean isClosed() { + return this.server.isClosed(); + } + + @Override + @Nullable + public String getCategory() { + return this.category; + } + + @Override + public void setCategory(@Nullable String category) { + this.category = category; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioPlayerImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioPlayerImpl.java new file mode 100644 index 0000000..a43fa9a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioPlayerImpl.java @@ -0,0 +1,102 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nonnull + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.audiochannel; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.audiochannel.AudioChannel; +import de.maxhenkel.voicechat.api.audiochannel.AudioPlayer; +import de.maxhenkel.voicechat.api.opus.OpusEncoder; +import de.maxhenkel.voicechat.debug.VoicechatUncaughtExceptionHandler; +import java.util.function.Supplier; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class AudioPlayerImpl +extends Thread +implements AudioPlayer { + private static final long FRAME_SIZE_NS = 20000000L; + public static final int FRAME_SIZE = 960; + private final AudioChannel audioChannel; + private final OpusEncoder encoder; + private final Supplier audioSupplier; + private boolean started; + @Nullable + private Runnable onStopped; + + public AudioPlayerImpl(AudioChannel audioChannel, @Nonnull OpusEncoder encoder, Supplier audioSupplier) { + this.audioChannel = audioChannel; + this.encoder = encoder; + this.audioSupplier = audioSupplier; + this.setDaemon(true); + this.setName(String.format("AudioPlayer-%s", audioChannel.getId())); + this.setUncaughtExceptionHandler(new VoicechatUncaughtExceptionHandler()); + } + + @Override + public void startPlaying() { + if (this.started) { + return; + } + this.start(); + this.started = true; + } + + @Override + public void stopPlaying() { + this.interrupt(); + } + + @Override + public boolean isStarted() { + return this.started; + } + + @Override + public boolean isPlaying() { + return this.isAlive(); + } + + @Override + public boolean isStopped() { + return this.started && !this.isAlive(); + } + + @Override + public void setOnStopped(Runnable onStopped) { + this.onStopped = onStopped; + } + + @Override + public void run() { + short[] frame; + int framePosition = 0; + long startTime = System.nanoTime(); + while ((frame = this.audioSupplier.get()) != null) { + if (frame.length != 960) { + Voicechat.LOGGER.error("Got invalid audio frame size {}!={}", frame.length, 960); + break; + } + this.audioChannel.send(this.encoder.encode(frame)); + long waitTimestamp = startTime + (long)(++framePosition) * 20000000L; + long waitNanos = waitTimestamp - System.nanoTime(); + try { + if (waitNanos <= 0L) continue; + Thread.sleep(waitNanos / 1000000L, (int)(waitNanos % 1000000L)); + } + catch (InterruptedException e) { + break; + } + } + this.encoder.close(); + this.audioChannel.flush(); + if (this.onStopped != null) { + this.onStopped.run(); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioSupplier.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioSupplier.java new file mode 100644 index 0000000..f137323 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/AudioSupplier.java @@ -0,0 +1,31 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.audiochannel; + +import java.util.Arrays; +import java.util.function.Supplier; + +public class AudioSupplier +implements Supplier { + private final short[] audioData; + private final short[] frame; + private int framePosition; + + public AudioSupplier(short[] audioData) { + this.audioData = audioData; + this.frame = new short[960]; + } + + @Override + public short[] get() { + if (this.framePosition >= this.audioData.length) { + return null; + } + Arrays.fill(this.frame, (short)0); + System.arraycopy(this.audioData, this.framePosition, this.frame, 0, Math.min(this.frame.length, this.audioData.length - this.framePosition)); + this.framePosition += this.frame.length; + return this.frame; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/EntityAudioChannelImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/EntityAudioChannelImpl.java new file mode 100644 index 0000000..7354d1f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/EntityAudioChannelImpl.java @@ -0,0 +1,88 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Entity + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl.audiochannel; + +import de.maxhenkel.voicechat.api.Entity; +import de.maxhenkel.voicechat.api.audiochannel.EntityAudioChannel; +import de.maxhenkel.voicechat.api.packets.MicrophonePacket; +import de.maxhenkel.voicechat.plugins.impl.ServerPlayerImpl; +import de.maxhenkel.voicechat.plugins.impl.audiochannel.AudioChannelImpl; +import de.maxhenkel.voicechat.voice.common.PlayerSoundPacket; +import de.maxhenkel.voicechat.voice.common.Utils; +import de.maxhenkel.voicechat.voice.server.Server; +import de.maxhenkel.voicechat.voice.server.ServerPlayerManager; +import java.util.UUID; +import org.bukkit.entity.Player; + +public class EntityAudioChannelImpl +extends AudioChannelImpl +implements EntityAudioChannel { + protected Entity entity; + protected boolean whispering; + protected float distance; + + public EntityAudioChannelImpl(UUID channelId, Server server, Entity entity) { + super(channelId, server); + this.entity = entity; + this.whispering = false; + this.distance = Utils.getDefaultDistance(); + } + + @Override + public void setWhispering(boolean whispering) { + this.whispering = whispering; + } + + @Override + public boolean isWhispering() { + return this.whispering; + } + + @Override + public void updateEntity(Entity entity) { + this.entity = entity; + } + + @Override + public Entity getEntity() { + return this.entity; + } + + @Override + public float getDistance() { + return this.distance; + } + + @Override + public void setDistance(float distance) { + this.distance = distance; + } + + @Override + public void send(byte[] opusData) { + this.broadcast(new PlayerSoundPacket(this.channelId, this.entity.getUuid(), opusData, this.sequenceNumber.getAndIncrement(), this.whispering, this.distance, this.category)); + } + + @Override + public void send(MicrophonePacket microphonePacket) { + this.broadcast(new PlayerSoundPacket(this.channelId, this.entity.getUuid(), microphonePacket.getOpusEncodedData(), this.sequenceNumber.getAndIncrement(), this.whispering, this.distance, this.category)); + } + + @Override + public void flush() { + this.broadcast(new PlayerSoundPacket(this.channelId, this.entity.getUuid(), new byte[0], this.sequenceNumber.getAndIncrement(), this.whispering, this.distance, this.category)); + } + + private void broadcast(PlayerSoundPacket packet) { + if (this.entity.getEntity() instanceof org.bukkit.entity.Entity) { + org.bukkit.entity.Entity entity = (org.bukkit.entity.Entity)this.entity.getEntity(); + this.server.broadcast(ServerPlayerManager.getPlayersInRange(entity.getWorld(), entity.getLocation(), this.server.getBroadcastRange(this.distance), this.filter == null ? null : player -> this.filter.test(new ServerPlayerImpl((Player)player))), packet, null, null, null, "plugin"); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/LocationalAudioChannelImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/LocationalAudioChannelImpl.java new file mode 100644 index 0000000..fb8cc3e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/LocationalAudioChannelImpl.java @@ -0,0 +1,81 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.World + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl.audiochannel; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.ServerLevel; +import de.maxhenkel.voicechat.api.audiochannel.LocationalAudioChannel; +import de.maxhenkel.voicechat.api.packets.MicrophonePacket; +import de.maxhenkel.voicechat.plugins.impl.PositionImpl; +import de.maxhenkel.voicechat.plugins.impl.ServerPlayerImpl; +import de.maxhenkel.voicechat.plugins.impl.audiochannel.AudioChannelImpl; +import de.maxhenkel.voicechat.voice.common.LocationSoundPacket; +import de.maxhenkel.voicechat.voice.common.Utils; +import de.maxhenkel.voicechat.voice.server.Server; +import de.maxhenkel.voicechat.voice.server.ServerPlayerManager; +import java.util.UUID; +import org.bukkit.World; +import org.bukkit.entity.Player; + +public class LocationalAudioChannelImpl +extends AudioChannelImpl +implements LocationalAudioChannel { + protected ServerLevel level; + protected PositionImpl position; + protected float distance; + + public LocationalAudioChannelImpl(UUID channelId, Server server, ServerLevel level, PositionImpl position) { + super(channelId, server); + this.level = level; + this.position = position; + this.distance = Utils.getDefaultDistance(); + } + + @Override + public void updateLocation(Position position) { + if (!(position instanceof PositionImpl)) { + throw new IllegalArgumentException("position is not an instance of PositionImpl"); + } + this.position = (PositionImpl)position; + } + + @Override + public Position getLocation() { + return this.position; + } + + @Override + public float getDistance() { + return this.distance; + } + + @Override + public void setDistance(float distance) { + this.distance = distance; + } + + @Override + public void send(byte[] opusData) { + this.broadcast(new LocationSoundPacket(this.channelId, this.channelId, this.position.getPosition(), opusData, this.sequenceNumber.getAndIncrement(), this.distance, this.category)); + } + + @Override + public void send(MicrophonePacket packet) { + this.send(packet.getOpusEncodedData()); + } + + @Override + public void flush() { + this.broadcast(new LocationSoundPacket(this.channelId, this.channelId, this.position.getPosition(), new byte[0], this.sequenceNumber.getAndIncrement(), this.distance, this.category)); + } + + private void broadcast(LocationSoundPacket packet) { + this.server.broadcast(ServerPlayerManager.getPlayersInRange((World)this.level.getServerLevel(), this.position.getPosition(), this.server.getBroadcastRange(this.distance), this.filter == null ? null : player -> this.filter.test(new ServerPlayerImpl((Player)player))), packet, null, null, null, "plugin"); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/StaticAudioChannelImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/StaticAudioChannelImpl.java new file mode 100644 index 0000000..fbd924f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiochannel/StaticAudioChannelImpl.java @@ -0,0 +1,113 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.Bukkit + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl.audiochannel; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.audiochannel.StaticAudioChannel; +import de.maxhenkel.voicechat.api.packets.MicrophonePacket; +import de.maxhenkel.voicechat.plugins.impl.ServerPlayerImpl; +import de.maxhenkel.voicechat.plugins.impl.VoicechatServerApiImpl; +import de.maxhenkel.voicechat.plugins.impl.audiochannel.AudioChannelImpl; +import de.maxhenkel.voicechat.voice.common.GroupSoundPacket; +import de.maxhenkel.voicechat.voice.server.ClientConnection; +import de.maxhenkel.voicechat.voice.server.Group; +import de.maxhenkel.voicechat.voice.server.Server; +import de.maxhenkel.voicechat.voice.server.ServerGroupManager; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +public class StaticAudioChannelImpl +extends AudioChannelImpl +implements StaticAudioChannel { + protected boolean bypassGroupIsolation; + protected final Set targets = new HashSet(); + + public StaticAudioChannelImpl(UUID channelId, Server server) { + super(channelId, server); + } + + @Override + public void send(byte[] opusData) { + this.broadcast(new GroupSoundPacket(this.channelId, this.channelId, opusData, this.sequenceNumber.getAndIncrement(), this.category)); + } + + @Override + public void send(MicrophonePacket packet) { + this.send(packet.getOpusEncodedData()); + } + + @Override + public void flush() { + GroupSoundPacket packet = new GroupSoundPacket(this.channelId, this.channelId, new byte[0], this.sequenceNumber.getAndIncrement(), this.category); + this.broadcast(packet); + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + private void broadcast(GroupSoundPacket packet) { + Set set = this.targets; + synchronized (set) { + ServerGroupManager groupManager = this.server.getGroupManager(); + for (UUID target : this.targets) { + Group playerGroup; + Player player; + ClientConnection connection = this.server.getConnection(target); + if (connection == null || (player = Bukkit.getPlayer((UUID)target)) == null || !this.bypassGroupIsolation && (playerGroup = groupManager.getPlayerGroup(player)) != null && playerGroup.isIsolated() || this.filter != null && !this.filter.test(new ServerPlayerImpl(player))) continue; + VoicechatServerApiImpl.sendPacket(player, packet); + } + } + } + + @Override + public void setBypassGroupIsolation(boolean bypassGroupIsolation) { + this.bypassGroupIsolation = bypassGroupIsolation; + } + + @Override + public boolean bypassesGroupIsolation() { + return this.bypassGroupIsolation; + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + @Override + public void addTarget(VoicechatConnection target) { + Set set = this.targets; + synchronized (set) { + this.targets.add(target.getPlayer().getUuid()); + } + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + @Override + public void removeTarget(VoicechatConnection target) { + Set set = this.targets; + synchronized (set) { + this.targets.remove(target.getPlayer().getUuid()); + } + } + + /* + * WARNING - Removed try catching itself - possible behaviour change. + */ + @Override + public void clearTargets() { + Set set = this.targets; + synchronized (set) { + this.targets.clear(); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiolistener/PlayerAudioListenerImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiolistener/PlayerAudioListenerImpl.java new file mode 100644 index 0000000..7c65b76 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiolistener/PlayerAudioListenerImpl.java @@ -0,0 +1,79 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.audiolistener; + +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.audiolistener.PlayerAudioListener; +import de.maxhenkel.voicechat.api.packets.SoundPacket; +import java.util.UUID; +import java.util.function.Consumer; +import javax.annotation.Nullable; + +public class PlayerAudioListenerImpl +implements PlayerAudioListener { + private final UUID playerUuid; + private final Consumer listener; + private final UUID listenerId; + + public PlayerAudioListenerImpl(UUID playerUuid, Consumer listener) { + this.playerUuid = playerUuid; + this.listener = listener; + this.listenerId = UUID.randomUUID(); + } + + @Override + public UUID getListenerId() { + return this.listenerId; + } + + @Override + public UUID getPlayerUuid() { + return this.playerUuid; + } + + public Consumer getListener() { + return this.listener; + } + + public static class BuilderImpl + implements PlayerAudioListener.Builder { + @Nullable + private UUID playerUuid; + @Nullable + private Consumer listener; + + @Override + public PlayerAudioListener.Builder setPlayer(ServerPlayer player) { + this.playerUuid = player.getUuid(); + return this; + } + + @Override + public PlayerAudioListener.Builder setPlayer(UUID playerUuid) { + this.playerUuid = playerUuid; + return this; + } + + @Override + public PlayerAudioListener.Builder setPacketListener(Consumer listener) { + this.listener = listener; + return this; + } + + @Override + public PlayerAudioListener build() { + if (this.playerUuid == null) { + throw new IllegalStateException("No player provided"); + } + if (this.listener == null) { + throw new IllegalStateException("No listener provided"); + } + return new PlayerAudioListenerImpl(this.playerUuid, this.listener); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiosender/AudioSenderImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiosender/AudioSenderImpl.java new file mode 100644 index 0000000..5c6734c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/audiosender/AudioSenderImpl.java @@ -0,0 +1,99 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.audiosender; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.audiosender.AudioSender; +import de.maxhenkel.voicechat.voice.common.MicPacket; +import de.maxhenkel.voicechat.voice.server.Server; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class AudioSenderImpl +implements AudioSender { + private static final Map AUDIO_SENDERS = new HashMap(); + private final UUID uuid; + private boolean whispering; + private long nextSequenceNumber; + + public AudioSenderImpl(UUID uuid) { + this.uuid = uuid; + } + + public static boolean registerAudioSender(AudioSenderImpl audioSender) { + if (Voicechat.SERVER.isCompatible(audioSender.uuid)) { + return false; + } + if (AUDIO_SENDERS.containsKey(audioSender.uuid)) { + return false; + } + AUDIO_SENDERS.put(audioSender.uuid, audioSender); + return true; + } + + public static boolean unregisterAudioSender(AudioSenderImpl audioSender) { + return AUDIO_SENDERS.remove(audioSender.uuid) != null; + } + + @Override + public AudioSender whispering(boolean whispering) { + this.whispering = whispering; + return this; + } + + @Override + public boolean isWhispering() { + return this.whispering; + } + + @Override + public AudioSender sequenceNumber(long sequenceNumber) { + if (sequenceNumber < 0L) { + throw new IllegalArgumentException("Sequence number must be positive"); + } + this.nextSequenceNumber = sequenceNumber; + return this; + } + + @Override + public boolean canSend() { + return !Voicechat.SERVER.isCompatible(this.uuid) && AUDIO_SENDERS.get(this.uuid) == this; + } + + @Override + public boolean send(byte[] opusEncodedAudioData) { + return this.sendMicrophonePacket(opusEncodedAudioData); + } + + @Override + public boolean reset() { + return this.sendMicrophonePacket(new byte[0]); + } + + public boolean sendMicrophonePacket(byte[] data) { + if (data == null) { + throw new IllegalStateException("opusEncodedData is not set"); + } + if (!this.canSend()) { + return false; + } + Server server = Voicechat.SERVER.getServer(); + if (server == null) { + return true; + } + try { + MicPacket packet = new MicPacket(data, data.length > 0 && this.whispering, this.nextSequenceNumber++); + if (data.length <= 0) { + this.nextSequenceNumber = 0L; + } + server.onMicPacket(this.uuid, packet); + } + catch (Exception e) { + Voicechat.LOGGER.error("Failed to send audio", e); + } + return true; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/config/ConfigAccessorImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/config/ConfigAccessorImpl.java new file mode 100644 index 0000000..8c1bda8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/config/ConfigAccessorImpl.java @@ -0,0 +1,32 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.config; + +import de.maxhenkel.voicechat.api.config.ConfigAccessor; +import de.maxhenkel.voicechat.configbuilder.Config; +import javax.annotation.Nullable; + +public class ConfigAccessorImpl +implements ConfigAccessor { + private Config config; + + public ConfigAccessorImpl(Config config) { + this.config = config; + } + + @Override + public boolean hasKey(String key) { + return this.config.getEntries().containsKey(key); + } + + @Override + @Nullable + public String getValue(String key) { + return this.config.getEntries().get(key); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/CreateGroupEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/CreateGroupEventImpl.java new file mode 100644 index 0000000..cf84f0e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/CreateGroupEventImpl.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.CreateGroupEvent; +import de.maxhenkel.voicechat.plugins.impl.events.GroupEventImpl; +import javax.annotation.Nullable; + +public class CreateGroupEventImpl +extends GroupEventImpl +implements CreateGroupEvent { + public CreateGroupEventImpl(Group group, @Nullable VoicechatConnection connection) { + super(group, connection); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/EntitySoundPacketEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/EntitySoundPacketEventImpl.java new file mode 100644 index 0000000..099623a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/EntitySoundPacketEventImpl.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.EntitySoundPacketEvent; +import de.maxhenkel.voicechat.api.packets.EntitySoundPacket; +import de.maxhenkel.voicechat.plugins.impl.events.SoundPacketEventImpl; +import javax.annotation.Nullable; + +public class EntitySoundPacketEventImpl +extends SoundPacketEventImpl +implements EntitySoundPacketEvent { + public EntitySoundPacketEventImpl(EntitySoundPacket packet, @Nullable VoicechatConnection senderConnection, VoicechatConnection receiverConnection, String source) { + super(packet, senderConnection, receiverConnection, source); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/EventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/EventImpl.java new file mode 100644 index 0000000..d91a360 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/EventImpl.java @@ -0,0 +1,31 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.events.Event; + +public class EventImpl +implements Event { + private boolean cancelled; + + @Override + public boolean isCancellable() { + return true; + } + + @Override + public boolean cancel() { + if (!this.isCancellable()) { + return false; + } + this.cancelled = true; + return true; + } + + @Override + public boolean isCancelled() { + return this.cancelled; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/GroupEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/GroupEventImpl.java new file mode 100644 index 0000000..482a10f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/GroupEventImpl.java @@ -0,0 +1,38 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.GroupEvent; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; +import javax.annotation.Nullable; + +public class GroupEventImpl +extends ServerEventImpl +implements GroupEvent { + @Nullable + protected Group group; + protected VoicechatConnection connection; + + public GroupEventImpl(@Nullable Group group, VoicechatConnection connection) { + this.group = group; + this.connection = connection; + } + + @Override + @Nullable + public Group getGroup() { + return this.group; + } + + @Override + public VoicechatConnection getConnection() { + return this.connection; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/JoinGroupEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/JoinGroupEventImpl.java new file mode 100644 index 0000000..0bd560f --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/JoinGroupEventImpl.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.JoinGroupEvent; +import de.maxhenkel.voicechat.plugins.impl.events.GroupEventImpl; + +public class JoinGroupEventImpl +extends GroupEventImpl +implements JoinGroupEvent { + public JoinGroupEventImpl(Group group, VoicechatConnection connection) { + super(group, connection); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/LeaveGroupEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/LeaveGroupEventImpl.java new file mode 100644 index 0000000..9edf8e4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/LeaveGroupEventImpl.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.LeaveGroupEvent; +import de.maxhenkel.voicechat.plugins.impl.events.GroupEventImpl; +import javax.annotation.Nullable; + +public class LeaveGroupEventImpl +extends GroupEventImpl +implements LeaveGroupEvent { + public LeaveGroupEventImpl(@Nullable Group group, VoicechatConnection connection) { + super(group, connection); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/LocationalSoundPacketEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/LocationalSoundPacketEventImpl.java new file mode 100644 index 0000000..5d8f86c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/LocationalSoundPacketEventImpl.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.LocationalSoundPacketEvent; +import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket; +import de.maxhenkel.voicechat.plugins.impl.events.SoundPacketEventImpl; +import javax.annotation.Nullable; + +public class LocationalSoundPacketEventImpl +extends SoundPacketEventImpl +implements LocationalSoundPacketEvent { + public LocationalSoundPacketEventImpl(LocationalSoundPacket packet, @Nullable VoicechatConnection senderConnection, VoicechatConnection receiverConnection, String source) { + super(packet, senderConnection, receiverConnection, source); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/MicrophonePacketEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/MicrophonePacketEventImpl.java new file mode 100644 index 0000000..7513910 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/MicrophonePacketEventImpl.java @@ -0,0 +1,18 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.MicrophonePacketEvent; +import de.maxhenkel.voicechat.api.packets.MicrophonePacket; +import de.maxhenkel.voicechat.plugins.impl.events.PacketEventImpl; + +public class MicrophonePacketEventImpl +extends PacketEventImpl +implements MicrophonePacketEvent { + public MicrophonePacketEventImpl(MicrophonePacket packet, VoicechatConnection connection) { + super(packet, connection, null); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PacketEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PacketEventImpl.java new file mode 100644 index 0000000..8de98f1 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PacketEventImpl.java @@ -0,0 +1,47 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.PacketEvent; +import de.maxhenkel.voicechat.api.packets.Packet; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; +import javax.annotation.Nullable; + +public class PacketEventImpl +extends ServerEventImpl +implements PacketEvent { + private final T packet; + @Nullable + private final VoicechatConnection receiverConnection; + @Nullable + private final VoicechatConnection senderConnection; + + public PacketEventImpl(T packet, @Nullable VoicechatConnection senderConnection, @Nullable VoicechatConnection receiverConnection) { + this.packet = packet; + this.senderConnection = senderConnection; + this.receiverConnection = receiverConnection; + } + + @Override + public T getPacket() { + return this.packet; + } + + @Override + @Nullable + public VoicechatConnection getReceiverConnection() { + return this.receiverConnection; + } + + @Override + @Nullable + public VoicechatConnection getSenderConnection() { + return this.senderConnection; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerConnectedEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerConnectedEventImpl.java new file mode 100644 index 0000000..7125f6c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerConnectedEventImpl.java @@ -0,0 +1,25 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.PlayerConnectedEvent; +import de.maxhenkel.voicechat.plugins.impl.VoicechatConnectionImpl; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; + +public class PlayerConnectedEventImpl +extends ServerEventImpl +implements PlayerConnectedEvent { + protected VoicechatConnectionImpl connection; + + public PlayerConnectedEventImpl(VoicechatConnectionImpl connection) { + this.connection = connection; + } + + @Override + public VoicechatConnection getConnection() { + return this.connection; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerDisconnectedEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerDisconnectedEventImpl.java new file mode 100644 index 0000000..5626075 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerDisconnectedEventImpl.java @@ -0,0 +1,24 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.events.PlayerDisconnectedEvent; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; +import java.util.UUID; + +public class PlayerDisconnectedEventImpl +extends ServerEventImpl +implements PlayerDisconnectedEvent { + protected UUID player; + + public PlayerDisconnectedEventImpl(UUID player) { + this.player = player; + } + + @Override + public UUID getPlayerUuid() { + return this.player; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerStateChangedEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerStateChangedEventImpl.java new file mode 100644 index 0000000..7e57322 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/PlayerStateChangedEventImpl.java @@ -0,0 +1,59 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.PlayerStateChangedEvent; +import de.maxhenkel.voicechat.plugins.impl.VoicechatConnectionImpl; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import java.util.UUID; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public class PlayerStateChangedEventImpl +extends ServerEventImpl +implements PlayerStateChangedEvent { + protected final PlayerState state; + @Nullable + protected VoicechatConnectionImpl connection; + + public PlayerStateChangedEventImpl(PlayerState state) { + this.state = state; + } + + @Override + public boolean isDisabled() { + return this.state.isDisabled(); + } + + @Override + public boolean isDisconnected() { + return this.state.isDisconnected(); + } + + @Override + public UUID getPlayerUuid() { + return this.state.getUuid(); + } + + @Override + @Nullable + public VoicechatConnection getConnection() { + if (this.connection == null) { + Player player = Voicechat.INSTANCE.getServer().getPlayer(this.state.getUuid()); + if (player == null) { + return null; + } + this.connection = VoicechatConnectionImpl.fromPlayer(player); + } + return this.connection; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/RegisterVolumeCategoryEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/RegisterVolumeCategoryEventImpl.java new file mode 100644 index 0000000..436d0f1 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/RegisterVolumeCategoryEventImpl.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.events.RegisterVolumeCategoryEvent; +import de.maxhenkel.voicechat.plugins.impl.events.VolumeCategoryEventImpl; + +public class RegisterVolumeCategoryEventImpl +extends VolumeCategoryEventImpl +implements RegisterVolumeCategoryEvent { + public RegisterVolumeCategoryEventImpl(VolumeCategory category) { + super(category); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/RemoveGroupEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/RemoveGroupEventImpl.java new file mode 100644 index 0000000..994dcf6 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/RemoveGroupEventImpl.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.api.events.RemoveGroupEvent; +import de.maxhenkel.voicechat.plugins.impl.events.GroupEventImpl; + +public class RemoveGroupEventImpl +extends GroupEventImpl +implements RemoveGroupEvent { + public RemoveGroupEventImpl(Group group) { + super(group, null); + } + + @Override + public boolean isCancellable() { + return super.isCancellable() && this.group.isPersistent(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/ServerEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/ServerEventImpl.java new file mode 100644 index 0000000..c412f9d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/ServerEventImpl.java @@ -0,0 +1,19 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatServerApi; +import de.maxhenkel.voicechat.api.events.ServerEvent; +import de.maxhenkel.voicechat.plugins.impl.VoicechatServerApiImpl; +import de.maxhenkel.voicechat.plugins.impl.events.EventImpl; + +public class ServerEventImpl +extends EventImpl +implements ServerEvent { + @Override + public VoicechatServerApi getVoicechat() { + return VoicechatServerApiImpl.instance(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/SoundPacketEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/SoundPacketEventImpl.java new file mode 100644 index 0000000..9ea9745 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/SoundPacketEventImpl.java @@ -0,0 +1,30 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.SoundPacketEvent; +import de.maxhenkel.voicechat.api.packets.Packet; +import de.maxhenkel.voicechat.plugins.impl.events.PacketEventImpl; +import javax.annotation.Nullable; + +public class SoundPacketEventImpl +extends PacketEventImpl +implements SoundPacketEvent { + private final String source; + + public SoundPacketEventImpl(T packet, @Nullable VoicechatConnection senderConnection, @Nullable VoicechatConnection receiverConnection, String source) { + super(packet, senderConnection, receiverConnection); + this.source = source; + } + + @Override + public String getSource() { + return this.source; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/StaticSoundPacketEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/StaticSoundPacketEventImpl.java new file mode 100644 index 0000000..107734c --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/StaticSoundPacketEventImpl.java @@ -0,0 +1,22 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.StaticSoundPacketEvent; +import de.maxhenkel.voicechat.api.packets.StaticSoundPacket; +import de.maxhenkel.voicechat.plugins.impl.events.SoundPacketEventImpl; +import javax.annotation.Nullable; + +public class StaticSoundPacketEventImpl +extends SoundPacketEventImpl +implements StaticSoundPacketEvent { + public StaticSoundPacketEventImpl(StaticSoundPacket packet, @Nullable VoicechatConnection senderConnection, VoicechatConnection receiverConnection, String source) { + super(packet, senderConnection, receiverConnection, source); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/UnregisterVolumeCategoryEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/UnregisterVolumeCategoryEventImpl.java new file mode 100644 index 0000000..1d0228a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/UnregisterVolumeCategoryEventImpl.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.events.UnregisterVolumeCategoryEvent; +import de.maxhenkel.voicechat.plugins.impl.events.VolumeCategoryEventImpl; + +public class UnregisterVolumeCategoryEventImpl +extends VolumeCategoryEventImpl +implements UnregisterVolumeCategoryEvent { + public UnregisterVolumeCategoryEventImpl(VolumeCategory category) { + super(category); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoiceDistanceEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoiceDistanceEventImpl.java new file mode 100644 index 0000000..fd14989 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoiceDistanceEventImpl.java @@ -0,0 +1,54 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatConnection; +import de.maxhenkel.voicechat.api.events.VoiceDistanceEvent; +import de.maxhenkel.voicechat.api.packets.MicrophonePacket; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; +import javax.annotation.Nullable; + +public class VoiceDistanceEventImpl +extends ServerEventImpl +implements VoiceDistanceEvent { + private final MicrophonePacket packet; + private final VoicechatConnection senderConnection; + private float distance; + + public VoiceDistanceEventImpl(MicrophonePacket packet, VoicechatConnection senderConnection, float distance) { + this.packet = packet; + this.senderConnection = senderConnection; + this.distance = distance; + } + + @Override + public boolean isCancellable() { + return false; + } + + @Override + public MicrophonePacket getPacket() { + return this.packet; + } + + @Override + @Nullable + public VoicechatConnection getSenderConnection() { + return this.senderConnection; + } + + @Override + public float getDistance() { + return this.distance; + } + + @Override + public void setDistance(float distance) { + this.distance = distance; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoiceHostEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoiceHostEventImpl.java new file mode 100644 index 0000000..cb62b56 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoiceHostEventImpl.java @@ -0,0 +1,41 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.ServerPlayer; +import de.maxhenkel.voicechat.api.events.VoiceHostEvent; +import de.maxhenkel.voicechat.plugins.impl.ServerPlayerImpl; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; +import org.bukkit.entity.Player; + +public class VoiceHostEventImpl +extends ServerEventImpl +implements VoiceHostEvent { + private final ServerPlayerImpl player; + private String voiceHost; + + public VoiceHostEventImpl(Player player, String voiceHost) { + this.player = new ServerPlayerImpl(player); + this.voiceHost = voiceHost; + } + + @Override + public String getVoiceHost() { + return this.voiceHost; + } + + @Override + public void setVoiceHost(String voiceHost) { + this.voiceHost = voiceHost; + } + + @Override + public ServerPlayer getPlayer() { + return this.player; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStartedEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStartedEventImpl.java new file mode 100644 index 0000000..851b8ec --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStartedEventImpl.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.events.VoicechatServerStartedEvent; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; + +public class VoicechatServerStartedEventImpl +extends ServerEventImpl +implements VoicechatServerStartedEvent { + @Override + public boolean isCancellable() { + return false; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStartingEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStartingEventImpl.java new file mode 100644 index 0000000..a91c6fd --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStartingEventImpl.java @@ -0,0 +1,36 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VoicechatSocket; +import de.maxhenkel.voicechat.api.events.VoicechatServerStartingEvent; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; +import javax.annotation.Nullable; + +public class VoicechatServerStartingEventImpl +extends ServerEventImpl +implements VoicechatServerStartingEvent { + @Nullable + private VoicechatSocket socketImplementation; + + @Override + public void setSocketImplementation(VoicechatSocket socket) { + this.socketImplementation = socket; + } + + @Override + @Nullable + public VoicechatSocket getSocketImplementation() { + return this.socketImplementation; + } + + @Override + public boolean isCancellable() { + return false; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStoppedEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStoppedEventImpl.java new file mode 100644 index 0000000..ba9ac36 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VoicechatServerStoppedEventImpl.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.events.VoicechatServerStoppedEvent; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; + +public class VoicechatServerStoppedEventImpl +extends ServerEventImpl +implements VoicechatServerStoppedEvent { + @Override + public boolean isCancellable() { + return false; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VolumeCategoryEventImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VolumeCategoryEventImpl.java new file mode 100644 index 0000000..2487233 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/events/VolumeCategoryEventImpl.java @@ -0,0 +1,29 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.events; + +import de.maxhenkel.voicechat.api.VolumeCategory; +import de.maxhenkel.voicechat.api.events.VolumeCategoryEvent; +import de.maxhenkel.voicechat.plugins.impl.events.ServerEventImpl; + +public class VolumeCategoryEventImpl +extends ServerEventImpl +implements VolumeCategoryEvent { + private final VolumeCategory category; + + public VolumeCategoryEventImpl(VolumeCategory category) { + this.category = category; + } + + @Override + public VolumeCategory getVolumeCategory() { + return this.category; + } + + @Override + public boolean isCancellable() { + return false; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/JavaOpusDecoderImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/JavaOpusDecoderImpl.java new file mode 100644 index 0000000..8854eb2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/JavaOpusDecoderImpl.java @@ -0,0 +1,116 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.opus; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.opus.OpusDecoder; +import de.maxhenkel.voicechat.concentus.OpusException; +import javax.annotation.Nullable; + +public class JavaOpusDecoderImpl +implements OpusDecoder { + protected de.maxhenkel.voicechat.concentus.OpusDecoder opusDecoder; + protected short[] buffer; + protected int sampleRate; + + public JavaOpusDecoderImpl(int sampleRate, int frameSize) { + this.sampleRate = sampleRate; + this.buffer = new short[frameSize]; + this.open(); + } + + private void open() { + if (this.opusDecoder != null) { + return; + } + try { + this.opusDecoder = new de.maxhenkel.voicechat.concentus.OpusDecoder(this.sampleRate, 1); + } + catch (OpusException e) { + throw new IllegalStateException("Failed to create Opus decoder", e); + } + Voicechat.LOGGER.debug("Initializing Java Opus decoder with sample rate {} Hz, frame size {} bytes", this.sampleRate, this.buffer.length); + } + + @Override + public short[] decode(@Nullable byte[] data) { + int result; + if (this.isClosed()) { + throw new IllegalStateException("Decoder is closed"); + } + try { + result = data == null || data.length == 0 ? this.opusDecoder.decode(null, 0, 0, this.buffer, 0, this.buffer.length, true) : this.opusDecoder.decode(data, 0, data.length, this.buffer, 0, this.buffer.length, false); + } + catch (Exception e) { + throw new RuntimeException("Failed to decode audio", e); + } + short[] audio = new short[result]; + System.arraycopy(this.buffer, 0, audio, 0, result); + return audio; + } + + @Override + public short[][] decode(byte[] data, int frames) { + if (this.isClosed()) { + throw new IllegalStateException("Decoder is closed"); + } + if (frames <= 0) { + throw new IllegalArgumentException("Frames must be greater than 0"); + } + if (data == null || data.length == 0) { + throw new IllegalArgumentException("Data must not be null or empty"); + } + short[][] recovered = new short[frames][]; + try { + int result; + if (frames > 2) { + for (int i = 0; i < frames - 2; ++i) { + result = this.opusDecoder.decode(null, 0, 0, this.buffer, 0, this.buffer.length, true); + short[] audio = new short[result]; + System.arraycopy(this.buffer, 0, audio, 0, result); + recovered[i] = audio; + } + } + if (frames > 1) { + result = this.opusDecoder.decode(data, 0, data.length, this.buffer, 0, this.buffer.length, true); + short[] audio = new short[result]; + System.arraycopy(this.buffer, 0, audio, 0, result); + recovered[frames - 2] = audio; + } + result = this.opusDecoder.decode(data, 0, data.length, this.buffer, 0, this.buffer.length, false); + short[] audio = new short[result]; + System.arraycopy(this.buffer, 0, audio, 0, result); + recovered[frames - 1] = audio; + } + catch (Exception e) { + throw new RuntimeException("Failed to decode audio", e); + } + return recovered; + } + + @Override + public boolean isClosed() { + return this.opusDecoder == null; + } + + @Override + public void close() { + if (this.isClosed()) { + return; + } + this.opusDecoder = null; + } + + @Override + public void resetState() { + if (this.isClosed()) { + throw new IllegalStateException("Decoder is closed"); + } + this.opusDecoder.resetState(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/JavaOpusEncoderImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/JavaOpusEncoderImpl.java new file mode 100644 index 0000000..39b2ac8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/JavaOpusEncoderImpl.java @@ -0,0 +1,82 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.opus; + +import de.maxhenkel.voicechat.concentus.OpusApplication; +import de.maxhenkel.voicechat.concentus.OpusEncoder; + +public class JavaOpusEncoderImpl +implements de.maxhenkel.voicechat.api.opus.OpusEncoder { + protected OpusEncoder opusEncoder; + protected byte[] buffer; + protected int sampleRate; + protected int frameSize; + protected int maxPayloadSize; + protected OpusApplication application; + + public JavaOpusEncoderImpl(int sampleRate, int frameSize, int maxPayloadSize, OpusApplication application) { + this.sampleRate = sampleRate; + this.frameSize = frameSize; + this.maxPayloadSize = maxPayloadSize; + this.application = application; + this.buffer = new byte[maxPayloadSize]; + this.open(); + } + + private void open() { + if (this.opusEncoder != null) { + return; + } + try { + this.opusEncoder = new OpusEncoder(this.sampleRate, 1, this.application); + this.opusEncoder.setUseInbandFEC(true); + this.opusEncoder.setPacketLossPercent(5); + } + catch (Exception e) { + throw new IllegalStateException("Opus encoder error " + e.getMessage()); + } + } + + @Override + public byte[] encode(short[] rawAudio) { + int result; + if (this.isClosed()) { + throw new IllegalStateException("Encoder is closed"); + } + try { + result = this.opusEncoder.encode(rawAudio, 0, this.frameSize, this.buffer, 0, this.buffer.length); + } + catch (Exception e) { + throw new RuntimeException("Failed to encode audio data: " + e.getMessage()); + } + if (result < 0) { + throw new RuntimeException("Failed to encode audio data"); + } + byte[] audio = new byte[result]; + System.arraycopy(this.buffer, 0, audio, 0, result); + return audio; + } + + @Override + public void resetState() { + if (this.isClosed()) { + throw new IllegalStateException("Encoder is closed"); + } + this.opusEncoder.resetState(); + } + + @Override + public boolean isClosed() { + return this.opusEncoder == null; + } + + @Override + public void close() { + if (this.isClosed()) { + return; + } + this.opusEncoder = null; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/OpusManager.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/OpusManager.java new file mode 100644 index 0000000..2f509b4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/opus/OpusManager.java @@ -0,0 +1,46 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.opus; + +import de.maxhenkel.voicechat.api.opus.OpusDecoder; +import de.maxhenkel.voicechat.api.opus.OpusEncoder; +import de.maxhenkel.voicechat.api.opus.OpusEncoderMode; +import de.maxhenkel.voicechat.concentus.OpusApplication; +import de.maxhenkel.voicechat.plugins.impl.opus.JavaOpusDecoderImpl; +import de.maxhenkel.voicechat.plugins.impl.opus.JavaOpusEncoderImpl; + +public class OpusManager { + public static final int SAMPLE_RATE = 48000; + public static final int FRAME_SIZE = 960; + + public static OpusEncoder createEncoder(int sampleRate, int frameSize, int maxPayloadSize, OpusApplication application) { + return new JavaOpusEncoderImpl(sampleRate, frameSize, maxPayloadSize, application); + } + + public static OpusEncoder createEncoder(OpusEncoderMode mode) { + OpusApplication application = OpusApplication.OPUS_APPLICATION_VOIP; + if (mode != null) { + switch (mode) { + case AUDIO: { + application = OpusApplication.OPUS_APPLICATION_AUDIO; + break; + } + case RESTRICTED_LOWDELAY: { + application = OpusApplication.OPUS_APPLICATION_RESTRICTED_LOWDELAY; + break; + } + } + } + return OpusManager.createEncoder(48000, 960, 1024, application); + } + + public static OpusDecoder createDecoder(int sampleRate, int frameSize) { + return new JavaOpusDecoderImpl(sampleRate, frameSize); + } + + public static OpusDecoder createDecoder() { + return OpusManager.createDecoder(48000, 960); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/ConvertablePacketImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/ConvertablePacketImpl.java new file mode 100644 index 0000000..b86b9fe --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/ConvertablePacketImpl.java @@ -0,0 +1,192 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.packets; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.packets.ConvertablePacket; +import de.maxhenkel.voicechat.api.packets.EntitySoundPacket; +import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket; +import de.maxhenkel.voicechat.api.packets.SoundPacket; +import de.maxhenkel.voicechat.api.packets.StaticSoundPacket; +import de.maxhenkel.voicechat.plugins.impl.PositionImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.EntitySoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.LocationalSoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.StaticSoundPacketImpl; +import de.maxhenkel.voicechat.voice.common.GroupSoundPacket; +import de.maxhenkel.voicechat.voice.common.LocationSoundPacket; +import de.maxhenkel.voicechat.voice.common.PlayerSoundPacket; +import de.maxhenkel.voicechat.voice.common.Utils; +import java.util.UUID; +import javax.annotation.Nullable; + +public class ConvertablePacketImpl +implements ConvertablePacket { + @Override + public EntitySoundPacket.Builder entitySoundPacketBuilder() { + return new GenericEntitySoundPacketBuilder(); + } + + @Override + public LocationalSoundPacket.Builder locationalSoundPacketBuilder() { + return new GenericLocationalSoundPacketBuilder(); + } + + @Override + public StaticSoundPacket.Builder staticSoundPacketBuilder() { + return new GenericStaticSoundPacketBuilder(); + } + + @Override + public EntitySoundPacket toEntitySoundPacket(UUID entityUuid, boolean whispering) { + throw new UnsupportedOperationException(); + } + + @Override + public LocationalSoundPacket toLocationalSoundPacket(Position position) { + throw new UnsupportedOperationException(); + } + + @Override + public StaticSoundPacket toStaticSoundPacket() { + throw new UnsupportedOperationException(); + } + + public static class GenericEntitySoundPacketBuilder + extends GenericSoundPacketBuilder + implements EntitySoundPacket.Builder { + protected UUID entityUuid; + protected boolean whispering; + protected Float distance; + + @Override + public GenericEntitySoundPacketBuilder entityUuid(UUID entityUuid) { + this.entityUuid = entityUuid; + return this; + } + + @Override + public GenericEntitySoundPacketBuilder whispering(boolean whispering) { + this.whispering = whispering; + return this; + } + + @Override + public GenericEntitySoundPacketBuilder distance(float distance) { + this.distance = Float.valueOf(distance); + return this; + } + + @Override + public EntitySoundPacket build() { + this.checkRequiredFields(); + if (this.entityUuid == null) { + throw new IllegalStateException("Entity UUID must be set"); + } + if (this.distance == null) { + this.distance = Float.valueOf(Utils.getDefaultDistance()); + } + return new EntitySoundPacketImpl(new PlayerSoundPacket(this.channelId, this.entityUuid, this.opusEncodedData, this.sequenceNumber, this.whispering, this.distance.floatValue(), this.category)); + } + } + + public static class GenericLocationalSoundPacketBuilder + extends GenericSoundPacketBuilder + implements LocationalSoundPacket.Builder { + protected PositionImpl position; + protected Float distance; + + @Override + public GenericLocationalSoundPacketBuilder position(Position position) { + this.position = (PositionImpl)position; + return this; + } + + @Override + public GenericLocationalSoundPacketBuilder distance(float distance) { + this.distance = Float.valueOf(distance); + return this; + } + + @Override + public LocationalSoundPacket build() { + this.checkRequiredFields(); + if (this.position == null) { + throw new IllegalStateException("Position must be set"); + } + if (this.distance == null) { + this.distance = Float.valueOf(Utils.getDefaultDistance()); + } + return new LocationalSoundPacketImpl(new LocationSoundPacket(this.channelId, this.sender, this.position.getPosition(), this.opusEncodedData, this.sequenceNumber, this.distance.floatValue(), this.category)); + } + } + + public static class GenericStaticSoundPacketBuilder + extends GenericSoundPacketBuilder + implements StaticSoundPacket.Builder { + @Override + public StaticSoundPacket build() { + return new StaticSoundPacketImpl(new GroupSoundPacket(this.channelId, this.sender, this.opusEncodedData, this.sequenceNumber, this.category)); + } + } + + public static abstract class GenericSoundPacketBuilder, P extends SoundPacket> + implements SoundPacket.Builder { + @Nullable + protected UUID channelId; + @Nullable + protected UUID sender; + @Nullable + protected byte[] opusEncodedData; + @Nullable + protected Long sequenceNumber; + @Nullable + protected String category; + + @Override + public T channelId(UUID channelId) { + this.channelId = channelId; + return (T)this; + } + + @Override + public T sender(UUID sender) { + this.sender = sender; + return (T)this; + } + + @Override + public T opusEncodedData(byte[] data) { + this.opusEncodedData = data; + return (T)this; + } + + @Override + public T sequenceNumber(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + return (T)this; + } + + @Override + public T category(@Nullable String category) { + this.category = category; + return (T)this; + } + + public void checkRequiredFields() { + if (this.channelId == null) { + throw new IllegalStateException("Channel id must be set"); + } + if (this.sequenceNumber == null) { + throw new IllegalStateException("Sequence number must be set"); + } + if (this.opusEncodedData == null) { + throw new IllegalStateException("Opus encoded data must be set"); + } + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/EntitySoundPacketImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/EntitySoundPacketImpl.java new file mode 100644 index 0000000..e368059 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/EntitySoundPacketImpl.java @@ -0,0 +1,102 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.packets; + +import de.maxhenkel.voicechat.api.packets.EntitySoundPacket; +import de.maxhenkel.voicechat.plugins.impl.packets.LocationalSoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.SoundPacketImpl; +import de.maxhenkel.voicechat.voice.common.PlayerSoundPacket; +import de.maxhenkel.voicechat.voice.common.Utils; +import java.util.UUID; +import javax.annotation.Nullable; + +public class EntitySoundPacketImpl +extends SoundPacketImpl +implements EntitySoundPacket { + private final PlayerSoundPacket packet; + + public EntitySoundPacketImpl(PlayerSoundPacket packet) { + super(packet); + this.packet = packet; + } + + @Override + public UUID getEntityUuid() { + return this.packet.getSender(); + } + + @Override + public boolean isWhispering() { + return this.packet.isWhispering(); + } + + @Override + public float getDistance() { + return this.packet.getDistance(); + } + + public PlayerSoundPacket getPacket() { + return this.packet; + } + + @Override + public UUID getChannelId() { + return this.packet.getChannelId(); + } + + public static class BuilderImpl + extends SoundPacketImpl.BuilderImpl + implements EntitySoundPacket.Builder { + protected UUID entityUuid; + protected boolean whispering; + protected float distance; + + public BuilderImpl(SoundPacketImpl soundPacket) { + super(soundPacket); + if (soundPacket instanceof EntitySoundPacketImpl) { + EntitySoundPacketImpl p = (EntitySoundPacketImpl)soundPacket; + this.entityUuid = p.getEntityUuid(); + this.whispering = p.isWhispering(); + this.distance = p.getDistance(); + } else { + this.distance = soundPacket instanceof LocationalSoundPacketImpl ? ((LocationalSoundPacketImpl)soundPacket).getDistance() : Utils.getDefaultDistance(); + } + } + + public BuilderImpl(UUID channelId, UUID sender, byte[] opusEncodedData, long sequenceNumber, @Nullable String category) { + super(channelId, sender, opusEncodedData, sequenceNumber, category); + this.distance = Utils.getDefaultDistance(); + } + + @Override + public BuilderImpl entityUuid(UUID entityUuid) { + this.entityUuid = entityUuid; + return this; + } + + @Override + public BuilderImpl whispering(boolean whispering) { + this.whispering = whispering; + return this; + } + + @Override + public BuilderImpl distance(float distance) { + this.distance = distance; + return this; + } + + @Override + public EntitySoundPacket build() { + if (this.entityUuid == null) { + throw new IllegalStateException("entityUuid missing"); + } + return new EntitySoundPacketImpl(new PlayerSoundPacket(this.channelId, this.entityUuid, this.opusEncodedData, this.sequenceNumber, this.whispering, this.distance, this.category)); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/LocationalSoundPacketImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/LocationalSoundPacketImpl.java new file mode 100644 index 0000000..c668b93 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/LocationalSoundPacketImpl.java @@ -0,0 +1,88 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.packets; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket; +import de.maxhenkel.voicechat.plugins.impl.PositionImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.EntitySoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.SoundPacketImpl; +import de.maxhenkel.voicechat.voice.common.LocationSoundPacket; +import de.maxhenkel.voicechat.voice.common.Utils; +import java.util.UUID; +import javax.annotation.Nullable; + +public class LocationalSoundPacketImpl +extends SoundPacketImpl +implements LocationalSoundPacket { + private final LocationSoundPacket packet; + private final PositionImpl position; + + public LocationalSoundPacketImpl(LocationSoundPacket packet) { + super(packet); + this.packet = packet; + this.position = new PositionImpl(packet.getLocation()); + } + + @Override + public Position getPosition() { + return this.position; + } + + @Override + public float getDistance() { + return this.packet.getDistance(); + } + + public LocationSoundPacket getPacket() { + return this.packet; + } + + public static class BuilderImpl + extends SoundPacketImpl.BuilderImpl + implements LocationalSoundPacket.Builder { + protected PositionImpl position; + protected float distance; + + public BuilderImpl(SoundPacketImpl soundPacket) { + super(soundPacket); + if (soundPacket instanceof LocationalSoundPacketImpl) { + LocationalSoundPacketImpl p = (LocationalSoundPacketImpl)soundPacket; + this.position = p.position; + this.distance = p.getDistance(); + } else { + this.distance = soundPacket instanceof EntitySoundPacketImpl ? ((EntitySoundPacketImpl)soundPacket).getDistance() : Utils.getDefaultDistance(); + } + } + + public BuilderImpl(UUID channelId, UUID sender, byte[] opusEncodedData, long sequenceNumber, @Nullable String category) { + super(channelId, sender, opusEncodedData, sequenceNumber, category); + this.distance = Utils.getDefaultDistance(); + } + + @Override + public BuilderImpl position(Position position) { + this.position = (PositionImpl)position; + return this; + } + + @Override + public BuilderImpl distance(float distance) { + this.distance = distance; + return this; + } + + @Override + public LocationalSoundPacket build() { + if (this.position == null) { + throw new IllegalStateException("position missing"); + } + return new LocationalSoundPacketImpl(new LocationSoundPacket(this.channelId, this.sender, this.position.getPosition(), this.opusEncodedData, this.sequenceNumber, this.distance, this.category)); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/MicrophonePacketImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/MicrophonePacketImpl.java new file mode 100644 index 0000000..de0432e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/MicrophonePacketImpl.java @@ -0,0 +1,84 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.plugins.impl.packets; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.packets.EntitySoundPacket; +import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket; +import de.maxhenkel.voicechat.api.packets.MicrophonePacket; +import de.maxhenkel.voicechat.api.packets.StaticSoundPacket; +import de.maxhenkel.voicechat.plugins.impl.PositionImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.EntitySoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.LocationalSoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.StaticSoundPacketImpl; +import de.maxhenkel.voicechat.voice.common.GroupSoundPacket; +import de.maxhenkel.voicechat.voice.common.LocationSoundPacket; +import de.maxhenkel.voicechat.voice.common.MicPacket; +import de.maxhenkel.voicechat.voice.common.PlayerSoundPacket; +import de.maxhenkel.voicechat.voice.common.Utils; +import java.util.Objects; +import java.util.UUID; + +public class MicrophonePacketImpl +implements MicrophonePacket { + private final MicPacket packet; + private final UUID sender; + + public MicrophonePacketImpl(MicPacket packet, UUID sender) { + this.packet = packet; + this.sender = sender; + } + + @Override + public boolean isWhispering() { + return this.packet.isWhispering(); + } + + @Override + public byte[] getOpusEncodedData() { + return this.packet.getData(); + } + + @Override + public void setOpusEncodedData(byte[] data) { + this.packet.setData(Objects.requireNonNull(data)); + } + + @Override + public EntitySoundPacket.Builder entitySoundPacketBuilder() { + return new EntitySoundPacketImpl.BuilderImpl(this.sender, this.sender, this.packet.getData(), this.packet.getSequenceNumber(), null); + } + + @Override + public LocationalSoundPacket.Builder locationalSoundPacketBuilder() { + return new LocationalSoundPacketImpl.BuilderImpl(this.sender, this.sender, this.packet.getData(), this.packet.getSequenceNumber(), null); + } + + @Override + public StaticSoundPacket.Builder staticSoundPacketBuilder() { + return new StaticSoundPacketImpl.BuilderImpl(this.sender, this.sender, this.packet.getData(), this.packet.getSequenceNumber(), null); + } + + @Override + @Deprecated + public EntitySoundPacket toEntitySoundPacket(UUID entityUuid, boolean whispering) { + return new EntitySoundPacketImpl(new PlayerSoundPacket(this.sender, entityUuid, this.packet.getData(), this.packet.getSequenceNumber(), whispering, Utils.getDefaultDistance(), null)); + } + + @Override + @Deprecated + public LocationalSoundPacket toLocationalSoundPacket(Position position) { + if (position instanceof PositionImpl) { + return new LocationalSoundPacketImpl(new LocationSoundPacket(this.sender, this.sender, ((PositionImpl)position).getPosition(), this.packet.getData(), this.packet.getSequenceNumber(), Utils.getDefaultDistance(), null)); + } + throw new IllegalArgumentException("position is not an instance of PositionImpl"); + } + + @Override + @Deprecated + public StaticSoundPacket toStaticSoundPacket() { + return new StaticSoundPacketImpl(new GroupSoundPacket(this.sender, this.sender, this.packet.getData(), this.packet.getSequenceNumber(), null)); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/SoundPacketImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/SoundPacketImpl.java new file mode 100644 index 0000000..f9df157 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/SoundPacketImpl.java @@ -0,0 +1,166 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.packets; + +import de.maxhenkel.voicechat.api.Position; +import de.maxhenkel.voicechat.api.packets.EntitySoundPacket; +import de.maxhenkel.voicechat.api.packets.LocationalSoundPacket; +import de.maxhenkel.voicechat.api.packets.SoundPacket; +import de.maxhenkel.voicechat.api.packets.StaticSoundPacket; +import de.maxhenkel.voicechat.plugins.impl.PositionImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.EntitySoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.LocationalSoundPacketImpl; +import de.maxhenkel.voicechat.plugins.impl.packets.StaticSoundPacketImpl; +import de.maxhenkel.voicechat.voice.common.GroupSoundPacket; +import de.maxhenkel.voicechat.voice.common.LocationSoundPacket; +import de.maxhenkel.voicechat.voice.common.PlayerSoundPacket; +import de.maxhenkel.voicechat.voice.common.SoundPacket; +import de.maxhenkel.voicechat.voice.common.Utils; +import java.util.UUID; +import javax.annotation.Nullable; + +public class SoundPacketImpl +implements de.maxhenkel.voicechat.api.packets.SoundPacket { + private final SoundPacket packet; + + public SoundPacketImpl(SoundPacket packet) { + this.packet = packet; + } + + @Override + public UUID getChannelId() { + return this.packet.getChannelId(); + } + + @Override + public UUID getSender() { + return this.packet.getSender(); + } + + @Override + public byte[] getOpusEncodedData() { + return this.packet.getData(); + } + + @Override + public long getSequenceNumber() { + return this.packet.getSequenceNumber(); + } + + @Override + @Nullable + public String getCategory() { + return this.packet.getCategory(); + } + + public SoundPacket getPacket() { + return this.packet; + } + + @Override + public EntitySoundPacket.Builder entitySoundPacketBuilder() { + return new EntitySoundPacketImpl.BuilderImpl(this); + } + + @Override + public LocationalSoundPacket.Builder locationalSoundPacketBuilder() { + return new LocationalSoundPacketImpl.BuilderImpl(this); + } + + @Override + public StaticSoundPacket.Builder staticSoundPacketBuilder() { + return new StaticSoundPacketImpl.BuilderImpl(this); + } + + @Override + public EntitySoundPacket toEntitySoundPacket(UUID entityUuid, boolean whispering) { + return new EntitySoundPacketImpl(new PlayerSoundPacket(this.packet.getChannelId(), entityUuid, this.packet.getData(), this.packet.getSequenceNumber(), whispering, this.getDistance(), null)); + } + + @Override + public LocationalSoundPacket toLocationalSoundPacket(Position position) { + if (position instanceof PositionImpl) { + return new LocationalSoundPacketImpl(new LocationSoundPacket(this.packet.getChannelId(), this.packet.getSender(), ((PositionImpl)position).getPosition(), this.packet.getData(), this.packet.getSequenceNumber(), this.getDistance(), null)); + } + throw new IllegalArgumentException("position is not an instance of PositionImpl"); + } + + private float getDistance() { + if (this instanceof EntitySoundPacket) { + return ((EntitySoundPacket)((Object)this)).getDistance(); + } + if (this instanceof LocationalSoundPacket) { + return ((LocationalSoundPacket)((Object)this)).getDistance(); + } + return Utils.getDefaultDistance(); + } + + @Override + public StaticSoundPacket toStaticSoundPacket() { + return new StaticSoundPacketImpl(new GroupSoundPacket(this.packet.getChannelId(), this.packet.getSender(), this.packet.getData(), this.packet.getSequenceNumber(), null)); + } + + public static abstract class BuilderImpl, P extends de.maxhenkel.voicechat.api.packets.SoundPacket> + implements SoundPacket.Builder { + protected UUID channelId; + protected UUID sender; + protected byte[] opusEncodedData; + protected long sequenceNumber; + @Nullable + protected String category; + + public BuilderImpl(SoundPacketImpl soundPacket) { + this.channelId = soundPacket.getChannelId(); + this.sender = soundPacket.getSender(); + this.opusEncodedData = soundPacket.getOpusEncodedData(); + this.sequenceNumber = soundPacket.getSequenceNumber(); + this.category = soundPacket.getCategory(); + } + + public BuilderImpl(UUID channelId, UUID sender, byte[] opusEncodedData, long sequenceNumber, @Nullable String category) { + this.channelId = channelId; + this.sender = sender; + this.opusEncodedData = opusEncodedData; + this.sequenceNumber = sequenceNumber; + this.category = category; + } + + @Override + public T channelId(UUID channelId) { + if (channelId == null) { + throw new IllegalArgumentException("channelId can't be null"); + } + this.channelId = channelId; + return (T)this; + } + + @Override + public T sender(UUID sender) { + this.sender = sender; + return (T)this; + } + + @Override + public T opusEncodedData(byte[] data) { + this.opusEncodedData = data; + return (T)this; + } + + @Override + public T sequenceNumber(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + return (T)this; + } + + @Override + public T category(@Nullable String category) { + this.category = category; + return (T)this; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/StaticSoundPacketImpl.java b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/StaticSoundPacketImpl.java new file mode 100644 index 0000000..efa1541 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/plugins/impl/packets/StaticSoundPacketImpl.java @@ -0,0 +1,39 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.plugins.impl.packets; + +import de.maxhenkel.voicechat.api.packets.StaticSoundPacket; +import de.maxhenkel.voicechat.plugins.impl.packets.SoundPacketImpl; +import de.maxhenkel.voicechat.voice.common.GroupSoundPacket; +import java.util.UUID; +import javax.annotation.Nullable; + +public class StaticSoundPacketImpl +extends SoundPacketImpl +implements StaticSoundPacket { + public StaticSoundPacketImpl(GroupSoundPacket packet) { + super(packet); + } + + public static class BuilderImpl + extends SoundPacketImpl.BuilderImpl + implements StaticSoundPacket.Builder { + public BuilderImpl(SoundPacketImpl soundPacket) { + super(soundPacket); + } + + public BuilderImpl(UUID channelId, UUID sender, byte[] opusEncodedData, long sequenceNumber, @Nullable String category) { + super(channelId, sender, opusEncodedData, sequenceNumber, category); + } + + @Override + public StaticSoundPacket build() { + return new StaticSoundPacketImpl(new GroupSoundPacket(this.channelId, this.sender, this.opusEncodedData, this.sequenceNumber, this.category)); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/util/FriendlyByteBuf.java b/source/decompiled/de/maxhenkel/voicechat/util/FriendlyByteBuf.java new file mode 100644 index 0000000..e44d97b --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/util/FriendlyByteBuf.java @@ -0,0 +1,866 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * io.netty.buffer.ByteBuf + * io.netty.buffer.ByteBufAllocator + * io.netty.buffer.Unpooled + * io.netty.handler.codec.DecoderException + * io.netty.handler.codec.EncoderException + * io.netty.util.ByteProcessor + */ +package de.maxhenkel.voicechat.util; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.DecoderException; +import io.netty.handler.codec.EncoderException; +import io.netty.util.ByteProcessor; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.FileChannel; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +public class FriendlyByteBuf +extends ByteBuf { + private final ByteBuf buf; + + public FriendlyByteBuf(ByteBuf byteBuf) { + this.buf = byteBuf; + } + + public FriendlyByteBuf() { + this(Unpooled.buffer()); + } + + public ByteBuf getUnderlyingByteBuf() { + return this.buf; + } + + public FriendlyByteBuf writeByteArray(byte[] bs) { + this.writeVarInt(bs.length); + this.writeBytes(bs); + return this; + } + + public byte[] readByteArray() { + return this.readByteArray(this.readableBytes()); + } + + public byte[] readByteArray(int i) { + int j = this.readVarInt(); + if (j > i) { + throw new DecoderException("ByteArray with size " + j + " is bigger than allowed " + i); + } + byte[] bs = new byte[j]; + this.readBytes(bs); + return bs; + } + + public int readVarInt() { + byte b; + int i = 0; + int j = 0; + do { + b = this.readByte(); + i |= (b & 0x7F) << j++ * 7; + if (j <= 5) continue; + throw new RuntimeException("VarInt too big"); + } while ((b & 0x80) == 128); + return i; + } + + public FriendlyByteBuf writeUUID(UUID uUID) { + this.writeLong(uUID.getMostSignificantBits()); + this.writeLong(uUID.getLeastSignificantBits()); + return this; + } + + public UUID readUUID() { + return new UUID(this.readLong(), this.readLong()); + } + + public FriendlyByteBuf writeVarInt(int i) { + while ((i & 0xFFFFFF80) != 0) { + this.writeByte(i & 0x7F | 0x80); + i >>>= 7; + } + this.writeByte(i); + return this; + } + + public String readUtf(int i) { + int j = this.readVarInt(); + if (j > i * 4) { + throw new DecoderException("The received encoded string buffer length is longer than maximum allowed (" + j + " > " + i * 4 + ')'); + } + if (j < 0) { + throw new DecoderException("The received encoded string buffer length is less than zero! Weird string!"); + } + String string = this.toString(this.readerIndex(), j, StandardCharsets.UTF_8); + this.readerIndex(this.readerIndex() + j); + if (string.length() > i) { + throw new DecoderException("The received string length is longer than maximum allowed (" + j + " > " + i + ')'); + } + return string; + } + + public FriendlyByteBuf writeUtf(String string, int i) { + byte[] bs = string.getBytes(StandardCharsets.UTF_8); + if (bs.length > i) { + throw new EncoderException("String too big (was " + bs.length + " bytes encoded, max " + i + ')'); + } + this.writeVarInt(bs.length); + this.writeBytes(bs); + return this; + } + + public String readUtf() { + return this.readUtf(Short.MAX_VALUE); + } + + public FriendlyByteBuf writeUtf(String string) { + return this.writeUtf(string, Short.MAX_VALUE); + } + + public int capacity() { + return this.buf.capacity(); + } + + public ByteBuf capacity(int i) { + return this.buf.capacity(i); + } + + public int maxCapacity() { + return this.buf.maxCapacity(); + } + + public ByteBufAllocator alloc() { + return this.buf.alloc(); + } + + public ByteOrder order() { + return this.buf.order(); + } + + public ByteBuf order(ByteOrder byteOrder) { + return this.buf.order(byteOrder); + } + + public ByteBuf unwrap() { + return this.buf.unwrap(); + } + + public boolean isDirect() { + return this.buf.isDirect(); + } + + public boolean isReadOnly() { + return this.buf.isReadOnly(); + } + + public ByteBuf asReadOnly() { + return this.buf.asReadOnly(); + } + + public int readerIndex() { + return this.buf.readerIndex(); + } + + public ByteBuf readerIndex(int i) { + return this.buf.readerIndex(i); + } + + public int writerIndex() { + return this.buf.writerIndex(); + } + + public ByteBuf writerIndex(int i) { + return this.buf.writerIndex(i); + } + + public ByteBuf setIndex(int i, int j) { + return this.buf.setIndex(i, j); + } + + public int readableBytes() { + return this.buf.readableBytes(); + } + + public int writableBytes() { + return this.buf.writableBytes(); + } + + public int maxWritableBytes() { + return this.buf.maxWritableBytes(); + } + + public boolean isReadable() { + return this.buf.isReadable(); + } + + public boolean isReadable(int i) { + return this.buf.isReadable(i); + } + + public boolean isWritable() { + return this.buf.isWritable(); + } + + public boolean isWritable(int i) { + return this.buf.isWritable(i); + } + + public ByteBuf clear() { + return this.buf.clear(); + } + + public ByteBuf markReaderIndex() { + return this.buf.markReaderIndex(); + } + + public ByteBuf resetReaderIndex() { + return this.buf.resetReaderIndex(); + } + + public ByteBuf markWriterIndex() { + return this.buf.markWriterIndex(); + } + + public ByteBuf resetWriterIndex() { + return this.buf.resetWriterIndex(); + } + + public ByteBuf discardReadBytes() { + return this.buf.discardReadBytes(); + } + + public ByteBuf discardSomeReadBytes() { + return this.buf.discardSomeReadBytes(); + } + + public ByteBuf ensureWritable(int i) { + return this.buf.ensureWritable(i); + } + + public int ensureWritable(int i, boolean bl) { + return this.buf.ensureWritable(i, bl); + } + + public boolean getBoolean(int i) { + return this.buf.getBoolean(i); + } + + public byte getByte(int i) { + return this.buf.getByte(i); + } + + public short getUnsignedByte(int i) { + return this.buf.getUnsignedByte(i); + } + + public short getShort(int i) { + return this.buf.getShort(i); + } + + public short getShortLE(int i) { + return this.buf.getShortLE(i); + } + + public int getUnsignedShort(int i) { + return this.buf.getUnsignedShort(i); + } + + public int getUnsignedShortLE(int i) { + return this.buf.getUnsignedShortLE(i); + } + + public int getMedium(int i) { + return this.buf.getMedium(i); + } + + public int getMediumLE(int i) { + return this.buf.getMediumLE(i); + } + + public int getUnsignedMedium(int i) { + return this.buf.getUnsignedMedium(i); + } + + public int getUnsignedMediumLE(int i) { + return this.buf.getUnsignedMediumLE(i); + } + + public int getInt(int i) { + return this.buf.getInt(i); + } + + public int getIntLE(int i) { + return this.buf.getIntLE(i); + } + + public long getUnsignedInt(int i) { + return this.buf.getUnsignedInt(i); + } + + public long getUnsignedIntLE(int i) { + return this.buf.getUnsignedIntLE(i); + } + + public long getLong(int i) { + return this.buf.getLong(i); + } + + public long getLongLE(int i) { + return this.buf.getLongLE(i); + } + + public char getChar(int i) { + return this.buf.getChar(i); + } + + public float getFloat(int i) { + return this.buf.getFloat(i); + } + + public double getDouble(int i) { + return this.buf.getDouble(i); + } + + public ByteBuf getBytes(int i, ByteBuf byteBuf) { + return this.buf.getBytes(i, byteBuf); + } + + public ByteBuf getBytes(int i, ByteBuf byteBuf, int j) { + return this.buf.getBytes(i, byteBuf, j); + } + + public ByteBuf getBytes(int i, ByteBuf byteBuf, int j, int k) { + return this.buf.getBytes(i, byteBuf, j, k); + } + + public ByteBuf getBytes(int i, byte[] bs) { + return this.buf.getBytes(i, bs); + } + + public ByteBuf getBytes(int i, byte[] bs, int j, int k) { + return this.buf.getBytes(i, bs, j, k); + } + + public ByteBuf getBytes(int i, ByteBuffer byteBuffer) { + return this.buf.getBytes(i, byteBuffer); + } + + public ByteBuf getBytes(int i, OutputStream outputStream, int j) throws IOException { + return this.buf.getBytes(i, outputStream, j); + } + + public int getBytes(int i, GatheringByteChannel gatheringByteChannel, int j) throws IOException { + return this.buf.getBytes(i, gatheringByteChannel, j); + } + + public int getBytes(int i, FileChannel fileChannel, long l, int j) throws IOException { + return this.buf.getBytes(i, fileChannel, l, j); + } + + public CharSequence getCharSequence(int i, int j, Charset charset) { + return this.buf.getCharSequence(i, j, charset); + } + + public ByteBuf setBoolean(int i, boolean bl) { + return this.buf.setBoolean(i, bl); + } + + public ByteBuf setByte(int i, int j) { + return this.buf.setByte(i, j); + } + + public ByteBuf setShort(int i, int j) { + return this.buf.setShort(i, j); + } + + public ByteBuf setShortLE(int i, int j) { + return this.buf.setShortLE(i, j); + } + + public ByteBuf setMedium(int i, int j) { + return this.buf.setMedium(i, j); + } + + public ByteBuf setMediumLE(int i, int j) { + return this.buf.setMediumLE(i, j); + } + + public ByteBuf setInt(int i, int j) { + return this.buf.setInt(i, j); + } + + public ByteBuf setIntLE(int i, int j) { + return this.buf.setIntLE(i, j); + } + + public ByteBuf setLong(int i, long l) { + return this.buf.setLong(i, l); + } + + public ByteBuf setLongLE(int i, long l) { + return this.buf.setLongLE(i, l); + } + + public ByteBuf setChar(int i, int j) { + return this.buf.setChar(i, j); + } + + public ByteBuf setFloat(int i, float f) { + return this.buf.setFloat(i, f); + } + + public ByteBuf setDouble(int i, double d) { + return this.buf.setDouble(i, d); + } + + public ByteBuf setBytes(int i, ByteBuf byteBuf) { + return this.buf.setBytes(i, byteBuf); + } + + public ByteBuf setBytes(int i, ByteBuf byteBuf, int j) { + return this.buf.setBytes(i, byteBuf, j); + } + + public ByteBuf setBytes(int i, ByteBuf byteBuf, int j, int k) { + return this.buf.setBytes(i, byteBuf, j, k); + } + + public ByteBuf setBytes(int i, byte[] bs) { + return this.buf.setBytes(i, bs); + } + + public ByteBuf setBytes(int i, byte[] bs, int j, int k) { + return this.buf.setBytes(i, bs, j, k); + } + + public ByteBuf setBytes(int i, ByteBuffer byteBuffer) { + return this.buf.setBytes(i, byteBuffer); + } + + public int setBytes(int i, InputStream inputStream, int j) throws IOException { + return this.buf.setBytes(i, inputStream, j); + } + + public int setBytes(int i, ScatteringByteChannel scatteringByteChannel, int j) throws IOException { + return this.buf.setBytes(i, scatteringByteChannel, j); + } + + public int setBytes(int i, FileChannel fileChannel, long l, int j) throws IOException { + return this.buf.setBytes(i, fileChannel, l, j); + } + + public ByteBuf setZero(int i, int j) { + return this.buf.setZero(i, j); + } + + public int setCharSequence(int i, CharSequence charSequence, Charset charset) { + return this.buf.setCharSequence(i, charSequence, charset); + } + + public boolean readBoolean() { + return this.buf.readBoolean(); + } + + public byte readByte() { + return this.buf.readByte(); + } + + public short readUnsignedByte() { + return this.buf.readUnsignedByte(); + } + + public short readShort() { + return this.buf.readShort(); + } + + public short readShortLE() { + return this.buf.readShortLE(); + } + + public int readUnsignedShort() { + return this.buf.readUnsignedShort(); + } + + public int readUnsignedShortLE() { + return this.buf.readUnsignedShortLE(); + } + + public int readMedium() { + return this.buf.readMedium(); + } + + public int readMediumLE() { + return this.buf.readMediumLE(); + } + + public int readUnsignedMedium() { + return this.buf.readUnsignedMedium(); + } + + public int readUnsignedMediumLE() { + return this.buf.readUnsignedMediumLE(); + } + + public int readInt() { + return this.buf.readInt(); + } + + public int readIntLE() { + return this.buf.readIntLE(); + } + + public long readUnsignedInt() { + return this.buf.readUnsignedInt(); + } + + public long readUnsignedIntLE() { + return this.buf.readUnsignedIntLE(); + } + + public long readLong() { + return this.buf.readLong(); + } + + public long readLongLE() { + return this.buf.readLongLE(); + } + + public char readChar() { + return this.buf.readChar(); + } + + public float readFloat() { + return this.buf.readFloat(); + } + + public double readDouble() { + return this.buf.readDouble(); + } + + public ByteBuf readBytes(int i) { + return this.buf.readBytes(i); + } + + public ByteBuf readSlice(int i) { + return this.buf.readSlice(i); + } + + public ByteBuf readRetainedSlice(int i) { + return this.buf.readRetainedSlice(i); + } + + public ByteBuf readBytes(ByteBuf byteBuf) { + return this.buf.readBytes(byteBuf); + } + + public ByteBuf readBytes(ByteBuf byteBuf, int i) { + return this.buf.readBytes(byteBuf, i); + } + + public ByteBuf readBytes(ByteBuf byteBuf, int i, int j) { + return this.buf.readBytes(byteBuf, i, j); + } + + public ByteBuf readBytes(byte[] bs) { + return this.buf.readBytes(bs); + } + + public ByteBuf readBytes(byte[] bs, int i, int j) { + return this.buf.readBytes(bs, i, j); + } + + public ByteBuf readBytes(ByteBuffer byteBuffer) { + return this.buf.readBytes(byteBuffer); + } + + public ByteBuf readBytes(OutputStream outputStream, int i) throws IOException { + return this.buf.readBytes(outputStream, i); + } + + public int readBytes(GatheringByteChannel gatheringByteChannel, int i) throws IOException { + return this.buf.readBytes(gatheringByteChannel, i); + } + + public CharSequence readCharSequence(int i, Charset charset) { + return this.buf.readCharSequence(i, charset); + } + + public int readBytes(FileChannel fileChannel, long l, int i) throws IOException { + return this.buf.readBytes(fileChannel, l, i); + } + + public ByteBuf skipBytes(int i) { + return this.buf.skipBytes(i); + } + + public ByteBuf writeBoolean(boolean bl) { + return this.buf.writeBoolean(bl); + } + + public ByteBuf writeByte(int i) { + return this.buf.writeByte(i); + } + + public ByteBuf writeShort(int i) { + return this.buf.writeShort(i); + } + + public ByteBuf writeShortLE(int i) { + return this.buf.writeShortLE(i); + } + + public ByteBuf writeMedium(int i) { + return this.buf.writeMedium(i); + } + + public ByteBuf writeMediumLE(int i) { + return this.buf.writeMediumLE(i); + } + + public ByteBuf writeInt(int i) { + return this.buf.writeInt(i); + } + + public ByteBuf writeIntLE(int i) { + return this.buf.writeIntLE(i); + } + + public ByteBuf writeLong(long l) { + return this.buf.writeLong(l); + } + + public ByteBuf writeLongLE(long l) { + return this.buf.writeLongLE(l); + } + + public ByteBuf writeChar(int i) { + return this.buf.writeChar(i); + } + + public ByteBuf writeFloat(float f) { + return this.buf.writeFloat(f); + } + + public ByteBuf writeDouble(double d) { + return this.buf.writeDouble(d); + } + + public ByteBuf writeBytes(ByteBuf byteBuf) { + return this.buf.writeBytes(byteBuf); + } + + public ByteBuf writeBytes(ByteBuf byteBuf, int i) { + return this.buf.writeBytes(byteBuf, i); + } + + public ByteBuf writeBytes(ByteBuf byteBuf, int i, int j) { + return this.buf.writeBytes(byteBuf, i, j); + } + + public ByteBuf writeBytes(byte[] bs) { + return this.buf.writeBytes(bs); + } + + public ByteBuf writeBytes(byte[] bs, int i, int j) { + return this.buf.writeBytes(bs, i, j); + } + + public ByteBuf writeBytes(ByteBuffer byteBuffer) { + return this.buf.writeBytes(byteBuffer); + } + + public int writeBytes(InputStream inputStream, int i) throws IOException { + return this.buf.writeBytes(inputStream, i); + } + + public int writeBytes(ScatteringByteChannel scatteringByteChannel, int i) throws IOException { + return this.buf.writeBytes(scatteringByteChannel, i); + } + + public int writeBytes(FileChannel fileChannel, long l, int i) throws IOException { + return this.buf.writeBytes(fileChannel, l, i); + } + + public ByteBuf writeZero(int i) { + return this.buf.writeZero(i); + } + + public int writeCharSequence(CharSequence charSequence, Charset charset) { + return this.buf.writeCharSequence(charSequence, charset); + } + + public int indexOf(int i, int j, byte b) { + return this.buf.indexOf(i, j, b); + } + + public int bytesBefore(byte b) { + return this.buf.bytesBefore(b); + } + + public int bytesBefore(int i, byte b) { + return this.buf.bytesBefore(i, b); + } + + public int bytesBefore(int i, int j, byte b) { + return this.buf.bytesBefore(i, j, b); + } + + public int forEachByte(ByteProcessor byteProcessor) { + return this.buf.forEachByte(byteProcessor); + } + + public int forEachByte(int i, int j, ByteProcessor byteProcessor) { + return this.buf.forEachByte(i, j, byteProcessor); + } + + public int forEachByteDesc(ByteProcessor byteProcessor) { + return this.buf.forEachByteDesc(byteProcessor); + } + + public int forEachByteDesc(int i, int j, ByteProcessor byteProcessor) { + return this.buf.forEachByteDesc(i, j, byteProcessor); + } + + public ByteBuf copy() { + return this.buf.copy(); + } + + public ByteBuf copy(int i, int j) { + return this.buf.copy(i, j); + } + + public ByteBuf slice() { + return this.buf.slice(); + } + + public ByteBuf retainedSlice() { + return this.buf.retainedSlice(); + } + + public ByteBuf slice(int i, int j) { + return this.buf.slice(i, j); + } + + public ByteBuf retainedSlice(int i, int j) { + return this.buf.retainedSlice(i, j); + } + + public ByteBuf duplicate() { + return this.buf.duplicate(); + } + + public ByteBuf retainedDuplicate() { + return this.buf.retainedDuplicate(); + } + + public int nioBufferCount() { + return this.buf.nioBufferCount(); + } + + public ByteBuffer nioBuffer() { + return this.buf.nioBuffer(); + } + + public ByteBuffer nioBuffer(int i, int j) { + return this.buf.nioBuffer(i, j); + } + + public ByteBuffer internalNioBuffer(int i, int j) { + return this.buf.internalNioBuffer(i, j); + } + + public ByteBuffer[] nioBuffers() { + return this.buf.nioBuffers(); + } + + public ByteBuffer[] nioBuffers(int i, int j) { + return this.buf.nioBuffers(i, j); + } + + public boolean hasArray() { + return this.buf.hasArray(); + } + + public byte[] array() { + return this.buf.array(); + } + + public int arrayOffset() { + return this.buf.arrayOffset(); + } + + public boolean hasMemoryAddress() { + return this.buf.hasMemoryAddress(); + } + + public long memoryAddress() { + return this.buf.memoryAddress(); + } + + public String toString(Charset charset) { + return this.buf.toString(charset); + } + + public String toString(int i, int j, Charset charset) { + return this.buf.toString(i, j, charset); + } + + public int hashCode() { + return this.buf.hashCode(); + } + + public boolean equals(Object object) { + return this.buf.equals(object); + } + + public int compareTo(ByteBuf byteBuf) { + return this.buf.compareTo(byteBuf); + } + + public String toString() { + return this.buf.toString(); + } + + public ByteBuf retain(int i) { + return this.buf.retain(i); + } + + public ByteBuf retain() { + return this.buf.retain(); + } + + public ByteBuf touch() { + return this.buf.touch(); + } + + public ByteBuf touch(Object object) { + return this.buf.touch(object); + } + + public int refCnt() { + return this.buf.refCnt(); + } + + public boolean release() { + return this.buf.release(); + } + + public boolean release(int i) { + return this.buf.release(i); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/util/Key.java b/source/decompiled/de/maxhenkel/voicechat/util/Key.java new file mode 100644 index 0000000..d77a3e0 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/util/Key.java @@ -0,0 +1,43 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.util; + +public class Key { + private final String namespace; + private final String value; + + public Key(String namespace, String value) { + this.namespace = namespace; + this.value = value; + } + + public static Key of(String namespace, String value) { + return new Key(namespace, value); + } + + public static Key of(String value) { + return new Key("voicechat", value); + } + + public static Key parse(String value) { + String[] split = value.split(":"); + if (split.length != 2) { + throw new IllegalArgumentException(String.format("Invalid key: %s", value)); + } + return new Key(split[0], split[1]); + } + + public String getNamespace() { + return this.namespace; + } + + public String getValue() { + return this.value; + } + + public String toString() { + return String.format("%s:%s", this.namespace, this.value); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/AudioUtils.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/AudioUtils.java new file mode 100644 index 0000000..6e6e3fe --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/AudioUtils.java @@ -0,0 +1,65 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.ShortBuffer; + +public class AudioUtils { + public static final int MAX_OPUS_PAYLOAD_SIZE = 1275; + + public static short[] bytesToShorts(byte[] bytes) { + if (bytes.length % 2 != 0) { + throw new IllegalArgumentException("Input bytes need to be divisible by 2"); + } + ShortBuffer sb = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer(); + short[] out = new short[sb.remaining()]; + sb.get(out); + return out; + } + + public static byte[] shortsToBytes(short[] shorts) { + ByteBuffer bb = ByteBuffer.allocate(shorts.length * 2).order(ByteOrder.LITTLE_ENDIAN); + for (short s : shorts) { + bb.putShort(s); + } + return bb.array(); + } + + public static short[] floatsToShorts(float[] floats) { + short[] shorts = new short[floats.length]; + for (int i = 0; i < floats.length; ++i) { + shorts[i] = Float.valueOf(floats[i]).shortValue(); + } + return shorts; + } + + public static float[] shortsToFloats(short[] shorts) { + float[] floats = new float[shorts.length]; + for (int i = 0; i < shorts.length; ++i) { + floats[i] = Short.valueOf(shorts[i]).floatValue(); + } + return floats; + } + + public static byte[] floatsToBytes(float[] floats) { + byte[] bytes = new byte[floats.length * 2]; + for (int i = 0; i < floats.length; ++i) { + short x = Float.valueOf(floats[i]).shortValue(); + bytes[i * 2] = (byte)(x & 0xFF); + bytes[i * 2 + 1] = (byte)((x & 0xFF00) >> 8); + } + return bytes; + } + + public static float[] bytesToFloats(byte[] bytes) { + float[] floats = new float[bytes.length / 2]; + for (int i = 0; i < bytes.length / 2; ++i) { + floats[i] = (bytes[i * 2 + 1] & 0x80) != 0 ? (float)(Short.MIN_VALUE + ((bytes[i * 2 + 1] & 0x7F) << 8) | bytes[i * 2] & 0xFF) : (float)(bytes[i * 2 + 1] << 8 & 0xFF00 | bytes[i * 2] & 0xFF); + } + return floats; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/AuthenticateAckPacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/AuthenticateAckPacket.java new file mode 100644 index 0000000..84130fb --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/AuthenticateAckPacket.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.Packet; + +public class AuthenticateAckPacket +implements Packet { + @Override + public AuthenticateAckPacket fromBytes(FriendlyByteBuf buf) { + return new AuthenticateAckPacket(); + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/AuthenticatePacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/AuthenticatePacket.java new file mode 100644 index 0000000..aaa9d50 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/AuthenticatePacket.java @@ -0,0 +1,46 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.Packet; +import de.maxhenkel.voicechat.voice.common.Secret; +import java.util.UUID; + +public class AuthenticatePacket +implements Packet { + private UUID playerUUID; + private Secret secret; + + public AuthenticatePacket(UUID playerUUID, Secret secret) { + this.playerUUID = playerUUID; + this.secret = secret; + } + + public AuthenticatePacket() { + } + + public UUID getPlayerUUID() { + return this.playerUUID; + } + + public Secret getSecret() { + return this.secret; + } + + @Override + public AuthenticatePacket fromBytes(FriendlyByteBuf buf) { + AuthenticatePacket packet = new AuthenticatePacket(); + packet.playerUUID = buf.readUUID(); + packet.secret = Secret.fromBytes(buf); + return packet; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(this.playerUUID); + this.secret.toBytes(buf); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/ClientGroup.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/ClientGroup.java new file mode 100644 index 0000000..73fbb25 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/ClientGroup.java @@ -0,0 +1,77 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.plugins.impl.GroupImpl; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import java.util.Objects; +import java.util.UUID; + +public class ClientGroup { + private final UUID id; + private final String name; + private final boolean hasPassword; + private final boolean persistent; + private final boolean hidden; + private final Group.Type type; + + public ClientGroup(UUID id, String name, boolean hasPassword, boolean persistent, boolean hidden, Group.Type type) { + this.id = id; + this.name = name; + this.hasPassword = hasPassword; + this.persistent = persistent; + this.hidden = hidden; + this.type = type; + } + + public UUID getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + public boolean hasPassword() { + return this.hasPassword; + } + + public boolean isPersistent() { + return this.persistent; + } + + public boolean isHidden() { + return this.hidden; + } + + public Group.Type getType() { + return this.type; + } + + public static ClientGroup fromBytes(FriendlyByteBuf buf) { + return new ClientGroup(buf.readUUID(), buf.readUtf(512), buf.readBoolean(), buf.readBoolean(), buf.readBoolean(), GroupImpl.TypeImpl.fromInt(buf.readShort())); + } + + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(this.id); + buf.writeUtf(this.name, 512); + buf.writeBoolean(this.hasPassword); + buf.writeBoolean(this.persistent); + buf.writeBoolean(this.hidden); + buf.writeShort(GroupImpl.TypeImpl.toInt(this.type)); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + ClientGroup group = (ClientGroup)o; + return Objects.equals(this.id, group.id); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/ConnectionCheckAckPacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/ConnectionCheckAckPacket.java new file mode 100644 index 0000000..ba645a7 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/ConnectionCheckAckPacket.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.Packet; + +public class ConnectionCheckAckPacket +implements Packet { + @Override + public ConnectionCheckAckPacket fromBytes(FriendlyByteBuf buf) { + return new ConnectionCheckAckPacket(); + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/ConnectionCheckPacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/ConnectionCheckPacket.java new file mode 100644 index 0000000..dc2aa61 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/ConnectionCheckPacket.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.Packet; + +public class ConnectionCheckPacket +implements Packet { + @Override + public ConnectionCheckPacket fromBytes(FriendlyByteBuf buf) { + return new ConnectionCheckPacket(); + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/GroupSoundPacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/GroupSoundPacket.java new file mode 100644 index 0000000..1aa2505 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/GroupSoundPacket.java @@ -0,0 +1,53 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.SoundPacket; +import java.util.UUID; +import javax.annotation.Nullable; + +public class GroupSoundPacket +extends SoundPacket { + public GroupSoundPacket(UUID channelId, UUID sender, byte[] data, long sequenceNumber, @Nullable String category) { + super(channelId, sender, data, sequenceNumber, category); + } + + public GroupSoundPacket() { + } + + @Override + public GroupSoundPacket fromBytes(FriendlyByteBuf buf) { + GroupSoundPacket soundPacket = new GroupSoundPacket(); + soundPacket.channelId = buf.readUUID(); + soundPacket.sender = buf.readUUID(); + soundPacket.data = buf.readByteArray(1275); + soundPacket.sequenceNumber = buf.readLong(); + byte data = buf.readByte(); + if (this.hasFlag(data, (byte)2)) { + soundPacket.category = buf.readUtf(16); + } + return soundPacket; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(this.channelId); + buf.writeUUID(this.sender); + buf.writeByteArray(this.data); + buf.writeLong(this.sequenceNumber); + byte data = 0; + if (this.category != null) { + data = this.setFlag(data, (byte)2); + } + buf.writeByte(data); + if (this.category != null) { + buf.writeUtf(this.category, 16); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/KeepAlivePacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/KeepAlivePacket.java new file mode 100644 index 0000000..5cadbd2 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/KeepAlivePacket.java @@ -0,0 +1,20 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.Packet; + +public class KeepAlivePacket +implements Packet { + @Override + public KeepAlivePacket fromBytes(FriendlyByteBuf buf) { + return new KeepAlivePacket(); + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/LocationSoundPacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/LocationSoundPacket.java new file mode 100644 index 0000000..8054941 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/LocationSoundPacket.java @@ -0,0 +1,74 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.Location + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.SoundPacket; +import java.util.UUID; +import javax.annotation.Nullable; +import org.bukkit.Location; + +public class LocationSoundPacket +extends SoundPacket { + protected Location location; + protected float distance; + + public LocationSoundPacket(UUID channelId, UUID sender, Location location, byte[] data, long sequenceNumber, float distance, @Nullable String category) { + super(channelId, sender, data, sequenceNumber, category); + this.location = location; + this.distance = distance; + } + + public LocationSoundPacket() { + } + + public Location getLocation() { + return this.location; + } + + public float getDistance() { + return this.distance; + } + + @Override + public LocationSoundPacket fromBytes(FriendlyByteBuf buf) { + LocationSoundPacket soundPacket = new LocationSoundPacket(); + soundPacket.channelId = buf.readUUID(); + soundPacket.sender = buf.readUUID(); + soundPacket.location = new Location(null, buf.readDouble(), buf.readDouble(), buf.readDouble()); + soundPacket.data = buf.readByteArray(1275); + soundPacket.sequenceNumber = buf.readLong(); + soundPacket.distance = buf.readFloat(); + byte data = buf.readByte(); + if (this.hasFlag(data, (byte)2)) { + soundPacket.category = buf.readUtf(16); + } + return soundPacket; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(this.channelId); + buf.writeUUID(this.sender); + buf.writeDouble(this.location.getX()); + buf.writeDouble(this.location.getY()); + buf.writeDouble(this.location.getZ()); + buf.writeByteArray(this.data); + buf.writeLong(this.sequenceNumber); + buf.writeFloat(this.distance); + byte data = 0; + if (this.category != null) { + data = this.setFlag(data, (byte)2); + } + buf.writeByte(data); + if (this.category != null) { + buf.writeUtf(this.category, 16); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/MicPacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/MicPacket.java new file mode 100644 index 0000000..43e4096 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/MicPacket.java @@ -0,0 +1,61 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.Packet; + +public class MicPacket +implements Packet { + private byte[] data; + private boolean whispering; + private long sequenceNumber; + + public MicPacket(byte[] data, boolean whispering, long sequenceNumber) { + this.data = data; + this.whispering = whispering; + this.sequenceNumber = sequenceNumber; + } + + public MicPacket() { + } + + @Override + public long getTTL() { + return 500L; + } + + public byte[] getData() { + return this.data; + } + + public void setData(byte[] data) { + this.data = data; + } + + public long getSequenceNumber() { + return this.sequenceNumber; + } + + public boolean isWhispering() { + return this.whispering; + } + + @Override + public MicPacket fromBytes(FriendlyByteBuf buf) { + MicPacket soundPacket = new MicPacket(); + soundPacket.data = buf.readByteArray(1275); + soundPacket.sequenceNumber = buf.readLong(); + soundPacket.whispering = buf.readBoolean(); + return soundPacket; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeByteArray(this.data); + buf.writeLong(this.sequenceNumber); + buf.writeBoolean(this.whispering); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/NetworkMessage.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/NetworkMessage.java new file mode 100644 index 0000000..78537a8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/NetworkMessage.java @@ -0,0 +1,175 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * io.netty.buffer.Unpooled + * io.netty.handler.codec.DecoderException + * javax.annotation.Nonnull + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.RawUdpPacket; +import de.maxhenkel.voicechat.debug.PingHandler; +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.AuthenticateAckPacket; +import de.maxhenkel.voicechat.voice.common.AuthenticatePacket; +import de.maxhenkel.voicechat.voice.common.ConnectionCheckAckPacket; +import de.maxhenkel.voicechat.voice.common.ConnectionCheckPacket; +import de.maxhenkel.voicechat.voice.common.GroupSoundPacket; +import de.maxhenkel.voicechat.voice.common.KeepAlivePacket; +import de.maxhenkel.voicechat.voice.common.LocationSoundPacket; +import de.maxhenkel.voicechat.voice.common.MicPacket; +import de.maxhenkel.voicechat.voice.common.Packet; +import de.maxhenkel.voicechat.voice.common.PingPacket; +import de.maxhenkel.voicechat.voice.common.PlayerSoundPacket; +import de.maxhenkel.voicechat.voice.common.Secret; +import de.maxhenkel.voicechat.voice.server.ClientConnection; +import de.maxhenkel.voicechat.voice.server.Server; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.DecoderException; +import java.lang.reflect.InvocationTargetException; +import java.net.SocketAddress; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +public class NetworkMessage { + public static final byte MAGIC_BYTE = -1; + private final long timestamp; + private Packet packet; + private SocketAddress address; + private static final Map> packetRegistry = new HashMap>(); + + public NetworkMessage(long timestamp, Packet packet) { + this(timestamp); + this.packet = packet; + } + + public NetworkMessage(Packet packet) { + this(System.currentTimeMillis()); + this.packet = packet; + } + + private NetworkMessage(long timestamp) { + this.timestamp = timestamp; + } + + @Nonnull + public Packet getPacket() { + return this.packet; + } + + public long getTimestamp() { + return this.timestamp; + } + + public long getTTL() { + return this.packet.getTTL(); + } + + public SocketAddress getAddress() { + return this.address; + } + + @Nullable + public static NetworkMessage readPacketServer(RawUdpPacket packet, Server server) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { + try { + byte[] data = packet.getData(); + FriendlyByteBuf b = new FriendlyByteBuf(Unpooled.wrappedBuffer((byte[])data)); + if (b.readByte() != -1) { + Voicechat.LOGGER.debug("Received invalid packet from {}", packet.getSocketAddress()); + return null; + } + UUID playerID = b.readUUID(); + if (!server.hasSecret(playerID)) { + if (PingHandler.onPacket(server, packet.getSocketAddress(), playerID, b)) { + return null; + } + Voicechat.LOGGER.debug("Player {} does not have a secret", playerID); + return null; + } + return NetworkMessage.readFromBytes(packet.getSocketAddress(), server.getSecret(playerID), b.readByteArray(2048), packet.getTimestamp()); + } + catch (DecoderException | IndexOutOfBoundsException e) { + Voicechat.LOGGER.debug("Received invalid packet from {}", packet.getSocketAddress()); + return null; + } + } + + @Nullable + private static NetworkMessage readFromBytes(SocketAddress socketAddress, Secret secret, byte[] encryptedPayload, long timestamp) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + byte[] decrypt; + try { + decrypt = secret.decrypt(encryptedPayload); + } + catch (Exception e) { + Voicechat.LOGGER.debug("Failed to decrypt packet from {}", socketAddress); + return null; + } + FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.wrappedBuffer((byte[])decrypt)); + byte packetType = buffer.readByte(); + Class packetClass = packetRegistry.get(packetType); + if (packetClass == null) { + Voicechat.LOGGER.debug("Got invalid packet ID {}", packetType); + return null; + } + Packet p = packetClass.getDeclaredConstructor(new Class[0]).newInstance(new Object[0]); + NetworkMessage message = new NetworkMessage(timestamp); + message.address = socketAddress; + message.packet = p.fromBytes(buffer); + return message; + } + + private static byte getPacketType(Packet packet) { + for (Map.Entry> entry : packetRegistry.entrySet()) { + if (!packet.getClass().equals(entry.getValue())) continue; + return entry.getKey(); + } + return -1; + } + + public byte[] writeServer(Server server, ClientConnection connection) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + byte[] payload = this.write(server.getSecret(connection.getPlayerUUID())); + FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer((int)(1 + payload.length))); + buffer.writeByte(-1); + buffer.writeByteArray(payload); + byte[] bytes = new byte[buffer.readableBytes()]; + buffer.readBytes(bytes); + return bytes; + } + + public byte[] write(Secret secret) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer()); + byte type = NetworkMessage.getPacketType(this.packet); + if (type < 0) { + throw new IllegalArgumentException("Packet type not found"); + } + buffer.writeByte(type); + this.packet.toBytes(buffer); + return secret.encrypt(buffer.array()); + } + + static { + packetRegistry.put((byte)1, MicPacket.class); + packetRegistry.put((byte)2, PlayerSoundPacket.class); + packetRegistry.put((byte)3, GroupSoundPacket.class); + packetRegistry.put((byte)4, LocationSoundPacket.class); + packetRegistry.put((byte)5, AuthenticatePacket.class); + packetRegistry.put((byte)6, AuthenticateAckPacket.class); + packetRegistry.put((byte)7, PingPacket.class); + packetRegistry.put((byte)8, KeepAlivePacket.class); + packetRegistry.put((byte)9, ConnectionCheckPacket.class); + packetRegistry.put((byte)10, ConnectionCheckAckPacket.class); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/Packet.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/Packet.java new file mode 100644 index 0000000..ec38293 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/Packet.java @@ -0,0 +1,17 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; + +public interface Packet { + public T fromBytes(FriendlyByteBuf var1); + + public void toBytes(FriendlyByteBuf var1); + + default public long getTTL() { + return 10000L; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/PingPacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/PingPacket.java new file mode 100644 index 0000000..35912b8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/PingPacket.java @@ -0,0 +1,45 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.Packet; +import java.util.UUID; + +public class PingPacket +implements Packet { + private UUID id; + private long timestamp; + + public PingPacket(UUID id, long timestamp) { + this.id = id; + this.timestamp = timestamp; + } + + public PingPacket() { + } + + public long getTimestamp() { + return this.timestamp; + } + + public UUID getId() { + return this.id; + } + + @Override + public PingPacket fromBytes(FriendlyByteBuf buf) { + PingPacket soundPacket = new PingPacket(); + soundPacket.id = buf.readUUID(); + soundPacket.timestamp = buf.readLong(); + return soundPacket; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(this.id); + buf.writeLong(this.timestamp); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/PlayerSoundPacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/PlayerSoundPacket.java new file mode 100644 index 0000000..116c09e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/PlayerSoundPacket.java @@ -0,0 +1,77 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import de.maxhenkel.voicechat.voice.common.SoundPacket; +import java.util.UUID; +import javax.annotation.Nullable; + +public class PlayerSoundPacket +extends SoundPacket { + protected boolean whispering; + protected float distance; + + public PlayerSoundPacket(UUID channelId, UUID sender, byte[] data, long sequenceNumber, boolean whispering, float distance, @Nullable String category) { + super(channelId, sender, data, sequenceNumber, category); + this.whispering = whispering; + this.distance = distance; + } + + public PlayerSoundPacket() { + } + + @Override + public UUID getSender() { + return this.sender; + } + + public boolean isWhispering() { + return this.whispering; + } + + public float getDistance() { + return this.distance; + } + + @Override + public PlayerSoundPacket fromBytes(FriendlyByteBuf buf) { + PlayerSoundPacket soundPacket = new PlayerSoundPacket(); + soundPacket.channelId = buf.readUUID(); + soundPacket.sender = buf.readUUID(); + soundPacket.data = buf.readByteArray(1275); + soundPacket.sequenceNumber = buf.readLong(); + soundPacket.distance = buf.readFloat(); + byte data = buf.readByte(); + soundPacket.whispering = this.hasFlag(data, (byte)1); + if (this.hasFlag(data, (byte)2)) { + soundPacket.category = buf.readUtf(16); + } + return soundPacket; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(this.channelId); + buf.writeUUID(this.sender); + buf.writeByteArray(this.data); + buf.writeLong(this.sequenceNumber); + buf.writeFloat(this.distance); + byte data = 0; + if (this.whispering) { + data = this.setFlag(data, (byte)1); + } + if (this.category != null) { + data = this.setFlag(data, (byte)2); + } + buf.writeByte(data); + if (this.category != null) { + buf.writeUtf(this.category, 16); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/PlayerState.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/PlayerState.java new file mode 100644 index 0000000..97ea9b9 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/PlayerState.java @@ -0,0 +1,100 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.util.FriendlyByteBuf; +import java.util.UUID; +import javax.annotation.Nullable; + +public class PlayerState { + private UUID uuid; + private String name; + private boolean disabled; + private boolean disconnected; + @Nullable + private UUID group; + + public PlayerState(UUID uuid, String name, boolean disabled, boolean disconnected) { + this.uuid = uuid; + this.name = name; + this.disabled = disabled; + this.disconnected = disconnected; + } + + public UUID getUuid() { + return this.uuid; + } + + public void setUuid(UUID uuid) { + this.uuid = uuid; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isDisabled() { + return this.disabled; + } + + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + + public boolean isDisconnected() { + return this.disconnected; + } + + public void setDisconnected(boolean disconnected) { + this.disconnected = disconnected; + } + + @Nullable + public UUID getGroup() { + return this.group; + } + + public void setGroup(@Nullable UUID group) { + this.group = group; + } + + public boolean hasGroup() { + return this.group != null; + } + + public String toString() { + return "{disabled=" + this.disabled + ", disconnected=" + this.disconnected + ", uuid=" + this.uuid + ", name=" + this.name + ", group=" + this.group + '}'; + } + + public static PlayerState fromBytes(FriendlyByteBuf buf) { + boolean disabled = buf.readBoolean(); + boolean disconnected = buf.readBoolean(); + UUID uuid = buf.readUUID(); + String name = buf.readUtf(Short.MAX_VALUE); + PlayerState state = new PlayerState(uuid, name, disabled, disconnected); + if (buf.readBoolean()) { + state.setGroup(buf.readUUID()); + } + return state; + } + + public void toBytes(FriendlyByteBuf buf) { + buf.writeBoolean(this.disabled); + buf.writeBoolean(this.disconnected); + buf.writeUUID(this.uuid); + buf.writeUtf(this.name); + buf.writeBoolean(this.hasGroup()); + if (this.hasGroup()) { + buf.writeUUID(this.group); + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/Secret.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/Secret.java new file mode 100644 index 0000000..b030976 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/Secret.java @@ -0,0 +1,140 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * io.netty.buffer.ByteBuf + */ +package de.maxhenkel.voicechat.voice.common; + +import io.netty.buffer.ByteBuf; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class Secret { + public static final int SECRET_SIZE_BYTES = 16; + public static final int IV_SIZE_BYTES = 12; + public static final int TAG_LEN_BITS = 128; + public static final String CIPHER = "AES/GCM/NoPadding"; + private static final SecureRandom RANDOM = new SecureRandom(); + private final byte[] secret; + private final SecretKeySpec keySpec; + private volatile boolean legacyProtocol; + + protected Secret(byte[] byArray) { + if (byArray == null || byArray.length != 16) { + throw new IllegalArgumentException("Voice chat secret must contain exactly 16 bytes"); + } + this.secret = (byte[])byArray.clone(); + this.keySpec = new SecretKeySpec(this.secret, "AES"); + } + + public static Secret generateNewRandomSecret() { + byte[] byArray = new byte[16]; + RANDOM.nextBytes(byArray); + return new Secret(byArray); + } + + public static Secret fromBytes(byte[] byArray) { + return new Secret(byArray); + } + + public static Secret fromBytes(ByteBuf byteBuf) { + byte[] byArray = new byte[16]; + byteBuf.readBytes(byArray); + return Secret.fromBytes(byArray); + } + + public void toBytes(ByteBuf byteBuf) { + byteBuf.writeBytes(this.secret); + } + + public byte[] getSecret() { + return (byte[])this.secret.clone(); + } + + public SecretKeySpec getKeySpec() { + return this.keySpec; + } + + public void setLegacyProtocol(boolean bl) { + this.legacyProtocol = bl; + } + + public boolean isLegacyProtocol() { + return this.legacyProtocol; + } + + public static byte[] generateIV() { + byte[] byArray = new byte[12]; + RANDOM.nextBytes(byArray); + return byArray; + } + + private static byte[] generateLegacyIV() { + byte[] byArray = new byte[16]; + RANDOM.nextBytes(byArray); + return byArray; + } + + public byte[] encrypt(byte[] byArray) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + if (this.legacyProtocol) { + byte[] byArray2 = Secret.generateLegacyIV(); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(1, (Key)this.keySpec, new IvParameterSpec(byArray2)); + byte[] byArray3 = cipher.doFinal(byArray); + byte[] byArray4 = new byte[byArray2.length + byArray3.length]; + System.arraycopy(byArray2, 0, byArray4, 0, byArray2.length); + System.arraycopy(byArray3, 0, byArray4, byArray2.length, byArray3.length); + return byArray4; + } + byte[] byArray5 = Secret.generateIV(); + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(1, (Key)this.keySpec, new GCMParameterSpec(128, byArray5)); + byte[] byArray6 = cipher.doFinal(byArray); + byte[] byArray7 = new byte[byArray5.length + byArray6.length]; + System.arraycopy(byArray5, 0, byArray7, 0, byArray5.length); + System.arraycopy(byArray6, 0, byArray7, byArray5.length, byArray6.length); + return byArray7; + } + + public byte[] decrypt(byte[] byArray) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + if (this.legacyProtocol) { + if (byArray.length < 32 || (byArray.length - 16) % 16 != 0) { + throw new IllegalBlockSizeException("Invalid legacy AES-CBC packet size"); + } + byte[] byArray2 = Arrays.copyOfRange(byArray, 0, 16); + byte[] byArray3 = Arrays.copyOfRange(byArray, 16, byArray.length); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(2, (Key)this.keySpec, new IvParameterSpec(byArray2)); + return cipher.doFinal(byArray3); + } + if (byArray.length < 28) { + throw new IllegalBlockSizeException("Invalid AES-GCM packet size"); + } + byte[] byArray4 = Arrays.copyOfRange(byArray, 0, 12); + byte[] byArray5 = Arrays.copyOfRange(byArray, 12, byArray.length); + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(2, (Key)this.keySpec, new GCMParameterSpec(128, byArray4)); + return cipher.doFinal(byArray5); + } + + public boolean equals(Object object) { + return object instanceof Secret && Arrays.equals(this.secret, ((Secret)object).secret); + } + + public int hashCode() { + return Arrays.hashCode(this.secret); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/SoundPacket.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/SoundPacket.java new file mode 100644 index 0000000..0b2b432 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/SoundPacket.java @@ -0,0 +1,64 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.voice.common.Packet; +import java.util.UUID; +import javax.annotation.Nullable; + +public abstract class SoundPacket +implements Packet { + public static final byte WHISPER_MASK = 1; + public static final byte HAS_CATEGORY_MASK = 2; + protected UUID channelId; + protected UUID sender; + protected byte[] data; + protected long sequenceNumber; + @Nullable + protected String category; + + public SoundPacket(UUID channelId, UUID sender, byte[] data, long sequenceNumber, @Nullable String category) { + this.channelId = channelId; + this.sender = sender; + this.data = data; + this.sequenceNumber = sequenceNumber; + this.category = category; + } + + public SoundPacket() { + } + + public UUID getChannelId() { + return this.channelId; + } + + public UUID getSender() { + return this.sender; + } + + public byte[] getData() { + return this.data; + } + + public long getSequenceNumber() { + return this.sequenceNumber; + } + + @Nullable + public String getCategory() { + return this.category; + } + + protected boolean hasFlag(byte data, byte mask) { + return (data & mask) != 0; + } + + protected byte setFlag(byte data, byte mask) { + return (byte)(data | mask); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/common/Utils.java b/source/decompiled/de/maxhenkel/voicechat/voice/common/Utils.java new file mode 100644 index 0000000..4bdd798 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/common/Utils.java @@ -0,0 +1,15 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.common; + +import de.maxhenkel.voicechat.Voicechat; + +public class Utils { + public static final int MAX_VOICE_CHAT_PACKET_SIZE = 2048; + + public static float getDefaultDistance() { + return Voicechat.SERVER_CONFIG.voiceChatDistance.get().floatValue(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/server/ClientConnection.java b/source/decompiled/de/maxhenkel/voicechat/voice/server/ClientConnection.java new file mode 100644 index 0000000..f5b70b8 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/server/ClientConnection.java @@ -0,0 +1,42 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.voice.common.NetworkMessage; +import de.maxhenkel.voicechat.voice.server.Server; +import java.net.SocketAddress; +import java.util.UUID; + +public class ClientConnection { + private UUID playerUUID; + private SocketAddress address; + private long lastKeepAliveResponse; + + public ClientConnection(UUID playerUUID, SocketAddress address) { + this.playerUUID = playerUUID; + this.address = address; + this.lastKeepAliveResponse = System.currentTimeMillis(); + } + + public UUID getPlayerUUID() { + return this.playerUUID; + } + + public SocketAddress getAddress() { + return this.address; + } + + public long getLastKeepAliveResponse() { + return this.lastKeepAliveResponse; + } + + public void setLastKeepAliveResponse(long lastKeepAliveResponse) { + this.lastKeepAliveResponse = lastKeepAliveResponse; + } + + public void send(Server server, NetworkMessage message) throws Exception { + server.getSocket().send(message.writeServer(server, this), this.address); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/server/Group.java b/source/decompiled/de/maxhenkel/voicechat/voice/server/Group.java new file mode 100644 index 0000000..d0ea554 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/server/Group.java @@ -0,0 +1,88 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + */ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.api.Group; +import de.maxhenkel.voicechat.voice.common.ClientGroup; +import java.util.UUID; +import javax.annotation.Nullable; + +public class Group { + private UUID id; + private String name; + @Nullable + private String password; + private boolean persistent; + private boolean hidden; + private Group.Type type; + + public Group(UUID id, String name, @Nullable String password, boolean persistent, boolean hidden, Group.Type type) { + this.id = id; + this.name = name; + this.password = password; + this.persistent = persistent; + this.hidden = hidden; + this.type = type; + } + + public Group(UUID id, String name, @Nullable String password, boolean persistent) { + this(id, name, password, persistent, false, Group.Type.NORMAL); + } + + public Group(UUID id, String name, @Nullable String password) { + this(id, name, password, false); + } + + public Group(UUID id, String name) { + this(id, name, null); + } + + public Group() { + } + + public UUID getId() { + return this.id; + } + + public String getName() { + return this.name; + } + + @Nullable + public String getPassword() { + return this.password; + } + + public boolean isPersistent() { + return this.persistent; + } + + public boolean isHidden() { + return this.hidden; + } + + public Group.Type getType() { + return this.type; + } + + public boolean isOpen() { + return this.type == Group.Type.OPEN; + } + + public boolean isNormal() { + return this.type == Group.Type.NORMAL; + } + + public boolean isIsolated() { + return this.type == Group.Type.ISOLATED; + } + + public ClientGroup toClientGroup() { + return new ClientGroup(this.id, this.name, this.password != null, this.persistent, this.hidden, this.type); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/server/PingManager.java b/source/decompiled/de/maxhenkel/voicechat/voice/server/PingManager.java new file mode 100644 index 0000000..ac0a70e --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/server/PingManager.java @@ -0,0 +1,100 @@ +/* + * Decompiled with CFR 0.152. + */ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.voice.common.PingPacket; +import de.maxhenkel.voicechat.voice.server.ClientConnection; +import de.maxhenkel.voicechat.voice.server.Server; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +public class PingManager { + private final Map listeners; + private final Server server; + + public PingManager(Server server) { + this.server = server; + this.listeners = new ConcurrentHashMap(); + } + + public void onPongPacket(PingPacket packet) { + Voicechat.LOGGER.info("Received pong {}", packet.getId()); + Ping ping = this.listeners.remove(packet.getId()); + if (ping == null) { + return; + } + ping.listener.onPong(ping.attempt, System.currentTimeMillis() - packet.getTimestamp()); + } + + public void checkTimeouts() { + if (this.listeners.isEmpty()) { + return; + } + List timedOut = this.listeners.entrySet().stream().filter(uuidPingEntry -> ((Ping)uuidPingEntry.getValue()).isTimedOut()).collect(Collectors.toList()); + for (Map.Entry pingEntry : timedOut) { + Ping ping = (Ping)pingEntry.getValue(); + if (ping.attempt >= ping.maxAttempts) { + this.listeners.remove(pingEntry.getKey()); + ping.listener.onTimeout(ping.attempt); + continue; + } + ping.listener.onFailedAttempt(ping.attempt); + try { + ping.send(); + } + catch (Exception e) { + ping.listener.onTimeout(ping.attempt); + Voicechat.LOGGER.warn("Failed to send ping {} after attempt {}", ping.id, ping.attempt); + } + } + } + + public void sendPing(ClientConnection connection, long timeout, int attempts, PingListener listener) throws Exception { + Ping ping = new Ping(connection, listener, timeout, attempts); + this.listeners.put(ping.id, ping); + ping.send(); + } + + private class Ping { + private final UUID id = UUID.randomUUID(); + private final ClientConnection connection; + private final PingListener listener; + private long timestamp; + private final long timeout; + private final int maxAttempts; + private int attempt; + + public Ping(ClientConnection connection, PingListener listener, long timeout, int maxAttempts) { + this.connection = connection; + this.listener = listener; + this.timeout = timeout; + this.maxAttempts = maxAttempts; + this.attempt = 0; + } + + public boolean isTimedOut() { + return System.currentTimeMillis() - this.timestamp >= this.timeout; + } + + public void send() throws Exception { + this.timestamp = System.currentTimeMillis(); + ++this.attempt; + PingManager.this.server.sendPacketRaw(new PingPacket(this.id, this.timestamp), this.connection); + Voicechat.LOGGER.info("Sent ping {} attempt {}", this.id, this.attempt); + } + } + + public static interface PingListener { + public void onPong(int var1, long var2); + + public void onFailedAttempt(int var1); + + public void onTimeout(int var1); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/server/PlayerStateManager.java b/source/decompiled/de/maxhenkel/voicechat/voice/server/PlayerStateManager.java new file mode 100644 index 0000000..e64af4d --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/server/PlayerStateManager.java @@ -0,0 +1,155 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.Bukkit + * org.bukkit.entity.Player + * org.bukkit.event.Listener + * org.bukkit.event.player.PlayerJoinEvent + * org.bukkit.event.player.PlayerQuitEvent + */ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.compatibility.PlayerHideEvent; +import de.maxhenkel.voicechat.compatibility.PlayerShowEvent; +import de.maxhenkel.voicechat.net.NetManager; +import de.maxhenkel.voicechat.net.PlayerStatePacket; +import de.maxhenkel.voicechat.net.PlayerStatesPacket; +import de.maxhenkel.voicechat.net.RemovePlayerStatePacket; +import de.maxhenkel.voicechat.net.UpdateStatePacket; +import de.maxhenkel.voicechat.plugins.PluginManager; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import java.util.ArrayList; +import java.util.Collection; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class PlayerStateManager +implements Listener { + private final ConcurrentHashMap states = new ConcurrentHashMap(); + + public void onUpdateStatePacket(Player player, UpdateStatePacket packet) { + PlayerState state = this.states.get(player.getUniqueId()); + if (state == null) { + state = PlayerStateManager.defaultDisconnectedState(player); + } + state.setDisabled(packet.isDisabled()); + this.states.put(player.getUniqueId(), state); + this.broadcastState(player, state); + Voicechat.LOGGER.debug("Got state of {}: {}", player.getName(), state); + } + + public void onPlayerQuit(PlayerQuitEvent event) { + this.states.remove(event.getPlayer().getUniqueId()); + this.broadcastRemoveState(event.getPlayer()); + Voicechat.LOGGER.debug("Removing state of {}", event.getPlayer().getName()); + } + + public void onPlayerJoin(PlayerJoinEvent event) { + PlayerState state = PlayerStateManager.defaultDisconnectedState(event.getPlayer()); + this.states.put(event.getPlayer().getUniqueId(), state); + this.broadcastState(event.getPlayer(), state); + Voicechat.LOGGER.debug("Setting default state of {}: {}", event.getPlayer().getName(), state); + } + + public void onPlayerHide(PlayerHideEvent event) { + RemovePlayerStatePacket packet = new RemovePlayerStatePacket(event.getHiddenPlayer().getUniqueId()); + NetManager.sendToClient(event.getObserver(), packet); + Voicechat.LOGGER.debug("Removing state of {} for {}", event.getHiddenPlayer().getName(), event.getObserver().getName()); + } + + public void onPlayerShow(PlayerShowEvent event) { + PlayerState state = this.states.get(event.getShownPlayer().getUniqueId()); + if (state == null) { + state = PlayerStateManager.defaultDisconnectedState(event.getShownPlayer()); + } + PlayerStatePacket packet = new PlayerStatePacket(state); + NetManager.sendToClient(event.getObserver(), packet); + Voicechat.LOGGER.debug("Sending state of {} to {}", event.getShownPlayer().getName(), event.getObserver().getName()); + } + + public void broadcastState(@Nullable Player stateOwner, PlayerState state) { + PlayerStatePacket packet = new PlayerStatePacket(state); + for (Player receiver : Voicechat.INSTANCE.getServer().getOnlinePlayers()) { + if (stateOwner != null && !Voicechat.compatibility.canSee(receiver, stateOwner)) continue; + NetManager.sendToClient(receiver, packet); + } + PluginManager.instance().onPlayerStateChanged(state); + } + + public void broadcastRemoveState(Player stateOwner) { + RemovePlayerStatePacket packet = new RemovePlayerStatePacket(stateOwner.getUniqueId()); + for (Player receiver : Voicechat.INSTANCE.getServer().getOnlinePlayers()) { + NetManager.sendToClient(receiver, packet); + } + PluginManager.instance().onPlayerStateChanged(PlayerStateManager.defaultDisconnectedState(stateOwner)); + } + + public void onPlayerCompatibilityCheckSucceeded(Player player) { + ArrayList stateList = new ArrayList(this.states.size()); + for (PlayerState state : this.states.values()) { + Player otherPlayer = Voicechat.INSTANCE.getServer().getPlayer(state.getUuid()); + if (otherPlayer == null || !Voicechat.compatibility.canSee(player, otherPlayer)) continue; + stateList.add(state); + } + PlayerStatesPacket packet = new PlayerStatesPacket(stateList); + NetManager.sendToClient(player, packet); + Voicechat.LOGGER.debug("Sending initial states to {}", player.getName()); + } + + public void onPlayerVoicechatDisconnect(UUID uuid) { + PlayerState state = this.states.get(uuid); + if (state == null) { + return; + } + state.setDisconnected(true); + Player player = Bukkit.getPlayer((UUID)uuid); + this.broadcastState(player, state); + Voicechat.LOGGER.debug("Set state of {} to disconnected: {}", uuid, state); + } + + public void onPlayerVoicechatConnect(Player player) { + PlayerState state = this.states.get(player.getUniqueId()); + if (state == null) { + state = PlayerStateManager.defaultDisconnectedState(player); + } + state.setDisconnected(false); + this.states.put(player.getUniqueId(), state); + this.broadcastState(player, state); + Voicechat.LOGGER.debug("Set state of {} to connected: {}", player.getName(), state); + } + + @Nullable + public PlayerState getState(UUID playerUUID) { + return this.states.get(playerUUID); + } + + public static PlayerState defaultDisconnectedState(Player player) { + return new PlayerState(player.getUniqueId(), player.getName(), false, true); + } + + public void setGroup(Player player, @Nullable UUID group) { + PlayerState state = this.states.get(player.getUniqueId()); + if (state == null) { + state = PlayerStateManager.defaultDisconnectedState(player); + Voicechat.LOGGER.debug("Defaulting to default state for {}: {}", player.getName(), state); + } + state.setGroup(group); + this.states.put(player.getUniqueId(), state); + this.broadcastState(player, state); + Voicechat.LOGGER.debug("Setting group of {}: {}", player.getName(), state); + } + + public Collection getStates() { + return this.states.values(); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/server/Server.java b/source/decompiled/de/maxhenkel/voicechat/voice/server/Server.java new file mode 100644 index 0000000..3cb1838 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/server/Server.java @@ -0,0 +1,502 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.Bukkit + * org.bukkit.GameMode + * org.bukkit.Server + * org.bukkit.entity.Entity + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.api.RawUdpPacket; +import de.maxhenkel.voicechat.api.VoicechatSocket; +import de.maxhenkel.voicechat.debug.CooldownTimer; +import de.maxhenkel.voicechat.debug.VoicechatUncaughtExceptionHandler; +import de.maxhenkel.voicechat.net.NetManager; +import de.maxhenkel.voicechat.permission.PermissionManager; +import de.maxhenkel.voicechat.plugins.PluginManager; +import de.maxhenkel.voicechat.voice.common.AuthenticateAckPacket; +import de.maxhenkel.voicechat.voice.common.AuthenticatePacket; +import de.maxhenkel.voicechat.voice.common.ConnectionCheckAckPacket; +import de.maxhenkel.voicechat.voice.common.ConnectionCheckPacket; +import de.maxhenkel.voicechat.voice.common.GroupSoundPacket; +import de.maxhenkel.voicechat.voice.common.KeepAlivePacket; +import de.maxhenkel.voicechat.voice.common.LocationSoundPacket; +import de.maxhenkel.voicechat.voice.common.MicPacket; +import de.maxhenkel.voicechat.voice.common.NetworkMessage; +import de.maxhenkel.voicechat.voice.common.Packet; +import de.maxhenkel.voicechat.voice.common.PingPacket; +import de.maxhenkel.voicechat.voice.common.PlayerSoundPacket; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import de.maxhenkel.voicechat.voice.common.Secret; +import de.maxhenkel.voicechat.voice.common.SoundPacket; +import de.maxhenkel.voicechat.voice.common.Utils; +import de.maxhenkel.voicechat.voice.server.ClientConnection; +import de.maxhenkel.voicechat.voice.server.Group; +import de.maxhenkel.voicechat.voice.server.PingManager; +import de.maxhenkel.voicechat.voice.server.PlayerStateManager; +import de.maxhenkel.voicechat.voice.server.ServerCategoryManager; +import de.maxhenkel.voicechat.voice.server.ServerGroupManager; +import de.maxhenkel.voicechat.voice.server.ServerPlayerManager; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.nio.channels.AsynchronousCloseException; +import java.util.Collection; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; + +public class Server +extends Thread { + private final Map connections; + private final Map unCheckedConnections; + private final Map secrets; + private final int port; + private final org.bukkit.Server server; + private final VoicechatSocket socket; + private final ProcessThread processThread; + private final BlockingQueue packetQueue; + private final PingManager pingManager; + private final PlayerStateManager playerStateManager; + private final ServerGroupManager groupManager; + private final ServerCategoryManager categoryManager; + + public Server() { + int configPort = Voicechat.SERVER_CONFIG.voiceChatPort.get(); + if (configPort < 0) { + Voicechat.LOGGER.info("Using the Minecraft servers port as voice chat port", new Object[0]); + this.port = Bukkit.getPort(); + } else { + this.port = configPort; + } + this.server = Bukkit.getServer(); + this.socket = PluginManager.instance().getSocketImplementation(); + this.connections = new ConcurrentHashMap(); + this.unCheckedConnections = new ConcurrentHashMap(); + this.secrets = new ConcurrentHashMap(); + this.packetQueue = new LinkedBlockingQueue(); + this.pingManager = new PingManager(this); + this.playerStateManager = new PlayerStateManager(); + this.groupManager = new ServerGroupManager(); + this.categoryManager = new ServerCategoryManager(); + this.setDaemon(true); + this.setName("VoiceChatServerThread"); + this.setUncaughtExceptionHandler(new VoicechatUncaughtExceptionHandler()); + this.processThread = new ProcessThread(); + this.processThread.start(); + } + + @Override + public void run() { + try { + String bindAddress = this.getBindAddress(); + try { + InetAddress.getByName(bindAddress); + } + catch (UnknownHostException e) { + Voicechat.LOGGER.error("Failed to parse bind IP address '{}'", bindAddress, e); + Voicechat.LOGGER.info("Binding to wildcard IP address", new Object[0]); + bindAddress = ""; + } + this.socket.open(this.port, bindAddress); + if (bindAddress.isEmpty()) { + Voicechat.LOGGER.info("Voice chat server started at port {}", this.socket.getLocalPort()); + } else { + Voicechat.LOGGER.info("Voice chat server started at {}:{}", bindAddress, this.socket.getLocalPort()); + } + while (!this.socket.isClosed()) { + try { + this.packetQueue.add(this.socket.read()); + } + catch (Exception e) { + if (e instanceof SocketException && e.getCause() instanceof AsynchronousCloseException || !Voicechat.debugMode()) continue; + Voicechat.LOGGER.error("Failed to read from socket", e); + } + } + } + catch (Exception e) { + Voicechat.LOGGER.error("Voice chat server error", e); + } + } + + private String getBindAddress() { + String bindAddress; + block8: { + bindAddress = Voicechat.SERVER_CONFIG.voiceChatBindAddress.get(); + if (bindAddress.trim().equals("*")) { + bindAddress = ""; + } else if (bindAddress.trim().isEmpty()) { + try { + bindAddress = Bukkit.getIp(); + if (bindAddress.trim().isEmpty()) break block8; + try { + InetAddress address = InetAddress.getByName(bindAddress); + if (address.isLoopbackAddress()) { + bindAddress = ""; + break block8; + } + Voicechat.LOGGER.info("Using server-ip as bind address: {}", bindAddress); + } + catch (Exception e) { + Voicechat.LOGGER.warn("Invalid server-ip", e); + bindAddress = ""; + } + } + catch (Throwable t) { + Voicechat.LOGGER.warn("Failed to get server-ip from server.properties - binding to wildcard address", t); + } + } + } + return bindAddress; + } + + public Secret getSecret(UUID playerUUID) { + if (this.hasSecret(playerUUID)) { + return this.secrets.get(playerUUID); + } + Secret secret = Secret.generateNewRandomSecret(); + this.secrets.put(playerUUID, secret); + return secret; + } + + @Nullable + public Secret generateNewSecret(UUID playerUUID) { + if (this.hasSecret(playerUUID)) { + return null; + } + return this.getSecret(playerUUID); + } + + public boolean hasSecret(UUID playerUUID) { + return this.secrets.containsKey(playerUUID); + } + + public void disconnectClient(UUID playerUUID) { + this.connections.remove(playerUUID); + this.unCheckedConnections.remove(playerUUID); + this.secrets.remove(playerUUID); + PluginManager.instance().onPlayerDisconnected(playerUUID); + } + + public void close() { + this.socket.close(); + this.processThread.close(); + PluginManager.instance().onServerStopped(); + } + + public boolean isClosed() { + return !this.processThread.running; + } + + public void onMicPacket(UUID playerUuid, MicPacket packet) { + Player player = this.server.getPlayer(playerUuid); + if (player == null) { + return; + } + if (!player.hasPermission(PermissionManager.SPEAK_PERMISSION)) { + CooldownTimer.run("no-speak-" + playerUuid, () -> NetManager.sendStatusMessage(player, "message.voicechat.no_speak_permission", new String[0])); + return; + } + PlayerState state = this.playerStateManager.getState(player.getUniqueId()); + if (state == null) { + return; + } + if (!PluginManager.instance().onMicPacket(player, state, packet)) { + this.processMicPacket(player, state, packet); + } + } + + private void processMicPacket(Player player, PlayerState state, MicPacket packet) { + if (state.hasGroup()) { + Group group = this.groupManager.getGroup(state.getGroup()); + this.processGroupPacket(state, player, packet); + if (group == null || group.isOpen()) { + this.processProximityPacket(state, player, packet); + } + return; + } + this.processProximityPacket(state, player, packet); + } + + private void processGroupPacket(PlayerState senderState, Player sender, MicPacket packet) { + UUID groupId = senderState.getGroup(); + if (groupId == null) { + return; + } + GroupSoundPacket groupSoundPacket = new GroupSoundPacket(senderState.getUuid(), senderState.getUuid(), packet.getData(), packet.getSequenceNumber(), null); + for (PlayerState state : this.playerStateManager.getStates()) { + Player p; + if (!groupId.equals(state.getGroup()) || senderState.getUuid().equals(state.getUuid()) || (p = this.server.getPlayer(state.getUuid())) == null) continue; + ClientConnection connection = this.getConnection(state.getUuid()); + this.sendSoundPacket(sender, senderState, p, state, connection, groupSoundPacket, "group"); + } + } + + private void processProximityPacket(PlayerState senderState, Player sender, MicPacket packet) { + UUID groupId = senderState.getGroup(); + float distance = packet.isWhispering() ? Voicechat.SERVER_CONFIG.whisperDistance.get().floatValue() : Utils.getDefaultDistance(); + distance = PluginManager.instance().getDistance(sender, senderState, packet, distance); + SoundPacket soundPacket = null; + String source = null; + if (sender.getGameMode().equals((Object)GameMode.SPECTATOR)) { + Player spectatingPlayer; + Entity camera; + if (Voicechat.SERVER_CONFIG.spectatorPlayerPossession.get().booleanValue() && (camera = sender.getSpectatorTarget()) instanceof Player && (spectatingPlayer = (Player)camera) != sender) { + PlayerState receiverState = this.playerStateManager.getState(spectatingPlayer.getUniqueId()); + if (receiverState == null) { + return; + } + GroupSoundPacket groupSoundPacket = new GroupSoundPacket(senderState.getUuid(), senderState.getUuid(), packet.getData(), packet.getSequenceNumber(), null); + ClientConnection connection = this.getConnection(receiverState.getUuid()); + this.sendSoundPacket(sender, senderState, spectatingPlayer, receiverState, connection, groupSoundPacket, "spectator"); + return; + } + if (Voicechat.SERVER_CONFIG.spectatorInteraction.get().booleanValue()) { + soundPacket = new LocationSoundPacket(sender.getUniqueId(), sender.getUniqueId(), sender.getLocation(), packet.getData(), packet.getSequenceNumber(), distance, null); + source = "spectator"; + } + } + if (soundPacket == null) { + soundPacket = new PlayerSoundPacket(sender.getUniqueId(), sender.getUniqueId(), packet.getData(), packet.getSequenceNumber(), packet.isWhispering(), distance, null); + source = "proximity"; + } + this.broadcast(ServerPlayerManager.getPlayersInRange(sender.getWorld(), sender.getLocation(), this.getBroadcastRange(distance), p -> !p.getUniqueId().equals(sender.getUniqueId())), soundPacket, sender, senderState, groupId, source); + } + + public void sendSoundPacket(@Nullable Player sender, @Nullable PlayerState senderState, Player receiver, PlayerState receiverState, @Nullable ClientConnection connection, SoundPacket soundPacket, String source) { + PluginManager.instance().onListenerAudio(receiver.getUniqueId(), soundPacket); + if (connection == null) { + return; + } + if (receiverState.isDisabled() || receiverState.isDisconnected()) { + return; + } + if (PluginManager.instance().onSoundPacket(sender, senderState, receiver, receiverState, soundPacket, source)) { + return; + } + if (!receiver.hasPermission(PermissionManager.LISTEN_PERMISSION)) { + CooldownTimer.run(String.format("no-listen-%s", receiver.getUniqueId()), 30000L, () -> NetManager.sendStatusMessage(receiver, "message.voicechat.no_listen_permission", new String[0])); + return; + } + this.sendPacket(soundPacket, connection); + } + + public double getBroadcastRange(float minRange) { + double broadcastRange = Voicechat.SERVER_CONFIG.broadcastRange.get(); + if (broadcastRange < 0.0) { + broadcastRange = Voicechat.SERVER_CONFIG.voiceChatDistance.get() + 1.0; + } + return Math.max(broadcastRange, (double)minRange); + } + + public void broadcast(Collection players, SoundPacket packet, @Nullable Player sender, @Nullable PlayerState senderState, @Nullable UUID groupId, String source) { + for (Player player : players) { + PlayerState state = this.playerStateManager.getState(player.getUniqueId()); + if (state == null || state.hasGroup() && state.getGroup().equals(groupId)) continue; + Group receiverGroup = null; + if (state.hasGroup()) { + receiverGroup = this.groupManager.getGroup(state.getGroup()); + } + if (receiverGroup != null && receiverGroup.isIsolated()) continue; + ClientConnection connection = this.getConnection(state.getUuid()); + this.sendSoundPacket(sender, senderState, player, state, connection, packet, source); + } + } + + private void sendKeepAlives() { + long timestamp = System.currentTimeMillis(); + this.connections.values().removeIf(connection -> { + if (timestamp - connection.getLastKeepAliveResponse() >= (long)Voicechat.SERVER_CONFIG.keepAlive.get().intValue() * 10L) { + this.secrets.remove(connection.getPlayerUUID()); + Voicechat.LOGGER.info("Player {} timed out", connection.getPlayerUUID()); + Player player = this.server.getPlayer(connection.getPlayerUUID()); + if (player != null) { + Voicechat.LOGGER.info("Reconnecting player {}", player.getName()); + Voicechat.SERVER.initializePlayerConnection(player); + } else { + Voicechat.LOGGER.error("Reconnecting player {} failed (Could not find player)", connection.getPlayerUUID()); + } + this.playerStateManager.onPlayerVoicechatDisconnect(connection.getPlayerUUID()); + PluginManager.instance().onPlayerDisconnected(connection.getPlayerUUID()); + return true; + } + return false; + }); + for (ClientConnection connection2 : this.connections.values()) { + this.sendPacket(new KeepAlivePacket(), connection2); + } + } + + @Nullable + public ClientConnection getSender(NetworkMessage message) { + return this.connections.values().stream().filter(connection -> connection.getAddress().equals(message.getAddress())).findAny().orElse(null); + } + + @Nullable + public ClientConnection getUnconnectedSender(NetworkMessage message) { + return this.unCheckedConnections.values().stream().filter(connection -> connection.getAddress().equals(message.getAddress())).findAny().orElse(null); + } + + public Map getConnections() { + return this.connections; + } + + @Nullable + public ClientConnection getConnection(UUID playerID) { + return this.connections.get(playerID); + } + + public VoicechatSocket getSocket() { + return this.socket; + } + + public int getPort() { + return this.socket.getLocalPort(); + } + + public boolean sendPacket(Packet packet, ClientConnection connection) { + try { + this.sendPacketRaw(packet, connection); + return true; + } + catch (Exception e) { + Voicechat.LOGGER.error("Failed to send voice chat packet to {}", connection.getPlayerUUID()); + return false; + } + } + + public void sendPacketRaw(Packet packet, ClientConnection connection) throws Exception { + connection.send(this, new NetworkMessage(packet)); + } + + public PingManager getPingManager() { + return this.pingManager; + } + + public PlayerStateManager getPlayerStateManager() { + return this.playerStateManager; + } + + public ServerGroupManager getGroupManager() { + return this.groupManager; + } + + public ServerCategoryManager getCategoryManager() { + return this.categoryManager; + } + + private class ProcessThread + extends Thread { + private boolean running = true; + private long lastKeepAlive = 0L; + + public ProcessThread() { + this.setDaemon(true); + this.setName("VoiceChatPacketProcessingThread"); + this.setUncaughtExceptionHandler(new VoicechatUncaughtExceptionHandler()); + } + + @Override + public void run() { + while (this.running) { + try { + NetworkMessage message; + RawUdpPacket rawPacket; + Server.this.pingManager.checkTimeouts(); + long keepAliveTime = System.currentTimeMillis(); + if (keepAliveTime - this.lastKeepAlive > (long)Voicechat.SERVER_CONFIG.keepAlive.get().intValue()) { + Server.this.sendKeepAlives(); + this.lastKeepAlive = keepAliveTime; + } + if ((rawPacket = (RawUdpPacket)Server.this.packetQueue.poll(10L, TimeUnit.MILLISECONDS)) == null) continue; + try { + message = NetworkMessage.readPacketServer(rawPacket, Server.this); + } + catch (Exception e) { + CooldownTimer.run("failed_reading_packet", () -> Voicechat.LOGGER.warn("Failed to read packet from {}", rawPacket.getSocketAddress())); + continue; + } + if (message == null) continue; + if (System.currentTimeMillis() - message.getTimestamp() > message.getTTL()) { + CooldownTimer.run("ttl", () -> { + Voicechat.LOGGER.error("Dropping voice chat packets! Your Server might be overloaded!", new Object[0]); + Voicechat.LOGGER.error("Packet queue has {} packets", Server.this.packetQueue.size()); + }); + continue; + } + if (message.getPacket() instanceof AuthenticatePacket) { + AuthenticatePacket packet = (AuthenticatePacket)message.getPacket(); + Secret secret = (Secret)Server.this.secrets.get(packet.getPlayerUUID()); + if (secret != null && secret.equals(packet.getSecret())) { + ClientConnection connection = (ClientConnection)Server.this.unCheckedConnections.get(packet.getPlayerUUID()); + if (connection == null) { + connection = (ClientConnection)Server.this.connections.get(packet.getPlayerUUID()); + } + if (connection == null) { + connection = new ClientConnection(packet.getPlayerUUID(), message.getAddress()); + Server.this.unCheckedConnections.put(packet.getPlayerUUID(), connection); + Voicechat.LOGGER.info("Successfully authenticated player {}", packet.getPlayerUUID()); + } + Server.this.sendPacket(new AuthenticateAckPacket(), connection); + } + } + if (message.getPacket() instanceof ConnectionCheckPacket) { + ClientConnection connection = Server.this.getUnconnectedSender(message); + if (connection == null) { + connection = Server.this.getSender(message); + if (connection == null) continue; + Server.this.sendPacket(new ConnectionCheckAckPacket(), connection); + continue; + } + connection.setLastKeepAliveResponse(System.currentTimeMillis()); + Server.this.connections.put(connection.getPlayerUUID(), connection); + Server.this.unCheckedConnections.remove(connection.getPlayerUUID()); + Voicechat.LOGGER.info("Successfully validated connection of player {}", connection.getPlayerUUID()); + Player player = Server.this.server.getPlayer(connection.getPlayerUUID()); + if (player != null) { + Server.this.playerStateManager.onPlayerVoicechatConnect(player); + PluginManager.instance().onPlayerConnected(player); + Voicechat.LOGGER.info("Player {} ({}) successfully connected to voice chat", player.getName(), connection.getPlayerUUID()); + } + Server.this.sendPacket(new ConnectionCheckAckPacket(), connection); + continue; + } + ClientConnection conn = Server.this.getSender(message); + if (conn == null) continue; + if (message.getPacket() instanceof MicPacket) { + Server.this.onMicPacket(conn.getPlayerUUID(), (MicPacket)message.getPacket()); + continue; + } + if (message.getPacket() instanceof PingPacket) { + PingPacket packet = (PingPacket)message.getPacket(); + Server.this.pingManager.onPongPacket(packet); + continue; + } + if (!(message.getPacket() instanceof KeepAlivePacket)) continue; + conn.setLastKeepAliveResponse(System.currentTimeMillis()); + } + catch (Exception e) { + Voicechat.LOGGER.error("Voice chat server error", e); + } + } + } + + public void close() { + this.running = false; + } + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerCategoryManager.java b/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerCategoryManager.java new file mode 100644 index 0000000..3fb712a --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerCategoryManager.java @@ -0,0 +1,54 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.entity.Player + */ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.AddCategoryPacket; +import de.maxhenkel.voicechat.net.NetManager; +import de.maxhenkel.voicechat.net.RemoveCategoryPacket; +import de.maxhenkel.voicechat.plugins.CategoryManager; +import de.maxhenkel.voicechat.plugins.impl.VolumeCategoryImpl; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; + +public class ServerCategoryManager +extends CategoryManager { + public void onPlayerCompatibilityCheckSucceeded(Player player) { + Voicechat.LOGGER.debug("Synchronizing {} volume categories with {}", this.categories.size(), player.getName()); + for (VolumeCategoryImpl category : this.getCategories()) { + NetManager.sendToClient(player, new AddCategoryPacket(category)); + } + } + + @Override + public void addCategory(VolumeCategoryImpl category) { + super.addCategory(category); + Voicechat.LOGGER.debug("Synchronizing volume category {} with all players", category.getId()); + this.broadcastAddCategory(category); + } + + @Override + @Nullable + public VolumeCategoryImpl removeCategory(String categoryId) { + VolumeCategoryImpl volumeCategory = super.removeCategory(categoryId); + Voicechat.LOGGER.debug("Removing volume category {} for all players", categoryId); + this.broadcastRemoveCategory(categoryId); + return volumeCategory; + } + + private void broadcastAddCategory(VolumeCategoryImpl category) { + AddCategoryPacket packet = new AddCategoryPacket(category); + Voicechat.INSTANCE.getServer().getOnlinePlayers().forEach(p -> NetManager.sendToClient(p, packet)); + } + + private void broadcastRemoveCategory(String categoryId) { + RemoveCategoryPacket packet = new RemoveCategoryPacket(categoryId); + Voicechat.INSTANCE.getServer().getOnlinePlayers().forEach(p -> NetManager.sendToClient(p, packet)); + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerGroupManager.java b/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerGroupManager.java new file mode 100644 index 0000000..5f99ea4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerGroupManager.java @@ -0,0 +1,185 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.entity.Player + * org.bukkit.event.player.PlayerQuitEvent + */ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.net.AddGroupPacket; +import de.maxhenkel.voicechat.net.CreateGroupPacket; +import de.maxhenkel.voicechat.net.JoinGroupPacket; +import de.maxhenkel.voicechat.net.JoinedGroupPacket; +import de.maxhenkel.voicechat.net.LeaveGroupPacket; +import de.maxhenkel.voicechat.net.NetManager; +import de.maxhenkel.voicechat.net.RemoveGroupPacket; +import de.maxhenkel.voicechat.permission.PermissionManager; +import de.maxhenkel.voicechat.plugins.PluginManager; +import de.maxhenkel.voicechat.voice.common.PlayerState; +import de.maxhenkel.voicechat.voice.server.Group; +import de.maxhenkel.voicechat.voice.server.PlayerStateManager; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerQuitEvent; + +public class ServerGroupManager { + private final Map groups = new ConcurrentHashMap(); + + public void onJoinGroupPacket(Player player, JoinGroupPacket packet) { + if (!Voicechat.SERVER_CONFIG.groupsEnabled.get().booleanValue()) { + return; + } + if (!player.hasPermission(PermissionManager.GROUPS_PERMISSION)) { + NetManager.sendStatusMessage(player, "message.voicechat.no_group_permission", new String[0]); + return; + } + this.joinGroup(this.groups.get(packet.getGroup()), player, packet.getPassword()); + } + + public void onCreateGroupPacket(Player player, CreateGroupPacket packet) { + if (!Voicechat.SERVER_CONFIG.groupsEnabled.get().booleanValue()) { + return; + } + if (!player.hasPermission(PermissionManager.GROUPS_PERMISSION)) { + NetManager.sendStatusMessage(player, "message.voicechat.no_group_permission", new String[0]); + return; + } + if (!Voicechat.GROUP_REGEX.matcher(packet.getName()).matches()) { + Voicechat.LOGGER.warn("Player {} tried to create a group with an invalid name", player.getDisplayName()); + return; + } + if (packet.getPassword() != null && !Voicechat.GROUP_REGEX.matcher(packet.getPassword()).matches()) { + Voicechat.LOGGER.warn("Player {} tried to create a group with an invalid password", player.getDisplayName()); + return; + } + Group group = new Group(UUID.randomUUID(), packet.getName(), packet.getPassword(), false, false, packet.getType()); + this.addGroup(group, player); + } + + public void onLeaveGroupPacket(Player player, LeaveGroupPacket packet) { + this.leaveGroup(player); + } + + public void onPlayerCompatibilityCheckSucceeded(Player player) { + Voicechat.LOGGER.debug("Synchronizing {} groups with {}", this.groups.size(), player.getDisplayName()); + for (Group group : this.groups.values()) { + NetManager.sendToClient(player, new AddGroupPacket(group.toClientGroup())); + } + } + + public void onPlayerQuit(PlayerQuitEvent event) { + this.cleanupGroups(); + } + + private PlayerStateManager getStates() { + return Voicechat.SERVER.getServer().getPlayerStateManager(); + } + + public void addGroup(Group group, @Nullable Player player) { + if (PluginManager.instance().onCreateGroup(player, group)) { + return; + } + this.groups.put(group.getId(), group); + this.broadcastAddGroup(group); + if (player == null) { + return; + } + PlayerStateManager manager = this.getStates(); + manager.setGroup(player, group.getId()); + NetManager.sendToClient(player, new JoinedGroupPacket(group.getId(), false)); + } + + public void joinGroup(@Nullable Group group, Player player, String password) { + if (group != null && group.getPassword() != null && !group.getPassword().equals(password)) { + NetManager.sendToClient(player, new JoinedGroupPacket(null, true)); + return; + } + if (PluginManager.instance().onJoinGroup(player, group)) { + return; + } + if (group == null) { + NetManager.sendToClient(player, new JoinedGroupPacket(null, false)); + return; + } + PlayerStateManager manager = this.getStates(); + manager.setGroup(player, group.getId()); + NetManager.sendToClient(player, new JoinedGroupPacket(group.getId(), false)); + } + + public void leaveGroup(Player player) { + if (PluginManager.instance().onLeaveGroup(player)) { + return; + } + PlayerStateManager manager = this.getStates(); + manager.setGroup(player, null); + NetManager.sendToClient(player, new JoinedGroupPacket(null, false)); + this.cleanupGroups(); + } + + public void cleanupGroups() { + PlayerStateManager manager = this.getStates(); + List usedGroups = manager.getStates().stream().filter(PlayerState::hasGroup).map(PlayerState::getGroup).distinct().collect(Collectors.toList()); + List groupsToRemove = this.groups.entrySet().stream().filter(entry -> !((Group)entry.getValue()).isPersistent()).map(Map.Entry::getKey).filter(uuid -> !usedGroups.contains(uuid)).collect(Collectors.toList()); + for (UUID uuid2 : groupsToRemove) { + this.removeGroup(uuid2); + } + } + + public boolean removeGroup(UUID groupId) { + Group group = this.groups.get(groupId); + if (group == null) { + return false; + } + PlayerStateManager manager = this.getStates(); + if (manager.getStates().stream().anyMatch(state -> state.hasGroup() && state.getGroup().equals(groupId))) { + return false; + } + if (PluginManager.instance().onRemoveGroup(group)) { + return false; + } + this.groups.remove(groupId); + this.broadcastRemoveGroup(groupId); + return true; + } + + @Nullable + public Group getGroup(UUID groupID) { + return this.groups.get(groupID); + } + + private void broadcastAddGroup(Group group) { + AddGroupPacket packet = new AddGroupPacket(group.toClientGroup()); + Voicechat.INSTANCE.getServer().getOnlinePlayers().forEach(p -> NetManager.sendToClient(p, packet)); + } + + private void broadcastRemoveGroup(UUID group) { + RemoveGroupPacket packet = new RemoveGroupPacket(group); + Voicechat.INSTANCE.getServer().getOnlinePlayers().forEach(p -> NetManager.sendToClient(p, packet)); + } + + @Nullable + public Group getPlayerGroup(Player player) { + PlayerState state = this.getStates().getState(player.getUniqueId()); + if (state == null) { + return null; + } + UUID groupId = state.getGroup(); + if (groupId == null) { + return null; + } + return this.getGroup(groupId); + } + + public Map getGroups() { + return this.groups; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerPlayerManager.java b/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerPlayerManager.java new file mode 100644 index 0000000..446ebc4 --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerPlayerManager.java @@ -0,0 +1,107 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * javax.annotation.Nullable + * org.bukkit.Bukkit + * org.bukkit.Location + * org.bukkit.World + * org.bukkit.entity.Player + * org.bukkit.event.EventHandler + * org.bukkit.event.Listener + * org.bukkit.event.player.PlayerJoinEvent + * org.bukkit.event.player.PlayerQuitEvent + * org.bukkit.plugin.Plugin + */ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.Voicechat; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.Plugin; + +public class ServerPlayerManager +implements Listener { + private static final long REFRESH_INTERVAL_TICKS = 600L; + public static final ServerPlayerManager INSTANCE = new ServerPlayerManager(); + private volatile Set players = Collections.emptySet(); + + public static void init(Plugin plugin) { + if (!Voicechat.SERVER_CONFIG.threadedServerSupport.get().booleanValue()) { + return; + } + Bukkit.getPluginManager().registerEvents((Listener)INSTANCE, plugin); + Voicechat.compatibility.scheduleSyncRepeatingTask(INSTANCE::refresh, 0L, 600L); + } + + private ServerPlayerManager() { + } + + private synchronized void refresh() { + this.players = new HashSet(Bukkit.getOnlinePlayers()); + } + + @EventHandler + public synchronized void onPlayerJoin(PlayerJoinEvent event) { + HashSet newPlayers = new HashSet(this.players); + newPlayers.add(event.getPlayer()); + this.players = newPlayers; + } + + @EventHandler + public synchronized void onPlayerQuit(PlayerQuitEvent event) { + HashSet newPlayers = new HashSet(this.players); + newPlayers.remove(event.getPlayer()); + this.players = newPlayers; + } + + public static Collection getPlayersInRange(World level, Location pos, double range, @Nullable Predicate filter) { + return INSTANCE.getPlayersInRangeInternal(level, pos, range, filter); + } + + private Collection getPlayersInRangeInternal(World world, Location pos, double range, @Nullable Predicate filter) { + if (!Voicechat.SERVER_CONFIG.threadedServerSupport.get().booleanValue()) { + return ServerPlayerManager.getPlayersInRangeDirect(world, pos, range, filter); + } + ArrayList nearbyPlayers = new ArrayList(); + for (Player player : this.players) { + if (!world.equals((Object)player.getWorld()) || !ServerPlayerManager.isInRange(player.getLocation(), pos, range) || filter != null && !filter.test(player)) continue; + nearbyPlayers.add(player); + } + return nearbyPlayers; + } + + private static Collection getPlayersInRangeDirect(World world, Location pos, double range, @Nullable Predicate filter) { + ArrayList nearbyPlayers = new ArrayList(); + List worldPlayers = world.getPlayers(); + for (int i = 0; i < worldPlayers.size(); ++i) { + Player player = (Player)worldPlayers.get(i); + if (!ServerPlayerManager.isInRange(player.getLocation(), pos, range) || filter != null && !filter.test(player)) continue; + nearbyPlayers.add(player); + } + return nearbyPlayers; + } + + public static boolean isInRange(Location pos1, Location pos2, double range) { + return ServerPlayerManager.square(pos1.getX() - pos2.getX()) + ServerPlayerManager.square(pos1.getY() - pos2.getY()) + ServerPlayerManager.square(pos1.getZ() - pos2.getZ()) <= ServerPlayerManager.square(range); + } + + private static double square(double value) { + return value * value; + } +} + diff --git a/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java b/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java new file mode 100644 index 0000000..f1886cc --- /dev/null +++ b/source/decompiled/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java @@ -0,0 +1,167 @@ +/* + * Decompiled with CFR 0.152. + * + * Could not load the following classes: + * org.bukkit.Bukkit + * org.bukkit.entity.Player + * org.bukkit.event.EventHandler + * org.bukkit.event.Listener + * org.bukkit.event.player.PlayerJoinEvent + * org.bukkit.event.player.PlayerQuitEvent + */ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.compatibility.PlayerHideEvent; +import de.maxhenkel.voicechat.compatibility.PlayerShowEvent; +import de.maxhenkel.voicechat.net.NetManager; +import de.maxhenkel.voicechat.net.PacketRateLimiter; +import de.maxhenkel.voicechat.net.RequestSecretPacket; +import de.maxhenkel.voicechat.net.SecretPacket; +import de.maxhenkel.voicechat.plugins.PluginManager; +import de.maxhenkel.voicechat.voice.common.Secret; +import de.maxhenkel.voicechat.voice.server.Server; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class ServerVoiceEvents +implements Listener { + private static final int LEGACY_COMPATIBILITY_VERSION = 18; + private static final int CURRENT_COMPATIBILITY_VERSION = 20; + private final Map clientCompatibilities = new ConcurrentHashMap(); + private final PacketRateLimiter rateLimiter; + private Server server; + + public ServerVoiceEvents() { + this.rateLimiter = new PacketRateLimiter(Voicechat.SERVER_CONFIG.tcpRateLimit.get()); + } + + public void init() { + if (this.server != null) { + return; + } + if (!Bukkit.getOnlineMode()) { + Voicechat.LOGGER.warn("Running in offline mode - Voice chat encryption is not secure!", new Object[0]); + } + Voicechat.compatibility.registerPlayerHideEvent(this::onPlayerHide); + Voicechat.compatibility.registerPlayerShowEvent(this::onPlayerShow); + this.server = new Server(); + this.server.start(); + PluginManager.instance().onServerStarted(); + } + + public void onRequestSecretPacket(Player player, RequestSecretPacket requestSecretPacket) { + UUID uUID; + int n = requestSecretPacket.getCompatibilityVersion(); + Voicechat.LOGGER.info("Received secret request of {} ({})", player.getName(), n); + try { + uUID = player.getUniqueId(); + } + catch (UnsupportedOperationException unsupportedOperationException) { + player.kickPlayer("Tried to authenticate voice chat while still connecting"); + Voicechat.LOGGER.warn("{} tried to authenticate voice chat while still connecting", player.getName()); + return; + } + this.clientCompatibilities.put(uUID, n); + if (!ServerVoiceEvents.isSupportedCompatibilityVersion(n)) { + Voicechat.LOGGER.warn("Connected client {} has incompatible voice chat version (server={}, client={})", player.getName(), Voicechat.COMPATIBILITY_VERSION, n); + ServerVoiceEvents.sendIncompatibleMessage(player, n); + return; + } + this.initializePlayerConnection(player); + } + + private static boolean isSupportedCompatibilityVersion(int n) { + return n == 18 || n == 20; + } + + public boolean isCompatible(Player player) { + return this.isCompatible(player.getUniqueId()); + } + + public boolean isCompatible(UUID uUID) { + return ServerVoiceEvents.isSupportedCompatibilityVersion(this.clientCompatibilities.getOrDefault(uUID, -1)); + } + + public static void sendIncompatibleMessage(Player player, int n) { + if (n <= 6) { + player.sendMessage(String.format(Voicechat.TRANSLATIONS.voicechatNotCompatibleMessage.get(), "2.6.x", "Simple Voice Chat")); + } else { + Voicechat.compatibility.sendIncompatibleMessage(player, "2.6.x", "Simple Voice Chat"); + } + } + + public void initializePlayerConnection(Player player) { + if (this.server == null) { + return; + } + UUID uUID = player.getUniqueId(); + this.server.getPlayerStateManager().onPlayerCompatibilityCheckSucceeded(player); + this.server.getCategoryManager().onPlayerCompatibilityCheckSucceeded(player); + this.server.getGroupManager().onPlayerCompatibilityCheckSucceeded(player); + Secret secret = this.server.generateNewSecret(uUID); + if (secret == null) { + Voicechat.LOGGER.warn("Player already requested secret - ignoring", new Object[0]); + return; + } + secret.setLegacyProtocol(this.clientCompatibilities.getOrDefault(uUID, -1) == 18); + String string = PluginManager.instance().getVoiceHost(player, Voicechat.SERVER_CONFIG.voiceHost.get()); + NetManager.sendToClient(player, new SecretPacket(player, secret, this.server.getPort(), Voicechat.SERVER_CONFIG, string)); + Voicechat.LOGGER.info("Sent secret to {}", player.getName()); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent playerJoinEvent) { + this.server.getPlayerStateManager().onPlayerJoin(playerJoinEvent); + if (!Voicechat.SERVER_CONFIG.forceVoiceChat.get().booleanValue()) { + return; + } + Player player = playerJoinEvent.getPlayer(); + Voicechat.compatibility.runTaskLater(() -> { + if (!player.isOnline()) { + return; + } + if (!this.isCompatible(player)) { + player.kickPlayer(String.format(Voicechat.TRANSLATIONS.forceVoicechatKickMessage.get(), "Simple Voice Chat", Voicechat.INSTANCE.getDescription().getVersion())); + } + }, (long)Voicechat.SERVER_CONFIG.loginTimeout.get().intValue() / 50L); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent playerQuitEvent) { + this.server.getPlayerStateManager().onPlayerQuit(playerQuitEvent); + this.server.getGroupManager().onPlayerQuit(playerQuitEvent); + UUID uUID = playerQuitEvent.getPlayer().getUniqueId(); + this.clientCompatibilities.remove(uUID); + this.rateLimiter.onPlayerLoggedOut(playerQuitEvent.getPlayer()); + if (this.server == null) { + return; + } + this.server.disconnectClient(uUID); + Voicechat.LOGGER.info("Disconnecting client {}", playerQuitEvent.getPlayer().getName()); + } + + public void onPlayerHide(PlayerHideEvent playerHideEvent) { + this.server.getPlayerStateManager().onPlayerHide(playerHideEvent); + } + + public void onPlayerShow(PlayerShowEvent playerShowEvent) { + this.server.getPlayerStateManager().onPlayerShow(playerShowEvent); + } + + public PacketRateLimiter getRateLimiter() { + return this.rateLimiter; + } + + public Server getServer() { + return this.server; + } +} + diff --git a/source/decompiled/summary.txt b/source/decompiled/summary.txt new file mode 100644 index 0000000..559f9a2 --- /dev/null +++ b/source/decompiled/summary.txt @@ -0,0 +1,2 @@ +Summary for A:\Projekty\voicechat\git\AsminVoiceChat-1.0.0.jar +Decompiled with CFR 0.152 diff --git a/source/patch/de/maxhenkel/voicechat/voice/common/Secret.java b/source/patch/de/maxhenkel/voicechat/voice/common/Secret.java new file mode 100644 index 0000000..9c3e451 --- /dev/null +++ b/source/patch/de/maxhenkel/voicechat/voice/common/Secret.java @@ -0,0 +1,137 @@ +package de.maxhenkel.voicechat.voice.common; + +import io.netty.buffer.ByteBuf; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Arrays; +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class Secret { + public static final int SECRET_SIZE_BYTES = 16; + public static final int IV_SIZE_BYTES = 12; + public static final int TAG_LEN_BITS = 128; + public static final String CIPHER = "AES/GCM/NoPadding"; + private static final SecureRandom RANDOM = new SecureRandom(); + private final byte[] secret; + private final SecretKeySpec keySpec; + private volatile boolean legacyProtocol; + + protected Secret(byte[] secret) { + if (secret == null || secret.length != SECRET_SIZE_BYTES) { + throw new IllegalArgumentException("Voice chat secret must contain exactly 16 bytes"); + } + this.secret = secret.clone(); + this.keySpec = new SecretKeySpec(this.secret, "AES"); + } + + public static Secret generateNewRandomSecret() { + byte[] secret = new byte[SECRET_SIZE_BYTES]; + RANDOM.nextBytes(secret); + return new Secret(secret); + } + + public static Secret fromBytes(byte[] secret) { + return new Secret(secret); + } + + public static Secret fromBytes(ByteBuf buf) { + byte[] secretBytes = new byte[SECRET_SIZE_BYTES]; + buf.readBytes(secretBytes); + return Secret.fromBytes(secretBytes); + } + + public void toBytes(ByteBuf buf) { + buf.writeBytes(this.secret); + } + + public byte[] getSecret() { + return this.secret.clone(); + } + + public SecretKeySpec getKeySpec() { + return this.keySpec; + } + + public void setLegacyProtocol(boolean legacyProtocol) { + this.legacyProtocol = legacyProtocol; + } + + public boolean isLegacyProtocol() { + return this.legacyProtocol; + } + + public static byte[] generateIV() { + byte[] iv = new byte[IV_SIZE_BYTES]; + RANDOM.nextBytes(iv); + return iv; + } + + private static byte[] generateLegacyIV() { + byte[] iv = new byte[16]; + RANDOM.nextBytes(iv); + return iv; + } + + public byte[] encrypt(byte[] data) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + if (this.legacyProtocol) { + byte[] iv = Secret.generateLegacyIV(); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, (Key) this.keySpec, new IvParameterSpec(iv)); + byte[] encrypted = cipher.doFinal(data); + byte[] payload = new byte[iv.length + encrypted.length]; + System.arraycopy(iv, 0, payload, 0, iv.length); + System.arraycopy(encrypted, 0, payload, iv.length, encrypted.length); + return payload; + } + byte[] iv = Secret.generateIV(); + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(Cipher.ENCRYPT_MODE, (Key) this.keySpec, new GCMParameterSpec(TAG_LEN_BITS, iv)); + byte[] encrypted = cipher.doFinal(data); + byte[] payload = new byte[iv.length + encrypted.length]; + System.arraycopy(iv, 0, payload, 0, iv.length); + System.arraycopy(encrypted, 0, payload, iv.length, encrypted.length); + return payload; + } + + public byte[] decrypt(byte[] payload) throws NoSuchPaddingException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException { + if (this.legacyProtocol) { + if (payload.length < 32 || (payload.length - 16) % 16 != 0) { + throw new IllegalBlockSizeException("Invalid legacy AES-CBC packet size"); + } + byte[] iv = Arrays.copyOfRange(payload, 0, 16); + byte[] encrypted = Arrays.copyOfRange(payload, 16, payload.length); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, (Key) this.keySpec, new IvParameterSpec(iv)); + return cipher.doFinal(encrypted); + } + if (payload.length < IV_SIZE_BYTES + 16) { + throw new IllegalBlockSizeException("Invalid AES-GCM packet size"); + } + byte[] iv = Arrays.copyOfRange(payload, 0, IV_SIZE_BYTES); + byte[] encrypted = Arrays.copyOfRange(payload, IV_SIZE_BYTES, payload.length); + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(Cipher.DECRYPT_MODE, (Key) this.keySpec, new GCMParameterSpec(TAG_LEN_BITS, iv)); + return cipher.doFinal(encrypted); + } + + @Override + public boolean equals(Object o) { + return o instanceof Secret && Arrays.equals(this.secret, ((Secret) o).secret); + } + + @Override + public int hashCode() { + return Arrays.hashCode(this.secret); + } +} diff --git a/source/patch/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java b/source/patch/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java new file mode 100644 index 0000000..6c4fe38 --- /dev/null +++ b/source/patch/de/maxhenkel/voicechat/voice/server/ServerVoiceEvents.java @@ -0,0 +1,154 @@ +package de.maxhenkel.voicechat.voice.server; + +import de.maxhenkel.voicechat.Voicechat; +import de.maxhenkel.voicechat.compatibility.PlayerHideEvent; +import de.maxhenkel.voicechat.compatibility.PlayerShowEvent; +import de.maxhenkel.voicechat.net.NetManager; +import de.maxhenkel.voicechat.net.PacketRateLimiter; +import de.maxhenkel.voicechat.net.RequestSecretPacket; +import de.maxhenkel.voicechat.net.SecretPacket; +import de.maxhenkel.voicechat.plugins.PluginManager; +import de.maxhenkel.voicechat.voice.common.Secret; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class ServerVoiceEvents implements Listener { + private static final int LEGACY_COMPATIBILITY_VERSION = 18; + private static final int CURRENT_COMPATIBILITY_VERSION = 20; + + private final Map clientCompatibilities = new ConcurrentHashMap<>(); + private final PacketRateLimiter rateLimiter; + private Server server; + + public ServerVoiceEvents() { + this.rateLimiter = new PacketRateLimiter(Voicechat.SERVER_CONFIG.tcpRateLimit.get()); + } + + public void init() { + if (this.server != null) { + return; + } + if (!Bukkit.getOnlineMode()) { + Voicechat.LOGGER.warn("Running in offline mode - Voice chat encryption is not secure!", new Object[0]); + } + Voicechat.compatibility.registerPlayerHideEvent(this::onPlayerHide); + Voicechat.compatibility.registerPlayerShowEvent(this::onPlayerShow); + this.server = new Server(); + this.server.start(); + PluginManager.instance().onServerStarted(); + } + + public void onRequestSecretPacket(Player player, RequestSecretPacket packet) { + UUID playerUUID; + int clientVersion = packet.getCompatibilityVersion(); + Voicechat.LOGGER.info("Received secret request of {} ({})", player.getName(), clientVersion); + try { + playerUUID = player.getUniqueId(); + } catch (UnsupportedOperationException e) { + player.kickPlayer("Tried to authenticate voice chat while still connecting"); + Voicechat.LOGGER.warn("{} tried to authenticate voice chat while still connecting", player.getName()); + return; + } + this.clientCompatibilities.put(playerUUID, clientVersion); + if (!ServerVoiceEvents.isSupportedCompatibilityVersion(clientVersion)) { + Voicechat.LOGGER.warn("Connected client {} has incompatible voice chat version (server={}, client={})", + player.getName(), Voicechat.COMPATIBILITY_VERSION, clientVersion); + ServerVoiceEvents.sendIncompatibleMessage(player, clientVersion); + return; + } + this.initializePlayerConnection(player); + } + + private static boolean isSupportedCompatibilityVersion(int version) { + return version == LEGACY_COMPATIBILITY_VERSION || version == CURRENT_COMPATIBILITY_VERSION; + } + + public boolean isCompatible(Player player) { + return this.isCompatible(player.getUniqueId()); + } + + public boolean isCompatible(UUID playerUuid) { + return ServerVoiceEvents.isSupportedCompatibilityVersion(this.clientCompatibilities.getOrDefault(playerUuid, -1)); + } + + public static void sendIncompatibleMessage(Player player, int clientCompatibilityVersion) { + if (clientCompatibilityVersion <= 6) { + player.sendMessage(String.format(Voicechat.TRANSLATIONS.voicechatNotCompatibleMessage.get(), "2.6.x", "Simple Voice Chat")); + } else { + Voicechat.compatibility.sendIncompatibleMessage(player, "2.6.x", "Simple Voice Chat"); + } + } + + public void initializePlayerConnection(Player player) { + if (this.server == null) { + return; + } + UUID playerUuid = player.getUniqueId(); + this.server.getPlayerStateManager().onPlayerCompatibilityCheckSucceeded(player); + this.server.getCategoryManager().onPlayerCompatibilityCheckSucceeded(player); + this.server.getGroupManager().onPlayerCompatibilityCheckSucceeded(player); + Secret secret = this.server.generateNewSecret(playerUuid); + if (secret == null) { + Voicechat.LOGGER.warn("Player already requested secret - ignoring", new Object[0]); + return; + } + secret.setLegacyProtocol(this.clientCompatibilities.getOrDefault(playerUuid, -1) == LEGACY_COMPATIBILITY_VERSION); + String voiceHost = PluginManager.instance().getVoiceHost(player, Voicechat.SERVER_CONFIG.voiceHost.get()); + NetManager.sendToClient(player, new SecretPacket(player, secret, this.server.getPort(), Voicechat.SERVER_CONFIG, voiceHost)); + Voicechat.LOGGER.info("Sent secret to {}", player.getName()); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + this.server.getPlayerStateManager().onPlayerJoin(event); + if (!Voicechat.SERVER_CONFIG.forceVoiceChat.get().booleanValue()) { + return; + } + Player player = event.getPlayer(); + Voicechat.compatibility.runTaskLater(() -> { + if (!player.isOnline()) { + return; + } + if (!this.isCompatible(player)) { + player.kickPlayer(String.format(Voicechat.TRANSLATIONS.forceVoicechatKickMessage.get(), "Simple Voice Chat", Voicechat.INSTANCE.getDescription().getVersion())); + } + }, (long) Voicechat.SERVER_CONFIG.loginTimeout.get().intValue() / 50L); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + this.server.getPlayerStateManager().onPlayerQuit(event); + this.server.getGroupManager().onPlayerQuit(event); + UUID playerUuid = event.getPlayer().getUniqueId(); + this.clientCompatibilities.remove(playerUuid); + this.rateLimiter.onPlayerLoggedOut(event.getPlayer()); + if (this.server == null) { + return; + } + this.server.disconnectClient(playerUuid); + Voicechat.LOGGER.info("Disconnecting client {}", event.getPlayer().getName()); + } + + public void onPlayerHide(PlayerHideEvent event) { + this.server.getPlayerStateManager().onPlayerHide(event); + } + + public void onPlayerShow(PlayerShowEvent event) { + this.server.getPlayerStateManager().onPlayerShow(event); + } + + public PacketRateLimiter getRateLimiter() { + return this.rateLimiter; + } + + public Server getServer() { + return this.server; + } +} diff --git a/source/resources/CONCENTUS_LICENSE b/source/resources/CONCENTUS_LICENSE new file mode 100644 index 0000000..62d21b6 --- /dev/null +++ b/source/resources/CONCENTUS_LICENSE @@ -0,0 +1,37 @@ +Copyright (c) by various holding parties, including (but not limited to): +Skype Limited, Xiph.Org Foundation, CSIRO, Microsoft Corporation, +Jean-Marc Valin, Gregory Maxwell, Mark Borgerding, Timothy B. Terriberry, +Logan Stromberg. All rights are reserved by their respective holders. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of Internet Society, IETF or IETF Trust, nor the + names of specific contributors, may be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +This repository and its redistributable packages contain independently compiled +versions of the Opus C reference library, which is maintained by Xiph.org and the +Opus open-source contributors. The source code for these libraries is freely available +at https://gitlab.xiph.org/xiph/opus/-/tags/v1.5.2, and all binaries are being +redistributed to you under the same terms of the general Opus license dictated above. \ No newline at end of file diff --git a/source/resources/META-INF/MANIFEST.MF b/source/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..936a396 --- /dev/null +++ b/source/resources/META-INF/MANIFEST.MF @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +Class-Path: voicechat-api-2.6.25.jar configbuilder-2.1.0.jar json-202512 + 24.jar commodore-2.2.jar Concentus-1.0.1.jar commodore-file-1.0.jar bri + gadier-1.0.18.jar +Multi-Release: true + diff --git a/source/resources/META-INF/maven/de.maxhenkel.configbuilder/configbuilder/pom.properties b/source/resources/META-INF/maven/de.maxhenkel.configbuilder/configbuilder/pom.properties new file mode 100644 index 0000000..083c94a --- /dev/null +++ b/source/resources/META-INF/maven/de.maxhenkel.configbuilder/configbuilder/pom.properties @@ -0,0 +1,3 @@ +artifactId=configbuilder +groupId=de.maxhenkel.configbuilder +version=2.1.0 diff --git a/source/resources/META-INF/maven/de.maxhenkel.configbuilder/configbuilder/pom.xml b/source/resources/META-INF/maven/de.maxhenkel.configbuilder/configbuilder/pom.xml new file mode 100644 index 0000000..8ffeade --- /dev/null +++ b/source/resources/META-INF/maven/de.maxhenkel.configbuilder/configbuilder/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + de.maxhenkel.configbuilder + configbuilder + 2.1.0 + + + 8 + 8 + UTF-8 + + + + + com.google.code.findbugs + jsr305 + 3.0.2 + provided + + + org.junit.jupiter + junit-jupiter + 5.9.2 + test + + + org.mockito + mockito-core + 5.4.0 + test + + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + verify + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.2 + + -Dfile.encoding=UTF-8 + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + + + + + henkelmax.public + https://maven.maxhenkel.de/repository/public + + + + \ No newline at end of file diff --git a/source/resources/META-INF/maven/me.lucko/commodore/pom.properties b/source/resources/META-INF/maven/me.lucko/commodore/pom.properties new file mode 100644 index 0000000..dd61b5d --- /dev/null +++ b/source/resources/META-INF/maven/me.lucko/commodore/pom.properties @@ -0,0 +1,5 @@ +#Generated by Maven +#Wed Aug 03 22:00:27 BST 2022 +groupId=me.lucko +artifactId=commodore +version=2.2 diff --git a/source/resources/META-INF/maven/me.lucko/commodore/pom.xml b/source/resources/META-INF/maven/me.lucko/commodore/pom.xml new file mode 100644 index 0000000..05aeec3 --- /dev/null +++ b/source/resources/META-INF/maven/me.lucko/commodore/pom.xml @@ -0,0 +1,162 @@ + + + 4.0.0 + + me.lucko + commodore + 2.2 + + commodore + Utility for using Minecraft's 1.13 'brigadier' library in Bukkit plugins. + https://github.com/lucko/commodore + + + + MIT + https://opensource.org/licenses/MIT + + + + + + Luck + git@lucko.me + https://github.com/lucko + + + + + scm:git:https://github.com/lucko/commodore.git + scm:git:git@github.com:lucko/commodore.git + https://github.com/lucko/commodore + + + + UTF-8 + 1.8 + 1.8 + + + + + me.lucko + commodore-file + 1.0 + compile + + + com.mojang + brigadier + + + + + com.mojang + brigadier + 1.0.18 + compile + + + com.google.guava + guava + + + + + io.papermc.paper + paper-api + 1.19.1-R0.1-SNAPSHOT + provided + + + io.papermc.paper + paper-mojangapi + 1.19.1-R0.1-SNAPSHOT + provided + + + + + + minecraft-repo + https://libraries.minecraft.net/ + + + papermc + https://repo.papermc.io/repository/maven-public/ + + + + + + sign + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + + ossrh + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + deployment + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.4.0 + + + attach-javadocs + + jar + + + + + + + + + + \ No newline at end of file diff --git a/source/resources/META-INF/maven/org.concentus/Concentus/pom.properties b/source/resources/META-INF/maven/org.concentus/Concentus/pom.properties new file mode 100644 index 0000000..d1e58be --- /dev/null +++ b/source/resources/META-INF/maven/org.concentus/Concentus/pom.properties @@ -0,0 +1,3 @@ +artifactId=Concentus +groupId=org.concentus +version=1.0.1 diff --git a/source/resources/META-INF/maven/org.concentus/Concentus/pom.xml b/source/resources/META-INF/maven/org.concentus/Concentus/pom.xml new file mode 100644 index 0000000..7352e4a --- /dev/null +++ b/source/resources/META-INF/maven/org.concentus/Concentus/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + org.concentus + Concentus + 1.0.1 + jar + + UTF-8 + 1.8 + 1.8 + + Concentus + This package is a pure portable Java implementation of the Opus audio compression codec (see + https://opus-codec.org/ for more details). This package contains the Opus encoder, decoder, multistream codecs, + and repacketizer. It does NOT contain code to parse .ogg or .opus container files or to manage RTP packet + streams. + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.14.0 + + 8 + 8 + + + + maven-antrun-plugin + 3.1.0 + + + copy-and-rename-license + process-resources + + run + + + + + + + + + + + + + + + henkelmax.public + https://maven.maxhenkel.de/repository/public + + + + \ No newline at end of file diff --git a/source/resources/META-INF/maven/org.json/json/pom.properties b/source/resources/META-INF/maven/org.json/json/pom.properties new file mode 100644 index 0000000..8683715 --- /dev/null +++ b/source/resources/META-INF/maven/org.json/json/pom.properties @@ -0,0 +1,3 @@ +version=20251224 +groupId=org.json +artifactId=json diff --git a/source/resources/META-INF/maven/org.json/json/pom.xml b/source/resources/META-INF/maven/org.json/json/pom.xml new file mode 100644 index 0000000..6269edc --- /dev/null +++ b/source/resources/META-INF/maven/org.json/json/pom.xml @@ -0,0 +1,263 @@ + + 4.0.0 + + org.json + json + 20251224 + bundle + + JSON in Java + + JSON is a light-weight, language independent, data interchange format. + See http://www.JSON.org/ + + The files in this package implement JSON encoders/decoders in Java. + It also includes the capability to convert between JSON and XML, HTTP + headers, Cookies, and CDL. + + This is a reference implementation. There are a large number of JSON packages + in Java. Perhaps someday the Java community will standardize on one. Until + then, choose carefully. + + https://github.com/douglascrockford/JSON-java + + + https://github.com/douglascrockford/JSON-java.git + scm:git:git://github.com/douglascrockford/JSON-java.git + scm:git:git@github.com:douglascrockford/JSON-java.git + + + + + Public Domain + https://github.com/stleary/JSON-java/blob/master/LICENSE + repo + + + + + + Douglas Crockford + douglas@crockford.com + + + + + UTF-8 + + + + + + ossrh + Central Repository OSSRH + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + + + + + junit + junit + 4.13.2 + test + + + com.jayway.jsonpath + json-path + 2.9.0 + test + + + org.mockito + mockito-core + 4.2.0 + test + + + + + + + org.sonatype.central + central-publishing-maven-plugin + 0.9.0 + true + + central + + + + org.apache.felix + maven-bundle-plugin + 5.1.9 + true + + + + org.json + + ${project.artifactId} + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 1.8 + 1.8 + + -Xlint:unchecked + + + + + org.apache.maven.plugins + maven-source-plugin + 3.3.0 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.5.0 + + + attach-javadocs + + jar + + + -Xdoclint:none + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + --pinentry-mode + loopback + + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.3 + true + + ossrh + https://oss.sonatype.org/ + false + + + + org.moditect + moditect-maven-plugin + 1.0.0.Final + + + add-module-infos + package + + add-module-info + + + 9 + + + module org.json { + exports org.json; + } + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + + + test-strict-mode + + + + com.google.code.maven-replacer-plugin + replacer + 1.5.3 + + + + enable-strict-mode + process-sources + + replace + + + src/main/java/org/json/JSONParserConfiguration.java + + + // this.strictMode = true; + this.strictMode = true; + + + + + + + restore-original + test + + replace + + + src/main/java/org/json/JSONParserConfiguration.java + + + this.strictMode = true; + // this.strictMode = true; + + + + + + + + + + + + diff --git a/source/resources/plugin.yml b/source/resources/plugin.yml new file mode 100644 index 0000000..5803eba --- /dev/null +++ b/source/resources/plugin.yml @@ -0,0 +1,28 @@ +name: AsminVoicechat +version: 1.0.0 +main: de.maxhenkel.voicechat.Voicechat +api-version: '1.13' +prefix: AsminVoicechat +authors: + - Asmin Studios (asmin.me) + - Max Henkel + - Matthew Wells +description: Simple Voice Chat server fork with support for 2.5 and 2.6 clients +website: https://git.asmin.me/asminme/AsminVoicechat +load: STARTUP +provides: [voicechat] +folia-supported: true +commands: + voicechat: + description: Manage voice chat + usage: Invalid command syntax +permissions: + voicechat.listen: + description: Allows to listen to voice chat + voicechat.speak: + description: Allows to speak in voice chat + voicechat.groups: + description: Allows to join groups + voicechat.admin: + description: Allows to test voice chat connections +softdepend: [PlaceholderAPI, ViaVersion] diff --git a/tests/ProtocolCryptoSelfTest.java b/tests/ProtocolCryptoSelfTest.java new file mode 100644 index 0000000..ea68a28 --- /dev/null +++ b/tests/ProtocolCryptoSelfTest.java @@ -0,0 +1,71 @@ +import de.maxhenkel.voicechat.voice.common.Secret; +import java.nio.ByteBuffer; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.UUID; +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class ProtocolCryptoSelfTest { + public static void main(String[] args) throws Exception { + byte[] key = new byte[16]; + for (int i = 0; i < key.length; i++) key[i] = (byte) (i * 13 + 7); + UUID legacyKey = uuidFromBytes(key); + Secret hybridSecret = Secret.fromBytes(key); + byte[] message = "SVC dual-protocol crypto self-test".getBytes("UTF-8"); + + hybridSecret.setLegacyProtocol(true); + for (int i = 0; i < 100; i++) { + byte[] oldClientPacket = legacyEncrypt(legacyKey, message); + if (!Arrays.equals(message, hybridSecret.decrypt(oldClientPacket))) { + throw new AssertionError("Hybrid server failed to decrypt a 2.5 CBC packet"); + } + byte[] hybridServerPacket = hybridSecret.encrypt(message); + if (!Arrays.equals(message, legacyDecrypt(legacyKey, hybridServerPacket))) { + throw new AssertionError("2.5 client failed to decrypt a hybrid server CBC packet"); + } + } + + hybridSecret.setLegacyProtocol(false); + for (int i = 0; i < 100; i++) { + byte[] packet = hybridSecret.encrypt(message); + if (!Arrays.equals(message, hybridSecret.decrypt(packet))) { + throw new AssertionError("Hybrid server AES-GCM round-trip failed"); + } + } + System.out.println("PASS: 100 CBC client/server interop cycles and 100 GCM round-trips"); + } + + private static UUID uuidFromBytes(byte[] bytes) { + ByteBuffer buffer = ByteBuffer.wrap(bytes); + return new UUID(buffer.getLong(), buffer.getLong()); + } + + private static byte[] legacyEncrypt(UUID secret, byte[] data) throws Exception { + byte[] iv = new byte[16]; + new SecureRandom().nextBytes(iv); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.ENCRYPT_MODE, key(secret), new IvParameterSpec(iv)); + byte[] encrypted = cipher.doFinal(data); + byte[] payload = Arrays.copyOf(iv, iv.length + encrypted.length); + System.arraycopy(encrypted, 0, payload, iv.length, encrypted.length); + return payload; + } + + private static byte[] legacyDecrypt(UUID secret, byte[] payload) throws Exception { + byte[] iv = Arrays.copyOfRange(payload, 0, 16); + byte[] encrypted = Arrays.copyOfRange(payload, 16, payload.length); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, key(secret), new IvParameterSpec(iv)); + return cipher.doFinal(encrypted); + } + + private static SecretKeySpec key(UUID secret) { + byte[] key = ByteBuffer.allocate(16) + .putLong(secret.getMostSignificantBits()) + .putLong(secret.getLeastSignificantBits()) + .array(); + return new SecretKeySpec(key, "AES"); + } +}

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