转自:http://vaero.blog.51cto.com/4350852/834880

Android Audio简述
简单点MediaPlayer,复杂点……不会啊T^T,怎么办!

一、MediaPlayer

在package android.media包内,MediaPlayer的API说明得很详细了^^。摘张图过来:

另外,在其Valid and invalid states一节中列了张表格,详细的描述了MediaPlayer各方法在各状态下是否有效。(就不摘过来了^^)

二、音乐播放器
MediaPlayer控制音乐文件播放的简易实现,就是控制上图的流程^^。

1)播放控制接口
定义了播放器基本模式、状态及控制操作方法。


    public interface IPlayer {              // 播放模式         enum PlayMode {             ONCE, CYCLE         }              // 播放状态         enum Status {             PLAYING, STOPPED, PAUSING         };              // 获得当前播放模式         PlayMode getMode();              // 设置当前播放模式         void setMode(PlayMode mode);              // 获得当前播放状态         Status getStatus();              // 指向上一首音乐         File prev();              // 指向下一首音乐         File next();              // 播放当前音乐         boolean play();              // 暂停当前音乐         boolean pause();              // 恢复播放音乐         boolean resume();              // 停止当前音乐         boolean stop();              // 释放资源         void release();          } 



2)文件列表控件
自定义的ListView控件,主要实现功能如下:
1. 异步搜索/mnt/sdcard/music/目录下所有mp3文件
2. 以自定义适配器方式,使得选中的Item保持高亮背景色

3)文件列表播放器
用MediaPlayer实现了IPlayer播放控制接口。


   public class FileListViewPlayer implements IPlayer,             AdapterView.OnItemClickListener {              private MediaPlayer mMediaPlayer; // MediaPlayer对象         private FileListView mFileListView; // FileListView组件         private ArrayList<File> musicFileList; // 音乐文件列表         private int index = 0; // 当前索引         private PlayMode mPlayMode = PlayMode.CYCLE; // 播放模式         private Status mStatus = Status.STOPPED; // 播放状态              private OnMusicClickListener listener; // 音乐文件点击监听              // 音乐点击事件监听接口         public interface OnMusicClickListener {             // 返回true及时播放,false则不及时播放             boolean onMusicClick(File musicFile);         }              public FileListViewPlayer(FileListView fileListView) {             this.mFileListView = fileListView;             fileListView.setOnItemClickListener(this); // 设置Item点击时间监听             mMediaPlayer = new MediaPlayer(); // 创建MediaPlayer对象         }              // 是否有音乐         private boolean hasMusic() {             musicFileList = mFileListView.getMusicFileList();             return (null != musicFileList && musicFileList.size() >= 1);         }              // 选中某项&设置索引         private void setSelection(int position) {             mFileListView.setSelectItem(position); // 选中position             index = position; // 指向position         }              @Override         public PlayMode getMode() {             return mPlayMode;         }              @Override         public void setMode(PlayMode mode) {             this.mPlayMode = mode;         }              @Override         public Status getStatus() {             return mStatus;         }              @Override         public File prev() {             if (hasMusic()) {                 int location = index - 1 >= 0 ? index - 1                         : musicFileList.size() - 1;                 setSelection(location);                 return musicFileList.get(location);             }             return null;         }              @Override         public File next() {             if (hasMusic()) {                 int location = index + 1 < musicFileList.size() ? index + 1 : 0;                 setSelection(location);                 return musicFileList.get(location);             }             return null;         }              @Override         public boolean play() {             if (mStatus != Status.STOPPED || !hasMusic()) {                 return false;             }             try {                 mMediaPlayer.reset();                 mMediaPlayer.setDataSource(musicFileList.get(index).toString());                 mMediaPlayer.prepare();                 mMediaPlayer.start();                 // 如果是顺序循环播放                 if (mPlayMode == PlayMode.CYCLE) {                     mMediaPlayer                             .setOnCompletionListener(new OnCompletionListener() {                                 @Override                                 public void onCompletion(MediaPlayer mp) {                                     stop(); // 停止                                     next(); // 下首                                     play(); // 播放                                 }                             });                 }                 mStatus = Status.PLAYING;                 return true;             } catch (Exception e) {                 e.printStackTrace();             }             return false;         }              @Override         public boolean pause() {             if (mStatus != Status.PLAYING) {                 return false;             }             mMediaPlayer.pause();             mStatus = Status.PAUSING;             return true;         }              @Override         public boolean resume() {             if (mStatus != Status.PAUSING) {                 return false;             }             mMediaPlayer.start();             mStatus = Status.PLAYING;             return true;         }              @Override         public boolean stop() {             if (mStatus != Status.STOPPED) {                 mMediaPlayer.stop();                 mStatus = Status.STOPPED;                 return true;             }             return false;         }              @Override         public void release() {             stop();             mMediaPlayer.release();             mMediaPlayer = null;         }              @Override         public void onItemClick(AdapterView<?> parent, View view, int position,                 long id) {             setSelection(position);             if (null != listener && hasMusic()) {                 if (listener.onMusicClick(musicFileList.get(position))) {                     stop(); // 停止                     play(); // 播放                 }             }         }              public void setOnMusicClickListener(OnMusicClickListener listener) {             this.listener = listener;         }          } 



4)播放器活动
界面上各点击事件与播放控制接口结合。

