Added RX/TX audio level indicators

pull/14/head
sh123 2020-12-09 19:10:01 +02:00
rodzic f7d1d18846
commit 0ea826fceb
3 zmienionych plików z 98 dodań i 8 usunięć

Wyświetl plik

@ -2,6 +2,7 @@ package com.radio.codec2talkie;
import android.bluetooth.BluetoothSocket;
import android.media.AudioAttributes;
import android.media.AudioDeviceCallback;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.AudioTrack;
@ -27,9 +28,14 @@ public class Codec2Player extends Thread {
public static int PLAYER_LISTENING = 2;
public static int PLAYER_RECORDING = 3;
public static int PLAYER_PLAYING = 4;
public static int PLAYER_RX_LEVEL = 5;
public static int PLAYER_TX_LEVEL = 6;
private static int AUDIO_MIN_LEVEL = -70;
private static int AUDIO_HIGH_LEVEL = -15;
private final int AUDIO_SAMPLE_SIZE = 8000;
private final int SLEEP_DELAY_MS = 10;
private final int SLEEP_IDLE_DELAY_MS = 10;
private final int RX_TIMEOUT = 100;
private final int TX_TIMEOUT = 2000;
@ -126,6 +132,14 @@ public class Codec2Player extends Thread {
setCodecModeInternal(codecMode);
}
public static int getAudioMinLevel() {
return AUDIO_MIN_LEVEL;
}
public static int getAudioHighLevel() {
return AUDIO_HIGH_LEVEL;
}
public void startPlayback() {
_isRecording = false;
}
@ -173,12 +187,33 @@ public class Codec2Player extends Thread {
@Override
protected void receiveFrame(byte[] frame) {
Codec2.decode(_codec2Con, _playbackAudioBuffer, frame);
notifyAudioLevel(_playbackAudioBuffer, false);
_audioPlayer.write(_playbackAudioBuffer, 0, _audioBufferSize);
}
};
private void notifyAudioLevel(short [] audioSamples, boolean isTx) {
double db = getAudioMinLevel();
if (audioSamples != null) {
double acc = 0;
for (short v : audioSamples) {
acc += Math.abs(v);
}
double avg = acc / audioSamples.length;
db = (20.0 * Math.log10(avg / 32768.0));
}
Message msg = Message.obtain();
if (isTx)
msg.what = PLAYER_TX_LEVEL;
else
msg.what = PLAYER_RX_LEVEL;
msg.arg1 = (int)db;
_onPlayerStateChanged.sendMessage(msg);
}
private void processRecording() throws IOException {
_audioRecorder.read(_recordAudioBuffer, 0, _audioBufferSize);
notifyAudioLevel(_recordAudioBuffer, true);
Codec2.encode(_codec2Con, _recordAudioBuffer, _recordAudioEncodedBuffer);
byte [] frame = new byte[_recordAudioEncodedBuffer.length];
@ -227,6 +262,7 @@ public class Codec2Player extends Thread {
_audioRecorder.startRecording();
_audioPlayer.stop();
_loopbackBuffer.clear();
notifyAudioLevel(null, false);
}
// recording -> playback
if (!_isRecording && _audioRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
@ -234,6 +270,7 @@ public class Codec2Player extends Thread {
_audioPlayer.play();
_kissProcessor.flush();
_loopbackBuffer.flip();
notifyAudioLevel(null, true);
}
}
@ -290,15 +327,22 @@ public class Codec2Player extends Thread {
while (true) {
processRecordPlaybackToggle();
// recording
if (_audioRecorder.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
processRecording();
setStatus(PLAYER_RECORDING);
} else {
// playback
if (processPlayback()) {
setStatus(PLAYER_PLAYING);
// idling
} else {
try {
Thread.sleep(SLEEP_DELAY_MS);
Thread.sleep(SLEEP_IDLE_DELAY_MS);
if (_currentStatus != PLAYER_LISTENING) {
notifyAudioLevel(null, false);
notifyAudioLevel(null, true);
}
setStatus(PLAYER_LISTENING);
} catch (InterruptedException e) {
e.printStackTrace();

Wyświetl plik

@ -8,6 +8,8 @@ import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@ -18,6 +20,7 @@ import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
@ -48,6 +51,8 @@ public class MainActivity extends AppCompatActivity {
private TextView _textConnInfo;
private TextView _textStatus;
private ProgressBar _progressRxLevel;
private ProgressBar _progressTxLevel;
private Codec2Player _codec2Player;
@ -59,6 +64,10 @@ public class MainActivity extends AppCompatActivity {
_textConnInfo = findViewById(R.id.textBtName);
_textStatus = findViewById(R.id.textStatus);
_progressRxLevel = findViewById(R.id.progressRxLevel);
_progressRxLevel.setMax(-Codec2Player.getAudioMinLevel());
_progressTxLevel = findViewById(R.id.progressTxLevel);
_progressTxLevel.setMax(-Codec2Player.getAudioMinLevel());
Button btnPtt = findViewById(R.id.btnPtt);
btnPtt.setOnTouchListener(onBtnPttTouchListener);
@ -103,6 +112,15 @@ public class MainActivity extends AppCompatActivity {
return true;
}
private int colorFromAudioLevel(int audioLevel) {
int color = Color.GREEN;
if (audioLevel > Codec2Player.getAudioHighLevel())
color = Color.RED;
else if (audioLevel == Codec2Player.getAudioMinLevel())
color = Color.LTGRAY;
return color;
}
private final CompoundButton.OnCheckedChangeListener onLoopbackCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
@ -183,6 +201,14 @@ public class MainActivity extends AppCompatActivity {
else if (msg.what == Codec2Player.PLAYER_PLAYING) {
_textStatus.setText("RX");
}
else if (msg.what == Codec2Player.PLAYER_RX_LEVEL) {
_progressRxLevel.getProgressDrawable().setColorFilter(colorFromAudioLevel(msg.arg1), PorterDuff.Mode.SRC_IN);
_progressRxLevel.setProgress(msg.arg1 - Codec2Player.getAudioMinLevel());
}
else if (msg.what == Codec2Player.PLAYER_TX_LEVEL) {
_progressTxLevel.getProgressDrawable().setColorFilter(colorFromAudioLevel((msg.arg1)), PorterDuff.Mode.SRC_IN);
_progressTxLevel.setProgress(msg.arg1 - Codec2Player.getAudioMinLevel());
}
}
};

Wyświetl plik

@ -11,19 +11,19 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Connected bluetooth"
android:layout_marginTop="8dp"
android:text="Device"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toBottomOf="@+id/progressRxLevel" />
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btnPtt"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="32dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@drawable/btn_ptt"
@ -61,10 +61,30 @@
android:id="@+id/textStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:text="Disconnected"
android:text="Status"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressRxLevel" />
<ProgressBar
android:id="@+id/progressRxLevel"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="11dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressTxLevel" />
<ProgressBar
android:id="@+id/progressTxLevel"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>