Midi plumbing refactoring

The reverse channel from the SynthesizerService to the views has been
refactored a bit so it's just a standard MidiListener interface rather
than a whole bunch of listeners for individual messages.
master
Raph Levien 11 years ago
parent 786c3dbc83
commit 8a63b11a04
  1. 52
      android/src/com/levien/synthesizer/android/service/SynthesizerService.java
  2. 195
      android/src/com/levien/synthesizer/android/ui/PianoActivity2.java
  3. 92
      core/src/com/levien/synthesizer/core/midi/MessageTee.java

@ -41,7 +41,7 @@ import android.util.Log;
import com.levien.synthesizer.R;
import com.levien.synthesizer.android.AndroidGlue;
import com.levien.synthesizer.android.usb.UsbMidiDevice;
import com.levien.synthesizer.core.midi.MessageForwarder;
import com.levien.synthesizer.core.midi.MessageTee;
import com.levien.synthesizer.core.midi.MidiListener;
import com.levien.synthesizer.core.model.composite.MultiChannelSynthesizer;
@ -100,31 +100,7 @@ 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);
}
}
};
midiListener_ = new MessageTee(androidGlue_);
androidGlue_.setPlayState(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED);
@ -288,25 +264,11 @@ 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;
public void setMidiListener(MidiListener target) {
midiListener_.setSecondTarget(target);
}
private MidiListener midiListener_;
private MessageTee midiListener_;
// Binder to use for Activities in this process.
private final IBinder binder_ = new LocalBinder();
@ -322,8 +284,4 @@ public class SynthesizerService extends Service {
private UsbInterface usbMidiInterface_;
private UsbDevice usbDeviceNeedsPermission_;
// Plumbing for MIDI events
private OnCcListener onCcListener_;
private OnNoteListener onNoteOnListener_;
private OnNoteListener onNoteOffListener_;
}