5)其他设置(样例中未写==)
5.1)播放进度
使用MediaPlayer提供了如下3个方法即可:

1. getDuration():获得总持续时间(毫秒)
{Idle, Initialized, Error}状态时,该方法无效。

2. getCurrentPosition():获得当前播放位置(毫秒)
{Error}状态时,该方法无效。

3. seekTo(int msec):跳至指定的时间位置(毫秒)

{Idle, Initialized, Stopped, Error}状态时,该方法无效。

5.2)音量控制

使用AudioManager的setStreamVolume(int streamType, int index, int flags)方法。第一个参数设置为AudioManager.STREAM_MUSIC,即为音乐音量。

三、其他音频类

一样是在package android.media包内,还有好几个以Player后缀结束的类呢T^T。没用过,也不多作介绍了。
这节呢,主要是想介绍下SoundPool,以及它与MediaPlayer的利弊及使用场合。

1)利弊及场合
MediaPlayer资源占用多、延迟长、不支持多个音频同时播放。在快速连续播放音效时,尤其会感受到。而SoundPool则不同,占用少、延迟短、支持多音频播放。因为其限制最大只能申请1M的内存,也就意味着是用于播放音频片断的。
总结就是,MediaPlayer播放长音乐,SoundPool播放短音效==。

2)SoundPool注意点
1. 音效文件不易过大(限制1M内存)
如果音效文件过大而没有载入完成,调用play()可能会产生严重的后果。当然可以用SoundPool.OnLoadCompleteListener来判断是否载入完成。
2. pause&stop方法建议不轻易使用
有时会使你程序莫名终止。也有反映不会立即终止,而是等缓冲区播放完,会多一秒。
3. 音频格式建议使用OGG格式。
说是WAV在音效播放间隔较短的情况下会出现异常关闭的情况。另外说是,目前只对16位的WAV支持较好。(目前不知道指什么时候==)

参考自网络,未亲自证实,尽量避免就行了^^。

