Home | History | Annotate | Download | only in audiolib
      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 
     53     public StreamPlayer() {}
     54 
     55     public int getSampleRate() { return mSampleRate; }
     56     public int getNumBurstFrames() { return mNumBurstFrames; }
     57 
     58     public void setChanGains(float[] chanGains) {
     59         mChanGains = chanGains;
     60     }
     61 
     62     public boolean isPlaying() { return mPlaying; }
     63 
     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 
     75     public void setFiller(AudioFiller filler) { mFiller = filler; }
     76 
     77     public void setRouting(AudioDeviceInfo routingDevice) {
     78         mRoutingDevice = routingDevice;
     79         if (mAudioTrack != null) {
     80             mAudioTrack.setPreferredDevice(mRoutingDevice);
     81         }
     82     }
     83 
     84     public AudioTrack getAudioTrack() { return mAudioTrack; }
     85 
     86     private void allocBurstBuffer() {
     87         mBurstBuffer = new float[mNumBurstFrames * mNumChans];
     88     }
     89 
     90     private static int calcNumBufferBytes(int sampleRate, int numChannels, int encoding) {
     91         return AudioTrack.getMinBufferSize(sampleRate,
     92                     AudioUtils.countToOutPositionMask(numChannels),
     93                     encoding);
     94     }
     95 
     96     private static int calcNumBufferFrames(int sampleRate, int numChannels, int encoding) {
     97         return calcNumBufferBytes(sampleRate, numChannels, encoding)
     98                 / AudioUtils.calcFrameSizeInBytes(encoding, numChannels);
     99     }
    100 
    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 
    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.e(TAG, "Couldn't open AudioTrack: " + ex);
    135             mAudioTrack = null;
    136             return false;
    137         }
    138     }
    139 
    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 
    151     public void close() {
    152         stop();
    153 
    154         waitForPlayerThreadToExit();
    155 
    156         if (mAudioTrack != null) {
    157             mAudioTrack.release();
    158             mAudioTrack = null;
    159         }
    160     }
    161 
    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 
    177     public void stop() {
    178         mPlaying = false;
    179     }
    180 
    181     //
    182     // StreamPlayerRunnable
    183     //
    184     private class StreamPlayerRunnable implements Runnable {
    185         @Override
    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