接下来笔者介绍一下Android中播放音频的几种方式,android.media包下面包含了Android开发中媒体类,当然笔者不会依次去介绍,下面介绍几个音频播放中常用的类:

1.使用MediaPlayer播放音频

MediaPlayer的功能很强大,下面附上一张该类封装音频的生命周期图:

MediaPlayer支持AAC、AMR、FLAC、MP3、MIDI、OGG、PCM等格式,MediaPlayer可以通过设置元数据和播放源来音频。

1.1播放Raw文件夹下面音频的元数据

  1. //直接创建,不需要设置setDataSource
  2. MediaPlayer mMediaPlayer
    mMediaPlayer=MediaPlayer.create(this, R.raw.audio);
    mMediaPlayer.start();

1.2通过设置播放源来播放音频文件

setDataSource(String path)

  1. //如果从sd卡中加载音乐
    //经过笔者的测试,需要加载sd卡的读权限,这里明明是从sd卡中读取文件
    // <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    //然后就可以利用 Environment.getExternalStorageDirectory() 获取SD卡的根目录了,一般为/storage/emulated/0
    //接下来把xxx.wav放到SD的根目录下,就可以获得文件的路径了 String path=Environment.getExternalStorageDirectory()+"/xxx.wav";
  2. mMediaPlayer.setDataSource(path) ;
  3. //如果从网络加载音乐,如果是从网络中加载那么需要设置网络权限
    //<uses-permission android:name="android.permission.INTERNET"/>
  4. mMediaPlayer.setDataSource("http://..../xxx.mp3") ;
  5. //需使用异步缓冲
  6. mMediaPlayer.prepareAsync() ;

setDataSource(FileDescriptor fd)

  1. //需将资源文件放在assets文件夹
  2. AssetFileDescriptor fd = getAssets().openFd("samsara.mp3");
  3. mMediaPlayer.setDataSource(fd.getFileDescriptor());//经过笔者的测试,发现这个方法有时候不能播放成功,尽量使用该方法的另一个重载方法 setDataSource(FileDescptor fd,long offset,long length)
  4. mMediaPlayer.prepare() ;
  5.  
  6. Ps:此方法系统需大于等于android

setDataSource(Context context,Uri uri)

  1. 这个方法没什么好说的,一般通过ContentProvider获取Android系统提供
  2. 的共享music获取uri,然后设置数据播放

setDataSource(FileDescptor fd,long offset,long length)

  1. //需将资源文件放在assets文件夹
  2. AssetFileDescriptor fd = getAssets().openFd("samsara.mp3");
  3. mMediaPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
  4. mMediaPlayer.prepare();

设置完数据源,不要忘记prepare(),尽量使用异步prepareAync(),这样不会阻塞UI线程。

1.3 MediaPlayer的常用方法

  1. start();//开始播放
  2. pause();//暂停播放
  3. reset()//清空MediaPlayer中的数据
  4. setLooping(boolean);//设置是否循环播放
  5. seekTo(msec)//定位到音频数据的位置,单位毫秒
  6. stop();//停止播放
  7. relase();//释放资源

2.使用SoundPool播放音频

SoundPool支持多个音频文件同时播放(组合音频也是有上限的),延时短,比较适合短促、密集的场景,是游戏开发中音效播放的福音。

2.1 SoundPool实例化方式

1. new SoundPool(适用与5.0以下)

SoundPool(int maxStreams, int streamType, int srcQuality)
    从android5.0开始此方法被标记为过时,稍微说以下几个参数。
      1.maxStreams :允许同时播放的流的最大值

  2.streamType :音频流的类型描述,
                   在Audiomanager中有种类型声明,游戏应用通常会使用流媒体音乐(AudioManager.STREAM_MUSIC)
                   
      3. srcQuality:采样率转化质量,默认值为0 。

2. SoundPool.Builder(从5.0开始支持)

//设置描述音频流信息的属性
    AudioAttributes abs = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build() ;
    SoundPool mSoundPoll =  new SoundPool.Builder()
                    .setMaxStreams(100)   //设置允许同时播放的流的最大值
                    .setAudioAttributes(abs)   //完全可以设置为null
                    .build() ;

