• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 
17 package com.android.cts.verifier.audio.audiolib;
18 
19 import android.content.Context;
20 import android.media.AudioDeviceInfo;
21 import android.media.AudioFormat;
22 import android.media.AudioManager;
23 import android.media.AudioTrack;
24 
25 import android.util.Log;
26 
27 /**
28  * Plays audio data from a stream. Audio data comes from a provided AudioFiller subclass instance.
29  */
30 public class StreamPlayer {
31     @SuppressWarnings("unused")
32     private static String TAG = "StreamPlayer";
33 
34     private int mSampleRate;
35     private int mNumChans;
36 
37     private AudioFiller mFiller;
38 
39     private Thread mPlayerThread;
40 
41     private AudioTrack mAudioTrack;
42     private int mNumAudioTrackFrames; // number of frames for INTERNAL AudioTrack buffer
43 
44     // The Burst Buffer. This is the buffer we fill with audio and feed into the AudioTrack.
45     private int mNumBurstFrames;
46     private float[] mBurstBuffer;
47 
48     private float[] mChanGains;
49     private volatile boolean mPlaying;
50 
51     private AudioDeviceInfo mRoutingDevice;
52 
StreamPlayer()53     public StreamPlayer() {}
54 
getSampleRate()55     public int getSampleRate() { return mSampleRate; }
getNumBurstFrames()56     public int getNumBurstFrames() { return mNumBurstFrames; }
57 
setChanGains(float[] chanGains)58     public void setChanGains(float[] chanGains) {
59         mChanGains = chanGains;
60     }
61 
isPlaying()62     public boolean isPlaying() { return mPlaying; }
63 
applyChannelGains()64     private void applyChannelGains() {
65         if (mChanGains != null) {
66             int buffIndex = 0;
67             for (int frame = 0; frame < mNumBurstFrames; frame++) {
68                 for (int chan = 0; chan < mNumChans; chan++) {
69                     mBurstBuffer[buffIndex++] *= mChanGains[chan];
70                 }
71             }
72         }
73     }
74 
setFiller(AudioFiller filler)75     public void setFiller(AudioFiller filler) { mFiller = filler; }
76 
setRouting(AudioDeviceInfo routingDevice)77     public void setRouting(AudioDeviceInfo routingDevice) {
78         mRoutingDevice = routingDevice;
79         if (mAudioTrack != null) {
80             mAudioTrack.setPreferredDevice(mRoutingDevice);
81         }
82     }
83 
getAudioTrack()84     public AudioTrack getAudioTrack() { return mAudioTrack; }
85 
allocBurstBuffer()86     private void allocBurstBuffer() {
87         mBurstBuffer = new float[mNumBurstFrames * mNumChans];
88     }
89 
calcNumBufferBytes(int sampleRate, int numChannels, int encoding)90     private static int calcNumBufferBytes(int sampleRate, int numChannels, int encoding) {
91         return AudioTrack.getMinBufferSize(sampleRate,
92                     AudioUtils.countToOutPositionMask(numChannels),
93                     encoding);
94     }
95 
calcNumBufferFrames(int sampleRate, int numChannels, int encoding)96     private static int calcNumBufferFrames(int sampleRate, int numChannels, int encoding) {
97         return calcNumBufferBytes(sampleRate, numChannels, encoding)
98                 / AudioUtils.calcFrameSizeInBytes(encoding, numChannels);
99     }
100 
calcNumBurstFrames(AudioManager am)101     public static int calcNumBurstFrames(AudioManager am) {
102         String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
103         return Integer.parseInt(framesPerBuffer, 10);
104     }
105 
open(int numChans, int sampleRate, int numBurstFrames, AudioFiller filler)106     public boolean open(int numChans, int sampleRate, int numBurstFrames, AudioFiller filler) {
107 //        Log.i(TAG, "StreamPlayer.open(chans:" + numChans + ", rate:" + sampleRate +
108 //                ", frames:" + numBurstFrames);
109 
110         mNumChans = numChans;
111         mSampleRate = sampleRate;
112         mNumBurstFrames = numBurstFrames;
113 
114         mNumAudioTrackFrames =
115                 calcNumBufferFrames(sampleRate, numChans, AudioFormat.ENCODING_PCM_FLOAT);
116 
117         mFiller = filler;
118 
119         int bufferSizeInBytes = mNumAudioTrackFrames *
120                 AudioUtils.calcFrameSizeInBytes(AudioFormat.ENCODING_PCM_FLOAT, mNumChans);
121         try {
122             mAudioTrack = new AudioTrack.Builder()
123                     .setAudioFormat(new AudioFormat.Builder()
124                             .setEncoding(AudioFormat.ENCODING_PCM_FLOAT)
125                             .setSampleRate(mSampleRate)
126                             .setChannelIndexMask(AudioUtils.countToIndexMask(mNumChans))
127                             .build())
128                     .setBufferSizeInBytes(bufferSizeInBytes)
129                     .build();
130 
131             allocBurstBuffer();
132             return true;
133         }  catch (UnsupportedOperationException ex) {
134             Log.i(TAG, "Couldn't open AudioTrack: " + ex);
135             mAudioTrack = null;
136             return false;
137         }
138     }
139 
waitForPlayerThreadToExit()140     private void waitForPlayerThreadToExit() {
141         try {
142             if (mPlayerThread != null) {
143                 mPlayerThread.join();
144                 mPlayerThread = null;
145             }
146         } catch (InterruptedException e) {
147             e.printStackTrace();
148         }
149     }
150 
close()151     public void close() {
152         stop();
153 
154         waitForPlayerThreadToExit();
155 
156         if (mAudioTrack != null) {
157             mAudioTrack.release();
158             mAudioTrack = null;
159         }
160     }
161 
start()162     public boolean start() {
163         if (!mPlaying && mAudioTrack != null) {
164             mPlaying = true;
165 
166             waitForPlayerThreadToExit(); // just to be sure.
167 
168             mPlayerThread = new Thread(new StreamPlayerRunnable(), "StreamPlayer Thread");
169             mPlayerThread.start();
170 
171             return true;
172         }
173 
174         return false;
175     }
176 
stop()177     public void stop() {
178         mPlaying = false;
179     }
180 
181     //
182     // StreamPlayerRunnable
183     //
184     private class StreamPlayerRunnable implements Runnable {
185         @Override
run()186         public void run() {
187             final int numBurstSamples = mNumBurstFrames * mNumChans;
188 
189             mAudioTrack.play();
190             while (true) {
191                 boolean playing;
192                 synchronized(this) {
193                     playing = mPlaying;
194                 }
195                 if (!playing) {
196                     break;
197                 }
198 
199                 mFiller.fill(mBurstBuffer, mNumBurstFrames, mNumChans);
200                 if (mChanGains != null) {
201                     applyChannelGains();
202                 }
203                 int numSamplesWritten =
204                         mAudioTrack.write(mBurstBuffer, 0, numBurstSamples, AudioTrack.WRITE_BLOCKING);
205                 if (numSamplesWritten < 0) {
206                     // error
207                     Log.i(TAG, "AudioTrack write error: " + numSamplesWritten);
208                     stop();
209                 } else if (numSamplesWritten < numBurstSamples) {
210                     // end of stream
211                     Log.i(TAG, "Stream Complete.");
212                     stop();
213                 }
214             }
215         }
216     }
217 }
218