AudioRecord和AudioTrack类的使用

2024-02-03 10:58

本文主要是介绍AudioRecord和AudioTrack类的使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

AudioRecord和AudioTrack类是Android获取和播放音频流的重要类,放置在android.media包中。与该包中的MediaRecorder和MediaPlayer类不同, AudioRecord和AudioTrack类在获取和播放音频数据流时无需通过文件保存和文件读取,可以动态地直接获取和播放音频流,在实时处理音频数据流时非常有用

   当然,如果用户只想录音后写入文件或从文件中取得音频流进行播放,那么直接使用MediaRecorder和MediaPlayer类是首选方案,因为这两个类使用非常方便,而且成功率很高。而AudioRecord和AudioTrack类的使用却比较复杂,我们发现很多人都不能成功地使用这两个类,甚至认为Android的这两个类是不能工作的。

   其实,AudioRecord和AudioTrack类的使用虽然比较复杂,但是可以工作,我们不仅可以很好地使用了这两个类,而且还通过套接字(Socket)实现了音频数据的网络传输,做到了一端使用AudioRecord获取音频流然后通过套接字传输出去,而另一端通过套接字接收后使用AudioTrack类播放。

   下面是我们对AudioRecord和AudioTrack类在使用方面的经验总结:

   (1)创建AudioRecord和AudioTrack类对象:创建这两个类的对象比较复杂,通过对文档的反复和仔细理解,并通过多次失败的尝试,并在北理工的某个Android大牛的网上的文章启发下,我们也最终成功地创建了这两个类的对象。创建AudioRecord和AudioTrack类对象的代码如下:

AudioRecord类:

        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) ;

AudioTrack类:

        m_out_buf_size =android.media.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);

   (2)关于AudioRecord和AudioTrack类的监听函数,不用也行。

 

   (3)调试方面,包括初始化后看logcat信息,以确定类的工作状态,初始化是否成功等。

     编写好代码,没有语法错误,调用模拟器运行、调试代码时,logcat发挥了很好的功用。刚调试时,经常会出现模拟器显示出现异常,这时我们可以在代码的一些关键语句后添加如Log.d("test1","OK");这样的语句进行标识,出现异常时我们就可以在logcat窗口观察代码执行到哪里出现异常,然后进行相应的修改、调试。模拟器不会出现异常时,又遇到了录放音的问题。录音方面,刚开始选择将语音编码数据存放在多个固定大小的文件中进行传送,但是这种情况下会出现声音断续的现象,而且要反复的建立文件,比较麻烦,后来想到要进行网上传输,直接将语音编码数据以数据流的形式传送,经过验证,这种方法可行并且使代码更加简洁。放音方面,将接收到的数据流存放在一个数组中,然后将数组中数据写到AudioTrack中。刚开始只是“嘟”几声,经过检查发现只是把数据写一次,加入循环,让数据反复写到AudioTrack中,就可以听到正常的语音了。接下来的工作主要是改善话音质量与话音延迟,在进行通话的过程中,观察logcat窗口,发现向数组中写数据时会出现Bufferflow的情况,于是把重心转移到数组大小的影响上,经过试验,发现AudioRecord一次会读640个数据,然后就对录音和放音中有数组的地方进行实验修改。AudioRecord和AudioTrack进行实例化时,参数中各有一个数组大小,经过试验这个数组大小和AudioRecord和AudioTrack能正常实例化所需的最小Buffer大小(即上面实例化时的m_in_buf_size和m_out_buf_size参数)相等且服务器方进行缓存数据的数组尺寸是上述数值的2倍时,语音质量最好。由于录音和放音的速度不一致,受到北理工大牛的启发,在录音方面,将存放录音数据的数组放到LinkedList中,当LinkedList中数组个数达到2(这个也是经过试验验证话音质量最好时的数据)时,将先录好的数组中数据传送出去。经过上述反复试验和修改,最终使双方通话质量较好,且延时较短(大概有2秒钟)。

   (4)通过套接字传输和接收数据

    数据传送部分,使用的是套接字。通信双方,通过不同的端口向服务器发送请求,与服务器连接上后,开始通话向服务器发送数据,服务器通过一个套接字接收到一方的数据后,先存在一个数组中,然后将该数组中数据以数据流的形式再通过另一个套接字传送到另一方。这样就实现了双方数据的传送。

    (5)代码架构

     为避免反复录入和读取数据占用较多资源,使程序在进行录放音时不能执行其他命令,故将录音和放音各写成一个线程类,然后在主程序中,通过MENU控制通话的开始、停止、结束。

     最后说明,AudioRecord和AudioTrack类可以用,只是稍微复杂些。以下贴出双方通信的源码,希望对大家有所帮助:

