From 5fd11bafc17798bbe97e59c2062f720d48146d6f Mon Sep 17 00:00:00 2001 From: sh123 Date: Wed, 10 Aug 2022 20:46:13 +0300 Subject: [PATCH] Freedv native wrappers --- .../radio/codec2talkie/protocol/Freedv.java | 70 ++++++++++ .../protocol/ProtocolFactory.java | 1 + libcodec2-android/src/main/cpp/Codec2JNI.cpp | 125 +++++++++++++++--- .../java/com/ustadmobile/codec2/Codec2.java | 31 ++++- 4 files changed, 204 insertions(+), 23 deletions(-) create mode 100644 codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java new file mode 100644 index 0000000..d3f96e0 --- /dev/null +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java @@ -0,0 +1,70 @@ +package com.radio.codec2talkie.protocol; + +import android.content.Context; + +import com.radio.codec2talkie.protocol.message.TextMessage; +import com.radio.codec2talkie.protocol.position.Position; +import com.radio.codec2talkie.transport.Transport; + +import java.io.IOException; + +public class Freedv implements Protocol { + + private final Protocol _childProtocol; + private ProtocolCallback _parentProtocolCallback; + + public Freedv(Protocol childProtocol, int freedvMode) { + _childProtocol = childProtocol; + } + + @Override + public void initialize(Transport transport, Context context, ProtocolCallback protocolCallback) throws IOException { + _parentProtocolCallback = protocolCallback; + _childProtocol.initialize(transport, context, _parentProtocolCallback); + } + + @Override + public int getPcmAudioBufferSize() { + return 0; + } + + @Override + public void sendPcmAudio(String src, String dst, int codec, short[] pcmFrame) throws IOException { + + } + + @Override + public void sendCompressedAudio(String src, String dst, int codec, byte[] frame) throws IOException { + + } + + @Override + public void sendTextMessage(TextMessage textMessage) throws IOException { + + } + + @Override + public void sendData(String src, String dst, byte[] dataPacket) throws IOException { + + } + + @Override + public boolean receive() throws IOException { + return false; + } + + @Override + public void sendPosition(Position position) throws IOException { + + } + + @Override + public void flush() throws IOException { + + } + + @Override + public void close() { + + } +} diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java index 08410e8..03b1194 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java @@ -33,6 +33,7 @@ public class ProtocolFactory { ProtocolFactory.ProtocolType protocolType; + // Use HDLC instead of KISS for the sound modem as we are the modem if (sharedPreferences.getString(PreferenceKeys.PORTS_TYPE, "loopback").equals("sound_modem")) { protocolType = ProtocolFactory.ProtocolType.HDLC; } else if (sharedPreferences.getBoolean(PreferenceKeys.KISS_ENABLED, true)) { diff --git a/libcodec2-android/src/main/cpp/Codec2JNI.cpp b/libcodec2-android/src/main/cpp/Codec2JNI.cpp index fe1df4b..15d176d 100644 --- a/libcodec2-android/src/main/cpp/Codec2JNI.cpp +++ b/libcodec2-android/src/main/cpp/Codec2JNI.cpp @@ -3,6 +3,7 @@ #include "codec2/codec2_fdmdv.h" #include "codec2/codec2.h" #include "codec2/fsk.h" +#include "codec2/freedv_api.h" namespace Java_com_ustadmobile_codec2_Codec2 { @@ -28,6 +29,12 @@ namespace Java_com_ustadmobile_codec2_Codec2 { int gain; }; + struct ContextFreedv { + struct freedv *freeDv; + short *speechSamples; + short *modemSamples; + }; + static Context *getContext(jlong jp) { auto p = (unsigned long) jp; Context *con; @@ -42,6 +49,13 @@ namespace Java_com_ustadmobile_codec2_Codec2 { return conFsk; } + static ContextFreedv *getContextFreedv(jlong jp) { + auto p = (unsigned long) jp; + ContextFreedv *conFreedv; + conFreedv = (ContextFreedv *) p; + return conFreedv; + } + static jlong create(JNIEnv *env, jclass clazz, int mode) { struct Context *con; con = (struct Context *) malloc(sizeof(struct Context)); @@ -80,8 +94,18 @@ namespace Java_com_ustadmobile_codec2_Codec2 { fsk_set_freq_est_limits(fsk, 500, sampleFrequency / 4); fsk_set_freq_est_alg(fsk, 0); - auto pv = (unsigned long) conFsk; - return pv; + return reinterpret_cast(conFsk); + } + + static jlong freedvCreate(JNIEnv *env, jclass clazz, int mode) { + struct ContextFreedv *conFreedv; + conFreedv = (struct ContextFreedv *) malloc(sizeof(struct ContextFreedv)); + conFreedv->freeDv = freedv_open(mode); + conFreedv->speechSamples = static_cast(malloc( + freedv_get_n_max_speech_samples(conFreedv->freeDv) * sizeof(short))); + conFreedv->modemSamples = static_cast(malloc( + freedv_get_n_max_modem_samples(conFreedv->freeDv) * sizeof(short))); + return reinterpret_cast(conFreedv); } static jint c2spf(JNIEnv *env, jclass clazz, jlong n) { @@ -124,6 +148,31 @@ namespace Java_com_ustadmobile_codec2_Codec2 { return fsk_nin(conFsk->fsk); } + static jint freedvGetMaxSpeechSamples(JNIEnv * env, jclass clazz, jlong n) { + ContextFreedv *conFreedv = getContextFreedv(n); + return freedv_get_n_max_speech_samples(conFreedv->freeDv); + } + + static jint freedvGetMaxModemSamples(JNIEnv * env, jclass clazz, jlong n) { + ContextFreedv *conFreedv = getContextFreedv(n); + return freedv_get_n_max_modem_samples(conFreedv->freeDv); + } + + static jint freedvGetNSpeechSamples(JNIEnv * env, jclass clazz, jlong n) { + ContextFreedv *conFreedv = getContextFreedv(n); + return freedv_get_n_speech_samples(conFreedv->freeDv); + } + + static jint freedvGetNomModemSamples(JNIEnv * env, jclass clazz, jlong n) { + ContextFreedv *conFreedv = getContextFreedv(n); + return freedv_get_n_nom_modem_samples(conFreedv->freeDv); + } + + static jint freedvNin(JNIEnv * env, jclass clazz, jlong n) { + ContextFreedv *conFreedv = getContextFreedv(n); + return freedv_nin(conFreedv->freeDv); + } + static jint destroy(JNIEnv *env, jclass clazz, jlong n) { Context *con = getContext(n); codec2_destroy(con->c2); @@ -145,6 +194,15 @@ namespace Java_com_ustadmobile_codec2_Codec2 { return 0; } + static jint freedvDestroy(JNIEnv *env, jclass clazz, jlong n) { + ContextFreedv *conFreedv = getContextFreedv(n); + freedv_close(conFreedv->freeDv); + free(conFreedv->modemSamples); + free(conFreedv->speechSamples); + free(conFreedv); + return 0; + } + static jlong encode(JNIEnv *env, jclass clazz, jlong n, jshortArray inputBuffer, jcharArray outputBits) { Context *con = getContext(n); jshort *jbuf = env->GetShortArrayElements(inputBuffer, nullptr); @@ -177,6 +235,16 @@ namespace Java_com_ustadmobile_codec2_Codec2 { return 0; } + static jlong freedvTx(JNIEnv *env, jclass clazz, jlong n, jshortArray outputModemSamples, jshortArray inputSpeechSamples) { + ContextFreedv *conFreedv = getContextFreedv(n); + int cntSpeechSamples = freedv_get_n_speech_samples(conFreedv->freeDv); + env->GetShortArrayRegion(inputSpeechSamples, 0, cntSpeechSamples, conFreedv->speechSamples); + int cntModemSamples = freedv_get_n_nom_modem_samples(conFreedv->freeDv); + freedv_tx(conFreedv->freeDv, conFreedv->modemSamples, conFreedv->speechSamples); + env->SetShortArrayRegion(outputModemSamples, 0, cntModemSamples, conFreedv->modemSamples); + return cntModemSamples; + } + static jlong decode(JNIEnv *env, jclass clazz, jlong n, jshortArray outputSamples, jbyteArray inputBits) { Context *con = getContext(n); env->GetByteArrayRegion(inputBits, 0, con->nbyte, reinterpret_cast(con->bits)); @@ -197,23 +265,44 @@ namespace Java_com_ustadmobile_codec2_Codec2 { return 0; } + static jlong freedvRx(JNIEnv *env, jclass clazz, jlong n, jshortArray outputSpeechSamples, jshortArray inputModemSamples) { + ContextFreedv *conFreedv = getContextFreedv(n); + int nin = freedv_nin(conFreedv->freeDv); + env->GetShortArrayRegion(inputModemSamples, 0, nin, conFreedv->speechSamples); + freedv_rx(conFreedv->freeDv, conFreedv->modemSamples, conFreedv->speechSamples); + env->SetShortArrayRegion(outputSpeechSamples, 0, nin, conFreedv->modemSamples); + return nin; + } + static JNINativeMethod method_table[] = { - {"create", "(I)J", (void *) create}, - {"getSamplesPerFrame", "(J)I", (void *) c2spf}, - {"getBitsSize", "(J)I", (void *) c2bits}, - {"destroy", "(J)I", (void *) destroy}, - {"encode", "(J[S[C)J", (void *) encode}, - {"decode", "(J[S[B)J", (void *) decode}, - {"fskCreate", "(IIIII)J", (void *) fskCreate}, - {"fskDestroy", "(J)I", (void *) fskDestroy}, - {"fskModulate", "(J[S[B)J", (void *) fskModulate}, - {"fskDemodulate", "(J[S[B)J", (void *) fskDemodulate}, - {"fskDemodBitsBufSize","(J)I", (void *) fskDemodBitsBufSize}, - {"fskModSamplesBufSize","(J)I", (void *) fskModSamplesBufSize}, - {"fskDemodSamplesBufSize","(J)I", (void *) fskDemodSamplesBufSize}, - {"fskModBitsBufSize", "(J)I", (void *) fskModBitsBufSize}, - {"fskSamplesPerSymbol","(J)I", (void *) fskSamplesPerSymbol}, - {"fskNin", "(J)I", (void *) fskNin} + // codec2 + {"create", "(I)J", (void *) create}, + {"getSamplesPerFrame", "(J)I", (void *) c2spf}, + {"getBitsSize", "(J)I", (void *) c2bits}, + {"destroy", "(J)I", (void *) destroy}, + {"encode", "(J[S[C)J", (void *) encode}, + {"decode", "(J[S[B)J", (void *) decode}, + // fsk + {"fskCreate", "(IIIII)J", (void *) fskCreate}, + {"fskDestroy", "(J)I", (void *) fskDestroy}, + {"fskModulate", "(J[S[B)J", (void *) fskModulate}, + {"fskDemodulate", "(J[S[B)J", (void *) fskDemodulate}, + {"fskDemodBitsBufSize", "(J)I", (void *) fskDemodBitsBufSize}, + {"fskModSamplesBufSize", "(J)I", (void *) fskModSamplesBufSize}, + {"fskDemodSamplesBufSize", "(J)I", (void *) fskDemodSamplesBufSize}, + {"fskModBitsBufSize", "(J)I", (void *) fskModBitsBufSize}, + {"fskSamplesPerSymbol", "(J)I", (void *) fskSamplesPerSymbol}, + {"fskNin", "(J)I", (void *) fskNin}, + // freedv + {"freedvCreate", "(I)J", (void *)freedvCreate}, + {"freedvDestroy", "(J)I", (void *)freedvDestroy}, + {"freedvGetMaxSpeechSamples", "(J)I", (void *)freedvGetMaxSpeechSamples}, + {"freedvGetMaxModemSamples", "(J)I", (void *)freedvGetMaxModemSamples}, + {"freedvGetNSpeechSamples", "(J)I", (void *)freedvGetNSpeechSamples}, + {"freedvGetNomModemSamples", "(J)I", (void *)freedvGetNomModemSamples}, + {"freedvNin", "(J)I", (void *)freedvNin}, + {"freedvTx", "(J[S[S)J", (void *)freedvTx}, + {"freedvRx", "(J[S[S)J", (void *)freedvRx} }; } diff --git a/libcodec2-android/src/main/java/com/ustadmobile/codec2/Codec2.java b/libcodec2-android/src/main/java/com/ustadmobile/codec2/Codec2.java index 5de0986..17f9b1b 100644 --- a/libcodec2-android/src/main/java/com/ustadmobile/codec2/Codec2.java +++ b/libcodec2-android/src/main/java/com/ustadmobile/codec2/Codec2.java @@ -10,6 +10,7 @@ public class Codec2 { System.loadLibrary("Codec2JNI"); } + // codec2 modes public static final int CODEC2_MODE_3200 = 0; public static final int CODEC2_MODE_2400 = 1; public static final int CODEC2_MODE_1600 = 2; @@ -20,12 +21,18 @@ public class Codec2 { public static final int CODEC2_MODE_450=10; public static final int CODEC2_MODE_450PWB=11; - /** - * The size of the file header that is written when using c2enc. When decoding, this must be - * skipped. - */ - public static final int CODEC2_FILE_HEADER_SIZE = 7; + // freedv modes + public static final int FREEDV_MODE_1600 = 0; + public static final int FREEDV_MODE_2400A = 3; + public static final int FREEDV_MODE_2400B = 4; + public static final int FREEDV_MODE_800XA = 5; + public static final int FREEDV_MODE_700C = 6; + public static final int FREEDV_MODE_700D = 7; + public static final int FREEDV_MODE_2020 = 8; + public static final int FREEDV_MODE_2020B = 16; + public static final int FREEDV_MODE_700E = 13; + // raw codec2 public native static long create(int mode); public native static int destroy(long con); @@ -35,6 +42,7 @@ public class Codec2 { public native static long encode(long con, short[] inputSamples, char[] outputBits); public native static long decode(long con, short[] outputSamples, byte[] inputsBits); + // raw fsk public native static long fskCreate(int sampleFrequency, int symbolRate, int toneFreq, int toneSpacing, int gain); public native static int fskDestroy(long conFsk); @@ -47,4 +55,17 @@ public class Codec2 { public native static long fskModulate(long conFsk, short[] outputSamples, byte[] inputBits); public native static long fskDemodulate(long conFsk, short[] inputSamples, byte[] outputBits); + + // freedv + public native static long freedvCreate(int mode); + public native static int freedvDestroy(long conFreedv); + + public native static int freedvGetMaxSpeechSamples(long conFreedv); + public native static int freedvGetMaxModemSamples(long conFreedv); + public native static int freedvGetNSpeechSamples(long conFreedv); + public native static int freedvGetNomModemSamples(long conFreedv); + public native static int freedvNin(long conFreedv); + + public native static long freedvTx(long conFsk, short[] outputModemSamples, short[] inputSpeechSamples); + public native static long freedvRx(long conFsk, short[] outputSpeechSamples, short[] inputModemSamples); } \ No newline at end of file