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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
|
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media;
import java.lang.ref.WeakReference;
import java.lang.IllegalArgumentException;
import java.lang.IllegalStateException;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.media.AudioManager;
import android.util.Log;
/**
* The AudioTrack class manages and plays a single audio resource for Java applications.
* It allows to stream PCM audio buffers to the audio hardware for playback. This is
* achieved by "pushing" the data to the AudioTrack object using one of the
* {@link #write(byte[], int, int)} and {@link #write(short[], 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 is to 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 hardware in chunks of
* sizes inferior to the total buffer size.
*/
public class AudioTrack
{
//---------------------------------------------------------
// Constants
//--------------------
/** Minimum value for a channel volume */
private static final float VOLUME_MIN = 0.0f;
/** Maximum value for a channel volume */
private static final float VOLUME_MAX = 1.0f;
/** indicates AudioTrack state is stopped */
public static final int PLAYSTATE_STOPPED = 1; // matches SL_PLAYSTATE_STOPPED
/** indicates AudioTrack state is paused */
public static final int PLAYSTATE_PAUSED = 2; // matches SL_PLAYSTATE_PAUSED
/** indicates AudioTrack state is playing */
public static final int PLAYSTATE_PLAYING = 3; // matches SL_PLAYSTATE_PLAYING
/**
* Creation mode where audio data is transferred from Java to the native layer
* only once before the audio starts playing.
*/
public static final int MODE_STATIC = 0;
/**
* Creation mode where audio data is streamed from Java to the native layer
* as the audio is playing.
*/
public static final int MODE_STREAM = 1;
/**
* State of an AudioTrack that was not successfully initialized upon creation.
*/
public static final int STATE_UNINITIALIZED = 0;
/**
* State of an AudioTrack that is ready to be used.
*/
public static final int STATE_INITIALIZED = 1;
/**
* State of a successfully initialized AudioTrack that uses static data,
* but that hasn't received that data yet.
*/
public static final int STATE_NO_STATIC_DATA = 2;
// Error codes:
// to keep in sync with frameworks/base/core/jni/android_media_AudioTrack.cpp
/**
* Denotes a successful operation.
*/
public static final int SUCCESS = 0;
/**
* Denotes a generic operation failure.
*/
public static final int ERROR = -1;
/**
* Denotes a failure due to the use of an invalid value.
*/
public static final int ERROR_BAD_VALUE = -2;
/**
* Denotes a failure due to the improper use of a method.
*/
public static final int ERROR_INVALID_OPERATION = -3;
private static final int ERROR_NATIVESETUP_AUDIOSYSTEM = -16;
private static final int ERROR_NATIVESETUP_INVALIDCHANNELMASK = -17;
private static final int ERROR_NATIVESETUP_INVALIDFORMAT = -18;
private static final int ERROR_NATIVESETUP_INVALIDSTREAMTYPE = -19;
private static final int ERROR_NATIVESETUP_NATIVEINITFAILED = -20;
// Events:
// to keep in sync with frameworks/base/include/media/AudioTrack.h
/**
* Event id denotes when playback head has reached a previously set marker.
*/
private static final int NATIVE_EVENT_MARKER = 3;
/**
* Event id denotes when previously set update period has elapsed during playback.
*/
private static final int NATIVE_EVENT_NEW_POS = 4;
private final static String TAG = "AudioTrack-Java";
//--------------------------------------------------------------------------
// Member variables
//--------------------
/**
* Indicates the state of the AudioTrack instance.
*/
private int mState = STATE_UNINITIALIZED;
/**
* Indicates the play state of the AudioTrack instance.
*/
private int mPlayState = PLAYSTATE_STOPPED;
/**
* Lock to make sure mPlayState updates are reflecting the actual state of the object.
*/
private final Object mPlayStateLock = new Object();
/**
* The listener the AudioTrack notifies when the playback position reaches a marker
* or for periodic updates during the progression of the playback head.
* @see #setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener)
*/
private OnPlaybackPositionUpdateListener mPositionListener = null;
/**
* Lock to protect event listener updates against event notifications.
*/
private final Object mPositionListenerLock = new Object();
/**
* Size of the native audio buffer.
*/
private int mNativeBufferSizeInBytes = 0;
/**
* Handler for marker events coming from the native code.
*/
private NativeEventHandlerDelegate mEventHandlerDelegate = null;
/**
* Looper associated with the thread that creates the AudioTrack instance.
*/
private Looper mInitializationLooper = null;
/**
* The audio data sampling rate in Hz.
*/
private int mSampleRate = 22050;
/**
* The number of audio output channels (1 is mono, 2 is stereo).
*/
private int mChannelCount = 1;
/**
* The audio channel mask.
*/
private int mChannels = AudioFormat.CHANNEL_OUT_MONO;
/**
* The type of the audio stream to play. See
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and
* {@link AudioManager#STREAM_ALARM}
*/
private int mStreamType = AudioManager.STREAM_MUSIC;
/**
* The way audio is consumed by the hardware, streaming or static.
*/
private int mDataLoadMode = MODE_STREAM;
/**
* The current audio channel configuration.
*/
private int mChannelConfiguration = AudioFormat.CHANNEL_OUT_MONO;
/**
* The encoding of the audio samples.
* @see AudioFormat#ENCODING_PCM_8BIT
* @see AudioFormat#ENCODING_PCM_16BIT
*/
private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
/**
* Audio session ID
*/
private int mSessionId = 0;
//--------------------------------
// Used exclusively by native code
//--------------------
/**
* Accessed by native methods: provides access to C++ AudioTrack object.
*/
@SuppressWarnings("unused")
private int mNativeTrackInJavaObj;
/**
* Accessed by native methods: provides access to the JNI data (i.e. resources used by
* the native AudioTrack object, but not stored in it).
*/
@SuppressWarnings("unused")
private int mJniData;
//--------------------------------------------------------------------------
// Constructor, Finalize
//--------------------
/**
* Class constructor.
* @param streamType the type of the audio stream. See
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and
* {@link AudioManager#STREAM_ALARM}
* @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but
* not limited to) 44100, 22050 and 11025.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
* @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read
* from for playback. If using the AudioTrack in streaming mode, you can write data into
* this buffer in smaller chunks than this size. If using the AudioTrack in static mode,
* this is the maximum size of the sound that will be played for this instance.
* See {@link #getMinBufferSize(int, int, int)} to determine the minimum required buffer size
* for the successful creation of an AudioTrack instance in streaming mode. Using values
* smaller than getMinBufferSize() will result in an initialization failure.
* @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM}
* @throws java.lang.IllegalArgumentException
*/
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode)
throws IllegalArgumentException {
this(streamType, sampleRateInHz, channelConfig, audioFormat,
bufferSizeInBytes, mode, 0);
}
/**
* Class constructor with audio session. Use this constructor when the AudioTrack must be
* attached to a particular audio session. The primary use of the audio session ID is to
* associate audio effects to a particular instance of AudioTrack: if an audio session ID
* is provided when creating an AudioEffect, this effect will be applied only to audio tracks
* and media players in the same session and not to the output mix.
* When an AudioTrack is created without specifying a session, it will create its own session
* which can be retreived by calling the {@link #getAudioSessionId()} method.
* If a session ID is provided, this AudioTrack will share effects attached to this session
* with all other media players or audio tracks in the same session.
* @param streamType the type of the audio stream. See
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and
* {@link AudioManager#STREAM_ALARM}
* @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but
* not limited to) 44100, 22050 and 11025.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
* @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read
* from for playback. If using the AudioTrack in streaming mode, you can write data into
* this buffer in smaller chunks than this size. If using the AudioTrack in static mode,
* this is the maximum size of the sound that will be played for this instance.
* See {@link #getMinBufferSize(int, int, int)} to determine the minimum required buffer size
* for the successful creation of an AudioTrack instance in streaming mode. Using values
* smaller than getMinBufferSize() will result in an initialization failure.
* @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM}
* @param sessionId Id of audio session the AudioTrack must be attached to
* @throws java.lang.IllegalArgumentException
*/
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode, int sessionId)
throws IllegalArgumentException {
mState = STATE_UNINITIALIZED;
// remember which looper is associated with the AudioTrack instanciation
if ((mInitializationLooper = Looper.myLooper()) == null) {
mInitializationLooper = Looper.getMainLooper();
}
audioParamCheck(streamType, sampleRateInHz, channelConfig, audioFormat, mode);
audioBuffSizeCheck(bufferSizeInBytes);
if (sessionId < 0) {
throw (new IllegalArgumentException("Invalid audio session ID: "+sessionId));
}
int[] session = new int[1];
session[0] = sessionId;
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this),
mStreamType, mSampleRate, mChannels, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
}
mSessionId = session[0];
if (mDataLoadMode == MODE_STATIC) {
mState = STATE_NO_STATIC_DATA;
} else {
mState = STATE_INITIALIZED;
}
}
// Convenience method for the constructor's parameter checks.
// This is where constructor IllegalArgumentException-s are thrown
// postconditions:
// mStreamType is valid
// mChannelCount is valid
// mChannels is valid
// mAudioFormat is valid
// mSampleRate is valid
// mDataLoadMode is valid
private void audioParamCheck(int streamType, int sampleRateInHz,
int channelConfig, int audioFormat, int mode) {
//--------------
// stream type
if( (streamType != AudioManager.STREAM_ALARM) && (streamType != AudioManager.STREAM_MUSIC)
&& (streamType != AudioManager.STREAM_RING) && (streamType != AudioManager.STREAM_SYSTEM)
&& (streamType != AudioManager.STREAM_VOICE_CALL)
&& (streamType != AudioManager.STREAM_NOTIFICATION)
&& (streamType != AudioManager.STREAM_BLUETOOTH_SCO)
&& (streamType != AudioManager.STREAM_DTMF)) {
throw (new IllegalArgumentException("Invalid stream type."));
} else {
mStreamType = streamType;
}
//--------------
// sample rate
if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) {
throw (new IllegalArgumentException(sampleRateInHz
+ "Hz is not a supported sample rate."));
} else {
mSampleRate = sampleRateInHz;
}
//--------------
// channel config
mChannelConfiguration = channelConfig;
switch (channelConfig) {
case AudioFormat.CHANNEL_OUT_DEFAULT: //AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
case AudioFormat.CHANNEL_OUT_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
mChannelCount = 1;
mChannels = AudioFormat.CHANNEL_OUT_MONO;
break;
case AudioFormat.CHANNEL_OUT_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
mChannelCount = 2;
mChannels = AudioFormat.CHANNEL_OUT_STEREO;
break;
default:
mChannelCount = 0;
mChannels = AudioFormat.CHANNEL_INVALID;
mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_INVALID;
throw(new IllegalArgumentException("Unsupported channel configuration."));
}
//--------------
// audio format
switch (audioFormat) {
case AudioFormat.ENCODING_DEFAULT:
mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
break;
case AudioFormat.ENCODING_PCM_16BIT:
case AudioFormat.ENCODING_PCM_8BIT:
mAudioFormat = audioFormat;
break;
default:
mAudioFormat = AudioFormat.ENCODING_INVALID;
throw(new IllegalArgumentException("Unsupported sample encoding."
+ " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT."));
}
//--------------
// audio load mode
if ( (mode != MODE_STREAM) && (mode != MODE_STATIC) ) {
throw(new IllegalArgumentException("Invalid mode."));
} else {
mDataLoadMode = mode;
}
}
// Convenience method for the contructor's audio buffer size check.
// preconditions:
// mChannelCount is valid
// mAudioFormat is valid
// postcondition:
// mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
private void audioBuffSizeCheck(int audioBufferSize) {
// NB: this section is only valid with PCM data.
// To update when supporting compressed formats
int frameSizeInBytes = mChannelCount
* (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2);
if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) {
throw (new IllegalArgumentException("Invalid audio buffer size."));
}
mNativeBufferSizeInBytes = audioBufferSize;
}
/**
* Releases the native AudioTrack resources.
*/
public void release() {
// even though native_release() stops the native AudioTrack, we need to stop
// AudioTrack subclasses too.
try {
stop();
} catch(IllegalStateException ise) {
// don't raise an exception, we're releasing the resources.
}
native_release();
mState = STATE_UNINITIALIZED;
}
@Override
protected void finalize() {
native_finalize();
}
//--------------------------------------------------------------------------
// Getters
//--------------------
/**
* Returns the minimum valid volume value. Volume values set under this one will
* be clamped at this value.
* @return the minimum volume expressed as a linear attenuation.
*/
static public float getMinVolume() {
return AudioTrack.VOLUME_MIN;
}
/**
* Returns the maximum valid volume value. Volume values set above this one will
* be clamped at this value.
* @return the maximum volume expressed as a linear attenuation.
*/
static public float getMaxVolume() {
return AudioTrack.VOLUME_MAX;
}
/**
* Returns the configured audio data sample rate in Hz
*/
public int getSampleRate() {
return mSampleRate;
}
/**
* Returns the current playback rate in Hz.
*/
public int getPlaybackRate() {
return native_get_playback_rate();
}
/**
* Returns the configured audio data format. See {@link AudioFormat#ENCODING_PCM_16BIT}
* and {@link AudioFormat#ENCODING_PCM_8BIT}.
*/
public int getAudioFormat() {
return mAudioFormat;
}
/**
* Returns the type of audio stream this AudioTrack is configured for.
* Compare the result against {@link AudioManager#STREAM_VOICE_CALL},
* {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING},
* {@link AudioManager#STREAM_MUSIC} or {@link AudioManager#STREAM_ALARM}
*/
public int getStreamType() {
return mStreamType;
}
/**
* Returns the configured channel configuration.
* See {@link AudioFormat#CHANNEL_OUT_MONO}
* and {@link AudioFormat#CHANNEL_OUT_STEREO}.
*/
public int getChannelConfiguration() {
return mChannelConfiguration;
}
/**
* Returns the configured number of channels.
*/
public int getChannelCount() {
return mChannelCount;
}
/**
* Returns the state of the AudioTrack instance. This is useful after the
* AudioTrack instance has been created to check if it was initialized
* properly. This ensures that the appropriate hardware resources have been
* acquired.
* @see #STATE_INITIALIZED
* @see #STATE_NO_STATIC_DATA
* @see #STATE_UNINITIALIZED
*/
public int getState() {
return mState;
}
/**
* Returns the playback state of the AudioTrack instance.
* @see #PLAYSTATE_STOPPED
* @see #PLAYSTATE_PAUSED
* @see #PLAYSTATE_PLAYING
*/
public int getPlayState() {
return mPlayState;
}
/**
* Returns the native frame count used by the hardware.
*/
protected int getNativeFrameCount() {
return native_get_native_frame_count();
}
/**
* Returns marker position expressed in frames.
*/
public int getNotificationMarkerPosition() {
return native_get_marker_pos();
}
/**
* Returns the notification update period expressed in frames.
*/
public int getPositionNotificationPeriod() {
return native_get_pos_update_period();
}
/**
* Returns the playback head position expressed in frames
*/
public int getPlaybackHeadPosition() {
return native_get_position();
}
/**
* Returns the hardware output sample rate
*/
static public int getNativeOutputSampleRate(int streamType) {
return native_get_output_sample_rate(streamType);
}
/**
* Returns the minimum buffer size required for the successful creation of an AudioTrack
* object to be created in the {@link #MODE_STREAM} mode. Note that this size doesn't
* guarantee a smooth playback under load, and higher values should be chosen according to
* the expected frequency at which the buffer will be refilled with additional data to play.
* @param sampleRateInHz the sample rate expressed in Hertz.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_OUT_MONO} and
* {@link AudioFormat#CHANNEL_OUT_STEREO}
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
* @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed,
* or {@link #ERROR} if the implementation was unable to query the hardware for its output
* properties,
* or the minimum buffer size expressed in bytes.
*/
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
switch(channelConfig) {
case AudioFormat.CHANNEL_OUT_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_OUT_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
channelCount = 2;
break;
default:
loge("getMinBufferSize(): Invalid channel configuration.");
return AudioTrack.ERROR_BAD_VALUE;
}
if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
&& (audioFormat != AudioFormat.ENCODING_PCM_8BIT)) {
loge("getMinBufferSize(): Invalid audio format.");
return AudioTrack.ERROR_BAD_VALUE;
}
if ( (sampleRateInHz < 4000) || (sampleRateInHz > 48000) ) {
loge("getMinBufferSize(): " + sampleRateInHz +"Hz is not a supported sample rate.");
return AudioTrack.ERROR_BAD_VALUE;
}
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if ((size == -1) || (size == 0)) {
loge("getMinBufferSize(): error querying hardware");
return AudioTrack.ERROR;
}
else {
return size;
}
}
/**
* Returns the audio session ID.
*
* @return the ID of the audio session this AudioTrack belongs to.
*/
public int getAudioSessionId() {
return mSessionId;
}
//--------------------------------------------------------------------------
// Initialization / configuration
//--------------------
/**
* Sets the listener the AudioTrack notifies when a previously set marker is reached or
* for each periodic playback head position update.
* Notifications will be received in the same thread as the one in which the AudioTrack
* instance was created.
* @param listener
*/
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener) {
setPlaybackPositionUpdateListener(listener, null);
}
/**
* Sets the listener the AudioTrack notifies when a previously set marker is reached or
* for each periodic playback head position update.
* Use this method to receive AudioTrack events in the Handler associated with another
* thread than the one in which you created the AudioTrack instance.
* @param listener
* @param handler the Handler that will receive the event notification messages.
*/
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener,
Handler handler) {
synchronized (mPositionListenerLock) {
mPositionListener = listener;
}
if (listener != null) {
mEventHandlerDelegate = new NativeEventHandlerDelegate(this, handler);
}
}
/**
* Sets the specified left/right output volume values on the AudioTrack. Values are clamped
* to the ({@link #getMinVolume()}, {@link #getMaxVolume()}) interval if outside this range.
* @param leftVolume output attenuation for the left channel. A value of 0.0f is silence,
* a value of 1.0f is no attenuation.
* @param rightVolume output attenuation for the right channel
* @return error code or success, see {@link #SUCCESS},
* {@link #ERROR_INVALID_OPERATION}
*/
public int setStereoVolume(float leftVolume, float rightVolume) {
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
// clamp the volumes
if (leftVolume < getMinVolume()) {
leftVolume = getMinVolume();
}
if (leftVolume > getMaxVolume()) {
leftVolume = getMaxVolume();
}
if (rightVolume < getMinVolume()) {
rightVolume = getMinVolume();
}
if (rightVolume > getMaxVolume()) {
rightVolume = getMaxVolume();
}
native_setVolume(leftVolume, rightVolume);
return SUCCESS;
}
/**
* Sets the playback sample rate for this track. This sets the sampling rate at which
* the audio data will be consumed and played back, not the original sampling rate of the
* content. Setting it to half the sample rate of the content will cause the playback to
* last twice as long, but will also result in a negative pitch shift.
* The valid sample rate range if from 1Hz to twice the value returned by
* {@link #getNativeOutputSampleRate(int)}.
* @param sampleRateInHz the sample rate expressed in Hz
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
public int setPlaybackRate(int sampleRateInHz) {
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
if (sampleRateInHz <= 0) {
return ERROR_BAD_VALUE;
}
return native_set_playback_rate(sampleRateInHz);
}
/**
* Sets the position of the notification marker.
* @param markerInFrames marker in frames
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
public int setNotificationMarkerPosition(int markerInFrames) {
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
return native_set_marker_pos(markerInFrames);
}
/**
* Sets the period for the periodic notification event.
* @param periodInFrames update period expressed in frames
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_INVALID_OPERATION}
*/
public int setPositionNotificationPeriod(int periodInFrames) {
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
return native_set_pos_update_period(periodInFrames);
}
/**
* Sets the playback head position. The track must be stopped for the position to be changed.
* @param positionInFrames playback head position expressed in frames
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
public int setPlaybackHeadPosition(int positionInFrames) {
synchronized(mPlayStateLock) {
if ((mPlayState == PLAYSTATE_STOPPED) || (mPlayState == PLAYSTATE_PAUSED)) {
return native_set_position(positionInFrames);
} else {
return ERROR_INVALID_OPERATION;
}
}
}
/**
* Sets the loop points and the loop count. The loop can be infinite.
* @param startInFrames loop start marker expressed in frames
* @param endInFrames loop end marker expressed in frames
* @param loopCount the number of times the loop is looped.
* A value of -1 means infinite looping.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
public int setLoopPoints(int startInFrames, int endInFrames, int loopCount) {
if (mDataLoadMode == MODE_STREAM) {
return ERROR_INVALID_OPERATION;
}
return native_set_loop(startInFrames, endInFrames, loopCount);
}
/**
* Sets the initialization state of the instance. To be used in an AudioTrack subclass
* constructor to set a subclass-specific post-initialization state.
* @param state the state of the AudioTrack instance
*/
protected void setState(int state) {
mState = state;
}
//---------------------------------------------------------
// Transport control methods
//--------------------
/**
* Starts playing an AudioTrack.
*
* @throws IllegalStateException
*/
public void play()
throws IllegalStateException {
if (mState != STATE_INITIALIZED) {
throw(new IllegalStateException("play() called on uninitialized AudioTrack."));
}
synchronized(mPlayStateLock) {
native_start();
mPlayState = PLAYSTATE_PLAYING;
}
}
/**
* Stops playing the audio data.
*
* @throws IllegalStateException
*/
public void stop()
throws IllegalStateException {
if (mState != STATE_INITIALIZED) {
throw(new IllegalStateException("stop() called on uninitialized AudioTrack."));
}
// stop playing
synchronized(mPlayStateLock) {
native_stop();
mPlayState = PLAYSTATE_STOPPED;
}
}
/**
* Pauses the playback of the audio data. Data that has not been played
* back will not be discarded. Subsequent calls to {@link #play} will play
* this data back.
*
* @throws IllegalStateException
*/
public void pause()
throws IllegalStateException {
if (mState != STATE_INITIALIZED) {
throw(new IllegalStateException("pause() called on uninitialized AudioTrack."));
}
//logd("pause()");
// pause playback
synchronized(mPlayStateLock) {
native_pause();
mPlayState = PLAYSTATE_PAUSED;
}
}
//---------------------------------------------------------
// Audio data supply
//--------------------
/**
* Flushes the audio data currently queued for playback. Any data that has
* not been played back will be discarded.
*/
public void flush() {
if (mState == STATE_INITIALIZED) {
// flush the data in native layer
native_flush();
}
}
/**
* Writes the audio data to the audio hardware for playback. Will block until
* all data has been written to the audio mixer.
* Note that the actual playback of this data might occur after this function
* returns. This function is thread safe with respect to {@link #stop} calls,
* in which case all of the specified data might not be written to the mixer.
*
* @param audioData the array that holds the data to play.
* @param offsetInBytes the offset expressed in bytes in audioData where the data to play
* starts.
* @param sizeInBytes the number of bytes to read in audioData after the offset.
* @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
*/
public int write(byte[] audioData,int offsetInBytes, int sizeInBytes) {
if ((mDataLoadMode == MODE_STATIC)
&& (mState == STATE_NO_STATIC_DATA)
&& (sizeInBytes > 0)) {
mState = STATE_INITIALIZED;
}
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
|| (offsetInBytes + sizeInBytes > audioData.length)) {
return ERROR_BAD_VALUE;
}
return native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat);
}
/**
* Writes the audio data to the audio hardware for playback. Will block until
* all data has been written to the audio mixer.
* Note that the actual playback of this data might occur after this function
* returns. This function is thread safe with respect to {@link #stop} calls,
* in which case all of the specified data might not be written to the mixer.
*
* @param audioData the array that holds the data to play.
* @param offsetInShorts the offset expressed in shorts in audioData where the data to play
* starts.
* @param sizeInShorts the number of bytes to read in audioData after the offset.
* @return the number of shorts that were written or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
* the parameters don't resolve to valid data and indexes.
*/
public int write(short[] audioData, int offsetInShorts, int sizeInShorts) {
if ((mDataLoadMode == MODE_STATIC)
&& (mState == STATE_NO_STATIC_DATA)
&& (sizeInShorts > 0)) {
mState = STATE_INITIALIZED;
}
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0)
|| (offsetInShorts + sizeInShorts > audioData.length)) {
return ERROR_BAD_VALUE;
}
return native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat);
}
/**
* Notifies the native resource to reuse the audio data already loaded in the native
* layer. This call is only valid with AudioTrack instances that don't use the streaming
* model.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
public int reloadStaticData() {
if (mDataLoadMode == MODE_STREAM) {
return ERROR_INVALID_OPERATION;
}
return native_reload_static();
}
//--------------------------------------------------------------------------
// Audio effects management
//--------------------
/**
* Attaches an auxiliary effect to the audio track. A typical auxiliary
* effect is a reverberation effect which can be applied on any sound source
* that directs a certain amount of its energy to this effect. This amount
* is defined by setAuxEffectSendLevel().
* {@see #setAuxEffectSendLevel(float)}.
* <p>After creating an auxiliary effect (e.g.
* {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
* {@link android.media.audiofx.AudioEffect#getId()} and use it when calling
* this method to attach the audio track to the effect.
* <p>To detach the effect from the audio track, call this method with a
* null effect id.
*
* @param effectId system wide unique id of the effect to attach
* @return error code or success, see {@link #SUCCESS},
* {@link #ERROR_INVALID_OPERATION}, {@link #ERROR_BAD_VALUE}
*/
public int attachAuxEffect(int effectId) {
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
return native_attachAuxEffect(effectId);
}
/**
* Sets the send level of the audio track to the attached auxiliary effect
* {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
* <p>By default the send level is 0, so even if an effect is attached to the player
* this method must be called for the effect to be applied.
* <p>Note that the passed level value is a raw scalar. UI controls should be scaled
* logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
* so an appropriate conversion from linear UI input x to level is:
* x == 0 -> level = 0
* 0 < x <= R -> level = 10^(72*(x-R)/20/R)
*
* @param level send level scalar
* @return error code or success, see {@link #SUCCESS},
* {@link #ERROR_INVALID_OPERATION}
*/
public int setAuxEffectSendLevel(float level) {
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
}
// clamp the level
if (level < getMinVolume()) {
level = getMinVolume();
}
if (level > getMaxVolume()) {
level = getMaxVolume();
}
native_setAuxEffectSendLevel(level);
return SUCCESS;
}
//---------------------------------------------------------
// Interface definitions
//--------------------
/**
* Interface definition for a callback to be invoked when the playback head position of
* an AudioTrack has reached a notification marker or has increased by a certain period.
*/
public interface OnPlaybackPositionUpdateListener {
/**
* Called on the listener to notify it that the previously set marker has been reached
* by the playback head.
*/
void onMarkerReached(AudioTrack track);
/**
* Called on the listener to periodically notify it that the playback head has reached
* a multiple of the notification period.
*/
void onPeriodicNotification(AudioTrack track);
}
//---------------------------------------------------------
// Inner classes
//--------------------
/**
* Helper class to handle the forwarding of native events to the appropriate listener
* (potentially) handled in a different thread
*/
private class NativeEventHandlerDelegate {
private final AudioTrack mAudioTrack;
private final Handler mHandler;
NativeEventHandlerDelegate(AudioTrack track, Handler handler) {
mAudioTrack = track;
// find the looper for our new event handler
Looper looper;
if (handler != null) {
looper = handler.getLooper();
} else {
// no given handler, use the looper the AudioTrack was created in
looper = mInitializationLooper;
}
// construct the event handler with this looper
if (looper != null) {
// implement the event handler delegate
mHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
if (mAudioTrack == null) {
return;
}
OnPlaybackPositionUpdateListener listener = null;
synchronized (mPositionListenerLock) {
listener = mAudioTrack.mPositionListener;
}
switch(msg.what) {
case NATIVE_EVENT_MARKER:
if (listener != null) {
listener.onMarkerReached(mAudioTrack);
}
break;
case NATIVE_EVENT_NEW_POS:
if (listener != null) {
listener.onPeriodicNotification(mAudioTrack);
}
break;
default:
Log.e(TAG, "[ android.media.AudioTrack.NativeEventHandler ] " +
"Unknown event type: " + msg.what);
break;
}
}
};
} else {
mHandler = null;
}
}
Handler getHandler() {
return mHandler;
}
}
//---------------------------------------------------------
// Java methods called from the native side
//--------------------
@SuppressWarnings("unused")
private static void postEventFromNative(Object audiotrack_ref,
int what, int arg1, int arg2, Object obj) {
//logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2);
AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
if (track == null) {
return;
}
if (track.mEventHandlerDelegate != null) {
Message m =
track.mEventHandlerDelegate.getHandler().obtainMessage(what, arg1, arg2, obj);
track.mEventHandlerDelegate.getHandler().sendMessage(m);
}
}
//---------------------------------------------------------
// Native methods called from the Java side
//--------------------
private native final int native_setup(Object audiotrack_this,
int streamType, int sampleRate, int nbChannels, int audioFormat,
int buffSizeInBytes, int mode, int[] sessionId);
private native final void native_finalize();
private native final void native_release();
private native final void native_start();
private native final void native_stop();
private native final void native_pause();
private native final void native_flush();
private native final int native_write_byte(byte[] audioData,
int offsetInBytes, int sizeInBytes, int format);
private native final int native_write_short(short[] audioData,
int offsetInShorts, int sizeInShorts, int format);
private native final int native_reload_static();
private native final int native_get_native_frame_count();
private native final void native_setVolume(float leftVolume, float rightVolume);
private native final int native_set_playback_rate(int sampleRateInHz);
private native final int native_get_playback_rate();
private native final int native_set_marker_pos(int marker);
private native final int native_get_marker_pos();
private native final int native_set_pos_update_period(int updatePeriod);
private native final int native_get_pos_update_period();
private native final int native_set_position(int position);
private native final int native_get_position();
private native final int native_set_loop(int start, int end, int loopCount);
static private native final int native_get_output_sample_rate(int streamType);
static private native final int native_get_min_buff_size(
int sampleRateInHz, int channelConfig, int audioFormat);
private native final int native_get_session_id();
private native final int native_attachAuxEffect(int effectId);
private native final void native_setAuxEffectSendLevel(float level);
//---------------------------------------------------------
// Utility methods
//------------------
private static void logd(String msg) {
Log.d(TAG, "[ android.media.AudioTrack ] " + msg);
}
private static void loge(String msg) {
Log.e(TAG, "[ android.media.AudioTrack ] " + msg);
}
}
|