1/*
2 * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license that can be found
5 * in the LICENSE file in the root of the source tree. An additional
6 * intellectual property rights grant can be found in the file PATENTS. All
7 * contributing project authors may be found in the AUTHORS file in the root of
8 * the source tree.
9 */
10
11/*
12 * VoiceEngine Android test application. It starts either auto test or acts like
13 * a GUI test.
14 */
15
16package org.webrtc.voiceengine.test;
17
18import java.io.File;
19import java.io.FileInputStream;
20import java.io.FileNotFoundException;
21import java.io.FileOutputStream;
22import java.io.FileReader;
23import java.io.IOException;
24
25import android.app.Activity;
26import android.content.Context;
27import android.media.AudioFormat;
28import android.media.AudioManager;
29import android.media.AudioRecord;
30import android.media.AudioTrack;
31import android.media.MediaRecorder;
32import android.os.Bundle;
33import android.util.Log;
34import android.view.View;
35import android.widget.AdapterView;
36import android.widget.ArrayAdapter;
37import android.widget.Button;
38import android.widget.EditText;
39import android.widget.Spinner;
40import android.widget.TextView;
41
42public class AndroidTest extends Activity {
43    private byte[] _playBuffer = null;
44    private short[] _circBuffer = new short[8000]; // can hold 50 frames
45
46    private int _recIndex = 0;
47    private int _playIndex = 0;
48    // private int _streamVolume = 4;
49    private int _maxVolume = 0; // Android max level (commonly 5)
50    // VoE level (0-255), corresponds to level 4 out of 5
51    private int _volumeLevel = 204;
52
53    private Thread _playThread;
54    private Thread _recThread;
55    private Thread _autotestThread;
56
57    private static AudioTrack _at;
58    private static AudioRecord _ar;
59
60    private File _fr = null;
61    private FileInputStream _in = null;
62
63    private boolean _isRunningPlay = false;
64    private boolean _isRunningRec = false;
65    private boolean _settingSet = true;
66    private boolean _isCallActive = false;
67    private boolean _runAutotest = false; // ENABLE AUTOTEST HERE!
68
69    private int _channel = -1;
70    private int _codecIndex = 0;
71    private int _ecIndex = 0;
72    private int _nsIndex = 0;
73    private int _agcIndex = 0;
74    private int _vadIndex = 0;
75    private int _audioIndex = 3;
76    private int _settingMenu = 0;
77    private int _receivePort = 1234;
78    private int _destinationPort = 1234;
79    private String _destinationIP = "127.0.0.1";
80
81    // "Build" settings
82    private final boolean _playFromFile = false;
83    // Set to true to send data to native code and back
84    private final boolean _runThroughNativeLayer = true;
85    private final boolean enableSend = true;
86    private final boolean enableReceive = true;
87    private final boolean useNativeThread = false;
88
89    /** Called when the activity is first created. */
90    public void onCreate(Bundle savedInstanceState) {
91        super.onCreate(savedInstanceState);
92        setContentView(R.layout.main);
93
94        TextView tv = (TextView) findViewById(R.id.TextView01);
95        tv.setText("");
96
97        final EditText ed = (EditText) findViewById(R.id.EditText01);
98        ed.setWidth(200);
99        ed.setText(_destinationIP);
100
101        final Button buttonStart = (Button) findViewById(R.id.Button01);
102        buttonStart.setWidth(200);
103        if (_runAutotest) {
104            buttonStart.setText("Run test");
105        } else {
106            buttonStart.setText("Start Call");
107        }
108        // button.layout(50, 50, 100, 40);
109        buttonStart.setOnClickListener(new View.OnClickListener() {
110            public void onClick(View v) {
111
112                if (_runAutotest) {
113                    startAutoTest();
114                } else {
115                    if (_isCallActive) {
116
117                        if (stopCall() != -1) {
118                            _isCallActive = false;
119                            buttonStart.setText("Start Call");
120                        }
121                    } else {
122
123                        _destinationIP = ed.getText().toString();
124                        if (startCall() != -1) {
125                            _isCallActive = true;
126                            buttonStart.setText("Stop Call");
127                        }
128                    }
129                }
130
131                // displayTextFromFile();
132                // recordAudioToFile();
133                // if(!_playFromFile)
134                // {
135                // recAudioInThread();
136                // }
137                // playAudioInThread();
138            }
139        });
140
141        final Button buttonStop = (Button) findViewById(R.id.Button02);
142        buttonStop.setWidth(200);
143        buttonStop.setText("Close app");
144        buttonStop.setOnClickListener(new View.OnClickListener() {
145            public void onClick(View v) {
146
147                if (!_runAutotest) {
148                    ShutdownVoE();
149                }
150
151                // This call terminates and should close the activity
152                finish();
153
154                // playAudioFromFile();
155                // if(!_playFromFile)
156                // {
157                // stopRecAudio();
158                // }
159                // stopPlayAudio();
160            }
161        });
162
163
164        String ap1[] = {"EC off", "AECM"};
165        final ArrayAdapter<String> adapterAp1 = new ArrayAdapter<String>(
166                        this,
167                        android.R.layout.simple_spinner_dropdown_item,
168                        ap1);
169        String ap2[] =
170                        {"NS off", "NS low", "NS moderate", "NS high",
171                                        "NS very high"};
172        final ArrayAdapter<String> adapterAp2 = new ArrayAdapter<String>(
173                        this,
174                        android.R.layout.simple_spinner_dropdown_item,
175                        ap2);
176        String ap3[] = {"AGC off", "AGC adaptive", "AGC fixed"};
177        final ArrayAdapter<String> adapterAp3 = new ArrayAdapter<String>(
178                        this,
179                        android.R.layout.simple_spinner_dropdown_item,
180                        ap3);
181        String ap4[] =
182                        {"VAD off", "VAD conventional", "VAD high rate",
183                                        "VAD mid rate", "VAD low rate"};
184        final ArrayAdapter<String> adapterAp4 = new ArrayAdapter<String>(
185                        this,
186                        android.R.layout.simple_spinner_dropdown_item,
187                        ap4);
188        String codecs[] = {"iSAC", "PCMU", "PCMA", "iLBC"};
189        final ArrayAdapter<String> adapterCodecs = new ArrayAdapter<String>(
190                        this,
191                        android.R.layout.simple_spinner_dropdown_item,
192                        codecs);
193
194        final Spinner spinnerSettings1 = (Spinner) findViewById(R.id.Spinner01);
195        final Spinner spinnerSettings2 = (Spinner) findViewById(R.id.Spinner02);
196        spinnerSettings1.setMinimumWidth(200);
197        String settings[] =
198                        {"Codec", "Echo Control", "Noise Suppression",
199                         "Automatic Gain Control",
200                         "Voice Activity Detection"};
201        ArrayAdapter<String> adapterSettings1 = new ArrayAdapter<String>(
202                        this,
203                        android.R.layout.simple_spinner_dropdown_item,
204                        settings);
205        spinnerSettings1.setAdapter(adapterSettings1);
206        spinnerSettings1.setOnItemSelectedListener(
207                        new AdapterView.OnItemSelectedListener() {
208            public void onItemSelected(AdapterView adapterView, View view,
209                            int position, long id) {
210
211                _settingMenu = position;
212                _settingSet = false;
213                if (position == 0) {
214                    spinnerSettings2.setAdapter(adapterCodecs);
215                    spinnerSettings2.setSelection(_codecIndex);
216                }
217                if (position == 1) {
218                    spinnerSettings2.setAdapter(adapterAp1);
219                    spinnerSettings2.setSelection(_ecIndex);
220                }
221                if (position == 2) {
222                    spinnerSettings2.setAdapter(adapterAp2);
223                    spinnerSettings2.setSelection(_nsIndex);
224                }
225                if (position == 3) {
226                    spinnerSettings2.setAdapter(adapterAp3);
227                    spinnerSettings2.setSelection(_agcIndex);
228                }
229                if (position == 4) {
230                    spinnerSettings2.setAdapter(adapterAp4);
231                    spinnerSettings2.setSelection(_vadIndex);
232                }
233            }
234
235            public void onNothingSelected(AdapterView adapterView) {
236                WebrtcLog("No setting1 selected");
237            }
238        });
239
240        spinnerSettings2.setMinimumWidth(200);
241        ArrayAdapter<String> adapterSettings2 = new ArrayAdapter<String>(
242                        this,
243                        android.R.layout.simple_spinner_dropdown_item,
244                        codecs);
245        spinnerSettings2.setAdapter(adapterSettings2);
246        spinnerSettings2.setOnItemSelectedListener(
247                        new AdapterView.OnItemSelectedListener() {
248            public void onItemSelected(AdapterView adapterView, View view,
249                            int position, long id) {
250
251                // avoid unintentional setting
252                if (_settingSet == false) {
253                    _settingSet = true;
254                    return;
255                }
256
257                // Change volume
258                if (_settingMenu == 0) {
259                    WebrtcLog("Selected audio " + position);
260                    setAudioProperties(position);
261                    spinnerSettings2.setSelection(_audioIndex);
262                }
263
264                // Change codec
265                if (_settingMenu == 1) {
266                    _codecIndex = position;
267                    WebrtcLog("Selected codec " + position);
268                    if (0 != SetSendCodec(_channel, _codecIndex)) {
269                        WebrtcLog("VoE set send codec failed");
270                    }
271                }
272
273                // Change EC
274                if (_settingMenu == 2) {
275                    boolean enable = true;
276                    int ECmode = 5; // AECM
277                    int AESmode = 0;
278
279                    _ecIndex = position;
280                    WebrtcLog("Selected EC " + position);
281
282                    if (position == 0) {
283                        enable = false;
284                    }
285                    if (position > 1) {
286                        ECmode = 4; // AES
287                        AESmode = position - 1;
288                    }
289
290                    if (0 != SetECStatus(enable, ECmode)) {
291                        WebrtcLog("VoE set EC status failed");
292                    }
293                }
294
295                // Change NS
296                if (_settingMenu == 3) {
297                    boolean enable = true;
298
299                    _nsIndex = position;
300                    WebrtcLog("Selected NS " + position);
301
302                    if (position == 0) {
303                        enable = false;
304                    }
305                    if (0 != SetNSStatus(enable, position + 2)) {
306                        WebrtcLog("VoE set NS status failed");
307                    }
308                }
309
310                // Change AGC
311                if (_settingMenu == 4) {
312                    boolean enable = true;
313
314                    _agcIndex = position;
315                    WebrtcLog("Selected AGC " + position);
316
317                    if (position == 0) {
318                        enable = false;
319                        position = 1; // default
320                    }
321                    if (0 != SetAGCStatus(enable, position + 2)) {
322                        WebrtcLog("VoE set AGC status failed");
323                    }
324                }
325
326                // Change VAD
327                if (_settingMenu == 5) {
328                    boolean enable = true;
329
330                    _vadIndex = position;
331                    WebrtcLog("Selected VAD " + position);
332
333                    if (position == 0) {
334                        enable = false;
335                        position++;
336                    }
337                    if (0 != SetVADStatus(_channel, enable, position - 1)) {
338                        WebrtcLog("VoE set VAD status failed");
339                    }
340                }
341            }
342
343            public void onNothingSelected(AdapterView adapterView) {
344            }
345        });
346
347        // Setup VoiceEngine
348        if (!_runAutotest && !useNativeThread) SetupVoE();
349
350        // Suggest to use the voice call audio stream for hardware volume
351        // controls
352        setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
353
354        // Get max Android volume and adjust default volume to map exactly to an
355        // Android level
356        AudioManager am =
357                        (AudioManager) getSystemService(Context.AUDIO_SERVICE);
358        _maxVolume = am.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL);
359        if (_maxVolume <= 0) {
360            WebrtcLog("Could not get max volume!");
361        } else {
362            int androidVolumeLevel = (_volumeLevel * _maxVolume) / 255;
363            _volumeLevel = (androidVolumeLevel * 255) / _maxVolume;
364        }
365
366        WebrtcLog("Started Webrtc Android Test");
367    }
368
369    // Will be called when activity is shutdown.
370    // NOTE: Activity may be killed without this function being called,
371    // but then we should not need to clean up.
372    protected void onDestroy() {
373        super.onDestroy();
374        // ShutdownVoE();
375    }
376
377    private void SetupVoE() {
378        // Create VoiceEngine
379        Create(); // Error logging is done in native API wrapper
380
381        // Initialize
382        if (0 != Init(false, false)) {
383            WebrtcLog("VoE init failed");
384        }
385
386        // Create channel
387        _channel = CreateChannel();
388        if (0 != _channel) {
389            WebrtcLog("VoE create channel failed");
390        }
391
392    }
393
394    private void ShutdownVoE() {
395        // Delete channel
396        if (0 != DeleteChannel(_channel)) {
397            WebrtcLog("VoE delete channel failed");
398        }
399
400        // Terminate
401        if (0 != Terminate()) {
402            WebrtcLog("VoE terminate failed");
403        }
404
405        // Delete VoiceEngine
406        Delete(); // Error logging is done in native API wrapper
407    }
408
409    int startCall() {
410
411        if (useNativeThread == true) {
412
413            Create();
414            return 0;
415        }
416
417        if (enableReceive == true) {
418            // Set local receiver
419            if (0 != SetLocalReceiver(_channel, _receivePort)) {
420                WebrtcLog("VoE set local receiver failed");
421            }
422
423            if (0 != StartListen(_channel)) {
424                WebrtcLog("VoE start listen failed");
425                return -1;
426            }
427
428            // Route audio to earpiece
429            if (0 != SetLoudspeakerStatus(false)) {
430                WebrtcLog("VoE set louspeaker status failed");
431                return -1;
432            }
433
434            /*
435             * WebrtcLog("VoE start record now"); if (0 !=
436             * StartRecordingPlayout(_channel, "/sdcard/singleUserDemoOut.pcm",
437             * false)) { WebrtcLog("VoE Recording Playout failed"); }
438             * WebrtcLog("VoE start Recording Playout end");
439             */
440            // Start playout
441            if (0 != StartPlayout(_channel)) {
442                WebrtcLog("VoE start playout failed");
443                return -1;
444            }
445
446            // Start playout file
447            // if (0 != StartPlayingFileLocally(_channel,
448            // "/sdcard/singleUserDemo.pcm", true)) {
449            // WebrtcLog("VoE start playout file failed");
450            // return -1;
451            // }
452        }
453
454        if (enableSend == true) {
455            if (0 != SetSendDestination(_channel, _destinationPort,
456                            _destinationIP)) {
457                WebrtcLog("VoE set send  destination failed");
458                return -1;
459            }
460
461            if (0 != SetSendCodec(_channel, _codecIndex)) {
462                WebrtcLog("VoE set send codec failed");
463                return -1;
464            }
465
466            /*
467             * if (0 != StartPlayingFileAsMicrophone(_channel,
468             * "/sdcard/singleUserDemo.pcm", true)) {
469             * WebrtcLog("VoE start playing file as microphone failed"); }
470             */
471            if (0 != StartSend(_channel)) {
472                WebrtcLog("VoE start send failed");
473                return -1;
474            }
475
476            // if (0 != StartPlayingFileAsMicrophone(_channel,
477            // "/sdcard/singleUserDemo.pcm", true)) {
478            // WebrtcLog("VoE start playing file as microphone failed");
479            // return -1;
480            // }
481        }
482
483        return 0;
484    }
485
486    int stopCall() {
487
488        if (useNativeThread == true) {
489
490            Delete();
491            return 0;
492        }
493
494        if (enableSend == true) {
495            // Stop playing file as microphone
496            /*
497             * if (0 != StopPlayingFileAsMicrophone(_channel)) {
498             * WebrtcLog("VoE stop playing file as microphone failed"); return
499             * -1; }
500             */
501            // Stop send
502            if (0 != StopSend(_channel)) {
503                WebrtcLog("VoE stop send failed");
504                return -1;
505            }
506        }
507
508        if (enableReceive == true) {
509            // if (0 != StopRecordingPlayout(_channel)) {
510            // WebrtcLog("VoE stop Recording Playout failed");
511            // }
512            // WebrtcLog("VoE stop Recording Playout ended");
513
514            // Stop listen
515            if (0 != StopListen(_channel)) {
516                WebrtcLog("VoE stop listen failed");
517                return -1;
518            }
519
520            // Stop playout file
521            // if (0 != StopPlayingFileLocally(_channel)) {
522            // WebrtcLog("VoE stop playout file failed");
523            // return -1;
524            // }
525
526            // Stop playout
527            if (0 != StopPlayout(_channel)) {
528                WebrtcLog("VoE stop playout failed");
529                return -1;
530            }
531
532            // Route audio to loudspeaker
533            if (0 != SetLoudspeakerStatus(true)) {
534                WebrtcLog("VoE set louspeaker status failed");
535                return -1;
536            }
537        }
538
539        return 0;
540    }
541
542    int startAutoTest() {
543
544        _autotestThread = new Thread(_autotestProc);
545        _autotestThread.start();
546
547        return 0;
548    }
549
550    private Runnable _autotestProc = new Runnable() {
551        public void run() {
552            // TODO(xians): choose test from GUI
553            // 1 = standard, not used
554            // 2 = extended, 2 = base
555            RunAutoTest(1, 2);
556        }
557    };
558
559    int setAudioProperties(int val) {
560
561        // AudioManager am = (AudioManager)
562        // getSystemService(Context.AUDIO_SERVICE);
563
564        if (val == 0) {
565            // _streamVolume =
566            // am.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
567            // am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
568            // (_streamVolume+1), 0);
569
570            int androidVolumeLevel = (_volumeLevel * _maxVolume) / 255;
571            if (androidVolumeLevel < _maxVolume) {
572                _volumeLevel = ((androidVolumeLevel + 1) * 255) / _maxVolume;
573                if (0 != SetSpeakerVolume(_volumeLevel)) {
574                    WebrtcLog("VoE set speaker volume failed");
575                }
576            }
577        } else if (val == 1) {
578            // _streamVolume =
579            // am.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
580            // am.setStreamVolume(AudioManager.STREAM_VOICE_CALL,
581            // (_streamVolume-1), 0);
582
583            int androidVolumeLevel = (_volumeLevel * _maxVolume) / 255;
584            if (androidVolumeLevel > 0) {
585                _volumeLevel = ((androidVolumeLevel - 1) * 255) / _maxVolume;
586                if (0 != SetSpeakerVolume(_volumeLevel)) {
587                    WebrtcLog("VoE set speaker volume failed");
588                }
589            }
590        } else if (val == 2) {
591            // route audio to back speaker
592            if (0 != SetLoudspeakerStatus(true)) {
593                WebrtcLog("VoE set loudspeaker status failed");
594            }
595            _audioIndex = 2;
596        } else if (val == 3) {
597            // route audio to earpiece
598            if (0 != SetLoudspeakerStatus(false)) {
599                WebrtcLog("VoE set loudspeaker status failed");
600            }
601            _audioIndex = 3;
602        }
603
604        return 0;
605    }
606
607    int displayTextFromFile() {
608
609        TextView tv = (TextView) findViewById(R.id.TextView01);
610        FileReader fr = null;
611        char[] fileBuffer = new char[64];
612
613        try {
614            fr = new FileReader("/sdcard/test.txt");
615        } catch (FileNotFoundException e) {
616            e.printStackTrace();
617            tv.setText("File not found!");
618        }
619
620        try {
621            fr.read(fileBuffer);
622        } catch (IOException e) {
623            e.printStackTrace();
624        }
625
626        String readString = new String(fileBuffer);
627        tv.setText(readString);
628        // setContentView(tv);
629
630        return 0;
631    }
632
633    int recordAudioToFile() {
634        File fr = null;
635        // final to be reachable within onPeriodicNotification
636        byte[] recBuffer = new byte[320];
637
638        int recBufSize =
639                        AudioRecord.getMinBufferSize(16000,
640                                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
641                                        AudioFormat.ENCODING_PCM_16BIT);
642        AudioRecord rec =
643                        new AudioRecord(MediaRecorder.AudioSource.MIC, 16000,
644                                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
645                                        AudioFormat.ENCODING_PCM_16BIT,
646                                        recBufSize);
647
648        fr = new File("/sdcard/record.pcm");
649        FileOutputStream out = null;
650        try {
651            out = new FileOutputStream(fr);
652        } catch (FileNotFoundException e1) {
653            e1.printStackTrace();
654        }
655
656        // start recording
657        try {
658            rec.startRecording();
659        } catch (IllegalStateException e) {
660            e.printStackTrace();
661        }
662
663        for (int i = 0; i < 550; i++) {
664            // note, there is a short version of write as well!
665            int wrBytes = rec.read(recBuffer, 0, 320);
666
667            try {
668                out.write(recBuffer);
669            } catch (IOException e) {
670                e.printStackTrace();
671            }
672        }
673
674        // stop playout
675        try {
676            rec.stop();
677        } catch (IllegalStateException e) {
678            e.printStackTrace();
679        }
680
681        return 0;
682    }
683
684    int playAudioFromFile() {
685
686        File fr = null;
687        // final to be reachable within onPeriodicNotification
688        // final byte[] playBuffer = new byte [320000];
689        // final to be reachable within onPeriodicNotification
690        final byte[] playBuffer = new byte[320];
691
692        final int playBufSize =
693                        AudioTrack.getMinBufferSize(16000,
694                                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
695                                        AudioFormat.ENCODING_PCM_16BIT);
696        // final int playBufSize = 1920; // 100 ms buffer
697        // byte[] playBuffer = new byte [playBufSize];
698        final AudioTrack play =
699                        new AudioTrack(AudioManager.STREAM_VOICE_CALL, 16000,
700                                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
701                                        AudioFormat.ENCODING_PCM_16BIT,
702                                        playBufSize, AudioTrack.MODE_STREAM);
703
704        // implementation of the playpos callback functions
705        play.setPlaybackPositionUpdateListener(
706                        new AudioTrack.OnPlaybackPositionUpdateListener() {
707
708            int count = 0;
709
710            public void onPeriodicNotification(AudioTrack track) {
711                // int wrBytes = play.write(playBuffer, count, 320);
712                count += 320;
713            }
714
715            public void onMarkerReached(AudioTrack track) {
716
717            }
718        });
719
720        // set the notification period = 160 samples
721        // int ret = play.setPositionNotificationPeriod(160);
722
723        fr = new File("/sdcard/record.pcm");
724        FileInputStream in = null;
725        try {
726            in = new FileInputStream(fr);
727        } catch (FileNotFoundException e1) {
728            e1.printStackTrace();
729        }
730
731        // try {
732        // in.read(playBuffer);
733        // } catch (IOException e) {
734        // e.printStackTrace();
735        // }
736
737        // play all at once
738        // int wrBytes = play.write(playBuffer, 0, 320000);
739
740
741        // start playout
742        try {
743            play.play();
744        } catch (IllegalStateException e) {
745            e.printStackTrace();
746        }
747
748        // returns the number of samples that has been written
749        // int headPos = play.getPlaybackHeadPosition();
750
751        // play with multiple writes
752        for (int i = 0; i < 500; i++) {
753            try {
754                in.read(playBuffer);
755            } catch (IOException e) {
756                e.printStackTrace();
757            }
758
759
760            // note, there is a short version of write as well!
761            int wrBytes = play.write(playBuffer, 0, 320);
762
763            Log.d("testWrite", "wrote");
764        }
765
766        // stop playout
767        try {
768            play.stop();
769        } catch (IllegalStateException e) {
770            e.printStackTrace();
771        }
772
773        return 0;
774    }
775
776    int playAudioInThread() {
777
778        if (_isRunningPlay) {
779            return 0;
780        }
781
782        // File fr = null;
783        // final byte[] playBuffer = new byte[320];
784        if (_playFromFile) {
785            _playBuffer = new byte[320];
786        } else {
787            // reset index
788            _playIndex = 0;
789        }
790        // within
791        // onPeriodicNotification
792
793        // Log some info (static)
794        WebrtcLog("Creating AudioTrack object");
795        final int minPlayBufSize =
796                        AudioTrack.getMinBufferSize(16000,
797                                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
798                                        AudioFormat.ENCODING_PCM_16BIT);
799        WebrtcLog("Min play buf size = " + minPlayBufSize);
800        WebrtcLog("Min volume = " + AudioTrack.getMinVolume());
801        WebrtcLog("Max volume = " + AudioTrack.getMaxVolume());
802        WebrtcLog("Native sample rate = "
803                        + AudioTrack.getNativeOutputSampleRate(
804                                        AudioManager.STREAM_VOICE_CALL));
805
806        final int playBufSize = minPlayBufSize; // 3200; // 100 ms buffer
807        // byte[] playBuffer = new byte [playBufSize];
808        try {
809            _at = new AudioTrack(
810                            AudioManager.STREAM_VOICE_CALL,
811                            16000,
812                            AudioFormat.CHANNEL_CONFIGURATION_MONO,
813                            AudioFormat.ENCODING_PCM_16BIT,
814                            playBufSize, AudioTrack.MODE_STREAM);
815        } catch (Exception e) {
816            WebrtcLog(e.getMessage());
817        }
818
819        // Log some info (non-static)
820        WebrtcLog("Notification marker pos = "
821                        + _at.getNotificationMarkerPosition());
822        WebrtcLog("Play head pos = " + _at.getPlaybackHeadPosition());
823        WebrtcLog("Pos notification dt = "
824                        + _at.getPositionNotificationPeriod());
825        WebrtcLog("Playback rate = " + _at.getPlaybackRate());
826        WebrtcLog("Sample rate = " + _at.getSampleRate());
827
828        // implementation of the playpos callback functions
829        // _at.setPlaybackPositionUpdateListener(
830        // new AudioTrack.OnPlaybackPositionUpdateListener() {
831        //
832        // int count = 3200;
833        //
834        // public void onPeriodicNotification(AudioTrack track) {
835        // // int wrBytes = play.write(playBuffer, count, 320);
836        // count += 320;
837        // }
838        //
839        // public void onMarkerReached(AudioTrack track) {
840        // }
841        // });
842
843        // set the notification period = 160 samples
844        // int ret = _at.setPositionNotificationPeriod(160);
845
846        if (_playFromFile) {
847            _fr = new File("/sdcard/singleUserDemo.pcm");
848            try {
849                _in = new FileInputStream(_fr);
850            } catch (FileNotFoundException e1) {
851                e1.printStackTrace();
852            }
853        }
854
855        // try {
856        // in.read(playBuffer);
857        // } catch (IOException e) {
858        // e.printStackTrace();
859        // }
860
861        _isRunningPlay = true;
862
863        // buffer = new byte[3200];
864        _playThread = new Thread(_playProc);
865        // ar.startRecording();
866        // bytesRead = 3200;
867        // recording = true;
868        _playThread.start();
869
870        return 0;
871    }
872
873    int stopPlayAudio() {
874        if (!_isRunningPlay) {
875            return 0;
876        }
877
878        _isRunningPlay = false;
879
880        return 0;
881    }
882
883    private Runnable _playProc = new Runnable() {
884        public void run() {
885
886            // set high thread priority
887            android.os.Process.setThreadPriority(
888                            android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
889
890            // play all at once
891            // int wrBytes = play.write(playBuffer, 0, 320000);
892
893            // fill the buffer
894            // play.write(playBuffer, 0, 3200);
895
896            // play.flush();
897
898            // start playout
899            try {
900                _at.play();
901            } catch (IllegalStateException e) {
902                e.printStackTrace();
903            }
904
905            // play with multiple writes
906            int i = 0;
907            for (; i < 3000 && _isRunningPlay; i++) {
908
909                if (_playFromFile) {
910                    try {
911                        _in.read(_playBuffer);
912                    } catch (IOException e) {
913                        e.printStackTrace();
914                    }
915
916                    int wrBytes = _at.write(_playBuffer, 0 /* i * 320 */, 320);
917                } else {
918                    int wrSamples =
919                                    _at.write(_circBuffer, _playIndex * 160,
920                                                    160);
921
922                    // WebrtcLog("Played 10 ms from buffer, _playIndex = " +
923                    // _playIndex);
924                    // WebrtcLog("Diff = " + (_recIndex - _playIndex));
925
926                    if (_playIndex == 49) {
927                        _playIndex = 0;
928                    } else {
929                        _playIndex += 1;
930                    }
931                }
932
933                // WebrtcLog("Wrote 10 ms to buffer, head = "
934                // + _at.getPlaybackHeadPosition());
935            }
936
937            // stop playout
938            try {
939                _at.stop();
940            } catch (IllegalStateException e) {
941                e.printStackTrace();
942            }
943
944            // returns the number of samples that has been written
945            WebrtcLog("Test stopped, i = " + i + ", head = "
946                            + _at.getPlaybackHeadPosition());
947            int headPos = _at.getPlaybackHeadPosition();
948
949            // flush the buffers
950            _at.flush();
951
952            // release the object
953            _at.release();
954            _at = null;
955
956            // try {
957            // Thread.sleep() must be within a try - catch block
958            // Thread.sleep(3000);
959            // }catch (Exception e){
960            // System.out.println(e.getMessage());
961            // }
962
963            _isRunningPlay = false;
964
965        }
966    };
967
968    int recAudioInThread() {
969
970        if (_isRunningRec) {
971            return 0;
972        }
973
974        // within
975        // onPeriodicNotification
976
977        // reset index
978        _recIndex = 20;
979
980        // Log some info (static)
981        WebrtcLog("Creating AudioRecord object");
982        final int minRecBufSize = AudioRecord.getMinBufferSize(16000,
983                        AudioFormat.CHANNEL_CONFIGURATION_MONO,
984                        AudioFormat.ENCODING_PCM_16BIT);
985        WebrtcLog("Min rec buf size = " + minRecBufSize);
986        // WebrtcLog("Min volume = " + AudioTrack.getMinVolume());
987        // WebrtcLog("Max volume = " + AudioTrack.getMaxVolume());
988        // WebrtcLog("Native sample rate = "
989        // + AudioRecord
990        // .getNativeInputSampleRate(AudioManager.STREAM_VOICE_CALL));
991
992        final int recBufSize = minRecBufSize; // 3200; // 100 ms buffer
993        try {
994            _ar = new AudioRecord(
995                            MediaRecorder.AudioSource.MIC,
996                            16000,
997                            AudioFormat.CHANNEL_CONFIGURATION_MONO,
998                            AudioFormat.ENCODING_PCM_16BIT,
999                            recBufSize);
1000        } catch (Exception e) {
1001            WebrtcLog(e.getMessage());
1002        }
1003
1004        // Log some info (non-static)
1005        WebrtcLog("Notification marker pos = "
1006                        + _ar.getNotificationMarkerPosition());
1007        // WebrtcLog("Play head pos = " + _ar.getRecordHeadPosition());
1008        WebrtcLog("Pos notification dt rec= "
1009                        + _ar.getPositionNotificationPeriod());
1010        // WebrtcLog("Playback rate = " + _ar.getRecordRate());
1011        // WebrtcLog("Playback rate = " + _ar.getPlaybackRate());
1012        WebrtcLog("Sample rate = " + _ar.getSampleRate());
1013        // WebrtcLog("Playback rate = " + _ar.getPlaybackRate());
1014        // WebrtcLog("Playback rate = " + _ar.getPlaybackRate());
1015
1016        _isRunningRec = true;
1017
1018        _recThread = new Thread(_recProc);
1019
1020        _recThread.start();
1021
1022        return 0;
1023    }
1024
1025    int stopRecAudio() {
1026        if (!_isRunningRec) {
1027            return 0;
1028        }
1029
1030        _isRunningRec = false;
1031
1032        return 0;
1033    }
1034
1035    private Runnable _recProc = new Runnable() {
1036        public void run() {
1037
1038            // set high thread priority
1039            android.os.Process.setThreadPriority(
1040                            android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
1041
1042            // start recording
1043            try {
1044                _ar.startRecording();
1045            } catch (IllegalStateException e) {
1046                e.printStackTrace();
1047            }
1048
1049            // keep recording to circular buffer
1050            // for a while
1051            int i = 0;
1052            int rdSamples = 0;
1053            short[] tempBuffer = new short[160]; // Only used for native case
1054
1055            for (; i < 3000 && _isRunningRec; i++) {
1056                if (_runThroughNativeLayer) {
1057                    rdSamples = _ar.read(tempBuffer, 0, 160);
1058                    // audioLoop(tempBuffer, 160); // Insert into native layer
1059                } else {
1060                    rdSamples = _ar.read(_circBuffer, _recIndex * 160, 160);
1061
1062                    // WebrtcLog("Recorded 10 ms to buffer, _recIndex = " +
1063                    // _recIndex);
1064                    // WebrtcLog("rdSamples = " + rdSamples);
1065
1066                    if (_recIndex == 49) {
1067                        _recIndex = 0;
1068                    } else {
1069                        _recIndex += 1;
1070                    }
1071                }
1072            }
1073
1074            // stop recording
1075            try {
1076                _ar.stop();
1077            } catch (IllegalStateException e) {
1078                e.printStackTrace();
1079            }
1080
1081            // release the object
1082            _ar.release();
1083            _ar = null;
1084
1085            // try {
1086            // Thread.sleep() must be within a try - catch block
1087            // Thread.sleep(3000);
1088            // }catch (Exception e){
1089            // System.out.println(e.getMessage());
1090            // }
1091
1092            _isRunningRec = false;
1093
1094            // returns the number of samples that has been written
1095            // WebrtcLog("Test stopped, i = " + i + ", head = "
1096            // + _at.getPlaybackHeadPosition());
1097            // int headPos = _at.getPlaybackHeadPosition();
1098        }
1099    };
1100
1101    private void WebrtcLog(String msg) {
1102        Log.d("*Webrtc*", msg);
1103    }
1104
1105    // //////////////// Native function prototypes ////////////////////
1106
1107    private native static boolean NativeInit();
1108
1109    private native int RunAutoTest(int testType, int extendedSel);
1110
1111    private native boolean Create();
1112
1113    private native boolean Delete();
1114
1115    private native int Init(boolean enableTrace, boolean useExtTrans);
1116
1117    private native int Terminate();
1118
1119    private native int CreateChannel();
1120
1121    private native int DeleteChannel(int channel);
1122
1123    private native int SetLocalReceiver(int channel, int port);
1124
1125    private native int SetSendDestination(int channel, int port,
1126                    String ipaddr);
1127
1128    private native int StartListen(int channel);
1129
1130    private native int StartPlayout(int channel);
1131
1132    private native int StartSend(int channel);
1133
1134    private native int StopListen(int channel);
1135
1136    private native int StopPlayout(int channel);
1137
1138    private native int StopSend(int channel);
1139
1140    private native int StartPlayingFileLocally(int channel, String fileName,
1141                    boolean loop);
1142
1143    private native int StopPlayingFileLocally(int channel);
1144
1145    private native int StartRecordingPlayout(int channel, String fileName,
1146                    boolean loop);
1147
1148    private native int StopRecordingPlayout(int channel);
1149
1150    private native int StartPlayingFileAsMicrophone(int channel,
1151                    String fileName, boolean loop);
1152
1153    private native int StopPlayingFileAsMicrophone(int channel);
1154
1155    private native int NumOfCodecs();
1156
1157    private native int SetSendCodec(int channel, int index);
1158
1159    private native int SetVADStatus(int channel, boolean enable, int mode);
1160
1161    private native int SetNSStatus(boolean enable, int mode);
1162
1163    private native int SetAGCStatus(boolean enable, int mode);
1164
1165    private native int SetECStatus(boolean enable, int mode);
1166
1167    private native int SetSpeakerVolume(int volume);
1168
1169    private native int SetLoudspeakerStatus(boolean enable);
1170
1171    /*
1172     * this is used to load the 'webrtc-voice-demo-jni'
1173     * library on application startup.
1174     * The library has already been unpacked into
1175     * /data/data/webrtc.android.AndroidTest/lib/libwebrtc-voice-demo-jni.so
1176     * at installation time by the package manager.
1177     */
1178    static {
1179        Log.d("*Webrtc*", "Loading webrtc-voice-demo-jni...");
1180        System.loadLibrary("webrtc-voice-demo-jni");
1181
1182        Log.d("*Webrtc*", "Calling native init...");
1183        if (!NativeInit()) {
1184            Log.e("*Webrtc*", "Native init failed");
1185            throw new RuntimeException("Native init failed");
1186        } else {
1187            Log.d("*Webrtc*", "Native init successful");
1188        }
1189    }
1190}
1191