3)SoundPool的使用
SoundPool基本的播放控制方法。方法的详细说明都在注释了^^。


  public class SoundPoolActivity extends Activity implements             SoundPool.OnLoadCompleteListener {              private static final int SOUND_BASE = 0;         private static final int SOUND_THUNDER = SOUND_BASE + 1;         private static final int SOUND_NIGHTINGALE = SOUND_BASE + 2;              private SoundPool mSoundPool; // SoundPool对象         private HashMap<Integer, Integer> soundPoolMap;              @Override         protected void onCreate(Bundle savedInstanceState) {             super.onCreate(savedInstanceState);             setContentView(R.layout.audio_pool);             initSounds(); // 初始化SoundPool         }              // 初始化SoundPool         private void initSounds() {             /*              * SoundPool(int maxStreams, int streamType, int srcQuality)              *               * maxStreams:同时播放的流的最大数量              * streamType:流的类型(AudioManager类中描述的)。例如:游戏应用一般使用STREAM_MUSIC              * srcQuality:采样率转化质量。当前无效果,使用0作为默认值              */             mSoundPool = new SoundPool(4, AudioManager.STREAM_MUSIC, 100);             /*              * Android 2.2(API 8及以上)才有这接口==              */             mSoundPool.setOnLoadCompleteListener(this);             soundPoolMap = new HashMap<Integer, Integer>();             /*              * load()有四种方法,如下:              *               * 1)int load(Context context, int resId, int priority)              *     从APK资源载入(一般在res/raw目录下)              * 2)int load(FileDescriptor fd, long offset, long length, int priority)              *     从FileDescriptor对象载入              * 3)int load(AssetFileDescriptor afd, int priority)              *     从Asset对象载入              * 4)int load(String path, int priority)              *     从完整文件路径名载入              *               * 最后priority参数为优先级,播放多文件时处理用。              */             soundPoolMap                     .put(SOUND_THUNDER, mSoundPool.load(this, R.raw.thunder, 1));             soundPoolMap.put(SOUND_NIGHTINGALE,                     mSoundPool.load(this, R.raw.nightingale, 2));         }              // 雷声         public void bird(View v) {             /*              * play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)              *               * soundID:load()方法返回的int值              * leftVolume:左音量。范围=[0.0,1.0]              * rightVolume:右音量。范围=[0.0,1.0]              * priority:优先级。最低=0              * loop:循环次数。不循环=0;永远循环=-1              * rate:速率。正常=1;范围=[0.5,2.0]              */             mSoundPool.play(soundPoolMap.get(SOUND_THUNDER), 1, 1, 0, 0, 1);         }              // 雷声+夜莺         public void mix(View v) {             mSoundPool.play(soundPoolMap.get(SOUND_THUNDER), 1, 1, 0, 0, 1);             mSoundPool.play(soundPoolMap.get(SOUND_NIGHTINGALE), 1, 1, 0, 0, 1);         }              @Override         public void onBackPressed() {             super.onBackPressed();             mSoundPool.release();         }              @Override         public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {             // Log.d("TAG", "==" + sampleId + "==");         }          } 



四、MediaRecorder
在Camera摄像里,MediaRecorder用来录制视频了。这里是介绍录制音频了^^。好吧,再摘张图过来(真心觉着看看API就好了,可以把我忽视==):


五、简易录音机
简易的录音机,就是能录音和回放一下的那种==。

1)录音机对象
录音机的简单实现。MediaRecorder控制录音、MediaPlayer用于播放,设置了其各个阶段状态。


  public class Recorder implements OnCompletionListener, OnErrorListener {              // 录音机状态         public enum Status {             IDLE, RECORDING, PLAYING         }              // 当前状态         private Status mStatus = Status.IDLE;              // 媒体录制对象         private MediaRecorder mRecorder = null;         // 媒体播放对象         private MediaPlayer mPlayer = null;         // 录制的文件         File mSampleFile = null;         // 录音或播放开始时间         long mSampleStart = 0;              private OnRecorderListener listener; // 播放器接口              public interface OnRecorderListener {             void error();                  void playOver();         }              // 返回录音机状态         public Status getStatus() {             return mStatus;         }              // 是否录过音         public boolean isRecorded() {             return mSampleFile != null;         }              // 返回时间进度(秒)         public int progress() {             if (mStatus == Status.RECORDING || mStatus == Status.PLAYING)                 return (int) ((System.currentTimeMillis() - mSampleStart) / 1000);             return 0;         }              // 开始录音         public void startRecording() {             stop(); // 停止             if (mSampleFile == null) {                 // 创建文件,以createTempFile方式避免覆盖                 try {                     File dir = new File(Environment.getExternalStorageDirectory()                             + "/AndroidMedia/");                     if (!dir.exists()) {                         dir.mkdirs();                     }                     mSampleFile = File.createTempFile("join_", ".3gpp", dir);                 } catch (IOException e) {                     e.printStackTrace();                     return;                 }             }             mRecorder = new MediaRecorder(); // 创建MediaRecorder对象             mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频信号源:麦克风             mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); // 设置输出格式             mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设置音频编码器             mRecorder.setOutputFile(mSampleFile.getAbsolutePath()); // 设置输出文件             try {                 mRecorder.prepare(); // 准备录音             } catch (Exception e) {                 mRecorder.reset(); // 重置空闲                 mRecorder.release(); // 释放资源                 mRecorder = null; // 重置为null                 return; // 返回             }             try {                 mRecorder.start(); // 开启录音             } catch (RuntimeException e) {                 // 可能开启不了,如果是来电的话,可以如下判断:                 // AudioManager.getMode() == MODE_IN_CALL || MODE_IN_COMMUNICATION                 mRecorder.reset(); // 重置空闲                 mRecorder.release(); // 释放资源                 mRecorder = null; // 重置为null                 return; // 返回             }             mSampleStart = System.currentTimeMillis(); // 初始化开始时间             mStatus = Status.RECORDING; // 设置录音状态         }              // 停止录音         public void stopRecording() {             if (mRecorder == null)                 return;             mRecorder.stop(); // 停止录音             mRecorder.release(); // 释放资源             mRecorder = null; // 重置为null             mStatus = Status.IDLE; // 设为空闲状态         }              // 开始录音         public void startPlayback() {             stop(); // 停止             mPlayer = new MediaPlayer(); // 创建MediaPlayer对象             try {                 mPlayer.setDataSource(mSampleFile.getAbsolutePath()); // 设置数据资源路径                 mPlayer.setOnCompletionListener(this); // 设置完成监听接口                 mPlayer.setOnErrorListener(this); // 设置错误监听接口                 mPlayer.prepare(); // 准备                 mPlayer.start(); // 播放             } catch (Exception e) {                 mPlayer = null; // 重置为null                 return;             }             mSampleStart = System.currentTimeMillis(); // 初始化开始时间             mStatus = Status.PLAYING; // 设置录音状态         }              // 停止录音         public void stopPlayback() {             if (mPlayer == null)                 return;             mPlayer.stop(); // 停止播放             mPlayer.release(); // 释放资源             mPlayer = null; // 重置为null             mStatus = Status.IDLE; // 设为空闲状态         }              // 停止操作          public void stop() {             stopRecording();             stopPlayback();         }              @Override         public boolean onError(MediaPlayer mp, int what, int extra) {             stop(); // 停止             if (null != listener) {                 listener.error();             }             return true;         }              @Override         public void onCompletion(MediaPlayer mp) {             stop(); // 停止             if (null != listener) {                 listener.playOver();             }         }              // 设置播放器接口         public void setOnRecorderListener(OnRecorderListener listener) {             this.listener = listener;         }          } 



2)录音机活动
录音机控制界面。进行录音&播放控制,并显示有时间。

