/* Nord Modular Midi Protocol 3.03 Library Copyright (C) 2003-2007 Marcus Andersson This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package net.sf.nmedit.jnmprotocol2; import java.util.*; import net.sf.nmedit.jnmprotocol2.MidiException; import net.sf.nmedit.jnmprotocol2.MidiMessage; import net.sf.nmedit.jnmprotocol2.NmProtocolListener; import net.sf.nmedit.jnmprotocol2.PDLData; import net.sf.nmedit.jnmprotocol2.utils.NmCharacter; import net.sf.nmedit.jpdl2.*; import net.sf.nmedit.jpdl2.stream.BitStream; import net.sf.nmedit.jpdl2.stream.IntStream; public class SynthSettingsMessage extends MidiMessage { private BitStream settingsStream; private Map<String, Object> parameterMap; private boolean extendedSettings = false; private static final String[] setting_parameters = { "midiClockSource", "midiVelScaleMin", "ledsActive", "midiVelScaleMax", "midiClockBpm", "localOn", "keyboardMode", "pedalPolarity", "globalSync", "masterTune", "programChangeReceive", "programChangeSend", "knobMode", "midiChannelSlot0", "midiChannelSlot1", "midiChannelSlot2", "midiChannelSlot3" }; private static final String[] extended_parameters = { "slot0Selected", "slot1Selected", "slot2Selected", "slot3Selected", "activeSlot", "slot0VoiceCount", "slot1VoiceCount", "slot2VoiceCount", "slot3VoiceCount" }; private static void append(IntStream dst, int i, Map<String, Object> params) { Object value = params.get(setting_parameters[i]); if (value == null) throw new RuntimeException("parameter missing: "+setting_parameters[i]); if (!(value instanceof Integer)) throw new RuntimeException("parameter not ant int: "+value); dst.append(((Integer)value).intValue()); } public static IntStream createIntStream(Map<String, Object> params) { Object name = params.get("name"); if (name == null || (!(name instanceof String)) || !NmCharacter.isValid((String)name)) throw new RuntimeException("invalid parameter 'name': "+name); IntStream is = new IntStream(); is.append(0x03); // type = 3 <= SynthSettings int i=0; append(is, i++, params); // midiClockSource append(is, i++, params); append(is, i++, params); append(is, i++, params); append(is, i++, params); // midiClockBpm append(is, i++, params); append(is, i++, params); append(is, i++, params); append(is, i++, params); append(is, i++, params); // masterTune append(is, i++, params); append(is, i++, params); append(is, i++, params); // knobMode NmCharacter.appendString(is, (String)name); // String$name append(is, i++, params); // midiChannelsSlotA append(is, i++, params); // midiChannelsSlotB append(is, i++, params); // midiChannelsSlotC append(is, i++, params); // midiChannelsSlotD return is; } public static BitStream createBitStream(IntStream data) { PDLPacketParser patchParser = new PDLPacketParser(PDLData.getPatchDoc()); try { patchParser.parse(data); return patchParser.getBitStream(); } catch (PDLException e) { throw new RuntimeException("could not generate bitstream", e); } } private SynthSettingsMessage() { super(); settingsStream = new BitStream(); addParameter("pid", "data:pid"); set("cc", 0x1c); set("pid", 0); expectsreply = false; // true ??? isreply = true; } public SynthSettingsMessage(PDLPacket packet) throws MidiException { this(); setAll(packet); settingsStream = PatchMessage.getEmbeddedStream(packet); /* packet = packet.getPacket("data:next"); while (packet != null) { settingsStream.append(packet.getVariable("data"), 7); packet = packet.getPacket("next"); } // Remove padding settingsStream.setSize((settingsStream.getSize()/8)*8); */ // parse settings try { getParamMap(); } catch (RuntimeException e) { // TODO do not use RuntimeException in getParamMap() MidiException me = new MidiException(e.getMessage(), MidiException.MIDI_PARSE_ERROR); me.initCause(e); } } public SynthSettingsMessage(Map<String, Object> params) throws MidiException { this(); set("slot", 0); this.parameterMap = params; IntStream is = createIntStream(params); BitStream bs = createBitStream(is); final int first = 1; final int last = 1; // Generate sysex bistream IntStream intStream = new IntStream(); intStream.append(get("cc") + first + 2*last); intStream.append(get("slot")); intStream.append(0x01); // command intStream.append(0x01); // sectionsEnded = 1 // Pad. Extra bits are ignored later. bs.append(0, 6); while (bs.isAvailable(7)) { intStream.append(bs.getInt(7)); } // Generate sysex bitstream settingsStream = getBitStream(intStream); } public Map<String, Object> getParamMap() { if (parameterMap != null) return parameterMap; parameterMap = new HashMap<String, Object>(); PDLPacketParser parser = new PDLPacketParser(PDLData.getPatchDoc()); PDLPacket packet; try { settingsStream.setPosition(0); packet = parser.parse(settingsStream); } catch (PDLException e) { throw new RuntimeException("could not parse settings", e); } finally { settingsStream.setPosition(0); } PDLPacket section = packet.getPacket("section"); int type = section.getVariable("type"); if (type != 0x03) throw new RuntimeException("wrong type: 0x"+Integer.toHexString(type)); PDLPacket data = section.getPacket("data"); for (int i=0;i<setting_parameters.length;i++) { String name = setting_parameters[i]; parameterMap.put(name, data.getVariable(name)); } { // name of the synth String name = NmCharacter.extractName(data.getPacket("name")); parameterMap.put("name", name); } data = data.getPacket("extended"); if (data != null) { extendedSettings = true; for (int i=0;i<extended_parameters.length;i++) { String name = extended_parameters[i]; parameterMap.put(name, data.getVariable(name)); } } return parameterMap; } public boolean containsExtendedSettings() { getParamMap(); // parse data return extendedSettings; } public BitStream getBitStream() throws MidiException { return settingsStream; } public void notifyListener(NmProtocolListener listener) { listener.messageReceived(this); } }