EQ 頻響曲線

import android.annotation.SuppressLint;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.PointF;

import android.util.AttributeSet;

import android.util.Log;

import android.view.View;


import java.util.ArrayList;

import java.util.List;



/**

?*?

?* 畫EQ(均衡器)頻響曲線

?*/

public class GainView extends View {

? ? private static final String TAG = "GainView";


? ? private int[] mGains;

? ? private Paint mPain;


? ? public GainView(Context context) {

? ? ? ? this(context, null);

? ? }


? ? public GainView(Context context, AttributeSet attrs) {

? ? ? ? this(context, attrs, 0);

? ? }


? ? public GainView(Context context, AttributeSet attrs, int defStyle) {

? ? ? ? super(context, attrs, defStyle);

? ? ? ? //8個點(diǎn),對應(yīng)8個頻段

? ? ? ? mGains = new int[]{0, 0, 0, 0, 0, 0, 0, 0};

? ? ? ? mPain = new Paint();

? ? }


? ? //更新增益曲線

? ? public void updateGain(int index, int gain) {

? ? ? ? mGains[index] = gain;

? ? ? ? postInvalidate();

? ? }


? ? //橫縱坐標(biāo)

? ? private int[] getXIndex() {

? ? ? ? int[] xs = new int[8];

? ? ? ? int realWidth = getWidth() - getPaddingLeft() - getPaddingRight();

? ? ? ? int step = realWidth / (xs.length - 1);

? ? ? ? for (int i = 0; i < xs.length; i++) {

? ? ? ? ? ? xs[i] = step * i;

? ? ? ? }

? ? ? ? return xs;

? ? }


? ? @SuppressLint("DrawAllocation")

? ? @Override

? ? protected void onDraw(Canvas canvas) {

? ? ? ? super.onDraw(canvas);

? ? ? ? int[] gains = new int[mGains.length];

? ? ? ? for (int i = 0; i < mGains.length; i++) {

? ? ? ? ? ? gains[i] = -mGains[i];

? ? ? ? }


? ? ? ? int[] xs = getXIndex();

? ? ? ? int[] ys = new int[xs.length];

? ? ? ? int half_height = (getHeight() - getPaddingTop() - getPaddingBottom()) / 2;

? ? ? ? for (int i = 0; i < ys.length; i++) {

? ? ? ? ? ? ys[i] = half_height * gains[i] / 12 + half_height;

? ? ? ? }


? ? ? ? double min = Double.MAX_VALUE;

? ? ? ? double max = Double.MIN_VALUE;

? ? ? ? for (int x : xs) {

? ? ? ? ? ? if (x > max)

? ? ? ? ? ? ? ? max = x;

? ? ? ? ? ? if (x < min)

? ? ? ? ? ? ? ? min = x;

? ? ? ? }


? ? ? ? int sc = canvas.save();

? ? ? ? int left = getPaddingLeft();

? ? ? ? int top = getPaddingTop();

? ? ? ? canvas.translate(left, top);

? ? ? ? Paint p = mPain;

? ? ? ? p.setAntiAlias(true);

? ? ? ? p.setStyle(Paint.Style.STROKE);

? ? ? ? p.setStrokeWidth(2);

? ? ? ? p.setColor(Color.parseColor("#959595"));

? ? ? ? //畫中間線

? ? ? ? canvas.drawLine(0, half_height, getWidth() - getPaddingRight() - getPaddingLeft(), half_height, p);



? ? ? ? PointF[] points = new PointF[xs.length];

? ? ? ? for (int i = 0; i < points.length; i++) {

? ? ? ? ? ? points[i] = new PointF();

? ? ? ? ? ? points[i].x = xs[i];

? ? ? ? ? ? points[i].y = ys[i];

? ? ? ? }


? ? ? ? List<PointF> pointFList = getControlPoints(points);


? ? ? ? p.setStrokeWidth(3);

? ? ? ? p.setColor(Color.BLACK);

? ? ? ? Path path = new Path();

? ? ? ? createCurve2(pointFList, points, path);

? ? ? ? //createCurve(points, path);

? ? ? ? canvas.drawPath(path, p);


//? ? ? ? p.setStyle(Paint.Style.FILL);

//? ? ? ? p.setStrokeWidth(1);

//? ? ? ? p.setColor(Color.LTGRAY);

//? ? ? ? p.setTextSize(8 * getContext().getResources().getDisplayMetrics().scaledDensity);

//? ? ? ? p.setFakeBoldText(true);

//? ? ? ? float density = getContext().getResources().getDisplayMetrics().density;

//? ? ? ? canvas.drawText("+12db", 5, 10 * density, p);

//? ? ? ? canvas.drawText("-12db", 5, getHeight() - getPaddingBottom() - 3 * density, p);

? ? ? ? canvas.restoreToCount(sc);

? ? }


? ? void createCurve(PointF[] originPoint, Path path) {

? ? ? ? int originCount = originPoint.length;

? ? ? ? float scale = 0.6f;

? ? ? ? PointF[] midPoints = new PointF[originCount];

? ? ? ? // 生成中點(diǎn)

? ? ? ? for (int i = 0; i < originCount; i++) {

? ? ? ? ? ? int next = (i + 1) % originCount;

? ? ? ? ? ? midPoints[i] = new PointF();

? ? ? ? ? ? if (i == originCount - 1) {

? ? ? ? ? ? ? ? //midPoints[i].x = getWidth() - getPaddingEnd();

? ? ? ? ? ? ? ? midPoints[i].x = originPoint[i].x;

? ? ? ? ? ? ? ? midPoints[i].y = (originPoint[i].y + (getHeight() - getPaddingTop() - getPaddingBottom()) / 2.0f) / 2.0f;

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? midPoints[i].x = (originPoint[i].x + originPoint[next].x) / 2.0f;

? ? ? ? ? ? ? ? midPoints[i].y = (originPoint[i].y + originPoint[next].y) / 2.0f;

? ? ? ? ? ? }

//? ? ? ? ? ? Log.d(TAG, " originPoint[" + i + "]= [" + originPoint[i].x + ", " + originPoint[i].y + "]");

//? ? ? ? ? ? Log.d(TAG, " midPoints[" + i + "]= [" + midPoints[i].x + ", " + midPoints[i].y + "]");

? ? ? ? }


? ? ? ? // 平移中點(diǎn)

? ? ? ? PointF[] extraPoints = new PointF[2 * originCount];

? ? ? ? for (int i = 0; i < originCount; i++) {

? ? ? ? ? ? int back = (i + originCount - 1) % originCount;

? ? ? ? ? ? if (i == 0)

? ? ? ? ? ? ? ? back = 0;

? ? ? ? ? ? PointF midInMid = new PointF();

? ? ? ? ? ? midInMid.x = (midPoints[i].x + midPoints[back].x) / 2.0f;

? ? ? ? ? ? midInMid.y = (midPoints[i].y + midPoints[back].y) / 2.0f;

? ? ? ? ? ? float offsetX = originPoint[i].x - midInMid.x;

? ? ? ? ? ? float offsetY = originPoint[i].y - midInMid.y;

? ? ? ? ? ? int extraIndex = 2 * i;

? ? ? ? ? ? extraPoints[extraIndex] = new PointF();

? ? ? ? ? ? extraPoints[extraIndex].x = midPoints[back].x + offsetX;

? ? ? ? ? ? extraPoints[extraIndex].y = midPoints[back].y + offsetY;

? ? ? ? ? ? // 朝 originPoint[i]方向收縮

? ? ? ? ? ? float addx = (extraPoints[extraIndex].x - originPoint[i].x) * scale;

? ? ? ? ? ? float addy = (extraPoints[extraIndex].y - originPoint[i].y) * scale;

? ? ? ? ? ? extraPoints[extraIndex].x = originPoint[i].x + addx;

? ? ? ? ? ? extraPoints[extraIndex].y = originPoint[i].y + addy;

? ? ? ? ? ? //Log.d(TAG, " extraPoints[" + extraIndex + "]= [" + extraPoints[extraIndex].x + ", " + extraPoints[extraIndex].y + "]");


? ? ? ? ? ? int extraNext = (extraIndex + 1) % (2 * originCount);

? ? ? ? ? ? extraPoints[extraNext] = new PointF();

? ? ? ? ? ? extraPoints[extraNext].x = midPoints[i].x + offsetX;

? ? ? ? ? ? extraPoints[extraNext].y = midPoints[i].y + offsetY;

? ? ? ? ? ? // 朝 originPoint[i]方向收縮

? ? ? ? ? ? addx = (extraPoints[extraNext].x - originPoint[i].x) * scale;

? ? ? ? ? ? addy = (extraPoints[extraNext].y - originPoint[i].y) * scale;

? ? ? ? ? ? extraPoints[extraNext].x = originPoint[i].x + addx;

? ? ? ? ? ? extraPoints[extraNext].y = originPoint[i].y + addy;

? ? ? ? ? ? //Log.d(TAG, " extraPoints[" + extraNext + "]= [" + extraPoints[extraNext].x + ", " + extraPoints[extraNext].y + "]");

? ? ? ? }


? ? ? ? //控制點(diǎn),即所在點(diǎn)位置

? ? ? ? PointF[] controlPoint = new PointF[4];

? ? ? ? // 生成4控制點(diǎn)蹋宦,產(chǎn)生貝塞爾曲線

? ? ? ? boolean first = true;

? ? ? ? for (int i = 0; i < originCount; i++) {

? ? ? ? ? ? controlPoint[0] = originPoint[i];

? ? ? ? ? ? int extraIndex = 2 * i;

? ? ? ? ? ? controlPoint[1] = extraPoints[extraIndex + 1];

? ? ? ? ? ? if (controlPoint[1].x < originPoint[i].x)

? ? ? ? ? ? ? ? controlPoint[1] = originPoint[i];

? ? ? ? ? ? //Log.d(TAG, " controlPoint[0]= " + controlPoint[0] + ", controlPoint[1]= " + controlPoint[1]);


? ? ? ? ? ? int extraNext = (extraIndex + 2) % (2 * originCount);

? ? ? ? ? ? controlPoint[2] = extraPoints[extraNext];

? ? ? ? ? ? if (controlPoint[2].x < originPoint[i].x)

? ? ? ? ? ? ? ? controlPoint[2] = originPoint[i];


? ? ? ? ? ? int next = (i + 1) % originCount;

? ? ? ? ? ? controlPoint[3] = originPoint[next];

? ? ? ? ? ? if (controlPoint[3].x < originPoint[i].x)

? ? ? ? ? ? ? ? controlPoint[3] = originPoint[i];

? ? ? ? ? ? //Log.d(TAG, " controlPoint[2]= " + controlPoint[2] + ", controlPoint[3]= " + controlPoint[3]);

? ? ? ? ? ? float u = 1;

? ? ? ? ? ? while (u >= 0) {

? ? ? ? ? ? ? ? float px = bezier3funcX(u, controlPoint);

? ? ? ? ? ? ? ? float py = bezier3funcY(u, controlPoint);

? ? ? ? ? ? ? ? // u的步長決定曲線的疏密

? ? ? ? ? ? ? ? u -= 0.005;

? ? ? ? ? ? ? ? //Log.d(TAG, "u= " + u + ", px= " + px + ", py= " + px );

? ? ? ? ? ? ? ? if (first) {

? ? ? ? ? ? ? ? ? ? //畫筆移動

? ? ? ? ? ? ? ? ? ? path.moveTo(px, py);

? ? ? ? ? ? ? ? ? ? //畫筆標(biāo)記清除

? ? ? ? ? ? ? ? ? ? first = false;

? ? ? ? ? ? ? ? } else

? ? ? ? ? ? ? ? ? ? //畫筆劃線

? ? ? ? ? ? ? ? ? ? path.lineTo(px, py);

? ? ? ? ? ? }

? ? ? ? }

? ? }


? ? /**

? ? ?* 計(jì)算控制點(diǎn)

? ? ?* 斜率 y = kx + b => k = (y - b)/x

? ? ?*

? ? ?* @param points 原始點(diǎn)

? ? ?* @return 控制點(diǎn)

? ? ?*/

? ? private List<PointF> getControlPoints(PointF[] points) {

? ? ? ? List<PointF> pointFList = new ArrayList<>();

? ? ? ? float rate = 0.3f;

? ? ? ? //從第一個控制點(diǎn)開始計(jì)算

? ? ? ? float cx1 = points[0].x + (points[1].x - points[0].x) * rate;

? ? ? ? float cy1 = points[0].y;

? ? ? ? pointFList.add(new PointF(cx1, cy1));


? ? ? ? for (int i = 1; i < points.length - 1; i++) {

? ? ? ? ? ? //下一個點(diǎn)

? ? ? ? ? ? float k = (points[i + 1].y - points[i - 1].y) / (points[i + 1].x - points[i - 1].x);

? ? ? ? ? ? float b = points[i].y - k * points[i].x;

? ? ? ? ? ? //左邊控制點(diǎn)

? ? ? ? ? ? float cxLeft = points[i].x - (points[i].x - points[i - 1].x) * rate;

? ? ? ? ? ? float cyLeft = k * cxLeft + b;

? ? ? ? ? ? pointFList.add(new PointF(cxLeft, cyLeft));

? ? ? ? ? ? //右邊控制點(diǎn)

? ? ? ? ? ? float cxRight = points[i].x + (points[i + 1].x - points[i].x) * rate;

? ? ? ? ? ? float cyRight = k * cxRight + b;

? ? ? ? ? ? pointFList.add(new PointF(cxRight, cyRight));

? ? ? ? }

? ? ? ? //最后一個點(diǎn)

? ? ? ? float cxLast = points[points.length - 1].x - (points[points.length - 1].x - points[points.length - 2].x) * rate;

? ? ? ? float cyLast = points[points.length - 1].y;

? ? ? ? pointFList.add(new PointF(cxLast, cyLast));

? ? ? ? return pointFList;

? ? }


? ? void createCurve2(List<PointF> pointFList, PointF[] originPoint, Path path) {

? ? ? ? for (int i = 0; i < originPoint.length - 1; i++) {

? ? ? ? ? ? path.moveTo(originPoint[i].x, originPoint[i].y);

? ? ? ? ? ? path.cubicTo(

? ? ? ? ? ? ? ? ? ? pointFList.get(i * 2).x, pointFList.get(i * 2).y,

? ? ? ? ? ? ? ? ? ? pointFList.get(i * 2 + 1).x, pointFList.get(i * 2 + 1).y,

? ? ? ? ? ? ? ? ? ? originPoint[i + 1].x, originPoint[i + 1].y

? ? ? ? ? ? );

? ? ? ? }

? ? }


? ? /**

? ? ?* 三次貝塞爾曲線

? ? ?* B(t) = P0 * (1-t)^3 + 3 * P1 * t(1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 *t^3

? ? ?*

? ? ?* @param uu? ? ? ?屬于[0, 1]

? ? ?* @param controlP 控制點(diǎn)

? ? ?* @return X 坐標(biāo)值

? ? ?*/

? ? float bezier3funcX(float uu, PointF[] controlP) {

? ? ? ? float part0 = controlP[0].x * uu * uu * uu;

? ? ? ? float part1 = 3 * controlP[1].x * uu * uu * (1 - uu);

? ? ? ? float part2 = 3 * controlP[2].x * uu * (1 - uu) * (1 - uu);

? ? ? ? float part3 = controlP[3].x * (1 - uu) * (1 - uu) * (1 - uu);

? ? ? ? return part0 + part1 + part2 + part3;

? ? }


? ? float bezier3funcY(float uu, PointF[] controlP) {

? ? ? ? float part0 = controlP[0].y * uu * uu * uu;

? ? ? ? float part1 = 3 * controlP[1].y * uu * uu * (1 - uu);

? ? ? ? float part2 = 3 * controlP[2].y * uu * (1 - uu) * (1 - uu);

? ? ? ? float part3 = controlP[3].y * (1 - uu) * (1 - uu) * (1 - uu);

? ? ? ? return part0 + part1 + part2 + part3;

? ? }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市碧绞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萍悴,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寓免,死亡現(xiàn)場離奇詭異癣诱,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)袜香,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門撕予,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蜈首,你說我怎么就攤上這事实抡。” “怎么了欢策?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵吆寨,是天一觀的道長。 經(jīng)常有香客問我踩寇,道長啄清,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任俺孙,我火速辦了婚禮辣卒,結(jié)果婚禮上掷贾,老公的妹妹穿的比我還像新娘。我一直安慰自己荣茫,他們只是感情好想帅,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著啡莉,像睡著了一般港准。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上票罐,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天叉趣,我揣著相機(jī)與錄音,去河邊找鬼该押。 笑死疗杉,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蚕礼。 我是一名探鬼主播烟具,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼奠蹬!你這毒婦竟也來了朝聋?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤囤躁,失蹤者是張志新(化名)和其女友劉穎冀痕,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狸演,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡言蛇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了宵距。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腊尚。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖满哪,靈堂內(nèi)的尸體忽然破棺而出婿斥,到底是詐尸還是另有隱情,我是刑警寧澤哨鸭,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布民宿,位于F島的核電站,受9級特大地震影響像鸡,放射性物質(zhì)發(fā)生泄漏勘高。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望华望。 院中可真熱鬧蕊蝗,春花似錦、人聲如沸赖舟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宾抓。三九已至子漩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間石洗,已是汗流浹背幢泼。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留讲衫,地道東北人缕棵。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像涉兽,于是被迫代替她去往敵國和親招驴。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

推薦閱讀更多精彩內(nèi)容