Update piano view from MIDI keyboard

This patch plumbs note on/off messages from the USB MIDI keyboard to
the piano view, so it's possible to see the notes being played.

Also contains some small UI improvements, like scaling some things to
the device density.
master
Raph Levien 11 years ago
parent 7a5706054e
commit cf96a0b192
  1. 46
      android/src/com/levien/synthesizer/android/service/SynthesizerService.java
  2. 41
      android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
  3. 13
      android/src/com/levien/synthesizer/android/widgets/knob/KnobView.java
  4. 10
      android/src/com/levien/synthesizer/android/widgets/piano/PianoKey.java
  5. 40
      android/src/com/levien/synthesizer/android/widgets/piano/PianoView.java

@ -86,15 +86,6 @@ public class SynthesizerService extends Service {
params.bufferSize = 64;
androidGlue_ = new AndroidGlue();
midiListener_ = new MessageForwarder(androidGlue_) {
@Override
public void onController(int channel, int control, int value) {
super.onController(channel, control, value);
if (onCcListener_!= null) {
onCcListener_.onCcChange(channel, control, value);
}
}
};
androidGlue_.start(params.sampleRate, params.bufferSize);
InputStream patchIs = getResources().openRawResource(R.raw.rom1a);
byte[] patchData = new byte[4104];
@ -109,6 +100,31 @@ public class SynthesizerService extends Service {
Log.e(getClass().getName(), "loading patches failed");
}
}
midiListener_ = new MessageForwarder(androidGlue_) {
@Override
public void onController(int channel, int control, int value) {
super.onController(channel, control, value);
if (onCcListener_!= null) {
onCcListener_.onCcChange(channel, control, value);
}
}
@Override
public void onNoteOn(int channel, int note, int velocity) {
super.onNoteOn(channel, note, velocity);
if (onNoteOnListener_ != null) {
onNoteOnListener_.onNote(channel, note, velocity);
}
}
@Override
public void onNoteOff(int channel, int note, int velocity) {
super.onNoteOff(channel, note, velocity);
if (onNoteOffListener_ != null) {
onNoteOffListener_.onNote(channel, note, velocity);
}
}
};
androidGlue_.setPlayState(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
@ -272,14 +288,24 @@ public class SynthesizerService extends Service {
}
};
// There's a pretty good case to be made this should just tee a MidiListener instead
public interface OnCcListener {
abstract void onCcChange(int channel, int cc, int value);
}
public interface OnNoteListener {
abstract void onNote(int channel, int note, int velocity);
}
public void setOnCcListener(OnCcListener onCcListener) {
onCcListener_ = onCcListener;
}
public void setOnNoteListeners(OnNoteListener onNoteOn, OnNoteListener onNoteOff) {
onNoteOnListener_ = onNoteOn;
onNoteOffListener_ = onNoteOff;
}
private MidiListener midiListener_;
// Binder to use for Activities in this process.
@ -298,4 +324,6 @@ public class SynthesizerService extends Service {
// Plumbing for MIDI events
private OnCcListener onCcListener_;
private OnNoteListener onNoteOnListener_;
private OnNoteListener onNoteOffListener_;
}

