Crude touch processing for keyboard view

This patch adds crude touch processing (non multitouch, no tracking),
but it's enough to noodle out melodies to validate the new approach.

The patch also wires up the new view and starts to make some style
changes (holo light theme).
master
Raph Levien 11 years ago
parent ceba5012c8
commit e2f12383ae
  1. 3
      android/AndroidManifest.xml
  2. 11
      android/res/layout/piano2.xml
  3. 6
      android/src/com/levien/synthesizer/android/service/SynthesizerService.java
  4. 16
      android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
  5. 73
      android/src/com/levien/synthesizer/android/widgets/keyboard/KeyboardView.java

@ -10,7 +10,8 @@
<uses-feature android:name="android.hardware.usb.host"
android:required="false"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:theme="@style/LightThemeSelector">
<activity
android:name="com.levien.synthesizer.android.ui.PianoActivity2"
android:label="@string/app_name"

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.levien.synthesizer"
android:id="@+id/TableLayout01"
android:background="#ffffff"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="*"
@ -82,17 +83,13 @@
</LinearLayout>
</TableRow>
<TableRow>
<LinearLayout
android:layout_height="fill_parent"
android:layout_span="6"
android:orientation="vertical">
<com.levien.synthesizer.android.widgets.piano.PianoView
<com.levien.synthesizer.android.widgets.keyboard.KeyboardView
android:id="@+id/piano"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"
android:layout_span="6"
app:octaves="2"
app:first_octave="4" />
</LinearLayout>
</TableRow>
</TableLayout>

@ -264,6 +264,12 @@ public class SynthesizerService extends Service {
}
};
/**
* Set a MidiListener. At the moment, this listener gets all MIDI events, but
* it might change to only get them from the USB MIDI device.
*
* @param target MidiListener to receive messages, or null if none
*/
public void setMidiListener(MidiListener target) {
midiListener_.setSecondTarget(target);
}

@ -42,6 +42,8 @@ import android.widget.Spinner;
import com.levien.synthesizer.R;
import com.levien.synthesizer.android.service.SynthesizerService;
import com.levien.synthesizer.android.widgets.keyboard.KeyboardSpec;
import com.levien.synthesizer.android.widgets.keyboard.KeyboardView;
import com.levien.synthesizer.android.widgets.knob.KnobListener;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
@ -62,7 +64,9 @@ public class PianoActivity2 extends Activity {
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.piano2);
piano_ = (PianoView)findViewById(R.id.piano);
//piano_ = (PianoView)findViewById(R.id.piano);
keyboard_ = (KeyboardView)findViewById(R.id.piano);
keyboard_.setKeyboardSpec(KeyboardSpec.make3Layer());
cutoffKnob_ = (KnobView)findViewById(R.id.cutoffKnob);
resonanceKnob_ = (KnobView)findViewById(R.id.resonanceKnob);
overdriveKnob_ = (KnobView)findViewById(R.id.overdriveKnob);
@ -183,7 +187,8 @@ public class PianoActivity2 extends Activity {
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private void onSynthConnected() {
final MidiListener synthMidi = synthesizerService_.getMidiListener();
piano_.bindTo(synthMidi);
//piano_.bindTo(synthMidi);
keyboard_.setMidiListener(synthMidi);
cutoffKnob_.setKnobListener(new KnobListener() {
public void onKnobChanged(double newValue) {
@ -209,7 +214,7 @@ public class PianoActivity2 extends Activity {
public void onNoteOn(final int channel, final int note, final int velocity) {
runOnUiThread(new Runnable() {
public void run() {
piano_.setNoteOn(note, true);
keyboard_.onNote(note, velocity);
}
});
}
@ -217,7 +222,7 @@ public class PianoActivity2 extends Activity {
public void onNoteOff(final int channel, final int note, final int velocity) {
runOnUiThread(new Runnable() {
public void run() {
piano_.setNoteOn(note, false);
keyboard_.onNote(note, 0);
}
});
}
@ -277,7 +282,8 @@ public class PianoActivity2 extends Activity {
private SynthesizerService synthesizerService_;
private PianoView piano_;
//private PianoView piano_;
private KeyboardView keyboard_;
private KnobView cutoffKnob_;
private KnobView resonanceKnob_;
private KnobView overdriveKnob_;

@ -24,12 +24,15 @@ import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.levien.synthesizer.core.midi.MidiListener;
public class KeyboardView extends View {
public KeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
nKeys_ = 24; // TODO: make configurable
nKeys_ = 36; // TODO: make configurable
firstKey_ = 48;
noteStatus_ = new byte[128];
drawingRect_ = new Rect();
@ -46,6 +49,10 @@ public class KeyboardView extends View {
invalidate();
}
public void setMidiListener(MidiListener listener) {
midiListener_ = listener;
}
public void onNote(int note, int velocity) {
if (note >= 0 && note < 128) {
noteStatus_[note] = (byte)velocity;
@ -63,7 +70,6 @@ public class KeyboardView extends View {
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;
@ -89,6 +95,7 @@ public class KeyboardView extends View {
paint_.setStyle(Style.STROKE);
canvas.drawRect(x, y, x + width + strokeWidth_, y + height + strokeWidth_, paint_);
}
// TODO: draw optional note text
}
}
@ -133,6 +140,68 @@ public class KeyboardView extends View {
setMeasuredDimension(width, height);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
int actionCode = action & MotionEvent.ACTION_MASK;
boolean redraw = false;
if (actionCode == MotionEvent.ACTION_DOWN) {
int pointerId = event.getPointerId(0);
float x = event.getX();
float y = event.getY();
float pressure = event.getPressure();
redraw |= onTouchDown(pointerId, x, y, pressure);
} else if (actionCode == MotionEvent.ACTION_UP) {
int pointerId = event.getPointerId(0);
float x = event.getX();
float y = event.getY();
float pressure = event.getPressure();
redraw |= onTouchUp(pointerId, x, y, pressure);
}
if (redraw) {
invalidate();
}
return true;
}
private int hitTest(float x, float y) {
/* convert x and y to KeyboardSpec space */
float xscale = (drawingRect_.width() - strokeWidth_) * keyboardScale_;
float yscale = (drawingRect_.height() - strokeWidth_) / keyboardSpec_.height;
float xk = (x - 0.5f * strokeWidth_) / xscale;
float yk = (y - 0.5f * strokeWidth_) / yscale;
for (int i = 0; i < nKeys_; i++) {
KeySpec ks = keyboardSpec_.keys[i % keyboardSpec_.keys.length];
float kx0 = ks.rect.left + (i / keyboardSpec_.keys.length) * keyboardSpec_.repeatWidth;
if (xk >= kx0 && xk < kx0 + ks.rect.width() &&
yk >= ks.rect.top && yk < ks.rect.bottom) {
return i + firstKey_;
}
}
return -1;
}
private boolean onTouchDown(int id, float x, float y, float pressure) {
int note = hitTest(x, y);
if (note >= 0) {
if (midiListener_ != null) {
midiListener_.onNoteOn(0, note, 64);
}
}
return false;
}
private boolean onTouchUp(int id, float x, float y, float pressure) {
int note = hitTest(x, y);
if (note >= 0) {
if (midiListener_ != null) {
midiListener_.onNoteOff(0, note, 64);
}
}
return false;
}
private MidiListener midiListener_;
private Rect drawingRect_;
private Paint paint_;
private float strokeWidth_;

Loading…
Cancel
Save