/* | |
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. | |
* | |
* Use of this source code is governed by a BSD-style license that can be found | |
* in the LICENSE file in the root of the source tree. An additional | |
* intellectual property rights grant can be found in the file PATENTS. All | |
* contributing project authors may be found in the AUTHORS file in the root of | |
* the source tree. | |
*/ | |
/* | |
* VoiceEngine Android test application. It starts either auto test or acts like | |
* a GUI test. | |
*/ | |
package org.webrtc.voiceengine.test; | |
import java.io.File; | |
import java.io.FileInputStream; | |
import java.io.FileNotFoundException; | |
import java.io.FileOutputStream; | |
import java.io.FileReader; | |
import java.io.IOException; | |
import android.app.Activity; | |
import android.content.Context; | |
import android.media.AudioFormat; | |
import android.media.AudioManager; | |
import android.media.AudioRecord; | |
import android.media.AudioTrack; | |
import android.media.MediaRecorder; | |
import android.os.Bundle; | |
import android.util.Log; | |
import android.view.View; | |
import android.widget.AdapterView; | |
import android.widget.ArrayAdapter; | |
import android.widget.Button; | |
import android.widget.EditText; | |
import android.widget.Spinner; | |
import android.widget.TextView; | |
public class AndroidTest extends Activity { | |
private byte[] _playBuffer = null; | |
private short[] _circBuffer = new short[8000]; // can hold 50 frames | |
private int _recIndex = 0; | |
private int _playIndex = 0; | |
// private int _streamVolume = 4; | |
private int _maxVolume = 0; // Android max level (commonly 5) | |
// VoE level (0-255), corresponds to level 4 out of 5 | |
private int _volumeLevel = 204; | |
private Thread _playThread; | |
private Thread _recThread; | |
private Thread _autotestThread; | |
private static AudioTrack _at; | |
private static AudioRecord _ar; | |
private File _fr = null; | |
private FileInputStream _in = null; | |
private boolean _isRunningPlay = false; | |
private boolean _isRunningRec = false; | |
private boolean _settingSet = true; | |
private boolean _isCallActive = false; | |
private boolean _runAutotest = false; // ENABLE AUTOTEST HERE! | |
private int _channel = -1; | |
private int _codecIndex = 0; | |
private int _ecIndex = 0; | |
private int _nsIndex = 0; | |
private int _agcIndex = 0; | |
private int _vadIndex = 0; | |
private int _audioIndex = 3; | |
private int _settingMenu = 0; | |
private int _receivePort = 1234; | |
private int _destinationPort = 1234; | |
private String _destinationIP = "127.0.0.1"; | |
// "Build" settings | |
private final boolean _playFromFile = false; | |
// Set to true to send data to native code and back | |
private final boolean _runThroughNativeLayer = true; | |
private final boolean enableSend = true; | |
private final boolean enableReceive = true; | |
private final boolean useNativeThread = false; | |
/** Called when the activity is first created. */ | |
public void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.main); | |
TextView tv = (TextView) findViewById(R.id.TextView01); | |
tv.setText(""); | |
final EditText ed = (EditText) findViewById(R.id.EditText01); | |
ed.setWidth(200); | |
ed.setText(_destinationIP); | |
final Button buttonStart = (Button) findViewById(R.id.Button01); | |
buttonStart.setWidth(200); | |
if (_runAutotest) { | |
buttonStart.setText("Run test"); | |
} else { | |
buttonStart.setText("Start Call"); | |
} | |
// button.layout(50, 50, 100, 40); | |
buttonStart.setOnClickListener(new View.OnClickListener() { | |
public void onClick(View v) { | |
if (_runAutotest) { | |
startAutoTest(); | |
} else { | |
if (_isCallActive) { | |
if (stopCall() != -1) { | |
_isCallActive = false; | |
buttonStart.setText("Start Call"); | |
} | |
} else { | |
_destinationIP = ed.getText().toString(); | |
if (startCall() != -1) { | |
_isCallActive = true; | |
buttonStart.setText("Stop Call"); | |
} | |
} | |
} | |
// displayTextFromFile(); | |
// recordAudioToFile(); | |
// if(!_playFromFile) | |
// { | |
// recAudioInThread(); | |
// } | |
// playAudioInThread(); | |
} | |
}); | |
final Button buttonStop = (Button) findViewById(R.id.Button02); | |
buttonStop.setWidth(200); | |
buttonStop.setText("Close app"); | |
buttonStop.setOnClickListener(new View.OnClickListener() { | |
public void onClick(View v) { | |
if (!_runAutotest) { | |
ShutdownVoE(); | |
} | |
// This call terminates and should close the activity | |
finish(); | |
// playAudioFromFile(); | |
// if(!_playFromFile) | |
// { | |
// stopRecAudio(); | |
// } | |
// stopPlayAudio(); | |
} | |
}); | |
String ap1[] = {"EC off", "AECM"}; | |
final ArrayAdapter<String> adapterAp1 = new ArrayAdapter<String>( | |
this, | |
android.R.layout.simple_spinner_dropdown_item, | |
ap1); | |
String ap2[] = | |
{"NS off", "NS low", "NS moderate", "NS high", | |
"NS very high"}; | |
final ArrayAdapter<String> adapterAp2 = new ArrayAdapter<String>( | |
this, | |
android.R.layout.simple_spinner_dropdown_item, | |
ap2); | |
String ap3[] = {"AGC off", "AGC adaptive", "AGC fixed"}; | |
final ArrayAdapter<String> adapterAp3 = new ArrayAdapter<String>( | |
this, | |
android.R.layout.simple_spinner_dropdown_item, | |
ap3); | |
String ap4[] = | |
{"VAD off", "VAD conventional", "VAD high rate", | |
"VAD mid rate", "VAD low rate"}; | |
final ArrayAdapter<String> adapterAp4 = new ArrayAdapter<String>( | |
this, | |
android.R.layout.simple_spinner_dropdown_item, | |
ap4); | |
String codecs[] = {"iSAC", "PCMU", "PCMA", "iLBC"}; | |
final ArrayAdapter<String> adapterCodecs = new ArrayAdapter<String>( | |
this, | |
android.R.layout.simple_spinner_dropdown_item, | |
codecs); | |
final Spinner spinnerSettings1 = (Spinner) findViewById(R.id.Spinner01); | |
final Spinner spinnerSettings2 = (Spinner) findViewById(R.id.Spinner02); | |
spinnerSettings1.setMinimumWidth(200); | |
String settings[] = | |
{"Codec", "Echo Control", "Noise Suppression", | |
"Automatic Gain Control", | |
"Voice Activity Detection"}; | |
ArrayAdapter<String> adapterSettings1 = new ArrayAdapter<String>( | |
this, | |
android.R.layout.simple_spinner_dropdown_item, | |
settings); | |
spinnerSettings1.setAdapter(adapterSettings1); | |
spinnerSettings1.setOnItemSelectedListener( | |
new AdapterView.OnItemSelectedListener() { | |
public void onItemSelected(AdapterView adapterView, View view, | |
int position, long id) { | |
_settingMenu = position; | |
_settingSet = false; | |
if (position == 0) { | |
spinnerSettings2.setAdapter(adapterCodecs); | |
spinnerSettings2.setSelection(_codecIndex); | |
} | |
if (position == 1) { | |
spinnerSettings2.setAdapter(adapterAp1); | |
spinnerSettings2.setSelection(_ecIndex); | |
} | |
if (position == 2) { | |
spinnerSettings2.setAdapter(adapterAp2); | |
spinnerSettings2.setSelection(_nsIndex); | |
} | |
if (position == 3) { | |
spinnerSettings2.setAdapter(adapterAp3); | |
spinnerSettings2.setSelection(_agcIndex); | |
} | |
if (position == 4) { | |
spinnerSettings2.setAdapter(adapterAp4); | |
spinnerSettings2.setSelection(_vadIndex); | |
} | |
} | |
public void onNothingSelected(AdapterView adapterView) { | |
WebrtcLog("No setting1 selected"); | |
} | |
}); | |
spinnerSettings2.setMinimumWidth(200); | |
ArrayAdapter<String> adapterSettings2 = new ArrayAdapter<String>( | |
this, | |
android.R.layout.simple_spinner_dropdown_item, | |
codecs); | |
spinnerSettings2.setAdapter(adapterSettings2); | |
spinnerSettings2.setOnItemSelectedListener( | |
new AdapterView.OnItemSelectedListener() { | |
public void onItemSelected(AdapterView adapterView, View view, | |
int position, long id) { | |
// avoid unintentional setting | |
if (_settingSet == false) { | |
_settingSet = true; | |
return; | |
} | |
// Change volume | |
if (_settingMenu == 0) { | |
WebrtcLog("Selected audio " + position); | |
setAudioProperties(position); | |
spinnerSettings2.setSelection(_audioIndex); | |
} | |
// Change codec | |
if (_settingMenu == 1) { | |
_codecIndex = position; | |
WebrtcLog("Selected codec " + position); | |
if (0 != SetSendCodec(_channel, _codecIndex)) { | |
WebrtcLog("VoE set send codec failed"); | |
} | |
} | |
// Change EC | |
if (_settingMenu == 2) { | |
boolean enable = true; | |
int ECmode = 5; // AECM | |
int AESmode = 0; | |
_ecIndex = position; | |
WebrtcLog("Selected EC " + position); | |
if (position == 0) { | |
enable = false; | |
} | |
if (position > 1) { | |
ECmode = 4; // AES | |
AESmode = position - 1; | |
} | |
if (0 != SetECStatus(enable, ECmode)) { | |
WebrtcLog("VoE set EC status failed"); | |
} | |
} | |
// Change NS | |
if (_settingMenu == 3) { | |
boolean enable = true; | |
_nsIndex = position; | |
WebrtcLog("Selected NS " + position); | |
if (position == 0) { | |
enable = false; | |
} | |
if (0 != SetNSStatus(enable, position + 2)) { | |
WebrtcLog("VoE set NS status failed"); | |
} | |
} | |
// Change AGC | |
if (_settingMenu == 4) { | |
boolean enable = true; | |
_agcIndex = position; | |
WebrtcLog("Selected AGC " + position); | |
if (position == 0) { | |
enable = false; | |
position = 1; // default | |
} | |
if (0 != SetAGCStatus(enable, position + 2)) { | |
WebrtcLog("VoE set AGC status failed"); | |
} | |
} | |
// Change VAD | |
if (_settingMenu == 5) { | |
boolean enable = true; | |
_vadIndex = position; | |
WebrtcLog("Selected VAD " + position); | |
if (position == 0) { | |
enable = false; | |
position++; | |
} | |
if (0 != SetVADStatus(_channel, enable, position - 1)) { | |
WebrtcLog("VoE set VAD status failed"); | |
} | |
} | |
} | |
public void onNothingSelected(AdapterView adapterView) { | |
} | |
}); | |
// Setup VoiceEngine | |
if (!_runAutotest && !useNativeThread) SetupVoE(); | |
// Suggest to use the voice call audio stream for hardware volume | |
// controls | |
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL); | |
// Get max Android volume and adjust default volume to map exactly to an | |
// Android level | |
AudioManager am = | |
(AudioManager) getSystemService(Context.AUDIO_SERVICE); | |
_maxVolume = am.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL); | |
if (_maxVolume <= 0) { | |
WebrtcLog("Could not get max volume!"); | |
} else { | |
int androidVolumeLevel = (_volumeLevel * _maxVolume) / 255; | |
_volumeLevel = (androidVolumeLevel * 255) / _maxVolume; | |
} | |
WebrtcLog("Started Webrtc Android Test"); | |
} | |
// Will be called when activity is shutdown. | |
// NOTE: Activity may be killed without this function being called, | |
// but then we should not need to clean up. | |
protected void onDestroy() { | |
super.onDestroy(); | |
// ShutdownVoE(); | |
} | |
private void SetupVoE() { | |
// Create VoiceEngine | |
Create(); // Error logging is done in native API wrapper | |
// Initialize | |
if (0 != Init(false, false)) { | |
WebrtcLog("VoE init failed"); | |
} | |
// Create channel | |
_channel = CreateChannel(); | |
if (0 != _channel) { | |
WebrtcLog("VoE create channel failed"); | |
} | |
} | |
private void ShutdownVoE() { | |
// Delete channel | |
if (0 != DeleteChannel(_channel)) { | |
WebrtcLog("VoE delete channel failed"); | |
} | |
// Terminate | |
if (0 != Terminate()) { | |
WebrtcLog("VoE terminate failed"); | |
} | |
// Delete VoiceEngine | |
Delete(); // Error logging is done in native API wrapper | |
} | |
int startCall() { | |
if (useNativeThread == true) { | |
Create(); | |
return 0; | |
} | |
if (enableReceive == true) { | |
// Set local receiver | |
if (0 != SetLocalReceiver(_channel, _receivePort)) { | |
WebrtcLog("VoE set local receiver failed"); | |
} | |
if (0 != StartListen(_channel)) { | |
WebrtcLog("VoE start listen failed"); | |
return -1; | |
} | |
// Route audio to earpiece | |
if (0 != SetLoudspeakerStatus(false)) { | |
WebrtcLog("VoE set louspeaker status failed"); | |
return -1; | |
} | |
/* | |
* WebrtcLog("VoE start record now"); if (0 != | |
* StartRecordingPlayout(_channel, "/sdcard/singleUserDemoOut.pcm", | |
* false)) { WebrtcLog("VoE Recording Playout failed"); } | |
* WebrtcLog("VoE start Recording Playout end"); | |
*/ | |
// Start playout | |
if (0 != StartPlayout(_channel)) { | |
WebrtcLog("VoE start playout failed"); | |
return -1; | |
} | |
// Start playout file | |
// if (0 != StartPlayingFileLocally(_channel, | |
// "/sdcard/singleUserDemo.pcm", true)) { | |
// WebrtcLog("VoE start playout file failed"); | |
// return -1; | |
// } | |
} | |
if (enableSend == true) { | |
if (0 != SetSendDestination(_channel, _destinationPort, | |
_destinationIP)) { | |
WebrtcLog("VoE set send destination failed"); | |
return -1; | |
} | |
if (0 != SetSendCodec(_channel, _codecIndex)) { | |
WebrtcLog("VoE set send codec failed"); | |
return -1; | |
} | |
/* | |
* if (0 != StartPlayingFileAsMicrophone(_channel, | |
* "/sdcard/singleUserDemo.pcm", true)) { | |
* WebrtcLog("VoE start playing file as microphone failed"); } | |
*/ | |
if (0 != StartSend(_channel)) { | |
WebrtcLog("VoE start send failed"); | |
return -1; | |
} | |
// if (0 != StartPlayingFileAsMicrophone(_channel, | |
// "/sdcard/singleUserDemo.pcm", true)) { | |
// WebrtcLog("VoE start playing file as microphone failed"); | |
// return -1; | |
// } | |
} | |
return 0; | |
} | |
int stopCall() { | |
if (useNativeThread == true) { | |
Delete(); | |
return 0; | |
} | |
if (enableSend == true) { | |
// Stop playing file as microphone | |
/* | |
* if (0 != StopPlayingFileAsMicrophone(_channel)) { | |
* WebrtcLog("VoE stop playing file as microphone failed"); return | |
* -1; } | |
*/ | |
// Stop send | |
if (0 != StopSend(_channel)) { | |
WebrtcLog("VoE stop send failed"); | |
return -1; | |
} | |
} | |
if (enableReceive == true) { | |
// if (0 != StopRecordingPlayout(_channel)) { | |
// WebrtcLog("VoE stop Recording Playout failed"); | |
// } | |
// WebrtcLog("VoE stop Recording Playout ended"); | |
// Stop listen | |
if (0 != StopListen(_channel)) { | |
WebrtcLog("VoE stop listen failed"); | |
return -1; | |
} | |
// Stop playout file | |
// if (0 != StopPlayingFileLocally(_channel)) { | |
// WebrtcLog("VoE stop playout file failed"); | |
// return -1; | |
// } | |
// Stop playout | |
if (0 != StopPlayout(_channel)) { | |
WebrtcLog("VoE stop playout failed"); | |
return -1; | |
} | |
// Route audio to loudspeaker | |
if (0 != SetLoudspeakerStatus(true)) { | |
WebrtcLog("VoE set louspeaker status failed"); | |
return -1; | |
} | |
} | |
return 0; | |
} | |
int startAutoTest() { | |
_autotestThread = new Thread(_autotestProc); | |
_autotestThread.start(); | |
return 0; | |
} | |
private Runnable _autotestProc = new Runnable() { | |
public void run() { | |
// TODO(xians): choose test from GUI | |
// 1 = standard, not used | |
// 2 = extended, 2 = base | |
RunAutoTest(1, 2); | |
} | |
}; | |
int setAudioProperties(int val) { | |
// AudioManager am = (AudioManager) | |
// getSystemService(Context.AUDIO_SERVICE); | |
if (val == 0) { | |
// _streamVolume = | |
// am.getStreamVolume(AudioManager.STREAM_VOICE_CALL); | |
// am.setStreamVolume(AudioManager.STREAM_VOICE_CALL, | |
// (_streamVolume+1), 0); | |
int androidVolumeLevel = (_volumeLevel * _maxVolume) / 255; | |
if (androidVolumeLevel < _maxVolume) { | |
_volumeLevel = ((androidVolumeLevel + 1) * 255) / _maxVolume; | |
if (0 != SetSpeakerVolume(_volumeLevel)) { | |
WebrtcLog("VoE set speaker volume failed"); | |
} | |
} | |
} else if (val == 1) { | |
// _streamVolume = | |
// am.getStreamVolume(AudioManager.STREAM_VOICE_CALL); | |
// am.setStreamVolume(AudioManager.STREAM_VOICE_CALL, | |
// (_streamVolume-1), 0); | |
int androidVolumeLevel = (_volumeLevel * _maxVolume) / 255; | |
if (androidVolumeLevel > 0) { | |
_volumeLevel = ((androidVolumeLevel - 1) * 255) / _maxVolume; | |
if (0 != SetSpeakerVolume(_volumeLevel)) { | |
WebrtcLog("VoE set speaker volume failed"); | |
} | |
} | |
} else if (val == 2) { | |
// route audio to back speaker | |
if (0 != SetLoudspeakerStatus(true)) { | |
WebrtcLog("VoE set loudspeaker status failed"); | |
} | |
_audioIndex = 2; | |
} else if (val == 3) { | |
// route audio to earpiece | |
if (0 != SetLoudspeakerStatus(false)) { | |
WebrtcLog("VoE set loudspeaker status failed"); | |
} | |
_audioIndex = 3; | |
} | |
return 0; | |
} | |
int displayTextFromFile() { | |
TextView tv = (TextView) findViewById(R.id.TextView01); | |
FileReader fr = null; | |
char[] fileBuffer = new char[64]; | |
try { | |
fr = new FileReader("/sdcard/test.txt"); | |
} catch (FileNotFoundException e) { | |
e.printStackTrace(); | |
tv.setText("File not found!"); | |
} | |
try { | |
fr.read(fileBuffer); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
String readString = new String(fileBuffer); | |
tv.setText(readString); | |
// setContentView(tv); | |
return 0; | |
} | |
int recordAudioToFile() { | |
File fr = null; | |
// final to be reachable within onPeriodicNotification | |
byte[] recBuffer = new byte[320]; | |
int recBufSize = | |
AudioRecord.getMinBufferSize(16000, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT); | |
AudioRecord rec = | |
new AudioRecord(MediaRecorder.AudioSource.MIC, 16000, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT, | |
recBufSize); | |
fr = new File("/sdcard/record.pcm"); | |
FileOutputStream out = null; | |
try { | |
out = new FileOutputStream(fr); | |
} catch (FileNotFoundException e1) { | |
e1.printStackTrace(); | |
} | |
// start recording | |
try { | |
rec.startRecording(); | |
} catch (IllegalStateException e) { | |
e.printStackTrace(); | |
} | |
for (int i = 0; i < 550; i++) { | |
// note, there is a short version of write as well! | |
int wrBytes = rec.read(recBuffer, 0, 320); | |
try { | |
out.write(recBuffer); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
} | |
// stop playout | |
try { | |
rec.stop(); | |
} catch (IllegalStateException e) { | |
e.printStackTrace(); | |
} | |
return 0; | |
} | |
int playAudioFromFile() { | |
File fr = null; | |
// final to be reachable within onPeriodicNotification | |
// final byte[] playBuffer = new byte [320000]; | |
// final to be reachable within onPeriodicNotification | |
final byte[] playBuffer = new byte[320]; | |
final int playBufSize = | |
AudioTrack.getMinBufferSize(16000, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT); | |
// final int playBufSize = 1920; // 100 ms buffer | |
// byte[] playBuffer = new byte [playBufSize]; | |
final AudioTrack play = | |
new AudioTrack(AudioManager.STREAM_VOICE_CALL, 16000, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT, | |
playBufSize, AudioTrack.MODE_STREAM); | |
// implementation of the playpos callback functions | |
play.setPlaybackPositionUpdateListener( | |
new AudioTrack.OnPlaybackPositionUpdateListener() { | |
int count = 0; | |
public void onPeriodicNotification(AudioTrack track) { | |
// int wrBytes = play.write(playBuffer, count, 320); | |
count += 320; | |
} | |
public void onMarkerReached(AudioTrack track) { | |
} | |
}); | |
// set the notification period = 160 samples | |
// int ret = play.setPositionNotificationPeriod(160); | |
fr = new File("/sdcard/record.pcm"); | |
FileInputStream in = null; | |
try { | |
in = new FileInputStream(fr); | |
} catch (FileNotFoundException e1) { | |
e1.printStackTrace(); | |
} | |
// try { | |
// in.read(playBuffer); | |
// } catch (IOException e) { | |
// e.printStackTrace(); | |
// } | |
// play all at once | |
// int wrBytes = play.write(playBuffer, 0, 320000); | |
// start playout | |
try { | |
play.play(); | |
} catch (IllegalStateException e) { | |
e.printStackTrace(); | |
} | |
// returns the number of samples that has been written | |
// int headPos = play.getPlaybackHeadPosition(); | |
// play with multiple writes | |
for (int i = 0; i < 500; i++) { | |
try { | |
in.read(playBuffer); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
// note, there is a short version of write as well! | |
int wrBytes = play.write(playBuffer, 0, 320); | |
Log.d("testWrite", "wrote"); | |
} | |
// stop playout | |
try { | |
play.stop(); | |
} catch (IllegalStateException e) { | |
e.printStackTrace(); | |
} | |
return 0; | |
} | |
int playAudioInThread() { | |
if (_isRunningPlay) { | |
return 0; | |
} | |
// File fr = null; | |
// final byte[] playBuffer = new byte[320]; | |
if (_playFromFile) { | |
_playBuffer = new byte[320]; | |
} else { | |
// reset index | |
_playIndex = 0; | |
} | |
// within | |
// onPeriodicNotification | |
// Log some info (static) | |
WebrtcLog("Creating AudioTrack object"); | |
final int minPlayBufSize = | |
AudioTrack.getMinBufferSize(16000, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT); | |
WebrtcLog("Min play buf size = " + minPlayBufSize); | |
WebrtcLog("Min volume = " + AudioTrack.getMinVolume()); | |
WebrtcLog("Max volume = " + AudioTrack.getMaxVolume()); | |
WebrtcLog("Native sample rate = " | |
+ AudioTrack.getNativeOutputSampleRate( | |
AudioManager.STREAM_VOICE_CALL)); | |
final int playBufSize = minPlayBufSize; // 3200; // 100 ms buffer | |
// byte[] playBuffer = new byte [playBufSize]; | |
try { | |
_at = new AudioTrack( | |
AudioManager.STREAM_VOICE_CALL, | |
16000, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT, | |
playBufSize, AudioTrack.MODE_STREAM); | |
} catch (Exception e) { | |
WebrtcLog(e.getMessage()); | |
} | |
// Log some info (non-static) | |
WebrtcLog("Notification marker pos = " | |
+ _at.getNotificationMarkerPosition()); | |
WebrtcLog("Play head pos = " + _at.getPlaybackHeadPosition()); | |
WebrtcLog("Pos notification dt = " | |
+ _at.getPositionNotificationPeriod()); | |
WebrtcLog("Playback rate = " + _at.getPlaybackRate()); | |
WebrtcLog("Sample rate = " + _at.getSampleRate()); | |
// implementation of the playpos callback functions | |
// _at.setPlaybackPositionUpdateListener( | |
// new AudioTrack.OnPlaybackPositionUpdateListener() { | |
// | |
// int count = 3200; | |
// | |
// public void onPeriodicNotification(AudioTrack track) { | |
// // int wrBytes = play.write(playBuffer, count, 320); | |
// count += 320; | |
// } | |
// | |
// public void onMarkerReached(AudioTrack track) { | |
// } | |
// }); | |
// set the notification period = 160 samples | |
// int ret = _at.setPositionNotificationPeriod(160); | |
if (_playFromFile) { | |
_fr = new File("/sdcard/singleUserDemo.pcm"); | |
try { | |
_in = new FileInputStream(_fr); | |
} catch (FileNotFoundException e1) { | |
e1.printStackTrace(); | |
} | |
} | |
// try { | |
// in.read(playBuffer); | |
// } catch (IOException e) { | |
// e.printStackTrace(); | |
// } | |
_isRunningPlay = true; | |
// buffer = new byte[3200]; | |
_playThread = new Thread(_playProc); | |
// ar.startRecording(); | |
// bytesRead = 3200; | |
// recording = true; | |
_playThread.start(); | |
return 0; | |
} | |
int stopPlayAudio() { | |
if (!_isRunningPlay) { | |
return 0; | |
} | |
_isRunningPlay = false; | |
return 0; | |
} | |
private Runnable _playProc = new Runnable() { | |
public void run() { | |
// set high thread priority | |
android.os.Process.setThreadPriority( | |
android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); | |
// play all at once | |
// int wrBytes = play.write(playBuffer, 0, 320000); | |
// fill the buffer | |
// play.write(playBuffer, 0, 3200); | |
// play.flush(); | |
// start playout | |
try { | |
_at.play(); | |
} catch (IllegalStateException e) { | |
e.printStackTrace(); | |
} | |
// play with multiple writes | |
int i = 0; | |
for (; i < 3000 && _isRunningPlay; i++) { | |
if (_playFromFile) { | |
try { | |
_in.read(_playBuffer); | |
} catch (IOException e) { | |
e.printStackTrace(); | |
} | |
int wrBytes = _at.write(_playBuffer, 0 /* i * 320 */, 320); | |
} else { | |
int wrSamples = | |
_at.write(_circBuffer, _playIndex * 160, | |
160); | |
// WebrtcLog("Played 10 ms from buffer, _playIndex = " + | |
// _playIndex); | |
// WebrtcLog("Diff = " + (_recIndex - _playIndex)); | |
if (_playIndex == 49) { | |
_playIndex = 0; | |
} else { | |
_playIndex += 1; | |
} | |
} | |
// WebrtcLog("Wrote 10 ms to buffer, head = " | |
// + _at.getPlaybackHeadPosition()); | |
} | |
// stop playout | |
try { | |
_at.stop(); | |
} catch (IllegalStateException e) { | |
e.printStackTrace(); | |
} | |
// returns the number of samples that has been written | |
WebrtcLog("Test stopped, i = " + i + ", head = " | |
+ _at.getPlaybackHeadPosition()); | |
int headPos = _at.getPlaybackHeadPosition(); | |
// flush the buffers | |
_at.flush(); | |
// release the object | |
_at.release(); | |
_at = null; | |
// try { | |
// Thread.sleep() must be within a try - catch block | |
// Thread.sleep(3000); | |
// }catch (Exception e){ | |
// System.out.println(e.getMessage()); | |
// } | |
_isRunningPlay = false; | |
} | |
}; | |
int recAudioInThread() { | |
if (_isRunningRec) { | |
return 0; | |
} | |
// within | |
// onPeriodicNotification | |
// reset index | |
_recIndex = 20; | |
// Log some info (static) | |
WebrtcLog("Creating AudioRecord object"); | |
final int minRecBufSize = AudioRecord.getMinBufferSize(16000, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT); | |
WebrtcLog("Min rec buf size = " + minRecBufSize); | |
// WebrtcLog("Min volume = " + AudioTrack.getMinVolume()); | |
// WebrtcLog("Max volume = " + AudioTrack.getMaxVolume()); | |
// WebrtcLog("Native sample rate = " | |
// + AudioRecord | |
// .getNativeInputSampleRate(AudioManager.STREAM_VOICE_CALL)); | |
final int recBufSize = minRecBufSize; // 3200; // 100 ms buffer | |
try { | |
_ar = new AudioRecord( | |
MediaRecorder.AudioSource.MIC, | |
16000, | |
AudioFormat.CHANNEL_CONFIGURATION_MONO, | |
AudioFormat.ENCODING_PCM_16BIT, | |
recBufSize); | |
} catch (Exception e) { | |
WebrtcLog(e.getMessage()); | |
} | |
// Log some info (non-static) | |
WebrtcLog("Notification marker pos = " | |
+ _ar.getNotificationMarkerPosition()); | |
// WebrtcLog("Play head pos = " + _ar.getRecordHeadPosition()); | |
WebrtcLog("Pos notification dt rec= " | |
+ _ar.getPositionNotificationPeriod()); | |
// WebrtcLog("Playback rate = " + _ar.getRecordRate()); | |
// WebrtcLog("Playback rate = " + _ar.getPlaybackRate()); | |
WebrtcLog("Sample rate = " + _ar.getSampleRate()); | |
// WebrtcLog("Playback rate = " + _ar.getPlaybackRate()); | |
// WebrtcLog("Playback rate = " + _ar.getPlaybackRate()); | |
_isRunningRec = true; | |
_recThread = new Thread(_recProc); | |
_recThread.start(); | |
return 0; | |
} | |
int stopRecAudio() { | |
if (!_isRunningRec) { | |
return 0; | |
} | |
_isRunningRec = false; | |
return 0; | |
} | |
private Runnable _recProc = new Runnable() { | |
public void run() { | |
// set high thread priority | |
android.os.Process.setThreadPriority( | |
android.os.Process.THREAD_PRIORITY_URGENT_AUDIO); | |
// start recording | |
try { | |
_ar.startRecording(); | |
} catch (IllegalStateException e) { | |
e.printStackTrace(); | |
} | |
// keep recording to circular buffer | |
// for a while | |
int i = 0; | |
int rdSamples = 0; | |
short[] tempBuffer = new short[160]; // Only used for native case | |
for (; i < 3000 && _isRunningRec; i++) { | |
if (_runThroughNativeLayer) { | |
rdSamples = _ar.read(tempBuffer, 0, 160); | |
// audioLoop(tempBuffer, 160); // Insert into native layer | |
} else { | |
rdSamples = _ar.read(_circBuffer, _recIndex * 160, 160); | |
// WebrtcLog("Recorded 10 ms to buffer, _recIndex = " + | |
// _recIndex); | |
// WebrtcLog("rdSamples = " + rdSamples); | |
if (_recIndex == 49) { | |
_recIndex = 0; | |
} else { | |
_recIndex += 1; | |
} | |
} | |
} | |
// stop recording | |
try { | |
_ar.stop(); | |
} catch (IllegalStateException e) { | |
e.printStackTrace(); | |
} | |
// release the object | |
_ar.release(); | |
_ar = null; | |
// try { | |
// Thread.sleep() must be within a try - catch block | |
// Thread.sleep(3000); | |
// }catch (Exception e){ | |
// System.out.println(e.getMessage()); | |
// } | |
_isRunningRec = false; | |
// returns the number of samples that has been written | |
// WebrtcLog("Test stopped, i = " + i + ", head = " | |
// + _at.getPlaybackHeadPosition()); | |
// int headPos = _at.getPlaybackHeadPosition(); | |
} | |
}; | |
private void WebrtcLog(String msg) { | |
Log.d("*Webrtc*", msg); | |
} | |
// //////////////// Native function prototypes //////////////////// | |
private native static boolean NativeInit(); | |
private native int RunAutoTest(int testType, int extendedSel); | |
private native boolean Create(); | |
private native boolean Delete(); | |
private native int Init(boolean enableTrace, boolean useExtTrans); | |
private native int Terminate(); | |
private native int CreateChannel(); | |
private native int DeleteChannel(int channel); | |
private native int SetLocalReceiver(int channel, int port); | |
private native int SetSendDestination(int channel, int port, | |
String ipaddr); | |
private native int StartListen(int channel); | |
private native int StartPlayout(int channel); | |
private native int StartSend(int channel); | |
private native int StopListen(int channel); | |
private native int StopPlayout(int channel); | |
private native int StopSend(int channel); | |
private native int StartPlayingFileLocally(int channel, String fileName, | |
boolean loop); | |
private native int StopPlayingFileLocally(int channel); | |
private native int StartRecordingPlayout(int channel, String fileName, | |
boolean loop); | |
private native int StopRecordingPlayout(int channel); | |
private native int StartPlayingFileAsMicrophone(int channel, | |
String fileName, boolean loop); | |
private native int StopPlayingFileAsMicrophone(int channel); | |
private native int NumOfCodecs(); | |
private native int SetSendCodec(int channel, int index); | |
private native int SetVADStatus(int channel, boolean enable, int mode); | |
private native int SetNSStatus(boolean enable, int mode); | |
private native int SetAGCStatus(boolean enable, int mode); | |
private native int SetECStatus(boolean enable, int mode); | |
private native int SetSpeakerVolume(int volume); | |
private native int SetLoudspeakerStatus(boolean enable); | |
/* | |
* this is used to load the 'webrtc-voice-demo-jni' | |
* library on application startup. | |
* The library has already been unpacked into | |
* /data/data/webrtc.android.AndroidTest/lib/libwebrtc-voice-demo-jni.so | |
* at installation time by the package manager. | |
*/ | |
static { | |
Log.d("*Webrtc*", "Loading webrtc-voice-demo-jni..."); | |
System.loadLibrary("webrtc-voice-demo-jni"); | |
Log.d("*Webrtc*", "Calling native init..."); | |
if (!NativeInit()) { | |
Log.e("*Webrtc*", "Native init failed"); | |
throw new RuntimeException("Native init failed"); | |
} else { | |
Log.d("*Webrtc*", "Native init successful"); | |
} | |
} | |
} |