• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package android.media.cts;
17 
18 import android.media.AudioFormat;
19 import android.media.AudioManager;
20 import android.media.AudioTrack;
21 import android.util.Log;
22 
23 import java.util.LinkedList;
24 
25 /**
26  * Class for playing audio by using audio track.
27  * {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods will
28  * block until all data has been written to system. In order to avoid blocking, this class
29  * caculates available buffer size first then writes to audio sink.
30  */
31 public class NonBlockingAudioTrack {
32     private static final String TAG = NonBlockingAudioTrack.class.getSimpleName();
33 
34     class QueueElem {
35         byte[] data;
36         int offset;
37         int size;
38     }
39 
40     private AudioTrack mAudioTrack;
41     private boolean mWriteMorePending = false;
42     private int mSampleRate;
43     private int mFrameSize;
44     private int mBufferSizeInFrames;
45     private int mNumFramesSubmitted = 0;
46     private int mNumBytesQueued = 0;
47     private LinkedList<QueueElem> mQueue = new LinkedList<QueueElem>();
48 
NonBlockingAudioTrack(int sampleRate, int channelCount)49     public NonBlockingAudioTrack(int sampleRate, int channelCount) {
50         int channelConfig;
51         switch (channelCount) {
52             case 1:
53                 channelConfig = AudioFormat.CHANNEL_OUT_MONO;
54                 break;
55             case 2:
56                 channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
57                 break;
58             case 6:
59                 channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
60                 break;
61             default:
62                 throw new IllegalArgumentException();
63         }
64 
65         int minBufferSize =
66             AudioTrack.getMinBufferSize(
67                     sampleRate,
68                     channelConfig,
69                     AudioFormat.ENCODING_PCM_16BIT);
70 
71         int bufferSize = 2 * minBufferSize;
72 
73         mAudioTrack = new AudioTrack(
74                 AudioManager.STREAM_MUSIC,
75                 sampleRate,
76                 channelConfig,
77                 AudioFormat.ENCODING_PCM_16BIT,
78                 bufferSize,
79                 AudioTrack.MODE_STREAM);
80 
81         mSampleRate = sampleRate;
82         mFrameSize = 2 * channelCount;
83         mBufferSizeInFrames = bufferSize / mFrameSize;
84     }
85 
getAudioTimeUs()86     public long getAudioTimeUs() {
87         int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
88 
89         return (numFramesPlayed * 1000000L) / mSampleRate;
90     }
91 
getNumBytesQueued()92     public int getNumBytesQueued() {
93         return mNumBytesQueued;
94     }
95 
play()96     public void play() {
97         mAudioTrack.play();
98     }
99 
stop()100     public void stop() {
101         cancelWriteMore();
102 
103         mAudioTrack.stop();
104 
105         mNumFramesSubmitted = 0;
106         mQueue.clear();
107         mNumBytesQueued = 0;
108     }
109 
pause()110     public void pause() {
111         cancelWriteMore();
112 
113         mAudioTrack.pause();
114     }
115 
release()116     public void release() {
117         cancelWriteMore();
118 
119         mAudioTrack.release();
120         mAudioTrack = null;
121     }
122 
process()123     public void process() {
124         mWriteMorePending = false;
125         writeMore();
126     }
127 
getPlayState()128     public int getPlayState() {
129         return mAudioTrack.getPlayState();
130     }
131 
writeMore()132     private void writeMore() {
133         if (mQueue.isEmpty()) {
134             return;
135         }
136 
137         int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
138         int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
139         int numFramesAvailableToWrite = mBufferSizeInFrames - numFramesPending;
140         int numBytesAvailableToWrite = numFramesAvailableToWrite * mFrameSize;
141 
142         while (numBytesAvailableToWrite > 0) {
143             QueueElem elem = mQueue.peekFirst();
144 
145             int numBytes = elem.size;
146             if (numBytes > numBytesAvailableToWrite) {
147                 numBytes = numBytesAvailableToWrite;
148             }
149 
150             int written = mAudioTrack.write(elem.data, elem.offset, numBytes);
151             assert(written == numBytes);
152 
153             mNumFramesSubmitted += written / mFrameSize;
154 
155             elem.size -= numBytes;
156             numBytesAvailableToWrite -= numBytes;
157             mNumBytesQueued -= numBytes;
158 
159             if (elem.size == 0) {
160                 mQueue.removeFirst();
161 
162                 if (mQueue.isEmpty()) {
163                     break;
164                 }
165             } else {
166                 elem.offset += numBytes;
167             }
168         }
169 
170         if (!mQueue.isEmpty()) {
171             scheduleWriteMore();
172         }
173     }
174 
scheduleWriteMore()175     private void scheduleWriteMore() {
176         if (mWriteMorePending) {
177             return;
178         }
179 
180         int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
181         int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
182         int pendingDurationMs = 1000 * numFramesPending / mSampleRate;
183 
184         mWriteMorePending = true;
185     }
186 
cancelWriteMore()187     private void cancelWriteMore() {
188         mWriteMorePending = false;
189     }
190 
write(byte[] data, int size)191     public void write(byte[] data, int size) {
192         QueueElem elem = new QueueElem();
193         elem.data = data;
194         elem.offset = 0;
195         elem.size = size;
196 
197         // accumulate size written to queue
198         mNumBytesQueued += size;
199         mQueue.add(elem);
200     }
201 }
202 
203