UHU-StartseiteInformatikTechnikenjavamidi
SonicCellLive



package midi;

import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiUnavailableException;
import org.json.JSONException;
import org.json.JSONObject;

/**
 *
 * @author Robert Rothhardt
 * @version 0.016
 * Grundidee ist eine Steuerung der temporären Performance über einfache Zeichenketten.
 * Eine Zeichenkette kann bis zu 4 Zonen enthalten, wie sie typischerweise von Masterkeyboards
 * zur Verfügung gestellt werden. Damit wird es möglich, das SonicCell - parallel zur Midiansteuerung
 * mit einem einfachen Keyboard - zusätzlich vom Computer anzusteuern.
 * Die Zeichenketten sind so aufgebaut, dass sie selbsterklärend sind. Beispiel:
 * "patch:'DPiano', keyrange:'C-1C5', switch:'x'|patch:'Organ', keyrange:'C#5G9'" 
 * steht für zwei Zonen: Zone 1 schaltet auf DX-EPiano und soll als 
 * höchste Note C5 enthalten, Zone 2 steht dann ab C#5 mit dem Standardorgelsound zur Verfügung...
 * Benötigt "soniccell.properties" für die Zuordnung symbolischer Namen auf SonicCell-Patches
 */
public class SonicCellLive extends SonicCell implements LiveDevice{
  public static final String PART     = "part";
  public static final String KEYRANGE = "keyrange";
  public static final String SWITCH   = "switch";
  public static final String MUTE     = "mute";
  public static final String LEVEL    = "level";
  public static final String SHIFT    = "shift";
  public static final String HOLD     = "hold";
  
  private Map<String,String> µSoundTable = new HashMap<>();
  private Map<String,Integer> µPartTable = new HashMap<>();
  
  public SonicCellLive(){
    super();
    Properties p = new Properties();
    try {
      p.load(new FileReader("soniccell.properties"));
    } catch (IOException ex) {
      System.err.append("soniccell.properties nicht gefunden");
    }
    for (Entry it:p.entrySet()){
      String[] values = it .getValue().toString().split(",");
      µSoundTable.put(it.getKey().toString(), values[1]);
      µPartTable.put(it.getKey().toString(), Integer.parseInt(values[0]));
    }
  }
  
  private int noteSymbolToID(char sym){
    switch(Character.toUpperCase(sym)){
      case 'C': return 0;
      case 'D': return 2;
      case 'E': return 4;
      case 'F': return 5;
      case 'G': return 7;
      case 'A': return 9;
      case 'B': case 'H': return 11;
      default : return Integer.MAX_VALUE;
    }
  }
  private int rangeNote(String note){
    int offset  = note.charAt(1)=='#'?1:0;
    boolean neg = offset==0?note.charAt(1)=='-':note.charAt(2)=='-';
    int octave  = neg?-1:note.charAt(note.length()-1)-48;
    int nnote   = noteSymbolToID(note.charAt(0));
    return (octave+1)*12+nnote+offset;
  }
  /**
   * Analysiert die einzelnen Zonen und übersetzt deren Textdarstellung in handfeste zu sendende
   * Bytes.
   * @param zones Ein Feld mit den Zeichenketten der jeweiligen Zonenbeschreibungen
   * @return die zu sendende Midi-Sequenz
   */
  @Override public List<Integer> translate(String[] zones) {
    List<Integer> result = new ArrayList<>();
    for (int i=0; i<zones.length; i++){
      try {
        JSONObject jo = new JSONObject('{'+zones[i]+'}');
        String soundName = jo.getString(PART);
        if (soundName!=null){
          String sonicName = µSoundTable.get(soundName);
          Integer partID   = µPartTable.get(soundName);
          if (sonicName!=null&&partID!=null){
            if (jo.has(SWITCH)){ // Einschalten und Sound einstellen...
              boolean on = jo.getString(SWITCH).charAt(0)=='x';
              result.addAll(setPartSwitch(partID,on));
              if (on) result.addAll(setPartProgram(partID, sonicName)); // patch einstellen
            }
            if (jo.has(MUTE)) result.addAll(setPartMute(partID,jo.getString(MUTE).charAt(0)=='x'));
            String keyRange = jo.optString(KEYRANGE); // keyrange einlesen
            if (keyRange!=null&&keyRange.length()>=4){
              int p = 1; while (!"0123456789".contains(keyRange.charAt(p)+"")) p++; // Mittelposition zwischen lo und hi
              int lo = rangeNote(keyRange.substring(0,p+1));
              int hi = rangeNote(keyRange.substring(p+1));  
              result.addAll(setPartKeyRange(partID,lo,hi));
            }
            if (jo.has(SHIFT)) result.addAll(setPartShift(partID,jo.getInt(SHIFT)));
            if (jo.has(HOLD))  result.addAll(setPartHold(partID,jo.getString(HOLD).charAt(0)=='x'));
            if (jo.has(LEVEL)) result.addAll(setPartLevelInTempPatch(partID,jo.getInt(LEVEL)));
          } else System.err.println(soundName+": soundName nicht in SonicCell() gefunden");
        } else System.err.print("'part:' in JSON-Object nicht gefunden");
      } catch (JSONException ex) {
        System.err.println(ex.getLocalizedMessage());
      }
    }
    return result;
  }

  /**
   * Nimmt eine zentrale Zeichenkette auf, zerlegt sie anhand von "|" in verschiedene Zonen und
   * analysiert die einzelnen Zonen. Das Ergebnis ist eine Byte-Sequenz, die an den soniccell
   * geschickt werden kann um diese Konfiguration dort einzustellen.
   * @param soundName Gesamtkonfiguration
   * @return Midi-Bytesequenz die an das Gerät geschickt werden kann.
   */
  @Override public List<Integer> translateConfiguration(String soundName) {
    return translate(soundName.split("\\|"));
  }

  @Override public List<Integer> resetAllPartBasics() {
    return Arrays.asList(new Integer[]{0xCF,0x02}); // auf <performance zwei schalten...
  }
  
  
  public static void main(String[] args) throws MidiUnavailableException, InvalidMidiDataException, InterruptedException{
    SonicCellLive sonicCell = new SonicCellLive();
    MidiBase.init();
    MidiBase.setDevice("1:Roland SonicCell:External MIDI Port (5.10)");
    // MidiBase.send(sonicCell.resetAllPartBasics());
    MidiBase.send(sonicCell.translateConfiguration(
      "part:'Strings', switch:'-'"
    ));
  }


}


optimal sichtbar mit Firefox Formeln mit asciimath Druckversion