/*
* Copyright (C) 2015 tyorikan
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package main.java.com.tyorikan.voicerecordingvisualizer;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.widget.FrameLayout;
import android.view.View;
import android.view.Gravity;
import main.java.com.tyorikan.voicerecordingvisualizer.R;
/**
* A class that draws visualizations of data received from {@link RecordingSampler}
*
* Created by tyorikan on 2015/06/08.
*/
public class VisualizerView extends View {
private static final int DEFAULT_NUM_COLUMNS = 20;
private static final int RENDAR_RANGE_TOP = 0;
private static final int RENDAR_RANGE_BOTTOM = 1;
private static final int RENDAR_RANGE_TOP_BOTTOM = 2;
private int mNumColumns = 20;
private int mRenderColor = Color.WHITE;
private int mType = 2; //can be 1, 2, 4
private int mRenderRange = RENDAR_RANGE_TOP_BOTTOM;
private int mBaseY = 0;
private Canvas mCanvas;
private Bitmap mCanvasBitmap;
private Rect mRect = new Rect();
private Paint mPaint = new Paint();
private Paint mFadePaint = new Paint();
private float mColumnWidth;
private float mSpace;
private int volume;
public VisualizerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
mPaint.setColor(mRenderColor);
mFadePaint.setColor(Color.argb(138, 255, 255, 255));
}
private void init(Context context, AttributeSet attrs) {
// TypedArray args = context.obtainStyledAttributes(attrs, R.styleable.visualizerView);
// mNumColumns = args.getInteger(R.styleable.visualizerView_numColumns, DEFAULT_NUM_COLUMNS);
// mRenderColor = args.getColor(R.styleable.visualizerView_renderColor, Color.WHITE);
// mType = args.getInt(R.styleable.visualizerView_renderType, Type.BAR.getFlag());
// mRenderRange = args.getInteger(R.styleable.visualizerView_renderRange, RENDAR_RANGE_TOP);
// args.recycle();
}
/**
* @param baseY center Y position of visualizer
*/
public void setBaseY(int baseY) {
mBaseY = baseY;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Create canvas once we're ready to draw
mRect.set(0, 0, getWidth(), getHeight());
if (mCanvasBitmap == null) {
mCanvasBitmap = Bitmap.createBitmap(
canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
}
if (mCanvas == null) {
mCanvas = new Canvas(mCanvasBitmap);
}
if (mNumColumns > getWidth()) {
mNumColumns = DEFAULT_NUM_COLUMNS;
}
mColumnWidth = (float) getWidth() / (float) mNumColumns;
mSpace = mColumnWidth / 8f;
if (mBaseY == 0) {
mBaseY = getHeight() / 2;
}
canvas.drawBitmap(mCanvasBitmap, new Matrix(), null);
}
/**
* receive volume from {@link RecordingSampler}
*
* @param volume volume from mic input
*/
protected void receive(final int volume) {
this.volume = volume;
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
if (mCanvas == null) {
return;
}
if (volume == 0) {
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
} else if ((mType & Type.FADE.getFlag()) != 0) {
// Fade out old contents
mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
mCanvas.drawPaint(mFadePaint);
} else {
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
}
if ((mType & Type.BAR.getFlag()) != 0) {
drawBar(volume);
}
if ((mType & Type.PIXEL.getFlag()) != 0) {
drawPixel(volume);
}
invalidate();
}
});
}
private void drawBar(int volume) {
this.volume = volume;
for (int i = 0; i < mNumColumns; i++) {
float height = getRandomHeight(volume);
float left = i * mColumnWidth + mSpace;
float right = (i + 1) * mColumnWidth - mSpace;
RectF rect = createRectF(left, right, height);
mCanvas.drawRect(rect, mPaint);
}
}
private void drawPixel(int volume) {
this.volume = volume;
for (int i = 0; i < mNumColumns; i++) {
float height = getRandomHeight(volume);
float left = i * mColumnWidth + mSpace;
float right = (i + 1) * mColumnWidth - mSpace;
int drawCount = (int) (height / (right - left));
if (drawCount == 0) {
drawCount = 1;
}
float drawHeight = height / drawCount;
// draw each pixel
for (int j = 0; j < drawCount; j++) {
float top, bottom;
RectF rect;
switch (mRenderRange) {
case RENDAR_RANGE_TOP:
bottom = mBaseY - (drawHeight * j);
top = bottom - drawHeight + mSpace;
rect = new RectF(left, top, right, bottom);
break;
case RENDAR_RANGE_BOTTOM:
top = mBaseY + (drawHeight * j);
bottom = top + drawHeight - mSpace;
rect = new RectF(left, top, right, bottom);
break;
case RENDAR_RANGE_TOP_BOTTOM:
bottom = mBaseY - (height / 2) + (drawHeight * j);
top = bottom - drawHeight + mSpace;
rect = new RectF(left, top, right, bottom);
break;
default:
return;
}
mCanvas.drawRect(rect, mPaint);
}
}
}
private float getRandomHeight(int volume) {
this.volume = volume;
double randomVolume = Math.random() * volume + 1;
float height = getHeight();
switch (mRenderRange) {
case RENDAR_RANGE_TOP:
height = mBaseY;
break;
case RENDAR_RANGE_BOTTOM:
height = (getHeight() - mBaseY);
break;
case RENDAR_RANGE_TOP_BOTTOM:
height = getHeight();
break;
}
return (height / 60f) * (float) randomVolume;
}
private RectF createRectF(float left, float right, float height) {
switch (mRenderRange) {
case RENDAR_RANGE_TOP:
return new RectF(left, mBaseY - height, right, mBaseY);
case RENDAR_RANGE_BOTTOM:
return new RectF(left, mBaseY, right, mBaseY + height);
case RENDAR_RANGE_TOP_BOTTOM:
return new RectF(left, mBaseY - height, right, mBaseY + height);
default:
return new RectF(left, mBaseY - height, right, mBaseY);
}
}
/**
* visualizer type
*/
public enum Type {
BAR(0x1), PIXEL(0x2), FADE(0x4);
private int mFlag;
Type(int flag) {
mFlag = flag;
}
public int getFlag() {
return mFlag;
}
}
public void setNumberOfColumns(int noc){
this.mNumColumns = noc;
this.invalidate();
}
public void setRenderColor(int rc){
mPaint.setColor(rc);
}
public void setRenderType(int rt){ //can be 1, 2, 4
this.mType = rt;
this.invalidate();
}
public void setRenderRange(int rr){
this.mRenderRange = rr;
this.invalidate();
}
public int getVolume()
{
return this.volume;
}
}