主程序Daudioclient:

package cn.Daudioclient;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class Daudioclient extends Activity {
 
    publicstatic final int MENU_START_ID = Menu.FIRST ;
    publicstatic final int MENU_STOP_ID = Menu.FIRST + 1 ;
    publicstatic final int MENU_EXIT_ID = Menu.FIRST + 2 ;
 
    protectedSaudioserver    m_player ;
    protectedSaudioclient    m_recorder ;
 
   
   @Override
    public voidonCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.main);
    }
   
    publicboolean onCreateOptionsMenu(Menu aMenu)
    {
       boolean res = super.onCreateOptionsMenu(aMenu) ;

       aMenu.add(0, MENU_START_ID, 0, "START") ;
       aMenu.add(0, MENU_STOP_ID, 0, "STOP") ;
       aMenu.add(0, MENU_EXIT_ID, 0, "EXIT") ;

       return res ;
    }

   
    publicboolean onOptionsItemSelected(MenuItem aMenuItem)
    {
       switch (aMenuItem.getItemId()) {
       case MENU_START_ID:
           {
            m_player = new Saudioserver() ;
               m_recorder = new Saudioclient() ;

               m_player.init() ;
               m_recorder.init() ;

               m_recorder.start() ;
               m_player.start() ;
               
           }
           break ;
       case MENU_STOP_ID:
            
            m_recorder.free() ;
               m_player.free() ;

               m_player = null ;
               m_recorder = null ;
           }
           break ;
       case MENU_EXIT_ID:
           {
               int pid = android.os.Process.myPid() ;
               android.os.Process.killProcess(pid) ;
           }
           break ;
       default:
           break ;
       }

       return super.onOptionsItemSelected(aMenuItem);
    }
}

录音程序Saudioclient:

package cn.Daudioclient;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedList;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;

public class Saudioclient extends Thread
{
 
    protectedAudioRecord m_in_rec ; 
    protectedint        m_in_buf_size ;
    protectedbyte[]    m_in_bytes ;
    protectedboolean    m_keep_running ;
    protectedSocket     s;
    protectedDataOutputStream dout;
    protectedLinkedList<byte[]> m_in_q ;
  
    public voidrun()
 {
     try
     {
         byte [] bytes_pkg ;
           m_in_rec.startRecording() ;
           while(m_keep_running)
           {
               m_in_rec.read(m_in_bytes, 0, m_in_buf_size) ;
               bytes_pkg = m_in_bytes.clone() ;
               if(m_in_q.size() >= 2)
               {
                       dout.write(m_in_q.removeFirst() , 0, m_in_q.removeFirst().length);
                   }
                   m_in_q.add(bytes_pkg) ;
           }
    
           m_in_rec.stop() ;
           m_in_rec = null ;
           m_in_bytes = null ;
      dout.close();
       
     }
     catch(Exception e)
     {
      e.printStackTrace();
     }
    }
   
    public voidinit()
    {
    m_in_buf_size=  AudioRecord.getMinBufferSize(8000,
                       AudioFormat.CHANNEL_CONFIGURATION_MONO,
                       AudioFormat.ENCODING_PCM_16BIT);
   
  m_in_rec = newAudioRecord(MediaRecorder.AudioSource.MIC,
  8000,
  AudioFormat.CHANNEL_CONFIGURATION_MONO,
  AudioFormat.ENCODING_PCM_16BIT,
  m_in_buf_size) ;
  
  m_in_bytes = new byte[m_in_buf_size] ;
  
  m_keep_running = true ;
  m_in_q=newLinkedList<byte[]>();
  
    try
    {
   s=newSocket("192.168.1.100",4332);
   dout=newDataOutputStream(s.getOutputStream());
   //newThread(R1).start();
  }
    catch (UnknownHostException e)
    {
   // TODOAuto-generated catch block
   e.printStackTrace();
  }
    catch (IOException e)
    {
   // TODOAuto-generated catch block
   e.printStackTrace();
  }

   }
   