@ -72,8 +72,9 @@ public class PianoActivity2 extends Activity {
presetSpinner_.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
synthesizerService_.getMidiListener().onProgramChange(0, position);
sendMidiBytes(new byte[] {(byte)0xc0, (byte)position});
if (synthesizerService_ != null) {
synthesizerService_.getMidiListener().onProgramChange(0, position);
}
}
public void onNothingSelected(AdapterView<?> parent) {
}
@ -81,20 +82,27 @@ public class PianoActivity2 extends Activity {
cutoffKnob_.setKnobListener(new KnobListener() {
public void onKnobChanged(double newValue) {
int value = (int)Math.round(newValue * 127);
synthesizerService_.getMidiListener().onController(0, 1, value);
// Maybe actually bind these at synthesizer service connection time?
if (synthesizerService_ != null) {
int value = (int)Math.round(newValue * 127);
synthesizerService_.getMidiListener().onController(0, 1, value);
}
}
});
resonanceKnob_.setKnobListener(new KnobListener() {
public void onKnobChanged(double newValue) {
int value = (int)Math.round(newValue * 127);
synthesizerService_.getMidiListener().onController(0, 2, value);
if (synthesizerService_ != null) {
int value = (int)Math.round(newValue * 127);
synthesizerService_.getMidiListener().onController(0, 2, value);
}
}
});
overdriveKnob_.setKnobListener(new KnobListener() {
public void onKnobChanged(double newValue) {
int value = (int)Math.round(newValue * 127);
synthesizerService_.getMidiListener().onController(0, 3, value);
if (synthesizerService_ != null) {
int value = (int)Math.round(newValue * 127);
synthesizerService_.getMidiListener().onController(0, 3, value);
}
}
});
@ -247,6 +255,23 @@ public class PianoActivity2 extends Activity {
});
}
});
synthesizerService_.setOnNoteListeners(new SynthesizerService.OnNoteListener() {
public void onNote(final int channel, final int note, final int velocity) {
runOnUiThread(new Runnable() {
public void run() {
piano_.setNoteOn(note, true);
}
});
}
}, new SynthesizerService.OnNoteListener() {
public void onNote(final int channel, final int note, final int velocity) {
runOnUiThread(new Runnable() {
public void run() {
piano_.setNoteOn(note, false);
}
});
}
});
}
public void onServiceDisconnected(ComponentName className) {
synthesizerService_ = null;

@ -21,14 +21,14 @@ import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.graphics.SweepGradient;
import android.graphics.Typeface;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
@ -52,11 +52,14 @@ public class KnobView extends View {
knobValue_ = a.getFloat(R.styleable.KnobView_value, 0.5f);
min_ = a.getFloat(R.styleable.KnobView_min, 0.0f);
max_ = a.getFloat(R.styleable.KnobView_max, 1.0f);
a.recycle();
// Set up the drawing structures.
knobPaint_ = new Paint();
knobPaint_.setAntiAlias(true);
knobPaint_.setColor(Color.WHITE);
float density = getResources().getDisplayMetrics().density;
knobPaint_.setStrokeWidth(2.0f * density);
rect_ = new Rect();
rectF_ = new RectF();
textRect_ = new Rect();
@ -64,7 +67,8 @@ public class KnobView extends View {
// The listener has to be set later.
listener_ = null;
setPadding(3, 3, 3, 3);
int padding = (int)(3.0 * density + 0.5);
setPadding(padding, padding, padding, padding);
}
/**
@ -262,7 +266,6 @@ public class KnobView extends View {
knobPaint_.setShader(null);
knobPaint_.setColor(Color.WHITE);
knobPaint_.setStyle(Style.STROKE);
knobPaint_.setStrokeWidth(4.0f);
final float arcWidth = 15.0f;
canvas.drawArc(rectF_,
(float)(knobValue_ * 360 * 0.8 + 90 - arcWidth / 2 + 36),

@ -1,12 +1,12 @@
/*
* Copyright 2011 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.
@ -44,6 +44,8 @@ public abstract class PianoKey {
fillPaint_.setStyle(Paint.Style.FILL);
strokePaint_.setStyle(Paint.Style.STROKE);
strokePaint_.setColor(Color.BLACK);
float strokeWidth = 1.0f * piano.getResources().getDisplayMetrics().density;
strokePaint_.setStrokeWidth(strokeWidth);
}
/**
@ -148,7 +150,7 @@ public abstract class PianoKey {
// It's +2 to reserve space for the octave-up/down buttons.
return drawingRect.width() / ((WHITE_KEYS.length * octaves) + 2);
}
/**
* Utility function to calculate the height that a standard white key on this keyboard should be.
*/

@ -1,12 +1,12 @@
/*
* Copyright 2010 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.
@ -47,6 +47,7 @@ public class PianoView extends View {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PianoView);
octaves_ = a.getInteger(R.styleable.PianoView_octaves, 1);
firstOctave_ = a.getInteger(R.styleable.PianoView_first_octave, 4);
a.recycle();
// Set up basic drawing structs, just so we don't have to allocate this later when we draw.
drawingRect_ = new Rect();
@ -222,7 +223,7 @@ public class PianoView extends View {
notifyNoteUp(finger);
return redraw;
}
/**
* Handler for all touch events.
@ -319,7 +320,7 @@ public class PianoView extends View {
if (redraw) {
invalidate();
}
return true;
return true;
}
/**
@ -335,7 +336,7 @@ public class PianoView extends View {
int width = 0;
int height = 0;
switch (widthMode) {
case MeasureSpec.EXACTLY:
width = widthSize;
@ -347,7 +348,7 @@ public class PianoView extends View {
width = 10;
break;
}
switch (heightMode) {
case MeasureSpec.EXACTLY:
height = heightSize;
@ -408,6 +409,29 @@ public class PianoView extends View {
});
}
public void setNoteOn(int note, boolean on) {
// This is somewhat painful, hopefully can be done better.
for (int i = 0; i < keys_.length; i++) {
PianoKey key = keys_[i];
if (key instanceof NotePianoKey) {
int midiNote = Note.getKeyforLog12TET(((NotePianoKey) key).getLogFrequency());
if (midiNote == note) {
boolean redraw;
if (on) {
// using the last finger is something of a hack
redraw = key.onTouchDown(FINGERS - 1);
} else {
redraw = key.onTouchUp(FINGERS - 1);
}
if (redraw) {
invalidate();
}
break;
}
}
}
}
// The most recent screen rect that this keyboard was drawn into.
//
// This is basically a stack variable for onDraw. It's a member variable only so that we can
@ -428,7 +452,7 @@ public class PianoView extends View {
// The number of simultaneous fingers supported by this control.
protected static final int FINGERS = 10;
// Whether to use pressure (doesn't work well on all hardware)
private boolean usePressure_ = false;
}

Loading…
Cancel
Save