|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package com.sun.media.sound; |
|
|
|
import java.io.UnsupportedEncodingException; |
|
import java.util.Arrays; |
|
|
|
import javax.sound.midi.Patch; |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public final class SoftTuning { |
|
|
|
private String name = null; |
|
private final double[] tuning = new double[128]; |
|
private Patch patch = null; |
|
|
|
public SoftTuning() { |
|
name = "12-TET"; |
|
for (int i = 0; i < tuning.length; i++) |
|
tuning[i] = i * 100; |
|
} |
|
|
|
public SoftTuning(byte[] data) { |
|
for (int i = 0; i < tuning.length; i++) |
|
tuning[i] = i * 100; |
|
load(data); |
|
} |
|
|
|
public SoftTuning(Patch patch) { |
|
this.patch = patch; |
|
name = "12-TET"; |
|
for (int i = 0; i < tuning.length; i++) |
|
tuning[i] = i * 100; |
|
} |
|
|
|
public SoftTuning(Patch patch, byte[] data) { |
|
this.patch = patch; |
|
for (int i = 0; i < tuning.length; i++) |
|
tuning[i] = i * 100; |
|
load(data); |
|
} |
|
|
|
private boolean checksumOK(byte[] data) { |
|
int x = data[1] & 0xFF; |
|
for (int i = 2; i < data.length - 2; i++) |
|
x = x ^ (data[i] & 0xFF); |
|
return (data[data.length - 2] & 0xFF) == (x & 127); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void load(byte[] data) { |
|
|
|
if ((data[1] & 0xFF) == 0x7E || (data[1] & 0xFF) == 0x7F) { |
|
int subid1 = data[3] & 0xFF; |
|
switch (subid1) { |
|
case 0x08: |
|
int subid2 = data[4] & 0xFF; |
|
switch (subid2) { |
|
case 0x01: |
|
{ |
|
// http://www.midi.org/about-midi/tuning.shtml |
|
//if (!checksumOK2(data)) |
|
|
|
try { |
|
name = new String(data, 6, 16, "ascii"); |
|
} catch (UnsupportedEncodingException e) { |
|
name = null; |
|
} |
|
int r = 22; |
|
for (int i = 0; i < 128; i++) { |
|
int xx = data[r++] & 0xFF; |
|
int yy = data[r++] & 0xFF; |
|
int zz = data[r++] & 0xFF; |
|
if (!(xx == 127 && yy == 127 && zz == 127)) |
|
tuning[i] = 100.0 * |
|
(((xx * 16384) + (yy * 128) + zz) / 16384.0); |
|
} |
|
break; |
|
} |
|
case 0x02: |
|
{ |
|
|
|
int ll = data[6] & 0xFF; |
|
int r = 7; |
|
for (int i = 0; i < ll; i++) { |
|
int kk = data[r++] & 0xFF; |
|
int xx = data[r++] & 0xFF; |
|
int yy = data[r++] & 0xFF; |
|
int zz = data[r++] & 0xFF; |
|
if (!(xx == 127 && yy == 127 && zz == 127)) |
|
tuning[kk] = 100.0*(((xx*16384) + (yy*128) + zz)/16384.0); |
|
} |
|
break; |
|
} |
|
case 0x04: |
|
{ |
|
|
|
if (!checksumOK(data)) |
|
break; |
|
try { |
|
name = new String(data, 7, 16, "ascii"); |
|
} catch (UnsupportedEncodingException e) { |
|
name = null; |
|
} |
|
int r = 23; |
|
for (int i = 0; i < 128; i++) { |
|
int xx = data[r++] & 0xFF; |
|
int yy = data[r++] & 0xFF; |
|
int zz = data[r++] & 0xFF; |
|
if (!(xx == 127 && yy == 127 && zz == 127)) |
|
tuning[i] = 100.0*(((xx*16384) + (yy*128) + zz)/16384.0); |
|
} |
|
break; |
|
} |
|
case 0x05: |
|
|
|
{ |
|
|
|
if (!checksumOK(data)) |
|
break; |
|
try { |
|
name = new String(data, 7, 16, "ascii"); |
|
} catch (UnsupportedEncodingException e) { |
|
name = null; |
|
} |
|
int[] octave_tuning = new int[12]; |
|
for (int i = 0; i < 12; i++) |
|
octave_tuning[i] = (data[i + 23] & 0xFF) - 64; |
|
for (int i = 0; i < tuning.length; i++) |
|
tuning[i] = i * 100 + octave_tuning[i % 12]; |
|
break; |
|
} |
|
case 0x06: |
|
|
|
{ |
|
|
|
if (!checksumOK(data)) |
|
break; |
|
try { |
|
name = new String(data, 7, 16, "ascii"); |
|
} catch (UnsupportedEncodingException e) { |
|
name = null; |
|
} |
|
double[] octave_tuning = new double[12]; |
|
for (int i = 0; i < 12; i++) { |
|
int v = (data[i * 2 + 23] & 0xFF) * 128 |
|
+ (data[i * 2 + 24] & 0xFF); |
|
octave_tuning[i] = (v / 8192.0 - 1) * 100.0; |
|
} |
|
for (int i = 0; i < tuning.length; i++) |
|
tuning[i] = i * 100 + octave_tuning[i % 12]; |
|
break; |
|
} |
|
case 0x07: |
|
// REAL-TIME/REAL-TIME) (BANK) |
|
|
|
int ll = data[7] & 0xFF; |
|
int r = 8; |
|
for (int i = 0; i < ll; i++) { |
|
int kk = data[r++] & 0xFF; |
|
int xx = data[r++] & 0xFF; |
|
int yy = data[r++] & 0xFF; |
|
int zz = data[r++] & 0xFF; |
|
if (!(xx == 127 && yy == 127 && zz == 127)) |
|
tuning[kk] = 100.0 |
|
* (((xx*16384) + (yy*128) + zz) / 16384.0); |
|
} |
|
break; |
|
case 0x08: |
|
|
|
{ |
|
|
|
int[] octave_tuning = new int[12]; |
|
for (int i = 0; i < 12; i++) |
|
octave_tuning[i] = (data[i + 8] & 0xFF) - 64; |
|
for (int i = 0; i < tuning.length; i++) |
|
tuning[i] = i * 100 + octave_tuning[i % 12]; |
|
break; |
|
} |
|
case 0x09: |
|
|
|
{ |
|
|
|
double[] octave_tuning = new double[12]; |
|
for (int i = 0; i < 12; i++) { |
|
int v = (data[i * 2 + 8] & 0xFF) * 128 |
|
+ (data[i * 2 + 9] & 0xFF); |
|
octave_tuning[i] = (v / 8192.0 - 1) * 100.0; |
|
} |
|
for (int i = 0; i < tuning.length; i++) |
|
tuning[i] = i * 100 + octave_tuning[i % 12]; |
|
break; |
|
} |
|
default: |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
// am: getTuning(int) is more effective. |
|
|
|
public double[] getTuning() { |
|
return Arrays.copyOf(tuning, tuning.length); |
|
} |
|
|
|
public double getTuning(int noteNumber) { |
|
return tuning[noteNumber]; |
|
} |
|
|
|
public Patch getPatch() { |
|
return patch; |
|
} |
|
|
|
public String getName() { |
|
return name; |
|
} |
|
|
|
public void setName(String name) { |
|
this.name = name; |
|
} |
|
} |