Android声音播放与录制

AudioTrack

AudioTrack类说明:

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
/**
* The AudioTrack class manages and plays a single audio resource for Java applications.
* It allows streaming of PCM audio buffers to the audio sink for playback. This is
* achieved by "pushing" the data to the AudioTrack object using one of the
* {@link #write(byte[], int, int)}, {@link #write(short[], int, int)},
* and {@link #write(float[], int, int, int)} methods.
*
* <p>An AudioTrack instance can operate under two modes: static or streaming.<br>
* In Streaming mode, the application writes a continuous stream of data to the AudioTrack, using
* one of the {@code write()} methods. These are blocking and return when the data has been
* transferred from the Java layer to the native layer and queued for playback. The streaming
* mode is most useful when playing blocks of audio data that for instance are:
*
* <ul>
* <li>too big to fit in memory because of the duration of the sound to play,</li>
* <li>too big to fit in memory because of the characteristics of the audio data
* (high sampling rate, bits per sample ...)</li>
* <li>received or generated while previously queued audio is playing.</li>
* </ul>
*
* The static mode should be chosen when dealing with short sounds that fit in memory and
* that need to be played with the smallest latency possible. The static mode will
* therefore be preferred for UI and game sounds that are played often, and with the
* smallest overhead possible.
*
* <p>Upon creation, an AudioTrack object initializes its associated audio buffer.
* The size of this buffer, specified during the construction, determines how long an AudioTrack
* can play before running out of data.<br>
* For an AudioTrack using the static mode, this size is the maximum size of the sound that can
* be played from it.<br>
* For the streaming mode, data will be written to the audio sink in chunks of
* sizes less than or equal to the total buffer size.
*
* AudioTrack is not final and thus permits subclasses, but such use is not recommended.
*/
public class AudioTrack extends PlayerBase
implements AudioRouting{

}

构造方法说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//根据采样率,采样精度,单双声道来得到frame的大小。
int bufsize = AudioTrack.getMinBufferSize(8000,//每秒8K个点  AudioFormat.CHANNEL_CONFIGURATION_STEREO,//双声道
AudioFormat.ENCODING_PCM_16BIT);//一个采样点16比特-2个字节
//注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。//创建AudioTrack
AudioTrack trackplayer = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,  AudioFormat.CHANNEL_CONFIGURATION_ STEREO,
  AudioFormat.ENCODING_PCM_16BIT,
  bufsize,
AudioTrack.MODE_STREAM);//
trackplayer.play() ;//开始

trackplayer.write(bytes_pkg, 0, bytes_pkg.length) ;//往track中写数据

….

trackplayer.stop();//停止播放

trackplayer.release();//释放底层资源。

参数说明

1. AudioTrack.MODE_STREAM的意思:

AudioTrack中有MODE_STATIC和MODE_STREAM两种分类。

  • STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。这种方式的坏处就是总是在JAVA层和Native层交互,效率损失较大。

  • 而STATIC的意思是一开始创建的时候,就把音频数据放到一个固定的buffer,然后直接传给audiotrack,后续就不用一次次得write了。AudioTrack会自己播放这个buffer中的数据。 这种方法对于铃声等内存占用较小,延时要求较高的声音来说很适用。

2. StreamType

