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.graphics.Canvas;
     21 import android.graphics.Color;
     22 import android.graphics.Paint;
     23 import android.util.AttributeSet;
     24 import android.view.View;
     25 
     26 public class WaveScopeView extends View {
     27     @SuppressWarnings("unused")
     28     private static final String TAG = "WaveScopeView";
     29 
     30     private final Paint mPaint = new Paint();
     31 
     32     private int mBackgroundColor = Color.WHITE;
     33     private int mTraceColor = Color.BLACK;
     34 
     35     private short[] mPCM16Buffer;
     36     private float[] mPCMFloatBuffer;
     37 
     38     private int mNumChannels = 2;
     39     private int mNumFrames = 0;
     40 
     41     private float[] mPointsBuffer;
     42 
     43     // Horrible kludge
     44     private static int mCachedWidth = 0;
     45 
     46     public WaveScopeView(Context context, AttributeSet attrs) {
     47         super(context, attrs);
     48     }
     49 
     50     @Override
     51     public void setBackgroundColor(int color) { mBackgroundColor = color; }
     52 
     53     public void setTraceColor(int color) { mTraceColor = color; }
     54 
     55     public void setPCM16Buff(short[] smpl16Buff, int numChans, int numFrames) {
     56         mPCM16Buffer = smpl16Buff;
     57         mPCMFloatBuffer = null;
     58 
     59         mNumChannels = numChans;
     60         mNumFrames = numFrames;
     61 
     62         setupPointBuffer();
     63 
     64         invalidate();
     65     }
     66 
     67     public void setPCMFloatBuff(float[] smplFloatBuff, int numChans, int numFrames) {
     68         mPCMFloatBuffer = smplFloatBuff;
     69         mPCM16Buffer = null;
     70 
     71         mNumChannels = numChans;
     72         mNumFrames = numFrames;
     73 
     74         setupPointBuffer();
     75 
     76         invalidate();
     77     }
     78 
     79     private void setupPointBuffer() {
     80         int width = getWidth();
     81 
     82         // Horrible kludge
     83         if (width == 0) {
     84             width = mCachedWidth;
     85         } else {
     86             mCachedWidth = width;
     87         }
     88 
     89         // Canvas.drawLines() uses 2 points (float pairs) per line-segment
     90         mPointsBuffer = new float[mNumFrames * 4];
     91 
     92         float xIncr = (float) width / (float) mNumFrames;
     93 
     94         float X = 0;
     95         int len = mPointsBuffer.length;
     96         for (int pntIndex = 0; pntIndex < len;) {
     97             mPointsBuffer[pntIndex] = X;
     98             pntIndex += 2; // skip Y
     99 
    100             X += xIncr;
    101 
    102             mPointsBuffer[pntIndex] = X;
    103             pntIndex += 2; // skip Y
    104         }
    105     }
    106 
    107     /**
    108      * Draws 1 channel of an interleaved block of SMPL16 samples.
    109      * @param cvs The Canvas to draw into.
    110      * @param samples The (potentially) multi-channel sample block.
    111      * @param numFrames The number of FRAMES in the specified sample block.
    112      * @param numChans The number of interleaved channels in the specified sample block.
    113      * @param chanIndex The (0-based) index of the channel to draw.
    114      * @param zeroY The Y-coordinate of sample value 0 (zero).
    115      */
    116     private void drawChannel16(Canvas cvs, short[] samples, int numFrames, int numChans,
    117             int chanIndex, float zeroY) {
    118         float yScale = getHeight() / (float) (Short.MAX_VALUE * 2 * numChans);
    119         int pntIndex = 1; // of the first Y coordinate
    120         float Y = zeroY;
    121         int smpl = chanIndex;
    122         for (int frame = 0; frame < numFrames; frame++) {
    123             mPointsBuffer[pntIndex] = Y;
    124             pntIndex += 2;
    125 
    126             Y = zeroY - (samples[smpl] * yScale);
    127 
    128             mPointsBuffer[pntIndex] = Y;
    129             pntIndex += 2;
    130 
    131             smpl += numChans;
    132         }
    133         cvs.drawLines(mPointsBuffer, mPaint);
    134     }
    135 
    136     /**
    137      * Draws 1 channel of an interleaved block of FLOAT samples.
    138      * @param cvs The Canvas to draw into.
    139      * @param samples The (potentially) multi-channel sample block.
    140      * @param numFrames The number of FRAMES in the specified sample block.
    141      * @param numChans The number of interleaved channels in the specified sample block.
    142      * @param chanIndex The (0-based) index of the channel to draw.
    143      * @param zeroY The Y-coordinate of sample value 0 (zero).
    144      */
    145     private void drawChannelFloat(Canvas cvs, float[] samples, int numFrames, int numChans,
    146             int chanIndex, float zeroY) {
    147         float yScale = getHeight() / (float) (2 * numChans);
    148         int pntIndex = 1; // of the first Y coordinate
    149         float Y = zeroY;
    150         int smpl = chanIndex;
    151         for (int frame = 0; frame < numFrames; frame++) {
    152             mPointsBuffer[pntIndex] = Y;
    153             pntIndex += 2;
    154 
    155             Y = zeroY - (samples[smpl] * yScale);
    156 
    157             mPointsBuffer[pntIndex] = Y;
    158             pntIndex += 2;
    159 
    160             smpl += numChans;
    161         }
    162         cvs.drawLines(mPointsBuffer, mPaint);
    163     }
    164 
    165     @Override
    166     protected void onDraw(Canvas canvas) {
    167         int height = getHeight();
    168         mPaint.setColor(mBackgroundColor);
    169         canvas.drawRect(0, 0, getWidth(), height, mPaint);
    170 
    171         mPaint.setColor(mTraceColor);
    172         if (mPCM16Buffer != null) {
    173             float yOffset = height / (2.0f * mNumChannels);
    174             float yDelta = height / (float) mNumChannels;
    175             for(int channel = 0; channel < mNumChannels; channel++) {
    176                 drawChannel16(canvas, mPCM16Buffer, mNumFrames, mNumChannels, channel, yOffset);
    177                 yOffset += yDelta;
    178             }
    179         } else if (mPCMFloatBuffer != null) {
    180             float yOffset = height / (2.0f * mNumChannels);
    181             float yDelta = height / (float) mNumChannels;
    182             for(int channel = 0; channel < mNumChannels; channel++) {
    183                 drawChannelFloat(canvas, mPCMFloatBuffer, mNumFrames, mNumChannels, channel, yOffset);
    184                 yOffset += yDelta;
    185             }
    186         }
    187         // Log.i("WaveView", "onDraw() - done");
    188     }
    189 }
    190