├── src ├── main │ ├── res │ │ ├── values-eo │ │ │ └── strings.xml │ │ ├── values-it │ │ │ └── strings.xml │ │ ├── values-pl │ │ │ └── strings.xml │ │ ├── values-ja │ │ │ └── strings.xml │ │ ├── values-ru │ │ │ └── strings.xml │ │ ├── values-nl │ │ │ └── strings.xml │ │ ├── values-es │ │ │ └── strings.xml │ │ ├── values-fr │ │ │ └── strings.xml │ │ ├── values-de │ │ │ └── strings.xml │ │ └── values │ │ │ └── strings.xml │ ├── jni │ │ ├── Application.mk │ │ ├── Android.mk │ │ ├── celt-0.7.0-build │ │ │ └── config.h │ │ └── celt-0.11.0-build │ │ │ └── config.h │ ├── java │ │ └── com │ │ │ └── morlunk │ │ │ └── jumble │ │ │ ├── audio │ │ │ ├── IAudioMixerSource.java │ │ │ ├── IAudioMixer.java │ │ │ ├── BasicClippingShortMixer.java │ │ │ ├── InvalidSampleRateException.java │ │ │ ├── inputmode │ │ │ │ ├── ContinuousInputMode.java │ │ │ │ ├── IInputMode.java │ │ │ │ ├── ActivityInputMode.java │ │ │ │ └── ToggleInputMode.java │ │ │ ├── AudioUser.java │ │ │ ├── IDecoder.java │ │ │ ├── encoder │ │ │ │ ├── IEncoder.java │ │ │ │ ├── ResamplingEncoder.java │ │ │ │ ├── PreprocessingEncoder.java │ │ │ │ ├── CELT11Encoder.java │ │ │ │ ├── CELT7Encoder.java │ │ │ │ └── OpusEncoder.java │ │ │ ├── BluetoothScoReceiver.java │ │ │ └── javacpp │ │ │ │ ├── CELT11.java │ │ │ │ ├── CELT7.java │ │ │ │ └── Opus.java │ │ │ ├── util │ │ │ ├── JumbleDisconnectedException.java │ │ │ ├── JumbleLogger.java │ │ │ ├── VoiceTargetMode.java │ │ │ ├── MessageFormatter.java │ │ │ ├── IJumbleObserver.java │ │ │ ├── MumbleURLParser.java │ │ │ ├── JumbleObserver.java │ │ │ ├── JumbleException.java │ │ │ ├── JumbleNetworkListener.java │ │ │ └── JumbleCallbacks.java │ │ │ ├── net │ │ │ ├── JumbleUDPMessageType.java │ │ │ ├── Permissions.java │ │ │ ├── JumbleTCPMessageType.java │ │ │ ├── JumbleNetworkThread.java │ │ │ ├── JumbleCertificateGenerator.java │ │ │ └── JumbleSSLSocketFactory.java │ │ │ ├── model │ │ │ ├── IMessage.java │ │ │ ├── WhisperTarget.java │ │ │ ├── WhisperTargetUsers.java │ │ │ ├── IChannel.java │ │ │ ├── IUser.java │ │ │ ├── TalkState.java │ │ │ ├── WhisperTargetChannel.java │ │ │ ├── WhisperTargetList.java │ │ │ ├── Server.java │ │ │ └── Message.java │ │ │ ├── exception │ │ │ ├── NotConnectedException.java │ │ │ ├── AudioException.java │ │ │ ├── NotSynchronizedException.java │ │ │ ├── NativeAudioException.java │ │ │ └── AudioInitializationException.java │ │ │ ├── protocol │ │ │ ├── JumbleUDPMessageListener.java │ │ │ └── JumbleTCPMessageListener.java │ │ │ ├── Constants.java │ │ │ └── IJumbleService.java │ └── AndroidManifest.xml └── androidTest │ └── java │ └── com │ └── morlunk │ └── jumble │ └── test │ ├── JumbleServiceTest.java │ ├── MixerTest.java │ ├── ModelTest.java │ ├── EncoderTest.java │ └── URLParserTest.java ├── tools ├── javacpp-0.7.jar ├── jnigen.sh └── mkp12.sh ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── libs ├── sc-morlunk-pkix-1.51.0.0.jar └── sc-morlunk-prov-1.51.0.0.jar ├── .gitignore ├── .gitmodules ├── README.md ├── gradlew.bat └── gradlew /src/main/res/values-eo/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /tools/javacpp-0.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acomminos/Jumble/HEAD/tools/javacpp-0.7.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acomminos/Jumble/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /libs/sc-morlunk-pkix-1.51.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acomminos/Jumble/HEAD/libs/sc-morlunk-pkix-1.51.0.0.jar -------------------------------------------------------------------------------- /libs/sc-morlunk-prov-1.51.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/acomminos/Jumble/HEAD/libs/sc-morlunk-prov-1.51.0.0.jar -------------------------------------------------------------------------------- /src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | #APP_OPTIM := debug 2 | APP_ABI := armeabi-v7a armeabi x86 3 | APP_STL := gnustl_static -------------------------------------------------------------------------------- /src/main/res/values-it/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Server 4 | 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle / Android Studio 2 | build/ 3 | .idea/ 4 | .gradle/ 5 | *.iml 6 | 7 | # Android 8 | local.properties 9 | 10 | # Native 11 | src/main/libs/ 12 | src/main/obj/ 13 | 14 | # Java 15 | *.class 16 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-1.12-all.zip 7 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/IAudioMixerSource.java: -------------------------------------------------------------------------------- 1 | package com.morlunk.jumble.audio; 2 | 3 | /** 4 | * A source for an {@link IAudioMixer}. 5 | * Stores samples in a collection of type {@link T}. 6 | */ 7 | public interface IAudioMixerSource { 8 | T getSamples(); 9 | int getNumSamples(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/IAudioMixer.java: -------------------------------------------------------------------------------- 1 | package com.morlunk.jumble.audio; 2 | 3 | import java.util.Collection; 4 | 5 | /** 6 | * A mixer for {@link IAudioMixerSource}s, where {@link T} is the source buffer type and {@link U} 7 | * is the destination buffer type. 8 | */ 9 | public interface IAudioMixer { 10 | void mix(Collection> sources, U buffer, int bufferOffset, 11 | int bufferLength); 12 | } 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/main/jni/opus"] 2 | path = src/main/jni/opus 3 | url = https://git.xiph.org/opus.git 4 | [submodule "src/main/jni/celt-0.11.0-src"] 5 | path = src/main/jni/celt-0.11.0-src 6 | url = https://git.xiph.org/celt.git 7 | [submodule "src/main/jni/celt-0.7.0-src"] 8 | path = src/main/jni/celt-0.7.0-src 9 | url = https://git.xiph.org/celt.git 10 | [submodule "src/main/jni/speex"] 11 | path = src/main/jni/speex 12 | url = https://git.xiph.org/speex.git 13 | -------------------------------------------------------------------------------- /tools/jnigen.sh: -------------------------------------------------------------------------------- 1 | # Generates the necessary JNI to use native audio libraries using JavaCPP. 2 | # Call this from the project root. 3 | if [ "$1" == "--build" ]; then 4 | # Build classes if we provide --build 5 | ./gradlew assembleDebug 6 | fi 7 | 8 | java -jar tools/javacpp-0.7.jar -cp build/intermediates/classes/debug/ -d src/main/jni/ -nocompile com.morlunk.jumble.audio.javacpp.* 9 | 10 | if [ "$1" == "--build" ]; then 11 | # Build native libs 12 | ndk-build -C src/main/jni 13 | fi 14 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/JumbleDisconnectedException.java: -------------------------------------------------------------------------------- 1 | package com.morlunk.jumble.util; 2 | 3 | /** 4 | * Called when a 5 | * Created by andrew on 01/03/17. 6 | */ 7 | 8 | public class JumbleDisconnectedException extends RuntimeException { 9 | public JumbleDisconnectedException() { 10 | super("Caller attempted to use the protocol while disconnected."); 11 | } 12 | 13 | public JumbleDisconnectedException(String reason) { 14 | super(reason); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tools/mkp12.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright (C) 2013 Andrew Comminos 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | if [ "$1" == "" ]; then 17 | echo "You need to specify an output path." 18 | exit 1 19 | fi 20 | 21 | openssl req -nodes -batch -newkey rsa:2048 -x509 -days 30 -keyout jumble.key -out jumble.crt 22 | openssl pkcs12 -export -out $1 -inkey jumble.key -in jumble.crt 23 | rm jumble.crt 24 | rm jumble.key -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/BasicClippingShortMixer.java: -------------------------------------------------------------------------------- 1 | package com.morlunk.jumble.audio; 2 | 3 | import java.util.Collection; 4 | 5 | /** 6 | * A simple mixer that downsamples source floating point PCM to shorts, clipping naively. 7 | */ 8 | public class BasicClippingShortMixer implements IAudioMixer { 9 | @Override 10 | public void mix(Collection> sources, short[] buffer, int bufferOffset, 11 | int bufferLength) { 12 | for (int i = 0; i < bufferLength; i++) { 13 | float mix = 0; 14 | for (IAudioMixerSource source : sources) { 15 | mix += source.getSamples()[i]; 16 | } 17 | // Clip to [-1,1]. 18 | if (mix > 1) 19 | mix = 1; 20 | else if (mix < -1) 21 | mix = -1; 22 | buffer[i + bufferOffset] = (short) (mix * Short.MAX_VALUE); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/net/JumbleUDPMessageType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.net; 19 | 20 | public enum JumbleUDPMessageType { 21 | UDPVoiceCELTAlpha, 22 | UDPPing, 23 | UDPVoiceSpeex, 24 | UDPVoiceCELTBeta, 25 | UDPVoiceOpus 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/JumbleLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.util; 19 | 20 | import com.morlunk.jumble.model.Message; 21 | 22 | /** 23 | * An interface for reporting user-readable information. 24 | * Created by andrew on 12/07/14. 25 | */ 26 | public interface JumbleLogger { 27 | void logInfo(String message); 28 | void logWarning(String message); 29 | void logError(String message); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/res/values-pl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | %1$s został przeniesiony z kanału %2$s przez %3$s. 4 | Ogłuszony oraz wyciszony mikrofon. 5 | Wyciszony mikrofon 6 | Wyłączono wyciszenie mikrofonu. 7 | Wyłączono ogłuszenie oraz wyciszenie mikrofonu. 8 | %s jest teraz ogłuszony oraz wyciszony. 9 | %s jest teraz wyciszony. 10 | %s nie jest już wyciszony. 11 | %s nie jest już ogłuszony. 12 | %s połączony. 13 | %s rozłączony. 14 | %1$s został przeniesiony z kanału %2$s przez %3$s. 15 | Serwer 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/InvalidSampleRateException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio; 19 | 20 | /** 21 | * Created by andrew on 23/04/14. 22 | */ 23 | public class InvalidSampleRateException extends Exception { 24 | public InvalidSampleRateException(Exception e) { 25 | super(e); 26 | } 27 | 28 | public InvalidSampleRateException(String message) { 29 | super(message); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/IMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | import java.util.List; 21 | 22 | public interface IMessage { 23 | int getActor(); 24 | 25 | String getActorName(); 26 | 27 | List getTargetChannels(); 28 | 29 | List getTargetTrees(); 30 | 31 | List getTargetUsers(); 32 | 33 | String getMessage(); 34 | 35 | long getReceivedTime(); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/inputmode/ContinuousInputMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.inputmode; 19 | 20 | /** 21 | * An input mode that always transmits audio. 22 | * Created by andrew on 13/02/16. 23 | */ 24 | public class ContinuousInputMode implements IInputMode { 25 | @Override 26 | public boolean shouldTransmit(short[] pcm, int length) { 27 | return true; 28 | } 29 | 30 | @Override 31 | public void waitForInput() { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/exception/NotConnectedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.exception; 19 | 20 | /** 21 | * Thrown when a Jumble connection has not yet been established. 22 | * Created by andrew on 24/10/15. 23 | */ 24 | public class NotConnectedException extends Exception { 25 | public NotConnectedException() { 26 | super("Not yet connected"); 27 | } 28 | 29 | public NotConnectedException(String message) { 30 | super(message); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/exception/AudioException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.exception; 19 | 20 | /** 21 | * Created by andrew on 28/04/14. 22 | */ 23 | public class AudioException extends Exception { 24 | public AudioException(String message) { 25 | super(message); 26 | } 27 | 28 | public AudioException(Throwable throwable) { 29 | super(throwable); 30 | } 31 | 32 | public AudioException(String message, Throwable throwable) { 33 | super(message, throwable); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/WhisperTarget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | import com.morlunk.jumble.protobuf.Mumble; 21 | 22 | /** 23 | * Created by andrew on 28/04/16. 24 | */ 25 | public interface WhisperTarget { 26 | Mumble.VoiceTarget.Target createTarget(); 27 | 28 | /** 29 | * Returns a user-readable name for the whisper target, to display in the UI. 30 | * @return A channel name or list of users, depending on the implementation. 31 | */ 32 | String getName(); 33 | } -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/exception/NotSynchronizedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.exception; 19 | 20 | /** 21 | * Called when Jumble has not yet received the ServerSync message from the server. 22 | * Created by andrew on 24/10/15. 23 | */ 24 | public class NotSynchronizedException extends Exception { 25 | public NotSynchronizedException() { 26 | super("Not yet synchronized"); 27 | } 28 | 29 | public NotSynchronizedException(String message) { 30 | super(message); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/exception/NativeAudioException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.exception; 19 | 20 | /** 21 | * Created by andrew on 07/03/14. 22 | */ 23 | public class NativeAudioException extends AudioException { 24 | public NativeAudioException(String message) { 25 | super(message); 26 | } 27 | 28 | public NativeAudioException(Throwable throwable) { 29 | super(throwable); 30 | } 31 | 32 | public NativeAudioException(String message, Throwable throwable) { 33 | super(message, throwable); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/WhisperTargetUsers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | import com.morlunk.jumble.protobuf.Mumble; 21 | 22 | /** 23 | * Created by andrew on 28/04/16. 24 | */ 25 | public class WhisperTargetUsers implements WhisperTarget { 26 | @Override 27 | public Mumble.VoiceTarget.Target createTarget() { 28 | throw new UnsupportedOperationException(); // TODO 29 | } 30 | 31 | @Override 32 | public String getName() { 33 | throw new UnsupportedOperationException(); // TODO 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/exception/AudioInitializationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.exception; 19 | 20 | /** 21 | * Created by andrew on 28/04/14. 22 | */ 23 | public class AudioInitializationException extends AudioException { 24 | public AudioInitializationException(String message) { 25 | super(message); 26 | } 27 | 28 | public AudioInitializationException(Throwable throwable) { 29 | super(throwable); 30 | } 31 | 32 | public AudioInitializationException(String message, Throwable throwable) { 33 | super(message, throwable); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/IChannel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | import java.util.List; 21 | 22 | public interface IChannel { 23 | List getUsers(); 24 | 25 | int getId(); 26 | 27 | int getPosition(); 28 | 29 | boolean isTemporary(); 30 | 31 | IChannel getParent(); 32 | 33 | String getName(); 34 | 35 | String getDescription(); 36 | 37 | byte[] getDescriptionHash(); 38 | 39 | List getSubchannels(); 40 | 41 | int getSubchannelUserCount(); 42 | 43 | List getLinks(); 44 | 45 | int getPermissions(); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/protocol/JumbleUDPMessageListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.protocol; 19 | 20 | import com.morlunk.jumble.net.JumbleUDPMessageType; 21 | 22 | /** 23 | * Created by andrew on 21/01/14. 24 | */ 25 | 26 | public interface JumbleUDPMessageListener { 27 | 28 | public void messageUDPPing(byte[] data); 29 | public void messageVoiceData(byte[] data, JumbleUDPMessageType messageType); 30 | 31 | public static class Stub implements JumbleUDPMessageListener { 32 | 33 | public void messageUDPPing(byte[] data) {} 34 | public void messageVoiceData(byte[] data, JumbleUDPMessageType messageType) {} 35 | } 36 | } -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/net/Permissions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.net; 19 | 20 | /** 21 | * Created by andrew on 21/08/13. 22 | */ 23 | public class Permissions { 24 | public static final int None = 0x0, 25 | Write = 0x1, 26 | Traverse = 0x2, 27 | Enter = 0x4, 28 | Speak = 0x8, 29 | MuteDeafen = 0x10, 30 | Move = 0x20, 31 | MakeChannel = 0x40, 32 | LinkChannel = 0x80, 33 | Whisper = 0x100, 34 | TextMessage = 0x200, 35 | MakeTempChannel = 0x400, 36 | 37 | // Root channel only 38 | Kick = 0x10000, 39 | Ban = 0x20000, 40 | Register = 0x40000, 41 | SelfRegister = 0x80000, 42 | 43 | Cached = 0x8000000, 44 | All = 0xf07ff; 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/VoiceTargetMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.util; 19 | 20 | /** 21 | * Created by andrew on 29/04/16. 22 | */ 23 | public enum VoiceTargetMode { 24 | NORMAL, 25 | WHISPER, 26 | SERVER_LOOPBACK; 27 | 28 | public static VoiceTargetMode fromId(byte targetId) { 29 | if (targetId == 0) { 30 | return VoiceTargetMode.NORMAL; 31 | } else if (targetId > 0 && targetId < 31) { 32 | return VoiceTargetMode.WHISPER; 33 | } else if (targetId == 31) { 34 | return VoiceTargetMode.SERVER_LOOPBACK; 35 | } else { 36 | throw new IllegalArgumentException(); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jumble 2 | ====== 3 | 4 | An Android service implementation of the Mumble protocol, designed as the backend of [Plumble](https://www.github.com/acomminos/Plumble). 5 | 6 | About 7 | ----- 8 | 9 | The primary goal of the Jumble project is to encourage developers to embrace the Mumble protocol on Android through a free, complete, and stable implementation. At the moment, development is focused on improving stability and security. 10 | 11 | Prior to the release of Jumble, all implementations of the Mumble protocol on Android have been using the same non-free code developed by @pcgod. To ensure the unencumbered use of Jumble, no sources or derivatives will be copied from that project. 12 | 13 | Projects using Jumble 14 | --------------------- 15 | 16 | [Plumble 3.0+](https://www.github.com/acomminos/Plumble) 17 | 18 | Including in your project 19 | ------------------------- 20 | 21 | Jumble is a standard Android library project using the gradle build system. [See here for instructions on how to include it into your gradle project](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Referencing-a-Library). 22 | 23 | Currently, there is no tutorial to integrate Jumble with your project. In the mean time, please examine the exposed interface IJumbleService as well as Plumble's implementation. 24 | 25 | License 26 | ------- 27 | 28 | Jumble is now licensed under the GNU GPL v3+. See LICENSE. 29 | 30 | If you wish to incorporate Jumble into your proprietary project, please email me to discuss licensing. 31 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/net/JumbleTCPMessageType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.net; 19 | 20 | /** 21 | * Message types pulled from the Mumble project's 'Message.h' for protocol version 1.2.4. 22 | */ 23 | public enum JumbleTCPMessageType { 24 | Version, 25 | UDPTunnel, 26 | Authenticate, 27 | Ping, 28 | Reject, 29 | ServerSync, 30 | ChannelRemove, 31 | ChannelState, 32 | UserRemove, 33 | UserState, 34 | BanList, 35 | TextMessage, 36 | PermissionDenied, 37 | ACL, 38 | QueryUsers, 39 | CryptSetup, 40 | ContextActionModify, 41 | ContextAction, 42 | UserList, 43 | VoiceTarget, 44 | PermissionQuery, 45 | CodecVersion, 46 | UserStats, 47 | RequestBlob, 48 | ServerConfig, 49 | SuggestConfig 50 | } 51 | -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/MessageFormatter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.util; 19 | 20 | /** 21 | * Formats strings into HTML. 22 | * Created by andrew on 24/08/13. 23 | */ 24 | public class MessageFormatter { 25 | 26 | public static final String HIGHLIGHT_COLOR = "33b5e5"; 27 | 28 | private static final String HTML_FONT_COLOR_FORMAT = "%s"; 29 | 30 | /** 31 | * Highlights the passed string using the service's defined color {@link MessageFormatter#HIGHLIGHT_COLOR}. 32 | * @param string The string to highlight. 33 | * @return The passed string enclosed with HTML font tags specifying the color. 34 | */ 35 | public static String highlightString(String string) { 36 | return String.format(HTML_FONT_COLOR_FORMAT, HIGHLIGHT_COLOR, string); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/Constants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble; 19 | 20 | /** 21 | * @deprecated Constant values should be associated with the class in which they are used. 22 | */ 23 | public class Constants { 24 | public static final int PROTOCOL_MAJOR = 1; 25 | public static final int PROTOCOL_MINOR = 2; 26 | public static final int PROTOCOL_PATCH = 5; 27 | 28 | public static final int TRANSMIT_VOICE_ACTIVITY = 0; 29 | public static final int TRANSMIT_PUSH_TO_TALK = 1; 30 | public static final int TRANSMIT_CONTINUOUS = 2; 31 | 32 | public static final int PROTOCOL_VERSION = (PROTOCOL_MAJOR << 16) | (PROTOCOL_MINOR << 8) | PROTOCOL_PATCH; 33 | public static final String PROTOCOL_STRING = PROTOCOL_MAJOR+ "." +PROTOCOL_MINOR+"."+PROTOCOL_PATCH; 34 | public static final int DEFAULT_PORT = 64738; 35 | 36 | public static final String TAG = "Jumble"; 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/IUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | /** 21 | * Created by andrew on 15/10/15. 22 | */ 23 | public interface IUser { 24 | int getSession(); 25 | 26 | Channel getChannel(); 27 | 28 | int getUserId(); 29 | 30 | String getName(); 31 | 32 | String getComment(); 33 | 34 | byte[] getCommentHash(); 35 | 36 | byte[] getTexture(); 37 | 38 | byte[] getTextureHash(); 39 | 40 | String getHash(); 41 | 42 | boolean isMuted(); 43 | 44 | boolean isDeafened(); 45 | 46 | boolean isSuppressed(); 47 | 48 | boolean isSelfMuted(); 49 | 50 | boolean isSelfDeafened(); 51 | 52 | boolean isPrioritySpeaker(); 53 | 54 | boolean isRecording(); 55 | 56 | boolean isLocalMuted(); 57 | 58 | boolean isLocalIgnored(); 59 | 60 | void setLocalMuted(boolean muted); 61 | 62 | void setLocalIgnored(boolean ignored); 63 | 64 | TalkState getTalkState(); 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/TalkState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | import android.os.Parcel; 21 | import android.os.Parcelable; 22 | 23 | /** 24 | * User talk state. 25 | * Created by andrew on 19/04/15. 26 | */ 27 | public enum TalkState implements Parcelable { 28 | TALKING, 29 | SHOUTING, 30 | PASSIVE, 31 | WHISPERING; 32 | 33 | public static Creator CREATOR = new Creator() { 34 | @Override 35 | public TalkState createFromParcel(Parcel source) { 36 | return TalkState.values()[source.readInt()]; 37 | } 38 | 39 | @Override 40 | public TalkState[] newArray(int size) { 41 | return new TalkState[size]; 42 | } 43 | }; 44 | 45 | @Override 46 | public int describeContents() { 47 | return 0; 48 | } 49 | 50 | @Override 51 | public void writeToParcel(Parcel dest, int flags) { 52 | dest.writeInt(ordinal()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/inputmode/IInputMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.inputmode; 19 | 20 | import java.util.concurrent.locks.Condition; 21 | 22 | /** 23 | * A talk state engine, providing information regarding when it is appropriate to send audio. 24 | * Created by andrew on 13/02/16. 25 | */ 26 | public interface IInputMode { 27 | /** 28 | * Called when new input is received from the audio recording thread. 29 | * @param pcm PCM data. 30 | * @param length The number of shorts in the PCM data. 31 | * @return true if the input should be transmitted. 32 | */ 33 | boolean shouldTransmit(short[] pcm, int length); 34 | 35 | /** 36 | * Called before any audio processing to wait for a change in input availability. 37 | * For example, a push to talk implementation will block the audio input thread until the 38 | * button has been activated. Other implementations may do nothing. 39 | * 40 | * This function should return immediately when shouldTransmit is returning true. 41 | */ 42 | void waitForInput(); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/AudioUser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio; 19 | 20 | /** 21 | * Created by andrew on 05/04/15. 22 | */ 23 | public class AudioUser { 24 | private static final float AVERAGE_DECAY = 0.99f; 25 | 26 | private int mSession; 27 | /** The average amount of packets in the jitter buffer. */ 28 | private float mAverageAvailable; 29 | 30 | public AudioUser(int session) { 31 | mSession = session; 32 | mAverageAvailable = 0.f; 33 | } 34 | 35 | public int getSession() { 36 | return mSession; 37 | } 38 | 39 | /** 40 | * Use the given packet data to update the average available packets. 41 | * If the number of packets present is greater than the accumulated average, update our average 42 | * to it- otherwise, decay our average by a factor of {@link #AVERAGE_DECAY}. 43 | * @param numPackets The number of packets present in this cycle of the jitter buffer. 44 | */ 45 | public void updateAveragePackets(int numPackets) { 46 | mAverageAvailable = Math.max(numPackets, mAverageAvailable * AVERAGE_DECAY); 47 | } 48 | 49 | public int getAveragePackets() { 50 | return (int) Math.ceil(mAverageAvailable); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/IJumbleObserver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.util; 19 | 20 | import com.morlunk.jumble.model.IChannel; 21 | import com.morlunk.jumble.model.IMessage; 22 | import com.morlunk.jumble.model.IUser; 23 | 24 | import java.security.cert.X509Certificate; 25 | 26 | /** 27 | * Created by andrew on 18/10/15. 28 | */ 29 | public interface IJumbleObserver { 30 | void onConnected(); 31 | 32 | void onConnecting(); 33 | 34 | void onDisconnected(JumbleException e); 35 | 36 | void onTLSHandshakeFailed(X509Certificate[] chain); 37 | 38 | void onChannelAdded(IChannel channel); 39 | 40 | void onChannelStateUpdated(IChannel channel); 41 | 42 | void onChannelRemoved(IChannel channel); 43 | 44 | void onChannelPermissionsUpdated(IChannel channel); 45 | 46 | void onUserConnected(IUser user); 47 | 48 | void onUserStateUpdated(IUser user); 49 | 50 | void onUserTalkStateUpdated(IUser user); 51 | 52 | void onUserJoinedChannel(IUser user, IChannel newChannel, IChannel oldChannel); 53 | 54 | void onUserRemoved(IUser user, String reason); 55 | 56 | void onPermissionDenied(String reason); 57 | 58 | void onMessageLogged(IMessage message); 59 | 60 | void onVoiceTargetChanged(VoiceTargetMode mode); 61 | 62 | void onLogInfo(String message); 63 | 64 | void onLogWarning(String message); 65 | 66 | void onLogError(String message); 67 | } 68 | -------------------------------------------------------------------------------- /src/androidTest/java/com/morlunk/jumble/test/JumbleServiceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.test; 19 | 20 | import android.content.Intent; 21 | import android.os.RemoteException; 22 | import android.test.ServiceTestCase; 23 | 24 | import com.morlunk.jumble.IJumbleService; 25 | import com.morlunk.jumble.JumbleService; 26 | import com.morlunk.jumble.model.Server; 27 | 28 | /** 29 | * Tests to ensure the integrity of {@link JumbleService}'s state. 30 | * Created by andrew on 02/02/15. 31 | */ 32 | public class JumbleServiceTest extends ServiceTestCase { 33 | private static final Server DUMMY_SERVER = new Server(-1, "dummy","example.com", 64738, 34 | "dummy_user", "dummy_pass"); 35 | 36 | public JumbleServiceTest() { 37 | super(JumbleService.class); 38 | } 39 | 40 | /** 41 | * Tests the state of a JumbleService prior to connection. 42 | */ 43 | public void testPreconnection() throws RemoteException { 44 | Intent intent = new Intent(getContext(), JumbleService.class); 45 | intent.putExtra(JumbleService.EXTRAS_SERVER, DUMMY_SERVER); 46 | startService(intent); 47 | IJumbleService service = getService(); 48 | assertFalse(service.isReconnecting()); 49 | assertNull(service.getConnectionError()); 50 | assertEquals(JumbleService.ConnectionState.DISCONNECTED, service.getConnectionState()); 51 | assertEquals(DUMMY_SERVER, service.getTargetServer()); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/WhisperTargetChannel.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | import android.os.Parcel; 21 | import android.os.Parcelable; 22 | 23 | import com.morlunk.jumble.protobuf.Mumble; 24 | 25 | import java.util.List; 26 | 27 | /** 28 | * An abstraction around a channel whisper target. 29 | * Created by andrew on 28/04/16. 30 | */ 31 | public class WhisperTargetChannel implements WhisperTarget { 32 | private final IChannel mChannel; 33 | private final boolean mIncludeLinked; 34 | private final boolean mIncludeSubchannels; 35 | private final String mGroupRestriction; 36 | 37 | public WhisperTargetChannel(final IChannel channel, boolean includeLinked, 38 | boolean includeSubchannels, String groupRestriction) { 39 | mChannel = channel; 40 | mIncludeLinked = includeLinked; 41 | mIncludeSubchannels = includeSubchannels; 42 | mGroupRestriction = groupRestriction; 43 | } 44 | 45 | @Override 46 | public Mumble.VoiceTarget.Target createTarget() { 47 | Mumble.VoiceTarget.Target.Builder vtb = Mumble.VoiceTarget.Target.newBuilder(); 48 | vtb.setLinks(mIncludeLinked); 49 | vtb.setChildren(mIncludeSubchannels); 50 | if (mGroupRestriction != null) 51 | vtb.setGroup(mGroupRestriction); 52 | vtb.setChannelId(mChannel.getId()); 53 | return vtb.build(); 54 | } 55 | 56 | @Override 57 | public String getName() { 58 | return mChannel.getName(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/androidTest/java/com/morlunk/jumble/test/MixerTest.java: -------------------------------------------------------------------------------- 1 | package com.morlunk.jumble.test; 2 | 3 | import com.morlunk.jumble.audio.BasicClippingShortMixer; 4 | import com.morlunk.jumble.audio.IAudioMixer; 5 | import com.morlunk.jumble.audio.IAudioMixerSource; 6 | 7 | import junit.framework.TestCase; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collection; 11 | import java.util.List; 12 | 13 | /** 14 | * Created by andrew on 16/07/15. 15 | */ 16 | public class MixerTest extends TestCase { 17 | /** 18 | * Tests that mixing order should not affect the output. 19 | */ 20 | public void testMixerCommutativity(IAudioMixer mixer) { 21 | BasicSource pcmA = new BasicSource<>(new float[] { 0.2f, 0.5f, 0.7f }, 3); 22 | BasicSource pcmB = new BasicSource<>(new float[] { 0.3f, 0.5f, 0.5f }, 3); 23 | BasicSource pcmC = new BasicSource<>(new float[] { 0.0f, 0.0f, -0.5f }, 3); 24 | final short[] outputABC = new short[3]; 25 | final short[] outputCBA = new short[3]; 26 | 27 | List> sourcesABC = new ArrayList<>(); 28 | sourcesABC.add(pcmA); 29 | sourcesABC.add(pcmB); 30 | sourcesABC.add(pcmC); 31 | List> sourcesCBA = new ArrayList<>(); 32 | sourcesCBA.add(pcmC); 33 | sourcesCBA.add(pcmB); 34 | sourcesCBA.add(pcmA); 35 | 36 | mixer.mix(sourcesABC, outputABC, 0, 3); 37 | mixer.mix(sourcesCBA, outputCBA, 0, 3); 38 | 39 | for (int i = 0; i < 3; i++) { 40 | assertEquals("Mixing should be commutative.", outputABC[i], outputCBA[i]); 41 | } 42 | } 43 | 44 | public void testBasicClippingShortMixer() { 45 | testMixerCommutativity(new BasicClippingShortMixer()); 46 | } 47 | 48 | private static class BasicSource implements IAudioMixerSource { 49 | private T mSamples; 50 | private int mLength; 51 | 52 | public BasicSource(T samples, int length) { 53 | mSamples = samples; 54 | mLength = length; 55 | } 56 | 57 | @Override 58 | public T getSamples() { 59 | return mSamples; 60 | } 61 | 62 | @Override 63 | public int getNumSamples() { 64 | return mLength; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/MumbleURLParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.util; 19 | 20 | import com.morlunk.jumble.Constants; 21 | import com.morlunk.jumble.model.Server; 22 | 23 | import java.net.MalformedURLException; 24 | import java.util.regex.Matcher; 25 | import java.util.regex.Pattern; 26 | 27 | /** 28 | * An implementation of the Mumble URL scheme. 29 | * @see http://mumble.sourceforge.net/Mumble_URL 30 | * Created by andrew on 03/03/14. 31 | */ 32 | public class MumbleURLParser { 33 | 34 | private static final Pattern URL_PATTERN = Pattern.compile("mumble://(([^:]+)?(:(.+?))?@)?(.+?)(:([0-9]+?))?/"); 35 | 36 | /** 37 | * Parses the passed Mumble URL into a Server object. 38 | * @param url A URL with the Mumble scheme. 39 | * @return A server with the data specified in the Mumble URL. 40 | * @throws MalformedURLException if the URL cannot be parsed. 41 | */ 42 | public static Server parseURL(String url) throws MalformedURLException { 43 | Matcher matcher = URL_PATTERN.matcher(url); 44 | if(matcher.find()) { 45 | String username = matcher.group(2); 46 | String password = matcher.group(4); 47 | String host = matcher.group(5); 48 | String portString = matcher.group(7); 49 | int port = portString == null ? Constants.DEFAULT_PORT : Integer.parseInt(portString); 50 | return new Server(-1, null, host, port, username, password); 51 | } else { 52 | throw new MalformedURLException(); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/androidTest/java/com/morlunk/jumble/test/ModelTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.test; 19 | 20 | import com.morlunk.jumble.model.Channel; 21 | import com.morlunk.jumble.model.User; 22 | 23 | import junit.framework.TestCase; 24 | 25 | /** 26 | * Tests the Channel-User tree model. 27 | * Created by andrew on 24/10/15. 28 | */ 29 | public class ModelTest extends TestCase { 30 | 31 | public void testUserAddRemove() { 32 | Channel root = new Channel(0, false); 33 | User user = new User(0, "Test user"); 34 | user.setChannel(root); 35 | assertEquals("Channel user list count is sane", 1, root.getUsers().size()); 36 | assertEquals("Channel subchannel user count is sane", 1, root.getSubchannelUserCount()); 37 | 38 | Channel sub = new Channel(1, false); 39 | root.addSubchannel(sub); 40 | User subuser = new User(1, "Test user in subchannel"); 41 | subuser.setChannel(sub); 42 | assertEquals("Adding a user to a subchannel doesn't affect the number of direct children of the root", 1, root.getUsers().size()); 43 | assertEquals("Adding a user to a subchannel updates the recursive user count", 2, root.getSubchannelUserCount()); 44 | 45 | user.setChannel(sub); 46 | assertEquals("Moving a user to a subchannel updates the number of children of the root", 0, root.getUsers().size()); 47 | assertEquals("Moving a user to a subchannel does not change the recursive user count of the root", 2, root.getSubchannelUserCount()); 48 | assertEquals("Subchannel user count is sane", 2, sub.getUsers().size()); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/IDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio; 19 | 20 | import com.morlunk.jumble.exception.NativeAudioException; 21 | 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * Created by andrew on 07/03/14. 26 | */ 27 | public interface IDecoder { 28 | /** 29 | * Decodes the encoded data provided into float PCM data. 30 | * @param input A byte array of encoded data of size inputSize. 31 | * @param inputSize The size of the encoded data. 32 | * @param output An initialized output array at least frameSize for float PCM data. 33 | * @param frameSize The maximum frame size possible. 34 | * @return The number of decoded samples. 35 | * @throws com.morlunk.jumble.exception.NativeAudioException if encoding failed. 36 | */ 37 | public int decodeFloat(ByteBuffer input, int inputSize, float[] output, int frameSize) throws NativeAudioException; 38 | 39 | /** 40 | * Decodes the encoded data provided into short PCM data. 41 | * @param input A byte array of encoded data of size inputSize. 42 | * @param inputSize The size of the encoded data. 43 | * @param output An initialized output array at least frameSize for short PCM data. 44 | * @param frameSize The maximum frame size possible. 45 | * @return The number of decoded samples. 46 | * @throws NativeAudioException if encoding failed. 47 | */ 48 | public int decodeShort(ByteBuffer input, int inputSize, short[] output, int frameSize) throws NativeAudioException; 49 | 50 | /** 51 | * Deallocates native resources. The decoder must no longer be called after this. 52 | */ 53 | public void destroy(); 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/inputmode/ActivityInputMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.inputmode; 19 | 20 | /** 21 | * An input mode that sends audio if the amplitude exceeds a certain threshold. 22 | * Created by andrew on 13/02/16. 23 | */ 24 | public class ActivityInputMode implements IInputMode { 25 | // Continue speech for 250ms to prevent dropping. 26 | private static final int SPEECH_DELAY = (int) (0.25 * Math.pow(10, 9)); 27 | 28 | private float mVADThreshold; 29 | private long mVADLastDetected; 30 | 31 | public ActivityInputMode(float detectionThreshold) { 32 | mVADThreshold = detectionThreshold; 33 | } 34 | 35 | @Override 36 | public boolean shouldTransmit(short[] pcm, int length) { 37 | // Use a logarithmic energy-based scale for VAD. 38 | float sum = 1.0f; 39 | for (int i = 0; i < length; i++) { 40 | sum += pcm[i] * pcm[i]; 41 | } 42 | float micLevel = (float) Math.sqrt(sum / (float)length); 43 | float peakSignal = (float) (20.0f * Math.log10(micLevel / 32768.0f)) / 96.0f; 44 | boolean talking = (peakSignal + 1) >= mVADThreshold; 45 | 46 | // Record the last time where VAD was detected in order to prevent speech dropping. 47 | if(talking) { 48 | mVADLastDetected = System.nanoTime(); 49 | } 50 | 51 | talking |= (System.nanoTime() - mVADLastDetected) < SPEECH_DELAY; 52 | 53 | return talking; 54 | } 55 | 56 | @Override 57 | public void waitForInput() { 58 | 59 | } 60 | 61 | public void setThreshold(float threshold) { 62 | mVADThreshold = threshold; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/encoder/IEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.encoder; 19 | 20 | import com.morlunk.jumble.exception.NativeAudioException; 21 | import com.morlunk.jumble.net.PacketBuffer; 22 | 23 | import java.nio.BufferUnderflowException; 24 | 25 | /** 26 | * IEncoder provides an interface for native audio encoders to buffer and serve encoded audio 27 | * data. 28 | * Created by andrew on 07/03/14. 29 | */ 30 | public interface IEncoder { 31 | /** 32 | * Encodes the provided input and returns the number of bytes encoded. 33 | * @param input The short PCM data to encode. 34 | * @param inputSize The number of samples to encode. 35 | * @return The number of bytes encoded. 36 | * @throws NativeAudioException if there was an error encoding. 37 | */ 38 | public int encode(short[] input, int inputSize) throws NativeAudioException; 39 | 40 | /** 41 | * @return the number of audio frames buffered. 42 | */ 43 | public int getBufferedFrames(); 44 | 45 | /** 46 | * @return true if enough buffered audio has been encoded to send to the server. 47 | */ 48 | public boolean isReady(); 49 | 50 | /** 51 | * Writes the currently encoded audio data into the provided {@link PacketBuffer}. 52 | * Use {@link #isReady()} to determine whether or not this should be called. 53 | * @throws BufferUnderflowException if insufficient audio data has been buffered. 54 | */ 55 | public void getEncodedData(PacketBuffer packetBuffer) throws BufferUnderflowException; 56 | 57 | /** 58 | * Informs the encoder that there are no more audio packets to be queued. Often, this will 59 | * trigger an encode operation, changing the result of {@link #isReady()}. 60 | */ 61 | public void terminate() throws NativeAudioException; 62 | 63 | /** 64 | * Destroys the encoder, cleaning up natively allocated resources. 65 | */ 66 | public void destroy(); 67 | } 68 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/inputmode/ToggleInputMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.inputmode; 19 | 20 | import android.util.Log; 21 | 22 | import com.morlunk.jumble.Constants; 23 | 24 | import java.util.concurrent.locks.Condition; 25 | import java.util.concurrent.locks.Lock; 26 | import java.util.concurrent.locks.ReentrantLock; 27 | 28 | /** 29 | * An input mode that depends on a toggle, such as push to talk. 30 | * Created by andrew on 13/02/16. 31 | */ 32 | public class ToggleInputMode implements IInputMode { 33 | private boolean mInputOn; 34 | private final Lock mToggleLock; 35 | private final Condition mToggleCondition; 36 | 37 | public ToggleInputMode() { 38 | mInputOn = false; 39 | mToggleLock = new ReentrantLock(); 40 | mToggleCondition = mToggleLock.newCondition(); 41 | } 42 | 43 | public void toggleTalkingOn() { 44 | setTalkingOn(!mInputOn); 45 | } 46 | 47 | public boolean isTalkingOn() { 48 | return mInputOn; 49 | } 50 | 51 | public void setTalkingOn(boolean talking) { 52 | mToggleLock.lock(); 53 | mInputOn = talking; 54 | mToggleCondition.signalAll(); 55 | mToggleLock.unlock(); 56 | } 57 | 58 | @Override 59 | public boolean shouldTransmit(short[] pcm, int length) { 60 | return mInputOn; 61 | } 62 | 63 | @Override 64 | public void waitForInput() { 65 | mToggleLock.lock(); 66 | if (!mInputOn) { 67 | Log.v(Constants.TAG, "PTT: Suspending audio input."); 68 | long startTime = System.currentTimeMillis(); 69 | try { 70 | mToggleCondition.await(); 71 | } catch (InterruptedException e) { 72 | Log.w(Constants.TAG, "Blocking for PTT interrupted, likely due to input thread shutdown."); 73 | } 74 | Log.v(Constants.TAG, "PTT: Suspended audio input for " + (System.currentTimeMillis() - startTime) + "ms."); 75 | } 76 | mToggleLock.unlock(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/WhisperTargetList.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | import java.util.List; 21 | 22 | /** 23 | * A simple implementation of a fixed-size whisper target list using a bit vector. 24 | * Created by andrew on 29/04/16. 25 | */ 26 | public class WhisperTargetList { 27 | public static final byte TARGET_MIN = 1; 28 | public static final byte TARGET_MAX = 30; 29 | 30 | private final WhisperTarget[] mActiveTargets; 31 | // Mumble stores voice targets using a 5-bit identifier. 32 | // Use a bit vector to represent this 32-element range. 33 | private int mTakenIds; 34 | 35 | public WhisperTargetList() { 36 | mActiveTargets = new WhisperTarget[TARGET_MAX - TARGET_MIN + 1]; 37 | clear(); 38 | } 39 | 40 | /** 41 | * Assigns the target to a slot. 42 | * @param target The whisper target to assign. 43 | * @return The slot number in range [1, 30]. 44 | */ 45 | public byte append(WhisperTarget target) { 46 | byte freeId = -1; 47 | for (byte i = TARGET_MIN; i < TARGET_MAX; i++) { 48 | if ((mTakenIds & (1 << i)) == 0) { 49 | freeId = i; 50 | break; 51 | } 52 | } 53 | if (freeId != -1) { 54 | mActiveTargets[freeId - TARGET_MIN] = target; 55 | } 56 | 57 | return freeId; 58 | } 59 | 60 | public WhisperTarget get(byte id) { 61 | if ((mTakenIds & (1 << id)) > 0) 62 | return null; 63 | return mActiveTargets[id - TARGET_MIN]; 64 | } 65 | 66 | public void free(byte slot) { 67 | if (slot < TARGET_MIN || slot > TARGET_MAX) 68 | throw new IllegalArgumentException(); 69 | 70 | mTakenIds &= ~(1 << slot); 71 | } 72 | 73 | public int spaceRemaining() { 74 | int counter = 0; 75 | for (byte i = TARGET_MIN; i < TARGET_MAX; i++) { 76 | if ((mTakenIds & (1 << i)) == 0) { 77 | counter++; 78 | } 79 | } 80 | return counter; 81 | } 82 | 83 | public void clear() { 84 | // Slots 0 and 31 are non-whisper targets. 85 | mTakenIds = 1 | (1 << 31); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/net/JumbleNetworkThread.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.net; 19 | 20 | import android.os.Handler; 21 | import android.os.Looper; 22 | 23 | import java.util.concurrent.ExecutorService; 24 | import java.util.concurrent.Executors; 25 | 26 | /** 27 | * Base class for TCP/UDP protocol implementations. 28 | * Provides a common threading model (single threaded queue for write) 29 | * Created by andrew on 25/03/14. 30 | * @deprecated This shouldn't be needed. Redundant inheritance with limited shared code. 31 | */ 32 | public abstract class JumbleNetworkThread implements Runnable { 33 | 34 | private ExecutorService mExecutor; 35 | private ExecutorService mSendExecutor; 36 | private ExecutorService mReceiveExecutor; 37 | private Handler mMainHandler; 38 | private boolean mInitialized; 39 | 40 | public JumbleNetworkThread() { 41 | mMainHandler = new Handler(Looper.getMainLooper()); 42 | } 43 | 44 | protected void startThreads() { 45 | if (mInitialized) { 46 | throw new IllegalArgumentException("Threads already initialized."); 47 | } 48 | mExecutor = Executors.newSingleThreadExecutor(); 49 | mSendExecutor = Executors.newSingleThreadExecutor(); 50 | mReceiveExecutor = Executors.newSingleThreadExecutor(); 51 | mExecutor.execute(this); 52 | mInitialized = true; 53 | } 54 | 55 | protected void stopThreads() { 56 | if (!mInitialized) { 57 | throw new IllegalArgumentException("Threads already shutdown."); 58 | } 59 | mSendExecutor.shutdown(); 60 | mReceiveExecutor.shutdownNow(); 61 | mExecutor.shutdownNow(); 62 | mSendExecutor = null; 63 | mReceiveExecutor = null; 64 | mExecutor = null; 65 | mInitialized = false; 66 | } 67 | 68 | protected void executeOnSendThread(Runnable r) { 69 | mSendExecutor.execute(r); 70 | } 71 | 72 | protected void executeOnReceiveThread(Runnable r) { 73 | mSendExecutor.execute(r); 74 | } 75 | 76 | protected void executeOnMainThread(Runnable r) { 77 | mMainHandler.post(r); 78 | } 79 | 80 | protected Handler getMainHandler() { 81 | return mMainHandler; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/BluetoothScoReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio; 19 | 20 | import android.content.BroadcastReceiver; 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.media.AudioManager; 24 | import android.widget.Toast; 25 | 26 | import com.morlunk.jumble.R; 27 | import com.morlunk.jumble.exception.AudioInitializationException; 28 | 29 | /** 30 | * Manages the state of Bluetooth SCO. 31 | * Created by andrew on 25/09/15. 32 | */ 33 | public class BluetoothScoReceiver extends BroadcastReceiver { 34 | private final Listener mListener; 35 | private final AudioManager mAudioManager; 36 | private boolean mBluetoothScoOn; 37 | 38 | public BluetoothScoReceiver(Context context, Listener listener) { 39 | mListener = listener; 40 | mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 41 | } 42 | 43 | @Override 44 | public void onReceive(Context context, Intent intent) { 45 | AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 46 | int audioState = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_ERROR); 47 | switch (audioState) { 48 | case AudioManager.SCO_AUDIO_STATE_CONNECTED: 49 | mBluetoothScoOn = true; 50 | mListener.onBluetoothScoConnected(); 51 | break; 52 | case AudioManager.SCO_AUDIO_STATE_DISCONNECTED: 53 | case AudioManager.SCO_AUDIO_STATE_ERROR: 54 | am.stopBluetoothSco(); 55 | mBluetoothScoOn = false; 56 | mListener.onBluetoothScoDisconnected(); 57 | break; 58 | } 59 | } 60 | 61 | public void startBluetoothSco() { 62 | mAudioManager.startBluetoothSco(); 63 | } 64 | 65 | public void stopBluetoothSco() { 66 | mAudioManager.stopBluetoothSco(); 67 | mBluetoothScoOn = false; 68 | } 69 | 70 | public boolean isBluetoothScoOn() { 71 | return mBluetoothScoOn; 72 | } 73 | 74 | public interface Listener { 75 | void onBluetoothScoConnected(); 76 | void onBluetoothScoDisconnected(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/JumbleObserver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.util; 19 | 20 | import com.morlunk.jumble.model.IChannel; 21 | import com.morlunk.jumble.model.IMessage; 22 | import com.morlunk.jumble.model.IUser; 23 | 24 | import java.security.cert.X509Certificate; 25 | 26 | /** 27 | * Stub class for Jumble service observation. 28 | * Created by andrew on 31/07/13. 29 | */ 30 | public class JumbleObserver implements IJumbleObserver { 31 | @Override 32 | public void onConnected() { 33 | 34 | } 35 | 36 | @Override 37 | public void onConnecting() { 38 | 39 | } 40 | 41 | @Override 42 | public void onDisconnected(JumbleException e) { 43 | 44 | } 45 | 46 | @Override 47 | public void onTLSHandshakeFailed(X509Certificate[] chain) { 48 | 49 | } 50 | 51 | @Override 52 | public void onChannelAdded(IChannel channel) { 53 | 54 | } 55 | 56 | @Override 57 | public void onChannelStateUpdated(IChannel channel) { 58 | 59 | } 60 | 61 | @Override 62 | public void onChannelRemoved(IChannel channel) { 63 | 64 | } 65 | 66 | @Override 67 | public void onChannelPermissionsUpdated(IChannel channel) { 68 | 69 | } 70 | 71 | @Override 72 | public void onUserConnected(IUser user) { 73 | 74 | } 75 | 76 | @Override 77 | public void onUserStateUpdated(IUser user) { 78 | 79 | } 80 | 81 | @Override 82 | public void onUserTalkStateUpdated(IUser user) { 83 | 84 | } 85 | 86 | @Override 87 | public void onUserJoinedChannel(IUser user, IChannel newChannel, IChannel oldChannel) { 88 | 89 | } 90 | 91 | @Override 92 | public void onUserRemoved(IUser user, String reason) { 93 | 94 | } 95 | 96 | @Override 97 | public void onPermissionDenied(String reason) { 98 | 99 | } 100 | 101 | @Override 102 | public void onMessageLogged(IMessage message) { 103 | 104 | } 105 | 106 | @Override 107 | public void onVoiceTargetChanged(VoiceTargetMode mode) { 108 | 109 | } 110 | 111 | @Override 112 | public void onLogInfo(String message) { 113 | 114 | } 115 | 116 | @Override 117 | public void onLogWarning(String message) { 118 | 119 | } 120 | 121 | @Override 122 | public void onLogError(String message) { 123 | 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/encoder/ResamplingEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.encoder; 19 | 20 | import com.morlunk.jumble.audio.javacpp.Speex; 21 | import com.morlunk.jumble.exception.NativeAudioException; 22 | import com.morlunk.jumble.net.PacketBuffer; 23 | 24 | import java.nio.BufferUnderflowException; 25 | 26 | /** 27 | * Wraps around another encoder, resampling up/down all input using the Speex resampler. 28 | * Created by andrew on 16/04/14. 29 | */ 30 | public class ResamplingEncoder implements IEncoder { 31 | private static final int SPEEX_RESAMPLE_QUALITY = 3; 32 | 33 | private IEncoder mEncoder; 34 | private Speex.SpeexResampler mResampler; 35 | private final int mInputSampleRate; 36 | private final int mTargetSampleRate; 37 | private final int mTargetFrameSize; 38 | private final short[] mResampleBuffer; 39 | 40 | public ResamplingEncoder(IEncoder encoder, int channels, int inputSampleRate, int targetFrameSize, int targetSampleRate) { 41 | mEncoder = encoder; 42 | mInputSampleRate = inputSampleRate; 43 | mTargetSampleRate = targetSampleRate; 44 | mTargetFrameSize = targetFrameSize; 45 | mResampleBuffer = new short[mTargetFrameSize]; 46 | mResampler = new Speex.SpeexResampler(channels, inputSampleRate, targetSampleRate, SPEEX_RESAMPLE_QUALITY); 47 | } 48 | 49 | @Override 50 | public int encode(short[] input, int inputSize) throws NativeAudioException { 51 | mResampler.resample(input, mResampleBuffer); 52 | return mEncoder.encode(mResampleBuffer, mTargetFrameSize); 53 | } 54 | 55 | @Override 56 | public int getBufferedFrames() { 57 | return mEncoder.getBufferedFrames(); 58 | } 59 | 60 | @Override 61 | public boolean isReady() { 62 | return mEncoder.isReady(); 63 | } 64 | 65 | @Override 66 | public void getEncodedData(PacketBuffer packetBuffer) throws BufferUnderflowException { 67 | mEncoder.getEncodedData(packetBuffer); 68 | } 69 | 70 | @Override 71 | public void terminate() throws NativeAudioException { 72 | mEncoder.terminate(); 73 | } 74 | 75 | public void setEncoder(IEncoder encoder) { 76 | if(mEncoder != null) mEncoder.destroy(); 77 | mEncoder = encoder; 78 | } 79 | 80 | @Override 81 | public void destroy() { 82 | mResampler.destroy(); 83 | mEncoder.destroy(); 84 | mResampler = null; 85 | mEncoder = null; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/androidTest/java/com/morlunk/jumble/test/EncoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.test; 19 | 20 | import android.test.AndroidTestCase; 21 | 22 | import com.googlecode.javacpp.Loader; 23 | import com.morlunk.jumble.audio.encoder.CELT7Encoder; 24 | import com.morlunk.jumble.audio.encoder.IEncoder; 25 | import com.morlunk.jumble.audio.encoder.OpusEncoder; 26 | import com.morlunk.jumble.audio.javacpp.Opus; 27 | import com.morlunk.jumble.exception.NativeAudioException; 28 | import com.morlunk.jumble.net.PacketBuffer; 29 | 30 | /** 31 | * This class tests the Opus and CELT encoders with blank PCM data. 32 | * The bitrate is set to 40000bps. TODO: add test for varying bitrates. 33 | * If any of these methods throw a NativeAudioException, then the test will fail. 34 | * Created by andrew on 13/10/13. 35 | */ 36 | public class EncoderTest extends AndroidTestCase { 37 | private static final int MAX_BUFFER_SIZE = 960; 38 | private static final int SAMPLE_RATE = 48000; 39 | private static final int BITRATE = 40000; 40 | private static final int FRAME_SIZE = 480; 41 | private static final int FRAMES_PER_PACKET = 4; 42 | 43 | static { 44 | Loader.load(Opus.class); 45 | } 46 | 47 | public void testOpusEncode() throws NativeAudioException { 48 | IEncoder encoder = new OpusEncoder(SAMPLE_RATE, 1, FRAME_SIZE, FRAMES_PER_PACKET, BITRATE, MAX_BUFFER_SIZE); 49 | testEncoder(encoder); 50 | encoder.destroy(); 51 | } 52 | 53 | public void testCELT7Encode() throws NativeAudioException { 54 | CELT7Encoder encoder = new CELT7Encoder(SAMPLE_RATE, FRAME_SIZE, 1, FRAMES_PER_PACKET, 55 | BITRATE, MAX_BUFFER_SIZE); 56 | testEncoder(encoder); 57 | encoder.destroy(); 58 | } 59 | 60 | public void testEncoder(IEncoder encoder) throws NativeAudioException { 61 | assertFalse(encoder.isReady()); 62 | assertEquals(0, encoder.getBufferedFrames()); 63 | 64 | // 65 | final short[] dummyFrame = new short[FRAME_SIZE]; 66 | for (int i = 0; i < FRAMES_PER_PACKET; i++) { 67 | assertFalse(encoder.isReady()); 68 | encoder.encode(dummyFrame, FRAME_SIZE); 69 | } 70 | assertTrue(encoder.isReady()); 71 | assertEquals(FRAMES_PER_PACKET, encoder.getBufferedFrames()); 72 | 73 | // Flushing 74 | PacketBuffer buffer = PacketBuffer.allocate(MAX_BUFFER_SIZE); 75 | encoder.getEncodedData(buffer); 76 | assertFalse(encoder.isReady()); 77 | assertEquals(0, encoder.getBufferedFrames()); 78 | 79 | // Termination (for frames per packet > 1) 80 | encoder.encode(dummyFrame, FRAME_SIZE); 81 | assertFalse(encoder.isReady()); 82 | encoder.terminate(); 83 | assertTrue(encoder.isReady()); 84 | 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/res/values-ja/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | サーバーの最大ネットワーク帯域幅は %1$d kbit/s のみです。オーディオ品質は %2$d kbit/s (%3$d ms) に自動調整されました 4 | %1$s は %2$s から %3$s で移動しました。 5 | ミュートおよび防音 6 | ミュートしました。 7 | ミュートを解除しました。 8 | ミュート解除および防音解除しました。 9 | %s がミュートおよび防音されました。 10 | %s がミュートされました。 11 | %s がミュート解除されました。 12 | %s がミュート解除および防音解除されました。 13 | あなたは %s によってミュートおよび防音されました。 14 | あなたは %s によってミュート解除および防音解除されました。 15 | あなたは %s によってミュートされました。 16 | あなたは %s によってミュート解除されました。 17 | あなたは %s によって防音解除されました。 18 | あなたはサプレスされました。 19 | あなたはサプレス解除されました。 20 | あなたは %s によってサプレス解除されました。 21 | %s が接続しました。 22 | %s が切断しました。 23 | あなたは %1$s によって、サーバーからキックおよび禁止されました: %2$s. 24 | あなたは %1$s によって、サーバーからキックされました: %2$s. 25 | %3$s は %1$s によって、サーバーからキックおよび禁止されました: %2$s. 26 | %3$s は %1$s によって、サーバーからキックされました: %2$s. 27 | 録音が開始しました 28 | 録音が停止しました 29 | %s が録音を開始しました。 30 | %s が録音を停止しました。 31 | %s がチャンネルに入りました。 32 | %1$s が %2$s に移動しました。 33 | %1$s は %2$s から %3$s で移動しました。 34 | %1$s が %3$s によって %2$s に移動しました。 35 | Bluetooth が接続しました 36 | Bluetooth が切断しました 37 | 拒否されました: チャンネル名が正しくありません。 38 | 拒否されました: テキストメッセージが長すぎます。 39 | 拒否されました: 一時チャンネルで操作が許可されていません。 40 | この操作を実行するには証明書が必要です。 41 | ユーザー名が正しくありません。 42 | チャンネルが一杯です。 43 | チャンネルがネストの上限に達しました。 44 | 理由: %s 45 | 権限が拒否されました。 46 | サーバー 47 | サーバー 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/encoder/PreprocessingEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.encoder; 19 | 20 | import com.googlecode.javacpp.IntPointer; 21 | import com.morlunk.jumble.audio.javacpp.Speex; 22 | import com.morlunk.jumble.exception.NativeAudioException; 23 | import com.morlunk.jumble.net.PacketBuffer; 24 | 25 | import java.nio.BufferUnderflowException; 26 | 27 | /** 28 | * Wrapper performing preprocessing options on the nested encoder. 29 | * Uses Speex preprocessor. 30 | * Created by andrew on 17/04/14. 31 | */ 32 | public class PreprocessingEncoder implements IEncoder { 33 | private IEncoder mEncoder; 34 | private Speex.SpeexPreprocessState mPreprocessor; 35 | 36 | public PreprocessingEncoder(IEncoder encoder, int frameSize, int sampleRate) { 37 | mEncoder = encoder; 38 | mPreprocessor = new Speex.SpeexPreprocessState(frameSize, sampleRate); 39 | 40 | IntPointer arg = new IntPointer(1); 41 | 42 | arg.put(0); 43 | mPreprocessor.control(Speex.SpeexPreprocessState.SPEEX_PREPROCESS_SET_VAD, arg); 44 | arg.put(1); 45 | mPreprocessor.control(Speex.SpeexPreprocessState.SPEEX_PREPROCESS_SET_AGC, arg); 46 | mPreprocessor.control(Speex.SpeexPreprocessState.SPEEX_PREPROCESS_SET_DENOISE, arg); 47 | mPreprocessor.control(Speex.SpeexPreprocessState.SPEEX_PREPROCESS_SET_DEREVERB, arg); 48 | 49 | arg.put(30000); 50 | mPreprocessor.control(Speex.SpeexPreprocessState.SPEEX_PREPROCESS_SET_AGC_TARGET, arg); 51 | 52 | // TODO AGC max gain, decrement, noise suppress, echo 53 | 54 | // Increase VAD difficulty 55 | arg.put(99); 56 | mPreprocessor.control(Speex.SpeexPreprocessState.SPEEX_PREPROCESS_GET_PROB_START, arg); 57 | } 58 | 59 | @Override 60 | public int encode(short[] input, int inputSize) throws NativeAudioException { 61 | mPreprocessor.preprocess(input); 62 | return mEncoder.encode(input, inputSize); 63 | } 64 | 65 | @Override 66 | public int getBufferedFrames() { 67 | return mEncoder.getBufferedFrames(); 68 | } 69 | 70 | @Override 71 | public boolean isReady() { 72 | return mEncoder.isReady(); 73 | } 74 | 75 | @Override 76 | public void getEncodedData(PacketBuffer packetBuffer) throws BufferUnderflowException { 77 | mEncoder.getEncodedData(packetBuffer); 78 | } 79 | 80 | @Override 81 | public void terminate() throws NativeAudioException { 82 | mEncoder.terminate(); 83 | } 84 | 85 | public void setEncoder(IEncoder encoder) { 86 | if(mEncoder != null) mEncoder.destroy(); 87 | mEncoder = encoder; 88 | } 89 | 90 | @Override 91 | public void destroy() { 92 | mPreprocessor.destroy(); 93 | mEncoder.destroy(); 94 | mPreprocessor = null; 95 | mEncoder = null; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/res/values-ru/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | %1$s перемещен(а) %3$s из %2$s. 4 | Звук и микрофон отключен. 5 | Микрофон отключен. 6 | Микрофон включен. 7 | Микрофон и звук включен. 8 | %s отключил(а) звук и микрофон. 9 | %s выключил(а) микрофон. 10 | %s включил(а) микрофон. 11 | %s включил(а) звук и микрофон. 12 | %s выключил(а) вам звук и микрофон. 13 | %s включил(а) вам звук и микрофон. 14 | %s выключил(а) вам микрофон. 15 | %s включил(а) вам микрофон. 16 | %s включил(а) вам звук. 17 | Вы были заглушены. 18 | Оглушение снято. 19 | %s снял оглушение с вас. 20 | %s соеденен(а). 21 | %s отсоединен(а). 22 | Вы были исключены и заблокированы на сервера, %1$s: %2$s. 23 | Вы были исключены с сервера, %1$s: %2$s. 24 | %3$s заблокирован(а) и исключен(а) с сервера, %1$s: %2$s. 25 | %3$s исключен(а) с сервера, %1$s: %2$s. 26 | Запись начата 27 | Запись остановлена 28 | %s начал(а) запись. 29 | %s остановил(а) запись. 30 | %s вошел(а) в канал 31 | %1$s перемещен(а) в %2$s. 32 | %1$s перемещен(а) %3$s из %2$s. 33 | %1$s перемещен(а) %3$s в %2$s. 34 | Bluetooth соединен 35 | Bluetooth отсоединен 36 | Отказано: Неверное имя канала. 37 | Отказано: Текстовое сообщение слишком длинное. 38 | Отказано: Операция не разрешена во временном канале. 39 | Вам нужен сертификат для выполнения данной операции. 40 | Неверное имя пользователя. 41 | Канал переполнен. 42 | Достигнул предел вложенности канала. 43 | Причина: %s 44 | Доступ запрещен. 45 | Сервер 46 | сервер 47 | 48 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/net/JumbleCertificateGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.net; 19 | 20 | import org.spongycastle.asn1.x500.X500Name; 21 | import org.spongycastle.asn1.x509.SubjectPublicKeyInfo; 22 | import org.spongycastle.cert.X509CertificateHolder; 23 | import org.spongycastle.cert.X509v3CertificateBuilder; 24 | import org.spongycastle.cert.jcajce.JcaX509CertificateConverter; 25 | import org.spongycastle.jce.provider.BouncyCastleProvider; 26 | import org.spongycastle.operator.ContentSigner; 27 | import org.spongycastle.operator.OperatorCreationException; 28 | import org.spongycastle.operator.jcajce.JcaContentSignerBuilder; 29 | 30 | import java.io.IOException; 31 | import java.io.OutputStream; 32 | import java.math.BigInteger; 33 | import java.security.KeyPair; 34 | import java.security.KeyPairGenerator; 35 | import java.security.KeyStore; 36 | import java.security.KeyStoreException; 37 | import java.security.NoSuchAlgorithmException; 38 | import java.security.NoSuchProviderException; 39 | import java.security.SecureRandom; 40 | import java.security.cert.CertificateException; 41 | import java.security.cert.X509Certificate; 42 | import java.util.Calendar; 43 | import java.util.Date; 44 | 45 | public class JumbleCertificateGenerator { 46 | private static final String ISSUER = "CN=Jumble Client"; 47 | private static final Integer YEARS_VALID = 20; 48 | 49 | public static X509Certificate generateCertificate(OutputStream output) throws NoSuchAlgorithmException, OperatorCreationException, CertificateException, KeyStoreException, NoSuchProviderException, IOException { 50 | BouncyCastleProvider provider = new BouncyCastleProvider(); // Use SpongyCastle provider, supports creating X509 certs 51 | KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA"); 52 | generator.initialize(2048, new SecureRandom()); 53 | 54 | KeyPair keyPair = generator.generateKeyPair(); 55 | 56 | SubjectPublicKeyInfo publicKeyInfo = SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()); 57 | ContentSigner signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider(provider).build(keyPair.getPrivate()); 58 | 59 | Date startDate = new Date(); 60 | Calendar calendar = Calendar.getInstance(); 61 | calendar.setTime(startDate); 62 | calendar.add(Calendar.YEAR, YEARS_VALID); 63 | Date endDate = calendar.getTime(); 64 | 65 | X509v3CertificateBuilder certBuilder = new X509v3CertificateBuilder(new X500Name(ISSUER), 66 | BigInteger.ONE, 67 | startDate, endDate, new X500Name(ISSUER), 68 | publicKeyInfo); 69 | 70 | X509CertificateHolder certificateHolder = certBuilder.build(signer); 71 | 72 | X509Certificate certificate = new JcaX509CertificateConverter().setProvider(provider).getCertificate(certificateHolder); 73 | 74 | KeyStore keyStore = KeyStore.getInstance("PKCS12", provider); 75 | keyStore.load(null, null); 76 | keyStore.setKeyEntry("Jumble Key", keyPair.getPrivate(), null, new X509Certificate[] { certificate }); 77 | 78 | keyStore.store(output, "".toCharArray()); 79 | 80 | return certificate; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/res/values-nl/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | %1$s verplaatst van %2$s door %3$s. 4 | Gedempt en doof gemaakt. 5 | Gedempt. 6 | Dempen opgeheven. 7 | Dempen opgeheven en horend gemaakt. 8 | %s is nu gedempt en doof gemaakt. 9 | %s is nu gedempt. 10 | %s is niet meer gedempt. 11 | %s is niet meer gedempt of doof. 12 | U bent gedempt en doof gemaakt door %s. 13 | U bent niet meer gedempt en horend gemaakt door %s. 14 | U bent gedempt door %s. 15 | U bent niet meer gedempt door %s. 16 | U bent horend gemaakt door %s. 17 | U werd onderdrukt 18 | U bent niet meer onderdrukt 19 | U bent niet meer onderdrukt door %s. 20 | %s verbonden. 21 | %s verbinding verbroken. 22 | U bent verbannnen van de server door %1$s: %2$s. 23 | U bent van de server geschopt door %1$s: %2$s. 24 | %3$s is van de server verbannen door %1$s: %2$s. 25 | %3$s is van de server geschopt door %1$s: %2$s. 26 | Opname gestart 27 | Opname gestopt 28 | %s heeft opname gestart. 29 | %s heeft opname gestopt 30 | %s kanaal binnengekomen 31 | %1$s verplaatst naar %2$s. 32 | %1$s verplaatst van %2$s door %3$s. 33 | %1$s verplaatst van %2$s door %3$s. 34 | Bluetooth verbonden 35 | Bluetooth verbroken 36 | Geweigerd: Ongeldige kanaal naam. 37 | Geweigerd: Tekst boodschap te lang. 38 | Geweigerd: Handeling niet toegestaan in tijdelijk kanaal. 39 | U hebt een certificaat nodig om deze handeling te voltooien. 40 | Ongeldige gebruikersnaam. 41 | Kanaal is vol 42 | Ingesloten kanaal limiet bereikt 43 | Reden: %s 44 | Geen toelating 45 | Server 46 | de server 47 | 48 | -------------------------------------------------------------------------------- /src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (C) 2013 Andrew Comminos 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | ROOT := $(call my-dir) 18 | 19 | include $(CLEAR_VARS) 20 | 21 | LOCAL_PATH := $(ROOT)/speex/libspeex 22 | LOCAL_MODULE := jnispeex 23 | LOCAL_C_INCLUDES := $(ROOT)/speex/include/ 24 | LOCAL_SRC_FILES := cb_search.c exc_10_32_table.c exc_8_128_table.c filters.c \ 25 | gain_table.c hexc_table.c high_lsp_tables.c lsp.c \ 26 | ltp.c speex.c stereo.c vbr.c \ 27 | vq.c bits.c exc_10_16_table.c exc_20_32_table.c exc_5_256_table.c \ 28 | exc_5_64_table.c gain_table_lbr.c hexc_10_32_table.c lpc.c \ 29 | lsp_tables_nb.c modes.c modes_wb.c nb_celp.c \ 30 | quant_lsp.c sb_celp.c speex_callbacks.c speex_header.c \ 31 | window.c resample.c jitter.c preprocess.c \ 32 | mdf.c kiss_fft.c kiss_fftr.c fftwrap.c \ 33 | filterbank.c scal.c \ 34 | $(ROOT)/jnispeex.cpp 35 | LOCAL_CFLAGS := -D__EMX__ -DUSE_KISS_FFT -DFIXED_POINT -DEXPORT='' 36 | LOCAL_CPP_FEATURES := exceptions 37 | LOCAL_LDLIBS := -llog 38 | include $(BUILD_SHARED_LIBRARY) 39 | 40 | include $(CLEAR_VARS) 41 | LOCAL_PATH := $(ROOT)/celt-0.11.0-src/libcelt 42 | LOCAL_MODULE := jnicelt11 43 | LOCAL_SRC_FILES := bands.c celt.c cwrs.c entcode.c entdec.c entenc.c header.c kiss_fft.c \ 44 | laplace.c mathops.c mdct.c modes.c pitch.c plc.c quant_bands.c rate.c vq.c \ 45 | $(ROOT)/jnicelt11.cpp 46 | LOCAL_C_INCLUDES := $(ROOT)/celt-0.11.0-src/libcelt/ 47 | LOCAL_CFLAGS := -I$(ROOT)/celt-0.11.0-build -DHAVE_CONFIG_H -fvisibility=hidden 48 | LOCAL_CPP_FEATURES := exceptions 49 | LOCAL_LDLIBS := -llog 50 | include $(BUILD_SHARED_LIBRARY) 51 | 52 | include $(CLEAR_VARS) 53 | LOCAL_PATH := $(ROOT)/celt-0.7.0-src/libcelt 54 | LOCAL_MODULE := jnicelt7 55 | LOCAL_SRC_FILES := bands.c celt.c cwrs.c entcode.c entdec.c entenc.c header.c kiss_fft.c \ 56 | kiss_fftr.c laplace.c mdct.c modes.c pitch.c psy.c quant_bands.c rangedec.c \ 57 | rangeenc.c rate.c vq.c $(ROOT)/jnicelt7.cpp 58 | LOCAL_C_INCLUDES := $(ROOT)/celt-0.7.0-src/libcelt/ 59 | LOCAL_CFLAGS := -I$(ROOT)/celt-0.7.0-build -DHAVE_CONFIG_H -fvisibility=hidden 60 | LOCAL_CPP_FEATURES := exceptions 61 | LOCAL_LDLIBS := -llog 62 | include $(BUILD_SHARED_LIBRARY) 63 | 64 | include $(CLEAR_VARS) 65 | LOCAL_PATH := $(ROOT)/opus 66 | LOCAL_MODULE := jniopus 67 | 68 | include $(LOCAL_PATH)/celt_sources.mk 69 | include $(LOCAL_PATH)/silk_sources.mk 70 | include $(LOCAL_PATH)/opus_sources.mk 71 | 72 | ifeq ($(TARGET_ARCH), arm) 73 | CELT_SOURCES += $(CELT_SOURCES_ARM) 74 | SILK_SOURCES += $(SILK_SOURCES_ARM) 75 | endif 76 | 77 | # TODO: add support for floating-point? 78 | SILK_SOURCES += $(SILK_SOURCES_FIXED) 79 | OPUS_SOURCES += $(OPUS_SOURCES_FLOAT) 80 | # end fixed point 81 | 82 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/include $(LOCAL_PATH)/celt $(LOCAL_PATH)/silk \ 83 | $(LOCAL_PATH)/silk/float $(LOCAL_PATH)/silk/fixed 84 | LOCAL_SRC_FILES := $(CELT_SOURCES) $(SILK_SOURCES) $(OPUS_SOURCES) $(ROOT)/jniopus.cpp 85 | LOCAL_CFLAGS := -DOPUS_BUILD -DVAR_ARRAYS -Wno-traditional -DFIXED_POINT 86 | LOCAL_CPP_FEATURES := exceptions 87 | LOCAL_LDLIBS := -llog 88 | include $(BUILD_SHARED_LIBRARY) 89 | -------------------------------------------------------------------------------- /src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | El ancho de banda máximo del servidor es solo de %1$d kbit/s. Calidad de audio ajustada a %2$d kbit/s (%3$d ms) 4 | %1$s fue movido a este canal desde %2$s por %3$s. 5 | Mudo y sordo. 6 | Mudo. 7 | Con voz 8 | Con voz y escucha. 9 | %s está ahora enmudecido y ensordecido 10 | %s está ahora enmudecido. 11 | %s ahora tiene voz. 12 | %s ahora tiene voz y escucha. 13 | Has sido enmudecido y ensordecido por %s 14 | %s te ha desenmudecido y dado escucha 15 | Has sido enmudecido por %s 16 | Has sido desenmudecido por %s 17 | %s te ha dado escucha 18 | Has sido suprimido. 19 | Ya no estás suprimido. 20 | %s ha cesado tu supresión. 21 | %s se ha conectado. 22 | %s se ha desconectado. 23 | Has sido expulsado y prohibido del servidor por %1$s: %2$s. 24 | Has sido expulsado del servidor por %1$s: %2$s. 25 | %3$s fue expulsado y prohibido del servidor por %1$s: %2$s. 26 | %3$s ha sido expulsado del servidor por %1$s: %2$s. 27 | Grabación iniciada 28 | Grabación parada 29 | %s ha empezado a grabar. 30 | %s ha parado de grabar. 31 | %s entró al canal. 32 | %1$s fue movido a %2$s. 33 | %1$s fue movido a este canal desde %2$s por %3$s. 34 | %1$s fue movido a %2$s por %3$s. 35 | Bluetooth conectado 36 | Bluetooth desconectado 37 | Denegado: Nombre de canal inválido. 38 | Denegado: Mensaje de texto demasiado largo. 39 | Denegado: Acción no permitida en un canal temporal. 40 | Necesitas un certificado para realizar esta operación. 41 | Nombre de usuario inválido. 42 | El canal está lleno. 43 | Límite de canales anidados alcanzado. 44 | Razón: %s 45 | Permiso denegado. 46 | Servidor 47 | el servidor 48 | 49 | -------------------------------------------------------------------------------- /src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | La bande passante réseau maximale du serveur est seulement de %1$d kbit/s. La qualité audio est ajustée à %2$d kbit/s (%3$d ms) 4 | %1$s a été déplacé de %2$s par %3$s. 5 | Muet et sourd. 6 | Muet. 7 | Non muet. 8 | N\'est plus muet et sourd. 9 | %s et maintenant muet et sourd. 10 | %s est maintenant muet. 11 | %s n\'est maintenant plus muet. 12 | %s n\'est plus muet et sourd. 13 | Vous avez été rendu muet et sourd par %s. 14 | Vous n\'êtes plus muet et sourd par %s. 15 | Vous avez été rendu muet par %s. 16 | Vous n\'êtes plus muet par %s. 17 | Vous n\'êtes plus sourd par %s. 18 | Vous avez été supprimé. 19 | Vous avez été rétabli. 20 | Vous avez été rétabli par %s. 21 | %s connecté. 22 | %s déconnecté. 23 | Vous avez été éjecté et banni du serveur par %1$s: %2$s. 24 | Vous avez été éjecté du serveur par %1$s: %2$s. 25 | %3$s a été éjecté et banni du serveur par %1$s: %2$s. 26 | %3$s a été éjecté du serveur par %1$s: %2$s. 27 | Enregistrement démarré 28 | Enregistrement interrompu 29 | %s a commencé à enregistrer. 30 | %s a arrêté d’enregistrer. 31 | %s est entré dans le canal. 32 | %1$s s\'est déplacé dans %2$s. 33 | %1$s s\'est déplacé de %2$s par %3$s. 34 | %1$s a été déplacé dans %2$s par %3$s. 35 | Bluetooth connecté 36 | Bluetooth déconnecté 37 | Refusé: Nom de canal invalide. 38 | Refusé : message texte trop long. 39 | Refusé: Opération non permise dans un canal temporaire. 40 | Vous avez besoin d\'un certificat pour effectuer cette opération. 41 | Nom d\'utilisateur invalide. 42 | Le canal est plein. 43 | Limite du nombre de canal enfant atteint. 44 | Raison : %s 45 | Permission non accordée. 46 | Serveur 47 | le serveur 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/JumbleException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.util; 19 | 20 | import android.os.Parcel; 21 | import android.os.Parcelable; 22 | 23 | import com.morlunk.jumble.protobuf.Mumble; 24 | 25 | /** 26 | * Created by andrew on 14/07/13. 27 | */ 28 | public class JumbleException extends Exception implements Parcelable { 29 | 30 | public static final Creator CREATOR = new Creator() { 31 | @Override 32 | public JumbleException createFromParcel(Parcel source) { 33 | return new JumbleException(source); 34 | } 35 | 36 | @Override 37 | public JumbleException[] newArray(int size) { 38 | return new JumbleException[size]; 39 | } 40 | }; 41 | 42 | private JumbleDisconnectReason mReason; 43 | /** Indicates that this exception was caused by a reject from the server. */ 44 | private Mumble.Reject mReject; 45 | /** Indicates that this exception was caused by being kicked/banned from the server. */ 46 | private Mumble.UserRemove mUserRemove; 47 | 48 | public JumbleException(String message, Throwable e, JumbleDisconnectReason reason) { 49 | super(message, e); 50 | mReason = reason; 51 | } 52 | 53 | public JumbleException(String message, JumbleDisconnectReason reason) { 54 | super(message); 55 | mReason = reason; 56 | } 57 | 58 | public JumbleException(Throwable e, JumbleDisconnectReason reason) { 59 | super(e); 60 | mReason = reason; 61 | } 62 | 63 | public JumbleException(Mumble.Reject reject) { 64 | super("Reject: "+reject.getReason()); 65 | mReject = reject; 66 | mReason = JumbleDisconnectReason.REJECT; 67 | } 68 | 69 | public JumbleException(Mumble.UserRemove userRemove) { 70 | super((userRemove.getBan() ? "Banned: " : "Kicked: ")+userRemove.getReason()); 71 | mUserRemove = userRemove; 72 | mReason = JumbleDisconnectReason.USER_REMOVE; 73 | } 74 | 75 | private JumbleException(Parcel in) { 76 | super(in.readString(), (Throwable) in.readSerializable()); 77 | mReason = JumbleDisconnectReason.values()[in.readInt()]; 78 | mReject = (Mumble.Reject) in.readSerializable(); 79 | mUserRemove = (Mumble.UserRemove) in.readSerializable(); 80 | } 81 | 82 | public JumbleDisconnectReason getReason() { 83 | return mReason; 84 | } 85 | 86 | public Mumble.Reject getReject() { 87 | return mReject; 88 | } 89 | 90 | public Mumble.UserRemove getUserRemove() { 91 | return mUserRemove; 92 | } 93 | @Override 94 | public int describeContents() { 95 | return 0; 96 | } 97 | 98 | @Override 99 | public void writeToParcel(Parcel dest, int flags) { 100 | dest.writeString(getMessage()); 101 | dest.writeSerializable(getCause()); 102 | dest.writeInt(mReason.ordinal()); 103 | dest.writeSerializable(mReject); 104 | dest.writeSerializable(mUserRemove); 105 | } 106 | 107 | public enum JumbleDisconnectReason { 108 | REJECT, 109 | USER_REMOVE, 110 | CONNECTION_ERROR, 111 | OTHER_ERROR 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/androidTest/java/com/morlunk/jumble/test/URLParserTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.test; 19 | 20 | import com.morlunk.jumble.Constants; 21 | import com.morlunk.jumble.model.Server; 22 | import com.morlunk.jumble.util.MumbleURLParser; 23 | 24 | import junit.framework.TestCase; 25 | 26 | import java.net.MalformedURLException; 27 | 28 | /** 29 | * Tests the Mumble URL parser. 30 | * Created by andrew on 03/03/14. 31 | */ 32 | public class URLParserTest extends TestCase { 33 | 34 | public void testURL() { 35 | String url = "mumble://server.com/"; 36 | try { 37 | Server server = MumbleURLParser.parseURL(url); 38 | assertEquals(server.getHost(), "server.com"); 39 | assertEquals(server.getPort(), Constants.DEFAULT_PORT); 40 | } catch (MalformedURLException e) { 41 | fail("Failed to parse URL."); 42 | } 43 | } 44 | 45 | public void testURLWithPort() { 46 | String url = "mumble://server.com:5000/"; 47 | try { 48 | Server server = MumbleURLParser.parseURL(url); 49 | assertEquals(server.getHost(), "server.com"); 50 | assertEquals(server.getPort(), 5000); 51 | } catch (MalformedURLException e) { 52 | fail("Failed to parse URL."); 53 | } 54 | } 55 | 56 | public void testURLWithUsername() { 57 | String url = "mumble://TestUser@server.com/"; 58 | try { 59 | Server server = MumbleURLParser.parseURL(url); 60 | assertEquals(server.getHost(), "server.com"); 61 | assertEquals(server.getUsername(), "TestUser"); 62 | assertEquals(server.getPort(), Constants.DEFAULT_PORT); 63 | } catch (MalformedURLException e) { 64 | fail("Failed to parse URL."); 65 | } 66 | } 67 | 68 | public void testURLWithCredentials() { 69 | String url = "mumble://TestUser:mypassword@server.com:5000/"; 70 | try { 71 | Server server = MumbleURLParser.parseURL(url); 72 | assertEquals(server.getHost(), "server.com"); 73 | assertEquals(server.getUsername(), "TestUser"); 74 | assertEquals(server.getPassword(), "mypassword"); 75 | assertEquals(server.getPort(), 5000); 76 | } catch (MalformedURLException e) { 77 | fail("Failed to parse URL."); 78 | } 79 | } 80 | 81 | public void testURLWithPassword() { 82 | String url = "mumble://:mypassword@server.com/"; 83 | try { 84 | Server server = MumbleURLParser.parseURL(url); 85 | assertEquals(server.getHost(), "server.com"); 86 | assertEquals(server.getPassword(), "mypassword"); 87 | assertEquals(server.getPort(), Constants.DEFAULT_PORT); 88 | } catch (MalformedURLException e) { 89 | fail("Failed to parse URL."); 90 | } 91 | } 92 | 93 | public void testInvalidScheme() { 94 | String url = "grumble://server.com/"; 95 | try { 96 | MumbleURLParser.parseURL(url); 97 | fail("Successfully parsed bad scheme!"); 98 | } catch (MalformedURLException e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/IJumbleService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble; 19 | 20 | import com.morlunk.jumble.model.IChannel; 21 | import com.morlunk.jumble.model.IUser; 22 | import com.morlunk.jumble.model.Message; 23 | import com.morlunk.jumble.model.Server; 24 | import com.morlunk.jumble.model.WhisperTarget; 25 | import com.morlunk.jumble.net.JumbleUDPMessageType; 26 | import com.morlunk.jumble.util.IJumbleObserver; 27 | import com.morlunk.jumble.util.JumbleDisconnectedException; 28 | import com.morlunk.jumble.util.JumbleException; 29 | import com.morlunk.jumble.util.VoiceTargetMode; 30 | 31 | import java.util.List; 32 | 33 | /** 34 | * A public interface for clients to communicate with a {@link JumbleService}. 35 | * The long-term goal for this class is to migrate of the complexity out of this class into a 36 | * JumbleProtocol class that is owned by a {@link com.morlunk.jumble.net.JumbleConnection}. 37 | *

