kopia lustrzana https://github.com/sh123/codec2_talkie
Added RX/TX audio level indicators
rodzic
f7d1d18846
commit
0ea826fceb
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
Ładowanie…
Reference in New Issue