@ -16,7 +16,6 @@
package com.levien.synthesizer.android.ui;
import java.util.HashMap;
import java.util.List;
import android.annotation.TargetApi;
@ -29,8 +28,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.Build;
import android.os.Bundle;
@ -45,10 +42,11 @@ import android.widget.Spinner;
import com.levien.synthesizer.R;
import com.levien.synthesizer.android.service.SynthesizerService;
import com.levien.synthesizer.android.usb.UsbMidiDevice;
import com.levien.synthesizer.android.widgets.knob.KnobListener;
import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView;
import com.levien.synthesizer.core.midi.MidiAdapter;
import com.levien.synthesizer.core.midi.MidiListener;
/**
* Activity for simply playing the piano.
@ -70,44 +68,6 @@ public class PianoActivity2 extends Activity {
overdriveKnob_ = (KnobView)findViewById(R.id.overdriveKnob);
presetSpinner_ = (Spinner)findViewById(R.id.presetSpinner);
presetSpinner_.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (synthesizerService_ != null) {
synthesizerService_.getMidiListener().onProgramChange(0, position);
}
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
cutoffKnob_.setKnobListener(new KnobListener() {
public void onKnobChanged(double newValue) {
// 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) {
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) {
if (synthesizerService_ != null) {
int value = (int)Math.round(newValue * 127);
synthesizerService_.getMidiListener().onController(0, 3, value);
}
}
});
//piano_.bindTo(synthesizerService_.getMidiListener());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
setupUsbMidi(getIntent());
}
@ -212,71 +172,108 @@ public class PianoActivity2 extends Activity {
public void onServiceConnected(ComponentName className, IBinder service) {
SynthesizerService.LocalBinder binder = (SynthesizerService.LocalBinder)service;
synthesizerService_ = binder.getService();
piano_.bindTo(synthesizerService_.getMidiListener());
onSynthConnected();
}
public void onServiceDisconnected(ComponentName className) {
onSynthDisconnected();
synthesizerService_ = null;
}
};
// 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();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
PianoActivity2.this, android.R.layout.simple_spinner_item, patchNames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
presetSpinner_.setAdapter(adapter);
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
private void onSynthConnected() {
final MidiListener synthMidi = synthesizerService_.getMidiListener();
piano_.bindTo(synthMidi);
// Handle any pending USB device events
if (usbDevicePending_ != null) {
synthesizerService_.connectUsbMidi(usbDevicePending_);
usbDevicePending_ = null;
} else {
UsbDevice device = synthesizerService_.usbDeviceNeedsPermission();
if (device != null) {
synchronized (usbReceiver_) {
if (!permissionRequestPending_) {
permissionRequestPending_ = true;
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
usbManager.requestPermission(device, permissionIntent_);
}
cutoffKnob_.setKnobListener(new KnobListener() {
public void onKnobChanged(double newValue) {
int value = (int)Math.round(newValue * 127);
synthMidi.onController(0, 1, value);
}
});
resonanceKnob_.setKnobListener(new KnobListener() {
public void onKnobChanged(double newValue) {
int value = (int)Math.round(newValue * 127);
synthMidi.onController(0, 2, value);
}
});
overdriveKnob_.setKnobListener(new KnobListener() {
public void onKnobChanged(double newValue) {
int value = (int)Math.round(newValue * 127);
synthMidi.onController(0, 3, value);
}
});
// Connect controller changes to knob views
synthesizerService_.setMidiListener(new MidiAdapter() {
public void onNoteOn(final int channel, final int note, final int velocity) {
runOnUiThread(new Runnable() {
public void run() {
piano_.setNoteOn(note, true);
}
}
});
}
// 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));
}
}
});
}
});
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 onNoteOff(final int channel, final int note, final int velocity) {
runOnUiThread(new Runnable() {
public void run() {
piano_.setNoteOn(note, false);
}
});
}
public void onController(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));
}
});
}
});
}
});
// 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();
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
PianoActivity2.this, android.R.layout.simple_spinner_item, patchNames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
presetSpinner_.setAdapter(adapter);
presetSpinner_.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
synthesizerService_.getMidiListener().onProgramChange(0, position);
}
public void onNothingSelected(AdapterView<?> parent) {
}
});
// Handle any pending USB device events
if (usbDevicePending_ != null) {
synthesizerService_.connectUsbMidi(usbDevicePending_);
usbDevicePending_ = null;
} else {
UsbDevice device = synthesizerService_.usbDeviceNeedsPermission();
if (device != null) {
synchronized (usbReceiver_) {
if (!permissionRequestPending_) {
permissionRequestPending_ = true;
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
usbManager.requestPermission(device, permissionIntent_);
}
}
});
}
public void onServiceDisconnected(ComponentName className) {
synthesizerService_ = null;
}
}
};
}
private void onSynthDisconnected() {
synthesizerService_.setMidiListener(null);
}
private SynthesizerService synthesizerService_;

@ -17,102 +17,172 @@
package com.levien.synthesizer.core.midi;
/**
* Class for forwarding messages to another MidiListener. Great for subclassing.
* Duplicate each MIDI message to two listeners
*/
public class MessageForwarder implements MidiListener {
public MessageForwarder(MidiListener target) {
public class MessageTee implements MidiListener {
public MessageTee(MidiListener target) {
target_ = target;
}
public void setSecondTarget(MidiListener target) {
target2_ = target;
}
// Control events.
public void onNoteOff(int channel, int note, int velocity) {
target_.onNoteOff(channel, note, velocity);
if (target2_ != null) {
target2_.onNoteOff(channel, note, velocity);
}
}
public void onNoteOn(int channel, int note, int velocity) {
target_.onNoteOn(channel, note, velocity);
if (target2_ != null) {
target2_.onNoteOn(channel, note, velocity);
}
}
public void onNoteAftertouch(int channel, int note, int aftertouch) {
target_.onNoteAftertouch(channel, note, aftertouch);
if (target2_ != null) {
target2_.onNoteAftertouch(channel, note, aftertouch);
}
}
public void onController(int channel, int control, int value) {
target_.onController(channel, control, value);
if (target2_ != null) {
target2_.onController(channel, control, value);
}
}
public void onProgramChange(int channel, int program) {
target_.onProgramChange(channel, program);
if (target2_ != null) {
target2_.onProgramChange(channel, program);
}
}
public void onChannelAftertouch(int channel, int aftertouch) {
target_.onChannelAftertouch(channel, aftertouch);
if (target2_ != null) {
target2_.onChannelAftertouch(channel, aftertouch);
}
}
public void onPitchBend(int channel, int value) {
target_.onPitchBend(channel, value);
if (target2_ != null) {
target2_.onPitchBend(channel, value);
}
}
// Other events.
public void onTimingClock() {
target_.onTimingClock();
if (target2_ != null) {
target2_.onTimingClock();
}
}
public void onActiveSensing() {
target_.onActiveSensing();
if (target2_ != null) {
target2_.onActiveSensing();
}
}
// Meta events.
public void onSequenceNumber(int sequenceNumber) {
target_.onSequenceNumber(sequenceNumber);
if (target2_ != null) {
target2_.onSequenceNumber(sequenceNumber);
}
}
public void onText(byte[] text) {
target_.onText(text);
if (target2_ != null) {
target2_.onText(text);
}
}
public void onCopyrightNotice(byte[] text) {
target_.onCopyrightNotice(text);
if (target2_ != null) {
target2_.onCopyrightNotice(text);
}
}
public void onSequenceName(byte[] text) {
target_.onSequenceName(text);
if (target2_ != null) {
target2_.onSequenceName(text);
}
}
public void onInstrumentName(byte[] text) {
target_.onInstrumentName(text);
if (target2_ != null) {
target2_.onInstrumentName(text);
}
}
public void onLyrics(byte[] text) {
target_.onLyrics(text);
if (target2_ != null) {
target2_.onLyrics(text);
}
}
public void onMarker(byte[] text) {
target_.onMarker(text);
if (target2_ != null) {
target2_.onMarker(text);
}
}
public void onCuePoint(byte[] text) {
target_.onCuePoint(text);
if (target2_ != null) {
target2_.onCuePoint(text);
}
}
public void onChannelPrefix(int channel) {
target_.onChannelPrefix(channel);
if (target2_ != null) {
target2_.onChannelPrefix(channel);
}
}
public void onPort(byte[] data) {
target_.onPort(data);
if (target2_ != null) {
target2_.onPort(data);
}
}
public void onEndOfTrack() {
target_.onEndOfTrack();
if (target2_ != null) {
target2_.onEndOfTrack();
}
}
public void onSetTempo(int microsecondsPerQuarterNote) {
target_.onSetTempo(microsecondsPerQuarterNote);
if (target2_ != null) {
target2_.onSetTempo(microsecondsPerQuarterNote);
}
}
public void onSmpteOffset(byte[] data) {
target_.onSmpteOffset(data);
if (target2_ != null) {
target2_.onSmpteOffset(data);
}
}
public void onTimeSignature(int numerator,
@ -121,20 +191,34 @@ public class MessageForwarder implements MidiListener {
int thirtySecondNotesPerQuarterNote) {
target_.onTimeSignature(numerator, denominator, metronomePulse,
thirtySecondNotesPerQuarterNote);
if (target2_ != null) {
target2_.onTimeSignature(numerator, denominator, metronomePulse,
thirtySecondNotesPerQuarterNote);
}
}
public void onKeySignature(int key, boolean isMinor) {
target_.onKeySignature(key, isMinor);
if (target2_ != null) {
target2_.onKeySignature(key, isMinor);
}
}
public void onSequencerSpecificEvent(byte[] data) {
target_.onSequencerSpecificEvent(data);
if (target2_ != null) {
target2_.onSequencerSpecificEvent(data);
}
}
// SysEx events.
public void onSysEx(byte[] data) {
target_.onSysEx(data);
if (target2_ != null) {
target2_.onSysEx(data);
}
}
private MidiListener target_;
private final MidiListener target_;
private MidiListener target2_;
}
Loading…
Cancel
Save