| /* |
| * Copyright 2007 Sun Microsystems, Inc. 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. Sun designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| * CA 95054 USA or visit www.sun.com if you need additional information or |
| * have any questions. |
| */ |
| package com.sun.media.sound; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| 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 class SoftChannel implements MidiChannel, ModelDirectedPlayer { |
| |
| private static boolean[] dontResetControls = new boolean[128]; |
| static { |
| for (int i = 0; i < dontResetControls.length; i++) |
| dontResetControls[i] = false; |
| |
| 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; |
| protected double portamento_time = 1; // keyschanges per control buffer time |
| protected int[] portamento_lastnote = new int[128]; |
| protected 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 Object control_mutex; |
| private int channel; |
| private SoftVoice[] voices; |
| private int bank; |
| private int program; |
| private SoftSynthesizer synthesizer; |
| private SoftMainMixer mainmixer; |
| private int[] polypressure = new int[128]; |
| private int channelpressure = 0; |
| private int[] controller = new int[128]; |
| private int pitchbend; |
| private double[] co_midi_pitch = new double[1]; |
| private double[] co_midi_channel_pressure = new double[1]; |
| protected SoftTuning tuning = new SoftTuning(); |
| protected int tuning_bank = 0; |
| protected int tuning_program = 0; |
| protected SoftInstrument current_instrument = null; |
| protected ModelChannelMixer current_mixer = null; |
| private ModelDirector current_director = null; |
| |
| // Controller Destination Settings |
| protected int cds_control_number = -1; |
| protected ModelConnectionBlock[] cds_control_connections = null; |
| protected ModelConnectionBlock[] cds_channelpressure_connections = null; |
| protected ModelConnectionBlock[] cds_polypressure_connections = null; |
| protected boolean sustain = false; |
| protected boolean[][] keybasedcontroller_active = null; |
| protected 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]; |
| |
| 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 SoftControl[] co_midi = new SoftControl[128]; |
| { |
| for (int i = 0; i < co_midi.length; i++) { |
| co_midi[i] = new MidiControlObject(); |
| } |
| } |
| |
| private double[][] co_midi_cc_cc = new double[128][1]; |
| private SoftControl co_midi_cc = new SoftControl() { |
| double[][] cc = co_midi_cc_cc; |
| 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<Integer, int[]>(); |
| Map<Integer, double[]> co_midi_rpn_rpn = new HashMap<Integer, double[]>(); |
| private SoftControl co_midi_rpn = new SoftControl() { |
| Map<Integer, double[]> rpn = co_midi_rpn_rpn; |
| 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<Integer, int[]>(); |
| Map<Integer, double[]> co_midi_nrpn_nrpn = new HashMap<Integer, double[]>(); |
| private SoftControl co_midi_nrpn = new SoftControl() { |
| Map<Integer, double[]> nrpn = co_midi_nrpn_nrpn; |
| 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; |
| } |
| |
| } |
| |
| protected 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; |
| } |
| } |
| } |
| } |
| |
| 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. |
| */ |
| protected 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); |
| } |
| } |
| } |
| */ |
| } |
| } |
| |
| 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 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; |
| |
| 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.getPerformers()[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); |
| } |
| |
| public void noteOff(int noteNumber) { |
| if(noteNumber < 0 || noteNumber > 127) return; |
| noteOff_internal(noteNumber, 64); |
| } |
| |
| 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); |
| } |
| } |
| } |
| |
| public int getPolyPressure(int noteNumber) { |
| synchronized (control_mutex) { |
| return polypressure[noteNumber]; |
| } |
| } |
| |
| 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); |
| } |
| } |
| } |
| |
| public int getChannelPressure() { |
| synchronized (control_mutex) { |
| return channelpressure; |
| } |
| } |
| |
| protected 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<ModelConnectionBlock>(); |
| |
| 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; |
| 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; |
| 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]; |
| Arrays.fill(keybasedcontroller_active[noteNumber], false); |
| keybasedcontroller_value[noteNumber] = new double[128]; |
| Arrays.fill(keybasedcontroller_value[noteNumber], 0); |
| } |
| |
| 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); |
| } |
| |
| 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); |
| |
| } |
| } |
| |
| 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)); |
| } |
| } |
| |
| public void programChange(int program) { |
| programChange(bank, program); |
| } |
| |
| public void programChange(int bank, int program) { |
| bank = restrict7Bit(bank); |
| program = restrict7Bit(program); |
| synchronized (control_mutex) { |
| mainmixer.activity(); |
| this.bank = bank; |
| this.program = program; |
| current_instrument = null; |
| } |
| } |
| |
| public int getProgram() { |
| synchronized (control_mutex) { |
| return program; |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| 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]); |
| } |
| |
| 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); // Bitch Bend sensitivity |
| rpnChange(1, 64 << 7); // Channel fine tunning |
| rpnChange(2, 64 << 7); // Channel Coarse Tuning |
| rpnChange(5, 64); // Modulation Depth, +/- 50 cent |
| |
| tuning_bank = 0; |
| tuning_program = 0; |
| tuning = new SoftTuning(); |
| |
| } |
| |
| } |
| } |
| |
| 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); |
| } |
| } |
| } |
| |
| 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(); |
| } |
| } |
| |
| public boolean localControl(boolean on) { |
| return false; |
| } |
| |
| public void setMono(boolean on) { |
| if (current_mixer != null) |
| current_mixer.setMono(on); |
| synchronized (control_mutex) { |
| allNotesOff(); |
| mono = on; |
| } |
| } |
| |
| public boolean getMono() { |
| synchronized (control_mutex) { |
| return mono; |
| } |
| } |
| |
| public void setOmni(boolean on) { |
| if (current_mixer != null) |
| current_mixer.setOmni(on); |
| allNotesOff(); |
| // Omni is not supported by GM2 |
| } |
| |
| public boolean getOmni() { |
| return false; |
| } |
| |
| 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); |
| } |
| } |
| |
| public boolean getMute() { |
| synchronized (control_mutex) { |
| return mute; |
| } |
| } |
| |
| 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); |
| } |
| } |
| |
| public boolean getSolo() { |
| synchronized (control_mutex) { |
| return solo; |
| } |
| } |
| } |