Update controller views from MIDI keyboard

This patch adds plumbing so that changing the controller values from
a USB MIDI keyboard updates the knob views as well.
master
Raph Levien 11 years ago
parent b22117bb52
commit 7a5706054e
  1. 27
      android/src/com/levien/synthesizer/android/service/SynthesizerService.java
  2. 22
      android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
  3. 59
      android/src/com/levien/synthesizer/android/widgets/knob/KnobView.java

@ -41,6 +41,7 @@ import android.util.Log;
import com.levien.synthesizer.R; import com.levien.synthesizer.R;
import com.levien.synthesizer.android.AndroidGlue; import com.levien.synthesizer.android.AndroidGlue;
import com.levien.synthesizer.android.usb.UsbMidiDevice; import com.levien.synthesizer.android.usb.UsbMidiDevice;
import com.levien.synthesizer.core.midi.MessageForwarder;
import com.levien.synthesizer.core.midi.MidiListener; import com.levien.synthesizer.core.midi.MidiListener;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer; import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
@ -85,6 +86,15 @@ public class SynthesizerService extends Service {
params.bufferSize = 64; params.bufferSize = 64;
androidGlue_ = new AndroidGlue(); 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); androidGlue_.start(params.sampleRate, params.bufferSize);
InputStream patchIs = getResources().openRawResource(R.raw.rom1a); InputStream patchIs = getResources().openRawResource(R.raw.rom1a);
byte[] patchData = new byte[4104]; byte[] patchData = new byte[4104];
@ -129,7 +139,7 @@ public class SynthesizerService extends Service {
} }
public MidiListener getMidiListener() { public MidiListener getMidiListener() {
return androidGlue_; return midiListener_;
} }
/** /**
@ -214,7 +224,7 @@ public class SynthesizerService extends Service {
usbDevice_ = device; usbDevice_ = device;
usbMidiConnection_ = connection; usbMidiConnection_ = connection;
usbMidiInterface_ = intf; usbMidiInterface_ = intf;
usbMidiDevice_ = new UsbMidiDevice(androidGlue_, usbMidiConnection_, intf); usbMidiDevice_ = new UsbMidiDevice(midiListener_, usbMidiConnection_, intf);
usbMidiDevice_.start(); usbMidiDevice_.start();
return true; return true;
} else { } else {
@ -262,6 +272,16 @@ public class SynthesizerService extends Service {
} }
}; };
public interface OnCcListener {
abstract void onCcChange(int channel, int cc, int value);
}
public void setOnCcListener(OnCcListener onCcListener) {
onCcListener_ = onCcListener;
}
private MidiListener midiListener_;
// Binder to use for Activities in this process. // Binder to use for Activities in this process.
private final IBinder binder_ = new LocalBinder(); private final IBinder binder_ = new LocalBinder();
@ -275,4 +295,7 @@ public class SynthesizerService extends Service {
private UsbMidiDevice usbMidiDevice_; private UsbMidiDevice usbMidiDevice_;
private UsbInterface usbMidiInterface_; private UsbInterface usbMidiInterface_;
private UsbDevice usbDeviceNeedsPermission_; private UsbDevice usbDeviceNeedsPermission_;
// Plumbing for MIDI events
private OnCcListener onCcListener_;
} }

@ -205,11 +205,16 @@ public class PianoActivity2 extends Activity {
SynthesizerService.LocalBinder binder = (SynthesizerService.LocalBinder)service; SynthesizerService.LocalBinder binder = (SynthesizerService.LocalBinder)service;
synthesizerService_ = binder.getService(); synthesizerService_ = binder.getService();
piano_.bindTo(synthesizerService_.getMidiListener()); piano_.bindTo(synthesizerService_.getMidiListener());
// Populate patch names (note: we could update an existing list rather than
// creating a new adapter, but it probably wouldn't save all that much).
List<String> patchNames = synthesizerService_.getPatchNames(); List<String> patchNames = synthesizerService_.getPatchNames();
ArrayAdapter<String> adapter = new ArrayAdapter<String>( ArrayAdapter<String> adapter = new ArrayAdapter<String>(
PianoActivity2.this, android.R.layout.simple_spinner_item, patchNames); PianoActivity2.this, android.R.layout.simple_spinner_item, patchNames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
presetSpinner_.setAdapter(adapter); presetSpinner_.setAdapter(adapter);
// Handle any pending USB device events
if (usbDevicePending_ != null) { if (usbDevicePending_ != null) {
synthesizerService_.connectUsbMidi(usbDevicePending_); synthesizerService_.connectUsbMidi(usbDevicePending_);
usbDevicePending_ = null; usbDevicePending_ = null;
@ -225,6 +230,23 @@ public class PianoActivity2 extends Activity {
} }
} }
} }
// Connect controller changes to knob views
synthesizerService_.setOnCcListener(new SynthesizerService.OnCcListener() {
public void onCcChange(final int channel, final int cc, final int value) {
runOnUiThread(new Runnable() {
public void run() {
if (cc == 1) {
cutoffKnob_.setValue(value * (1.0 / 127));
} else if (cc == 2) {
resonanceKnob_.setValue(value * (1.0 / 127));
} else if (cc == 3) {
overdriveKnob_.setValue(value * (1.0 / 127));
}
}
});
}
});
} }
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
synthesizerService_ = null; synthesizerService_ = null;

@ -1,12 +1,12 @@
/* /*
* Copyright 2010 Google Inc. * Copyright 2010 Google Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -83,10 +83,10 @@ public class KnobView extends View {
diffAngle_ = 0; diffAngle_ = 0;
break; break;
} }
case MotionEvent.ACTION_MOVE: { case MotionEvent.ACTION_MOVE: {
getDrawingRect(rect_); getDrawingRect(rect_);
// Compare the previous angle of the finger position (relative to the center of the control) // Compare the previous angle of the finger position (relative to the center of the control)
// to the new angle, and update the value accordingly. // to the new angle, and update the value accordingly.
currentTouchAngle_ = knobValue_ * 2 * Math.PI * 0.8 + (Math.PI / 5.0); currentTouchAngle_ = knobValue_ * 2 * Math.PI * 0.8 + (Math.PI / 5.0);
@ -102,7 +102,7 @@ public class KnobView extends View {
knobValue_ = currentTouchAngle_ / (2.0 * Math.PI); knobValue_ = currentTouchAngle_ / (2.0 * Math.PI);
if (knobValue_ < 0.1) knobValue_ = 0.1; if (knobValue_ < 0.1) knobValue_ = 0.1;
if (knobValue_ > 0.9) knobValue_ = 0.9; if (knobValue_ > 0.9) knobValue_ = 0.9;
knobValue_ -= 0.1; knobValue_ -= 0.1;
knobValue_ /= 0.8; knobValue_ /= 0.8;
previousX_ = currentX_; previousX_ = currentX_;
previousY_ = currentY_; previousY_ = currentY_;
@ -115,7 +115,7 @@ public class KnobView extends View {
break; break;
} }
case MotionEvent.ACTION_UP: { case MotionEvent.ACTION_UP: {
break; break;
} }
@ -147,7 +147,9 @@ public class KnobView extends View {
} }
/** /**
* Sets the current value of the knob. * Sets the current value of the knob. Note that this call does not
* invoke the listener. The assumption is that any caller will also
* update the client.
*/ */
public void setValue(double value) { public void setValue(double value) {
if (value < min_) { if (value < min_) {
@ -157,9 +159,6 @@ public class KnobView extends View {
} else { } else {
knobValue_ = (value - min_) / (max_ - min_); knobValue_ = (value - min_) / (max_ - min_);
} }
if (listener_ != null) {
listener_.onKnobChanged(value);
}
invalidate(); invalidate();
} }
@ -189,30 +188,30 @@ public class KnobView extends View {
rectF_.left = center - rectF_.height() / 2; rectF_.left = center - rectF_.height() / 2;
rectF_.right = center + rectF_.height() / 2; rectF_.right = center + rectF_.height() / 2;
} }
// Draw outer white glow. // Draw outer white glow.
int[] radialGradientColors = {Color.WHITE, Color.WHITE, 0x00000000}; int[] radialGradientColors = {Color.WHITE, Color.WHITE, 0x00000000};
float[] radialGradientPositions = {0.0f, 0.87f, 1.0f}; float[] radialGradientPositions = {0.0f, 0.87f, 1.0f};
radialGradient_ = new RadialGradient(rect_.exactCenterX(), radialGradient_ = new RadialGradient(rect_.exactCenterX(),
rect_.exactCenterY(), rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) / 2, Math.min(rect_.width(), rect_.height()) / 2,
radialGradientColors, radialGradientColors,
radialGradientPositions, radialGradientPositions,
TileMode.CLAMP); TileMode.CLAMP);
knobPaint_.setShader(radialGradient_); knobPaint_.setShader(radialGradient_);
canvas.drawCircle(rect_.exactCenterX(), canvas.drawCircle(rect_.exactCenterX(),
rect_.exactCenterY(), rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) / 2, Math.min(rect_.width(), rect_.height()) / 2,
knobPaint_); knobPaint_);
// Draw outer gauge. // Draw outer gauge.
final int fullDark = Color.BLACK; final int fullDark = Color.BLACK;
final int guageStartColor = 0xff202050; final int guageStartColor = 0xff202050;
final int guageEndColor = 0xff4040A0; final int guageEndColor = 0xff4040A0;
final int adjustedStartColor = Color.argb( final int adjustedStartColor = Color.argb(
0xFF, 0xFF,
(int)(0.1875 * Color.red(guageStartColor) + (1.0 - 0.1875) * Color.red(guageEndColor)), (int)(0.1875 * Color.red(guageStartColor) + (1.0 - 0.1875) * Color.red(guageEndColor)),
(int)(0.1875 * Color.green(guageStartColor) + (1.0 - 0.1875) * Color.green(guageEndColor)), (int)(0.1875 * Color.green(guageStartColor) + (1.0 - 0.1875) * Color.green(guageEndColor)),
(int)(0.1875 * Color.blue(guageStartColor) + (1.0 - 0.1875) * Color.blue(guageEndColor))); (int)(0.1875 * Color.blue(guageStartColor) + (1.0 - 0.1875) * Color.blue(guageEndColor)));
@ -243,20 +242,20 @@ public class KnobView extends View {
rect_.exactCenterY(), rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) / 4, Math.min(rect_.width(), rect_.height()) / 4,
knobPaint_); knobPaint_);
// Draw inner white glow. // Draw inner white glow.
int[] innerRadialGradientColors = { 0x00000000, 0x00000000, Color.WHITE }; int[] innerRadialGradientColors = { 0x00000000, 0x00000000, Color.WHITE };
float[] innerRadialGradientPositions = { 0.0f, 0.6f, 1.0f }; float[] innerRadialGradientPositions = { 0.0f, 0.6f, 1.0f };
innerRadialGradient_ = new RadialGradient(rect_.exactCenterX(), innerRadialGradient_ = new RadialGradient(rect_.exactCenterX(),
rect_.exactCenterY(), rect_.exactCenterY(),
Math.min(rect_.width(), rect_.height()) / 4f, Math.min(rect_.width(), rect_.height()) / 4f,
innerRadialGradientColors, innerRadialGradientColors,
innerRadialGradientPositions, innerRadialGradientPositions,
TileMode.CLAMP); TileMode.CLAMP);
knobPaint_.setShader(innerRadialGradient_); knobPaint_.setShader(innerRadialGradient_);
canvas.drawCircle(rect_.exactCenterX(), canvas.drawCircle(rect_.exactCenterX(),
rect_.exactCenterY(), rect_.exactCenterY(),
Math.min(rect_.width(),rect_.height()) / 4, Math.min(rect_.width(),rect_.height()) / 4,
knobPaint_); knobPaint_);
// Draw indicator. // Draw indicator.
@ -270,7 +269,7 @@ public class KnobView extends View {
(float)arcWidth, (float)arcWidth,
false, false,
knobPaint_); knobPaint_);
// Draw text. // Draw text.
String knobValueString = String.format("%.2f", getValue()); String knobValueString = String.format("%.2f", getValue());
Typeface typeface = Typeface.create(knobPaint_.getTypeface(), Typeface.BOLD); Typeface typeface = Typeface.create(knobPaint_.getTypeface(), Typeface.BOLD);
@ -329,7 +328,7 @@ public class KnobView extends View {
if (height > width && heightMode != MeasureSpec.EXACTLY) { if (height > width && heightMode != MeasureSpec.EXACTLY) {
height = width; height = width;
} }
setMeasuredDimension(width, height); setMeasuredDimension(width, height);
} }

Loading…
Cancel
Save