UHU-StartseiteInformatikTechnikenjavamidi
SonicCell



package midi;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiUnavailableException;

/**
 *
 * @author Robert Rothhardt
 * Nützliches bezüglich der Midi-USB-Verbindung zum SoniCell
 * @version 0.018
 * @serial 1968
 */
public class SonicCell {
  private Map<String,Integer[]>soundLibrary = new HashMap<>();
  
  private Integer[] tempPatchAddress(int part){
    switch (part){
      case  1: return new Integer[]{0x11,0x20,0,0};
      case  2: return new Integer[]{0x11,0x40,0,0};
      case  3: return new Integer[]{0x11,0x60,0,0};
      case  4: return new Integer[]{0x12,0x00,0,0};
      case  5: return new Integer[]{0x12,0x20,0,0};
      case  6: return new Integer[]{0x12,0x40,0,0};
      case  7: return new Integer[]{0x12,0x50,0,0};
      case  8: return new Integer[]{0x13,0x00,0,0};
      case  9: return new Integer[]{0x13,0x20,0,0};
      case 10: return new Integer[]{0x13,0x40,0,0};
      case 11: return new Integer[]{0x13,0x60,0,0};
      case 12: return new Integer[]{0x14,0x00,0,0};
      case 13: return new Integer[]{0x14,0x20,0,0};
      case 14: return new Integer[]{0x14,0x40,0,0};
      case 15: return new Integer[]{0x14,0x60,0,0};
      default: return new Integer[]{0x11,0x00,0,0};
    }
  }
  private void initSoundLibrary(){
    soundLibrary = SonicCellConstants.NameToMsbLsbPrg();
  }
  /**
   * Berechnet die Roland-Checksumme
   * @param sequence SysX-Daten
   * @param offset   Start für die Checksummenberechnung
   * @return         Checksumme
   */
  private int checkSum(List<Integer> sequence, int offset){
    int sum = 0;
    for (int i=offset; i<sequence.size(); i++){
      sum += sequence.get(i);
      sum %= 128;
    }
    return 128-sum;
  }
  
  /**
   * Baut eine SyxX-Nachricht für den SonicCell zusammen
   * @param address Startadresse
   * @param data    Daten, die übertragen werden sollen
   * @return int-Liste mit den fertigen Daten inklusive Header und checkSum
   */
  protected List<Integer> createSysX(Integer[] address, Integer[] data){
    List<Integer> result = new ArrayList<>();
    Integer[] starter = {0xF0,0x41,0x10,0x00,0x00,0x25,0x12};
    result.addAll(Arrays.asList(starter));
    result.addAll(Arrays.asList(address));
    result.addAll(Arrays.asList(data));
    result.add(checkSum(result,starter.length));
    result.add(0xF7);
    return result;
  }
  
  public SonicCell(){
    initSoundLibrary();
  }
  
  /**
   * Gibt eine Map zurück, in der alle (für mich wichtigen) Sounds symbolisch gespeichert sind.
   * @return Map
   */
  public Map<String,Integer[]> getSoundLibrary(){
    return soundLibrary;
  }
  /**
   * Setzt die Performance so, dass alle Parts ausgeschaltet sind und auf Midikanal 1 stehen
   * @return  Sequenz, die alle nötigen zu sendenden Bytes enthält
   */
  public List<Integer> resetAllPartBasics(){
    List<Integer> sequence = new ArrayList<>();
    for (int i=0; i<16; i++){
      sequence.addAll(createSysX(new Integer[]{0x10,00,0x20+i,0x00},new Integer[]{0,0}));
      sequence.addAll(setPartKeyRange(i,0,127));
      sequence.addAll(setPartMute(i,false));
      sequence.addAll(setPartShift(i,0));
    }
    return sequence; // ProgramChange on Channel 16 to 0
  }
  
  /**
   * Setzt den Sound für einen Part der temporären Performance
   * @param part Partnummer 0..15
   * @param msb  msb wie in der Patchlist beschrieben
   * @param lsb  lsb wie in der Patchlist beschrieben
   * @param prg  Programmnummer (Preset#)-1, wie in der Patchlist beschrieben
   * @return  Sequenz, die alle nötigen zu sendenden Bytes enthält
   */
  public List<Integer> setPartProgram(int part, int msb, int lsb, int prg){
    return createSysX(new Integer[]{0x10,00,0x20+part,0x04},new Integer[]{msb,lsb,prg});
  }
  
  /**
   * Setzte den Sound für einen Part der temporären Performance
   * @param part Partnummer 0..15
   * @param symbolicName der symbolische Name des Sounds, wie er in der Patchlist definiert ist.
   * @return  Sequenz, die alle nötigen zu sendenden Bytes enhält
   */
  public List<Integer> setPartProgram(int part, String symbolicName){
    Integer[] tmp = soundLibrary.get(symbolicName);
    if (tmp==null) return null;
    return setPartProgram(part,tmp[0],tmp[1],tmp[2]);
  }
  
