kopia lustrzana https://github.com/sh123/codec2_talkie
Sound modem
rodzic
edaec455fe
commit
cddc47ed85
|
@ -255,9 +255,9 @@ public class MainActivity extends AppCompatActivity implements ServiceConnection
|
|||
_textConnInfo.setText(R.string.main_status_loopback_test);
|
||||
startAppService(TransportFactory.TransportType.LOOPBACK);
|
||||
} else if (requestPermissions()) {
|
||||
if (_sharedPreferences.getBoolean(PreferenceKeys.PORTS_AUDIO_ENABLED, false)) {
|
||||
if (_sharedPreferences.getBoolean(PreferenceKeys.PORTS_SOUND_MODEM_ENABLED, false)) {
|
||||
_textConnInfo.setText(R.string.main_status_sound_modem);
|
||||
startAppService(TransportFactory.TransportType.AUDIO);
|
||||
startAppService(TransportFactory.TransportType.SOUND_MODEM);
|
||||
} else if (_sharedPreferences.getBoolean(PreferenceKeys.PORTS_TCP_IP_ENABLED, false)) {
|
||||
startTcpIpConnectActivity();
|
||||
} else {
|
||||
|
|
|
@ -87,7 +87,7 @@ public class AppWorker extends Thread {
|
|||
String codec2ModeName = _sharedPreferences.getString(PreferenceKeys.CODEC2_MODE, _context.getResources().getStringArray(R.array.codec2_modes)[0]);
|
||||
_codec2Mode = AudioTools.extractCodec2ModeId(codec2ModeName);
|
||||
|
||||
_transport = TransportFactory.create(transportType);
|
||||
_transport = TransportFactory.create(transportType, context);
|
||||
_protocol = ProtocolFactory.create(_codec2Mode, context);
|
||||
|
||||
_processPeriodicTimer = new Timer();
|
||||
|
|
|
@ -66,11 +66,11 @@ public class AudioFrameAggregator implements Protocol {
|
|||
public void sendCompressedAudio(String src, String dst, int codec2Mode, byte[] frame) throws IOException {
|
||||
if ( _outputBufferPos + frame.length >= _outputBufferSize) {
|
||||
_childProtocol.sendCompressedAudio(src, dst, codec2Mode, Arrays.copyOf(_outputBuffer, _outputBufferPos));
|
||||
_lastSrc = src;
|
||||
_lastDst = dst;
|
||||
_lastCodec2Mode = codec2Mode;
|
||||
_outputBufferPos = 0;
|
||||
}
|
||||
_lastSrc = src;
|
||||
_lastDst = dst;
|
||||
_lastCodec2Mode = codec2Mode;
|
||||
System.arraycopy(frame, 0, _outputBuffer, _outputBufferPos, frame.length);
|
||||
_outputBufferPos += frame.length;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ public final class PreferenceKeys {
|
|||
public static String PORTS_TCP_IP_RETRY_COUNT = "ports_tcp_ip_retry_count";
|
||||
public static String PORTS_TCP_IP_RETRY_DELAY = "ports_tcp_ip_retry_delay";
|
||||
|
||||
public static String PORTS_AUDIO_ENABLED = "ports_audio_enabled";
|
||||
public static String PORTS_SOUND_MODEM_ENABLED = "ports_sound_modem_enable";
|
||||
|
||||
public static String CODEC2_MODE = "codec2_mode";
|
||||
public static String CODEC2_TEST_MODE = "codec2_test_mode";
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
package com.radio.codec2talkie.transport;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class Audio implements Transport {
|
||||
|
||||
private final String _name;
|
||||
|
||||
public Audio(String name) {
|
||||
_name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] data) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(byte[] data) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
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.provider.MediaStore;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.radio.codec2talkie.settings.PreferenceKeys;
|
||||
import com.ustadmobile.codec2.Codec2;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SoundModem implements Transport {
|
||||
|
||||
private static final int AUDIO_SAMPLE_SIZE = 8000;
|
||||
|
||||
private final String _name;
|
||||
|
||||
private AudioTrack _systemAudioPlayer;
|
||||
private AudioRecord _systemAudioRecorder;
|
||||
|
||||
private final short[] _recordAudioBuffer;
|
||||
private final byte[] _recordBitBuffer;
|
||||
private final short[] _playbackAudioBuffer;
|
||||
private final byte[] _playbackBitBuffer;
|
||||
|
||||
private final Context _context;
|
||||
private final SharedPreferences _sharedPreferences;
|
||||
|
||||
private final long _fskModem;
|
||||
|
||||
public SoundModem(String name, Context context) {
|
||||
_name = name;
|
||||
|
||||
_context = context;
|
||||
_sharedPreferences = PreferenceManager.getDefaultSharedPreferences(_context);
|
||||
|
||||
_fskModem = Codec2.fskCreate(AUDIO_SAMPLE_SIZE, 300, 1600, 200);
|
||||
|
||||
_recordAudioBuffer = new short[Codec2.fskDemodSamplesBufSize(_fskModem)];
|
||||
_recordBitBuffer = new byte[Codec2.fskDemodBitsBufSize(_fskModem)];
|
||||
_playbackAudioBuffer = new short[Codec2.fskModSamplesBufSize(_fskModem)];
|
||||
_playbackBitBuffer = new byte[Codec2.fskModBitsBufSize(_fskModem)];
|
||||
|
||||
constructSystemAudioDevices();
|
||||
}
|
||||
|
||||
private void constructSystemAudioDevices() {
|
||||
int _audioRecorderMinBufferSize = AudioRecord.getMinBufferSize(
|
||||
AUDIO_SAMPLE_SIZE,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT);
|
||||
int audioSource = MediaRecorder.AudioSource.MIC;
|
||||
_systemAudioRecorder = new AudioRecord(
|
||||
audioSource,
|
||||
AUDIO_SAMPLE_SIZE,
|
||||
AudioFormat.CHANNEL_IN_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
10 * _audioRecorderMinBufferSize);
|
||||
|
||||
int _audioPlayerMinBufferSize = AudioTrack.getMinBufferSize(
|
||||
AUDIO_SAMPLE_SIZE,
|
||||
AudioFormat.CHANNEL_OUT_MONO,
|
||||
AudioFormat.ENCODING_PCM_16BIT);
|
||||
|
||||
int usage = AudioAttributes.USAGE_MEDIA;
|
||||
_systemAudioPlayer = new AudioTrack.Builder()
|
||||
.setAudioAttributes(new AudioAttributes.Builder()
|
||||
.setUsage(usage)
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
|
||||
.build())
|
||||
.setAudioFormat(new AudioFormat.Builder()
|
||||
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
|
||||
.setSampleRate(AUDIO_SAMPLE_SIZE)
|
||||
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
|
||||
.build())
|
||||
.setTransferMode(AudioTrack.MODE_STREAM)
|
||||
.setBufferSizeInBytes(10 * _audioPlayerMinBufferSize)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] data) throws IOException {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(byte[] data) throws IOException {
|
||||
Codec2.fskModulate(_fskModem, _playbackAudioBuffer, _playbackBitBuffer);
|
||||
_systemAudioPlayer.write(_playbackAudioBuffer, 0, _playbackAudioBuffer.length);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
Codec2.fskDestroy(_fskModem);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package com.radio.codec2talkie.transport;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.radio.codec2talkie.connect.BleHandler;
|
||||
import com.radio.codec2talkie.connect.BluetoothSocketHandler;
|
||||
import com.radio.codec2talkie.connect.TcpIpSocketHandler;
|
||||
|
@ -15,10 +17,10 @@ public class TransportFactory {
|
|||
LOOPBACK,
|
||||
TCP_IP,
|
||||
BLE,
|
||||
AUDIO
|
||||
SOUND_MODEM
|
||||
};
|
||||
|
||||
public static Transport create(TransportType transportType) throws IOException {
|
||||
public static Transport create(TransportType transportType, Context context) throws IOException {
|
||||
switch (transportType) {
|
||||
case USB:
|
||||
return new UsbSerial(UsbPortHandler.getPort(), UsbPortHandler.getName());
|
||||
|
@ -28,8 +30,8 @@ public class TransportFactory {
|
|||
return new TcpIp(TcpIpSocketHandler.getSocket(), TcpIpSocketHandler.getName());
|
||||
case BLE:
|
||||
return new Ble(BleHandler.getGatt(), BleHandler.getName());
|
||||
case AUDIO:
|
||||
return new Audio("SoundModem");
|
||||
case SOUND_MODEM:
|
||||
return new SoundModem("SoundModem", context);
|
||||
case LOOPBACK:
|
||||
default:
|
||||
return new Loopback();
|
||||
|
|
|
@ -291,7 +291,7 @@
|
|||
<string name="log_item_group_holder_unknown_km">\? km</string>
|
||||
<string name="log_view_group_item_icon_description">APRS icon</string>
|
||||
<string name="settings_aprs_symbol_image_view_description">APRS symbols</string>
|
||||
<string name="ports_audio_enable_title">Enable sound modem</string>
|
||||
<string name="ports_audio_enable_summary">Send data and receive data through sound modem by using phone speaker and mic</string>
|
||||
<string name="ports_sound_modem_enable_title">Enable sound modem</string>
|
||||
<string name="ports_sound_modem_enable_summary">Send data and receive data through sound modem by using phone speaker and mic</string>
|
||||
<string name="main_status_sound_modem">SoundModem</string>
|
||||
</resources>
|
|
@ -142,9 +142,9 @@
|
|||
</Preference>
|
||||
|
||||
<SwitchPreference
|
||||
app:key="ports_audio_enable"
|
||||
app:title="@string/ports_audio_enable_title"
|
||||
app:summary="@string/ports_audio_enable_summary"
|
||||
app:key="ports_sound_modem_enable"
|
||||
app:title="@string/ports_sound_modem_enable_title"
|
||||
app:summary="@string/ports_sound_modem_enable_summary"
|
||||
app:defaultValue="false">
|
||||
</SwitchPreference>
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
|
|||
unsigned char *demodBits;
|
||||
int Nbits;
|
||||
int N;
|
||||
int Ts;
|
||||
};
|
||||
|
||||
static Context *getContext(jlong jp) {
|
||||
|
@ -64,6 +65,7 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
|
|||
|
||||
conFsk->Nbits = fsk->Nbits;
|
||||
conFsk->N = fsk->N;
|
||||
conFsk->Ts = fsk->Ts;
|
||||
|
||||
conFsk->modBuf = (float*)malloc(conFsk->N);
|
||||
conFsk->modBits = (uint8_t*)malloc(conFsk->Nbits);
|
||||
|
@ -86,6 +88,11 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
|
|||
return con->nbyte;
|
||||
}
|
||||
|
||||
static jint fskDemodSamplesBufSize(JNIEnv * env, jclass clazz, jlong n) {
|
||||
ContextFsk *conFsk = getContextFsk(n);
|
||||
return sizeof(short) * (conFsk->N + 2 * conFsk->Ts);
|
||||
}
|
||||
|
||||
static jint fskDemodBitsBufSize(JNIEnv * env, jclass clazz, jlong n) {
|
||||
ContextFsk *conFsk = getContextFsk(n);
|
||||
return sizeof(uint8_t) * conFsk->Nbits;
|
||||
|
@ -96,6 +103,11 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
|
|||
return conFsk->N;
|
||||
}
|
||||
|
||||
static jint fskModBitsBufSize(JNIEnv * env, jclass clazz, jlong n) {
|
||||
ContextFsk *conFsk = getContextFsk(n);
|
||||
return conFsk->Nbits;
|
||||
}
|
||||
|
||||
static jint destroy(JNIEnv *env, jclass clazz, jlong n) {
|
||||
Context *con = getContext(n);
|
||||
codec2_destroy(con->c2);
|
||||
|
@ -193,7 +205,9 @@ namespace Java_com_ustadmobile_codec2_Codec2 {
|
|||
{"fskModulate", "(J[S[B)J", (void *) fskModulate},
|
||||
{"fskDemodulate", "(J[S[B)J", (void *) fskDemodulate},
|
||||
{"fskDemodBitsBufSize","(J)I", (void *) fskDemodBitsBufSize},
|
||||
{"fskModSamplesBufSize","(J)I", (void *) fskModSamplesBufSize}
|
||||
{"fskModSamplesBufSize","(J)I", (void *) fskModSamplesBufSize},
|
||||
{"fskDemodSamplesBufSize","(J)I", (void *) fskDemodSamplesBufSize},
|
||||
{"fskModBitsBufSize", "(J)I", (void *) fskModBitsBufSize}
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -40,6 +40,8 @@ public class Codec2 {
|
|||
|
||||
public native static int fskDemodBitsBufSize(long conFsk);
|
||||
public native static int fskModSamplesBufSize(long conFsk);
|
||||
public native static int fskDemodSamplesBufSize(long conFsk);
|
||||
public native static int fskModBitsBufSize(long conFsk);
|
||||
|
||||
public native static long fskModulate(long conFsk, short[] outputSamples, byte[] inputBits);
|
||||
public native static long fskDemodulate(long conFsk, short[] inputSamples, byte[] outputBits);
|
||||
|
|
Ładowanie…
Reference in New Issue