• 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.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     private int mTextColor = Color.CYAN;
35 
36     private float mDisplayFontSize = 32f;
37 
38     private short[] mPCM16Buffer;
39     private float[] mPCMFloatBuffer;
40 
41     private int mNumChannels = 2;
42     private int mNumFrames = 0;
43 
44     private boolean mDisplayBufferSize = true;
45     private boolean mDisplayMaxMagnitudes = false;
46     private boolean mDisplayPersistentMaxMagnitude = false;
47     private float mPersistentMaxMagnitude;
48 
49     private float[] mPointsBuffer;
50 
51     // Horrible kludge
52     private static int mCachedWidth = 0;
53 
WaveScopeView(Context context, AttributeSet attrs)54     public WaveScopeView(Context context, AttributeSet attrs) {
55         super(context, attrs);
56     }
57 
58     @Override
setBackgroundColor(int color)59     public void setBackgroundColor(int color) { mBackgroundColor = color; }
60 
setTraceColor(int color)61     public void setTraceColor(int color) { mTraceColor = color; }
62 
getDisplayBufferSize()63     public boolean getDisplayBufferSize() {
64         return mDisplayBufferSize;
65     }
66 
setDisplayBufferSize(boolean display)67     public void setDisplayBufferSize(boolean display) {
68         mDisplayBufferSize = display;
69     }
70 
setDisplayMaxMagnitudes(boolean display)71     public void setDisplayMaxMagnitudes(boolean display) {
72         mDisplayMaxMagnitudes = display;
73     }
74 
setDisplayPersistentMaxMagnitude(boolean display)75     public void setDisplayPersistentMaxMagnitude(boolean display) {
76         mDisplayPersistentMaxMagnitude = display;
77     }
78 
79     /**
80      * Clears persistent max magnitude so a new value can be calculated.
81      */
resetPersistentMaxMagnitude()82     public void resetPersistentMaxMagnitude() {
83         mPersistentMaxMagnitude = 0.0f;
84     }
85 
setPCM16Buff(short[] smpl16Buff, int numChans, int numFrames)86     public void setPCM16Buff(short[] smpl16Buff, int numChans, int numFrames) {
87         mPCM16Buffer = smpl16Buff;
88         mPCMFloatBuffer = null;
89 
90         mNumChannels = numChans;
91         mNumFrames = numFrames;
92 
93         setupPointBuffer();
94 
95         invalidate();
96     }
97 
setPCMFloatBuff(float[] smplFloatBuff, int numChans, int numFrames)98     public void setPCMFloatBuff(float[] smplFloatBuff, int numChans, int numFrames) {
99         mPCMFloatBuffer = smplFloatBuff;
100         mPCM16Buffer = null;
101 
102         mNumChannels = numChans;
103         mNumFrames = numFrames;
104 
105         setupPointBuffer();
106 
107         invalidate();
108     }
109 
110     /**
111      * Specifies the number of channels contained in the data buffer to display
112      * @param numChannels
113      */
setNumChannels(int numChannels)114     public void setNumChannels(int numChannels) {
115         mNumChannels = numChannels;
116         setupPointBuffer();
117     }
118 
setupPointBuffer()119     private void setupPointBuffer() {
120         int width = getWidth();
121 
122         // Horrible kludge
123         if (width == 0) {
124             width = mCachedWidth;
125         } else {
126             mCachedWidth = width;
127         }
128 
129         // Canvas.drawLines() uses 2 points (float pairs) per line-segment
130         mPointsBuffer = new float[mNumFrames * 4];
131 
132         float xIncr = (float) width / (float) mNumFrames;
133 
134         float X = 0;
135         int len = mPointsBuffer.length;
136         for (int pntIndex = 0; pntIndex < len;) {
137             mPointsBuffer[pntIndex] = X;
138             pntIndex += 2; // skip Y
139 
140             X += xIncr;
141 
142             mPointsBuffer[pntIndex] = X;
143             pntIndex += 2; // skip Y
144         }
145     }
146 
147     /**
148      * Draws 1 channel of an interleaved block of SMPL16 samples.
149      * @param cvs The Canvas to draw into.
150      * @param samples The (potentially) multi-channel sample block.
151      * @param numFrames The number of FRAMES in the specified sample block.
152      * @param numChans The number of interleaved channels in the specified sample block.
153      * @param chanIndex The (0-based) index of the channel to draw.
154      * @param zeroY The Y-coordinate of sample value 0 (zero).
155      */
drawChannel16(Canvas cvs, short[] samples, int numFrames, int numChans, int chanIndex, float zeroY)156     private void drawChannel16(Canvas cvs, short[] samples, int numFrames, int numChans,
157             int chanIndex, float zeroY) {
158         float yScale = getHeight() / (float) (Short.MAX_VALUE * 2 * numChans);
159         int pntIndex = 1; // of the first Y coordinate
160         float Y = zeroY;
161         int smplIndex = chanIndex;
162         if (numFrames > mNumFrames) {
163             // This shouldn't happen, but there could be a race condition where a callback
164             // with a larger frame count comes around after changing this view in anticipation
165             // of a smaller count
166             numFrames = mNumFrames;
167         }
168         if (mDisplayMaxMagnitudes) {
169             short maxMagnitude = 0;
170             for (int frame = 0; frame < numFrames; frame++) {
171                 mPointsBuffer[pntIndex] = Y;
172                 pntIndex += 2;
173 
174                 short smpl = samples[smplIndex];
175                 if (smpl > maxMagnitude) {
176                     maxMagnitude = smpl;
177                 } else if (-smpl > maxMagnitude) {
178                     maxMagnitude = (short) -smpl;
179                 }
180 
181                 Y = zeroY - (smpl * yScale);
182 
183                 mPointsBuffer[pntIndex] = Y;
184                 pntIndex += 2;
185 
186                 smplIndex += numChans;
187             }
188             mPaint.setColor(mTextColor);
189             mPaint.setTextSize(mDisplayFontSize);
190             cvs.drawText("" + maxMagnitude, 0, zeroY, mPaint);
191 
192             mPaint.setColor(mTraceColor);
193             cvs.drawLines(mPointsBuffer, mPaint);
194         } else {
195             for (int frame = 0; frame < numFrames; frame++) {
196                 mPointsBuffer[pntIndex] = Y;
197                 pntIndex += 2;
198 
199                 Y = zeroY - (samples[smplIndex] * yScale);
200 
201                 mPointsBuffer[pntIndex] = Y;
202                 pntIndex += 2;
203 
204                 smplIndex += numChans;
205             }
206             mPaint.setColor(mTraceColor);
207             cvs.drawLines(mPointsBuffer, mPaint);
208         }
209     }
210 
211     /**
212      * Draws 1 channel of an interleaved block of FLOAT samples.
213      * @param cvs The Canvas to draw into.
214      * @param samples The (potentially) multi-channel sample block.
215      * @param numFrames The number of FRAMES in the specified sample block.
216      * @param numChans The number of interleaved channels in the specified sample block.
217      * @param chanIndex The (0-based) index of the channel to draw.
218      * @param zeroY The Y-coordinate of sample value 0 (zero).
219      */
drawChannelFloat(Canvas cvs, float[] samples, int numFrames, int numChans, int chanIndex, float zeroY)220     private void drawChannelFloat(Canvas cvs, float[] samples, int numFrames, int numChans,
221             int chanIndex, float zeroY) {
222         float yScale = getHeight() / (float) (2 * numChans);
223         int pntIndex = 1; // of the first Y coordinate
224         float Y = zeroY;
225         int smplIndex = chanIndex;
226         if (numFrames > mNumFrames) {
227             // This shouldn't happen, but there could be a race condition where a callback
228             // with a larger frame count comes around after changing this view in anticipation
229             // of a smaller count
230             numFrames = mNumFrames;
231         }
232         if (mDisplayMaxMagnitudes) {
233             float maxMagnitude = 0f;
234             for (int frame = 0; frame < numFrames; frame++) {
235                 mPointsBuffer[pntIndex] = Y;
236                 pntIndex += 2;
237 
238                 float smpl = samples[smplIndex];
239                 if (smpl > maxMagnitude) {
240                     maxMagnitude = smpl;
241                 } else if (-smpl > maxMagnitude) {
242                     maxMagnitude = -smpl;
243                 }
244 
245                 Y = zeroY - (smpl * yScale);
246 
247                 mPointsBuffer[pntIndex] = Y;
248                 pntIndex += 2;
249 
250                 smplIndex += numChans;
251             }
252             mPaint.setColor(mTextColor);
253             mPaint.setTextSize(mDisplayFontSize);
254             cvs.drawText("" + maxMagnitude, 0, zeroY, mPaint);
255 
256             mPaint.setColor(mTraceColor);
257             cvs.drawLines(mPointsBuffer, mPaint);
258         } else {
259             for (int frame = 0; frame < numFrames; frame++) {
260                 mPointsBuffer[pntIndex] = Y;
261                 pntIndex += 2;
262 
263                 Y = zeroY - (samples[smplIndex] * yScale);
264 
265                 mPointsBuffer[pntIndex] = Y;
266                 pntIndex += 2;
267 
268                 smplIndex += numChans;
269             }
270             mPaint.setColor(mTraceColor);
271             cvs.drawLines(mPointsBuffer, mPaint);
272         }
273 
274         if (mDisplayPersistentMaxMagnitude) {
275             smplIndex = chanIndex;
276             for (int frame = 0; frame < numFrames; frame++) {
277                 if (samples[smplIndex] > mPersistentMaxMagnitude) {
278                     mPersistentMaxMagnitude = samples[smplIndex];
279                 } else if (-samples[smplIndex] > mPersistentMaxMagnitude) {
280                     mPersistentMaxMagnitude = -samples[smplIndex];
281                 }
282 
283                 Y = mDisplayFontSize + (chanIndex * (getHeight() / mNumChannels));
284                 mPaint.setColor(mTextColor);
285                 mPaint.setTextSize(mDisplayFontSize);
286                 cvs.drawText("" + mPersistentMaxMagnitude, 0, Y, mPaint);
287             }
288         }
289     }
290 
291     @Override
onDraw(Canvas canvas)292     protected void onDraw(Canvas canvas) {
293         int height = getHeight();
294         mPaint.setColor(mBackgroundColor);
295         canvas.drawRect(0, 0, getWidth(), height, mPaint);
296 
297         if (mDisplayBufferSize) {
298             // Buffer Size
299             mPaint.setColor(mTextColor);
300             mPaint.setTextSize(mDisplayFontSize);
301             canvas.drawText("" + mNumFrames + " frames", 0, height, mPaint);
302         }
303 
304         if (mPCM16Buffer != null) {
305             float yOffset = height / (2.0f * mNumChannels);
306             float yDelta = height / (float) mNumChannels;
307             for(int channel = 0; channel < mNumChannels; channel++) {
308                 drawChannel16(canvas, mPCM16Buffer, mNumFrames, mNumChannels, channel, yOffset);
309                 yOffset += yDelta;
310             }
311         } else if (mPCMFloatBuffer != null) {
312             float yOffset = height / (2.0f * mNumChannels);
313             float yDelta = height / (float) mNumChannels;
314             for(int channel = 0; channel < mNumChannels; channel++) {
315                 drawChannelFloat(canvas, mPCMFloatBuffer, mNumFrames, mNumChannels, channel, yOffset);
316                 yOffset += yDelta;
317             }
318         }
319         // Log.i("WaveView", "onDraw() - done");
320     }
321 }
322