package org.deeplearning4j.examples.recurrent.character.melodl4j;
import java.util.HashMap;
import java.util.Map;
public class SymbolicNotes {
private static final String durationChars = "tsiqhw"; // x=64, t=32nd, s = 16th, i=eigth, q=quarter, h=half, w=whole
private static Map<String,Integer> noteMap = new HashMap<>();
private static final String[] pitches= {"C","C#","D","Eb","E","F","F#","G","Ab","A","Bb","B"};
static {
noteMap.put("C", 0);
noteMap.put("C#", 1);
noteMap.put("Db", 1);
noteMap.put("D", 2);
noteMap.put("D#", 3);
noteMap.put("Eb", 3);
noteMap.put("E", 4);
noteMap.put("Fb", 4);
noteMap.put("E#", 5);
noteMap.put("F", 5);
noteMap.put("F#", 6);
noteMap.put("Gb", 6);
noteMap.put("G", 7);
noteMap.put("G#", 8);
noteMap.put("Ab", 8);
noteMap.put("A", 9);
noteMap.put("A#", 10);
noteMap.put("Bb", 10);
noteMap.put("B", 11);
}
//--------------------------------------------------------
// Assumes quarter note duration
public static String pitchToJFugueNoteString(int rawPitch) {
int key = rawPitch%12;
int scale=rawPitch/12;
String pitchString = pitches[key];
return pitchString + scale;
} // Middle C = C5 = 60
public static String pitchToJFugueNoteStringWithDefaultDuration(int rawPitch) {
return pitchToJFugueNoteString(rawPitch) + "q";
}
//----------------------
public static char getMelodyDurationCharacterFromJFuguePatternString(String noteSymbolAndScale) {// C5 or Ab4 or or C5q or D3w or C5q.
int duration=getDurationInMultiplesOf32ndNoteFromJFuguePatternString(noteSymbolAndScale);
return Midi2MelodyStrings.durationChars.charAt(duration);
}
//----------------------------
public static String melodyToJFugue(final String melodyString, int startPitch) {
StringBuilder sb = new StringBuilder();
char ch = melodyString.charAt(0);
sb.append(pitchToJFugueNoteString(startPitch));
int index=0;
if (Midi2MelodyStrings.isDurationChar(ch)) {
sb.append(durationCharToJFugueTempo(ch));
index++;
} else {
sb.append("q"); // default
}
sb.append(' ');
int lastPitch=startPitch;
while (index<melodyString.length()) {
ch=melodyString.charAt(index);
index++;
if (Midi2MelodyStrings.isPitchDeltaChar(ch)) {
int delta = Midi2MelodyStrings.getPitchDeltaFromMelodyChar(ch);
lastPitch+= delta;
sb.append(pitchToJFugueNoteString(lastPitch));
} else if (ch=='R') {
sb.append(ch);
} else {
System.err.println("WARNING: bad char (" + ch + ") in melody");
continue; // Skip duration calculation below
}
ch=melodyString.charAt(index);
if (Midi2MelodyStrings.isDurationChar(ch)) {
sb.append(durationCharToJFugueTempo(ch));
index++;
} else {
sb.append('q');
}
sb.append(' ');
}
return sb.toString();
}
public static String durationCharToJFugueTempo(char ch) {
int index= Midi2MelodyStrings.durationChars.indexOf(ch);
if (index<0) { // invalid
return "q"; // default
}
switch (index) {
case 1:
return "t";
case 2:
return "s";
case 3:
return "s.";
case 4:
return "i";
case 5:
return "it";
case 6:
return "is";
case 7:
return "ist";
case 8:
return "q";
case 9:
return "qt";
case 10:
return "qs";
case 11:
return "qst";
case 12:
return "qi";
case 13:
return "qit";
case 14:
return "qis";
case 15:
return "qist";
case 16:
return "h";
case 32:
return "w";
default:
return "q";
}
}
//---------------------
// Default to quarter note (4), up to a maximum of 32 (a whole note)
public static int getDurationInMultiplesOf32ndNoteFromJFuguePatternString(String noteSymbolAndScale) {// Rq or C5 or Ab4 or or C5q or D3w or C5q.
final String originalNoteSymbolAndScale=noteSymbolAndScale;
boolean dotted = noteSymbolAndScale.endsWith(".");
if (dotted) {
noteSymbolAndScale = noteSymbolAndScale.substring(0,noteSymbolAndScale.length()-1);
}
char lastChar = noteSymbolAndScale.charAt(noteSymbolAndScale.length()-1);
int index= durationChars.indexOf(lastChar);
if (index<0) {
//throw new IllegalArgumentException("Illegal duration char " + lastChar + " in " + originalNoteSymbolAndScale);
return 4; // default is quarter note
}
int duration=1;
while (index>0) {
duration+= duration;
index--;
}
if (dotted) {
duration+= duration/2;
}
if (duration>32) {
duration=32; // max is whole note
}
return duration;
}
//---------------------
public static int getPitchFromJFuguePatternString(String jfugue) {// C5 or Ab4 or or C5q or D3w or C5q.
final String originalNoteSymbolAndScale=jfugue;
int len=jfugue.length();
if (len==0) {
return 0;
}
char lastChar = jfugue.charAt(len-1);
while (lastChar == '.' || durationChars.indexOf(lastChar)>=0) {
jfugue= jfugue.substring(0,len-1);
len--;
if (len==0) {
throw new IllegalArgumentException("Missing pitch in " + originalNoteSymbolAndScale);
}
lastChar = jfugue.charAt(len-1);
}
if (!Character.isDigit(lastChar)) {
throw new IllegalArgumentException("Invalid noteSymbol because of missing digit at end: " + jfugue);
}
int scale=Integer.parseInt(""+lastChar);
String noteSymbol = jfugue.substring(0, len-1);
Integer value = noteMap.get(noteSymbol);
if (value==null) {
throw new IllegalArgumentException("Invalid noteSymbol " + jfugue);
}
return scale*12+value.intValue();
}
//----------------
public static void main(String [] args) {
try {
for(int i=20;i<=72;i++) {
String asString = pitchToJFugueNoteString(i);
System.out.println(i + " " + asString + " " + getPitchFromJFuguePatternString(asString));
}
} catch (Exception exc) {
exc.printStackTrace();
System.exit(1);
}
}
}