当前位置:   article > 正文

Android多媒体应用——AudioTrack, AudioRecord_android audiotrack github

android audiotrack github

github源码:https://github.com/heinsven/Test5.git

 

AudioRecord 是Android提供的用于录音的工具类。

AudioRecord管理应用的各类音频资源,记录平台的各类输入设备的音频输入。

AudioRecord实现Android录音的流程:

  • 构造一个AudioRecord对象,其中需要的最小录音缓存buffer大小可以通过getMinBufferSize方法得到。如果buffer容量过小,将导致对象构造的失败
  • 初始化一个buffer,该buffer大于等于AudioRecord对象用于写声音数据的buffer大小
  • 调用startRecording函数,开始录音
  • 创建一个数据流,一边从AudioRecord中读取声音数据到初始化的buffer,一边将buffer中数据导入数据流,生成PCM格式文件
  • 录音结束时,关闭数据流, 停止录音

 

AudioRecord由于输出的数据是原始数据PCM,MediaPlayer播放器是不能识别播放的,需要通过AudioTrack处理播放,或者把PCM转化为可以播放的格式类似wav,MP3等。

  1. public class Test5 extends AppCompatActivity {
  2. private ProgressBar mProgressBar;
  3. private Button mBtn1;
  4. private Button mBtn2;
  5. private Button mBtn3;
  6. private AudioRecord mAudioRecord;
  7. private boolean isRecording = false;
  8. private byte[] audioData;
  9. int laiyuan = MediaRecorder.AudioSource.MIC;//来源
  10. int samHz = 16000;//采样频率
  11. int shengdao = AudioFormat.CHANNEL_IN_MONO;//声道
  12. int audioFormat = AudioFormat.ENCODING_PCM_16BIT;//格式
  13. int bufferSize = 2 * AudioRecord.getMinBufferSize(samHz, shengdao, audioFormat);//缓冲区大小
  14. private String voicePath = Environment.getExternalStorageDirectory().getPath() + "/audio";
  15. private String voiceName = "/test.pcm";
  16. @Override
  17. protected void onCreate(Bundle savedInstanceState) {
  18. super.onCreate(savedInstanceState);
  19. setContentView(R.layout.activity_test5);
  20. init();
  21. }
  22. private void init() {
  23. verifyStoragePermissions(this);
  24. mProgressBar = findViewById(R.id.progressbar);
  25. mProgressBar.setVisibility(View.GONE);
  26. mBtn1 = findViewById(R.id.btn1);
  27. mBtn2 = findViewById(R.id.btn2);
  28. mBtn3 = findViewById(R.id.btn3);
  29. mBtn1.setOnClickListener(new View.OnClickListener() {
  30. @Override
  31. public void onClick(View v) {
  32. if (mAudioRecord == null) {
  33. startRecord();
  34. }
  35. }
  36. });
  37. mBtn2.setOnClickListener(new View.OnClickListener() {
  38. @Override
  39. public void onClick(View v) {
  40. if (mAudioRecord != null) {
  41. stopRecord();
  42. }
  43. }
  44. });
  45. mBtn3.setOnClickListener(new View.OnClickListener() {
  46. @Override
  47. public void onClick(View v) {
  48. try {
  49. InputStream in = new FileInputStream(voicePath + voiceName);
  50. try {
  51. ByteArrayOutputStream out = new ByteArrayOutputStream(264848);
  52. for (int b; (b = in.read()) != -1; ) {
  53. out.write(b);
  54. }
  55. audioData = out.toByteArray();
  56. } finally {
  57. in.close();
  58. }
  59. } catch (IOException e) {
  60. e.printStackTrace();
  61. }
  62. AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, samHz,
  63. AudioFormat.CHANNEL_OUT_MONO, audioFormat,
  64. audioData.length, AudioTrack.MODE_STATIC);
  65. audioTrack.write(audioData, 0, audioData.length);
  66. audioTrack.play();
  67. }
  68. });
  69. }
  70. private static final int REQUEST_EXTERNAL_STORAGE = 1;
  71. private static String[] PERMISSIONS_STORAGE = {"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.RECORD_AUDIO"};
  72. public static void verifyStoragePermissions(Activity activity) {
  73. try {
  74. //检测是否有写的权限
  75. int permission1 = ActivityCompat.checkSelfPermission(activity,
  76. "android.permission.WRITE_EXTERNAL_STORAGE");
  77. int permission2 = ActivityCompat.checkSelfPermission(activity,
  78. "android.permission.RECORD_AUDIO");
  79. if (permission1 != PackageManager.PERMISSION_GRANTED || permission2 != PackageManager.PERMISSION_GRANTED) {
  80. // 没有写的权限,去申请写的权限,会弹出对话框
  81. ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
  82. }
  83. } catch (Exception e) {
  84. e.printStackTrace();
  85. }
  86. }
  87. public void startRecord() {
  88. mAudioRecord = new AudioRecord(laiyuan, samHz, shengdao, audioFormat, bufferSize);
  89. final byte data[] = new byte[bufferSize];
  90. final File file = new File(voicePath);
  91. final File fileaudio = new File(voicePath + voiceName);
  92. if (fileaudio.exists()) {
  93. fileaudio.delete();
  94. }
  95. if (!file.exists()) {
  96. file.mkdirs();
  97. }
  98. mAudioRecord.startRecording();
  99. isRecording = true;
  100. mProgressBar.setVisibility(View.VISIBLE);
  101. new Thread(new Runnable() {
  102. @Override
  103. public void run() {
  104. FileOutputStream fos = null;
  105. try {
  106. fos = new FileOutputStream(fileaudio);
  107. } catch (FileNotFoundException e) {
  108. e.printStackTrace();
  109. }
  110. if (null != fos) {
  111. while (isRecording) {
  112. int read = mAudioRecord.read(data, 0, bufferSize);
  113. //返回正确时才读取数据
  114. if (AudioRecord.ERROR_INVALID_OPERATION != read) {
  115. try {
  116. fos.write(data);
  117. } catch (IOException e) {
  118. e.printStackTrace();
  119. }
  120. }
  121. }
  122. try {
  123. fos.close();
  124. } catch (IOException e) {
  125. e.printStackTrace();
  126. }
  127. }
  128. }
  129. }).start();
  130. }
  131. public void stopRecord() {
  132. isRecording = false;
  133. if (mAudioRecord != null) {
  134. mProgressBar.setVisibility(View.GONE);
  135. mAudioRecord.stop();
  136. mAudioRecord.release();
  137. //调用release之后必须置为null
  138. mAudioRecord = null;
  139. }
  140. }
  141. }

AudioTrack只能播放已经解码的PCM流,对于文件,也支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能播放不需要解码的wav文件

AudioTrack可以在两种模式下工作,一种是静态(static),一种是流(streaming)

MODE_STREAM:在这种模式下,通过write一次次把音频数据写到AudioTrack中。但这种工作方式每次都需要把数据从用户提供的Buffer中拷贝到AudioTrack内部的Buffer中,这在一定程度上会使引入延时。

MODE_STATIC:在play之前,只需要把所有数据通过一次write调用传递到AudioTrack中的内部缓冲区,后续就不必再传递数据了。这种模式适用于像铃声这种内存占用量较小,延时要求较高的文件。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/481290
推荐阅读
相关标签
  

闽ICP备14008679号