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