马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?快速注册
x
说到音频应用,首先想到的就是音乐播放器。有些播放器可以播放流媒体,有些可以播放本地音乐文件。随着Android平台的演变,需要更多高级的音频API。好在谷歌新增了这方面的API,支持低延迟的音频流媒体和录制。
' Z, q9 V% T, V
- ^- t/ V( M/ Z$ d/ G# YAndroid音频API提供了一些高级的功能,开发者可以把它们集成到自己的应用中。有了这些API,现在可以更容易地实现VoIP应用程序,构建定制的流媒体音乐客户端,实现低延迟的游戏音效。此外,还有提供文本到语音转换以及语音识别的API,用户可以直接使用音频和应用交互,而不需要使用用户界面或者触控技术。
l0 \. _ k8 E+ ` 6 g; b+ {4 r6 _( x3 V
低延迟音频# C& c* n8 i7 P% l R4 g
Android有四个用来播放音频的API(算上MIDI的话一共五个)和三个用来录音的API。接下来会简要的介绍这些API,以及一些高级用法示例。, I6 X9 p- H/ ~$ p3 |$ x& e' i: S
( H2 Z* J7 j6 q) k; ~) j$ P" ~
①音频播放API
) y$ C" U8 _/ F, G
1 |' |- H" d& h3 k7 b( n0 r音乐播放默认使用MediaPlayer。该类适合播放音乐或者视频,既能播放流式资源,还可以播放本地文件。每个MediaPlayer都有一个关联的状态机,需要在应用程序中跟踪这些状态。开发者可以使用MediaPlayer类的API在自己应用中嵌入音乐或者视频播放功能,而无需额外处理或者考虑延迟要求。
$ h7 p6 ~" x; M* K! X ' M8 a }, x2 Y
第二个选择是SoundPool类,它提供了低延迟支持,适合播放音效和其他比较短的音频,比如可以使用SoundPool播放游戏声音。但是,它不支持音频流,所以不适合那些需要实时音频流处理的应用,如VoIP。9 b& y5 t8 P, F- _
' j9 ~( }! C9 F2 z d0 u! r4 Y第三个选择是AudioTrack类,它允许把音频流缓冲到硬件中,支持低延迟播放,甚至适合流媒体场景。AudioTrack类通常提供足够低的延迟,可在VoIP或类似应用中使用。
- v; C& H' ~ N" C7 _2 Q% ? " P: k5 M2 D# v/ @
下面的代码展示了如何在VoIP应用中使用AudioTrack:
]% v1 N. p2 R: x4 y% c: u% O: t I4 i ) C* f) T5 s5 |. j. W
public class AudioTrackDemo {
( }) Y5 R" T6 ` Y& z% r0 S$ A private final int mMinBufferSize;
$ N! B2 \/ I& ?% [2 Z private final AudioTrack mAudioTrack;* W; h! d* O4 p5 {$ H& n9 p
1 |1 }; ]/ m# P- U, W5 { public AudioTrackDemo() {
4 R9 K' K N: w' l; x1 J this.mMinBufferSize = AudioTrack.getMinBufferSize(16000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
5 j+ `1 Y& [% h3 n' Z0 K, _" \/ ] this.mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 16000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, this.mMinBufferSize*2, AudioTrack.MODE_STREAM);
3 K* q8 `/ O! K) [ g3 J5 \ }
9 k8 f A8 C2 t/ f1 B! @/ @
' T: {7 _1 _/ t% |1 y: c2 Z public void playPcmPacket(byte[] pcmData) {
1 n( _6 s# m& G3 @% p) a g if (this.mAudioTrack != null && this.mAudioTrack.getState() == AudioTrack.STATE_INITIALIZED) {
@/ Z; r0 j0 Y4 C# [ if (this.mAudioTrack.getPlaybackRate() != AudioTrack.PLAYSTATE_PLAYING) {
. Z: U2 @1 s2 M5 C- r9 f this.mAudioTrack.play();
5 `' m7 |! Q, p* x: Q }6 o5 e9 r+ |5 D$ y
this.mAudioTrack.write(pcmData, 0, pcmData.length);
4 [3 p' h7 N; a9 \' |6 r }
4 e; }" [) h1 `7 m" D }% H7 W% ~/ z9 J" p
/ M0 V2 w( _* K& i5 l" a public void stopPlayback() {
" b0 w+ b( Z0 n* T if (this.mAudioTrack != null) {# F% B1 `) Y1 o" U
this.mAudioTrack.stop();/ d4 B" K# M/ T8 c' c* e
this.mAudioTrack.release();. z/ s7 ]1 P; `
}# S( ]9 G4 r, G; P N; }4 x; e
}+ Z$ G# X2 U3 f- c* c
}9 X# A* R) C1 Q/ s
! a# K* s$ {, `9 H' M" a) C/ q4 D # w7 L; _/ J1 W! }8 P
首先,确定音频流的最小缓冲区大小。要做到这一点,需要知道采样率,数据是单声道还是立体声,以及是否使用8位或者16位PCM编码。然后以采样率和采样大小作为参数调用AudioTrack.getMinBufferSize(),该方法会以字节形式返回AudioTrack实例的最小缓冲区大小。+ P9 [6 m9 g+ e, [5 V, S
5 c: O i0 d1 r接下来,根据需要使用正确参数创建AudioTrack实例。第一个参数为音频的类型,不同的应用使用不同的值。对VoIP应用来说,使用STREAM_VOICE_CALL,而对于流媒体音乐应用则使用STREAM_MUSIC。; e) N! Z9 x5 I7 H6 o* V
6 W& F! W" ?8 W5 l具体的选择有:
2 n% i- ]9 w; M" RSTREAM_ALARM:手机闹铃的声音
8 R& X {" B# y5 rSTREAM_MUSIC:手机音乐的声音4 g3 O Y, m# N8 Z5 M+ b( ~
STREAM_DTMF:DTMF音调的声音
/ d! I5 D. L0 K d0 FSTREAM_RING:电话铃声的声音 [) |6 Y% \* Z$ d/ c9 u5 _
STREAM_NOTFICATION:系统提示的声音
1 r2 x x/ \" Z; wSTREAM_SYSTEM:系统的声音, O) ?: J+ ~0 N* u. |: t% L0 d
STREAM_VOICE_CALL:语音电话声音8 e5 n. E& H5 y
/ \7 Q1 }% I4 g2 O* _* u
第二,第三,第四个参数根据使用场景会有所不同。这些参数分别表示采样率,立体声或者单声道,以及采样大小。一般而言,一个VoIP应用会使用16KHZ的16位单声道,而常规的音乐CD可能采用44.1KHZ的16位立体声。16位立体声高采样率需要更大的缓冲区以及更多的数据传输,但是音质会更好。所有的Android设备都支持PCM以8KHZ,16KHZ,44.1KHZ的采样率播放8或者16位立体声。
& N( u$ v, f! x/ d5 G 7 W6 c5 S( q' k7 p2 l
缓冲区大小参数应该是最小缓冲区的倍数,实际取决于具体的需求,有时网络延迟等因素也会影响缓冲区大小。
. E3 l# [0 o+ b5 C. {$ _- P任何时候都应该避免使用空的缓冲区,因为可能导致播放出现故障。 E$ x+ v) R$ v# u
* r1 Q5 b. B; a- F- z# [& ?
最后一个参数决定只发送一次音频数据(MODE_STATIC)还是连续发送数据流(MODE_STREAM)。第一种情况需要一次发送整个音频剪辑。对于持续发送音频流的情况,可以发送任大小块的PCM数据,处理流媒体音乐或者VoIP通话可以会使用这种方式。
$ ]2 L5 J# [/ v, t, d6 M- h
' n7 J5 A6 {' S3 k1 Q+ X( Z; p3 w+ b②录制API
7 @) u9 T; V- B) r X& W
. E8 P4 _- J' D6 F9 O谈到录制音频,首先要考虑的API是MediaRecorder。和MediaPlayer类似,需要在应用代码中跟踪MediaRecorder类的内部状态。由于MediaRecorder只能把录音保存到文件中,所以它不适合录制流媒体。
( ?: S$ Y, d0 A$ n+ a如果需要录制流媒体,可以使用AudioRecord,和刚才展示的代码非常类似。
- i0 @# k2 X. u! @; ^
) P t; v3 N; o6 q( c下面的示例显示了如何创建AudioRecord实例录制16位单声道16KHZ的音频采样:3 j& ^! E1 g) Z# Y S. y- r
, `- l4 N; I: v$ kpublic class AudioRecordDemo {. D V. w7 u4 f( _
private final AudioRecord mAudioRecord;7 w* L' T1 g# S: m; H E5 j
private final int mMinBufferSize;
. o- z% @ B, i" G5 C3 X private boolean mDoRecord = false;
- ?4 j J; h6 d
$ y( `0 f6 P0 U8 l+ |& M% C% J8 X public AudioRecordDemo() {2 Y/ i- n8 A. Q j, I9 D+ q
this.mMinBufferSize = AudioTrack.getMinBufferSize(16000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
! K$ ^5 F# a/ h. r2 a+ o# Q I3 Y this.mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, this.mMinBufferSize * 2);/ [) m1 {# {4 i" N/ Z5 `
}
" [7 @, U6 U4 E E {% o4 h( Q% A3 z% t z" k8 u
public void writeAudioToStream(OutputStream stream){7 A4 {% }6 U: l) r' q. x1 t
this.mDoRecord=true;6 W0 j$ S' P7 Z9 U1 A: X) B" _! s
this.mAudioRecord.startRecording();. I% }; n3 }; c! u6 W
byte[] buffer=new byte[this.mMinBufferSize*2];
! Z5 o1 Z& r; v# d# i while(this.mDoRecord){
9 @) v* r. K) b! F) @ int byteWritten=this.mAudioRecord.read(buffer,0,buffer.length); v, s y4 Z! q% G3 v" B, A
try{
/ U' I0 r9 V& w; V6 u stream.write(buffer,0,byteWritten);1 H! A9 P O+ _3 u. K; v5 d
}catch(IOException e){
2 ~2 U' ?: U8 i& L# y this.mDoRecord=false;7 P5 J2 v' ~$ m8 Z$ h# i
}
0 y: ^( d6 X* l0 S, L4 }4 g }
$ c8 V8 D1 g0 l7 V/ R. M( [4 H this.mAudioRecord.stop();7 |/ i: |6 E; G, E1 E4 C; T
this.mAudioRecord.release(); p3 H2 d% g$ c, r* x: Y* t8 _
}7 k, `* s) U6 q) A& S
6 M2 D. s) G, I; w' P
public void stopRecording(){
, i4 @* {! Y& B- g this.mDoRecord=false;5 F# C( q5 @, p/ L
}
' N: |% W0 L1 n4 R j- s/ G" H, Q% |( X9 P0 @
* ~( X/ T1 u+ X! j
}. A! p* V2 @3 n, U2 D
/ B7 S% l. c+ ?+ Q
3 t/ v6 ? _( R5 \
因为和AudioTrack的创建过程非常类似,在使用VoIP或者类似应用时可以很方便地把它们结合起来。# G5 o$ q. {" ?6 G
1 m9 i6 c" v: e% ]; H1 d相信学过多媒体的人对采样率等这些东西并不陌生,如果缺乏这方面的知识,可以适当的补充后在来看这段代码。
0 A. I: W2 {6 c- F& y延伸阅读:
. Q. `- _' l# F: TAndroidAnnnotations注入框架使用之最佳实践之Adapters和lists(! R( Y* {/ T! z( `. b
AndroidAnnnotations注入框架使用之第三方框架集成RoboGuice(十" U! \; v. w0 B X$ N' A, s' z- l
Android MVP开发模式详解(十九)/ ~9 g9 p) A! i0 P
谷歌发布会:Nexus 4\10 Android4.2正式发布
: ?0 M$ E; v' ]( O/ p9 t0 B& \Android 自定义对话框,进度条,下拉刷新等0 ?: w. K W- z, w6 }' u* q7 D
更多相关手机软件开发、安卓软件开发、Android软件开发教程学习请移步到手机软件开发频道。 |