2.2 SoundPool的几个重要的方法

  1. // 几个load方法和上文提到的MediaPlayer基本一致,这里的每个load都会返回一个SoundId值,这个值可以用来播放和卸载音乐。
  2. //------------------------------------------------------------
  3.  
  4. int load(AssetFileDescriptor afd, int priority)
  5.  
  6. int load(Context context, int resId, int priority)
  7.  
  8. int load(String path, int priority)
  9.  
  10. int load(FileDescriptor fd, long offset, long length, int priority)
  11.  
  12. //-------------------------------------------------------------
  13.  
  14. // 通过流id暂停播放
  15. final void pause(int streamID)
  16.  
  17. // 播放声音,soundID:音频id(这个id来自load的返回值); left/rightVolume:左右声道(默认1,1);loop:循环次数(-1无限循环,0代表不循环);rate:播放速率(1为标准),该方法会返回一个streamID,如果StreamID为0表示播放失败,否则为播放成功
  18. final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
  19.  
  20. //释放资源(很重要)
  21. final void release()
  22.  
  23. //恢复播放
  24. final void resume(int streamID)
  25.  
  26. //设置指定id的音频循环播放次数
  27. final void setLoop(int streamID, int loop)
  28.  
  29. //设置加载监听(因为加载是异步的,需要监听加载,完成后再播放)
  30. void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
  31.  
  32. //设置优先级(同时播放个数超过最大值时,优先级低的先被移除)
  33. final void setPriority(int streamID, int priority)
  34.  
  35. //设置指定音频的播放速率,0.5~2.0(rate>1:加快播放,反之慢速播放)
  36. final void setRate(int streamID, float rate)
  37.  
  38. //停止指定音频播放
  39. final void stop(int streamID)
  40.  
  41. //卸载指定音频,soundID来自load()方法的返回值
  42. final boolean unload(int soundID)
  43.  
  44. //暂停所有音频的播放
  45. final void autoPause()
  46.  
  47. //恢复所有暂停的音频播放
  48. final void autoResum()

下面简单演示一下SoundPool如何播放音乐:

  1. protected void onCreate(Bundle savedInstanceState) {
  2. super.onCreate(savedInstanceState);
  3. setContentView(R.layout.activity_main);
  4. SoundPool soundPool=new SoundPool(100,AudioManager.STREAM_MUSIC,0);//构建对象
  5. int soundId=soundPool.load(context,R.raw.test,1);//加载资源,得到soundId
  6. int streamId= soundPool.play(soundId, 1,1,1,-1,1);//播放,得到StreamId
  7. // soundPool.stop(streamId);//暂停
  8. }

3.使用AudioTrack播放音频

AudioTrack属于更偏底层的音频播放,MediaPlayerService的内部就是使用了AudioTrack。

AudioTrack用于单个音频播放和管理,相比于MediaPlayer具有:精炼、高效的优点。
更适合实时产生播放数据的情况,如加密的音频,
MediaPlayer是束手无策的,AudioTrack却可以。

AudioTrack用于播放PCM(PCM无压缩的音频格式)音乐流的回放,
如果要播需放其它格式音频,需要响应的解码器,
这也是AudioTrack用的比较少的原因,需要自己解码音频。

AudioTreack的2种播放模式
静态模式—static
静态的言下之意就是数据一次性交付给接收方。好处是简单高效,只需要进行一次操作就完成了数据的传递;缺点当然也很明显,对于数据量较大的音频回放,显然它是无法胜任的,因而通常只用于播放铃声、系统提醒等对内存小的操作

流模式streaming
流模式和网络上播放视频是类似的,即数据是按照一定规律不断地传递给接收方的。理论上它可用于任何音频播放的场景,不过我们一般在以下情况下采用:
    音频文件过大
    音频属性要求高,比如采样率高、深度大的数据
    音频数据是实时产生的,这种情况就只能用流模式了

通过write(byte[], int, int), write(short[], int, int)等方法推送解码数据到AudioTrack

4.使用Ringtone播放音频

Ringtone为铃声、通知和其他类似声音提供快速播放的方法,这里还不得不提到一个管理类”RingtoneManager”,提供系统铃声列表检索方法,并且,Ringtone实例需要从RingtoneManager获取。

1. 获取实例

  1. //获取实例方法,均为RingtoneManager类提供
  2.  
  3. //1.通过铃声uri获取
  4. static Ringtone getRingtone(Context context, Uri ringtoneUri)
  5.  
  6. //2.通过铃声检索位置获取
  7. Ringtone getRingtone(int position)

2. RingtoneManager几个重要的方法

  1. 1. // 两个构造方法
  2. RingtoneManager(Activity activity)
  3. RingtoneManager(Context context)
  4.  
  5. 2. // 获取指定声音类型(铃声、通知、闹铃等)的默认声音的Uri
  6. static Uri getDefaultUri(int type)
  7.  
  8. 3. // 获取系统所有Ringtone的cursor
  9. Cursor getCursor()
  10.  
  11. 4. // 获取cursor指定位置的Ringtone uri
  12. Uri getRingtoneUri(int position)
  13.  
  14. 5. // 判断指定Uri是否为默认铃声
  15. static boolean isDefault(Uri ringtoneUri)
  16.  
  17. 6. //获取指定uri的所属类型
  18. static int getDefaultType(Uri defaultRingtoneUri)
  19.  
  20. 7. //将指定Uri设置为指定声音类型的默认声音
  21. static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri)

