From b0c331a8bb4d18c634efb913411a6b3e88db6a7a Mon Sep 17 00:00:00 2001 From: sh123 Date: Thu, 11 Aug 2022 12:01:02 +0300 Subject: [PATCH] Enable squelch and add loopback setting --- .../com/radio/codec2talkie/app/AppWorker.java | 4 +- .../radio/codec2talkie/protocol/Freedv.java | 24 ++++++++---- .../protocol/ProtocolFactory.java | 14 +++++-- .../codec2talkie/settings/PreferenceKeys.java | 1 + .../codec2talkie/transport/SoundModem.java | 38 +++++++++++++++---- .../codec2talkie/transport/SoundModemFsk.java | 4 +- .../transport/TransportFactory.java | 13 ++++++- codec2talkie/src/main/res/values/arrays.xml | 8 ++-- codec2talkie/src/main/res/values/strings.xml | 2 + .../main/res/xml/preferences_sound_modem.xml | 21 ++++++---- libcodec2-android/src/main/cpp/Codec2JNI.cpp | 1 + 11 files changed, 98 insertions(+), 32 deletions(-) diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppWorker.java b/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppWorker.java index 3398d6e..b111fa8 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppWorker.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/app/AppWorker.java @@ -62,7 +62,7 @@ public class AppWorker extends Thread { // output data., mic -> bt private AudioRecord _systemAudioRecorder; - private final short[] _recordAudioBuffer; + private short[] _recordAudioBuffer; // callbacks private final Handler _onPlayerStateChanged; @@ -98,7 +98,6 @@ public class AppWorker extends Thread { _protocol = ProtocolFactory.create(_codec2Mode, context); _processPeriodicTimer = new Timer(); - _recordAudioBuffer = new short[_protocol.getPcmAudioBufferSize()]; constructSystemAudioDevices(transportType); } @@ -541,6 +540,7 @@ public class AppWorker extends Thread { try { _protocol.initialize(_transport, _context, _protocolCallback); + _recordAudioBuffer = new short[_protocol.getPcmAudioBufferSize()]; startWorkerMessageHandler(); Looper.loop(); } catch (IOException e) { diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java index df1b16e..c3c06ea 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java @@ -1,9 +1,15 @@ package com.radio.codec2talkie.protocol; import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; +import androidx.preference.PreferenceManager; + +import com.radio.codec2talkie.app.AppWorker; import com.radio.codec2talkie.protocol.message.TextMessage; import com.radio.codec2talkie.protocol.position.Position; +import com.radio.codec2talkie.settings.PreferenceKeys; import com.radio.codec2talkie.transport.Transport; import com.ustadmobile.codec2.Codec2; @@ -11,8 +17,7 @@ import java.io.IOException; import java.util.Arrays; public class Freedv implements Protocol { - private final int _freedvMode; - private final Protocol _childProtocol; + private static final String TAG = Freedv.class.getSimpleName(); private ProtocolCallback _parentProtocolCallback; private Transport _transport; @@ -22,18 +27,20 @@ public class Freedv implements Protocol { private short[] _modemTxBuffer; private short[] _speechRxBuffer; - public Freedv(Protocol childProtocol, int freedvMode) { - _childProtocol = childProtocol; - _freedvMode = freedvMode; + public Freedv() { } @Override public void initialize(Transport transport, Context context, ProtocolCallback parentProtocolCallback) throws IOException { _transport = transport; _parentProtocolCallback = parentProtocolCallback; - _childProtocol.initialize(transport, context, _parentProtocolCallback); - _freedv = Codec2.freedvCreate(_freedvMode); + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + String modemType = sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_TYPE, "1200"); + int mode = Integer.parseInt(modemType.substring(1)); + Log.i(TAG, "Using freedv mode " + mode); + + _freedv = Codec2.freedvCreate(mode); _modemTxBuffer = new short[Codec2.freedvGetNomModemSamples(_freedv)]; _speechRxBuffer = new short[Codec2.freedvGetMaxSpeechSamples(_freedv)]; } @@ -46,7 +53,9 @@ public class Freedv implements Protocol { @Override public void sendPcmAudio(String src, String dst, int codec, short[] pcmFrame) throws IOException { Codec2.freedvTx(_freedv, _modemTxBuffer, pcmFrame); + Log.i(TAG, "send pcm " + _modemTxBuffer.length); _transport.write(_modemTxBuffer); + _parentProtocolCallback.onTransmitPcmAudio(src, dst, codec, pcmFrame); } @Override @@ -69,6 +78,7 @@ public class Freedv implements Protocol { if (bytesRead == nin) { long cntRead = Codec2.freedvRx(_freedv, _speechRxBuffer, buf); if (cntRead > 0) { + Log.i(TAG, "receive " + cntRead); _parentProtocolCallback.onReceivePcmAudio(null, null, -1, Arrays.copyOf(_speechRxBuffer, (int) cntRead)); return true; } 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 03b1194..b695bd6 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/ProtocolFactory.java @@ -14,7 +14,8 @@ public class ProtocolFactory { HDLC("HDLC"), KISS("KISS"), KISS_BUFFERED("KISS BUF"), - KISS_PARROT("KISS RPT"); + KISS_PARROT("KISS RPT"), + FREEDV("FREEDV"); private final String _name; @@ -35,7 +36,11 @@ public class ProtocolFactory { // 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; + if (sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_TYPE, "1200").startsWith("F")) { + protocolType = ProtocolType.FREEDV; + } else { + protocolType = ProtocolFactory.ProtocolType.HDLC; + } } else if (sharedPreferences.getBoolean(PreferenceKeys.KISS_ENABLED, true)) { if (sharedPreferences.getBoolean(PreferenceKeys.KISS_PARROT, false)) { protocolType = ProtocolFactory.ProtocolType.KISS_PARROT; @@ -77,6 +82,9 @@ public class ProtocolFactory { case HDLC: proto = new Hdlc(sharedPreferences); break; + case FREEDV: + // standalone + return new Freedv(); case RAW: default: proto = new Raw(); @@ -96,7 +104,7 @@ public class ProtocolFactory { proto = new AudioFrameAggregator(proto, codec2ModeId); proto = new AudioCodec2(proto, codec2ModeId); - if (aprsEnabled) { // && protocolType != ProtocolType.RAW) { + if (aprsEnabled) { proto = new Aprs(proto); } return proto; diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java b/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java index 6104448..a3978cf 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/settings/PreferenceKeys.java @@ -23,6 +23,7 @@ public final class PreferenceKeys { public static String PORTS_SOUND_MODEM_RIG = "ports_sound_modem_rig"; public static String PORTS_SOUND_MODEM_GAIN ="ports_sound_modem_gain"; public static String PORTS_SOUND_MODEM_PTT_OFF_DELAY_MS = "ports_sound_modem_ptt_off_delay_ms"; + public static String PORTS_SOUND_MODEM_LOOPBACK="ports_sound_modem_loopback"; public static String CODEC2_MODE = "codec2_mode"; public static String CODEC2_RECORDING_ENABLED = "codec2_recording_enabled"; diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModem.java b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModem.java index 73f00c7..ce9791c 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModem.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModem.java @@ -17,6 +17,7 @@ import com.radio.codec2talkie.rigctl.RigCtlFactory; import com.radio.codec2talkie.settings.PreferenceKeys; import java.io.IOException; +import java.nio.BufferOverflowException; import java.nio.ByteBuffer; import java.nio.ShortBuffer; import java.util.Timer; @@ -43,12 +44,17 @@ public class SoundModem implements Transport, Runnable { private final ShortBuffer _recordAudioBuffer; + private boolean _isLoopback = false; + public SoundModem(Context context) { + _name = "SndModem"; + _isPttOn = false; + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); boolean disableRx = sharedPreferences.getBoolean(PreferenceKeys.PORTS_SOUND_MODEM_DISABLE_RX, false); _pttOffDelayMs = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_PTT_OFF_DELAY_MS, "1000")); - _name = "SndModem"; + _isLoopback = sharedPreferences.getBoolean(PreferenceKeys.PORTS_SOUND_MODEM_LOOPBACK, false); constructSystemAudioDevices(disableRx); @@ -59,11 +65,9 @@ public class SoundModem implements Transport, Runnable { e.printStackTrace(); } - _recordAudioBuffer = ShortBuffer.allocate(4096); + _recordAudioBuffer = ShortBuffer.allocate(_isLoopback ? 1024*100 : 1024*10); - _isPttOn = false; - - if (!disableRx) + if (!disableRx && !_isLoopback) new Thread(this).start(); } @@ -124,6 +128,7 @@ public class SoundModem implements Transport, Runnable { public int read(short[] audioSamples) throws IOException { synchronized (_recordAudioBuffer) { if (_recordAudioBuffer.position() >= audioSamples.length) { + //Log.i(TAG, "read " + _recordAudioBuffer.position()); _recordAudioBuffer.flip(); _recordAudioBuffer.get(audioSamples); _recordAudioBuffer.compact(); @@ -138,7 +143,20 @@ public class SoundModem implements Transport, Runnable { pttOn(); if (_systemAudioPlayer.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) _systemAudioPlayer.play(); - _systemAudioPlayer.write(audioSamples, 0, audioSamples.length); + if (_isLoopback) { + synchronized (_recordAudioBuffer) { + for (short sample : audioSamples) { + try { + _recordAudioBuffer.put(sample); + } catch (BufferOverflowException e) { + e.printStackTrace(); + _recordAudioBuffer.clear(); + } + } + } + } else { + _systemAudioPlayer.write(audioSamples, 0, audioSamples.length); + } _systemAudioPlayer.stop(); pttOff(); return audioSamples.length; @@ -156,6 +174,7 @@ public class SoundModem implements Transport, Runnable { @Override public void run() { + Log.i(TAG, "Starting receive thread"); android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO); int readSize = 16; short [] sampleBuf = new short[readSize]; @@ -172,7 +191,12 @@ public class SoundModem implements Transport, Runnable { } synchronized (_recordAudioBuffer) { for (short sample : sampleBuf) { - _recordAudioBuffer.put(sample); + try { + _recordAudioBuffer.put(sample); + } catch (BufferOverflowException e) { + e.printStackTrace(); + _recordAudioBuffer.clear(); + } } } } diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModemFsk.java b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModemFsk.java index 5fc69d4..f045af6 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModemFsk.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModemFsk.java @@ -53,7 +53,7 @@ public class SoundModemFsk implements Transport, Runnable { private boolean _isRunning = true; private final ByteBuffer _sampleBuffer; - private final boolean _isLoopback = false; + private boolean _isLoopback = false; private final long _fskModem; @@ -70,6 +70,8 @@ public class SoundModemFsk implements Transport, Runnable { int bitRate = Integer.parseInt(_sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_TYPE, "1200")); int gain = Integer.parseInt(_sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_GAIN, "10000")); _pttOffDelayMs = Integer.parseInt(_sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_PTT_OFF_DELAY_MS, "1000")); + _isLoopback = _sharedPreferences.getBoolean(PreferenceKeys.PORTS_SOUND_MODEM_LOOPBACK, false); + _name = "SndModemFsk" + bitRate; if (bitRate == 300) { // <230 spacing for 300 bps does not work with codec2 fsk for receive diff --git a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/TransportFactory.java b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/TransportFactory.java index 98ef175..103ba19 100644 --- a/codec2talkie/src/main/java/com/radio/codec2talkie/transport/TransportFactory.java +++ b/codec2talkie/src/main/java/com/radio/codec2talkie/transport/TransportFactory.java @@ -1,11 +1,15 @@ package com.radio.codec2talkie.transport; import android.content.Context; +import android.content.SharedPreferences; + +import androidx.preference.PreferenceManager; import com.radio.codec2talkie.connect.BleHandler; import com.radio.codec2talkie.connect.BluetoothSocketHandler; import com.radio.codec2talkie.connect.TcpIpSocketHandler; import com.radio.codec2talkie.connect.UsbPortHandler; +import com.radio.codec2talkie.settings.PreferenceKeys; import java.io.IOException; @@ -32,6 +36,7 @@ public class TransportFactory { } public static Transport create(TransportType transportType, Context context) throws IOException { + switch (transportType) { case USB: return new UsbSerial(UsbPortHandler.getPort(), UsbPortHandler.getName()); @@ -42,10 +47,16 @@ public class TransportFactory { case BLE: return new Ble(BleHandler.getGatt(), BleHandler.getName()); case SOUND_MODEM: - return new SoundModemFsk(context); + return isFreeDv(context) ? new SoundModem(context) : new SoundModemFsk(context); case LOOPBACK: default: return new Loopback(); } } + + private static boolean isFreeDv(Context context) { + SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context); + String modemType = sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_TYPE, "1200"); + return modemType.startsWith("F"); + } } diff --git a/codec2talkie/src/main/res/values/arrays.xml b/codec2talkie/src/main/res/values/arrays.xml index 2e94a39..4bec223 100644 --- a/codec2talkie/src/main/res/values/arrays.xml +++ b/codec2talkie/src/main/res/values/arrays.xml @@ -332,9 +332,9 @@ FreeDV 800XA FreeDV 700C FreeDV 700D - FreeDV 2020 + @@ -346,9 +346,9 @@ F5 F6 F7 - F8 + diff --git a/codec2talkie/src/main/res/values/strings.xml b/codec2talkie/src/main/res/values/strings.xml index 0c4d40d..aaeec1b 100644 --- a/codec2talkie/src/main/res/values/strings.xml +++ b/codec2talkie/src/main/res/values/strings.xml @@ -307,4 +307,6 @@ 🎛 CAT PTT transmit off delay How long to wait before switching off the transmission after last data is sent out (milliseconds) + Enable modulator loopback mode + Audio modulated samples will be fed back to de-modulator \ No newline at end of file diff --git a/codec2talkie/src/main/res/xml/preferences_sound_modem.xml b/codec2talkie/src/main/res/xml/preferences_sound_modem.xml index c13cc1f..18dc913 100644 --- a/codec2talkie/src/main/res/xml/preferences_sound_modem.xml +++ b/codec2talkie/src/main/res/xml/preferences_sound_modem.xml @@ -22,13 +22,6 @@ app:defaultValue="200"> - - - + + + + + + \ No newline at end of file diff --git a/libcodec2-android/src/main/cpp/Codec2JNI.cpp b/libcodec2-android/src/main/cpp/Codec2JNI.cpp index 73b9151..d794ac3 100644 --- a/libcodec2-android/src/main/cpp/Codec2JNI.cpp +++ b/libcodec2-android/src/main/cpp/Codec2JNI.cpp @@ -105,6 +105,7 @@ namespace Java_com_ustadmobile_codec2_Codec2 { 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))); + freedv_set_squelch_en(conFreedv->freeDv, 1); return reinterpret_cast(conFreedv); }