package us.achromaticmetaphor.imcktg; import java.io.PrintStream; import java.util.Collections; import java.util.HashSet; import java.util.Set; public abstract class IMelodyFormat { public static final String styleContinuous = "S1"; public static final int repeatForever = 0; public static final int defaultOctave = 4; public static final String octavePrefix = "*"; public static final String rest = "r"; public static final String durationSuffixDotted = "."; public static final String headerSeparator = ":"; public static final String lineEnding = "\r\n"; public static final String lineContinuation = lineEnding + " "; public static final int beatMin = 25; public static final int beatMax = 900; public static final int octaveMin = 0; public static final int octaveMax = 8; public static final int volumeMin = 0; public static final int volumeMax = 15; private static final Set<String> validNotes; private static final Set<String> validStyles; public static String note(int octave, String tone) { if (octave < octaveMin || octave > octaveMax) throw new IllegalArgumentException("invalid octave: " + octave); if (! isValidNote(tone)) throw new IllegalArgumentException("invalid tone: " + tone); return (octave == defaultOctave ? "" : octavePrefix + octave) + tone; } public static String duration(int dur) { if (dur < 0 || dur > 5) throw new IllegalArgumentException(); return "" + dur; } public static String durationDotted(int dur) { return duration(dur) + durationSuffixDotted; } public static boolean isValidNote(String note) { return validNotes.contains(note); } public static void writeHeader(PrintStream out, String key, String value) { out.print(key + headerSeparator + value + lineEnding); } public static void writeRequiredHeaders(PrintStream out) { writeHeader(out, "BEGIN", "IMELODY"); writeHeader(out, "VERSION", "1.2"); writeHeader(out, "FORMAT", "CLASS1.0"); } public static void writeBeatHeader(PrintStream out, int beat) { if (beat < beatMin || beat > beatMax) throw new IllegalArgumentException("invalid beat: " + beat); writeHeader(out, "BEAT", "" + beat); } public static void writeStyleHeader(PrintStream out, String style) { if (! validStyles.contains(style)) throw new IllegalArgumentException("invalid style: " + style); writeHeader(out, "STYLE", style); } public static void writeVolumeHeader(PrintStream out, int volume) { if (volume < volumeMin || volume > volumeMax) throw new IllegalArgumentException("invalid volume: " + volume); writeHeader(out, "VOLUME", "V" + volume); } public static void writeNameHeader(PrintStream out, String name) { if (name.contains("\n")) throw new IllegalArgumentException("invalid name: " + name); writeHeader(out, "NAME", name); } public static void writeNameHeaderMangled(PrintStream out, String name) { writeNameHeader(out, name.replaceAll("\n", "")); } public static void writeBeginMelody(PrintStream out) { writeHeader(out, "MELODY", ""); out.print(" "); } public static void writeEndMelody(PrintStream out) { out.print(lineEnding); } public static void writeRequiredFooters(PrintStream out) { writeHeader(out, "END", "IMELODY"); } static { Set<String> s = new HashSet<String>(); for (char note : new char [] {'a', 'b', 'c', 'd', 'e', 'f', 'g'}) for (String prefix : new String [] {"", "#", "&"}) s.add(prefix + note); validNotes = Collections.unmodifiableSet(s); } static { Set<String> s = new HashSet<String>(); for (char style : new char [] {'0', '1', '2'}) s.add("S" + style); validStyles = Collections.unmodifiableSet(s); } public static void beginRepeatBlock(PrintStream out) { out.print("("); } public static void endRepeatBlock(PrintStream out, int repeatCount) { if (repeatCount < 0) throw new IllegalArgumentException("invalid repeat count: " + repeatCount); out.print("@" + repeatCount + ")"); } }