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.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.MessageTee;
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;
@ -100,31 +100,7 @@ public class SynthesizerService extends Service {
Log.e(getClass().getName(), "loading patches failed"); Log.e(getClass().getName(), "loading patches failed");
} }
} }
midiListener_ = new MessageForwarder(androidGlue_) { midiListener_ = new MessageTee(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); androidGlue_.setPlayState(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_DEVICE_DETACHED); 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 void setMidiListener(MidiListener target) {
public interface OnCcListener { midiListener_.setSecondTarget(target);
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_; private MessageTee 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();
@ -322,8 +284,4 @@ public class SynthesizerService extends Service {
private UsbInterface usbMidiInterface_; private UsbInterface usbMidiInterface_;
private UsbDevice usbDeviceNeedsPermission_; 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; package com.levien.synthesizer.android.ui;
import java.util.HashMap;
import java.util.List; import java.util.List;
import android.annotation.TargetApi; import android.annotation.TargetApi;
@ -29,8 +28,6 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager; import android.hardware.usb.UsbManager;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -45,10 +42,11 @@ import android.widget.Spinner;
import com.levien.synthesizer.R; import com.levien.synthesizer.R;
import com.levien.synthesizer.android.service.SynthesizerService; 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.KnobListener;
import com.levien.synthesizer.android.widgets.knob.KnobView; import com.levien.synthesizer.android.widgets.knob.KnobView;
import com.levien.synthesizer.android.widgets.piano.PianoView; 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. * Activity for simply playing the piano.
@ -70,44 +68,6 @@ public class PianoActivity2 extends Activity {
overdriveKnob_ = (KnobView)findViewById(R.id.overdriveKnob); overdriveKnob_ = (KnobView)findViewById(R.id.overdriveKnob);
presetSpinner_ = (Spinner)findViewById(R.id.presetSpinner); 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) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {
setupUsbMidi(getIntent()); setupUsbMidi(getIntent());
} }
@ -212,71 +172,108 @@ public class PianoActivity2 extends Activity {
public void onServiceConnected(ComponentName className, IBinder service) { public void onServiceConnected(ComponentName className, IBinder service) {
SynthesizerService.LocalBinder binder = (SynthesizerService.LocalBinder)service; SynthesizerService.LocalBinder binder = (SynthesizerService.LocalBinder)service;
synthesizerService_ = binder.getService(); 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 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
// creating a new adapter, but it probably wouldn't save all that much). private void onSynthConnected() {
List<String> patchNames = synthesizerService_.getPatchNames(); final MidiListener synthMidi = synthesizerService_.getMidiListener();
ArrayAdapter<String> adapter = new ArrayAdapter<String>( piano_.bindTo(synthMidi);
PianoActivity2.this, android.R.layout.simple_spinner_item, patchNames);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
presetSpinner_.setAdapter(adapter);
// Handle any pending USB device events cutoffKnob_.setKnobListener(new KnobListener() {
if (usbDevicePending_ != null) { public void onKnobChanged(double newValue) {
synthesizerService_.connectUsbMidi(usbDevicePending_); int value = (int)Math.round(newValue * 127);
usbDevicePending_ = null; synthMidi.onController(0, 1, value);
} else { }
UsbDevice device = synthesizerService_.usbDeviceNeedsPermission(); });
if (device != null) { resonanceKnob_.setKnobListener(new KnobListener() {
synchronized (usbReceiver_) { public void onKnobChanged(double newValue) {
if (!permissionRequestPending_) { int value = (int)Math.round(newValue * 127);
permissionRequestPending_ = true; synthMidi.onController(0, 2, value);
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); }
usbManager.requestPermission(device, permissionIntent_); });
} 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 public void onNoteOff(final int channel, final int note, final int velocity) {
synthesizerService_.setOnCcListener(new SynthesizerService.OnCcListener() { runOnUiThread(new Runnable() {
public void onCcChange(final int channel, final int cc, final int value) { public void run() {
runOnUiThread(new Runnable() { piano_.setNoteOn(note, false);
public void run() { }
if (cc == 1) { });
cutoffKnob_.setValue(value * (1.0 / 127)); }
} else if (cc == 2) {
resonanceKnob_.setValue(value * (1.0 / 127)); public void onController(final int channel, final int cc, final int value) {
} else if (cc == 3) { runOnUiThread(new Runnable() {
overdriveKnob_.setValue(value * (1.0 / 127)); 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) {
synthesizerService_.setOnNoteListeners(new SynthesizerService.OnNoteListener() { overdriveKnob_.setValue(value * (1.0 / 127));
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);
} }
}); }
});
}
});
// 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_; private SynthesizerService synthesizerService_;

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