这个在构造AudioTrack的第一个参数中使用。这个参数和Android中的AudioManager有关系,涉及到手机上的音频管理策略。 Android将系统的声音分为以下几类常见的(定义在AudioManager):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/** The audio stream for phone calls */
public static final int STREAM_VOICE_CALL = AudioSystem.STREAM_VOICE_CALL;
/** The audio stream for system sounds */
public static final int STREAM_SYSTEM = AudioSystem.STREAM_SYSTEM;
/** The audio stream for the phone ring */
public static final int STREAM_RING = AudioSystem.STREAM_RING;
/** The audio stream for music playback */
public static final int STREAM_MUSIC = AudioSystem.STREAM_MUSIC;
/** The audio stream for alarms */
public static final int STREAM_ALARM = AudioSystem.STREAM_ALARM;
/** The audio stream for notifications */
public static final int STREAM_NOTIFICATION = AudioSystem.STREAM_NOTIFICATION;
/** @hide The audio stream for phone calls when connected to bluetooth */
public static final int STREAM_BLUETOOTH_SCO = AudioSystem.STREAM_BLUETOOTH_SCO;
/** @hide The audio stream for enforced system sounds in certain countries (e.g camera in Japan) */
public static final int STREAM_SYSTEM_ENFORCED = AudioSystem.STREAM_SYSTEM_ENFORCED;
/** The audio stream for DTMF Tones */
public static final int STREAM_DTMF = AudioSystem.STREAM_DTMF;
/** @hide The audio stream for text to speech (TTS) */
public static final int STREAM_TTS = AudioSystem.STREAM_TTS;

常用说明:

  • STREAM_ALARM:警告声
  • STREAM_MUSCI:音乐声,例如music等
  • STREAM_RING:铃声
  • STREAM_SYSTEM:系统声音
  • STREAM_VOCIE_CALL:电话声音

为什么要分这么多呢?以前在台式机上开发的时候很少知道有这么多的声音类型,不过仔细思考下,发现这样做是有道理的。例如你在听music的时候接到电话,这个时候music播放肯定会停止,此时你只能听到电话,如果你调节音量的话,这个调节肯定只对电话起作用。当电话打完了,再回到music,你肯定不用再调节音量了。

其实系统将这几种声音的数据分开管理,所以,这个参数对AudioTrack来说,它的含义就是告诉系统,我现在想使用的是哪种类型的声音,这样系统就可以对应管理他们了。

AudioRecord

AudioRecord说明

The AudioRecord class manages the audio resources for Java applications to record audio from the audio input hardware of the platform. This is achieved by “pulling” (reading) the data from the AudioRecord object. The application is responsible for polling the AudioRecord object in time using one of the following three methods: read(byte[], int, int), read(short[], int, int) or read(ByteBuffer, int). The choice of which method to use will be based on the audio data storage format that is the most convenient for the user of AudioRecord.

Upon creation, an AudioRecord object initializes its associated audio buffer that it will fill with the new audio data. The size of this buffer, specified during the construction, determines how long an AudioRecord can record before “over-running” data that has not been read yet. Data should be read from the audio hardware in chunks of sizes inferior to the total recording buffer size.

audiosource类型

定义在MediaRecorder中

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
/** Default audio source **/
public static final int DEFAULT = 0;

/** Microphone audio source */
public static final int MIC = 1;

/** Voice call uplink (Tx) audio source */
public static final int VOICE_UPLINK = 2;

/** Voice call downlink (Rx) audio source */
public static final int VOICE_DOWNLINK = 3;

/** Voice call uplink + downlink audio source */
public static final int VOICE_CALL = 4;

/** Microphone audio source with same orientation as camera if available, the main
* device microphone otherwise */
public static final int CAMCORDER = 5;

/** Microphone audio source tuned for voice recognition if available, behaves like
* {@link #DEFAULT} otherwise. */
public static final int VOICE_RECOGNITION = 6;

/** Microphone audio source tuned for voice communications such as VoIP. It
* will for instance take advantage of echo cancellation or automatic gain control
* if available. It otherwise behaves like {@link #DEFAULT} if no voice processing
* is applied.
*/
public static final int VOICE_COMMUNICATION = 7;

AudioManager

获取系统音量

代码

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
//初始化AudioManager:
AudioManager mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

//通话音量
int max = mAudioManager.getStreamMaxVolume( AudioManager.STREAM_VOICE_CALL );
int current = mAudioManager.getStreamVolume( AudioManager.STREAM_VOICE_CALL );
Log.d(“VIOCE_CALL”, “max : ” + max + ” current : ” + current);

