在Android开发中有时候需要用到播放声音操作,在android API 的media包中有三种方式可供我们选择,它们分别是Ringtone,SoundPool,MediaPlayer。因为在我目前的项目中暂时用不到播放很长的音频文件,只需要播放一些简短的提示音,所以在这篇博文中我只是简单的记录下。
- 1.Ringtone.java
- 2.SoundPool.java
- 3.MediaPlayer.java
- 4.demo
- 5.bug record
注意:
关于这三个类的介绍我全部用的是android 自身的Java doc描述;
全部摘自于android-23;
因为我的项目中主要用到Ringtone,所以展示了它全部的描述;
1.Ringtone.java
关于Ringtone的介绍。
package android.media; /** * Ringtone provides a quick method for playing a ringtone, notification, or * other similar types of sounds. * <p> * For ways of retrieving {@link Ringtone} objects or to show a ringtone * picker, see {@link RingtoneManager}. * * @see RingtoneManager */ public class Ringtone {}
2.SoundPool.java
关于SoundPool的介绍。
/** * The SoundPool class manages and plays audio resources for applications. * * <p>A SoundPool is a collection of samples that can be loaded into memory * from a resource inside the APK or from a file in the file system. The * SoundPool library uses the MediaPlayer service to decode the audio * into a raw 16-bit PCM mono or stereo stream. This allows applications * to ship with compressed streams without having to suffer the CPU load * and latency of decompressing during playback.</p> * * <p>In addition to low-latency playback, SoundPool can also manage the number * of audio streams being rendered at once. When the SoundPool object is * constructed, the maxStreams parameter sets the maximum number of streams * that can be played at a time from this single SoundPool. SoundPool tracks * the number of active streams. If the maximum number of streams is exceeded, * SoundPool will automatically stop a previously playing stream based first * on priority and then by age within that priority. Limiting the maximum * number of streams helps to cap CPU loading and reducing the likelihood that * audio mixing will impact visuals or UI performance.</p> * * <p>Sounds can be looped by setting a non-zero loop value. A value of -1 * causes the sound to loop forever. In this case, the application must * explicitly call the stop() function to stop the sound. Any other non-zero * value will cause the sound to repeat the specified number of times, e.g. * a value of 3 causes the sound to play a total of 4 times.</p> * * <p>The playback rate can also be changed. A playback rate of 1.0 causes * the sound to play at its original frequency (resampled, if necessary, * to the hardware output frequency). A playback rate of 2.0 causes the * sound to play at twice its original frequency, and a playback rate of * 0.5 causes it to play at half its original frequency. The playback * rate range is 0.5 to 2.0.</p> * * <p>Priority runs low to high, i.e. higher numbers are higher priority. * Priority is used when a call to play() would cause the number of active * streams to exceed the value established by the maxStreams parameter when * the SoundPool was created. In this case, the stream allocator will stop * the lowest priority stream. If there are multiple streams with the same * low priority, it will choose the oldest stream to stop. In the case * where the priority of the new stream is lower than all the active * streams, the new sound will not play and the play() function will return * a streamID of zero.</p> * * <p>Let's examine a typical use case: A game consists of several levels of * play. For each level, there is a set of unique sounds that are used only * by that level. In this case, the game logic should create a new SoundPool * object when the first level is loaded. The level data itself might contain * the list of sounds to be used by this level. The loading logic iterates * through the list of sounds calling the appropriate SoundPool.load() * function. This should typically be done early in the process to allow time * for decompressing the audio to raw PCM format before they are needed for * playback.</p> * * <p>Once the sounds are loaded and play has started, the application can * trigger sounds by calling SoundPool.play(). Playing streams can be * paused or resumed, and the application can also alter the pitch by * adjusting the playback rate in real-time for doppler or synthesis * effects.</p> * * <p>Note that since streams can be stopped due to resource constraints, the * streamID is a reference to a particular instance of a stream. If the stream * is stopped to allow a higher priority stream to play, the stream is no * longer be valid. However, the application is allowed to call methods on * the streamID without error. This may help simplify program logic since * the application need not concern itself with the stream lifecycle.</p> * * <p>In our example, when the player has completed the level, the game * logic should call SoundPool.release() to release all the native resources * in use and then set the SoundPool reference to null. If the player starts * another level, a new SoundPool is created, sounds are loaded, and play * resumes.</p> */ public class SoundPool {}
3.MediaPlayer.java
package android.media; /** * MediaPlayer class can be used to control playback * of audio/video files and streams. An example on how to use the methods in * this class can be found in {@link android.widget.VideoView}. * For ways of retrieving {@link Ringtone} objects or to show a ringtone * picker, see {@link RingtoneManager}. * * @see RingtoneManager */ public class MediaPlayer implements SubtitleController.Listener{}
4.demo
因为这个demo很简单,就一个Activity和xml,所以我就不提供源码下载了。
请直接看码:
package android.media; package org.tuzhao.demo.activity; import android.app.Activity; import android.content.res.AssetFileDescriptor; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.Ringtone; import android.media.RingtoneManager; import android.media.SoundPool; import android.net.Uri; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import org.tuzhao.sqlite.R; import java.io.IOException; /** * three types to play sound * @author tuzhao */ public class SoundActivity extends AppCompatActivity implements View.OnClickListener { private Ringtone ringtone; private SoundPool soundPool; private int loadId; private MediaPlayer mediaPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sound); //初始化这三个方法 ringtone = initRingtone(this); soundPool = initSoundPool(); loadId = soundPool.load(this, R.raw.beep, 1); mediaPlayer = initMediaPlayer(this, 0); findViewById(R.id.bt_ringtone).setOnClickListener(this); findViewById(R.id.bt_sound_pool).setOnClickListener(this); findViewById(R.id.bt_media).setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.bt_ringtone: ringtone.play(); break; case R.id.bt_sound_pool: soundPool.play(loadId, 1.0f, 1.0f, 0, 0, 1.0f); break; case R.id.bt_media: mediaPlayer.start(); break; } } @Override protected void onDestroy() { super.onDestroy(); //release resource of this two types. soundPool.release(); mediaPlayer.release(); } /** * init type of Ringtone * @param context Activity * @return Ringtone */ private Ringtone initRingtone(Activity context) { Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); return RingtoneManager.getRingtone(context, notification); } /** * init type of SoundPool * @return SoundPool */ @SuppressWarnings("deprecation") private SoundPool initSoundPool() { // The content of the comments need API-23 // SoundPool.Builder builder=new SoundPool.Builder(); // builder.setMaxStreams(10); // // AudioAttributes.Builder audioBuilder=new AudioAttributes.Builder(); // audioBuilder.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN); // audioBuilder.setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED); // audioBuilder.setLegacyStreamType(AudioManager.STREAM_SYSTEM); // audioBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION); // AudioAttributes attributes = audioBuilder.build(); // // builder.setAudioAttributes(attributes); // SoundPool soundPool = builder.build(); return new SoundPool(10, AudioManager.STREAM_NOTIFICATION, 5); } /** * init type of MediaPlayer * @param context Activity * @param rawId the id of raw * @return MediaPlayer */ public MediaPlayer initMediaPlayer(Activity context, int rawId) { context.setVolumeControlStream(AudioManager.STREAM_MUSIC); final MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); /** * When the beep has finished playing, rewind to queue up another one. */ mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { mediaPlayer.seekTo(0); } }); mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { mediaPlayer.release(); return true; } }); AssetFileDescriptor file; if (rawId == 0) { file = context.getResources().openRawResourceFd(R.raw.beep); } else { file = context.getResources().openRawResourceFd(rawId); } try { mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); file.close(); float BEEP_VOLUME = 1.0f; mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME); mediaPlayer.prepare(); } catch (IOException e) { e.printStackTrace(); Log.i("initSpecifiedSound", "what happened to init sound? you need to deal it ."); return null; } return mediaPlayer; } }
下面是xml布局:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="org.tuzhao.sqlite.activity.SoundActivity"> <Button android:id="@+id/bt_ringtone" android:layout_width="120dp" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" android:layout_marginTop="88dp" android:text="Ringtone" android:textAllCaps="false" /> <Button android:id="@+id/bt_sound_pool" android:layout_width="120dp" android:layout_height="wrap_content" android:layout_alignLeft="@+id/bt_ringtone" android:layout_alignStart="@+id/bt_ringtone" android:layout_below="@+id/bt_ringtone" android:layout_marginTop="15dp" android:text="SoundPool" android:textAllCaps="false"/> <Button android:id="@+id/bt_media" android:layout_width="120dp" android:layout_height="wrap_content" android:layout_alignLeft="@+id/bt_sound_pool" android:layout_alignStart="@+id/bt_sound_pool" android:layout_below="@+id/bt_sound_pool" android:layout_marginTop="15dp" android:text="MediaPlayer" android:textAllCaps="false"/> </RelativeLayout>
5.bug record
bugs:
AudioTrack: AudioFlinger could not create track, status: -12
AudioFlinger: no more track names available
AudioSink: Unable to create audio track
MediaPlayer: Should have subtitle controller already set
resolve:我出现这种情况是因为我最刚开始采用的不是上面Java文件初始化的方法,而是每播放一次声音就new 一个对象来播放声音。这样能播放,但是播放了十几次就报上面的错误。正确做法是只初始化一次。
每次调用下面这三个分别对应方法:
soundPool.play(sourceId, 1.0f, 1.0f, 0, 0, 1.0f);
ringtone.play();
mediaPlayer.start();
本人才疏学浅,出错在所难免。如文中有错误的地方望指正,谢谢!
欢迎关注我的Github账号:https://github.com/tuzhao