`
wen742538485
  • 浏览: 241121 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

android 通过数组,流播放声音的方法

阅读更多
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 {

    public static final int MENU_START_ID = Menu.FIRST ;
    public static final int MENU_STOP_ID = Menu.FIRST + 1 ;
    public static final int MENU_EXIT_ID = Menu.FIRST + 2 ;

    protected Saudioserver     m_player ;
    protected Saudioclient     m_recorder ;

  
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
  
    public boolean 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 ;
    }

  
    public boolean 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
{

    protected AudioRecord m_in_rec ;
    protected int         m_in_buf_size ;
    protected byte []     m_in_bytes ;
    protected boolean     m_keep_running ;
    protected Socket      s;
    protected DataOutputStream dout;
    protected LinkedList<byte[]>  m_in_q ;
 
    public void run()
{
      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 void init()
    {
     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) ;
 
  m_in_bytes = new byte [m_in_buf_size] ;
 
  m_keep_running = true ;
  m_in_q=new LinkedList<byte[]>();
 
     try
     {
   s=new Socket("192.168.1.100",4332);
   dout=new DataOutputStream(s.getOutputStream());
   //new Thread(R1).start();
  }
     catch (UnknownHostException e)
     {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
     catch (IOException e)
     {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

    }
  
    public void free()
{
  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

    protected AudioTrack m_out_trk ;
    protected int        m_out_buf_size ;
    protected byte []    m_out_bytes ;
    protected boolean    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();
   }
  }
}
       
分享到:
评论

相关推荐

    Android中默认系统的声音/大小修改和配置详解

    Android中默认系统的声音/大小修改和配置可以通过多种方法来实现,包括直接修改系统代码、修改数据库中的通知声音值、使用AudioManager类和使用SharedPreferences类等方法。这些方法可以满足不同的需求和场景,帮助...

    Android-语音录音以及播放

    如果是字符流播放,我们需要使用AudioTrack,先创建一个AudioTrack实例,传入音频格式参数,然后将接收到的字节数组写入到AudioTrack,最后调用play()方法播放。 在处理语音过程中,还需要注意音频的采样率、位深度...

    android 读取 assets 文件夹下的音频

    在这里放置的文件不会被Android编译器处理,而是原封不动地打包到APK中,可以在运行时通过`AssetManager`访问。 要读取`assets`文件夹下的音频文件,首先需要创建一个`AssetManager`对象,它是Android系统提供的一...

    android 音量控制 定时 震动 静音 铃声 情景模式 设置

    在Android中,可以通过AudioManager的`setRingerMode()`方法切换到静音模式。此外,某些设备还支持“请勿打扰”模式,它允许用户在特定时间段内屏蔽通知。 铃声设置涉及选择和播放不同的音频文件作为通知、来电或...

    Android实现调用震动的方法android滑动解锁震动效果的开启和取消android获取情景模式和铃声 实现震动、铃声提醒

    在Android开发中,为了增强用户体验,常常需要对设备进行震动控制和声音管理。本文将详细介绍如何在Android系统中实现调用震动服务、控制滑动解锁震动效果的开启和关闭,以及如何获取当前的情景模式和铃声设置,进而...

    android FFT 获取音频的频率并实时显示

    在Android中,音频数据的获取通常通过AudioRecord类完成。AudioRecord提供了录音功能,允许开发者读取设备麦克风输入的原始音频数据。你需要设置适当的采样率、通道数和位深度,然后启动录音。 接下来,我们需要对...

    Android高级应用源码-手机调用自带的麦克风进行声音录制,录制的同时声音转换成波形不停的变动显示在手机的屏幕上.zip

    在Android中,可以使用AudioRecord类来读取音频流,并将其转化为可处理的字节数组。 3. **波形绘制**: 将音频数据转化为可视化的波形图,需要用到Android的图形库,例如Canvas和Paint。在每一帧数据到达时,根据...

    Android代码-吹一吹效果源码.zip

    2. **数据读取与处理**:在一个循环中,使用`read()`方法读取麦克风数据,将其转换为可以分析的格式,如短整型数组。 3. **信号分析**:对读取到的数据进行滤波、峰值检测等处理,识别出吹气动作。这一步可能涉及到...

    对mid音乐文件的解析成自定义view的曲谱,可以实现播放,滚动等功能 windows 与android

    将解析出的MIDI数据转换为音频流后,可以通过这些API播放音乐。 总的来说,解析MID音乐文件并将其转化为自定义View的曲谱,是一个涉及音乐理论、图形绘制和多平台音频处理的综合项目。通过这个过程,我们可以为用户...

    如何将音频,视频文件转换为二进制格式并存储在数组中

    与VB.NET类似,C#中也是通过 `File.ReadAllBytes` 方法完成文件到字节数组的转换。这个方法简洁而高效,适用于处理各种大小的文件。 在实际应用中,将文件存储在字节数组中可以方便地进行数据操作,比如加密、解密...

    TestAudioTrack

    一旦创建,你可以通过调用`write()`方法将音频数据写入轨道进行播放。 接下来是PCM音频格式。PCM是一种无损的数字音频编码方式,它直接记录音频信号的模拟波形。在PCM中,每个采样点都对应模拟信号在某个时间点的...

    Android 海贼王连连看游戏源码

    在“Android 海贼王连连看游戏”源码中,我们可以学习如何同步游戏动作和声音效果,以及如何优化音频播放,保证流畅性。 此外,源码中还可能包含了一些游戏的状态管理,如游戏的开始、暂停、结束状态,以及得分系统...

    android游戏源码分享

    4. 声音管理:Android的AudioTrack和MediaPlayer类用于播放音效和背景音乐,源码中会展示如何控制音频流和同步。 5. 文件存储和网络通信:游戏可能会保存用户进度、高分,或者进行在线对战,这就涉及到SQLite数据库...

    Qt4使用openAL播放音乐

    在本文中,我们将深入探讨如何在Qt4框架下利用OpenAL技术进行音乐播放,并实现同时播放多个音频流的功能。OpenAL(Open Audio Library)是一种跨平台的API,专为3D音频处理和游戏音效设计,它提供了丰富的功能,包括...

    Android电子麦克风.rar

    在Android平台上,电子麦克风是实现声音采集和处理的关键组件,尤其对于开发音频应用的开发者而言,理解如何在Android系统中有效地使用麦克风至关重要。在"Android电子麦克风.rar"这个压缩包中,可能包含了关于...

    Android 测试周围环境分贝的功能源码.zip

    在Android平台上,开发一个能够测试周围环境分贝(音量)的应用是一项常见的需求。这个功能可以帮助用户了解他们的环境...通过提供的源码,开发者可以学习到如何实际操作这些步骤,从而提高自己的Android音频编程能力。

    植物大战僵尸超简AndroidDEMO

    7. **资源管理**:图像、声音和动画等资源可能被组织在res目录下,通过Android的资源ID进行引用。 8. **事件处理**:点击事件、触摸事件等用户交互会被监听和响应,以控制游戏行为。 9. **多线程**:游戏的主循环...

    Android游戏与应用开发最佳学习路线图.docx

    最后,可以通过在线视频教程进行辅助学习,如提供的Android教程链接,逐步提升技能,通过实践项目巩固理论知识,最终成为一名合格的Android开发者。在学习过程中,不断练习和实践,参加开源项目,以提高实际开发能力...

    安卓播放录音

    PCM是一种模拟信号到数字信号转换的标准方法,它通过采样、量化和编码三个步骤来表示声音。在Android中,音频数据通常以16位的整数形式存储,代表每个采样点的声压等级。 要播放PCM录音,我们需要使用Android的...

    Android利用AudioRecord类实现音频录制程序

    Android音频录制程序是Android开发中的一项重要功能,通过使用AudioRecord类可以实现音频录制程序。下面是Android音频录制程序开发的详细介绍: 一、AudioRecord类简介 AudioRecord类是Android中用于音频录制的类...

Global site tag (gtag.js) - Google Analytics