从api看,Ringtone和RingtoneManager还是比较简单的,不多做解释了,直接放上一段使用代码。

  1. /**
  2. * 播放来电铃声的默认音乐
  3. */
  4. private void playRingtoneDefault(){
  5. Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE) ;
  6. Ringtone mRingtone = RingtoneManager.getRingtone(this,uri);
  7. mRingtone.play();
  8. }
  9.  
  10. /**
  11. * 随机播放一个Ringtone(有可能是提示音、铃声等)
  12. */
  13. private void ShufflePlayback(){
  14. RingtoneManager manager = new RingtoneManager(this) ;
  15. Cursor cursor = manager.getCursor();
  16. int count = cursor.getCount() ;
  17. int position = (int)(Math.random()*count) ;
  18. Ringtone mRingtone = manager.getRingtone(position) ;
  19. mRingtone.play();
  20. }

最后记得添加权限:

<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

其实,Rington这个类比较简单,只需要掌握,播放、停止(paly(),stop())等方法就可以了,而RingtoneManager却是比较重要的。

5.总结

1.对于延迟度要求不高,并且希望能够更全面的控制音乐的播放,MediaPlayer比较适合
2.声音短小,延迟度小,并且需要几种声音同时播放的场景,适合使用SoundPool
3.播放大文件音乐,如WAV无损音频和PCM无压缩音频,可使用更底层的播放方式AudioTrack。它支持流式播放,可以读取(可来自本地和网络)音频流,却播放延迟较小。
ps:据我测试AudioTrack直接支持WAV和PCM,其他音频需要解码成PCM格式才能播放。(其他无损格式没有尝试,有兴趣可以使本文提供的例子测试一下)
4. .jet的音频比较少见(有的游戏中在使用),可使用专门的播放器JetPlayer播放
5.对于系统类声音的播放和操作,Ringtone更适合、

Android中播放音乐的几种方式

前言

前几天一直在研究RxJava2,也写了记录了几篇博客,但因为工作任务原因,需要研究音频相关的知识,暂时放下Rxjava,本文的demo中,MediaPalyer

部分使用RxJava编写一点逻辑,其中涉及,RxJava2的被压、解除订阅等知识点,虽然简单,最起码没有丢了RxJava,后续Rxjava会继续研究,做记录.

andorid提供了对声音和视频处理的api包android.media.本文编写了针对这几种方式播放的Demo,文章最后贴出。

MediaPlayer播放音频

对于android音频的播放,这个类可能是大家最熟悉的了,从入门就一直想编写一个自己的音乐播放器,有木有?MediaPlayer确实强大,提供了对音频播放的各种控制,生命周期:

都很熟悉了,讲几个重点,其余不啰嗦了

1. MediaPlayer支持:AAC、AMR、FLAC、MP3、MIDI、OGG、PCM等格式
2. 播放Raw下的元数据

  1. //直接创建,不需要设置setDataSource
  2. mMediaPlayer=MediaPlayer.create(this, R.raw.audio);
  3. mMediaPlayer.start();
  • 1
  • 2
  • 3
  • 4

3. MediaPlayer设置播放源的4中方式

  • setDataSource (String path)
  1. //从sd卡中加载音乐
  2. mMediaPlayer.setDataSource("../music/samsara.mp3") ;
  3. //从网路加载音乐
  4. mMediaPlayer.setDataSource("http://..../xxx.mp3") ;
  5. //需使用异步缓冲
  6. mMediaPlayer.prepareAsync() ;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • setDataSource (FileDescriptor fd)
  1. //需将资源文件放在assets文件夹
  2. AssetFileDescriptor fd = getAssets().openFd("samsara.mp3");
  3. mMediaPlayer.setDataSource(fd)
  4. mMediaPlayer.prepare() ;
  5. Ps:此方法系统需大于等于android
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • setDataSource (Context context, Uri uri)
  1. 这个方法没什么好说的,一般通过ContentProvider获取Android系统提供
  2. 的共享music获取uri,然后设置数据播放
  • 1
  • 2
  • setDataSource (FileDescriptor fd, long offset, long length)
  1. //需将资源文件放在assets文件夹
  2. AssetFileDescriptor fd = getAssets().openFd("samsara.mp3");
  3. mMediaPlayer.setDataSource(fd, fd.getStartOffset(), fd.getLegth())
  4. mMediaPlayer.prepare() ;
  • 1
  • 2
  • 3
  • 4
  • 5

