请参阅我的其他问题因为我认为它们是相关的:
Question 1
Question 2
Question 3
这是我正在使用的代码,当我按下一个按钮时,它会将麦克风上获得的音频信号传递给扬声器:
public class MainActivity extends Activity {
AudioManager am = null;
AudioRecord record =null;
AudioTrack track =null;
final int SAMPLE_FREQUENCY = 44100;
final int SIZE_OF_RECORD_ARRAY = 1024; // 1024 ORIGINAL
final int WAV_SAMPLE_MULTIPLICATION_FACTOR = 1;
int i= 0;
boolean isPlaying = false;
private volatile boolean keepThreadRunning;
private RandomAccessFile stateFile, stateFileTemp;
private File delFile, renFile;
String stateFileLoc = Environment.getExternalStorageDirectory().getPath();
class MyThread extends Thread{
private volatile boolean needsToPassThrough;
// /*
MyThread(){
super();
}
MyThread(boolean newPTV){
this.needsToPassThrough = newPTV;
}
// */
// /*
@Override
public void run(){
// short[] lin = new short[SIZE_OF_RECORD_ARRAY];
byte[] lin = new byte[SIZE_OF_RECORD_ARRAY];
int num = 0;
// /*
if(needsToPassThrough){
record.startRecording();
track.play();
}
// */
while (keepThreadRunning) {
// while (!isInterrupted()) {
num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY);
for(i=0;i<lin.length;i++)
lin[i] *= WAV_SAMPLE_MULTIPLICATION_FACTOR;
track.write(lin, 0, num);
}
// /*
record.stop();
track.stop();
record.release();
track.release();
// */
}
// */
// /*
public void stopThread(){
keepThreadRunning = false;
}
// */
}
MyThread newThread;
private void init() {
int min = AudioRecord.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
record = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT, min);
int maxJitter = AudioTrack.getMinBufferSize(SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
track = new AudioTrack(AudioManager.MODE_IN_COMMUNICATION, SAMPLE_FREQUENCY, AudioFormat.CHANNEL_OUT_MONO,
AudioFormat.ENCODING_PCM_16BIT, maxJitter, AudioTrack.MODE_STREAM);
am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
try {
stateFile = new RandomAccessFile(stateFileLoc+"/appState.txt", "rwd");
stateFileTemp = new RandomAccessFile(stateFileLoc+"/appStateTemp.txt", "rwd");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
delFile = new File(stateFileLoc+"/appState.txt");
renFile = new File(stateFileLoc+"/appStateTemp.txt");
}
@Override
protected void onResume(){
super.onResume();
// newThread.stopThread();
Log.d("MYLOG", "onResume() called");
init();
keepThreadRunning = true;
try {
if(stateFile.readInt() == 1){
isPlaying = true;
Log.d("MYLOG", "readInt == 1");
}
else{
isPlaying = false;
Log.d("MYLOG", "readInt <> 1");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// */
// newThread = new MyThread(true);
newThread = new MyThread(isPlaying);
newThread.start();
}
@Override
protected void onPause(){
super.onPause();
Log.d("MYLOG", "onPause() called");
newThread.stopThread();
// android.os.Process.killProcess(android.os.Process.myPid());
try {
if(isPlaying)
stateFileTemp.writeInt(1);
else
stateFileTemp.writeInt(0);
delFile.delete();
renFile.renameTo(delFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setVolumeControlStream(AudioManager.MODE_IN_COMMUNICATION);
Log.d("MYLOG","onCreate() called");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
protected void onDestroy() {
super.onDestroy();
newThread.stopThread();
// android.os.Process.killProcess(android.os.Process.myPid());
// killProcess(android.os.Process.myPid());
// newThread.interrupt();
delFile.delete();
Log.d("MYLOG", "onDestroy() called");
}
public void passStop(View view){
Button playBtn = (Button) findViewById(R.id.button1);
// /*
if(!isPlaying){
record.startRecording();
track.play();
isPlaying = true;
playBtn.setText("Pause");
}
else{
record.stop();
track.pause();
isPlaying=false;
playBtn.setText("Pass through");
}
// */
}
添加了文件appState.txt
和appStateTemp.txt
以保存在应用上次失去焦点时是否正在执行传递,但这可能不是很重要。我想知道的是:
在未调用record.read()
的情况下调用record.startrecording()
时会发生什么?
SIZE_OF_RECORD_ARRAY
有什么意义?我认为它至少应该是AudioRecord.getMinBufferSize()
返回的值,但是在这个程序中,即使我将它设置为1,它也不会影响输出。
如果我使用16位PCM编码,我至少需要一个短变量来存储音频样本的数字等效值。但是在这段代码中,即使我将lin变量从短数组更改为字节数组,输出也没有明显的变化。那么read函数如何将数字样本存储在数组中呢?它是否为每个样本自动分配2个字节的元素?如果是这种情况,它是以小端还是大端进行的?
答案 0 :(得分:2)
问题1和3应该很容易让您查看您的应用程序,但这里是:
1:调用record.read()而不调用record.startrecording()会发生什么?
我希望底层音频输入流没有数据流,因此read()
返回0或可能是错误代码,表示没有数据被读取。
2:SIZE_OF_RECORD_ARRAY有什么意义?我认为它至少应该是AudioRecord.getMinBufferSize()返回的值,但是在这个程序中,即使我将它设置为1,它也不会影响输出。
在getMinBufferSize
构造函数的调用中指定缓冲区大小时,AudioRecord
的值很重要。您使用SIZE_OF_RECORD_ARRAY
更改的内容只是每次调用read()
时您正在阅读的数据量 - 而且每次调用read()
不是特别好的主意字节(由于所有这些函数调用的开销),我可以想象它仍然可以工作。
3:如果我使用16位PCM编码,我至少需要一个短变量来存储音频样本的数字等效值。但是在这段代码中,即使我将lin变量从短数组更改为字节数组,输出也没有明显的变化。那么read函数如何将数字样本存储在数组中呢?它是否为每个样本自动分配2个字节的元素?如果是这种情况,它是以小端还是大端进行的?
底层本机代码始终使用byte
版本。 short
版本只是byte
版本的包装器。所以是的,在这种情况下,每个样本都会使用一对字节
至于字节序;对于绝大多数Android设备来说,它将是小端的。
答案 1 :(得分:0)
试试这个我希望100%
MediaRecorder mRecorder = null;
String mFileName;
private void startRecording() {
try {
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mFileName = getRecordDefaultFileName();
mRecorder.setOutputFile(mFileName);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
mRecorder.prepare();
} catch (IOException e) {
System.out.println("prepare() failed");
}
mRecorder.start();
} catch (Exception e) {
return;
}
}
private void stopRecording() {
try {
if (mRecorder != null) {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
} catch (Exception e) {
}
}
private String getRecordDefaultFileName() {
File wallpaperDirectory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/" + "recordingFolder" + "/");
if (!wallpaperDirectory.exists()) {
wallpaperDirectory.mkdirs();
}
return wallpaperDirectory.getAbsolutePath() + File.separator + "iarecord" + ".3gp";
}