  /**
   * Setzt die untere und obere Note für den Keyrange dieses Parts
   * @param part Partnummer 0..15
   * @param lower tiefste noch zu erklingende Note
   * @param upper höchste noch zu erklingende Note
   * @return  Sequenz, die alle nötigen zu sendenden Bytes enthält
   */
  public List<Integer> setPartKeyRange(int part, int lower, int upper){
    return createSysX(new Integer[]{0x10,00,0x20+part,0x17},new Integer[]{lower,upper});
  }
  /**
   * Setzt den Receive-Switch des Parts
   * @param part  Part dessen Empfang eingestellt werden soll
   * @param state Empfangszustand: true=ein false=aus
   * @return  Sequenz, die alle nötigen zu sendenden Bytes enthält
   */
  public List<Integer> setPartSwitch(int part, boolean state){
    int tmp = state?1:0;
    System.out.println("send switch: "+part+"->"+tmp);
    return createSysX(new Integer[]{0x10,000,0x20+part,0x01},new Integer[]{tmp});
  }
  
  /**
   * Setzt die Midi-kontrollierbare Lautstärke eines Parts
   * @param part  Part dessen Lautstärke eingestellt werden soll
   * @param level Lautstärke im Bereich 0..127
   * @return  Sequenz, die alle nötigen zu sendenden Bytes enthält
   */
  public List<Integer> setPartLevel(int part, int level){
    return createSysX(new Integer[]{0x10,000,0x20+part,0x07},new Integer[]{level});
  }
  
  /**
   * Setzt die End-Lautstärke eines Parts
   * @param part  Part dessen Lautstärke eingestellt werden soll
   * @param level Lautstärke im Bereich 0..127
   * @return  Sequenz, die alle nötigen zu sendenden Bytes enthält
   */
  public List<Integer> setPartOutputLevel(int part, int level){
    return createSysX(new Integer[]{0x10,000,0x20+part,0x1C},new Integer[]{level});
  }
  
  public List<Integer> setPartLevelInTempPatch(int part, int level){
    Integer[] address = tempPatchAddress(part);
    address[3] = 0x0E;
    return createSysX(address,new Integer[]{level});
  }
  
  public List<Integer> setPartHold(int part, boolean receive){
    return createSysX(new Integer[]{0x10,0x00,0x10+part,0x09},new Integer[]{receive?1:0});
  }
  /**
   * Setzt die Verschiebung um bis zu 3 Oktaven nach unten oder oben. Wenn das nicht reicht
   * dann gibt es auch noch CoarseTune...
   * @param part  Part dessen Oktave verschoben werden soll
   * @param shift Anzahl der zu verschiebenden Oktaven: -3..+3
   * @return
   */
  public List<Integer> setPartShift(int part, int shift){
    shift += 64;
    return createSysX(new Integer[]{0x10,000,0x20+part,0x15},new Integer[]{shift});
  }
  
  public List<Integer> setPartMute(int part, boolean mute){
    int tmp = mute?1:0;
    System.out.println("mute switch: "+part+"->"+tmp);
    return createSysX(new Integer[]{0x10,000,0x20+part,0x1B},new Integer[]{tmp});
  }
  
  /**
   * Wechselt das Program auf Kanal "channel"
   * @param channel Midi-Kanal
   * @param msb     Bank-Select msb
   * @param lsb     Bank-Select lsb
   * @param program program für Program-Change
   * @return Sequenz, die alle nötigen zu sendenden Bytes enthält
   */
  public List<Integer> changeProgram(int channel, int msb, int lsb, int program){
    // return Arrays.asList(new Integer[]{0xB0+channel,0,msb,0xB0+channel,32,lsb,0xC0+channel,program}); 
    return Arrays.asList(new Integer[]{0xC0+channel,program}); 
  }
  
  /**
   * Only for test-purposes
   * @param args -- nothing
   * @throws MidiUnavailableException
   * @throws InvalidMidiDataException
   * @throws InterruptedException 
   */
  public static void main(String[] args) throws MidiUnavailableException, InvalidMidiDataException, InterruptedException{
    SonicCell sonicCell = new SonicCell();
    MidiBase.init();
    MidiBase.setDevice("1:Roland SonicCell:External MIDI Port (5.10)");
    // MidiBase.send(sonicCell.resetAllPartBasics());
    MidiBase.send(sonicCell.setPartSwitch(3, false));
  }
  
}


optimal sichtbar mit Firefox Formeln mit asciimath Druckversion