| /* | |
| * Copyright (c) 2007, 2023, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package com.sun.media.sound; | |
| import java.util.ArrayList; | |
| import java.util.HashMap; | |
| import java.util.Iterator; | |
| import java.util.List; | |
| import java.util.Map; | |
| import javax.sound.midi.MidiChannel; | |
| import javax.sound.midi.Patch; | |
| /** | |
| * Software Synthesizer MIDI channel class. | |
| * | |
| * @author Karl Helgason | |
| */ | |
| public final class SoftChannel implements MidiChannel, ModelDirectedPlayer { | |
| private static final boolean[] dontResetControls = new boolean[128]; | |
| static { | |
| dontResetControls[0] = true; // Bank Select (MSB) | |
| dontResetControls[32] = true; // Bank Select (LSB) | |
| dontResetControls[7] = true; // Channel Volume (MSB) | |
| dontResetControls[8] = true; // Balance (MSB) | |
| dontResetControls[10] = true; // Pan (MSB) | |
| dontResetControls[11] = true; // Expression (MSB) | |
| dontResetControls[91] = true; // Effects 1 Depth (default: Reverb Send) | |
| dontResetControls[92] = true; // Effects 2 Depth (default: Tremolo Depth) | |
| dontResetControls[93] = true; // Effects 3 Depth (default: Chorus Send) | |
| dontResetControls[94] = true; // Effects 4 Depth (default: Celeste [Detune] Depth) | |
| dontResetControls[95] = true; // Effects 5 Depth (default: Phaser Depth) | |
| dontResetControls[70] = true; // Sound Controller 1 (default: Sound Variation) | |
| dontResetControls[71] = true; // Sound Controller 2 (default: Timbre / Harmonic Quality) | |
| dontResetControls[72] = true; // Sound Controller 3 (default: Release Time) | |
| dontResetControls[73] = true; // Sound Controller 4 (default: Attack Time) | |
| dontResetControls[74] = true; // Sound Controller 5 (default: Brightness) | |
| dontResetControls[75] = true; // Sound Controller 6 (GM2 default: Decay Time) | |
| dontResetControls[76] = true; // Sound Controller 7 (GM2 default: Vibrato Rate) | |
| dontResetControls[77] = true; // Sound Controller 8 (GM2 default: Vibrato Depth) | |
| dontResetControls[78] = true; // Sound Controller 9 (GM2 default: Vibrato Delay) | |
| dontResetControls[79] = true; // Sound Controller 10 (GM2 default: Undefined) | |
| dontResetControls[120] = true; // All Sound Off | |
| dontResetControls[121] = true; // Reset All Controllers | |
| dontResetControls[122] = true; // Local Control On/Off | |
| dontResetControls[123] = true; // All Notes Off | |
| dontResetControls[124] = true; // Omni Mode Off | |
| dontResetControls[125] = true; // Omni Mode On | |
| dontResetControls[126] = true; // Poly Mode Off | |
| dontResetControls[127] = true; // Poly Mode On | |
| dontResetControls[6] = true; // Data Entry (MSB) | |
| dontResetControls[38] = true; // Data Entry (LSB) | |
| dontResetControls[96] = true; // Data Increment | |
| dontResetControls[97] = true; // Data Decrement | |
| dontResetControls[98] = true; // Non-Registered Parameter Number (LSB) | |
| dontResetControls[99] = true; // Non-Registered Parameter Number(MSB) | |
| dontResetControls[100] = true; // RPN = Null | |
| dontResetControls[101] = true; // RPN = Null | |
| } | |
| private static final int RPN_NULL_VALUE = (127 << 7) + 127; | |
| private int rpn_control = RPN_NULL_VALUE; | |
| private int nrpn_control = RPN_NULL_VALUE; | |
| double portamento_time = 1; // keyschanges per control buffer time | |
| int[] portamento_lastnote = new int[128]; | |
| int portamento_lastnote_ix = 0; | |
| private boolean portamento = false; | |
| private boolean mono = false; | |
| private boolean mute = false; | |
| private boolean solo = false; | |
| private boolean solomute = false; | |
| private final Object control_mutex; | |
| private final int channel; | |
| private final SoftVoice[] voices; | |
| private int bank; | |
| private int program; | |
| private final SoftSynthesizer synthesizer; | |
| private final SoftMainMixer mainmixer; | |
| private final int[] polypressure = new int[128]; | |
| private int channelpressure = 0; | |
| private final int[] controller = new int[128]; | |
| private int pitchbend; | |
| private final double[] co_midi_pitch = new double[1]; | |
| private final double[] co_midi_channel_pressure = new double[1]; | |
| SoftTuning tuning = new SoftTuning(); | |
| int tuning_bank = 0; | |
| int tuning_program = 0; | |
| SoftInstrument current_instrument = null; | |
| ModelChannelMixer current_mixer = null; | |
| ModelDirector current_director = null; | |
| // Controller Destination Settings | |
| int cds_control_number = -1; | |
| ModelConnectionBlock[] cds_control_connections = null; | |
| ModelConnectionBlock[] cds_channelpressure_connections = null; | |
| ModelConnectionBlock[] cds_polypressure_connections = null; | |
| boolean sustain = false; | |
| boolean[][] keybasedcontroller_active = null; | |
| double[][] keybasedcontroller_value = null; | |
| private class MidiControlObject implements SoftControl { | |
| double[] pitch = co_midi_pitch; | |
| double[] channel_pressure = co_midi_channel_pressure; | |
| double[] poly_pressure = new double[1]; | |
| @Override | |
| public double[] get(int instance, String name) { | |
| if (name == null) | |
| return null; | |
| if (name.equals("pitch")) | |
| return pitch; | |
| if (name.equals("channel_pressure")) | |
| return channel_pressure; | |
| if (name.equals("poly_pressure")) | |
| return poly_pressure; | |
| return null; | |
| } | |
| } | |
| private final SoftControl[] co_midi = new SoftControl[128]; | |
| { | |
| for (int i = 0; i < co_midi.length; i++) { | |
| co_midi[i] = new MidiControlObject(); | |
| } | |
| } | |
| private final double[][] co_midi_cc_cc = new double[128][1]; | |
| private final SoftControl co_midi_cc = new SoftControl() { | |
| double[][] cc = co_midi_cc_cc; | |
| @Override | |
| public double[] get(int instance, String name) { | |
| if (name == null) | |
| return null; | |
| return cc[Integer.parseInt(name)]; | |
| } | |
| }; | |
| Map<Integer, int[]> co_midi_rpn_rpn_i = new HashMap<>(); | |
| Map<Integer, double[]> co_midi_rpn_rpn = new HashMap<>(); | |
| private final SoftControl co_midi_rpn = new SoftControl() { | |
| Map<Integer, double[]> rpn = co_midi_rpn_rpn; | |
| @Override | |
| public double[] get(int instance, String name) { | |
| if (name == null) | |
| return null; | |
| int iname = Integer.parseInt(name); | |
| double[] v = rpn.get(iname); | |
| if (v == null) { | |
| v = new double[1]; | |
| rpn.put(iname, v); | |
| } | |
| return v; | |
| } | |
| }; | |
| Map<Integer, int[]> co_midi_nrpn_nrpn_i = new HashMap<>(); | |
| Map<Integer, double[]> co_midi_nrpn_nrpn = new HashMap<>(); | |
| private final SoftControl co_midi_nrpn = new SoftControl() { | |
| Map<Integer, double[]> nrpn = co_midi_nrpn_nrpn; | |
| @Override | |
| public double[] get(int instance, String name) { | |
| if (name == null) | |
| return null; | |
| int iname = Integer.parseInt(name); | |
| double[] v = nrpn.get(iname); | |
| if (v == null) { | |
| v = new double[1]; | |
| nrpn.put(iname, v); | |
| } | |
| return v; | |
| } | |
| }; | |
| private static int restrict7Bit(int value) | |
| { | |
| if(value < 0) return 0; | |
| if(value > 127) return 127; | |
| return value; | |
| } | |
| private static int restrict14Bit(int value) | |
| { | |
| if(value < 0) return 0; | |
| if(value > 16256) return 16256; | |
| return value; | |
| } | |
| public SoftChannel(SoftSynthesizer synth, int channel) { | |
| this.channel = channel; | |
| this.voices = synth.getVoices(); | |
| this.synthesizer = synth; | |
| this.mainmixer = synth.getMainMixer(); | |
| control_mutex = synth.control_mutex; | |
| resetAllControllers(true); | |
| } | |
| private int findFreeVoice(int x) { | |
| if(x == -1) | |
| { | |
| // x = -1 means that there where no available voice | |
| // last time we called findFreeVoice | |
| // and it hasn't changed because no audio has been | |
| // rendered in the meantime. | |
| // Therefore we have to return -1. | |
| return -1; | |
| } | |
| for (int i = x; i < voices.length; i++) | |
| if (!voices[i].active) | |
| return i; | |
| // No free voice was found, we must steal one | |
| int vmode = synthesizer.getVoiceAllocationMode(); | |
| if (vmode == 1) { | |
| // DLS Static Voice Allocation | |
| // * priority ( 10, 1-9, 11-16) | |
| // Search for channel to steal from | |
| int steal_channel = channel; | |
| for (int j = 0; j < voices.length; j++) { | |
| if (voices[j].stealer_channel == null) { | |
| if (steal_channel == 9) { | |
| steal_channel = voices[j].channel; | |
| } else { | |
| if (voices[j].channel != 9) { | |
| if (voices[j].channel > steal_channel) | |
| steal_channel = voices[j].channel; | |
| } | |
| } | |
| } | |
| } | |
| int voiceNo = -1; | |
| SoftVoice v = null; | |
| // Search for oldest voice in off state on steal_channel | |
| for (int j = 0; j < voices.length; j++) { | |
| if (voices[j].channel == steal_channel) { | |
| if (voices[j].stealer_channel == null && !voices[j].on) { | |
| if (v == null) { | |
| v = voices[j]; | |
| voiceNo = j; | |
| } | |
| if (voices[j].voiceID < v.voiceID) { | |
| v = voices[j]; | |
| voiceNo = j; | |
| } | |
| } | |
| } | |
| } | |
| // Search for oldest voice in on state on steal_channel | |
| if (voiceNo == -1) { | |
| for (int j = 0; j < voices.length; j++) { | |
| if (voices[j].channel == steal_channel) { | |
| if (voices[j].stealer_channel == null) { | |
| if (v == null) { | |
| v = voices[j]; | |
| voiceNo = j; | |
| } | |
| if (voices[j].voiceID < v.voiceID) { | |
| v = voices[j]; | |
| voiceNo = j; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return voiceNo; | |
| } else { | |
| // Default Voice Allocation | |
| // * Find voice that is on | |
| // and Find voice which has lowest voiceID ( oldest voice) | |
| // * Or find voice that is off | |
| // and Find voice which has lowest voiceID ( oldest voice) | |
| int voiceNo = -1; | |
| SoftVoice v = null; | |
| // Search for oldest voice in off state | |
| for (int j = 0; j < voices.length; j++) { | |
| if (voices[j].stealer_channel == null && !voices[j].on) { | |
| if (v == null) { | |
| v = voices[j]; | |
| voiceNo = j; | |
| } | |
| if (voices[j].voiceID < v.voiceID) { | |
| v = voices[j]; | |
| voiceNo = j; | |
| } | |
| } | |
| } | |
| // Search for oldest voice in on state | |
| if (voiceNo == -1) { | |
| for (int j = 0; j < voices.length; j++) { | |
| if (voices[j].stealer_channel == null) { | |
| if (v == null) { | |
| v = voices[j]; | |
| voiceNo = j; | |
| } | |
| if (voices[j].voiceID < v.voiceID) { | |
| v = voices[j]; | |
| voiceNo = j; | |
| } | |
| } | |
| } | |
| } | |
| return voiceNo; | |
| } | |
| } | |
| void initVoice(SoftVoice voice, SoftPerformer p, int voiceID, | |
| int noteNumber, int velocity, int delay, ModelConnectionBlock[] connectionBlocks, | |
| ModelChannelMixer channelmixer, boolean releaseTriggered) { | |
| if (voice.active) { | |
| // Voice is active , we must steal the voice | |
| voice.stealer_channel = this; | |
| voice.stealer_performer = p; | |
| voice.stealer_voiceID = voiceID; | |
| voice.stealer_noteNumber = noteNumber; | |
| voice.stealer_velocity = velocity; | |
| voice.stealer_extendedConnectionBlocks = connectionBlocks; | |
| voice.stealer_channelmixer = channelmixer; | |
| voice.stealer_releaseTriggered = releaseTriggered; | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active && voices[i].voiceID == voice.voiceID) | |
| voices[i].soundOff(); | |
| return; | |
| } | |
| voice.extendedConnectionBlocks = connectionBlocks; | |
| voice.channelmixer = channelmixer; | |
| voice.releaseTriggered = releaseTriggered; | |
| voice.voiceID = voiceID; | |
| voice.tuning = tuning; | |
| voice.exclusiveClass = p.exclusiveClass; | |
| voice.softchannel = this; | |
| voice.channel = channel; | |
| voice.bank = bank; | |
| voice.program = program; | |
| voice.instrument = current_instrument; | |
| voice.performer = p; | |
| voice.objects.clear(); | |
| voice.objects.put("midi", co_midi[noteNumber]); | |
| voice.objects.put("midi_cc", co_midi_cc); | |
| voice.objects.put("midi_rpn", co_midi_rpn); | |
| voice.objects.put("midi_nrpn", co_midi_nrpn); | |
| voice.noteOn(noteNumber, velocity, delay); | |
| voice.setMute(mute); | |
| voice.setSoloMute(solomute); | |
| if (releaseTriggered) | |
| return; | |
| if (controller[84] != 0) { | |
| voice.co_noteon_keynumber[0] | |
| = (tuning.getTuning(controller[84]) / 100.0) | |
| * (1f / 128f); | |
| voice.portamento = true; | |
| controlChange(84, 0); | |
| } else if (portamento) { | |
| if (mono) { | |
| if (portamento_lastnote[0] != -1) { | |
| voice.co_noteon_keynumber[0] | |
| = (tuning.getTuning(portamento_lastnote[0]) / 100.0) | |
| * (1f / 128f); | |
| voice.portamento = true; | |
| controlChange(84, 0); | |
| } | |
| portamento_lastnote[0] = noteNumber; | |
| } else { | |
| if (portamento_lastnote_ix != 0) { | |
| portamento_lastnote_ix--; | |
| voice.co_noteon_keynumber[0] | |
| = (tuning.getTuning( | |
| portamento_lastnote[portamento_lastnote_ix]) | |
| / 100.0) | |
| * (1f / 128f); | |
| voice.portamento = true; | |
| } | |
| } | |
| } | |
| } | |
| @Override | |
| public void noteOn(int noteNumber, int velocity) { | |
| noteOn(noteNumber, velocity, 0); | |
| } | |
| /* A special noteOn with delay parameter, which is used to | |
| * start note within control buffers. | |
| */ | |
| void noteOn(int noteNumber, int velocity, int delay) { | |
| noteNumber = restrict7Bit(noteNumber); | |
| velocity = restrict7Bit(velocity); | |
| noteOn_internal(noteNumber, velocity, delay); | |
| if (current_mixer != null) | |
| current_mixer.noteOn(noteNumber, velocity); | |
| } | |
| private void noteOn_internal(int noteNumber, int velocity, int delay) { | |
| if (velocity == 0) { | |
| noteOff_internal(noteNumber, 64); | |
| return; | |
| } | |
| synchronized (control_mutex) { | |
| if (sustain) { | |
| sustain = false; | |
| for (int i = 0; i < voices.length; i++) { | |
| if ((voices[i].sustain || voices[i].on) | |
| && voices[i].channel == channel && voices[i].active | |
| && voices[i].note == noteNumber) { | |
| voices[i].sustain = false; | |
| voices[i].on = true; | |
| voices[i].noteOff(0); | |
| } | |
| } | |
| sustain = true; | |
| } | |
| mainmixer.activity(); | |
| if (mono) { | |
| if (portamento) { | |
| boolean n_found = false; | |
| for (int i = 0; i < voices.length; i++) { | |
| if (voices[i].on && voices[i].channel == channel | |
| && voices[i].active | |
| && voices[i].releaseTriggered == false) { | |
| voices[i].portamento = true; | |
| voices[i].setNote(noteNumber); | |
| n_found = true; | |
| } | |
| } | |
| if (n_found) { | |
| portamento_lastnote[0] = noteNumber; | |
| return; | |
| } | |
| } | |
| if (controller[84] != 0) { | |
| boolean n_found = false; | |
| for (int i = 0; i < voices.length; i++) { | |
| if (voices[i].on && voices[i].channel == channel | |
| && voices[i].active | |
| && voices[i].note == controller[84] | |
| && voices[i].releaseTriggered == false) { | |
| voices[i].portamento = true; | |
| voices[i].setNote(noteNumber); | |
| n_found = true; | |
| } | |
| } | |
| controlChange(84, 0); | |
| if (n_found) | |
| return; | |
| } | |
| } | |
| if (mono) | |
| allNotesOff(); | |
| if (current_instrument == null) { | |
| current_instrument | |
| = synthesizer.findInstrument(program, bank, channel); | |
| if (current_instrument == null) | |
| return; | |
| if (current_mixer != null) | |
| mainmixer.stopMixer(current_mixer); | |
| current_mixer = current_instrument.getSourceInstrument() | |
| .getChannelMixer(this, synthesizer.getFormat()); | |
| if (current_mixer != null) | |
| mainmixer.registerMixer(current_mixer); | |
| current_director = current_instrument.getDirector(this, this); | |
| applyInstrumentCustomization(); | |
| } | |
| prevVoiceID = synthesizer.voiceIDCounter++; | |
| firstVoice = true; | |
| voiceNo = 0; | |
| int tunedKey = (int)(Math.round(tuning.getTuning(noteNumber)/100.0)); | |
| play_noteNumber = noteNumber; | |
| play_velocity = velocity; | |
| play_delay = delay; | |
| play_releasetriggered = false; | |
| lastVelocity[noteNumber] = velocity; | |
| current_director.noteOn(tunedKey, velocity); | |
| /* | |
| SoftPerformer[] performers = current_instrument.getPerformers(); | |
| for (int i = 0; i < performers.length; i++) { | |
| SoftPerformer p = performers[i]; | |
| if (p.keyFrom <= tunedKey && p.keyTo >= tunedKey) { | |
| if (p.velFrom <= velocity && p.velTo >= velocity) { | |
| if (firstVoice) { | |
| firstVoice = false; | |
| if (p.exclusiveClass != 0) { | |
| int x = p.exclusiveClass; | |
| for (int j = 0; j < voices.length; j++) { | |
| if (voices[j].active | |
| && voices[j].channel == channel | |
| && voices[j].exclusiveClass == x) { | |
| if (!(p.selfNonExclusive | |
| && voices[j].note == noteNumber)) | |
| voices[j].shutdown(); | |
| } | |
| } | |
| } | |
| } | |
| voiceNo = findFreeVoice(voiceNo); | |
| if (voiceNo == -1) | |
| return; | |
| initVoice(voices[voiceNo], p, prevVoiceID, noteNumber, | |
| velocity); | |
| } | |
| } | |
| } | |
| */ | |
| } | |
| } | |
| @Override | |
| public void noteOff(int noteNumber, int velocity) { | |
| noteNumber = restrict7Bit(noteNumber); | |
| velocity = restrict7Bit(velocity); | |
| noteOff_internal(noteNumber, velocity); | |
| if (current_mixer != null) | |
| current_mixer.noteOff(noteNumber, velocity); | |
| } | |
| private void noteOff_internal(int noteNumber, int velocity) { | |
| synchronized (control_mutex) { | |
| if (!mono) { | |
| if (portamento) { | |
| if (portamento_lastnote_ix != 127) { | |
| portamento_lastnote[portamento_lastnote_ix] = noteNumber; | |
| portamento_lastnote_ix++; | |
| } | |
| } | |
| } | |
| mainmixer.activity(); | |
| for (int i = 0; i < voices.length; i++) { | |
| if (voices[i].on && voices[i].channel == channel | |
| && voices[i].note == noteNumber | |
| && voices[i].releaseTriggered == false) { | |
| voices[i].noteOff(velocity); | |
| } | |
| // We must also check stolen voices | |
| if (voices[i].stealer_channel == this && voices[i].stealer_noteNumber == noteNumber) { | |
| SoftVoice v = voices[i]; | |
| v.stealer_releaseTriggered = false; | |
| v.stealer_channel = null; | |
| v.stealer_performer = null; | |
| v.stealer_voiceID = -1; | |
| v.stealer_noteNumber = 0; | |
| v.stealer_velocity = 0; | |
| v.stealer_extendedConnectionBlocks = null; | |
| v.stealer_channelmixer = null; | |
| } | |
| } | |
| // Try play back note-off triggered voices, | |
| if (current_instrument == null) { | |
| current_instrument | |
| = synthesizer.findInstrument(program, bank, channel); | |
| if (current_instrument == null) | |
| return; | |
| if (current_mixer != null) | |
| mainmixer.stopMixer(current_mixer); | |
| current_mixer = current_instrument.getSourceInstrument() | |
| .getChannelMixer(this, synthesizer.getFormat()); | |
| if (current_mixer != null) | |
| mainmixer.registerMixer(current_mixer); | |
| current_director = current_instrument.getDirector(this, this); | |
| applyInstrumentCustomization(); | |
| } | |
| prevVoiceID = synthesizer.voiceIDCounter++; | |
| firstVoice = true; | |
| voiceNo = 0; | |
| int tunedKey = (int)(Math.round(tuning.getTuning(noteNumber)/100.0)); | |
| play_noteNumber = noteNumber; | |
| play_velocity = lastVelocity[noteNumber]; | |
| play_releasetriggered = true; | |
| play_delay = 0; | |
| current_director.noteOff(tunedKey, velocity); | |
| } | |
| } | |
| private final int[] lastVelocity = new int[128]; | |
| private int prevVoiceID; | |
| private boolean firstVoice = true; | |
| private int voiceNo = 0; | |
| private int play_noteNumber = 0; | |
| private int play_velocity = 0; | |
| private int play_delay = 0; | |
| private boolean play_releasetriggered = false; | |
| @Override | |
| public void play(int performerIndex, ModelConnectionBlock[] connectionBlocks) { | |
| int noteNumber = play_noteNumber; | |
| int velocity = play_velocity; | |
| int delay = play_delay; | |
| boolean releasetriggered = play_releasetriggered; | |
| SoftPerformer p = current_instrument.getPerformer(performerIndex); | |
| if (firstVoice) { | |
| firstVoice = false; | |
| if (p.exclusiveClass != 0) { | |
| int x = p.exclusiveClass; | |
| for (int j = 0; j < voices.length; j++) { | |
| if (voices[j].active && voices[j].channel == channel | |
| && voices[j].exclusiveClass == x) { | |
| if (!(p.selfNonExclusive && voices[j].note == noteNumber)) | |
| voices[j].shutdown(); | |
| } | |
| } | |
| } | |
| } | |
| voiceNo = findFreeVoice(voiceNo); | |
| if (voiceNo == -1) | |
| return; | |
| initVoice(voices[voiceNo], p, prevVoiceID, noteNumber, velocity, delay, | |
| connectionBlocks, current_mixer, releasetriggered); | |
| } | |
| @Override | |
| public void noteOff(int noteNumber) { | |
| if(noteNumber < 0 || noteNumber > 127) return; | |
| noteOff_internal(noteNumber, 64); | |
| } | |
| @Override | |
| public void setPolyPressure(int noteNumber, int pressure) { | |
| noteNumber = restrict7Bit(noteNumber); | |
| pressure = restrict7Bit(pressure); | |
| if (current_mixer != null) | |
| current_mixer.setPolyPressure(noteNumber, pressure); | |
| synchronized (control_mutex) { | |
| mainmixer.activity(); | |
| co_midi[noteNumber].get(0, "poly_pressure")[0] = pressure*(1.0/128.0); | |
| polypressure[noteNumber] = pressure; | |
| for (int i = 0; i < voices.length; i++) { | |
| if (voices[i].active && voices[i].note == noteNumber) | |
| voices[i].setPolyPressure(pressure); | |
| } | |
| } | |
| } | |
| @Override | |
| public int getPolyPressure(int noteNumber) { | |
| synchronized (control_mutex) { | |
| return polypressure[noteNumber]; | |
| } | |
| } | |
| @Override | |
| public void setChannelPressure(int pressure) { | |
| pressure = restrict7Bit(pressure); | |
| if (current_mixer != null) | |
| current_mixer.setChannelPressure(pressure); | |
| synchronized (control_mutex) { | |
| mainmixer.activity(); | |
| co_midi_channel_pressure[0] = pressure * (1.0 / 128.0); | |
| channelpressure = pressure; | |
| for (int i = 0; i < voices.length; i++) { | |
| if (voices[i].active) | |
| voices[i].setChannelPressure(pressure); | |
| } | |
| } | |
| } | |
| @Override | |
| public int getChannelPressure() { | |
| synchronized (control_mutex) { | |
| return channelpressure; | |
| } | |
| } | |
| void applyInstrumentCustomization() { | |
| if (cds_control_connections == null | |
| && cds_channelpressure_connections == null | |
| && cds_polypressure_connections == null) { | |
| return; | |
| } | |
| ModelInstrument src_instrument = current_instrument.getSourceInstrument(); | |
| ModelPerformer[] performers = src_instrument.getPerformers(); | |
| ModelPerformer[] new_performers = new ModelPerformer[performers.length]; | |
| for (int i = 0; i < new_performers.length; i++) { | |
| ModelPerformer performer = performers[i]; | |
| ModelPerformer new_performer = new ModelPerformer(); | |
| new_performer.setName(performer.getName()); | |
| new_performer.setExclusiveClass(performer.getExclusiveClass()); | |
| new_performer.setKeyFrom(performer.getKeyFrom()); | |
| new_performer.setKeyTo(performer.getKeyTo()); | |
| new_performer.setVelFrom(performer.getVelFrom()); | |
| new_performer.setVelTo(performer.getVelTo()); | |
| new_performer.getOscillators().addAll(performer.getOscillators()); | |
| new_performer.getConnectionBlocks().addAll( | |
| performer.getConnectionBlocks()); | |
| new_performers[i] = new_performer; | |
| List<ModelConnectionBlock> connblocks = | |
| new_performer.getConnectionBlocks(); | |
| if (cds_control_connections != null) { | |
| String cc = Integer.toString(cds_control_number); | |
| Iterator<ModelConnectionBlock> iter = connblocks.iterator(); | |
| while (iter.hasNext()) { | |
| ModelConnectionBlock conn = iter.next(); | |
| ModelSource[] sources = conn.getSources(); | |
| boolean removeok = false; | |
| if (sources != null) { | |
| for (int j = 0; j < sources.length; j++) { | |
| ModelSource src = sources[j]; | |
| if ("midi_cc".equals(src.getIdentifier().getObject()) | |
| && cc.equals(src.getIdentifier().getVariable())) { | |
| removeok = true; | |
| } | |
| } | |
| } | |
| if (removeok) | |
| iter.remove(); | |
| } | |
| for (int j = 0; j < cds_control_connections.length; j++) | |
| connblocks.add(cds_control_connections[j]); | |
| } | |
| if (cds_polypressure_connections != null) { | |
| Iterator<ModelConnectionBlock> iter = connblocks.iterator(); | |
| while (iter.hasNext()) { | |
| ModelConnectionBlock conn = iter.next(); | |
| ModelSource[] sources = conn.getSources(); | |
| boolean removeok = false; | |
| if (sources != null) { | |
| for (int j = 0; j < sources.length; j++) { | |
| ModelSource src = sources[j]; | |
| if ("midi".equals(src.getIdentifier().getObject()) | |
| && "poly_pressure".equals( | |
| src.getIdentifier().getVariable())) { | |
| removeok = true; | |
| } | |
| } | |
| } | |
| if (removeok) | |
| iter.remove(); | |
| } | |
| for (int j = 0; j < cds_polypressure_connections.length; j++) | |
| connblocks.add(cds_polypressure_connections[j]); | |
| } | |
| if (cds_channelpressure_connections != null) { | |
| Iterator<ModelConnectionBlock> iter = connblocks.iterator(); | |
| while (iter.hasNext()) { | |
| ModelConnectionBlock conn = iter.next(); | |
| ModelSource[] sources = conn.getSources(); | |
| boolean removeok = false; | |
| if (sources != null) { | |
| for (int j = 0; j < sources.length; j++) { | |
| ModelIdentifier srcid = sources[j].getIdentifier(); | |
| if ("midi".equals(srcid.getObject()) && | |
| "channel_pressure".equals(srcid.getVariable())) { | |
| removeok = true; | |
| } | |
| } | |
| } | |
| if (removeok) | |
| iter.remove(); | |
| } | |
| for (int j = 0; j < cds_channelpressure_connections.length; j++) | |
| connblocks.add(cds_channelpressure_connections[j]); | |
| } | |
| } | |
| current_instrument = new SoftInstrument(src_instrument, new_performers); | |
| } | |
| private ModelConnectionBlock[] createModelConnections(ModelIdentifier sid, | |
| int[] destination, int[] range) { | |
| /* | |
| controlled parameter (pp)|range (rr)| Description |Default | |
| -------------------------|----------|-------------------------|------- | |
| 00 Pitch Control | 28H..58H | -24..+24 semitones | 40H | |
| 01 Filter Cutoff Control | 00H..7FH | -9600..+9450 cents | 40H | |
| 02 Amplitude Control | 00H..7FH | 0..(127/64)*100 percent | 40H | |
| 03 LFO Pitch Depth | 00H..7FH | 0..600 cents | 0 | |
| 04 LFO Filter Depth | 00H..7FH | 0..2400 cents | 0 | |
| 05 LFO Amplitude Depth | 00H..7FH | 0..100 percent | 0 | |
| */ | |
| List<ModelConnectionBlock> conns = new ArrayList<>(); | |
| for (int i = 0; i < destination.length; i++) { | |
| int d = destination[i]; | |
| int r = range[i]; | |
| if (d == 0) { | |
| double scale = (r - 64) * 100; | |
| ModelConnectionBlock conn = new ModelConnectionBlock( | |
| new ModelSource(sid, | |
| ModelStandardTransform.DIRECTION_MIN2MAX, | |
| ModelStandardTransform.POLARITY_UNIPOLAR, | |
| ModelStandardTransform.TRANSFORM_LINEAR), | |
| scale, | |
| new ModelDestination( | |
| new ModelIdentifier("osc", "pitch"))); | |
| conns.add(conn); | |
| } | |
| if (d == 1) { | |
| double scale = (r / 64.0 - 1.0) * 9600.0; | |
| ModelConnectionBlock conn; | |
| if (scale > 0) { | |
| conn = new ModelConnectionBlock( | |
| new ModelSource(sid, | |
| ModelStandardTransform.DIRECTION_MAX2MIN, | |
| ModelStandardTransform.POLARITY_UNIPOLAR, | |
| ModelStandardTransform.TRANSFORM_LINEAR), | |
| -scale, | |
| new ModelDestination( | |
| ModelDestination.DESTINATION_FILTER_FREQ)); | |
| } else { | |
| conn = new ModelConnectionBlock( | |
| new ModelSource(sid, | |
| ModelStandardTransform.DIRECTION_MIN2MAX, | |
| ModelStandardTransform.POLARITY_UNIPOLAR, | |
| ModelStandardTransform.TRANSFORM_LINEAR), | |
| scale, | |
| new ModelDestination( | |
| ModelDestination.DESTINATION_FILTER_FREQ)); | |
| } | |
| conns.add(conn); | |
| } | |
| if (d == 2) { | |
| final double scale = (r / 64.0); | |
| ModelTransform mt = new ModelTransform() { | |
| double s = scale; | |
| @Override | |
| public double transform(double value) { | |
| if (s < 1) | |
| value = s + (value * (1.0 - s)); | |
| else if (s > 1) | |
| value = 1 + (value * (s - 1.0)); | |
| else | |
| return 0; | |
| return -((5.0 / 12.0) / Math.log(10)) * Math.log(value); | |
| } | |
| }; | |
| ModelConnectionBlock conn = new ModelConnectionBlock( | |
| new ModelSource(sid, mt), -960, | |
| new ModelDestination(ModelDestination.DESTINATION_GAIN)); | |
| conns.add(conn); | |
| } | |
| if (d == 3) { | |
| double scale = (r / 64.0 - 1.0) * 9600.0; | |
| ModelConnectionBlock conn = new ModelConnectionBlock( | |
| new ModelSource(ModelSource.SOURCE_LFO1, | |
| ModelStandardTransform.DIRECTION_MIN2MAX, | |
| ModelStandardTransform.POLARITY_BIPOLAR, | |
| ModelStandardTransform.TRANSFORM_LINEAR), | |
| new ModelSource(sid, | |
| ModelStandardTransform.DIRECTION_MIN2MAX, | |
| ModelStandardTransform.POLARITY_UNIPOLAR, | |
| ModelStandardTransform.TRANSFORM_LINEAR), | |
| scale, | |
| new ModelDestination( | |
| ModelDestination.DESTINATION_PITCH)); | |
| conns.add(conn); | |
| } | |
| if (d == 4) { | |
| double scale = (r / 128.0) * 2400.0; | |
| ModelConnectionBlock conn = new ModelConnectionBlock( | |
| new ModelSource(ModelSource.SOURCE_LFO1, | |
| ModelStandardTransform.DIRECTION_MIN2MAX, | |
| ModelStandardTransform.POLARITY_BIPOLAR, | |
| ModelStandardTransform.TRANSFORM_LINEAR), | |
| new ModelSource(sid, | |
| ModelStandardTransform.DIRECTION_MIN2MAX, | |
| ModelStandardTransform.POLARITY_UNIPOLAR, | |
| ModelStandardTransform.TRANSFORM_LINEAR), | |
| scale, | |
| new ModelDestination( | |
| ModelDestination.DESTINATION_FILTER_FREQ)); | |
| conns.add(conn); | |
| } | |
| if (d == 5) { | |
| final double scale = (r / 127.0); | |
| ModelTransform mt = new ModelTransform() { | |
| double s = scale; | |
| @Override | |
| public double transform(double value) { | |
| return -((5.0 / 12.0) / Math.log(10)) | |
| * Math.log(1 - value * s); | |
| } | |
| }; | |
| ModelConnectionBlock conn = new ModelConnectionBlock( | |
| new ModelSource(ModelSource.SOURCE_LFO1, | |
| ModelStandardTransform.DIRECTION_MIN2MAX, | |
| ModelStandardTransform.POLARITY_UNIPOLAR, | |
| ModelStandardTransform.TRANSFORM_LINEAR), | |
| new ModelSource(sid, mt), | |
| -960, | |
| new ModelDestination( | |
| ModelDestination.DESTINATION_GAIN)); | |
| conns.add(conn); | |
| } | |
| } | |
| return conns.toArray(new ModelConnectionBlock[conns.size()]); | |
| } | |
| public void mapPolyPressureToDestination(int[] destination, int[] range) { | |
| current_instrument = null; | |
| if (destination.length == 0) { | |
| cds_polypressure_connections = null; | |
| return; | |
| } | |
| cds_polypressure_connections | |
| = createModelConnections( | |
| new ModelIdentifier("midi", "poly_pressure"), | |
| destination, range); | |
| } | |
| public void mapChannelPressureToDestination(int[] destination, int[] range) { | |
| current_instrument = null; | |
| if (destination.length == 0) { | |
| cds_channelpressure_connections = null; | |
| return; | |
| } | |
| cds_channelpressure_connections | |
| = createModelConnections( | |
| new ModelIdentifier("midi", "channel_pressure"), | |
| destination, range); | |
| } | |
| public void mapControlToDestination(int control, int[] destination, int[] range) { | |
| if (!((control >= 0x01 && control <= 0x1F) | |
| || (control >= 0x40 && control <= 0x5F))) { | |
| cds_control_connections = null; | |
| return; | |
| } | |
| current_instrument = null; | |
| cds_control_number = control; | |
| if (destination.length == 0) { | |
| cds_control_connections = null; | |
| return; | |
| } | |
| cds_control_connections | |
| = createModelConnections( | |
| new ModelIdentifier("midi_cc", Integer.toString(control)), | |
| destination, range); | |
| } | |
| public void controlChangePerNote(int noteNumber, int controller, int value) { | |
| /* | |
| CC# | nn | Name | vv | default | description | |
| -----|------|-------------------------|----------------|------------|------------------------------- | |
| 7 |07H |Note Volume |00H-40H-7FH |40H |0-100-(127/64)*100(%)(Relative) | |
| 10 |0AH |*Pan |00H-7FH absolute|Preset Value|Left-Center-Right (absolute) | |
| 33-63|21-3FH|LSB for |01H-1FH | | | |
| 71 |47H |Timbre/Harmonic Intensity|00H-40H-7FH |40H (???) | | |
| 72 |48H |Release Time |00H-40H-7FH |40H (???) | | |
| 73 |49H |Attack Time |00H-40H-7FH |40H (???) | | |
| 74 |4AH |Brightness |00H-40H-7FH |40H (???) | | |
| 75 |4BH |Decay Time |00H-40H-7FH |40H (???) | | |
| 76 |4CH |Vibrato Rate |00H-40H-7FH |40H (???) | | |
| 77 |4DH |Vibrato Depth |00H-40H-7FH |40H (???) | | |
| 78 |4EH |Vibrato Delay |00H-40H-7FH |40H (???) | | |
| 91 |5BH |*Reverb Send |00H-7FH absolute|Preset Value|Left-Center-Right (absolute) | |
| 93 |5DH |*Chorus Send |00H-7FH absolute|Preset Value|Left-Center-Right (absolute) | |
| 120 |78H |**Fine Tuning |00H-40H-7FH |40H (???) | | |
| 121 |79H |**Coarse Tuning |00H-40H-7FH |40H (???) | | |
| */ | |
| if (keybasedcontroller_active == null) { | |
| keybasedcontroller_active = new boolean[128][]; | |
| keybasedcontroller_value = new double[128][]; | |
| } | |
| if (keybasedcontroller_active[noteNumber] == null) { | |
| keybasedcontroller_active[noteNumber] = new boolean[128]; | |
| keybasedcontroller_value[noteNumber] = new double[128]; | |
| } | |
| if (value == -1) { | |
| keybasedcontroller_active[noteNumber][controller] = false; | |
| } else { | |
| keybasedcontroller_active[noteNumber][controller] = true; | |
| keybasedcontroller_value[noteNumber][controller] = value / 128.0; | |
| } | |
| if (controller < 120) { | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active) | |
| voices[i].controlChange(controller, -1); | |
| } else if (controller == 120) { | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active) | |
| voices[i].rpnChange(1, -1); | |
| } else if (controller == 121) { | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active) | |
| voices[i].rpnChange(2, -1); | |
| } | |
| } | |
| public int getControlPerNote(int noteNumber, int controller) { | |
| if (keybasedcontroller_active == null) | |
| return -1; | |
| if (keybasedcontroller_active[noteNumber] == null) | |
| return -1; | |
| if (!keybasedcontroller_active[noteNumber][controller]) | |
| return -1; | |
| return (int)(keybasedcontroller_value[noteNumber][controller] * 128); | |
| } | |
| @Override | |
| public void controlChange(int controller, int value) { | |
| controller = restrict7Bit(controller); | |
| value = restrict7Bit(value); | |
| if (current_mixer != null) | |
| current_mixer.controlChange(controller, value); | |
| synchronized (control_mutex) { | |
| switch (controller) { | |
| /* | |
| Map<String, int[]>co_midi_rpn_rpn_i = new HashMap<String, int[]>(); | |
| Map<String, double[]>co_midi_rpn_rpn = new HashMap<String, double[]>(); | |
| Map<String, int[]>co_midi_nrpn_nrpn_i = new HashMap<String, int[]>(); | |
| Map<String, double[]>co_midi_nrpn_nrpn = new HashMap<String, double[]>(); | |
| */ | |
| case 5: | |
| // This produce asin-like curve | |
| // as described in General Midi Level 2 Specification, page 6 | |
| double x = -Math.asin((value / 128.0) * 2 - 1) / Math.PI + 0.5; | |
| x = Math.pow(100000.0, x) / 100.0; // x is now cent/msec | |
| // Convert x from cent/msec to key/controlbuffertime | |
| x = x / 100.0; // x is now keys/msec | |
| x = x * 1000.0; // x is now keys/sec | |
| x = x / synthesizer.getControlRate(); // x is now keys/controlbuffertime | |
| portamento_time = x; | |
| break; | |
| case 6: | |
| case 38: | |
| case 96: | |
| case 97: | |
| int val = 0; | |
| if (nrpn_control != RPN_NULL_VALUE) { | |
| int[] val_i = co_midi_nrpn_nrpn_i.get(nrpn_control); | |
| if (val_i != null) | |
| val = val_i[0]; | |
| } | |
| if (rpn_control != RPN_NULL_VALUE) { | |
| int[] val_i = co_midi_rpn_rpn_i.get(rpn_control); | |
| if (val_i != null) | |
| val = val_i[0]; | |
| } | |
| if (controller == 6) | |
| val = (val & 127) + (value << 7); | |
| else if (controller == 38) | |
| val = (val & (127 << 7)) + value; | |
| else if (controller == 96 || controller == 97) { | |
| int step = 1; | |
| if (rpn_control == 2 || rpn_control == 3 || rpn_control == 4) | |
| step = 128; | |
| if (controller == 96) | |
| val += step; | |
| if (controller == 97) | |
| val -= step; | |
| } | |
| if (nrpn_control != RPN_NULL_VALUE) | |
| nrpnChange(nrpn_control, val); | |
| if (rpn_control != RPN_NULL_VALUE) | |
| rpnChange(rpn_control, val); | |
| break; | |
| case 64: // Hold1 (Damper) (cc#64) | |
| boolean on = value >= 64; | |
| if (sustain != on) { | |
| sustain = on; | |
| if (!on) { | |
| for (int i = 0; i < voices.length; i++) { | |
| if (voices[i].active && voices[i].sustain && | |
| voices[i].channel == channel) { | |
| voices[i].sustain = false; | |
| if (!voices[i].on) { | |
| voices[i].on = true; | |
| voices[i].noteOff(0); | |
| } | |
| } | |
| } | |
| } else { | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active && voices[i].channel == channel) | |
| voices[i].redamp(); | |
| } | |
| } | |
| break; | |
| case 65: | |
| //allNotesOff(); | |
| portamento = value >= 64; | |
| portamento_lastnote[0] = -1; | |
| /* | |
| for (int i = 0; i < portamento_lastnote.length; i++) | |
| portamento_lastnote[i] = -1; | |
| */ | |
| portamento_lastnote_ix = 0; | |
| break; | |
| case 66: // Sostenuto (cc#66) | |
| on = value >= 64; | |
| if (on) { | |
| for (int i = 0; i < voices.length; i++) { | |
| if (voices[i].active && voices[i].on && | |
| voices[i].channel == channel) { | |
| voices[i].sostenuto = true; | |
| } | |
| } | |
| } | |
| if (!on) { | |
| for (int i = 0; i < voices.length; i++) { | |
| if (voices[i].active && voices[i].sostenuto && | |
| voices[i].channel == channel) { | |
| voices[i].sostenuto = false; | |
| if (!voices[i].on) { | |
| voices[i].on = true; | |
| voices[i].noteOff(0); | |
| } | |
| } | |
| } | |
| } | |
| break; | |
| case 98: | |
| nrpn_control = (nrpn_control & (127 << 7)) + value; | |
| rpn_control = RPN_NULL_VALUE; | |
| break; | |
| case 99: | |
| nrpn_control = (nrpn_control & 127) + (value << 7); | |
| rpn_control = RPN_NULL_VALUE; | |
| break; | |
| case 100: | |
| rpn_control = (rpn_control & (127 << 7)) + value; | |
| nrpn_control = RPN_NULL_VALUE; | |
| break; | |
| case 101: | |
| rpn_control = (rpn_control & 127) + (value << 7); | |
| nrpn_control = RPN_NULL_VALUE; | |
| break; | |
| case 120: | |
| allSoundOff(); | |
| break; | |
| case 121: | |
| resetAllControllers(value == 127); | |
| break; | |
| case 122: | |
| localControl(value >= 64); | |
| break; | |
| case 123: | |
| allNotesOff(); | |
| break; | |
| case 124: | |
| setOmni(false); | |
| break; | |
| case 125: | |
| setOmni(true); | |
| break; | |
| case 126: | |
| if (value == 1) | |
| setMono(true); | |
| break; | |
| case 127: | |
| setMono(false); | |
| break; | |
| default: | |
| break; | |
| } | |
| co_midi_cc_cc[controller][0] = value * (1.0 / 128.0); | |
| if (controller == 0x00) { | |
| bank = /*(bank & 127) +*/ (value << 7); | |
| return; | |
| } | |
| if (controller == 0x20) { | |
| bank = (bank & (127 << 7)) + value; | |
| return; | |
| } | |
| this.controller[controller] = value; | |
| if(controller < 0x20) | |
| this.controller[controller + 0x20] = 0; | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active) | |
| voices[i].controlChange(controller, value); | |
| } | |
| } | |
| @Override | |
| public int getController(int controller) { | |
| synchronized (control_mutex) { | |
| // Should only return lower 7 bits, | |
| // even when controller is "boosted" higher. | |
| return this.controller[controller] & 127; | |
| } | |
| } | |
| public void tuningChange(int program) { | |
| tuningChange(0, program); | |
| } | |
| public void tuningChange(int bank, int program) { | |
| synchronized (control_mutex) { | |
| tuning = synthesizer.getTuning(new Patch(bank, program)); | |
| } | |
| } | |
| @Override | |
| public void programChange(int program) { | |
| programChange(bank, program); | |
| } | |
| @Override | |
| public void programChange(int bank, int program) { | |
| bank = restrict14Bit(bank); | |
| program = restrict7Bit(program); | |
| synchronized (control_mutex) { | |
| mainmixer.activity(); | |
| if(this.bank != bank || this.program != program) | |
| { | |
| this.bank = bank; | |
| this.program = program; | |
| current_instrument = null; | |
| } | |
| } | |
| } | |
| @Override | |
| public int getProgram() { | |
| synchronized (control_mutex) { | |
| return program; | |
| } | |
| } | |
| @Override | |
| public void setPitchBend(int bend) { | |
| bend = restrict14Bit(bend); | |
| if (current_mixer != null) | |
| current_mixer.setPitchBend(bend); | |
| synchronized (control_mutex) { | |
| mainmixer.activity(); | |
| co_midi_pitch[0] = bend * (1.0 / 16384.0); | |
| pitchbend = bend; | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active) | |
| voices[i].setPitchBend(bend); | |
| } | |
| } | |
| @Override | |
| public int getPitchBend() { | |
| synchronized (control_mutex) { | |
| return pitchbend; | |
| } | |
| } | |
| public void nrpnChange(int controller, int value) { | |
| /* | |
| System.out.println("(" + channel + ").nrpnChange(" | |
| + Integer.toHexString(controller >> 7) | |
| + " " + Integer.toHexString(controller & 127) | |
| + ", " + Integer.toHexString(value >> 7) | |
| + " " + Integer.toHexString(value & 127) + ")"); | |
| */ | |
| if (synthesizer.getGeneralMidiMode() == 0) { | |
| if (controller == (0x01 << 7) + (0x08)) // Vibrato Rate | |
| controlChange(76, value >> 7); | |
| if (controller == (0x01 << 7) + (0x09)) // Vibrato Depth | |
| controlChange(77, value >> 7); | |
| if (controller == (0x01 << 7) + (0x0A)) // Vibrato Delay | |
| controlChange(78, value >> 7); | |
| if (controller == (0x01 << 7) + (0x20)) // Brightness | |
| controlChange(74, value >> 7); | |
| if (controller == (0x01 << 7) + (0x21)) // Filter Resonance | |
| controlChange(71, value >> 7); | |
| if (controller == (0x01 << 7) + (0x63)) // Attack Time | |
| controlChange(73, value >> 7); | |
| if (controller == (0x01 << 7) + (0x64)) // Decay Time | |
| controlChange(75, value >> 7); | |
| if (controller == (0x01 << 7) + (0x66)) // Release Time | |
| controlChange(72, value >> 7); | |
| if (controller >> 7 == 0x18) // Pitch coarse | |
| controlChangePerNote(controller % 128, 120, value >> 7); | |
| if (controller >> 7 == 0x1A) // Volume | |
| controlChangePerNote(controller % 128, 7, value >> 7); | |
| if (controller >> 7 == 0x1C) // Panpot | |
| controlChangePerNote(controller % 128, 10, value >> 7); | |
| if (controller >> 7 == 0x1D) // Reverb | |
| controlChangePerNote(controller % 128, 91, value >> 7); | |
| if (controller >> 7 == 0x1E) // Chorus | |
| controlChangePerNote(controller % 128, 93, value >> 7); | |
| } | |
| int[] val_i = co_midi_nrpn_nrpn_i.get(controller); | |
| double[] val_d = co_midi_nrpn_nrpn.get(controller); | |
| if (val_i == null) { | |
| val_i = new int[1]; | |
| co_midi_nrpn_nrpn_i.put(controller, val_i); | |
| } | |
| if (val_d == null) { | |
| val_d = new double[1]; | |
| co_midi_nrpn_nrpn.put(controller, val_d); | |
| } | |
| val_i[0] = value; | |
| val_d[0] = val_i[0] * (1.0 / 16384.0); | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active) | |
| voices[i].nrpnChange(controller, val_i[0]); | |
| } | |
| public void rpnChange(int controller, int value) { | |
| /* | |
| System.out.println("(" + channel + ").rpnChange(" | |
| + Integer.toHexString(controller >> 7) | |
| + " " + Integer.toHexString(controller & 127) | |
| + ", " + Integer.toHexString(value >> 7) | |
| + " " + Integer.toHexString(value & 127) + ")"); | |
| */ | |
| if (controller == 3) { | |
| tuning_program = (value >> 7) & 127; | |
| tuningChange(tuning_bank, tuning_program); | |
| } | |
| if (controller == 4) { | |
| tuning_bank = (value >> 7) & 127; | |
| } | |
| int[] val_i = co_midi_rpn_rpn_i.get(controller); | |
| double[] val_d = co_midi_rpn_rpn.get(controller); | |
| if (val_i == null) { | |
| val_i = new int[1]; | |
| co_midi_rpn_rpn_i.put(controller, val_i); | |
| } | |
| if (val_d == null) { | |
| val_d = new double[1]; | |
| co_midi_rpn_rpn.put(controller, val_d); | |
| } | |
| val_i[0] = value; | |
| val_d[0] = val_i[0] * (1.0 / 16384.0); | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active) | |
| voices[i].rpnChange(controller, val_i[0]); | |
| } | |
| @Override | |
| public void resetAllControllers() { | |
| resetAllControllers(false); | |
| } | |
| public void resetAllControllers(boolean allControls) { | |
| synchronized (control_mutex) { | |
| mainmixer.activity(); | |
| for (int i = 0; i < 128; i++) { | |
| setPolyPressure(i, 0); | |
| } | |
| setChannelPressure(0); | |
| setPitchBend(8192); | |
| for (int i = 0; i < 128; i++) { | |
| if (!dontResetControls[i]) | |
| controlChange(i, 0); | |
| } | |
| controlChange(71, 64); // Filter Resonance | |
| controlChange(72, 64); // Release Time | |
| controlChange(73, 64); // Attack Time | |
| controlChange(74, 64); // Brightness | |
| controlChange(75, 64); // Decay Time | |
| controlChange(76, 64); // Vibrato Rate | |
| controlChange(77, 64); // Vibrato Depth | |
| controlChange(78, 64); // Vibrato Delay | |
| controlChange(8, 64); // Balance | |
| controlChange(11, 127); // Expression | |
| controlChange(98, 127); // NRPN Null | |
| controlChange(99, 127); // NRPN Null | |
| controlChange(100, 127); // RPN = Null | |
| controlChange(101, 127); // RPN = Null | |
| // see DLS 2.1 (Power-on Default Values) | |
| if (allControls) { | |
| keybasedcontroller_active = null; | |
| keybasedcontroller_value = null; | |
| controlChange(7, 100); // Volume | |
| controlChange(10, 64); // Pan | |
| controlChange(91, 40); // Reverb | |
| for (int controller : co_midi_rpn_rpn.keySet()) { | |
| // don't reset tuning settings | |
| if (controller != 3 && controller != 4) | |
| rpnChange(controller, 0); | |
| } | |
| for (int controller : co_midi_nrpn_nrpn.keySet()) | |
| nrpnChange(controller, 0); | |
| rpnChange(0, 2 << 7); // Pitch Bend sensitivity | |
| rpnChange(1, 64 << 7); // Channel fine tuning | |
| rpnChange(2, 64 << 7); // Channel Coarse Tuning | |
| rpnChange(5, 64); // Modulation Depth, +/- 50 cent | |
| tuning_bank = 0; | |
| tuning_program = 0; | |
| tuning = new SoftTuning(); | |
| } | |
| } | |
| } | |
| @Override | |
| public void allNotesOff() { | |
| if (current_mixer != null) | |
| current_mixer.allNotesOff(); | |
| synchronized (control_mutex) { | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].on && voices[i].channel == channel | |
| && voices[i].releaseTriggered == false) { | |
| voices[i].noteOff(0); | |
| } | |
| } | |
| } | |
| @Override | |
| public void allSoundOff() { | |
| if (current_mixer != null) | |
| current_mixer.allSoundOff(); | |
| synchronized (control_mutex) { | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].on && voices[i].channel == channel) | |
| voices[i].soundOff(); | |
| } | |
| } | |
| @Override | |
| public boolean localControl(boolean on) { | |
| return false; | |
| } | |
| @Override | |
| public void setMono(boolean on) { | |
| if (current_mixer != null) | |
| current_mixer.setMono(on); | |
| synchronized (control_mutex) { | |
| allNotesOff(); | |
| mono = on; | |
| } | |
| } | |
| @Override | |
| public boolean getMono() { | |
| synchronized (control_mutex) { | |
| return mono; | |
| } | |
| } | |
| @Override | |
| public void setOmni(boolean on) { | |
| if (current_mixer != null) | |
| current_mixer.setOmni(on); | |
| allNotesOff(); | |
| // Omni is not supported by GM2 | |
| } | |
| @Override | |
| public boolean getOmni() { | |
| return false; | |
| } | |
| @Override | |
| public void setMute(boolean mute) { | |
| if (current_mixer != null) | |
| current_mixer.setMute(mute); | |
| synchronized (control_mutex) { | |
| this.mute = mute; | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active && voices[i].channel == channel) | |
| voices[i].setMute(mute); | |
| } | |
| } | |
| @Override | |
| public boolean getMute() { | |
| synchronized (control_mutex) { | |
| return mute; | |
| } | |
| } | |
| @Override | |
| public void setSolo(boolean soloState) { | |
| if (current_mixer != null) | |
| current_mixer.setSolo(soloState); | |
| synchronized (control_mutex) { | |
| this.solo = soloState; | |
| boolean soloinuse = false; | |
| for (SoftChannel c : synthesizer.channels) { | |
| if (c.solo) { | |
| soloinuse = true; | |
| break; | |
| } | |
| } | |
| if (!soloinuse) { | |
| for (SoftChannel c : synthesizer.channels) | |
| c.setSoloMute(false); | |
| return; | |
| } | |
| for (SoftChannel c : synthesizer.channels) | |
| c.setSoloMute(!c.solo); | |
| } | |
| } | |
| private void setSoloMute(boolean mute) { | |
| synchronized (control_mutex) { | |
| if (solomute == mute) | |
| return; | |
| this.solomute = mute; | |
| for (int i = 0; i < voices.length; i++) | |
| if (voices[i].active && voices[i].channel == channel) | |
| voices[i].setSoloMute(solomute); | |
| } | |
| } | |
| @Override | |
| public boolean getSolo() { | |
| synchronized (control_mutex) { | |
| return solo; | |
| } | |
| } | |
| } |