codec2_talkie/codec2talkie/src/main/java/com/radio/codec2talkie/protocol/Freedv.java

193 wiersze
7.6 KiB
Java
Czysty Zwykły widok Historia

2022-08-10 17:46:13 +00:00
package com.radio.codec2talkie.protocol;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Log;
2022-08-10 17:46:13 +00:00
import androidx.preference.PreferenceManager;
2022-08-10 17:46:13 +00:00
import com.radio.codec2talkie.protocol.message.TextMessage;
import com.radio.codec2talkie.protocol.position.Position;
import com.radio.codec2talkie.settings.PreferenceKeys;
2022-08-11 14:18:47 +00:00
import com.radio.codec2talkie.settings.SettingsWrapper;
2022-08-11 13:40:06 +00:00
import com.radio.codec2talkie.tools.AudioTools;
2022-08-10 17:46:13 +00:00
import com.radio.codec2talkie.transport.Transport;
2022-08-10 19:46:43 +00:00
import com.ustadmobile.codec2.Codec2;
2022-08-10 17:46:13 +00:00
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ShortBuffer;
2022-08-10 19:46:43 +00:00
import java.util.Arrays;
2022-08-10 17:46:13 +00:00
public class Freedv implements Protocol {
private static final String TAG = Freedv.class.getSimpleName();
2022-08-10 19:46:43 +00:00
private static final int CRC_LENGTH = 2;
private static final int PKT_SIZE_LENGTH = 2;
2022-08-10 17:46:13 +00:00
private ProtocolCallback _parentProtocolCallback;
2022-08-10 19:46:43 +00:00
private Transport _transport;
private long _freedv;
2022-08-14 12:57:05 +00:00
private long _freedvData;
2022-08-10 19:46:43 +00:00
private short[] _modemTxBuffer;
private short[] _speechRxBuffer;
2022-08-10 17:46:13 +00:00
2022-08-14 12:57:05 +00:00
private short[] _dataSamplesBuffer;
private byte[] _dataBuffer;
ShortBuffer _dataSamples;
ShortBuffer _speechSamples;
2022-08-14 12:57:05 +00:00
public Freedv() {
2022-08-10 17:46:13 +00:00
}
@Override
2022-08-10 19:46:43 +00:00
public void initialize(Transport transport, Context context, ProtocolCallback parentProtocolCallback) throws IOException {
_transport = transport;
_parentProtocolCallback = parentProtocolCallback;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
2022-08-11 14:18:47 +00:00
int mode = SettingsWrapper.getFreeDvSoundModemModulation(sharedPreferences);
2022-08-13 13:29:30 +00:00
int dataMode = Integer.parseInt(sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_FREEDV_DATA_MODE, "12"));
boolean isSquelchEnabled = sharedPreferences.getBoolean(PreferenceKeys.PORTS_SOUND_MODEM_FREEDV_ENABLE_SQUELCH, true);
float squelchSnr = Float.parseFloat( sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_FREEDV_SQUELCH_SNR, "0.0"));
2022-08-13 13:29:30 +00:00
Log.i(TAG, "Using freedv mode " + AudioTools.getFreedvModeAsText(sharedPreferences));
2022-08-11 15:55:16 +00:00
2022-08-23 14:09:36 +00:00
_freedv = Codec2.freedvCreate(mode, isSquelchEnabled, squelchSnr, 0); // unset for speech
2022-08-10 19:46:43 +00:00
_modemTxBuffer = new short[Codec2.freedvGetNomModemSamples(_freedv)];
_speechRxBuffer = new short[Codec2.freedvGetMaxSpeechSamples(_freedv)];
_speechSamples = ShortBuffer.allocate(1024*10);
2022-08-14 12:57:05 +00:00
2022-08-23 14:09:36 +00:00
_freedvData = Codec2.freedvCreate(dataMode, isSquelchEnabled, squelchSnr, 1);
_dataBuffer = new byte[Codec2.freedvGetBitsPerModemFrame(_freedvData) / 8];
2022-08-14 12:57:05 +00:00
_dataSamplesBuffer = new short[Codec2.freedvGetNTxSamples(_freedvData)];
_dataSamples = ShortBuffer.allocate(1024*10);
2022-08-10 17:46:13 +00:00
}
@Override
2022-08-13 17:48:45 +00:00
public int getPcmAudioRecordBufferSize() {
2022-08-10 19:46:43 +00:00
return Codec2.freedvGetNSpeechSamples(_freedv);
2022-08-10 17:46:13 +00:00
}
@Override
2023-12-09 12:27:49 +00:00
public void sendPcmAudio(String src, String dst, short[] pcmFrame) throws IOException {
2022-08-10 19:46:43 +00:00
Codec2.freedvTx(_freedv, _modemTxBuffer, pcmFrame);
2022-08-11 13:40:06 +00:00
//Log.i(TAG, "send pcm " + _modemTxBuffer.length);
2022-08-10 20:35:58 +00:00
_transport.write(_modemTxBuffer);
2023-12-09 12:27:49 +00:00
_parentProtocolCallback.onTransmitPcmAudio(src, dst, pcmFrame);
2022-08-10 17:46:13 +00:00
}
@Override
2023-12-09 12:27:49 +00:00
public void sendCompressedAudio(String src, String dst, byte[] frame) throws IOException {
2022-08-13 17:48:45 +00:00
Log.w(TAG, "sendCompressedAudio() is not supported");
2022-08-10 17:46:13 +00:00
}
@Override
public void sendTextMessage(TextMessage textMessage) throws IOException {
2022-08-13 17:48:45 +00:00
Log.w(TAG, "sendTextMessage() is not supported");
2022-08-10 17:46:13 +00:00
}
@Override
public void sendData(String src, String dst, String path, byte[] dataPacket) throws IOException {
if (dataPacket.length > _dataBuffer.length - CRC_LENGTH - PKT_SIZE_LENGTH) {
Log.e(TAG, "Too large packet " + dataPacket.length + " > " + _dataBuffer.length);
2022-08-14 12:57:05 +00:00
return;
}
long cnt = Codec2.freedvRawDataPreambleTx(_freedvData, _dataSamplesBuffer);
_transport.write(Arrays.copyOf(_dataSamplesBuffer, (int) cnt));
Arrays.fill(_dataBuffer, (byte) 0);
// transmit packet size first
_dataBuffer[0] = (byte)((dataPacket.length >> 8) & 0xff);
_dataBuffer[1] = (byte)(dataPacket.length & 0xff);
System.arraycopy(dataPacket, 0, _dataBuffer, 2, dataPacket.length);
Codec2.freedvRawDataTx(_freedvData, _dataSamplesBuffer, _dataBuffer);
2022-08-14 12:57:05 +00:00
_transport.write(_dataSamplesBuffer);
cnt = Codec2.freedvRawDataPostambleTx(_freedvData, _dataSamplesBuffer);
_transport.write(Arrays.copyOf(_dataSamplesBuffer, (int) cnt));
2022-08-10 17:46:13 +00:00
}
@Override
public boolean receive() throws IOException {
2022-08-10 20:35:58 +00:00
int nin = Codec2.freedvNin(_freedv);
int ninData = Codec2.freedvNin(_freedvData);
int cntToReadAll = Math.max(nin, ninData);
short[] samples = new short[cntToReadAll];
int cntReadAll = _transport.read(samples);
if (cntReadAll == 0) return false;
try {
_speechSamples.put(samples);
_dataSamples.put(samples);
} catch (BufferOverflowException e) {
_speechSamples.clear();
_dataSamples.clear();
return false;
}
boolean isRead = false;
while (_speechSamples.position() >= nin) {
//Log.i(TAG, "read speech " + nin + " " + _speechSamples.position());
short[] samplesSpeech = new short[nin];
_speechSamples.flip();
_speechSamples.get(samplesSpeech);
_speechSamples.compact();
long cntRead = Codec2.freedvRx(_freedv, _speechRxBuffer, samplesSpeech);
2022-08-10 20:35:58 +00:00
if (cntRead > 0) {
//Log.i(TAG, "receive " + cntRead);
2023-12-09 12:27:49 +00:00
_parentProtocolCallback.onReceivePcmAudio(null, null, Arrays.copyOf(_speechRxBuffer, (int) cntRead));
2022-08-14 19:22:04 +00:00
float snr = Codec2.freedvGetModemStat(_freedv);
_parentProtocolCallback.onReceiveSignalLevel((short) 0, (short)(100 * snr));
isRead = true;
2022-08-10 20:35:58 +00:00
}
nin = Codec2.freedvNin(_freedv);
2022-08-10 19:46:43 +00:00
}
while (_dataSamples.position() >= ninData) {
//Log.i(TAG, "read data " + ninData + " " + _dataSamples.position());
short[] samplesData = new short[ninData];
_dataSamples.flip();
_dataSamples.get(samplesData);
_dataSamples.compact();
long cntRead = Codec2.freedvRawDataRx(_freedvData, _dataBuffer, samplesData);
if (cntRead > 0) {
Log.i(TAG, "receive " + cntRead);
// extract packet length
int pktLen = (((int)_dataBuffer[0] & 0xff) << 8) | ((int)_dataBuffer[1] & 0xff);
byte [] pkt = new byte[pktLen];
System.arraycopy(_dataBuffer, 2, pkt, 0, pktLen);
2023-12-09 12:27:49 +00:00
// NOTE, default data is compressed audio, upper layer should distinguish
_parentProtocolCallback.onReceiveCompressedAudio(null, null, pkt);
2022-08-23 08:16:41 +00:00
float snr = Codec2.freedvGetModemStat(_freedvData);
2022-08-14 19:22:04 +00:00
_parentProtocolCallback.onReceiveSignalLevel((short) 0, (short)(100 * snr));
isRead = true;
}
ninData = Codec2.freedvNin(_freedvData);
}
return isRead;
2022-08-10 17:46:13 +00:00
}
@Override
public void sendPosition(Position position) throws IOException {
2022-08-13 17:48:45 +00:00
Log.w(TAG, "sendPosition() is not supported");
2022-08-10 17:46:13 +00:00
}
@Override
public void flush() throws IOException {
2022-08-25 17:41:26 +00:00
_speechSamples.clear();
_dataSamples.clear();
2022-08-10 17:46:13 +00:00
}
@Override
public void close() {
2022-08-14 12:57:05 +00:00
Codec2.freedvDestroy(_freedvData);
2022-08-10 19:46:43 +00:00
Codec2.freedvDestroy(_freedv);
2022-08-10 17:46:13 +00:00
}
}