3)其他设置(样例中未写)
MediaRecorder的getMaxAmplitude()可以获得音频资源的最大振幅,可以用于展示下录音时的波幅线条?
增加设置文件大小限制或物体空间限制,提示剩余时间。物理空间可以用package android.os包内的StatFs类。StatFs是Unix statfs()的一个包装,用于检索文件系统空间的整体信息。在之前Camera摄像的CameraVideoActivity内稍带用了下的^^。
当然,这些功能在系统自带的录音机里都有实现了的,直接可以用==。

六、后记
1)扩展内容
1.1)android获取多媒体信息之音频文件
http://hi.baidu.com/shiwl111/blog/item/98b42ed041ea7c2d960a16d8.html

1.2)RandomMusicPlayer:Service方式如何进行音乐播放控制(官方例子)
http://developer.android.com/resources/samples/RandomMusicPlayer/index.html

1.3)Jamendo:一款开源在线音乐播放器

Google Code:http://code.google.com/p/jamendo/
源码分析系列:http://blog.csdn.net/gaomatrix/article/category/900092

2)模块概览
2.1)Audio MediaPlayer


2.2)Audio SoundPool


2.3)Audio MeidaRecorder

Audio MeidaRecorder

3)运行效果
3.1)音乐播放器

Audio MeidaRecorder

3.2)SoundPool

Audio MeidaRecorder

3.3)简单录音机

Audio MeidaRecorder



本文出自 “-_--___---_-” 博客,请务必保留此出处http://vaero.blog.51cto.com/4350852/834880

更多相关文章

  1. Android 获取网络状态及调用网络配置界面(转帖)
  2. android去除状态栏和下面视图之前的黑线
  3. 获取Android状态栏的高度
  4. android 播放assets目录内的音乐,及使用Seekbar控制播放进度
  5. ionic3中android状态栏
  6. 2017-9-16(沉浸式状态栏StatusBar)
  7. Android 状态栏透明的一些小结
  8. Android 沉浸式状态栏完美解决方案

随机推荐

  1. android Initializing a Build Environme
  2. Android(安卓)购物车的功能逻辑
  3. Android菜鸟历程7
  4. DES加密解密工具类
  5. Android(安卓)保存和回复应用状态
  6. SwipeRefreshLayout使用
  7. Android:ADT 17.0.0 发布
  8. android 获取wifi信息
  9. android上特效(源代码)
  10. 获取sd卡容量