1 /*
2  * Copyright (C) 2021 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.content.res.Resources;
21 import android.content.res.TypedArray;
22 import android.graphics.Canvas;
23 import android.graphics.Color;
24 import android.graphics.Paint;
25 import android.util.AttributeSet;
26 import android.view.View;
27 
28 import com.android.cts.verifier.R;
29 
30 /**
31  * Display an audio waveform in a custom View.
32  */
33 public class WaveformView extends View {
34     private Paint mWavePaint;
35     private int mCurrentWidth;
36     private int mCurrentHeight;
37     private Paint mBackgroundPaint;
38     private float[] mData;
39     private int mSampleCount;
40     private int mSampleOffset;
41     private float mOffsetY;
42     private float mScaleY;
43     private int[] mCursors;
44     private Paint mCursorPaint;
45 
WaveformView(Context context, AttributeSet attrs)46     public WaveformView(Context context, AttributeSet attrs) {
47         super(context, attrs);
48         TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
49                 R.styleable.WaveformView, 0, 0);
50         init();
51     }
52     @SuppressWarnings("deprecation")
init()53     private void init() {
54         Resources res = getResources();
55 
56         mWavePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
57         mWavePaint.setColor(res.getColor(R.color.waveform_line));
58         float strokeWidth = res.getDimension(R.dimen.waveform_stroke_width);
59         mWavePaint.setStrokeWidth(strokeWidth);
60 
61         mCursorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
62         mCursorPaint.setColor(Color.RED);
63         mCursorPaint.setStrokeWidth(3.0f);
64 
65         mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
66         mBackgroundPaint.setColor(res.getColor(R.color.waveform_background));
67         mBackgroundPaint.setStyle(Paint.Style.FILL);
68     }
69 
70     @Override
onSizeChanged(int w, int h, int oldw, int oldh)71     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
72         mCurrentWidth = w;
73         mCurrentHeight = h;
74         mOffsetY = 0.5f * h;
75         mScaleY = 0.0f - mOffsetY;
76     }
77 
78     @Override
onDraw(Canvas canvas)79     protected void onDraw(Canvas canvas) {
80         super.onDraw(canvas);
81         float [] localData = mData;
82         canvas.drawRect(0.0f, 0.0f, mCurrentWidth,
83                 mCurrentHeight, mBackgroundPaint);
84         if (localData == null || mSampleCount == 0) {
85             return;
86         }
87         float xScale = ((float) mCurrentWidth) / (mSampleCount - 1);
88         float x0 = 0.0f;
89         if (xScale < 1.0) {
90             // Draw a vertical bar for multiple samples.
91             float ymin = mOffsetY;
92             float ymax = mOffsetY;
93             for (int i = 0; i < mSampleCount; i++) {
94                 float x1 = i * xScale;
95                 if ((int) x0 != (int) x1) {
96                     // draw old data
97                     canvas.drawLine(x0, ymin, x0, ymax, mWavePaint);
98                     x0 = x1;
99                     ymin = mOffsetY;
100                     ymax = mOffsetY;
101                 }
102                 float y1 = (localData[i] * mScaleY) + mOffsetY;
103                 ymin = Math.min(ymin, y1);
104                 ymax = Math.max(ymax, y1);
105             }
106         } else {
107             // Draw line between samples.
108             float y0 = (localData[0] * mScaleY) + mOffsetY;
109             for (int i = 1; i < mSampleCount; i++) {
110                 float x1 = i * xScale;
111                 float y1 = (localData[i] * mScaleY) + mOffsetY;
112                 canvas.drawLine(x0, y0, x1, y1, mWavePaint);
113                 x0 = x1;
114                 y0 = y1;
115             }
116         }
117         if (mCursors != null) {
118             for (int i = 0; i < mCursors.length; i++) {
119                 float x = mCursors[i] * xScale;
120                 canvas.drawLine(x, 0, x, mCurrentHeight, mCursorPaint);
121             }
122         }
123     }
124 
125     /**
126      * Copy data into internal buffer then repaint.
127      */
setSampleData(float[] samples)128     public void setSampleData(float[] samples) {
129         setSampleData(samples, 0, samples.length);
130     }
131 
132     /**
133      * Sets the sample data to display
134      * @param samples
135      * @param offset
136      * @param count
137      */
setSampleData(float[] samples, int offset, int count)138     public void setSampleData(float[] samples, int offset, int count) {
139         if ((offset + count) > samples.length) {
140             throw new IllegalArgumentException("Exceed array bounds. ("
141                     + offset + " + " + count + ") > " + samples.length);
142         }
143         if (mData == null || count > mData.length) {
144             mData = new float[count];
145         }
146         System.arraycopy(samples, offset, mData, 0, count);
147         mSampleCount = count;
148         mSampleOffset = offset;
149     }
150 
151     /**
152      * Clears the display data
153      */
clearSampleData()154     public void clearSampleData() {
155         mData = null;
156         mSampleCount = 0;
157         mSampleOffset = 0;
158     }
159 
160     /**
161      * Copy cursor positions into internal buffer then repaint.
162      */
setCursorData(int[] cursors)163     public void setCursorData(int[] cursors) {
164         if (cursors == null) {
165             mCursors = null;
166         } else {
167             if (mCursors == null || cursors.length != mCursors.length) {
168                 mCursors = new int[cursors.length];
169             }
170             System.arraycopy(cursors, 0, mCursors, 0, mCursors.length);
171         }
172     }
173 }
174