/*
* Created on Feb 2, 2007
*
* Copyright (c) 2007 Jens Gulden
*
* http://www.frinika.com
*
* This file is part of Frinika.
*
* Frinika 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.
* Frinika 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 Frinika; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.frinika.sequencer.midi.sysex;
import com.frinika.sequencer.model.AbstractSysexMacro;
/**
* Abstract superclass with tools methods for macro implementations specific
* to some Roland devices.
*
* Note that this implements a 3-byte size format for addresses and sizes,
* devices that require 4-byte sizes will require additional work.
*
* @author Jens Gulden
*/
abstract class RolandSysexMacroAbstract extends AbstractSysexMacro {
public final static byte DEVICE_ID_ROLAND = (byte)0x10;
public final static byte COMMAND_SYSEX_SET = (byte)0x12;
public final static byte COMMAND_SYSEX_REQUEST = (byte)0x11;
public void checksum(byte[] data) {
int l = data.length;
byte c = calculateChecksum(data, 5, l-3);
data[l-2] = c;
}
/**
* Creates sysex-data for sending a block of data to the device.
* The data will automatically converted to nibblized format,
* as internally required for transmission.
* @param modelId
* @param addr
* @param data
* @return
*/
protected byte[] rolandSysexSet(byte modelId, int addr, byte[] data) {
data = nibblize(data);
return rolandSysexSetRaw(modelId, addr, data);
}
/**
* Creates sysex-data for sending a block of data to the device.
* The data is expected to already be in nibblized format, as required
* for transmission.
*
* @param modelId
* @param addr
* @param data
* @return
*/
protected byte[] rolandSysexSetRaw(byte modelId, int addr, byte[] data) {
byte[] a = new byte[10 + data.length];
byte deviceId = DEVICE_ID_ROLAND;
byte cmd = COMMAND_SYSEX_SET;
a[0] = (byte)0xf0;
a[1] = 0x41;
a[2] = deviceId;
a[3] = modelId;
a[4] = cmd;
// 3-byte address
a[5] = (byte)((addr>>16) & 0xff);
a[6] = (byte)((addr>>8) & 0xff);
a[7] = (byte)(addr & 0xff);
System.arraycopy(data, 0, a, 8, data.length);
//a[a.size-2] = 0; // checksum
a[a.length-1] = (byte)0xf7;
checksum(a);
return a;
}
/**
* Creates sysex-data for requesting a block of data from the midi-device.
*
* @param modelId
* @param addr
* @param size
* @return
*/
protected byte[] rolandSysexReq(byte modelId, int addr, int size) {
byte[] a = new byte[13];
a[0] = (byte)0xf0;
a[1] = 0x41;
a[2] = DEVICE_ID_ROLAND;
a[3] = modelId;
a[4] = COMMAND_SYSEX_REQUEST;
// 3-byte address
a[5] = (byte)((addr>>16) & 0xff);
a[6] = (byte)((addr>>8) & 0xff);
a[7] = (byte)(addr & 0xff);
// 3-byte size
a[8] = (byte)((size>>16) & 0xff);
a[9] = (byte)((size>>8) & 0xff);
a[10] = (byte)((size) & 0xff);
//a[11] = 0; // checksum
a[12] = (byte)0xf7;
checksum(a);
return a;
}
public static byte[] nibblize(byte[] data) {
byte[] nibbles = new byte[2 * data.length];
int j = 0;
boolean up = false;
byte b = 0;
for (int i = 0; i < nibbles.length; i++) {
if (up) {
nibbles[i] = (byte)((b>>4) & 0x0f);
up = false;
} else {
b = data[j++];
nibbles[i] = (byte)(b & 0x0f);
up = true;
}
}
return nibbles;
}
public static byte[] denibblize(byte[] nibbles) {
byte[] data = new byte[nibbles.length / 2];
int j = 0;
boolean up = true;
byte b = nibbles[0];
for (int i = 1; i < nibbles.length; i++) {
if (up) {
data[j++] = (byte)(b | (nibbles[i] << 4));
up = false;
} else {
b = nibbles[i];
up = true;
}
}
return data;
}
public static byte calculateChecksum(byte[] data, int from, int to) {
int sum = 0;
for (int i = from; i <= to; i++) {
sum += data[i];
}
return (byte)(128 - (sum % 128));
}
}