Android實現(xiàn)邊錄邊播功能
Android可以通過MediaRecorder和AudioRecord這兩個工具來實現(xiàn)錄音,MediaRecorder直接把麥克風的數(shù)據(jù)存到文件,并且能夠直接進行編碼(如AMR,MP3等),而AudioRecord則是讀取麥克風的音頻流。本文使用AudioRecord讀取音頻流,使用AudioTrack播放音頻流,通過“邊讀邊播放”以及增大音量的方式來實現(xiàn)一個簡單的助聽器程序。
PS:由于目前的Android模擬器還不支持AudioRecord,因此本程序需要編譯之后放到真機運行。
Activity_layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<Button
android:id="@+id/bt_yinpinhuilu_testing_exit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Close"
/>
</LinearLayout>
MainActivity:
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.pm.PackageManager;
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.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.util.LinkedList;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "ktv";
/**
* 按鈕
*/
private Button bt_exit;
/**
* AudioRecord 寫入緩沖區(qū)大小
*/
protected int m_in_buf_size;
/**
* 錄制音頻對象
*/
private AudioRecord m_in_rec;
/**
* 錄入的字節(jié)數(shù)組
*/
private byte[] m_in_bytes;
/**
* 存放錄入字節(jié)數(shù)組的大小
*/
private LinkedList<byte[]> m_in_q;
/**
* AudioTrack 播放緩沖大小
*/
private int m_out_buf_size;
/**
* 播放音頻對象
*/
private AudioTrack m_out_trk;
/**
* 播放的字節(jié)數(shù)組
*/
private byte[] m_out_bytes;
/**
* 錄制音頻線程
*/
private Thread record;
/**
* 播放音頻線程
*/
private Thread play;
/**
* 讓線程停止的標志
*/
private boolean flag = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.setTitle("音頻回路");
requestAllPower();
Log.d("dfdfd", "333333333333");
init();
record = new Thread(new recordSound());
play = new Thread(new playRecord());
// 啟動錄制線程
record.start();
// 啟動播放線程
play.start();
}
private void init() {
bt_exit = findViewById(R.id.bt_yinpinhuilu_testing_exit);
Log.i(TAG, "bt_exit====" + bt_exit);
bt_exit.setOnClickListener(this);
// AudioRecord 得到錄制最小緩沖區(qū)的大小
m_in_buf_size = AudioRecord.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
// 實例化播放音頻對象
m_in_rec = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, m_in_buf_size);
// 實例化一個字節(jié)數(shù)組,長度為最小緩沖區(qū)的長度
m_in_bytes = new byte[m_in_buf_size];
// 實例化一個鏈表,用來存放字節(jié)組數(shù)
m_in_q = new LinkedList<byte[]>();
// AudioTrack 得到播放最小緩沖區(qū)的大小
m_out_buf_size = AudioTrack.getMinBufferSize(8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT);
// 實例化播放音頻對象
m_out_trk = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_16BIT, m_out_buf_size,
AudioTrack.MODE_STREAM);
// 實例化一個長度為播放最小緩沖大小的字節(jié)數(shù)組
m_out_bytes = new byte[m_out_buf_size];
}
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.bt_yinpinhuilu_testing_exit:
flag = false;
m_in_rec.stop();
m_in_rec = null;
m_out_trk.stop();
m_out_trk = null;
this.finish();
}
}
/**
* 錄音線程
*/
class recordSound implements Runnable {
@Override
public void run() {
Log.i(TAG, "........recordSound run()......");
byte[] bytes_pkg;
// 開始錄音
m_in_rec.startRecording();
while (flag) {
m_in_rec.read(m_in_bytes, 0, m_in_buf_size);
bytes_pkg = m_in_bytes.clone();
Log.i(TAG, "........recordSound bytes_pkg==" + bytes_pkg.length);
if (m_in_q.size() >= 2) {
m_in_q.removeFirst();
}
m_in_q.add(bytes_pkg);
}
}
}
/**
* 播放線程
*/
class playRecord implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
Log.i(TAG, "........playRecord run()......");
byte[] bytes_pkg = null;
// 開始播放
m_out_trk.play();
while (flag) {
try {
m_out_bytes = m_in_q.getFirst();
bytes_pkg = m_out_bytes.clone();
m_out_trk.write(bytes_pkg, 0, bytes_pkg.length);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
/**
* 錄音哦動態(tài)權(quán)限
*/
public void requestAllPower() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.RECORD_AUDIO)) {
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
for (int i = 0; i < permissions.length; i++) {
Toast.makeText(this, "" + "權(quán)限" + permissions[i] + "申請成功", Toast.LENGTH_SHORT).show();
}
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.speex">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android Canvas和Bitmap結(jié)合繪圖詳解流程
在 Android Canvas 上繪圖非常難,在繪圖時需要理解許多不同的類和概念。這篇文章中,將介紹 Android 框架中可用的一些類,它們可以讓畫布使用時更輕松2021-11-11
Templates實戰(zhàn)之更優(yōu)雅實現(xiàn)自定義View構(gòu)造方法詳解
本篇文章介紹如何利用Android Studio提供的Live Templates更優(yōu)雅實現(xiàn)自定義View的構(gòu)造方法,說句人話就是:簡化自定義View構(gòu)造參數(shù)模板代碼的編寫,實現(xiàn)自動生成,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09
Recyclerview添加頭布局和尾布局、item點擊事件詳解
這篇文章主要為大家詳細介紹了Recyclerview添加頭布局和尾布局、item點擊事件的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
Android使用RecyclerView實現(xiàn)自定義列表、點擊事件以及下拉刷新
這篇文章主要介紹了Android使用RecyclerView實現(xiàn)自定義列表、點擊事件以及下拉刷新的相關(guān)資料,需要的朋友可以參考下2016-04-04
Flutter開發(fā)Mac桌面應(yīng)用實現(xiàn)自動提取生成視頻字幕文件
這篇文章主要為大家介紹了Flutter開發(fā)Mac桌面應(yīng)用實現(xiàn)自動提取生成視頻字幕文件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
Android Handler內(nèi)存泄漏詳解及其解決方案
在android開發(fā)過程中,我們可能會遇到過令人奔潰的OOM異常,這篇文章主要介紹了Android Handler內(nèi)存泄漏詳解及其解決方案,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-08-08

