diff options
author | Andrew Comminos <andrewcomminos@gmail.com> | 2013-11-09 04:55:32 +0400 |
---|---|---|
committer | Andrew Comminos <andrewcomminos@gmail.com> | 2013-11-09 04:55:32 +0400 |
commit | fde2187b916986e36322e1843fde82e9df56d4c6 (patch) | |
tree | 9cb7950c04b700e54a5e2ccf62ab3aa3a0ae4cf1 /src/main/java | |
parent | 58e6f022f7256a27dada42c0ceb664815a6538fb (diff) |
Implemented CELT Alpha and Beta output, much better Opus input, Speex
resampling.
Diffstat (limited to 'src/main/java')
8 files changed, 161 insertions, 93 deletions
diff --git a/src/main/java/com/morlunk/jumble/Constants.java b/src/main/java/com/morlunk/jumble/Constants.java index 67dcaa6..b2190ab 100644 --- a/src/main/java/com/morlunk/jumble/Constants.java +++ b/src/main/java/com/morlunk/jumble/Constants.java @@ -16,31 +16,34 @@ package com.morlunk.jumble; +import com.googlecode.javacpp.IntPointer; +import com.googlecode.javacpp.Loader; +import com.googlecode.javacpp.Pointer; import com.morlunk.jumble.audio.Audio; +import com.morlunk.jumble.audio.javacpp.CELT11; +import com.morlunk.jumble.audio.javacpp.CELT7; public class Constants { - /** Set dynamically by JNI calls. */ public static int CELT_11_VERSION ; /** Set dynamically by JNI calls. */ public static int CELT_7_VERSION; static { - // Load CELT bitstream versions from JNI. TODO clean me with renamed types for version -// int[] error = new int[1]; -// com.morlunk.jumble.audio.celt11.SWIGTYPE_p_CELTMode celt11Mode = CELT11.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, error); -// com.morlunk.jumble.audio.celt7.SWIGTYPE_p_CELTMode celt7Mode = CELT7.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, error); -// -// int[] celt11Version = new int[1]; -// int[] celt7Version = new int[1]; -// -// CELT11.celt_mode_info(celt11Mode, CELT11Constants.CELT_GET_BITSTREAM_VERSION, celt11Version); -// CELT7.celt_mode_info(celt7Mode, CELT7Constants.CELT_GET_BITSTREAM_VERSION, celt7Version); -// CELT11.celt_mode_destroy(celt11Mode); -// CELT7.celt_mode_destroy(celt7Mode); -// -// CELT_11_VERSION = celt11Version[0]; -// CELT_7_VERSION = celt7Version[0]; + // Load CELT bitstream versions from JNI. + Pointer celt11Mode = CELT11.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, null); + Pointer celt7Mode = CELT7.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, null); + + IntPointer celt11Version = new IntPointer(); + IntPointer celt7Version = new IntPointer(); + + CELT11.celt_mode_info(celt11Mode, CELT11.CELT_GET_BITSTREAM_VERSION, celt11Version); + CELT7.celt_mode_info(celt7Mode, CELT7.CELT_GET_BITSTREAM_VERSION, celt7Version); + CELT11.celt_mode_destroy(celt11Mode); + CELT7.celt_mode_destroy(celt7Mode); + + CELT_11_VERSION = celt11Version.get(); + CELT_7_VERSION = celt7Version.get(); } public static final int PROTOCOL_MAJOR = 1; diff --git a/src/main/java/com/morlunk/jumble/JumbleService.java b/src/main/java/com/morlunk/jumble/JumbleService.java index aba134e..16e867b 100644 --- a/src/main/java/com/morlunk/jumble/JumbleService.java +++ b/src/main/java/com/morlunk/jumble/JumbleService.java @@ -243,12 +243,12 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon @Override public void requestBanList() throws RemoteException { - + // TODO } @Override public void requestUserList() throws RemoteException { - + // TODO } @Override @@ -605,7 +605,6 @@ public class JumbleService extends Service implements JumbleConnection.JumbleCon runnable.run(mObservers.getBroadcastItem(i)); } catch (RemoteException e) { e.printStackTrace(); - // TODO handle this error } } mObservers.finishBroadcast(); diff --git a/src/main/java/com/morlunk/jumble/audio/AudioInput.java b/src/main/java/com/morlunk/jumble/audio/AudioInput.java index e9be7fb..d9aaebb 100644 --- a/src/main/java/com/morlunk/jumble/audio/AudioInput.java +++ b/src/main/java/com/morlunk/jumble/audio/AudioInput.java @@ -18,6 +18,7 @@ package com.morlunk.jumble.audio; import android.media.AudioFormat; import android.media.AudioRecord; +import android.media.AudioTrack; import android.media.MediaRecorder; import android.util.Log; @@ -47,6 +48,8 @@ public class AudioInput extends JumbleMessageHandler.Stub implements Runnable { public void onFrameEncoded(byte[] data, int length, JumbleUDPMessageType messageType); } + public static final int[] SAMPLE_RATES = { 48000, 44100, 22050, 160000, 11025, 8000 }; + public static final int SPEEX_RESAMPLE_QUALITY = 3; public static final int OPUS_MAX_BYTES = 512; // Opus specifies 4000 bytes as a recommended value for encoding, but the official mumble project uses 512. private Pointer mOpusEncoder;; @@ -54,15 +57,17 @@ public class AudioInput extends JumbleMessageHandler.Stub implements Runnable { // private com.morlunk.jumble.audio.celt7.SWIGTYPE_p_CELTMode mCELTAlphaMode; // private com.morlunk.jumble.audio.celt7.SWIGTYPE_p_CELTEncoder mCELTAlphaEncoder; private Speex.SpeexPreprocessState mPreprocessState; + private Speex.SpeexResampler mResampler; private AudioInputListener mListener; private int mTransmitMode = Constants.TRANSMIT_PUSH_TO_TALK; private AudioRecord mAudioRecord; private int mMinBufferSize; - private int mInputSampleRate = 48000; //44100; FIXME + private int mInputSampleRate = -1; private int mFrameSize = Audio.FRAME_SIZE; - private int mFramesPerPacket = 2; + private int mMicFrameSize = Audio.FRAME_SIZE; + private int mFramesPerPacket = 6; // Temporary encoder state private final short[] mOpusBuffer = new short[mFrameSize*mFramesPerPacket]; @@ -88,8 +93,24 @@ public class AudioInput extends JumbleMessageHandler.Stub implements Runnable { switchCodec(codec); configurePreprocessState(); - // TODO support input resampling. We can't expect 48000hz on all android devices. - int reportedMinBufferSize = AudioRecord.getMinBufferSize(Audio.SAMPLE_RATE, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); + // Use the highest sample rate we can get. TODO make this an option in settings. + for(int rate : SAMPLE_RATES) { + if(AudioRecord.getMinBufferSize(rate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT) > 0) { + mInputSampleRate = rate; + break; + } + } + if(mInputSampleRate != -1) { + Log.d(Constants.TAG, "Initialized AudioInput with sample rate "+mInputSampleRate); + if(mInputSampleRate != Audio.SAMPLE_RATE) { + mResampler = new Speex.SpeexResampler(1, mInputSampleRate, Audio.SAMPLE_RATE, SPEEX_RESAMPLE_QUALITY); + mMicFrameSize = (mFrameSize * mInputSampleRate)/Audio.SAMPLE_RATE; + } + } else { + throw new RuntimeException("Device does not support any compatible input sampling rates!"); + } + + int reportedMinBufferSize = AudioRecord.getMinBufferSize(mInputSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT); mMinBufferSize = Math.max(reportedMinBufferSize, mFrameSize); mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, mInputSampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, mMinBufferSize); } @@ -205,16 +226,22 @@ public class AudioInput extends JumbleMessageHandler.Stub implements Runnable { if(mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED) return; + final short[] audioData = new short[mFrameSize]; + final short[] resampleBuffer = new short[mMicFrameSize]; // We loop when the 'recording' instance var is true instead of checking audio record state because we want to always cleanly shutdown. while(mRecording) { - int shortsRead = mAudioRecord.read(audioData, 0, mFrameSize); + int shortsRead = mAudioRecord.read(mResampler != null ? resampleBuffer : audioData, 0, mResampler != null ? mMicFrameSize : mFrameSize); if(shortsRead > 0) { int len; boolean encoded = false; mFrameCounter++; + // Resample if necessary + if(mResampler != null) + mResampler.resample(resampleBuffer, audioData); + boolean talking = true; if(mTransmitMode == Constants.TRANSMIT_VOICE_ACTIVITY) { @@ -310,6 +337,8 @@ public class AudioInput extends JumbleMessageHandler.Stub implements Runnable { Opus.opus_encoder_destroy(mOpusEncoder); if(mPreprocessState != null) mPreprocessState.destroy(); + if(mResampler != null) + mResampler.destroy(); } @Override diff --git a/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java b/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java index 107ef7b..5d2c85e 100644 --- a/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java +++ b/src/main/java/com/morlunk/jumble/audio/AudioOutputSpeech.java @@ -21,6 +21,8 @@ import com.googlecode.javacpp.FloatPointer; import com.googlecode.javacpp.IntPointer; import com.googlecode.javacpp.Loader; import com.googlecode.javacpp.Pointer; +import com.morlunk.jumble.audio.javacpp.CELT11; +import com.morlunk.jumble.audio.javacpp.CELT7; import com.morlunk.jumble.audio.javacpp.Opus; import com.morlunk.jumble.audio.javacpp.Speex; import com.morlunk.jumble.model.User; @@ -40,14 +42,10 @@ public class AudioOutputSpeech { public void onTalkStateUpdated(int session, User.TalkState state); } - // Native audio pointers -// private com.morlunk.jumble.audio.celt7.SWIGTYPE_p_CELTDecoder mCELTAlphaDecoder; -// private com.morlunk.jumble.audio.celt7.SWIGTYPE_p_CELTMode mCELTAlphaMode; -// private com.morlunk.jumble.audio.celt11.SWIGTYPE_p_CELTDecoder mCELTBetaDecoder; -// private SWIGTYPE_p_void mSpeexDecoder; -// private SpeexBits mSpeexBits; - private Pointer mOpusDecoder; + private Pointer mCELTBetaDecoder; + private Pointer mCELTAlphaMode; + private Pointer mCELTAlphaDecoder; private Speex.JitterBuffer mJitterBuffer; private int mSession; @@ -58,11 +56,10 @@ public class AudioOutputSpeech { private float[] mBuffer; private Queue<BytePointer> mFrames = new ConcurrentLinkedQueue<BytePointer>(); private int mMissCount = 0; - private int mMissedFrames = 0; private float mAverageAvailable = 0; private boolean mHasTerminator = false; private boolean mLastAlive = true; - private int mBufferOffset, mBufferFilled, mLastConsume = 0; + private int mBufferFilled, mLastConsume = 0; private int ucFlags; private TalkStateListener mTalkStateListener; @@ -83,16 +80,16 @@ public class AudioOutputSpeech { // mSpeexDecoder = Speex.speex_decoder_init(Speex.getSpeex_uwb_mode()); // Speex.speex_decoder_ctl(mSpeexDecoder, SpeexConstants.SPEEX_SET_ENH, new int[] { 1 }); // break; -// case UDPVoiceCELTBeta: -// mCELTBetaDecoder = CELT11.celt_decoder_create(Audio.SAMPLE_RATE, 1, error); -// break; -// case UDPVoiceCELTAlpha: -// mCELTAlphaMode = CELT7.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, error); -// mCELTAlphaDecoder = CELT7.celt_decoder_create(mCELTAlphaMode, 1, error); -// break; + case UDPVoiceCELTBeta: + mCELTBetaDecoder = CELT11.celt_decoder_create(Audio.SAMPLE_RATE, 1, null); + break; + case UDPVoiceCELTAlpha: + mCELTAlphaMode = CELT7.celt_mode_create(Audio.SAMPLE_RATE, Audio.FRAME_SIZE, null); + mCELTAlphaDecoder = CELT7.celt_decoder_create(mCELTAlphaMode, 1, null); + break; } - mBuffer = new float[mAudioBufferSize*2]; // Leave room for an extra frame in the buffer- TODO fix. + mBuffer = new float[mAudioBufferSize*2]; // Leave room for an extra frame in the buffer, this saves buffer resize operations at runtime normally. mJitterBuffer = new Speex.JitterBuffer(Audio.FRAME_SIZE); IntPointer margin = new IntPointer(1); margin.put(10 * Audio.FRAME_SIZE); @@ -152,7 +149,7 @@ public class AudioOutputSpeech { while(mBufferFilled < num) { int decodedSamples = Audio.FRAME_SIZE; - //resizeBuffer(mBufferFilled + mAudioBufferSize); TODO reimplement resize + resizeBuffer(mBufferFilled + mAudioBufferSize); if(!mLastAlive) out.fill(0); @@ -236,16 +233,16 @@ public class AudioOutputSpeech { BytePointer data = mFrames.poll(); if(mCodec == JumbleUDPMessageType.UDPVoiceCELTAlpha) { -// CELT7.celt_decode_float(mCELTAlphaDecoder, -// data, -// data.length, -// out); + CELT7.celt_decode_float(mCELTAlphaDecoder, + data, + data.capacity(), + out); } else if(mCodec == JumbleUDPMessageType.UDPVoiceCELTBeta) { -// CELT11.celt_decode_float(mCELTBetaDecoder, -// data, -// data.length, -// out, -// Audio.FRAME_SIZE); + CELT11.celt_decode_float(mCELTBetaDecoder, + data, + data.capacity(), + out, + Audio.FRAME_SIZE); } else if(mCodec == JumbleUDPMessageType.UDPVoiceOpus) { decodedSamples = Opus.opus_decode_float(mOpusDecoder, data, @@ -275,11 +272,11 @@ public class AudioOutputSpeech { if(mFrames.isEmpty() && mHasTerminator) nextAlive = false; } else { -// if(mCodec == JumbleUDPMessageType.UDPVoiceCELTAlpha) -// CELT7.celt_decode_float(mCELTAlphaDecoder, null, 0, out); -// else if(mCodec == JumbleUDPMessageType.UDPVoiceCELTBeta) -// CELT11.celt_decode_float(mCELTBetaDecoder, null, 0, out, Audio.FRAME_SIZE); - /*else*/ if(mCodec == JumbleUDPMessageType.UDPVoiceOpus) + if(mCodec == JumbleUDPMessageType.UDPVoiceCELTAlpha) + CELT7.celt_decode_float(mCELTAlphaDecoder, null, 0, out); + else if(mCodec == JumbleUDPMessageType.UDPVoiceCELTBeta) + CELT11.celt_decode_float(mCELTBetaDecoder, null, 0, out, Audio.FRAME_SIZE); + else if(mCodec == JumbleUDPMessageType.UDPVoiceOpus) decodedSamples = Opus.opus_decode_float(mOpusDecoder, null, 0, out, Audio.FRAME_SIZE, 0); // else { // Speex.speex_decode(mSpeexDecoder, null, out); @@ -329,9 +326,8 @@ public class AudioOutputSpeech { if(newSize > mBuffer.length) { float[] n = new float[newSize]; if(mBuffer != null) - System.arraycopy(mBuffer, 0, n, 0, mAudioBufferSize); + System.arraycopy(mBuffer, 0, n, 0, mBuffer.length); mBuffer = n; - mAudioBufferSize = newSize; } } @@ -354,18 +350,16 @@ public class AudioOutputSpeech { public void destroy() { if(mOpusDecoder != null) Opus.opus_decoder_destroy(mOpusDecoder); -// if(mCELTBetaDecoder != null) -// CELT11.celt_decoder_destroy(mCELTBetaDecoder); -// if(mCELTAlphaMode != null) -// CELT7.celt_mode_destroy(mCELTAlphaMode); -// if(mCELTAlphaDecoder != null) -// CELT7.celt_decoder_destroy(mCELTAlphaDecoder); + if(mCELTBetaDecoder != null) + CELT11.celt_decoder_destroy(mCELTBetaDecoder); + if(mCELTAlphaMode != null) + CELT7.celt_mode_destroy(mCELTAlphaMode); + if(mCELTAlphaDecoder != null) + CELT7.celt_decoder_destroy(mCELTAlphaDecoder); // if(mSpeexBits != null) // Speex.speex_bits_destroy(mSpeexBits); // if(mSpeexDecoder != null) // Speex.speex_decoder_destroy(mSpeexDecoder); -// if(mJitterBuffer != null) -// Speex.jitter_buffer_destroy(mJitterBuffer); mJitterBuffer.destroy(); } } diff --git a/src/main/java/com/morlunk/jumble/audio/javacpp/CELT11.java b/src/main/java/com/morlunk/jumble/audio/javacpp/CELT11.java index bab4cee..c5753ed 100644 --- a/src/main/java/com/morlunk/jumble/audio/javacpp/CELT11.java +++ b/src/main/java/com/morlunk/jumble/audio/javacpp/CELT11.java @@ -16,8 +16,14 @@ package com.morlunk.jumble.audio.javacpp; +import com.googlecode.javacpp.BytePointer; +import com.googlecode.javacpp.FloatPointer; import com.googlecode.javacpp.IntPointer; +import com.googlecode.javacpp.Loader; import com.googlecode.javacpp.Pointer; +import com.googlecode.javacpp.ShortPointer; +import com.googlecode.javacpp.annotation.Cast; +import com.googlecode.javacpp.annotation.NoDeallocator; import com.googlecode.javacpp.annotation.Platform; /** @@ -26,21 +32,19 @@ import com.googlecode.javacpp.annotation.Platform; @Platform(library="celt11", cinclude={"<celt.h>","<celt_types.h>"}) public class CELT11 { - public static class Decoder { + public static final int CELT_GET_BITSTREAM_VERSION = 2000; - private Pointer mNativeDecoder; - private int mError; - - public Decoder(int sampleRate, int channels) { - IntPointer error = new IntPointer(1); - mNativeDecoder = celt_decoder_init(sampleRate, channels, error); - mError = error.get(); - } - - public int getError() { - return mError; - } + static { + Loader.load(); } - private static native Pointer celt_decoder_init(int sampleRate, int channels, IntPointer error); -} + public static native @NoDeallocator Pointer celt_mode_create(int sampleRate, int frameSize, IntPointer error); + public static native int celt_mode_info(@Cast("const CELTMode*") Pointer mode, int request, IntPointer value); + public static native void celt_mode_destroy(@Cast("CELTMode*") Pointer mode); + + public static native @NoDeallocator Pointer celt_decoder_create(int sampleRate, int channels, IntPointer error); + public static native int celt_decode(@Cast("CELTDecoder*") Pointer st, @Cast("const unsigned char*") BytePointer data, int len, ShortPointer pcm, int frameSize); + public static native int celt_decode_float(@Cast("CELTDecoder*") Pointer st, @Cast("const unsigned char*") BytePointer data, int len, FloatPointer pcm, int frameSize); + public static native int celt_decoder_ctl(@Cast("CELTDecoder*") Pointer st, int request, Pointer val); + public static native void celt_decoder_destroy(@Cast("CELTDecoder*") Pointer st); +}
\ No newline at end of file diff --git a/src/main/java/com/morlunk/jumble/audio/javacpp/CELT7.java b/src/main/java/com/morlunk/jumble/audio/javacpp/CELT7.java index 7032cf8..383e4df 100644 --- a/src/main/java/com/morlunk/jumble/audio/javacpp/CELT7.java +++ b/src/main/java/com/morlunk/jumble/audio/javacpp/CELT7.java @@ -16,8 +16,35 @@ package com.morlunk.jumble.audio.javacpp; +import com.googlecode.javacpp.BytePointer; +import com.googlecode.javacpp.FloatPointer; +import com.googlecode.javacpp.IntPointer; +import com.googlecode.javacpp.Loader; +import com.googlecode.javacpp.Pointer; +import com.googlecode.javacpp.ShortPointer; +import com.googlecode.javacpp.annotation.Cast; +import com.googlecode.javacpp.annotation.NoDeallocator; +import com.googlecode.javacpp.annotation.Platform; + /** * Created by andrew on 20/10/13. */ +@Platform(library="celt7", cinclude={"<celt.h>","<celt_types.h>"}) public class CELT7 { + + public static final int CELT_GET_BITSTREAM_VERSION = 2000; + + static { + Loader.load(); + } + + public static native @NoDeallocator Pointer celt_mode_create(int sampleRate, int frameSize, IntPointer error); + public static native int celt_mode_info(@Cast("const CELTMode*") Pointer mode, int request, IntPointer value); + public static native void celt_mode_destroy(@Cast("CELTMode*") Pointer mode); + + public static native @NoDeallocator Pointer celt_decoder_create(@Cast("CELTMode*") Pointer mode, int channels, IntPointer error); + public static native int celt_decode(@Cast("CELTDecoder*") Pointer st, @Cast("const unsigned char*") BytePointer data, int len, ShortPointer pcm); + public static native int celt_decode_float(@Cast("CELTDecoder*") Pointer st, @Cast("const unsigned char*") BytePointer data, int len, FloatPointer pcm); + public static native int celt_decoder_ctl(@Cast("CELTDecoder*") Pointer st, int request, Pointer val); + public static native void celt_decoder_destroy(@Cast("CELTDecoder*") Pointer st); } diff --git a/src/main/java/com/morlunk/jumble/audio/javacpp/Opus.java b/src/main/java/com/morlunk/jumble/audio/javacpp/Opus.java index b71bddd..72d284e 100644 --- a/src/main/java/com/morlunk/jumble/audio/javacpp/Opus.java +++ b/src/main/java/com/morlunk/jumble/audio/javacpp/Opus.java @@ -43,16 +43,6 @@ public class Opus { Loader.load(); } -// TODO work on an oo decoder wrapper -// public static class Decoder { -// private Pointer mNativeDecoder; -// -// public Decoder(int frameSize, int channels) { -// IntPointer error = new IntPointer(1); -// mNativeDecoder = opus_decoder_create(frameSize, channels, error); -// } -// } - public static native int opus_decoder_get_size(int channels); public static native @NoDeallocator Pointer opus_decoder_create(int fs, int channels, IntPointer error); public static native int opus_decoder_init(@Cast("OpusDecoder*") Pointer st, int fs, int channels); diff --git a/src/main/java/com/morlunk/jumble/audio/javacpp/Speex.java b/src/main/java/com/morlunk/jumble/audio/javacpp/Speex.java index 05936f2..3ba0018 100644 --- a/src/main/java/com/morlunk/jumble/audio/javacpp/Speex.java +++ b/src/main/java/com/morlunk/jumble/audio/javacpp/Speex.java @@ -32,7 +32,7 @@ import com.googlecode.javacpp.annotation.Platform; * JavaCPP interface for Speex JNI. * Created by andrew on 18/10/13. */ -@Platform(library="speex", link="speex", cinclude={"<speex/speex.h>","<speex/speex_types.h>","<speex/speex_jitter.h>", "<speex/speex_preprocess.h>"}) +@Platform(library="speex", link="speex", cinclude={"<speex/speex.h>","<speex/speex_types.h>","<speex/speex_jitter.h>", "<speex/speex_preprocess.h>", "<speex/speex_resampler.h>"}) public class Speex { static { @@ -125,7 +125,7 @@ public class Speex { } - public static class SpeexPreprocessState extends Pointer { + public static class SpeexPreprocessState { public static final int SPEEX_PREPROCESS_SET_DENOISE = 0; public static final int SPEEX_PREPROCESS_GET_DENOISE = 1; @@ -189,6 +189,28 @@ public class Speex { } + public static class SpeexResampler { + private Pointer mNativeState; + + public SpeexResampler(int channels, int inSampleRate, int outSampleRate, int quality) { + mNativeState = speex_resampler_init(channels, inSampleRate, outSampleRate, quality, null); + } + + public void resample(short[] in, short[] out) { + speex_resampler_process_int(mNativeState, 0, in, new int[] { in.length }, out, new int[] { out.length }); + } + + public void destroy() { + speex_resampler_destroy(mNativeState); + } + + } + + // Resampler + private static native Pointer speex_resampler_init(int channels, int inSampleRate, int outSampleRate, int quality, IntPointer error); + private static native int speex_resampler_process_int(@Cast("SpeexResamplerState*") Pointer state, int channelIndex, @Cast("short*") short[] in, @Cast("unsigned int*") int[] inLen, @Cast("short*") short[] out, @Cast("unsigned int*") int[] outLen); + private static native void speex_resampler_destroy(@Cast("SpeexResamplerState*") Pointer state); + // Jitter buffer private static native Pointer jitter_buffer_init(int tick); private static native void jitter_buffer_reset(@Cast("JitterBuffer*") Pointer jitterBuffer); |