4. 注意点

  • 设置完数据源,不要忘记prepare(),尽量使用异步prepareAync(),这样不会阻塞UI线程。
  • 播放完毕即使释放资源

    mediaPlayer.stop();
    mediaPlayer.release();
    mediaPlayer = null;

不足

资源占用量较高、延迟时间较长、不支持多个音频同时播放等

  • ### MeidaPlayer demo片段
  1. //创建播放时间格式化工具
  2. mFormat = new SimpleDateFormat("mm:ss");
  3. //创建MediaPlayer和设置监听
  4. mPlayer = new MediaPlayer() ;
  5. mSeekBar.setOnSeekBarChangeListener(new MySeekBarChangeListener());
  6. mPlayer.setOnPreparedListener(new MyOnPrepareListener());
  7. mPlayer.setOnCompletionListener(new MyOnCompletionListener());
  8. /**
  9. * 从assets资源文件夹中播放
  10. * @param name
  11. */
  12. private void playSoundFromA(String name) {
  13. if (mPlayer.isPlaying()) {
  14. mPlayer.stop();
  15. }
  16. // 设置当前播放歌曲的名字
  17. title.setText(names[current]);
  18. mPlayer.reset();
  19. AssetFileDescriptor afd = getAssetFileDescriptor(name);
  20. try {
  21. mPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
  22. hasResource = true;
  23. mPlayer.prepareAsync();
  24. } catch (IOException e) {
  25. e.printStackTrace();
  26. }
  27. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

SoundPool播放音频

SoundPool支持多个音频文件同时播放(组合音频也是有上限的),延时短,比较适合短促、密集的场景,是游戏开发中音效播放的福音。

SoundPool实例化方式

1. new SoundPool(适用与5.0以下)
  1. SoundPool(int maxStreams, int streamType, int srcQuality)
  2. android5.0开始此方法被标记为过时,稍微说以下几个参数。
  3. 1.maxStreams :允许同时播放的流的最大值
  4. 2.streamType :音频流的类型描述,
  5. Audiomanager中有种类型声明,游戏应用通常会使用流媒体音乐。
  6. 3. srcQuality:采样率转化质量
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
2. SoundPool.Builder(从5.0开始支持)
  1. //设置描述音频流信息的属性
  2. AudioAttributes abs = new AudioAttributes.Builder()
  3. .setUsage(AudioAttributes.USAGE_MEDIA)
  4. .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
  5. .build() ;
  6. SoundPool mSoundPoll = new SoundPool.Builder()
  7. .setMaxStreams(100) //设置允许同时播放的流的最大值
  8. .setAudioAttributes(abs) //完全可以设置为null
  9. .build() ;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
3. 几个重要的方法
  1. // 几个load方法和上文提到的MediaPlayer基本一致,不做多的解释
  2. //------------------------------------------------------------
  3. int load(AssetFileDescriptor afd, int priority)
  4. int load(Context context, int resId, int priority)
  5. int load(String path, int priority)
  6. int load(FileDescriptor fd, long offset, long length, int priority)
  7. //-------------------------------------------------------------
  8. // 通过流id暂停播放
  9. final void pause(int streamID)
  10. // 播放声音,soundID:音频id; left/rightVolume:左右声道(默认1,1);loop:循环次数(-1无限循环);rate:播放速率(1为标准)
  11. final int play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
  12. //释放资源(很重要)
  13. final void release()
  14. //恢复播放
  15. final void resume(int streamID)
  16. //设置指定id的音频循环播放次数
  17. final void setLoop(int streamID, int loop)
  18. //设置加载监听(因为加载是异步的,需要监听加载,完成后再播放)
  19. void setOnLoadCompleteListener(SoundPool.OnLoadCompleteListener listener)
  20. //设置优先级(同时播放个数超过最大值时,优先级低的先被移除)
  21. final void setPriority(int streamID, int priority)
  22. //设置指定音频的播放速率,0.5~2.0(rate>1:加快播放,反之慢速播放)
  23. final void setRate(int streamID, float rate)
  24. //停止指定音频播放
  25. final void stop(int streamID)
  26. //卸载指定音频
  27. final boolean unload(int soundID)
  28. //暂停所有音频的播放
  29. final void autoPause()
  30. //恢复所有暂停的音频播放
  31. final void autoResum()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

以上方法基本上是SoundPool的所有方法了,也都很常用。

4. 区分2个概念

看了Sounpool的api,是不是感觉对 streamID 和 soundID 一脸懵逼?
1. soundID:加载音乐资源时的返回值,int load(String path, int priority),这个int返回值就是soundID
2. streamID:播放时返回的值,即play()方法的返回值

这两个值都很重要,需要缓存下来

5. SoundPool Demo片段
  1. 注:我把SoundPool做了简单封装,SoundPoolUtil,会在文末上传,
  2. 有兴趣可下载看一下,时间比较急,还有很多不足的地方
  3. //初始化SoundPool
  4. if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
  5. AudioAttributes aab = new AudioAttributes.Builder()
  6. .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
  7. .setUsage(AudioAttributes.USAGE_MEDIA)
  8. .build() ;
  9. mSoundPool = new SoundPool.Builder()
  10. .setMaxStreams(10)
  11. .setAudioAttributes(aab)
  12. .build() ;
  13. }else{
  14. mSoundPool = new SoundPool(60, AudioManager.STREAM_MUSIC,8) ;
  15. }
  16. mSoundPool = new SoundPool(60, AudioManager.STREAM_MUSIC,8) ;
  17. //设置资源加载监听
  18. mSoundPool.setOnLoadCompleteListener(new MyOnLoadCompleteListener());
  19. //加载资源
  20. /**
  21. * 加载指定路径列表的资源
  22. * @param map
  23. */
  24. public void loadR(Map<String, String> map){
  25. Set<Map.Entry<String, String>> entries = map.entrySet();
  26. for(Map.Entry<String, String> entry : entries){
  27. String key = entry.getKey() ;
  28. if(checkSoundPool()){
  29. if(!idCache.containsKey(key)){
  30. idCache.put(key, mSoundPool.load(entry.getValue(),1)) ;
  31. }
  32. }
  33. }
  34. }
  35. /**
  36. * 播放指定音频,并返用于停止、暂停、恢复的StreamId
  37. * @param name
  38. * @param times
  39. * @return
  40. */
  41. public int play(String name, int times){
  42. return this.play(name,1,1,1,times,1) ;
  43. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

AudioTrack播放音频

  1. AudioTrack属于更偏底层的音频播放,MediaPlayerService的内部就是使用了AudioTrack
  2. AudioTrack用于单个音频播放和管理,相比于MediaPlayer具有:精炼、高效的优点。
  3. 更适合实时产生播放数据的情况,如加密的音频,
  4. MediaPlayer是束手无策的,AudioTrack却可以。
  5. AudioTrack用于播放PCM(PCM无压缩的音频格式)音乐流的回放,
  6. 如果需要播放其它格式音频,需要响应的解码器,
  7. 这也是AudioTrack用的比较少的原因,需要自己解码音频。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

AudioTreack的2种播放模式

静态模式—static

静态的言下之意就是数据一次性交付给接收方。好处是简单高效,只需要进行一次操作就完成了数据的传递;缺点当然也很明显,对于数据量较大的音频回放,显然它是无法胜任的,因而通常只用于播放铃声、系统提醒等对内存小的操作

流模式streaming

流模式和网络上播放视频是类似的,即数据是按照一定规律不断地传递给接收方的。理论上它可用于任何音频播放的场景,不过我们一般在以下情况下采用:

  • 音频文件过大

  • 音频属性要求高,比如采样率高、深度大的数据

  • 音频数据是实时产生的,这种情况就只能用流模式了

  1. 通过write(byte[], int, int), write(short[], int, int)
  2. write(float[], int, int, int)等方法推送解码数据到AudioTrack
  • 1
  • 2
  • 3

使用Demo

  1. private void jetPlayStream(){
  2. new Thread(new Runnable() {
  3. @RequiresApi(api = Build.VERSION_CODES.M)
  4. @Override
  5. public void run() {
  6. // 获取最小缓冲区
  7. int bufSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
  8. // 实例化AudioTrack(设置缓冲区为最小缓冲区的2倍,至少要等于最小缓冲区)
  9. AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO,
  10. AudioFormat.ENCODING_PCM_16BIT, bufSize*2, AudioTrack.MODE_STREAM);
  11. // 设置音量
  12. audioTrack.setVolume(2f) ;
  13. // 设置播放频率
  14. audioTrack.setPlaybackRate(10) ;
  15. audioTrack.play();
  16. // 获取音乐文件输入流
  17. InputStream is = getResources().openRawResource(R.raw.zbc);
  18. byte[] buffer = new byte[bufSize*2] ;
  19. int len ;
  20. try {
  21. while((len=is.read(buffer,0,buffer.length)) != -1){
  22. System.out.println("读取数据中...");
  23. // 将读取的数据,写入Audiotrack
  24. audioTrack.write(buffer,0,buffer.length) ;
  25. }
  26. is.close();
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }).start();
  32. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

更深入的研究,请参考以下博客
http://blog.csdn.net/edmond999/article/details/18600323
http://blog.csdn.net/conowen/article/details/7799155/

AsyncPlayer播放音频

1.介绍

从名字就可看出AsyncPlayer属于异步播放器,官方给出的说明是:所有工作都在子线程进行,不影响调用线程任何操作。

AsyncPlayer就是对MediaPlayer的一次简单的封装,对MediaPlaer所有的操作都在新开线程中执行。

AsyncPlayer只适合简单的异步播放,不能控制进度,只能开始或停止播放。如果播放在此调用play()方法,AsyncPlayer会停止当前播放,开始新的播放。

播放源码


  1. /**
  2. *内部线程类
  3. **/
  4. private final class Thread extends java.lang.Thread {
  5. public void run() {
  6. while (true) {
  7. Command cmd = null;
  8. synchronized (mCmdQueue) {
  9. //取出链表中新加入的cmd
  10. cmd = mCmdQueue.removeFirst();
  11. }
  12. switch (cmd.code) {
  13. case PLAY:
  14. if (mDebug) Log.d(mTag, "PLAY");
  15. //调用MediaPlayer播放
  16. startSound(cmd);
  17. break;
  18. }
  19. ....
  20. }
  21. }
  22. }
  23. private void startSound(Command cmd) {
  24. // Preparing can be slow, so if there is something else
  25. // is playing, let it continue until we're done, so
  26. if (mDebug) Log.d(mTag, "Starting playback");
  27. MediaPlayer player = new MediaPlayer();
  28. player.setAudioStreamType(cmd.stream);
  29. player.setDataSource(cmd.context, cmd.uri);
  30. player.setLooping(cmd.looping);
  31. player.prepare();
  32. player.start();
  33. ....
  34. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

2.简单demo

  1. 十分简单,不再贴出,可以在文末Demo中看到
  • 1

JetPlayer播放音频

Jet是由OHA联盟成员SONiVOX开发的一个交互音乐引擎。其包括两部分:JET播放器和JET引擎。JET常用于控制游戏的声音特效,采用MIDI(Musical Instrument Digital Interface)格式。

ps:以后遇到手机中的”.jet”文件,就只到它究竟是什么东东了。。。

  • 获取实例
  1. //获取JetPlayer播放器
  2. JetPlayer mJet = JetPlayer.getJetPlayer() ;
  • 1
  • 2
  • 3
  • 几个重要方法
  1. // 清空分段队列,并清除所有要进行播放的剪辑。
  2. 1. boolean clearQueue() //每次播放前,记得做一次清空操作
  3. // 加载jet文件的方法
  4. 2. boolean loadJetFile(String path)
  5. boolean loadJetFile(AssetFileDescriptor afd)
  6. // 开始播放
  7. 3. boolean play()
  8. // 暂停播放
  9. 4. boolean pause()
  10. // 释放资源
  11. 5. void release()
  12. // 指定jet队列的播放序列(调用play()前需要调用此方法)
  13. 6. boolean queueJetSegment(int segmentNum, int libNum, int repeatCount, int transpose, int muteFlags, byte userID)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • JetPlayer Demo
  1. private void jetPlayer(){
  2. // 获取JetPlayer播放器
  3. JetPlayer mJet = JetPlayer.getJetPlayer() ;
  4. //清空播放队列
  5. mJet.clearQueue() ;
  6. //绑定事件监听
  7. mJet.setEventListener(new JetPlayer.OnJetEventListener() {
  8. //播放次数记录
  9. int playNum = 1 ;
  10. @Override
  11. public void onJetEvent(JetPlayer player, short segment, byte track, byte channel, byte controller, byte value) {
  12. Log.i(TAG,"----->onJetEvent") ;
  13. }
  14. @Override
  15. public void onJetUserIdUpdate(JetPlayer player, int userId, int repeatCount) {
  16. Log.i(TAG,"----->onJetUserIdUpdate") ;
  17. }
  18. @Override
  19. public void onJetNumQueuedSegmentUpdate(JetPlayer player, int nbSegments) {
  20. Log.i(TAG,"----->onJetNumQueuedSegmentUpdate") ;
  21. }
  22. @Override
  23. public void onJetPauseUpdate(JetPlayer player, int paused) {
  24. Log.i(TAG,"----->onJetPauseUpdate") ;
  25. if(playNum == 2){
  26. playNum = -1 ;
  27. //释放资源,并关闭jet文件
  28. player.release();
  29. player.closeJetFile() ;
  30. }else{
  31. playNum++ ;
  32. }
  33. }
  34. });
  35. //加载资源
  36. mJet.loadJetFile(getResources().openRawResourceFd(R.raw.level1)) ;
  37. byte sSegmentID = 0 ;
  38. //指定播放序列
  39. mJet.queueJetSegment(0, 0, 0, 0, 0, sSegmentID);
  40. mJet.queueJetSegment(1, 0, 1, 0, 0, sSegmentID);
  41. //开始播放
  42. mJet.play() ;
  43. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

Ringtone

Ringtone为铃声、通知和其他类似声音提供快速播放的方法,这里还不得不提到一个管理类”RingtoneManager”,提供系统铃声列表检索方法,并且,Ringtone实例需要从RingtoneManager获取。

1. 获取实例

  1. 获取实例方法,均为RingtoneManager类提供
  2. //通过铃声uri获取
  3. static Ringtone getRingtone(Context context, Uri ringtoneUri)
  4. //通过铃声检索位置获取
  5. Ringtone getRingtone(int position)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

其实,Rington这个类比较简单,只需要掌握,播放、停止(paly(),stop())等方法就可以了,而RingtoneManager却是比较重要的。

2. RingtoneManager几个钟要的方法

  1. 1. // 两个构造方法
  2. RingtoneManager(Activity activity)
  3. RingtoneManager(Context context)
  4. 2. // 获取指定声音类型(铃声、通知、闹铃等)的默认声音的Uri
  5. static Uri getDefaultUri(int type)
  6. 3. // 获取系统所有Ringtone的cursor
  7. Cursor getCursor()
  8. 4. // 获取cursor指定位置的Ringtone uri
  9. Uri getRingtoneUri(int position)
  10. 5. // 判断指定Uri是否为默认铃声
  11. static boolean isDefault(Uri ringtoneUri)
  12. 6. //获取指定uri的所属类型
  13. static int getDefaultType(Uri defaultRingtoneUri)
  14. 7. //将指定Uri设置为指定声音类型的默认声音
  15. static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

从api看,Ringtone和RingtoneManager还是比较简单的,不多做解释了,直接放上一段使用代码。

  1. /**
  2. * 播放来电铃声的默认音乐
  3. */
  4. private void playRingtoneDefault(){
  5. Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE) ;
  6. Ringtone mRingtone = RingtoneManager.getRingtone(this,uri);
  7. mRingtone.play();
  8. }
  9. /**
  10. * 随机播放一个Ringtone(有可能是提示音、铃声等)
  11. */
  12. private void ShufflePlayback(){
  13. RingtoneManager manager = new RingtoneManager(this) ;
  14. Cursor cursor = manager.getCursor();
  15. int count = cursor.getCount() ;
  16. int position = (int)(Math.random()*count) ;
  17. Ringtone mRingtone = manager.getRingtone(position) ;
  18. mRingtone.play();
  19. }
  20. //记得添加下面两个权限
  21. <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
  22. <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

总结

以上介绍了几种播放方式,可以说各有优劣。
本文写的不够详细,只是大概的介绍一下几种播放方式。在此做下简单的总结。

  1. 对于延迟度要求不高,并且希望能够更全面的控制音乐的播放,MediaPlayer比较适合
  2. 声音短小,延迟度小,并且需要几种声音同时播放的场景,适合使用SoundPool
  3. 对于简单的播放,不需要复杂控制的播放,可以给使用AsyncPlayer,所有操作均在子线程不阻塞UI
  4. 播放大文件音乐,如WAV无损音频和PCM无压缩音频,可使用更底层的播放方式AudioTrack。它支持流式播放,可以读取(可来自本地和网络)音频流,却播放延迟较小。
    ps:据我测试AudioTrack直接支持WAV和PCM,其他音频需要解码成PCM格式才能播放。(其他无损格式没有尝试,有兴趣可以使本文提供的例子测试一下)
  5. .jet的音频比较少见(有的游戏中在使用),可使用专门的播放器JetPlayer播放
  6. 对于系统类声音的播放和操作,Ringtone更适合(主要是掌握好RingtoneManager)

android.media包中提供的播放音频的方式,远不止这些,本文只是参考api和其他大牛的博客做一些研究和记录,android.media种还有很多只是等着我们探索……

【转】Android播放音频MediaPlayer的几种方式介绍的更多相关文章

  1. 【Android】播放音频的几种方式介绍

    接下来笔者介绍一下Android中播放音频的几种方式,android.media包下面包含了Android开发中媒体类,当然笔者不会依次去介绍,下面介绍几个音频播放中常用的类: 1.使用MediaPl ...

  2. android中跨进程通讯的4种方式

    转自:http://blog.csdn.net/lyf_007217/article/details/8542359 帖子写的很好.看来一遍,试了一遍,感觉太有意义.必须转过来! android中跨进 ...

  3. Android异步更新UI的四种方式

    Android异步更新UI的四种方式 2015-09-06 09:23 segmentfault 字号:T | T 大家都知道由于性能要求,android要求只能在UI线程中更新UI,要想在其他线程中 ...

  4. android中解析文件的三种方式

    android中解析文件的三种方式     好久没有动手写点东西了,最近在研究android的相关技术,现在就android中解析文件的三种方式做以下总结.其主要有:SAX(Simple API fo ...

  5. android点击事件的四种方式

    android点击事件的四种方式 第一种方式:创建内部类实现点击事件 代码如下: package com.example.dail; import android.text.TextUtils; im ...

  6. android 定位的几种方式介绍

    [地理位置] android 定位的几种方式介绍 开发中对于地图及地理位置的定位是我们经常要用地,地图功能的使用使得我们应用功能更加完善,下面 www.androidkaifa.com 总结了一下网络 ...

  7. Spark部署三种方式介绍:YARN模式、Standalone模式、HA模式

    参考自:Spark部署三种方式介绍:YARN模式.Standalone模式.HA模式http://www.aboutyun.com/forum.php?mod=viewthread&tid=7 ...

  8. Mysql查看版本号的五种方式介绍

    Mysql查看版本号的五种方式介绍 作者: 字体:[增加 减小] 类型:转载 时间:2013-05-03   一.使用命令行模式进入mysql会看到最开始的提示符;二.命令行中使用status可以看到 ...

  9. MySQL查看版本号的五种方式介绍1111111

    MySQL查看版本号的五种方式介绍 1 命令行模式登录MySQL [root@localhost ~]# mysql -uroot -p Enter password: Welcome to the ...

随机推荐

  1. JPG、PNG、GIF、SVG 等格式图片区别

    1.图片 2. 前言 首先,我们要清楚的是,图片从类型上分,可以分为 位图 和 矢量图. 位图:位图又叫点阵图或像素图,计算机屏幕上的图是由屏幕上的发光点(即像素)构成的,每个点用二进制数据来描述其颜 ...

  2. insert

    (1)INSERT INTO SELECT语句 语句形式为: Insert into Table2(field1,field2,,field3,...) select key1,key2,,key3, ...

  3. Linux指令 压缩与解压

    打包: 格式:tar -cvf  压缩后的名称.tar   压缩的文件1 压缩的文件2 ```压缩的文件n(压缩多个文件为一份时各个文件以空格隔开) 例子:tar -cvf  tomcats.tar ...

  4. LeetCode编程训练 - 折半查找(Binary Search)

    Binary Search基础 应用于已排序的数据查找其中特定值,是折半查找最常的应用场景.相比线性查找(Linear Search),其时间复杂度减少到O(lgn).算法基本框架如下: //704. ...

  5. MS-UAP发布的UWP的个人隐私策略

    我们十分重视您的隐私.本隐私声明解释了我们从您那里收集的个人数据内容以及我们将如何使用这些数据. 我们不收集任何与个人信息相关的数据,只收集与本UWP运行相关的数据,如: 产品使用数据:如每个页面的使 ...

  6. 使用Nginx做图片服务器时候,配置之后图片访问一直是 404问题解决

    我的错误配置是: 服务器文件根地址: 想通过浏览器输入这个地址访问到图片: 但是会发现文件找不到会一直404,原因是根路径配置错误,来看下root路径原理: root 配置的意思是,会在root配置的 ...

  7. [Swift]LeetCode500. 键盘行 | Keyboard Row

    Given a List of words, return the words that can be typed using letters of alphabet on only one row' ...

  8. [Swift]LeetCode854. 相似度为 K 的字符串 | K-Similar Strings

    Strings A and B are K-similar (for some non-negative integer K) if we can swap the positions of two ...

  9. [Swift]LeetCode868. 二进制间距 | Binary Gap

    Given a positive integer N, find and return the longest distance between two consecutive 1's in the ...

  10. java.lang.AbstractMethodError: org.mybatis.spring.transaction.SpringManagedTransaction.getTimeout()Ljava/lang/Integer; 报错解决

    我的妈呀  真的是各种报错..... 这个问题    解决方法: https://www.cnblogs.com/beppezhang/p/6118661.html