aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/jsyn/midi
diff options
context:
space:
mode:
authorPhil Burk <[email protected]>2016-08-02 07:52:17 -0700
committerPhil Burk <[email protected]>2016-10-24 08:29:20 -0700
commit580fea450ec0982d0bd8be589f00566267e7b0d1 (patch)
tree0420f768fc7c63208b1720232c447e17af9017af /src/com/jsyn/midi
parenta6583e89166f7477a675cf3094a91b303ba7850a (diff)
Instruments: add better synth, pitch control
Diffstat (limited to 'src/com/jsyn/midi')
-rw-r--r--src/com/jsyn/midi/MessageParser.java90
-rw-r--r--src/com/jsyn/midi/MidiConstants.java33
-rw-r--r--src/com/jsyn/midi/MidiSynthesizer.java98
3 files changed, 212 insertions, 9 deletions
diff --git a/src/com/jsyn/midi/MessageParser.java b/src/com/jsyn/midi/MessageParser.java
index 43d10c8..d0f5d4d 100644
--- a/src/com/jsyn/midi/MessageParser.java
+++ b/src/com/jsyn/midi/MessageParser.java
@@ -4,9 +4,9 @@
* 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.
@@ -18,10 +18,15 @@ package com.jsyn.midi;
/**
* Parse the message and call the appropriate method to handle it.
- *
+ *
* @author Phil Burk (C) 2010 Mobileer Inc
*/
public class MessageParser {
+ private int[] parameterIndices = new int[MidiConstants.MAX_CHANNELS];
+ private int[] parameterValues = new int[MidiConstants.MAX_CHANNELS];
+ private int BIT_NON_RPM = 1 << 14;
+ private int MASK_14BIT = (1 << 14) - 1;
+
public void parse(byte[] message) {
int status = message[0];
int command = status & 0xF0;
@@ -41,27 +46,102 @@ public class MessageParser {
noteOff(channel, message[1], message[2]);
break;
+ case MidiConstants.POLYPHONIC_AFTERTOUCH:
+ polyphonicAftertouch(channel, message[1], message[2]);
+ break;
+
+ case MidiConstants.CHANNEL_PRESSURE:
+ channelPressure(channel, message[1]);
+ break;
+
case MidiConstants.CONTROL_CHANGE:
- controlChange(channel, message[1], message[2]);
+ rawControlChange(channel, message[1], message[2]);
+ break;
+
+ case MidiConstants.PROGRAM_CHANGE:
+ programChange(channel, message[1]);
break;
case MidiConstants.PITCH_BEND:
- int bend = (((message[2]) & 0x007F) << 7) + ((message[1]) & 0x007F);
+ int bend = (message[2] << 7) + message[1];
pitchBend(channel, bend);
break;
}
}
+ public void rawControlChange(int channel, int index, int value) {
+ int paramIndex;
+ int paramValue;
+ switch(index) {
+ case MidiConstants.CONTROLLER_DATA_ENTRY:
+ parameterValues[channel] = value << 7;
+ fireParameterChange(channel);
+ break;
+ case MidiConstants.CONTROLLER_DATA_ENTRY_LSB:
+ paramValue = parameterValues[channel] & ~0x7F;
+ paramValue |= value;
+ parameterValues[channel] = paramValue;
+ fireParameterChange(channel);
+ break;
+ case MidiConstants.CONTROLLER_NRPN_LSB:
+ paramIndex = parameterIndices[channel] & ~0x7F;
+ paramIndex |= value | BIT_NON_RPM;
+ parameterIndices[channel] = paramIndex;
+ break;
+ case MidiConstants.CONTROLLER_NRPN_MSB:
+ parameterIndices[channel] = (value << 7) | BIT_NON_RPM;;
+ break;
+ case MidiConstants.CONTROLLER_RPN_LSB:
+ paramIndex = parameterIndices[channel] & ~0x7F;
+ paramIndex |= value;
+ parameterIndices[channel] = paramIndex;
+ break;
+ case MidiConstants.CONTROLLER_RPN_MSB:
+ parameterIndices[channel] = value << 7;
+ break;
+ default:
+ controlChange(channel, index, value);
+ break;
+
+ }
+ }
+
+ private void fireParameterChange(int channel) {
+ int paramIndex;
+ paramIndex = parameterIndices[channel];
+ if ((paramIndex & BIT_NON_RPM) == 0) {
+ registeredParameter(channel, paramIndex, parameterValues[channel]);
+ } else {
+ nonRegisteredParameter(channel, paramIndex & MASK_14BIT, parameterValues[channel]);
+ }
+ }
+
+ public void nonRegisteredParameter(int channel, int index14, int value14) {
+ }
+
+ public void registeredParameter(int channel, int index14, int value14) {
+ }
+
public void pitchBend(int channel, int bend) {
}
+ public void programChange(int channel, int program) {
+ }
+
+ public void polyphonicAftertouch(int channel, int pitch, int pressure) {
+ }
+
+ public void channelPressure(int channel, int pressure) {
+ }
+
public void controlChange(int channel, int index, int value) {
}
public void noteOn(int channel, int pitch, int velocity) {
}
+ // If a NOTE_ON with zero velocity is received then noteOff will be called.
public void noteOff(int channel, int pitch, int velocity) {
}
}
diff --git a/src/com/jsyn/midi/MidiConstants.java b/src/com/jsyn/midi/MidiConstants.java
index dae9390..8c92119 100644
--- a/src/com/jsyn/midi/MidiConstants.java
+++ b/src/com/jsyn/midi/MidiConstants.java
@@ -4,9 +4,9 @@
* 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.
@@ -18,10 +18,12 @@ package com.jsyn.midi;
/**
* Constants that define the MIDI standard.
- *
+ *
* @author Phil Burk (C) 2010 Mobileer Inc
*/
public class MidiConstants {
+
+ public static final int MAX_CHANNELS = 16;
// Basic commands.
public static final int NOTE_OFF = 0x80;
public static final int NOTE_ON = 0x90;
@@ -29,10 +31,33 @@ public class MidiConstants {
public static final int CONTROL_CHANGE = 0xB0;
public static final int PROGRAM_CHANGE = 0xC0;
public static final int CHANNEL_AFTERTOUCH = 0xD0;
+ public static final int CHANNEL_PRESSURE = CHANNEL_AFTERTOUCH;
public static final int PITCH_BEND = 0xE0;
public static final int SYSTEM_COMMON = 0xF0;
- public static final int PITCH_BEND_CENTER = 8192;
+ public static final int PITCH_BEND_CENTER = 0x2000;
+
+ public static final int CONTROLLER_BANK_SELECT = 0;
+ public static final int CONTROLLER_MOD_WHEEL = 1;
+ public static final int CONTROLLER_BREATH = 2;
+ public static final int CONTROLLER_DATA_ENTRY = 6;
+ public static final int CONTROLLER_VOLUME = 7;
+ public static final int CONTROLLER_PAN = 10;
+
+ public static final int CONTROLLER_LSB_OFFSET = 32;
+ public static final int CONTROLLER_DATA_ENTRY_LSB = CONTROLLER_DATA_ENTRY + CONTROLLER_LSB_OFFSET;
+
+ public static final int CONTROLLER_TIMBRE = 74; // Often used by MPE for Y axis control.
+
+ public static final int CONTROLLER_DATA_INCREMENT = 96;
+ public static final int CONTROLLER_DATA_DECREMENT = 97;
+ public static final int CONTROLLER_NRPN_LSB = 98;
+ public static final int CONTROLLER_NRPN_MSB = 99;
+ public static final int CONTROLLER_RPN_LSB = 100;
+ public static final int CONTROLLER_RPN_MSB = 101;
+
+ public static final int RPN_BEND_RANGE = 0;
+ public static final int RPN_FINE_TUNING = 1;
public static final String PITCH_NAMES[] = {
"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
diff --git a/src/com/jsyn/midi/MidiSynthesizer.java b/src/com/jsyn/midi/MidiSynthesizer.java
new file mode 100644
index 0000000..e011430
--- /dev/null
+++ b/src/com/jsyn/midi/MidiSynthesizer.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016 Phil Burk, Mobileer 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.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.jsyn.midi;
+
+import com.jsyn.util.MultiChannelSynthesizer;
+
+public class MidiSynthesizer extends MessageParser {
+
+ private MultiChannelSynthesizer multiSynth;
+
+ public MidiSynthesizer(MultiChannelSynthesizer multiSynth) {
+ this.multiSynth = multiSynth;
+ }
+
+ @Override
+ public void controlChange(int channel, int index, int value) {
+ //System.out.println("controlChange(" + channel + ", " + index + ", " + value + ")");
+ double normalized = value * (1.0 / 127.0);
+ switch (index) {
+ case MidiConstants.CONTROLLER_MOD_WHEEL:
+ double vibratoDepth = 0.1 * normalized;
+ System.out.println( "vibratoDepth = " + vibratoDepth );
+ multiSynth.setVibratoDepth(channel, vibratoDepth);
+ break;
+ case MidiConstants.CONTROLLER_TIMBRE:
+ multiSynth.setTimbre(channel, normalized);
+ break;
+ case MidiConstants.CONTROLLER_VOLUME:
+ multiSynth.setVolume(channel, normalized);
+ break;
+ case MidiConstants.CONTROLLER_PAN:
+ // convert to -1 to +1 range
+ multiSynth.setPan(channel, (normalized * 2.0) - 1.0);
+ break;
+ }
+ }
+
+ @Override
+ public void registeredParameter(int channel, int index14, int value14) {
+ switch(index14) {
+ case MidiConstants.RPN_BEND_RANGE:
+ int semitones = value14 >> 7;
+ int cents = value14 & 0x7F;
+ double bendRange = semitones + (cents * 0.01);
+ multiSynth.setBendRange(channel, bendRange);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void programChange(int channel, int program) {
+ multiSynth.programChange(channel, program);
+ }
+
+ @Override
+ public void channelPressure(int channel, int value) {
+ double normalized = value * (1.0 / 127.0);
+ multiSynth.setPressure(channel, normalized);
+ }
+
+ @Override
+ public void noteOff(int channel, int noteNumber, int velocity) {
+ multiSynth.noteOff(channel, noteNumber, velocity);
+ }
+
+ @Override
+ public void noteOn(int channel, int noteNumber, int velocity) {
+ multiSynth.noteOn(channel, noteNumber, velocity);
+ }
+
+ @Override
+ public void pitchBend(int channel, int bend) {
+ double offset = (bend - MidiConstants.PITCH_BEND_CENTER)
+ * (1.0 / (MidiConstants.PITCH_BEND_CENTER));
+ multiSynth.setPitchBend(channel, offset);
+ }
+
+ public void onReceive(byte[] bytes, int i, int length) {
+ parse(bytes); // TODO
+ }
+
+}