    public voidfree()
 {
  m_keep_running = false ;
       try {
           Thread.sleep(1000) ;
       } catch(Exception e) {
           Log.d("sleep exceptions...\n","") ;
       }
 }
}
放音程序Saudioserver:

package cn.Daudioclient;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;

public class Saudioserver extends Thread
 
    protectedAudioTrack m_out_trk ;
    protectedint       m_out_buf_size ;
    protectedbyte []   m_out_bytes ;
    protectedboolean   m_keep_running ;
 private Socket s;
 private DataInputStream din;
 public void init()
 {
  try
    {
           s=new Socket("192.168.1.100",4331);
           din=new DataInputStream(s.getInputStream());
           
           m_keep_running = true ;
       
           
           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);
        
           m_out_bytes=new byte[m_out_buf_size];
           
          // new Thread(R1).start();
           
    }
    catch(Exception e)
    {
     e.printStackTrace();
    }
 }
   
 public void free()
 {
  m_keep_running = false ;
       try {
           Thread.sleep(1000) ;
       } catch(Exception e) {
           Log.d("sleep exceptions...\n","") ;
       }
 }
 
  public void run()
  {
   byte [] bytes_pkg = null;
        m_out_trk.play() ;
        while(m_keep_running) {
            try
            {
             din.read(m_out_bytes);
                bytes_pkg = m_out_bytes.clone() ;
                m_out_trk.write(bytes_pkg, 0, bytes_pkg.length) ;
            }
            catch(Exception e)
            {
             e.printStackTrace();
            }
            
        }
        
        m_out_trk.stop() ;
        m_out_trk = null ;
        try {
    din.close();
   } catch(IOException e) {
    //TODO Auto-generated catch block
    e.printStackTrace();
   }
  }
}

这篇关于AudioRecord和AudioTrack类的使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/673932

相关文章

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

ModelMapper基本使用和常见场景示例详解

《ModelMapper基本使用和常见场景示例详解》ModelMapper是Java对象映射库,支持自动映射、自定义规则、集合转换及高级配置(如匹配策略、转换器),可集成SpringBoot,减少样板... 目录1. 添加依赖2. 基本用法示例:简单对象映射3. 自定义映射规则4. 集合映射5. 高级配置匹

Spring 框架之Springfox使用详解

《Spring框架之Springfox使用详解》Springfox是Spring框架的API文档工具,集成Swagger规范,自动生成文档并支持多语言/版本,模块化设计便于扩展,但存在版本兼容性、性... 目录核心功能工作原理模块化设计使用示例注意事项优缺点优点缺点总结适用场景建议总结Springfox 是

嵌入式数据库SQLite 3配置使用讲解

《嵌入式数据库SQLite3配置使用讲解》本文强调嵌入式项目中SQLite3数据库的重要性,因其零配置、轻量级、跨平台及事务处理特性,可保障数据溯源与责任明确,详细讲解安装配置、基础语法及SQLit... 目录0、惨痛教训1、SQLite3环境配置(1)、下载安装SQLite库(2)、解压下载的文件(3)、

使用Python绘制3D堆叠条形图全解析

《使用Python绘制3D堆叠条形图全解析》在数据可视化的工具箱里,3D图表总能带来眼前一亮的效果,本文就来和大家聊聊如何使用Python实现绘制3D堆叠条形图,感兴趣的小伙伴可以了解下... 目录为什么选择 3D 堆叠条形图代码实现:从数据到 3D 世界的搭建核心代码逐行解析细节优化应用场景:3D 堆叠图

Springboot如何正确使用AOP问题

《Springboot如何正确使用AOP问题》:本文主要介绍Springboot如何正确使用AOP问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录​一、AOP概念二、切点表达式​execution表达式案例三、AOP通知四、springboot中使用AOP导出

Navicat数据表的数据添加,删除及使用sql完成数据的添加过程

《Navicat数据表的数据添加,删除及使用sql完成数据的添加过程》:本文主要介绍Navicat数据表的数据添加,删除及使用sql完成数据的添加过程,具有很好的参考价值,希望对大家有所帮助,如有... 目录Navicat数据表数据添加,删除及使用sql完成数据添加选中操作的表则出现如下界面,查看左下角从左