1
1
package com .radio .codec2talkie ;
2
2
3
+ import android .bluetooth .BluetoothAdapter ;
4
+ import android .bluetooth .BluetoothDevice ;
3
5
import android .bluetooth .BluetoothSocket ;
6
+ import android .content .BroadcastReceiver ;
7
+ import android .content .Context ;
8
+ import android .content .Intent ;
9
+ import android .content .IntentFilter ;
4
10
import android .media .AudioAttributes ;
5
11
import android .media .AudioFormat ;
6
12
import android .media .AudioRecord ;
7
13
import android .media .AudioTrack ;
8
14
import android .media .MediaRecorder ;
9
15
import android .os .Handler ;
10
16
import android .os .Message ;
17
+ import android .util .Log ;
11
18
12
19
import java .io .IOException ;
13
20
import java .io .InputStream ;
@@ -36,6 +43,7 @@ public class Codec2Player extends Thread {
36
43
37
44
private final int AUDIO_SAMPLE_SIZE = 8000 ;
38
45
private final int SLEEP_IDLE_DELAY_MS = 20 ;
46
+ private final int POST_PLAY_DELAY_MS = 1000 ;
39
47
40
48
private final int RX_TIMEOUT = 100 ;
41
49
private final int TX_TIMEOUT = 2000 ;
@@ -52,7 +60,9 @@ public class Codec2Player extends Thread {
52
60
private UsbSerialPort _usbPort ;
53
61
54
62
private int _audioBufferSize ;
63
+ private int _audioEncodedBufferSize ;
55
64
65
+ private boolean _isRunning = true ;
56
66
private boolean _isRecording = false ;
57
67
private int _currentStatus = PLAYER_DISCONNECT ;
58
68
@@ -96,7 +106,7 @@ public Codec2Player(Handler onPlayerStateChanged, int codec2Mode) {
96
106
AUDIO_SAMPLE_SIZE ,
97
107
AudioFormat .CHANNEL_IN_MONO ,
98
108
AudioFormat .ENCODING_PCM_16BIT ,
99
- 3 * _audioRecorderMinBufferSize );
109
+ 10 * _audioRecorderMinBufferSize );
100
110
_audioRecorder .startRecording ();
101
111
102
112
int _audioPlayerMinBufferSize = AudioTrack .getMinBufferSize (
@@ -106,15 +116,15 @@ public Codec2Player(Handler onPlayerStateChanged, int codec2Mode) {
106
116
_audioPlayer = new AudioTrack .Builder ()
107
117
.setAudioAttributes (new AudioAttributes .Builder ()
108
118
.setUsage (AudioAttributes .USAGE_MEDIA )
109
- .setContentType (AudioAttributes .CONTENT_TYPE_MUSIC )
119
+ .setContentType (AudioAttributes .CONTENT_TYPE_SPEECH )
110
120
.build ())
111
121
.setAudioFormat (new AudioFormat .Builder ()
112
122
.setEncoding (AudioFormat .ENCODING_PCM_16BIT )
113
123
.setSampleRate (AUDIO_SAMPLE_SIZE )
114
124
.setChannelMask (AudioFormat .CHANNEL_OUT_MONO )
115
125
.build ())
116
126
.setTransferMode (AudioTrack .MODE_STREAM )
117
- .setBufferSizeInBytes (3 * _audioPlayerMinBufferSize )
127
+ .setBufferSizeInBytes (10 * _audioPlayerMinBufferSize )
118
128
.build ();
119
129
_audioPlayer .play ();
120
130
}
@@ -154,11 +164,15 @@ public void startRecording() {
154
164
_isRecording = true ;
155
165
}
156
166
167
+ public void stopRunning () {
168
+ _isRunning = false ;
169
+ }
170
+
157
171
private void setCodecModeInternal (int codecMode ) {
158
172
_codec2Con = Codec2 .create (codecMode );
159
173
160
174
_audioBufferSize = Codec2 .getSamplesPerFrame (_codec2Con );
161
- int _audioEncodedBufferSize = Codec2 .getBitsSize (_codec2Con ); // returns number of bytes
175
+ _audioEncodedBufferSize = Codec2 .getBitsSize (_codec2Con ); // returns number of bytes
162
176
163
177
_recordAudioBuffer = new short [_audioBufferSize ];
164
178
_recordAudioEncodedBuffer = new char [_audioEncodedBufferSize ];
@@ -167,12 +181,7 @@ private void setCodecModeInternal(int codecMode) {
167
181
168
182
_loopbackBuffer = ByteBuffer .allocateDirect (1024 * _audioEncodedBufferSize );
169
183
170
- _kissProcessor = new KissProcessor (
171
- _audioEncodedBufferSize ,
172
- CSMA_PERSISTENCE ,
173
- CSMA_SLOT_TIME ,
174
- TX_TAIL_10MS_UNITS ,
175
- _kissCallback );
184
+ _kissProcessor = new KissProcessor (CSMA_PERSISTENCE , CSMA_SLOT_TIME , TX_TAIL_10MS_UNITS , _kissCallback );
176
185
}
177
186
178
187
private final KissCallback _kissCallback = new KissCallback () {
@@ -182,8 +191,14 @@ protected void onSend(byte[] data) throws IOException {
182
191
}
183
192
184
193
@ Override
185
- protected void onReceive (byte [] audioData ) {
186
- decodeAndPlayAudio (audioData );
194
+ protected void onReceive (byte [] data ) {
195
+ // split by audio frame and play
196
+ byte [] audioFrame = new byte [_audioEncodedBufferSize ];
197
+ for (int i = 0 ; i < data .length ; i += _audioEncodedBufferSize ) {
198
+ for (int j = 0 ; j < _audioEncodedBufferSize ; j ++)
199
+ audioFrame [j ] = data [i + j ];
200
+ decodeAndPlayAudio (audioFrame );
201
+ }
187
202
}
188
203
};
189
204
@@ -197,16 +212,16 @@ private void sendRawDataToModem(byte[] data) throws IOException {
197
212
} else {
198
213
if (_btOutputStream != null )
199
214
_btOutputStream .write (data );
200
- if (_usbPort != null ) {
215
+ else if (_usbPort != null ) {
201
216
_usbPort .write (data , TX_TIMEOUT );
202
217
}
203
218
}
204
219
}
205
220
206
221
private void decodeAndPlayAudio (byte [] data ) {
207
- notifyAudioLevel (_playbackAudioBuffer , false );
208
222
Codec2 .decode (_codec2Con , _playbackAudioBuffer , data );
209
223
_audioPlayer .write (_playbackAudioBuffer , 0 , _audioBufferSize );
224
+ notifyAudioLevel (_playbackAudioBuffer , false );
210
225
}
211
226
212
227
private void notifyAudioLevel (short [] pcmAudioSamples , boolean isTx ) {
@@ -240,9 +255,10 @@ private boolean processLoopbackPlayback() {
240
255
}
241
256
242
257
private void recordAudio () throws IOException {
258
+ setStatus (PLAYER_RECORDING , 0 );
259
+ notifyAudioLevel (_recordAudioBuffer , true );
243
260
_audioRecorder .read (_recordAudioBuffer , 0 , _audioBufferSize );
244
261
Codec2 .encode (_codec2Con , _recordAudioBuffer , _recordAudioEncodedBuffer );
245
- notifyAudioLevel (_recordAudioBuffer , true );
246
262
247
263
byte [] frame = new byte [_recordAudioEncodedBuffer .length ];
248
264
@@ -263,10 +279,11 @@ private boolean playAudio() throws IOException {
263
279
bytesRead = _btInputStream .read (_rxDataBuffer );
264
280
}
265
281
}
266
- if (_usbPort != null ) {
282
+ else if (_usbPort != null ) {
267
283
bytesRead = _usbPort .read (_rxDataBuffer , RX_TIMEOUT );
268
284
}
269
285
if (bytesRead > 0 ) {
286
+ setStatus (PLAYER_PLAYING , 0 );
270
287
_kissProcessor .receive (Arrays .copyOf (_rxDataBuffer , bytesRead ));
271
288
return true ;
272
289
}
@@ -315,42 +332,40 @@ private void cleanup() {
315
332
Codec2 .destroy (_codec2Con );
316
333
}
317
334
318
- private void setStatus (int status ) {
335
+ private void setStatus (int status , int delayMs ) {
319
336
if (status != _currentStatus ) {
320
337
_currentStatus = status ;
321
338
Message msg = Message .obtain ();
322
339
msg .what = status ;
323
- _onPlayerStateChanged .sendMessage (msg );
340
+ _onPlayerStateChanged .sendMessageDelayed (msg , delayMs );
324
341
}
325
342
}
326
343
327
344
@ Override
328
345
public void run () {
329
346
setPriority (Thread .MAX_PRIORITY );
330
347
try {
348
+ setStatus (PLAYER_LISTENING , 0 );
331
349
if (!_isLoopbackMode ) {
332
350
_kissProcessor .initialize ();
333
351
}
334
- while (true ) {
352
+ while (_isRunning ) {
335
353
processRecordPlaybackToggle ();
336
354
337
355
// recording
338
356
if (_audioRecorder .getRecordingState () == AudioRecord .RECORDSTATE_RECORDING ) {
339
357
recordAudio ();
340
- setStatus (PLAYER_RECORDING );
341
358
} else {
342
359
// playback
343
- if (playAudio ()) {
344
- setStatus (PLAYER_PLAYING );
345
- // idling
346
- } else {
360
+ if (!playAudio ()) {
361
+ // idling
347
362
try {
348
- Thread .sleep (SLEEP_IDLE_DELAY_MS );
349
363
if (_currentStatus != PLAYER_LISTENING ) {
350
364
notifyAudioLevel (null , false );
351
365
notifyAudioLevel (null , true );
352
366
}
353
- setStatus (PLAYER_LISTENING );
367
+ setStatus (PLAYER_LISTENING , POST_PLAY_DELAY_MS );
368
+ Thread .sleep (SLEEP_IDLE_DELAY_MS );
354
369
} catch (InterruptedException e ) {
355
370
e .printStackTrace ();
356
371
}
@@ -361,7 +376,7 @@ public void run() {
361
376
e .printStackTrace ();
362
377
}
363
378
379
+ setStatus (PLAYER_DISCONNECT , 0 );
364
380
cleanup ();
365
- setStatus (PLAYER_DISCONNECT );
366
381
}
367
382
}
0 commit comments