codec2_talkie/codec2talkie/src/main/java/com/radio/codec2talkie/transport/SoundModemBase.java

196 wiersze
6.8 KiB
Java

package com.radio.codec2talkie.transport;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Process;
import android.util.Log;
import androidx.preference.PreferenceManager;
import com.radio.codec2talkie.rigctl.RigCtl;
import com.radio.codec2talkie.rigctl.RigCtlFactory;
import com.radio.codec2talkie.settings.PreferenceKeys;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ShortBuffer;
import java.util.Timer;
import java.util.TimerTask;
public class SoundModemBase implements Runnable {
private static final String TAG = SoundModemBase.class.getSimpleName();
protected String _name;
protected Context _context;
protected SharedPreferences _sharedPreferences;
protected AudioTrack _systemAudioPlayer;
protected AudioRecord _systemAudioRecorder;
protected boolean _isRunning = true;
private final RigCtl _rigCtl;
private Timer _pttOffTimer;
private boolean _isPttOn;
private final int _pttOffDelayMs;
protected final ShortBuffer _recordAudioSampleBuffer;
protected final boolean _isLoopback;
public SoundModemBase(Context context, int sampleRate) {
_name = "SoundModem";
_isPttOn = false;
_context = context;
_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"));
_isLoopback = _sharedPreferences.getBoolean(PreferenceKeys.PORTS_SOUND_MODEM_LOOPBACK, false);
if (_isLoopback) _name += "_";
int audioSource = Integer.parseInt(_sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_AUDIO_SOURCE, "6"));
int audioDestination = Integer.parseInt(_sharedPreferences.getString(PreferenceKeys.PORTS_SOUND_MODEM_AUDIO_DESTINATION, "1"));
constructSystemAudioDevices(disableRx, sampleRate, audioSource, audioDestination);
_rigCtl = RigCtlFactory.create(context);
try {
_rigCtl.initialize(TransportFactory.create(TransportFactory.TransportType.USB, context), context, null);
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "Failed to initialize RigCtl");
}
_recordAudioSampleBuffer = ShortBuffer.allocate(1024*100);
if (!disableRx && !_isLoopback)
new Thread(this).start();
}
private void constructSystemAudioDevices(boolean disableRx, int sampleRate, int audioSource, int audioPlaybackUsage) {
int audioRecorderMinBufferSize = 10 * AudioRecord.getMinBufferSize(
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT);
_systemAudioRecorder = new AudioRecord(
audioSource,
sampleRate,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
audioRecorderMinBufferSize);
int audioPlayerMinBufferSize = 10 * AudioTrack.getMinBufferSize(
sampleRate,
AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT);
if (!disableRx)
_systemAudioRecorder.startRecording();
_systemAudioPlayer = new AudioTrack.Builder()
.setAudioAttributes(new AudioAttributes.Builder()
.setUsage(audioPlaybackUsage)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build())
.setAudioFormat(new AudioFormat.Builder()
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(sampleRate)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build())
.setTransferMode(AudioTrack.MODE_STREAM)
.setBufferSizeInBytes(audioPlayerMinBufferSize)
.build();
Log.i(TAG, "Play buffer size " + audioPlayerMinBufferSize + ", recorder " + audioRecorderMinBufferSize);
}
protected int read(short[] sampleBuffer, int samplesToRead) {
synchronized (_recordAudioSampleBuffer) {
if (_recordAudioSampleBuffer.position() >= samplesToRead) {
_recordAudioSampleBuffer.flip();
_recordAudioSampleBuffer.get(sampleBuffer, 0, samplesToRead);
_recordAudioSampleBuffer.compact();
return samplesToRead;
}
}
return 0;
}
@Override
public void run() {
Log.i(TAG, "Starting receive thread");
android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);
int readSize = 32;
short [] sampleBuf = new short[readSize];
while (_isRunning) {
int readCnt = _systemAudioRecorder.read(sampleBuf, 0, readSize);
if (readCnt != readSize) {
Log.w(TAG, "" + readCnt + " != " + readSize);
continue;
}
synchronized (_recordAudioSampleBuffer) {
for (short sample : sampleBuf) {
try {
_recordAudioSampleBuffer.put(sample);
} catch (BufferOverflowException e) {
// user is probably transmitting and cannot consume, just discard
e.printStackTrace();
_recordAudioSampleBuffer.clear();
}
}
}
}
}
protected void stop() {
Log.i(TAG, "stop()");
_isRunning = false;
_systemAudioRecorder.stop();
_systemAudioPlayer.stop();
_systemAudioRecorder.release();
_systemAudioPlayer.release();
}
protected void pttPurge() {
if (_pttOffTimer != null) {
_pttOffTimer.cancel();
_pttOffTimer.purge();
_pttOffTimer = null;
}
}
protected void pttOn() {
pttPurge();
if (_isPttOn) return;
try {
_rigCtl.pttOn();
_isPttOn = true;
} catch (IOException e) {
e.printStackTrace();
}
}
protected void pttOff() {
pttPurge();
_pttOffTimer = new Timer();
_pttOffTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
_rigCtl.pttOff();
_isPttOn = false;
} catch (IOException e) {
e.printStackTrace();
}
}
}, _pttOffDelayMs);
}
}