//系统音量
max = mAudioManager.getStreamMaxVolume( AudioManager.STREAM_SYSTEM );
current = mAudioManager.getStreamVolume( AudioManager.STREAM_SYSTEM );
Log.d(“SYSTEM”, “max : ” + max + ” current : ” + current);

//铃声音量
max = mAudioManager.getStreamMaxVolume( AudioManager.STREAM_RING );
current = mAudioManager.getStreamVolume( AudioManager.STREAM_RING );
Log.d(“RING”, “max : ” + max + ” current : ” + current);

//音乐音量

max = mAudioManager.getStreamMaxVolume( AudioManager.STREAM_MUSIC );
current = mAudioManager.getStreamVolume( AudioManager.STREAM_MUSIC );
Log.d(“MUSIC”, “max : ” + max + ” current : ” + current);

//提示声音音量

max = mAudioManager.getStreamMaxVolume( AudioManager.STREAM_ALARM );
current = mAudioManager.getStreamVolume( AudioManager.STREAM_ALARM );
Log.d(“ALARM”, “max : ” + max + ” current : ” + current);

ps:   游戏过程中只允许调整多媒体音量,而不允许调整通话音量。

1
setVolumeControlStream(AudioManager.STREAM_MUSIC);

控制音量

AudioManager提供了设置音量的方法:

1
public void setStreamVolume(intstreamType,intindex,intflags)

其中streamType有内置的常量,去文档里面就可以看到。 使用示例:

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
//音量控制,初始化定义    
AudioManager mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//最大音量
int maxVolume = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
//当前音量
int currentVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);

//直接控制音量
if(isSilent){
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0);
}else{
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, tempVolume, 0); //tempVolume:音量绝对值
}

//以一步步长控制音量的增减,并弹出系统默认音量控制条:
//降低音量,调出系统音量控制
if(flag == 0){
mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_LOWER,
AudioManager.FX_FOCUS_NAVIGATION_UP);
}
//增加音量,调出系统音量控制
else if(flag == 1){
mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,AudioManager.ADJUST_RAISE,
AudioManager.FX_FOCUS_NAVIGATION_UP);
}

监听按键手动控制音量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
AudioManager audio = (AudioManager) getSystemService(Service.AUDIO_SERVICE);

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
audio.adjustStreamVolume(
AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_RAISE,
AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_SHOW_UI);
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
audio.adjustStreamVolume(
AudioManager.STREAM_MUSIC,
AudioManager.ADJUST_LOWER,
AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_SHOW_UI);
return true;
default:
break;
}
return super.onKeyDown(keyCode, event);

插入耳机状态仍使用扬声器外放音乐

插入耳机的时候也可以选择使用扬声器播放音乐,来电铃声就是这么用的。但是只能用MediaPlayer,播放音频文件。 使用AudioTrack.write播放是行不通的(有待验证)。按理说AudioRecord、AudioTrack类相对于MediaRecorder mediaPlayer来说,更加接近底层,应该也行得通的。

插入耳机,选择外放的代码如下(兼容性验证):

1
2
3
4
5
6
7
8
9
AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);  
audioManager.setMicrophoneMute(false);
audioManager.setSpeakerphoneOn(true);//使用扬声器外放,即使已经插入耳机
setVolumeControlStream(AudioManager.STREAM_MUSIC);//控制声音的大小
audioManager.setMode(AudioManager.STREAM_MUSIC);

//播放一段声音,查看效果
MediaPlayer playerSound = MediaPlayer.create(this, Uri.parse("file:///system/media/audio/ui/camera_click.ogg"));
playerSound.start();

使用STREAM_VOCIE_CALL播放声音与耳机冲突

使用STREAM_VOCIE_CALL播放声音在某些手机,比如魅蓝等上面会导致声音仍外放,耳机没声音现象. WebRtc使用STREAM_VOCIE_CALL播放声音,导致某些手机声音低,没法使用音量键调节,插入耳机声音仍外放等问题.

坚持原创技术分享,您的支持将鼓励我继续创作!