38 | * Calls are not guaranteed to be thread-safe, so only call the binder from the main thread. 39 | * Service state changes related to connection state are only guaranteed to work if isConnected() 40 | * is checked to be true. 41 | *

42 | * If not explicitly stated in the method documentation, any call that depends on connection state 43 | * will throw IllegalStateException if disconnected or not synchronized. 44 | */ 45 | public interface IJumbleService { 46 | void registerObserver(IJumbleObserver observer); 47 | 48 | void unregisterObserver(IJumbleObserver observer); 49 | 50 | /** 51 | * @return true if handshaking with the server has completed. 52 | */ 53 | boolean isConnected(); 54 | 55 | /** 56 | * Disconnects from the active connection, or does nothing if no connection is active. 57 | */ 58 | void disconnect(); 59 | 60 | /** 61 | * Returns the current connection state of the service. 62 | * @return one of {@link JumbleService.ConnectionState}. 63 | */ 64 | JumbleService.ConnectionState getConnectionState(); 65 | 66 | /** 67 | * If the {@link JumbleService} disconnected due to an error, returns that error. 68 | * @return The error causing disconnection. If the last disconnection was successful or a 69 | * connection has yet to be established, returns null. 70 | */ 71 | JumbleException getConnectionError(); 72 | 73 | /** 74 | * Returns the reconnection state of the {@link JumbleService}. 75 | * @return true if the service will attempt to automatically reconnect in the future. 76 | */ 77 | boolean isReconnecting(); 78 | 79 | /** 80 | * Cancels any future reconnection attempts. Does nothing if reconnection is not in progress. 81 | */ 82 | void cancelReconnect(); 83 | 84 | /** 85 | * @return the server that Jumble is currently connected to, was connected to, or will attempt connection to. 86 | */ 87 | Server getTargetServer(); 88 | 89 | /** 90 | * Returns the active session with the remote, or throws an exception if no session is currently 91 | * active. This can be checked using {@link IJumbleService#isConnected()}. 92 | * @return the active session. 93 | * @throws JumbleDisconnectedException if the connection state is not CONNECTED. 94 | */ 95 | IJumbleSession getSession() throws JumbleDisconnectedException; 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/encoder/CELT11Encoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.encoder; 19 | 20 | import com.googlecode.javacpp.IntPointer; 21 | import com.googlecode.javacpp.Pointer; 22 | import com.morlunk.jumble.audio.javacpp.CELT11; 23 | import com.morlunk.jumble.exception.NativeAudioException; 24 | import com.morlunk.jumble.net.PacketBuffer; 25 | import com.morlunk.jumble.protocol.AudioHandler; 26 | 27 | import java.nio.BufferOverflowException; 28 | import java.nio.BufferUnderflowException; 29 | 30 | /** 31 | * Created by andrew on 08/12/14. 32 | */ 33 | public class CELT11Encoder implements IEncoder { 34 | private final byte[][] mBuffer; 35 | private final int mBufferSize; 36 | private final int mFramesPerPacket; 37 | private int mBufferedFrames; 38 | 39 | private Pointer mState; 40 | 41 | public CELT11Encoder(int sampleRate, int channels, int framesPerPacket) throws 42 | NativeAudioException { 43 | mFramesPerPacket = framesPerPacket; 44 | mBufferSize = sampleRate / 800; 45 | mBuffer = new byte[framesPerPacket][mBufferSize]; 46 | mBufferedFrames = 0; 47 | 48 | IntPointer error = new IntPointer(1); 49 | error.put(0); 50 | mState = CELT11.celt_encoder_create(sampleRate, channels, error); 51 | if(error.get() < 0) throw new NativeAudioException("CELT 0.11.0 encoder initialization " + 52 | "failed with error: "+error.get()); 53 | } 54 | 55 | @Override 56 | public int encode(short[] input, int frameSize) throws NativeAudioException { 57 | if (mBufferedFrames >= mFramesPerPacket) { 58 | throw new BufferOverflowException(); 59 | } 60 | 61 | int result = CELT11.celt_encode(mState, input, frameSize, mBuffer[mBufferedFrames], 62 | mBufferSize); 63 | if(result < 0) throw new NativeAudioException("CELT 0.11.0 encoding failed with error: " 64 | + result); 65 | mBufferedFrames++; 66 | return result; 67 | } 68 | 69 | @Override 70 | public int getBufferedFrames() { 71 | return mBufferedFrames; 72 | } 73 | 74 | @Override 75 | public boolean isReady() { 76 | return mBufferedFrames == mFramesPerPacket; 77 | } 78 | 79 | @Override 80 | public void getEncodedData(PacketBuffer packetBuffer) throws BufferUnderflowException { 81 | if (mBufferedFrames < mFramesPerPacket) { 82 | throw new BufferUnderflowException(); 83 | } 84 | 85 | for (int x = 0; x < mBufferedFrames; x++) { 86 | byte[] frame = mBuffer[x]; 87 | int head = frame.length; 88 | if(x < mBufferedFrames - 1) 89 | head |= 0x80; 90 | packetBuffer.append(head); 91 | packetBuffer.append(frame, frame.length); 92 | } 93 | 94 | mBufferedFrames = 0; 95 | } 96 | 97 | @Override 98 | public void terminate() throws NativeAudioException { 99 | // TODO 100 | } 101 | 102 | @Override 103 | public void destroy() { 104 | CELT11.celt_encoder_destroy(mState); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Die maximale Netzwerk-Bandbreite des Servers beträgt nur %1$d kbit/s. 4 | Die Audioqualität wird automatisch angepasst auf %2$d kbit/s (%3$d ms) 5 | %1$s hinein verschoben von %2$s durch %3$s. 6 | Stumm und taub. 7 | Stumm. 8 | Nicht stumm. 9 | Nicht mehr stumm und nicht taub. 10 | %s ist nun stumm und taub. 11 | %s ist jetzt stumm gestellt. 12 | %s ist nicht mehr stumm. 13 | %s ist nicht mehr stumm und nicht mehr taub. 14 | Sie wurden stumm und taub gestellt durch %s. 15 | Sie wurden entstummt und enttaubt durch %s. 16 | Sie wurden stumm gestellt durch %s. 17 | Stummstellung wurde deaktiviert durch %s. 18 | Sie wurden enttaubt durch %s. 19 | Ihre Sprachunterdrückung wurde aktiviert. 20 | Ihre Sprachunterdrückung wurde entfernt. 21 | Ihre Sprachunterdrückung wurde entfernt durch %s. 22 | %s hat den Server betreten. 23 | %s hat den Server verlassen. 24 | Sie wurden vom Server gekickt und gebannt von %1$s: %2$s. 25 | Sie wurden vom Server gekickt durch %1$s: %2$s. 26 | %3$s wurde vom Server gekickt und gebannt durch %1$s: %2$s. 27 | %3$s wurde vom Server gekickt durch %1$s: %2$s. 28 | Aufnahme gestartet 29 | Aufnahme beendet 30 | %s nimmt jetzt auf. 31 | %s hat die Aufnahme beendet. 32 | %s betrat den Kanal. 33 | %1$s verschoben nach %2$s. 34 | %1$s wurde aus %2$s hinein verschoben durch %3$s. 35 | %1$s wurde verschoben nach %2$s durch %3$s. 36 | Bluetooth verbunden 37 | Bluetooth wurde getrennt 38 | Verweigert: Ungültiger Kanalname. 39 | Verweigert: Textnachricht zu lang. 40 | Verweigert: Operation nicht erlaubt in einem temporären Kanal. 41 | Sie benötigten ein Zertifikat, um diese Operation auszuführen. 42 | Ungültiger Benutzername. 43 | Kanal ist voll. 44 | Channel Nesting Limit erreicht. 45 | Grund: %s 46 | Zugriff verweigert. 47 | Server 48 | der Server 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/Server.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | import android.os.Parcel; 21 | import android.os.Parcelable; 22 | 23 | public class Server implements Parcelable { 24 | 25 | private long mId; 26 | private String mName; 27 | private String mHost; 28 | private int mPort; 29 | private String mUsername; 30 | private String mPassword; 31 | 32 | public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 33 | 34 | @Override 35 | public Server createFromParcel(Parcel parcel) { 36 | return new Server(parcel); 37 | } 38 | 39 | @Override 40 | public Server[] newArray(int i) { 41 | return new Server[i]; 42 | } 43 | }; 44 | 45 | public Server(long id, String name, String host, int port, String username, String password) { 46 | mId = id; 47 | mName = name; 48 | mHost = host; 49 | mPort = port; 50 | mUsername = username; 51 | mPassword = password; 52 | } 53 | 54 | private Server(Parcel in) { 55 | readFromParcel(in); 56 | 57 | } 58 | 59 | @Override 60 | public void writeToParcel(Parcel parcel, int i) { 61 | parcel.writeLong(mId); 62 | parcel.writeString(mName); 63 | parcel.writeString(mHost); 64 | parcel.writeInt(mPort); 65 | parcel.writeString(mUsername); 66 | parcel.writeString(mPassword); 67 | } 68 | 69 | public void readFromParcel(Parcel in) { 70 | mId = in.readLong(); 71 | mName = in.readString(); 72 | mHost = in.readString(); 73 | mPort = in.readInt(); 74 | mUsername = in.readString(); 75 | mPassword = in.readString(); 76 | } 77 | 78 | @Override 79 | public int describeContents() { 80 | return 0; 81 | } 82 | 83 | public long getId() { 84 | return mId; 85 | } 86 | 87 | public void setId(long id) { 88 | mId = id; 89 | } 90 | 91 | /** 92 | * Returns a user-defined name for the server, or the host if the user-defined name is not set. 93 | * @return A user readable name for the server. 94 | */ 95 | public String getName() { 96 | return (mName != null && mName.length() > 0) ? mName : mHost; 97 | } 98 | 99 | public void setName(String mName) { 100 | this.mName = mName; 101 | } 102 | 103 | public String getHost() { 104 | return mHost; 105 | } 106 | 107 | public void setHost(String mHost) { 108 | this.mHost = mHost; 109 | } 110 | 111 | public int getPort() { 112 | return mPort; 113 | } 114 | 115 | public void setPort(int mPort) { 116 | this.mPort = mPort; 117 | } 118 | 119 | public String getUsername() { 120 | return mUsername; 121 | } 122 | 123 | public void setUsername(String mUsername) { 124 | this.mUsername = mUsername; 125 | } 126 | 127 | public String getPassword() { 128 | return mPassword; 129 | } 130 | 131 | public void setPassword(String mPassword) { 132 | this.mPassword = mPassword; 133 | } 134 | 135 | /** 136 | * Returns whether or not the server is stored in a database. 137 | * @return true if the server's ID is in the database. 138 | */ 139 | public boolean isSaved() { 140 | return mId != -1; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/JumbleNetworkListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.util; 19 | 20 | import com.morlunk.jumble.net.JumbleUDPMessageType; 21 | import com.morlunk.jumble.protobuf.Mumble; 22 | import com.morlunk.jumble.protocol.JumbleTCPMessageListener; 23 | import com.morlunk.jumble.protocol.JumbleUDPMessageListener; 24 | 25 | /** 26 | * Created by andrew on 23/04/14. 27 | */ 28 | public class JumbleNetworkListener implements JumbleTCPMessageListener, JumbleUDPMessageListener { 29 | @Override 30 | public void messageAuthenticate(Mumble.Authenticate msg) { 31 | 32 | } 33 | 34 | @Override 35 | public void messageBanList(Mumble.BanList msg) { 36 | 37 | } 38 | 39 | @Override 40 | public void messageReject(Mumble.Reject msg) { 41 | 42 | } 43 | 44 | @Override 45 | public void messageServerSync(Mumble.ServerSync msg) { 46 | 47 | } 48 | 49 | @Override 50 | public void messageServerConfig(Mumble.ServerConfig msg) { 51 | 52 | } 53 | 54 | @Override 55 | public void messagePermissionDenied(Mumble.PermissionDenied msg) { 56 | 57 | } 58 | 59 | @Override 60 | public void messageUDPTunnel(Mumble.UDPTunnel msg) { 61 | 62 | } 63 | 64 | @Override 65 | public void messageUserState(Mumble.UserState msg) { 66 | 67 | } 68 | 69 | @Override 70 | public void messageUserRemove(Mumble.UserRemove msg) { 71 | 72 | } 73 | 74 | @Override 75 | public void messageChannelState(Mumble.ChannelState msg) { 76 | 77 | } 78 | 79 | @Override 80 | public void messageChannelRemove(Mumble.ChannelRemove msg) { 81 | 82 | } 83 | 84 | @Override 85 | public void messageTextMessage(Mumble.TextMessage msg) { 86 | 87 | } 88 | 89 | @Override 90 | public void messageACL(Mumble.ACL msg) { 91 | 92 | } 93 | 94 | @Override 95 | public void messageQueryUsers(Mumble.QueryUsers msg) { 96 | 97 | } 98 | 99 | @Override 100 | public void messagePing(Mumble.Ping msg) { 101 | 102 | } 103 | 104 | @Override 105 | public void messageCryptSetup(Mumble.CryptSetup msg) { 106 | 107 | } 108 | 109 | @Override 110 | public void messageContextAction(Mumble.ContextAction msg) { 111 | 112 | } 113 | 114 | @Override 115 | public void messageContextActionModify(Mumble.ContextActionModify msg) { 116 | 117 | } 118 | 119 | @Override 120 | public void messageRemoveContextAction(Mumble.ContextActionModify msg) { 121 | 122 | } 123 | 124 | @Override 125 | public void messageVersion(Mumble.Version msg) { 126 | 127 | } 128 | 129 | @Override 130 | public void messageUserList(Mumble.UserList msg) { 131 | 132 | } 133 | 134 | @Override 135 | public void messagePermissionQuery(Mumble.PermissionQuery msg) { 136 | 137 | } 138 | 139 | @Override 140 | public void messageCodecVersion(Mumble.CodecVersion msg) { 141 | 142 | } 143 | 144 | @Override 145 | public void messageUserStats(Mumble.UserStats msg) { 146 | 147 | } 148 | 149 | @Override 150 | public void messageRequestBlob(Mumble.RequestBlob msg) { 151 | 152 | } 153 | 154 | @Override 155 | public void messageSuggestConfig(Mumble.SuggestConfig msg) { 156 | 157 | } 158 | 159 | @Override 160 | public void messageVoiceTarget(Mumble.VoiceTarget msg) { 161 | 162 | } 163 | 164 | @Override 165 | public void messageUDPPing(byte[] data) { 166 | 167 | } 168 | 169 | @Override 170 | public void messageVoiceData(byte[] data, JumbleUDPMessageType messageType) { 171 | 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 20 | Server maximum network bandwidth is only %1$d kbit/s. Audio quality auto-adjusted to %2$d kbit/s (%3$d ms) 21 | %1$s moved in from %2$s by %3$s. 22 | Muted and deafened. 23 | Muted. 24 | Unmuted. 25 | Unmuted and undeafened. 26 | %s is now muted and deafened. 27 | %s is now muted. 28 | %s is now unmuted. 29 | %s is now unmuted and undeafened. 30 | You were muted and deafened by %s. 31 | You were unmuted and undeafened by %s. 32 | You were muted by %s. 33 | You were unmuted by %s. 34 | You were undeafened by %s. 35 | You were suppressed. 36 | You were unsuppressed. 37 | You were unsuppressed by %s. 38 | %s connected. 39 | %s disconnected. 40 | You were kicked and banned from the server by %1$s: %2$s. 41 | You were kicked from the server by %1$s: %2$s. 42 | %3$s was kicked and banned from the server by %1$s: %2$s. 43 | %3$s was kicked from the server by %1$s: %2$s. 44 | Recording started 45 | Recording stopped 46 | %s started recording. 47 | %s stopped recording. 48 | %s entered channel. 49 | %1$s moved to %2$s. 50 | %1$s moved in from %2$s by %3$s. 51 | %1$s moved to %2$s by %3$s. 52 | Bluetooth connected 53 | Bluetooth disconnected 54 | Denied: Invalid channel name. 55 | Denied: Text message too long. 56 | Denied: Operation not permitted in temporary channel. 57 | You need a certificate to perform this operation. 58 | Invalid username. 59 | Channel is full. 60 | Channel nesting limit reached. 61 | Reason: %s 62 | Permission denied. 63 | Server 64 | the server 65 | 66 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/encoder/CELT7Encoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.encoder; 19 | 20 | import com.googlecode.javacpp.IntPointer; 21 | import com.googlecode.javacpp.Pointer; 22 | import com.morlunk.jumble.audio.javacpp.CELT7; 23 | import com.morlunk.jumble.exception.NativeAudioException; 24 | import com.morlunk.jumble.net.PacketBuffer; 25 | import com.morlunk.jumble.protocol.AudioHandler; 26 | 27 | import java.nio.BufferOverflowException; 28 | import java.nio.BufferUnderflowException; 29 | 30 | /** 31 | * Created by andrew on 08/12/14. 32 | */ 33 | public class CELT7Encoder implements IEncoder { 34 | private final byte[][] mBuffer; 35 | private final int[] mPacketLengths; 36 | private final int mBufferSize; 37 | private final int mFramesPerPacket; 38 | private int mBufferedFrames; 39 | private boolean mReady; 40 | 41 | private Pointer mMode; 42 | private Pointer mState; 43 | 44 | public CELT7Encoder(int sampleRate, int frameSize, int channels, 45 | int framesPerPacket, int bitrate, int maxBufferSize) 46 | throws NativeAudioException { 47 | mFramesPerPacket = framesPerPacket; 48 | mBufferSize = Math.min(maxBufferSize, bitrate / 800); 49 | mBuffer = new byte[framesPerPacket][mBufferSize]; 50 | mPacketLengths = new int[framesPerPacket]; 51 | mBufferedFrames = 0; 52 | 53 | IntPointer error = new IntPointer(1); 54 | error.put(0); 55 | mMode = CELT7.celt_mode_create(sampleRate, frameSize, error); 56 | if(error.get() < 0) throw new NativeAudioException("CELT 0.7.0 encoder initialization failed with error: "+error.get()); 57 | mState = CELT7.celt_encoder_create(mMode, channels, error); 58 | if(error.get() < 0) throw new NativeAudioException("CELT 0.7.0 encoder initialization failed with error: "+error.get()); 59 | CELT7.celt_encoder_ctl(mState, CELT7.CELT_SET_PREDICTION_REQUEST, 0); 60 | CELT7.celt_encoder_ctl(mState, CELT7.CELT_SET_VBR_RATE_REQUEST, bitrate); 61 | } 62 | 63 | @Override 64 | public int encode(short[] input, int inputSize) throws NativeAudioException { 65 | if (mBufferedFrames >= mFramesPerPacket) { 66 | throw new BufferOverflowException(); 67 | } 68 | 69 | int result = CELT7.celt_encode(mState, input, null, mBuffer[mBufferedFrames], mBufferSize); 70 | if(result < 0) throw new NativeAudioException("CELT 0.7.0 encoding failed with error: " 71 | + result); 72 | mPacketLengths[mBufferedFrames] = result; 73 | mBufferedFrames++; 74 | 75 | if (mBufferedFrames >= mFramesPerPacket) 76 | mReady = true; 77 | 78 | return result; 79 | } 80 | 81 | @Override 82 | public int getBufferedFrames() { 83 | return mBufferedFrames; 84 | } 85 | 86 | @Override 87 | public boolean isReady() { 88 | return mReady && mBufferedFrames > 0; 89 | } 90 | 91 | @Override 92 | public void getEncodedData(PacketBuffer packetBuffer) throws BufferUnderflowException { 93 | if (!mReady) 94 | throw new BufferUnderflowException(); 95 | 96 | for (int x = 0; x < mBufferedFrames; x++) { 97 | byte[] frame = mBuffer[x]; 98 | int length = mPacketLengths[x]; 99 | int head = length; 100 | if(x < mBufferedFrames - 1) 101 | head |= 0x80; 102 | packetBuffer.append(head); 103 | packetBuffer.append(frame, length); 104 | } 105 | 106 | mBufferedFrames = 0; 107 | mReady = false; 108 | } 109 | 110 | @Override 111 | public void terminate() throws NativeAudioException { 112 | mReady = true; 113 | } 114 | 115 | @Override 116 | public void destroy() { 117 | CELT7.celt_encoder_destroy(mState); 118 | CELT7.celt_mode_destroy(mMode); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/protocol/JumbleTCPMessageListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.protocol; 19 | 20 | import com.morlunk.jumble.protobuf.Mumble; 21 | 22 | public interface JumbleTCPMessageListener { 23 | public void messageAuthenticate(Mumble.Authenticate msg); 24 | public void messageBanList(Mumble.BanList msg); 25 | public void messageReject(Mumble.Reject msg); 26 | public void messageServerSync(Mumble.ServerSync msg); 27 | public void messageServerConfig(Mumble.ServerConfig msg); 28 | public void messagePermissionDenied(Mumble.PermissionDenied msg); 29 | public void messageUDPTunnel(Mumble.UDPTunnel msg); 30 | public void messageUserState(Mumble.UserState msg); 31 | public void messageUserRemove(Mumble.UserRemove msg); 32 | public void messageChannelState(Mumble.ChannelState msg); 33 | public void messageChannelRemove(Mumble.ChannelRemove msg); 34 | public void messageTextMessage(Mumble.TextMessage msg); 35 | public void messageACL(Mumble.ACL msg); 36 | public void messageQueryUsers(Mumble.QueryUsers msg); 37 | public void messagePing(Mumble.Ping msg); 38 | public void messageCryptSetup(Mumble.CryptSetup msg); 39 | public void messageContextAction(Mumble.ContextAction msg); 40 | public void messageContextActionModify(Mumble.ContextActionModify msg); 41 | public void messageRemoveContextAction(Mumble.ContextActionModify msg); 42 | public void messageVersion(Mumble.Version msg); 43 | public void messageUserList(Mumble.UserList msg); 44 | public void messagePermissionQuery(Mumble.PermissionQuery msg); 45 | public void messageCodecVersion(Mumble.CodecVersion msg); 46 | public void messageUserStats(Mumble.UserStats msg); 47 | public void messageRequestBlob(Mumble.RequestBlob msg); 48 | public void messageSuggestConfig(Mumble.SuggestConfig msg); 49 | public void messageVoiceTarget(Mumble.VoiceTarget msg); 50 | 51 | /** 52 | * Reads incoming protobuf TCP messages and performs the necessary action(s). 53 | * Designed to be subclassed at any level of the library, the default implementations do nothing. 54 | * Created by andrew on 24/06/13. 55 | */ 56 | public static class Stub implements JumbleTCPMessageListener { 57 | 58 | public void messageAuthenticate(Mumble.Authenticate msg) {} 59 | public void messageBanList(Mumble.BanList msg) {} 60 | public void messageReject(Mumble.Reject msg) {} 61 | public void messageServerSync(Mumble.ServerSync msg) {} 62 | public void messageServerConfig(Mumble.ServerConfig msg) {} 63 | public void messagePermissionDenied(Mumble.PermissionDenied msg) {} 64 | public void messageUDPTunnel(Mumble.UDPTunnel msg) {} 65 | public void messageUserState(Mumble.UserState msg) {} 66 | public void messageUserRemove(Mumble.UserRemove msg) {} 67 | public void messageChannelState(Mumble.ChannelState msg) {} 68 | public void messageChannelRemove(Mumble.ChannelRemove msg) {} 69 | public void messageTextMessage(Mumble.TextMessage msg) {} 70 | public void messageACL(Mumble.ACL msg) {} 71 | public void messageQueryUsers(Mumble.QueryUsers msg) {} 72 | public void messagePing(Mumble.Ping msg) {} 73 | public void messageCryptSetup(Mumble.CryptSetup msg) {} 74 | public void messageContextAction(Mumble.ContextAction msg) {} 75 | public void messageContextActionModify(Mumble.ContextActionModify msg) {} 76 | public void messageRemoveContextAction(Mumble.ContextActionModify msg) {} 77 | public void messageVersion(Mumble.Version msg) {} 78 | public void messageUserList(Mumble.UserList msg) {} 79 | public void messagePermissionQuery(Mumble.PermissionQuery msg) {} 80 | public void messageCodecVersion(Mumble.CodecVersion msg) {} 81 | public void messageUserStats(Mumble.UserStats msg) {} 82 | public void messageRequestBlob(Mumble.RequestBlob msg) {} 83 | public void messageSuggestConfig(Mumble.SuggestConfig msg) {} 84 | public void messageVoiceTarget(Mumble.VoiceTarget msg) {} 85 | } 86 | } -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/model/Message.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.model; 19 | 20 | import android.os.Parcel; 21 | import android.os.Parcelable; 22 | import android.text.format.Time; 23 | 24 | import java.util.ArrayList; 25 | import java.util.Collections; 26 | import java.util.Date; 27 | import java.util.List; 28 | 29 | /** 30 | * A class encapsulating a text message from a Mumble server. 31 | * NOTE: Always prefer using getActorName(). You CANNOT rely on getActor() to provide this info, 32 | * as the actor may no longer be on the server. 33 | * Created by andrew on 03/12/13. 34 | */ 35 | public class Message implements IMessage { 36 | private int mActor; 37 | private String mActorName; 38 | private List mChannels; 39 | private List mTrees; 40 | private List mUsers; 41 | private String mMessage; 42 | private long mReceivedTime; 43 | 44 | public Message(String message) { 45 | mMessage = message; 46 | mActor = -1; 47 | mReceivedTime = new Date().getTime(); 48 | mChannels = new ArrayList(); 49 | mTrees = new ArrayList(); 50 | mUsers = new ArrayList(); 51 | } 52 | 53 | public Message(int actor, String actorName, List channels, List trees, List users, String message) { 54 | this(message); 55 | mActor = actor; 56 | mActorName = actorName; 57 | mChannels = channels; 58 | mTrees = trees; 59 | mUsers = users; 60 | } 61 | @Override 62 | public int getActor() { 63 | return mActor; 64 | } 65 | 66 | @Override 67 | public String getActorName() { 68 | return mActorName; 69 | } 70 | 71 | @Override 72 | public List getTargetChannels() { 73 | return Collections.unmodifiableList(mChannels); 74 | } 75 | 76 | @Override 77 | public List getTargetTrees() { 78 | return Collections.unmodifiableList(mTrees); 79 | } 80 | 81 | @Override 82 | public List getTargetUsers() { 83 | return Collections.unmodifiableList(mUsers); 84 | } 85 | 86 | @Override 87 | public String getMessage() { 88 | return mMessage; 89 | } 90 | 91 | @Override 92 | public long getReceivedTime() { 93 | return mReceivedTime; 94 | } 95 | 96 | @Override 97 | public boolean equals(Object o) { 98 | if (this == o) return true; 99 | if (o == null || getClass() != o.getClass()) return false; 100 | 101 | Message message = (Message) o; 102 | 103 | if (mActor != message.mActor) return false; 104 | if (mReceivedTime != message.mReceivedTime) return false; 105 | if (mActorName != null ? !mActorName.equals(message.mActorName) : message.mActorName != null) 106 | return false; 107 | if (mChannels != null ? !mChannels.equals(message.mChannels) : message.mChannels != null) 108 | return false; 109 | if (mMessage != null ? !mMessage.equals(message.mMessage) : message.mMessage != null) 110 | return false; 111 | if (mTrees != null ? !mTrees.equals(message.mTrees) : message.mTrees != null) return false; 112 | if (mUsers != null ? !mUsers.equals(message.mUsers) : message.mUsers != null) return false; 113 | 114 | return true; 115 | } 116 | 117 | @Override 118 | public int hashCode() { 119 | int result = mActor; 120 | result = 31 * result + (mActorName != null ? mActorName.hashCode() : 0); 121 | result = 31 * result + (mChannels != null ? mChannels.hashCode() : 0); 122 | result = 31 * result + (mTrees != null ? mTrees.hashCode() : 0); 123 | result = 31 * result + (mUsers != null ? mUsers.hashCode() : 0); 124 | result = 31 * result + (mMessage != null ? mMessage.hashCode() : 0); 125 | result = 31 * result + (int) (mReceivedTime ^ (mReceivedTime >>> 32)); 126 | return result; 127 | } 128 | 129 | /** 130 | * The type of message this object represents. 131 | * @deprecated 132 | */ 133 | public enum Type { 134 | INFO, 135 | WARNING, 136 | TEXT_MESSAGE 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/javacpp/CELT11.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.javacpp; 19 | 20 | import com.googlecode.javacpp.IntPointer; 21 | import com.googlecode.javacpp.Loader; 22 | import com.googlecode.javacpp.Pointer; 23 | import com.googlecode.javacpp.annotation.Cast; 24 | import com.googlecode.javacpp.annotation.Platform; 25 | import com.morlunk.jumble.audio.IDecoder; 26 | import com.morlunk.jumble.exception.NativeAudioException; 27 | import com.morlunk.jumble.protocol.AudioHandler; 28 | 29 | import java.nio.ByteBuffer; 30 | 31 | /** 32 | * Created by andrew on 20/10/13. 33 | */ 34 | @Platform(library="jnicelt11", cinclude={"",""}) 35 | public class CELT11 { 36 | public static final int CELT_GET_BITSTREAM_VERSION = 2000; 37 | public static final int CELT_SET_BITRATE_REQUEST = 6; 38 | public static final int CELT_SET_PREDICTION_REQUEST = 4; 39 | 40 | static { 41 | Loader.load(); 42 | } 43 | 44 | public static native Pointer celt_mode_create(int sampleRate, int frameSize, IntPointer error); 45 | public static native int celt_mode_info(@Cast("const CELTMode*") Pointer mode, int request, IntPointer value); 46 | public static native void celt_mode_destroy(@Cast("CELTMode*") Pointer mode); 47 | 48 | public static native Pointer celt_decoder_create(int sampleRate, int channels, IntPointer error); 49 | public static native int celt_decode(@Cast("CELTDecoder*") Pointer st, @Cast("const unsigned char*") ByteBuffer data, int len, short[] pcm, int frameSize); 50 | public static native int celt_decode_float(@Cast("CELTDecoder*") Pointer st, @Cast("const unsigned char*") ByteBuffer data, int len, float[] pcm, int frameSize); 51 | public static native int celt_decoder_ctl(@Cast("CELTDecoder*") Pointer st, int request, Pointer val); 52 | public static native void celt_decoder_destroy(@Cast("CELTDecoder*") Pointer st); 53 | 54 | public static native Pointer celt_encoder_create(int sampleRate, int channels, IntPointer error); 55 | public static native int celt_encoder_ctl(@Cast("CELTEncoder*")Pointer state, int request, Pointer val); 56 | public static native int celt_encoder_ctl(@Cast("CELTEncoder*")Pointer state, int request, int val); 57 | public static native int celt_encode(@Cast("CELTEncoder*") Pointer state, @Cast("const short*") short[] pcm, int frameSize, @Cast("unsigned char*") byte[] compressed, int maxCompressedBytes); 58 | public static native void celt_encoder_destroy(@Cast("CELTEncoder*") Pointer state); 59 | 60 | /** 61 | * @return an integer describing the CELT bitstream version. 62 | */ 63 | public static int getBitstreamVersion() { 64 | IntPointer versionPtr = new IntPointer(); 65 | Pointer modePtr = celt_mode_create(AudioHandler.SAMPLE_RATE, AudioHandler.FRAME_SIZE, null); 66 | celt_mode_info(modePtr, CELT_GET_BITSTREAM_VERSION, versionPtr); 67 | celt_mode_destroy(modePtr); 68 | return versionPtr.get(); 69 | } 70 | 71 | public static class CELT11Decoder implements IDecoder { 72 | 73 | private Pointer mState; 74 | 75 | public CELT11Decoder(int sampleRate, int channels) throws NativeAudioException { 76 | IntPointer error = new IntPointer(1); 77 | error.put(0); 78 | mState = celt_decoder_create(sampleRate, channels, error); 79 | if(error.get() < 0) throw new NativeAudioException("CELT 0.11.0 decoder initialization failed with error: "+error.get()); 80 | } 81 | 82 | @Override 83 | public int decodeFloat(ByteBuffer input, int inputSize, float[] output, int frameSize) throws NativeAudioException { 84 | int result = celt_decode_float(mState, input, inputSize, output, frameSize); 85 | if(result < 0) throw new NativeAudioException("CELT 0.11.0 decoding failed with error: "+result); 86 | return frameSize; 87 | } 88 | 89 | @Override 90 | public int decodeShort(ByteBuffer input, int inputSize, short[] output, int frameSize) throws NativeAudioException { 91 | int result = celt_decode(mState, input, inputSize, output, frameSize); 92 | if(result < 0) throw new NativeAudioException("CELT 0.11.0 decoding failed with error: "+result); 93 | return frameSize; 94 | } 95 | 96 | @Override 97 | public void destroy() { 98 | celt_decoder_destroy(mState); 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/javacpp/CELT7.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.javacpp; 19 | 20 | import com.googlecode.javacpp.IntPointer; 21 | import com.googlecode.javacpp.Loader; 22 | import com.googlecode.javacpp.Pointer; 23 | import com.googlecode.javacpp.annotation.Cast; 24 | import com.googlecode.javacpp.annotation.Platform; 25 | import com.morlunk.jumble.audio.IDecoder; 26 | import com.morlunk.jumble.exception.NativeAudioException; 27 | import com.morlunk.jumble.protocol.AudioHandler; 28 | 29 | import java.nio.ByteBuffer; 30 | 31 | /** 32 | * Created by andrew on 20/10/13. 33 | */ 34 | @Platform(library="jnicelt7", cinclude={"",""}) 35 | public class CELT7 { 36 | public static final int CELT_GET_BITSTREAM_VERSION = 2000; 37 | public static final int CELT_SET_VBR_RATE_REQUEST = 6; 38 | public static final int CELT_SET_PREDICTION_REQUEST = 4; 39 | 40 | static { 41 | Loader.load(); 42 | } 43 | 44 | public static native Pointer celt_mode_create(int sampleRate, int frameSize, IntPointer error); 45 | public static native int celt_mode_info(@Cast("const CELTMode*") Pointer mode, int request, IntPointer value); 46 | public static native void celt_mode_destroy(@Cast("CELTMode*") Pointer mode); 47 | 48 | public static native Pointer celt_decoder_create(@Cast("CELTMode*") Pointer mode, int channels, IntPointer error); 49 | public static native int celt_decode(@Cast("CELTDecoder*") Pointer st, @Cast("const unsigned char*") ByteBuffer data, int len, short[] pcm); 50 | public static native int celt_decode_float(@Cast("CELTDecoder*") Pointer st, @Cast("const unsigned char*") ByteBuffer data, int len, float[] pcm); 51 | public static native int celt_decoder_ctl(@Cast("CELTDecoder*") Pointer st, int request, Pointer val); 52 | public static native void celt_decoder_destroy(@Cast("CELTDecoder*") Pointer st); 53 | 54 | public static native Pointer celt_encoder_create(@Cast("const CELTMode *") Pointer mode, int channels, IntPointer error); 55 | public static native int celt_encoder_ctl(@Cast("CELTEncoder*")Pointer state, int request, Pointer val); 56 | public static native int celt_encoder_ctl(@Cast("CELTEncoder*")Pointer state, int request, int val); 57 | public static native int celt_encode(@Cast("CELTEncoder *") Pointer state, @Cast("const short *") short[] pcm, @Cast("short *") short[] optionalSynthesis, @Cast("unsigned char *") byte[] compressed, int nbCompressedBytes); 58 | public static native void celt_encoder_destroy(@Cast("CELTEncoder *") Pointer state); 59 | 60 | /** 61 | * @return an integer describing the CELT bitstream version. 62 | */ 63 | public static int getBitstreamVersion() { 64 | IntPointer versionPtr = new IntPointer(); 65 | Pointer modePtr = celt_mode_create(AudioHandler.SAMPLE_RATE, AudioHandler.FRAME_SIZE, null); 66 | celt_mode_info(modePtr, CELT_GET_BITSTREAM_VERSION, versionPtr); 67 | celt_mode_destroy(modePtr); 68 | return versionPtr.get(); 69 | } 70 | 71 | public static class CELT7Decoder implements IDecoder { 72 | 73 | private Pointer mMode; 74 | private Pointer mState; 75 | 76 | public CELT7Decoder(int sampleRate, int frameSize, int channels) throws NativeAudioException { 77 | IntPointer error = new IntPointer(1); 78 | error.put(0); 79 | mMode = celt_mode_create(sampleRate, frameSize, error); 80 | if(error.get() < 0) throw new NativeAudioException("CELT 0.7.0 decoder initialization failed with error: "+error.get()); 81 | mState = celt_decoder_create(mMode, channels, error); 82 | if(error.get() < 0) throw new NativeAudioException("CELT 0.7.0 decoder initialization failed with error: "+error.get()); 83 | } 84 | 85 | @Override 86 | public int decodeFloat(ByteBuffer input, int inputSize, float[] output, int frameSize) throws NativeAudioException { 87 | int result = celt_decode_float(mState, input, inputSize, output); 88 | if(result < 0) throw new NativeAudioException("CELT 0.7.0 decoding failed with error: "+result); 89 | return frameSize; 90 | } 91 | 92 | @Override 93 | public int decodeShort(ByteBuffer input, int inputSize, short[] output, int frameSize) throws NativeAudioException { 94 | int result = celt_decode(mState, input, inputSize, output); 95 | if(result < 0) throw new NativeAudioException("CELT 0.7.0 decoding failed with error: "+result); 96 | return frameSize; 97 | } 98 | 99 | @Override 100 | public void destroy() { 101 | celt_decoder_destroy(mState); 102 | celt_mode_destroy(mMode); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/jni/celt-0.7.0-build/config.h: -------------------------------------------------------------------------------- 1 | /* config.h. Generated from config.h.in by configure. */ 2 | /* config.h.in. Generated from configure.ac by autoheader. */ 3 | 4 | /* Define if building universal (internal helper macro) */ 5 | /* #undef AC_APPLE_UNIVERSAL_BUILD */ 6 | 7 | /* This is a build of CELT */ 8 | #define CELT_BUILD /**/ 9 | 10 | /* Version extra */ 11 | #define CELT_EXTRA_VERSION "" 12 | 13 | /* Version major */ 14 | #define CELT_MAJOR_VERSION 0 15 | 16 | /* Version micro */ 17 | #define CELT_MICRO_VERSION 0 18 | 19 | /* Version minor */ 20 | #define CELT_MINOR_VERSION 7 21 | 22 | /* Complete version string */ 23 | #define CELT_VERSION "0.7.0" 24 | 25 | /* Compile as fixed-point */ 26 | #define DOUBLE_PRECISION 27 | 28 | /* Assertions */ 29 | /* #undef ENABLE_ASSERTIONS */ 30 | 31 | /* Debug fixed-point implementation */ 32 | /* #undef FIXED_DEBUG */ 33 | 34 | /* Compile as fixed-point */ 35 | #define FIXED_POINT 36 | 37 | /* Compile as floating-point */ 38 | /* #define FLOATING_POINT */ 39 | 40 | /* Define to 1 if you have the header file. */ 41 | #define HAVE_ALLOCA_H 1 42 | 43 | /* Define to 1 if you have the header file. */ 44 | #define HAVE_DLFCN_H 1 45 | 46 | /* Define to 1 if you have the header file. */ 47 | #define HAVE_GETOPT_H 1 48 | 49 | /* Define to 1 if you have the `getopt_long' function. */ 50 | #define HAVE_GETOPT_LONG 1 51 | 52 | /* Define to 1 if you have the header file. */ 53 | #define HAVE_INTTYPES_H 1 54 | 55 | /* Define to 1 if you have the `m' library (-lm). */ 56 | #define HAVE_LIBM 1 57 | 58 | /* Define to 1 if you have the `winmm' library (-lwinmm). */ 59 | /* #undef HAVE_LIBWINMM */ 60 | 61 | /* Define if you have C99's lrint function. */ 62 | #define HAVE_LRINT 1 63 | 64 | /* Define if you have C99's lrintf function. */ 65 | #define HAVE_LRINTF 1 66 | 67 | /* Define to 1 if you have the header file. */ 68 | #define HAVE_MEMORY_H 1 69 | 70 | /* Define to 1 if you have the header file. */ 71 | #define HAVE_STDINT_H 1 72 | 73 | /* Define to 1 if you have the header file. */ 74 | #define HAVE_STDLIB_H 1 75 | 76 | /* Define to 1 if you have the header file. */ 77 | #define HAVE_STRINGS_H 1 78 | 79 | /* Define to 1 if you have the header file. */ 80 | #define HAVE_STRING_H 1 81 | 82 | /* Define to 1 if you have the header file. */ 83 | /* #undef HAVE_SYS_AUDIOIO_H */ 84 | 85 | /* Define to 1 if you have the header file. */ 86 | #define HAVE_SYS_SOUNDCARD_H 1 87 | 88 | /* Define to 1 if you have the header file. */ 89 | #define HAVE_SYS_STAT_H 1 90 | 91 | /* Define to 1 if you have the header file. */ 92 | #define HAVE_SYS_TYPES_H 1 93 | 94 | /* Define to 1 if you have the header file. */ 95 | #define HAVE_UNISTD_H 1 96 | 97 | /* Define to the sub-directory in which libtool stores uninstalled 98 | libraries. 99 | */ 100 | #define LT_OBJDIR ".libs/" 101 | 102 | /* Compile as fixed-point */ 103 | /* #undef MIXED_PRECISION */ 104 | 105 | /* Define to the address where bug reports for this package should be 106 | sent. */ 107 | #define PACKAGE_BUGREPORT "" 108 | 109 | /* Define to the full name of this package. */ 110 | #define PACKAGE_NAME "" 111 | 112 | /* Define to the full name and version of this package. */ 113 | #define PACKAGE_STRING "" 114 | 115 | /* Define to the one symbol short name of this package. */ 116 | #define PACKAGE_TARNAME "" 117 | 118 | /* Define to the version of this package. */ 119 | #define PACKAGE_VERSION "" 120 | 121 | /* The size of `int', as computed by sizeof. */ 122 | #define SIZEOF_INT 4 123 | 124 | /* The size of `long', as computed by sizeof. */ 125 | #define SIZEOF_LONG 8 126 | 127 | /* The size of `long long', as computed by sizeof. */ 128 | #define SIZEOF_LONG_LONG 8 129 | 130 | /* The size of `short', as computed by sizeof. */ 131 | #define SIZEOF_SHORT 2 132 | 133 | /* Static modes */ 134 | /* #undef STATIC_MODES */ 135 | 136 | /* Define to 1 if you have the ANSI C header files. */ 137 | #define STDC_HEADERS 1 138 | 139 | /* Make use of alloca */ 140 | /* #undef USE_ALLOCA */ 141 | 142 | /* Use C99 variable-size arrays */ 143 | #define VAR_ARRAYS /**/ 144 | 145 | /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the 146 | most 147 | significant byte first (like Motorola and SPARC, unlike Intel). */ 148 | #if defined AC_APPLE_UNIVERSAL_BUILD 149 | # if defined __BIG_ENDIAN__ 150 | # define WORDS_BIGENDIAN 1 151 | # endif 152 | #else 153 | # ifndef WORDS_BIGENDIAN 154 | /* # undef WORDS_BIGENDIAN */ 155 | # endif 156 | #endif 157 | 158 | /* Define to empty if `const' does not conform to ANSI C. */ 159 | /* #undef const */ 160 | 161 | /* Define to `__inline__' or `__inline' if that's what the C compiler 162 | calls it, or to nothing if 'inline' is not supported under any name. 163 | */ 164 | #ifndef __cplusplus 165 | /* #undef inline */ 166 | #endif 167 | 168 | /* Define to the equivalent of the C99 'restrict' keyword, or to 169 | nothing if this is not supported. Do not define if restrict is 170 | supported directly. */ 171 | #define restrict __restrict 172 | /* Work around a bug in Sun C++: it does not support _Restrict, even 173 | though the corresponding Sun C compiler does, which causes 174 | "#define restrict _Restrict" in the previous line. Perhaps some 175 | future 176 | version of Sun C++ will work with _Restrict; if so, it'll probably 177 | define __RESTRICT, just as Sun C does. */ 178 | #if defined __SUNPRO_CC && !defined __RESTRICT 179 | # define _Restrict 180 | #endif 181 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/encoder/OpusEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.encoder; 19 | 20 | import com.googlecode.javacpp.IntPointer; 21 | import com.googlecode.javacpp.Pointer; 22 | import com.morlunk.jumble.audio.javacpp.Opus; 23 | import com.morlunk.jumble.exception.NativeAudioException; 24 | import com.morlunk.jumble.net.PacketBuffer; 25 | 26 | import java.nio.BufferOverflowException; 27 | import java.nio.BufferUnderflowException; 28 | import java.util.Arrays; 29 | 30 | /** 31 | * Created by andrew on 08/12/14. 32 | */ 33 | public class OpusEncoder implements IEncoder { 34 | private final byte[] mBuffer; 35 | private final short[] mAudioBuffer; 36 | private final int mFramesPerPacket; 37 | private final int mFrameSize; 38 | 39 | // Stateful 40 | private int mBufferedFrames; 41 | private int mEncodedLength; 42 | private boolean mTerminated; 43 | 44 | private Pointer mState; 45 | 46 | public OpusEncoder(int sampleRate, int channels, int frameSize, int framesPerPacket, 47 | int bitrate, int maxBufferSize) throws NativeAudioException { 48 | mBuffer = new byte[maxBufferSize]; 49 | mAudioBuffer = new short[framesPerPacket * frameSize]; 50 | mFramesPerPacket = framesPerPacket; 51 | mFrameSize = frameSize; 52 | mBufferedFrames = 0; 53 | mEncodedLength = 0; 54 | mTerminated = false; 55 | 56 | IntPointer error = new IntPointer(1); 57 | error.put(0); 58 | mState = Opus.opus_encoder_create(sampleRate, channels, Opus.OPUS_APPLICATION_VOIP, error); 59 | if(error.get() < 0) throw new NativeAudioException("Opus encoder initialization failed with error: "+error.get()); 60 | Opus.opus_encoder_ctl(mState, Opus.OPUS_SET_VBR_REQUEST, 0); 61 | Opus.opus_encoder_ctl(mState, Opus.OPUS_SET_BITRATE_REQUEST, bitrate); 62 | } 63 | 64 | @Override 65 | public int encode(short[] input, int inputSize) throws NativeAudioException { 66 | if (mBufferedFrames >= mFramesPerPacket) { 67 | throw new BufferOverflowException(); 68 | } 69 | 70 | if (inputSize != mFrameSize) { 71 | throw new IllegalArgumentException("This Opus encoder implementation requires a " + 72 | "constant frame size."); 73 | } 74 | 75 | mTerminated = false; 76 | System.arraycopy(input, 0, mAudioBuffer, mFrameSize * mBufferedFrames, mFrameSize); 77 | mBufferedFrames++; 78 | 79 | if (mBufferedFrames == mFramesPerPacket) { 80 | return encode(); 81 | } 82 | return 0; 83 | } 84 | 85 | private int encode() throws NativeAudioException { 86 | if (mBufferedFrames < mFramesPerPacket) { 87 | // If encoding is done before enough frames are buffered, fill rest of packet. 88 | Arrays.fill(mAudioBuffer, mFrameSize * mBufferedFrames, mAudioBuffer.length, (short)0); 89 | mBufferedFrames = mFramesPerPacket; 90 | } 91 | int result = Opus.opus_encode(mState, mAudioBuffer, mFrameSize * mBufferedFrames, 92 | mBuffer, mBuffer.length); 93 | if(result < 0) throw new NativeAudioException("Opus encoding failed with error: " 94 | + result); 95 | mEncodedLength = result; 96 | return result; 97 | } 98 | 99 | @Override 100 | public int getBufferedFrames() { 101 | return mBufferedFrames; 102 | } 103 | 104 | @Override 105 | public boolean isReady() { 106 | return mEncodedLength > 0; 107 | } 108 | 109 | @Override 110 | public void getEncodedData(PacketBuffer packetBuffer) throws BufferUnderflowException { 111 | if (!isReady()) { 112 | throw new BufferUnderflowException(); 113 | } 114 | 115 | int size = mEncodedLength; 116 | if(mTerminated) 117 | size |= 1 << 13; 118 | packetBuffer.writeLong(size); 119 | packetBuffer.append(mBuffer, mEncodedLength); 120 | 121 | mBufferedFrames = 0; 122 | mEncodedLength = 0; 123 | mTerminated = false; 124 | } 125 | 126 | @Override 127 | public void terminate() throws NativeAudioException { 128 | mTerminated = true; 129 | if (mBufferedFrames > 0 && !isReady()) { 130 | // Perform encode operation on remaining audio if available. 131 | encode(); 132 | } 133 | } 134 | 135 | public int getBitrate() { 136 | IntPointer ptr = new IntPointer(1); 137 | Opus.opus_encoder_ctl(mState, Opus.OPUS_GET_BITRATE_REQUEST, ptr); 138 | return ptr.get(); 139 | } 140 | 141 | @Override 142 | public void destroy() { 143 | Opus.opus_encoder_destroy(mState); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/audio/javacpp/Opus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.audio.javacpp; 19 | 20 | import com.googlecode.javacpp.IntPointer; 21 | import com.googlecode.javacpp.Loader; 22 | import com.googlecode.javacpp.Pointer; 23 | import com.googlecode.javacpp.annotation.Cast; 24 | import com.googlecode.javacpp.annotation.Platform; 25 | import com.morlunk.jumble.audio.IDecoder; 26 | import com.morlunk.jumble.exception.NativeAudioException; 27 | 28 | import java.nio.ByteBuffer; 29 | 30 | /** 31 | * Created by andrew on 18/10/13. 32 | */ 33 | 34 | @Platform(library= "jniopus", cinclude={"",""}) 35 | public class Opus { 36 | public static final int OPUS_APPLICATION_VOIP = 2048; 37 | 38 | public static final int OPUS_SET_BITRATE_REQUEST = 4002; 39 | public static final int OPUS_GET_BITRATE_REQUEST = 4003; 40 | public static final int OPUS_SET_VBR_REQUEST = 4006; 41 | 42 | public static native int opus_decoder_get_size(int channels); 43 | public static native Pointer opus_decoder_create(int fs, int channels, IntPointer error); 44 | public static native int opus_decoder_init(@Cast("OpusDecoder*") Pointer st, int fs, int channels); 45 | public static native int opus_decode(@Cast("OpusDecoder*") Pointer st, @Cast("const unsigned char*") ByteBuffer data, int len, short[] out, int frameSize, int decodeFec); 46 | public static native int opus_decode_float(@Cast("OpusDecoder*") Pointer st, @Cast("const unsigned char*") ByteBuffer data, int len, float[] out, int frameSize, int decodeFec); 47 | //public static native int opus_decoder_ctl(@Cast("OpusDecoder*") Pointer st, int request); 48 | public static native void opus_decoder_destroy(@Cast("OpusDecoder*") Pointer st); 49 | //public static native int opus_packet_parse(@Cast("const unsigned char*") BytePointer data, int len, ... 50 | public static native int opus_packet_get_bandwidth(@Cast("const unsigned char*") byte[] data); 51 | public static native int opus_packet_get_samples_per_frame(@Cast("const unsigned char*") byte[] data, int fs); 52 | public static native int opus_packet_get_nb_channels(@Cast("const unsigned char*") byte[] data); 53 | public static native int opus_packet_get_nb_frames(@Cast("const unsigned char*") byte[] packet, int len); 54 | public static native int opus_packet_get_nb_samples(@Cast("const unsigned char*") byte[] packet, int len, int fs); 55 | 56 | 57 | public static native int opus_encoder_get_size(int channels); 58 | public static native Pointer opus_encoder_create(int fs, int channels, int application, IntPointer error); 59 | public static native int opus_encoder_init(@Cast("OpusEncoder*") Pointer st, int fs, int channels, int application); 60 | public static native int opus_encode(@Cast("OpusEncoder*") Pointer st, @Cast("const short*") short[] pcm, int frameSize, @Cast("unsigned char*") byte[] data, int maxDataBytes); 61 | public static native int opus_encode_float(@Cast("OpusEncoder*") Pointer st, @Cast("const float*") float[] pcm, int frameSize, @Cast("unsigned char*") byte[] data, int maxDataBytes); 62 | public static native void opus_encoder_destroy(@Cast("OpusEncoder*") Pointer st); 63 | public static native int opus_encoder_ctl(@Cast("OpusEncoder*") Pointer st, int request, Pointer value); 64 | public static native int opus_encoder_ctl(@Cast("OpusEncoder*") Pointer st, int request, @Cast("opus_int32") int value); 65 | 66 | static { 67 | Loader.load(); 68 | } 69 | 70 | public static class OpusDecoder implements IDecoder { 71 | 72 | private Pointer mState; 73 | 74 | public OpusDecoder(int sampleRate, int channels) throws NativeAudioException { 75 | IntPointer error = new IntPointer(1); 76 | error.put(0); 77 | mState = opus_decoder_create(sampleRate, channels, error); 78 | if(error.get() < 0) throw new NativeAudioException("Opus decoder initialization failed with error: "+error.get()); 79 | } 80 | 81 | @Override 82 | public int decodeFloat(ByteBuffer input, int inputSize, float[] output, int frameSize) throws NativeAudioException { 83 | int result = opus_decode_float(mState, input, inputSize, output, frameSize, 0); 84 | if(result < 0) throw new NativeAudioException("Opus decoding failed with error: "+result); 85 | return result; 86 | } 87 | 88 | @Override 89 | public int decodeShort(ByteBuffer input, int inputSize, short[] output, int frameSize) throws NativeAudioException { 90 | int result = opus_decode(mState, input, inputSize, output, frameSize, 0); 91 | if(result < 0) throw new NativeAudioException("Opus decoding failed with error: "+result); 92 | return result; 93 | } 94 | 95 | @Override 96 | public void destroy() { 97 | opus_decoder_destroy(mState); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/jni/celt-0.11.0-build/config.h: -------------------------------------------------------------------------------- 1 | /* config.h. Generated from config.h.in by configure. */ 2 | /* config.h.in. Generated from configure.ac by autoheader. */ 3 | 4 | /* Define if building universal (internal helper macro) */ 5 | /* #undef AC_APPLE_UNIVERSAL_BUILD */ 6 | 7 | /* This is a build of CELT */ 8 | #define CELT_BUILD /**/ 9 | 10 | /* Version extra */ 11 | #define CELT_EXTRA_VERSION "" 12 | 13 | /* Version major */ 14 | #define CELT_MAJOR_VERSION 0 15 | 16 | /* Version micro */ 17 | #define CELT_MICRO_VERSION 0 18 | 19 | /* Version minor */ 20 | #define CELT_MINOR_VERSION 11 21 | 22 | /* Complete version string */ 23 | #define CELT_VERSION "0.11.0" 24 | 25 | /* Compile as fixed-point */ 26 | #define DOUBLE_PRECISION 27 | 28 | /* Assertions */ 29 | /* #undef ENABLE_ASSERTIONS */ 30 | 31 | /* Postfilter */ 32 | /* #undef ENABLE_POSTFILTER */ 33 | 34 | /* Debug fixed-point implementation */ 35 | /* #undef FIXED_DEBUG */ 36 | 37 | /* Compile as fixed-point */ 38 | #define FIXED_POINT 39 | 40 | /* Compile as floating-point */ 41 | /* #define FLOATING_POINT */ 42 | 43 | /* Float approximations */ 44 | /* #undef FLOAT_APPROX */ 45 | 46 | /* Define to 1 if you have the header file. */ 47 | #define HAVE_ALLOCA_H 1 48 | 49 | /* Define to 1 if you have the header file. */ 50 | #define HAVE_DLFCN_H 1 51 | 52 | /* Define to 1 if you have the header file. */ 53 | #define HAVE_GETOPT_H 1 54 | 55 | /* Define to 1 if you have the `getopt_long' function. */ 56 | #define HAVE_GETOPT_LONG 1 57 | 58 | /* Define to 1 if you have the header file. */ 59 | #define HAVE_INTTYPES_H 1 60 | 61 | /* Define to 1 if you have the `m' library (-lm). */ 62 | #define HAVE_LIBM 1 63 | 64 | /* Define to 1 if you have the `winmm' library (-lwinmm). */ 65 | /* #undef HAVE_LIBWINMM */ 66 | 67 | /* Define if you have C99's lrint function. */ 68 | #define HAVE_LRINT 1 69 | 70 | /* Define if you have C99's lrintf function. */ 71 | #define HAVE_LRINTF 1 72 | 73 | /* Define to 1 if you have the header file. */ 74 | #define HAVE_MEMORY_H 1 75 | 76 | /* Define to 1 if you have the header file. */ 77 | #define HAVE_STDINT_H 1 78 | 79 | /* Define to 1 if you have the header file. */ 80 | #define HAVE_STDLIB_H 1 81 | 82 | /* Define to 1 if you have the header file. */ 83 | #define HAVE_STRINGS_H 1 84 | 85 | /* Define to 1 if you have the header file. */ 86 | #define HAVE_STRING_H 1 87 | 88 | /* Define to 1 if you have the header file. */ 89 | /* #undef HAVE_SYS_AUDIOIO_H */ 90 | 91 | /* Define to 1 if you have the header file. */ 92 | #define HAVE_SYS_SOUNDCARD_H 1 93 | 94 | /* Define to 1 if you have the header file. */ 95 | #define HAVE_SYS_STAT_H 1 96 | 97 | /* Define to 1 if you have the header file. */ 98 | #define HAVE_SYS_TYPES_H 1 99 | 100 | /* Define to 1 if you have the header file. */ 101 | #define HAVE_UNISTD_H 1 102 | 103 | /* Define to the sub-directory in which libtool stores uninstalled libraries. 104 | * */ 105 | #define LT_OBJDIR ".libs/" 106 | 107 | /* Compile as fixed-point */ 108 | /* #undef MIXED_PRECISION */ 109 | 110 | /* Define to the address where bug reports for this package should be sent. */ 111 | #define PACKAGE_BUGREPORT "" 112 | 113 | /* Define to the full name of this package. */ 114 | #define PACKAGE_NAME "" 115 | 116 | /* Define to the full name and version of this package. */ 117 | #define PACKAGE_STRING "" 118 | 119 | /* Define to the one symbol short name of this package. */ 120 | #define PACKAGE_TARNAME "" 121 | 122 | /* Define to the version of this package. */ 123 | #define PACKAGE_VERSION "" 124 | 125 | /* The size of `int', as computed by sizeof. */ 126 | #define SIZEOF_INT 4 127 | 128 | /* The size of `long', as computed by sizeof. */ 129 | #define SIZEOF_LONG 8 130 | 131 | /* The size of `long long', as computed by sizeof. */ 132 | #define SIZEOF_LONG_LONG 8 133 | 134 | /* The size of `short', as computed by sizeof. */ 135 | #define SIZEOF_SHORT 2 136 | 137 | /* Custom modes */ 138 | /* #undef CUSTOM_MODES */ 139 | 140 | /* Define to 1 if you have the ANSI C header files. */ 141 | #define STDC_HEADERS 1 142 | 143 | /* Make use of alloca */ 144 | /* #undef USE_ALLOCA */ 145 | 146 | /* Use C99 variable-size arrays */ 147 | #define VAR_ARRAYS /**/ 148 | 149 | /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most 150 | * significant byte first (like Motorola and SPARC, unlike Intel). */ 151 | #if defined AC_APPLE_UNIVERSAL_BUILD 152 | # if defined __BIG_ENDIAN__ 153 | # define WORDS_BIGENDIAN 1 154 | # endif 155 | #else 156 | # ifndef WORDS_BIGENDIAN 157 | /* # undef WORDS_BIGENDIAN */ 158 | # endif 159 | #endif 160 | 161 | /* Define to empty if `const' does not conform to ANSI C. */ 162 | /* #undef const */ 163 | 164 | /* Define to `__inline__' or `__inline' if that's what the C compiler 165 | * calls it, or to nothing if 'inline' is not supported under any name. */ 166 | #ifndef __cplusplus 167 | /* #undef inline */ 168 | #endif 169 | 170 | /* Define to the equivalent of the C99 'restrict' keyword, or to 171 | * nothing if this is not supported. Do not define if restrict is 172 | * supported directly. */ 173 | #define restrict __restrict 174 | /* Work around a bug in Sun C++: it does not support _Restrict, even 175 | * though the corresponding Sun C compiler does, which causes 176 | * "#define restrict _Restrict" in the previous line. Perhaps some future 177 | * version of Sun C++ will work with _Restrict; if so, it'll probably 178 | * define __RESTRICT, just as Sun C does. */ 179 | #if defined __SUNPRO_CC && !defined __RESTRICT 180 | # define _Restrict 181 | #endif 182 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/util/JumbleCallbacks.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.util; 19 | 20 | import com.morlunk.jumble.model.IChannel; 21 | import com.morlunk.jumble.model.IMessage; 22 | import com.morlunk.jumble.model.IUser; 23 | 24 | import org.spongycastle.jcajce.provider.asymmetric.X509; 25 | 26 | import java.security.cert.X509Certificate; 27 | import java.util.Collections; 28 | import java.util.HashSet; 29 | import java.util.Set; 30 | import java.util.concurrent.ConcurrentHashMap; 31 | 32 | /** 33 | * A composite wrapper around Jumble observers to easily broadcast to each observer. 34 | * Created by andrew on 12/07/14. 35 | */ 36 | public class JumbleCallbacks implements IJumbleObserver { 37 | private final Set mCallbacks; 38 | 39 | public JumbleCallbacks() { 40 | mCallbacks = Collections.newSetFromMap(new ConcurrentHashMap()); 41 | } 42 | 43 | public void registerObserver(IJumbleObserver observer) { 44 | mCallbacks.add(observer); 45 | } 46 | 47 | public void unregisterObserver(IJumbleObserver observer) { 48 | mCallbacks.remove(observer); 49 | } 50 | 51 | @Override 52 | public void onConnected() { 53 | for (IJumbleObserver observer : mCallbacks) { 54 | observer.onConnected(); 55 | } 56 | } 57 | 58 | @Override 59 | public void onConnecting() { 60 | for (IJumbleObserver observer : mCallbacks) { 61 | observer.onConnecting(); 62 | } 63 | } 64 | 65 | @Override 66 | public void onDisconnected(JumbleException e) { 67 | for (IJumbleObserver observer : mCallbacks) { 68 | observer.onDisconnected(e); 69 | } 70 | } 71 | 72 | @Override 73 | public void onTLSHandshakeFailed(X509Certificate[] chain) { 74 | for (IJumbleObserver observer : mCallbacks) { 75 | observer.onTLSHandshakeFailed(chain); 76 | } 77 | } 78 | 79 | @Override 80 | public void onChannelAdded(IChannel channel) { 81 | for (IJumbleObserver observer : mCallbacks) { 82 | observer.onChannelAdded(channel); 83 | } 84 | } 85 | 86 | @Override 87 | public void onChannelStateUpdated(IChannel channel) { 88 | for (IJumbleObserver observer : mCallbacks) { 89 | observer.onChannelStateUpdated(channel); 90 | } 91 | } 92 | 93 | @Override 94 | public void onChannelRemoved(IChannel channel) { 95 | for (IJumbleObserver observer : mCallbacks) { 96 | observer.onChannelRemoved(channel); 97 | } 98 | } 99 | 100 | @Override 101 | public void onChannelPermissionsUpdated(IChannel channel) { 102 | for (IJumbleObserver observer : mCallbacks) { 103 | observer.onChannelPermissionsUpdated(channel); 104 | } 105 | } 106 | 107 | @Override 108 | public void onUserConnected(IUser user) { 109 | for (IJumbleObserver observer : mCallbacks) { 110 | observer.onUserConnected(user); 111 | } 112 | } 113 | 114 | @Override 115 | public void onUserStateUpdated(IUser user) { 116 | for (IJumbleObserver observer : mCallbacks) { 117 | observer.onUserStateUpdated(user); 118 | } 119 | } 120 | 121 | @Override 122 | public void onUserTalkStateUpdated(IUser user) { 123 | for (IJumbleObserver observer : mCallbacks) { 124 | observer.onUserTalkStateUpdated(user); 125 | } 126 | } 127 | 128 | @Override 129 | public void onUserJoinedChannel(IUser user, IChannel newChannel, IChannel oldChannel) { 130 | for (IJumbleObserver observer : mCallbacks) { 131 | observer.onUserJoinedChannel(user, newChannel, oldChannel); 132 | } 133 | } 134 | 135 | @Override 136 | public void onUserRemoved(IUser user, String reason) { 137 | for (IJumbleObserver observer : mCallbacks) { 138 | observer.onUserRemoved(user, reason); 139 | } 140 | } 141 | 142 | @Override 143 | public void onPermissionDenied(String reason) { 144 | for (IJumbleObserver observer : mCallbacks) { 145 | observer.onPermissionDenied(reason); 146 | } 147 | } 148 | 149 | @Override 150 | public void onMessageLogged(IMessage message) { 151 | for (IJumbleObserver observer : mCallbacks) { 152 | observer.onMessageLogged(message); 153 | } 154 | } 155 | 156 | @Override 157 | public void onVoiceTargetChanged(VoiceTargetMode mode) { 158 | for (IJumbleObserver observer : mCallbacks) { 159 | observer.onVoiceTargetChanged(mode); 160 | } 161 | } 162 | 163 | @Override 164 | public void onLogInfo(String message) { 165 | for (IJumbleObserver observer : mCallbacks) { 166 | observer.onLogInfo(message); 167 | } 168 | } 169 | 170 | @Override 171 | public void onLogWarning(String message) { 172 | for (IJumbleObserver observer : mCallbacks) { 173 | observer.onLogWarning(message); 174 | } 175 | } 176 | 177 | @Override 178 | public void onLogError(String message) { 179 | for (IJumbleObserver observer : mCallbacks) { 180 | observer.onLogError(message); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/main/java/com/morlunk/jumble/net/JumbleSSLSocketFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Andrew Comminos 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package com.morlunk.jumble.net; 19 | 20 | import android.util.Log; 21 | 22 | import com.morlunk.jumble.Constants; 23 | 24 | import java.io.FileInputStream; 25 | import java.io.IOException; 26 | import java.net.InetAddress; 27 | import java.net.InetSocketAddress; 28 | import java.net.Proxy; 29 | import java.net.Socket; 30 | import java.security.KeyManagementException; 31 | import java.security.KeyStore; 32 | import java.security.KeyStoreException; 33 | import java.security.NoSuchAlgorithmException; 34 | import java.security.NoSuchProviderException; 35 | import java.security.UnrecoverableKeyException; 36 | import java.security.cert.CertificateException; 37 | import java.security.cert.X509Certificate; 38 | 39 | import javax.net.ssl.KeyManagerFactory; 40 | import javax.net.ssl.SSLContext; 41 | import javax.net.ssl.SSLSocket; 42 | import javax.net.ssl.TrustManager; 43 | import javax.net.ssl.TrustManagerFactory; 44 | import javax.net.ssl.X509TrustManager; 45 | 46 | public class JumbleSSLSocketFactory { 47 | private SSLContext mContext; 48 | private JumbleTrustManagerWrapper mTrustWrapper; 49 | 50 | public JumbleSSLSocketFactory(KeyStore keystore, String keystorePassword, String trustStorePath, String trustStorePassword, String trustStoreFormat) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException, NoSuchProviderException, IOException, CertificateException { 51 | mContext = SSLContext.getInstance("TLS"); 52 | 53 | KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509"); 54 | kmf.init(keystore, keystorePassword != null ? keystorePassword.toCharArray() : new char[0]); 55 | 56 | if(trustStorePath != null) { 57 | KeyStore trustStore = KeyStore.getInstance(trustStoreFormat); 58 | FileInputStream fis = new FileInputStream(trustStorePath); 59 | trustStore.load(fis, trustStorePassword.toCharArray()); 60 | 61 | TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 62 | tmf.init(trustStore); 63 | mTrustWrapper = new JumbleTrustManagerWrapper((X509TrustManager) tmf.getTrustManagers()[0]); 64 | Log.i(Constants.TAG, "Using custom trust store " + trustStorePath + " with system trust store"); 65 | } else { 66 | mTrustWrapper = new JumbleTrustManagerWrapper(null); 67 | Log.i(Constants.TAG, "Using system trust store"); 68 | } 69 | 70 | mContext.init(kmf.getKeyManagers(), new TrustManager[] { mTrustWrapper }, null); 71 | } 72 | 73 | /** 74 | * Creates a new SSLSocket that runs through a SOCKS5 proxy to reach its destination. 75 | */ 76 | public SSLSocket createTorSocket(InetAddress host, int port, String proxyHost, int proxyPort) throws IOException { 77 | Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress(proxyHost, proxyPort)); 78 | Socket socket = new Socket(proxy); 79 | socket.connect(new InetSocketAddress(host, port)); 80 | return (SSLSocket) mContext.getSocketFactory().createSocket(socket, host.getHostName(), port, true); 81 | } 82 | 83 | public SSLSocket createSocket(InetAddress host, int port) throws IOException { 84 | return (SSLSocket) mContext.getSocketFactory().createSocket(host, port); 85 | } 86 | 87 | /** 88 | * Gets the certificate chain of the remote host. 89 | * @return The remote server's certificate chain, or null if a connection has not reached handshake yet. 90 | */ 91 | public X509Certificate[] getServerChain() { 92 | return mTrustWrapper.getServerChain(); 93 | } 94 | 95 | /** 96 | * Wraps around a custom trust manager and stores the certificate chains that did not validate. 97 | * We can then send the chain to the user for manual validation. 98 | */ 99 | private static class JumbleTrustManagerWrapper implements X509TrustManager { 100 | 101 | private X509TrustManager mDefaultTrustManager; 102 | private X509TrustManager mTrustManager; 103 | private X509Certificate[] mServerChain; 104 | 105 | public JumbleTrustManagerWrapper(X509TrustManager trustManager) throws NoSuchAlgorithmException, KeyStoreException { 106 | TrustManagerFactory dmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 107 | dmf.init((KeyStore) null); 108 | mDefaultTrustManager = (X509TrustManager) dmf.getTrustManagers()[0]; 109 | mTrustManager = trustManager; 110 | } 111 | 112 | @Override 113 | public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 114 | try { 115 | mDefaultTrustManager.checkClientTrusted(chain, authType); 116 | } catch (CertificateException e) { 117 | if(mTrustManager != null) mTrustManager.checkClientTrusted(chain, authType); 118 | else throw e; 119 | } 120 | } 121 | 122 | @Override 123 | public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 124 | mServerChain = chain; 125 | try { 126 | mDefaultTrustManager.checkServerTrusted(chain, authType); 127 | } catch (CertificateException e) { 128 | if(mTrustManager != null) mTrustManager.checkServerTrusted(chain, authType); 129 | else throw e; 130 | } 131 | } 132 | 133 | @Override 134 | public X509Certificate[] getAcceptedIssuers() { 135 | return mDefaultTrustManager.getAcceptedIssuers(); 136 | } 137 | 138 | public X509Certificate[] getServerChain() { 139 | return mServerChain; 140 | } 141 | } 142 | } --------------------------------------------------------------------------------