This patch contains a new keyboard view with an adjustable layout. As of this version, it draws and displays notes from the MIDI keyboard but doesn't handle any of its own touch events.master
parent
a30a6b0e75
commit
ceba5012c8
@ -0,0 +1,5 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<style name="LightThemeSelector" parent="android:Theme.Holo.Light"> |
||||
</style> |
||||
</resources> |
@ -0,0 +1,5 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<resources> |
||||
<style name="LightThemeSelector" parent="android:Theme.Light"> |
||||
</style> |
||||
</resources> |
@ -0,0 +1,28 @@ |
||||
/* |
||||
* Copyright 2013 Google Inc. |
||||
* |
||||
* 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 com.levien.synthesizer.android.widgets.keyboard; |
||||
|
||||
import android.graphics.RectF; |
||||
|
||||
/** |
||||
* A single key in a keyboard spec. |
||||
*/ |
||||
public class KeySpec { |
||||
public RectF rect; |
||||
// we may add an optional draw poly later
|
||||
public int color; |
||||
} |
@ -0,0 +1,89 @@ |
||||
/* |
||||
* Copyright 2013 Google Inc. |
||||
* |
||||
* 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 com.levien.synthesizer.android.widgets.keyboard; |
||||
|
||||
import android.graphics.Color; |
||||
import android.graphics.RectF; |
||||
|
||||
public class KeyboardSpec { |
||||
public KeyboardSpec(int nkeys, float repeatWidth, float height) { |
||||
keys = new KeySpec[nkeys]; |
||||
this.repeatWidth = repeatWidth; |
||||
this.height = height; |
||||
} |
||||
|
||||
public void addKey(KeySpec key) { |
||||
keys[ix_++] = key; |
||||
this.maxX = Math.max(this.maxX, key.rect.right); |
||||
} |
||||
|
||||
public void addKey(float x0, float y0, float w, float h, int color) { |
||||
KeySpec ks = new KeySpec(); |
||||
ks.rect = new RectF(x0, y0, x0 + w, y0 + h); |
||||
ks.color = color; |
||||
addKey(ks); |
||||
} |
||||
|
||||
public static KeyboardSpec make2Layer() { |
||||
KeyboardSpec ks = new KeyboardSpec(12, 84, 24); |
||||
final int w = Color.WHITE; |
||||
final int b = Color.BLACK; |
||||
ks.addKey(0, 12, 12, 12, w); // C
|
||||
ks.addKey(6, 0, 12, 12, b); // C#
|
||||
ks.addKey(12, 12, 12, 12, w); // D
|
||||
ks.addKey(18, 0, 12, 12, b); // D#
|
||||
ks.addKey(24, 12, 12, 12, w); // E
|
||||
ks.addKey(36, 12, 12, 12, w); // F
|
||||
ks.addKey(42, 0, 12, 12, b); // F#
|
||||
ks.addKey(48, 12, 12, 12, w); // G
|
||||
ks.addKey(54, 0, 12, 12, b); // G#
|
||||
ks.addKey(60, 12, 12, 12, w); // A
|
||||
ks.addKey(66, 0, 12, 12, b); // A#
|
||||
ks.addKey(72, 12, 12, 12, w); // B
|
||||
return ks; |
||||
} |
||||
|
||||
public static KeyboardSpec make3Layer() { |
||||
KeyboardSpec ks = new KeyboardSpec(24, 84, 32); |
||||
final int w = Color.WHITE; |
||||
final int b = Color.BLACK; |
||||
for (int oct = 0; oct < 2; oct++) { |
||||
float x0 = 42 * oct; |
||||
float y1 = 20 - 12 * oct; |
||||
float y2 = 28 - y1; |
||||
ks.addKey(x0 + 0, y1, 12, 12, w); // C
|
||||
ks.addKey(x0 + 4, 0, 8, 8, b); // C#
|
||||
ks.addKey(x0 + 6, y2, 12, 12, w); // D
|
||||
ks.addKey(x0 + 12, 0, 8, 8, b); // D#
|
||||
ks.addKey(x0 + 12, y1, 12, 12, w); // E
|
||||
ks.addKey(x0 + 18, y2, 12, 12, w); // F
|
||||
ks.addKey(x0 + 21, 0, 8, 8, b); // F#
|
||||
ks.addKey(x0 + 24, y1, 12, 12, w); // G
|
||||
ks.addKey(x0 + 29, 0, 8, 8, b); // G#
|
||||
ks.addKey(x0 + 30, y2, 12, 12, w); // A
|
||||
ks.addKey(x0 + 37, 0, 8, 8, b); // A#
|
||||
ks.addKey(x0 + 36, y1, 12, 12, w); // B
|
||||
} |
||||
return ks; |
||||
} |
||||
|
||||
public KeySpec keys[]; |
||||
public float repeatWidth; |
||||
public float height; |
||||
public float maxX; |
||||
private int ix_; |
||||
} |
@ -0,0 +1,145 @@ |
||||
/* |
||||
* Copyright 2013 Google Inc. |
||||
* |
||||
* 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 com.levien.synthesizer.android.widgets.keyboard; |
||||
|
||||
import android.content.Context; |
||||
import android.graphics.Canvas; |
||||
import android.graphics.Color; |
||||
import android.graphics.Paint; |
||||
import android.graphics.Paint.Style; |
||||
import android.graphics.Rect; |
||||
import android.util.AttributeSet; |
||||
import android.util.Log; |
||||
import android.view.View; |
||||
|
||||
public class KeyboardView extends View { |
||||
public KeyboardView(Context context, AttributeSet attrs) { |
||||
super(context, attrs); |
||||
nKeys_ = 24; // TODO: make configurable
|
||||
firstKey_ = 48; |
||||
noteStatus_ = new byte[128]; |
||||
drawingRect_ = new Rect(); |
||||
paint_ = new Paint(); |
||||
paint_.setAntiAlias(true); |
||||
float density = getResources().getDisplayMetrics().density; |
||||
strokeWidth_ = 1.0f * density; |
||||
paint_.setStrokeWidth(strokeWidth_); |
||||
} |
||||
|
||||
public void setKeyboardSpec(KeyboardSpec keyboardSpec) { |
||||
keyboardSpec_ = keyboardSpec; |
||||
keyboardScale_ = 1.0f / keyboardSpec_.repeatWidth * keyboardSpec_.keys.length / nKeys_; |
||||
invalidate(); |
||||
} |
||||
|
||||
public void onNote(int note, int velocity) { |
||||
if (note >= 0 && note < 128) { |
||||
noteStatus_[note] = (byte)velocity; |
||||
invalidate(); // could do smarter invalidation, whatev
|
||||
} |
||||
} |
||||
|
||||
@Override |
||||
protected void onDraw(Canvas canvas) { |
||||
super.onDraw(canvas); |
||||
getDrawingRect(drawingRect_); |
||||
float xscale = (drawingRect_.width() - strokeWidth_) * keyboardScale_; |
||||
float yscale = (drawingRect_.height() - strokeWidth_) / keyboardSpec_.height; |
||||
float x0 = drawingRect_.left + strokeWidth_ * 0.5f; |
||||
float y0 = drawingRect_.top + strokeWidth_ * 0.5f; |
||||
for (int i = 0; i < nKeys_; i++) { |
||||
KeySpec ks = keyboardSpec_.keys[i % keyboardSpec_.keys.length]; |
||||
Log.d("synth", "ks["+i+"] = " + ks); |
||||
float x = x0 + ((i / keyboardSpec_.keys.length) * keyboardSpec_.repeatWidth + |
||||
ks.rect.left) * xscale; |
||||
float y = y0 + ks.rect.top * yscale; |
||||
float width = ks.rect.width() * xscale - strokeWidth_; |
||||
float height = ks.rect.height() * yscale - strokeWidth_; |
||||
int vel = noteStatus_[i + firstKey_]; |
||||
if (vel == 0) { |
||||
paint_.setColor(ks.color); |
||||
} else { |
||||
// green->yellow->red gradient, dependent on velocity
|
||||
int color; |
||||
if (vel < 64) { |
||||
color = 0xff00ff00 + (vel << 18); |
||||
} else { |
||||
color = 0xffffff00 - ((vel - 64) << 10); |
||||
} |
||||
paint_.setColor(color); |
||||
} |
||||
paint_.setStyle(Style.FILL); |
||||
canvas.drawRect(x, y, x + width, y + height, paint_); |
||||
if (ks.color != Color.BLACK) { |
||||
paint_.setColor(Color.BLACK); |
||||
paint_.setStyle(Style.STROKE); |
||||
canvas.drawRect(x, y, x + width + strokeWidth_, y + height + strokeWidth_, paint_); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Layout measurement for this widget. |
||||
* This method just sets a basic minimum size and makes the widget maximized otherwise. |
||||
*/ |
||||
@Override |
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec); |
||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec); |
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec); |
||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec); |
||||
|
||||
int width = 0; |
||||
int height = 0; |
||||
|
||||
switch (widthMode) { |
||||
case MeasureSpec.EXACTLY: |
||||
width = widthSize; |
||||
break; |
||||
case MeasureSpec.AT_MOST: |
||||
width = widthSize; |
||||
break; |
||||
case MeasureSpec.UNSPECIFIED: |
||||
width = 10; |
||||
break; |
||||
} |
||||
|
||||
switch (heightMode) { |
||||
case MeasureSpec.EXACTLY: |
||||
height = heightSize; |
||||
break; |
||||
case MeasureSpec.AT_MOST: |
||||
height = heightSize; |
||||
break; |
||||
case MeasureSpec.UNSPECIFIED: |
||||
height = 10; |
||||
break; |
||||
} |
||||
|
||||
setMeasuredDimension(width, height); |
||||
} |
||||
|
||||
private Rect drawingRect_; |
||||
private Paint paint_; |
||||
private float strokeWidth_; |
||||
private float keyboardScale_; |
||||
|
||||
private KeyboardSpec keyboardSpec_; |
||||
private int nKeys_; |
||||
private int firstKey_; |
||||
private byte[] noteStatus_; |
||||
} |
Loading…
Reference in new issue