import java.util.stream.*; import java.util.function.*; import java.util.*; import java.nio.file.*; import java.nio.channels.*; import java.nio.*; import java.io.*; import static java.util.Comparator.comparing; import static java.lang.Integer.parseInt; public enum MusicalNote { @MusicalMode({"Mixolydian", "Ionian"}) C, CSharp, @MusicalMode({"Mixolydian", "Ionian"}) D, DSharp, @MusicalMode({"Mixolydian", "Ionian"}) E, @MusicalMode({"Mixolydian", "Ionian"}) F, FSharp, @MusicalMode({"Mixolydian", "Ionian"}) G, GSharp, @MusicalMode({"Mixolydian", "Ionian"}) A, @MusicalMode({"Mixolydian"}) ASharp, @MusicalMode({"Ionian"}) B; public static MusicalNote transpose(MusicalNote note, int offset) { return values()[Math.floorMod(note.ordinal() + offset, values().length)]; } public final static BiFunction> fifths = (isUp, note) -> { IntUnaryOperator update = isUp ? n -> n + 7 : n -> n - 7; return IntStream.iterate(0, update).mapToObj(offset -> transpose(note, offset)); }; private static A readFromPos(FileChannel ch, long pos, int len, Function f) throws IOException { System.out.println("Reading " + pos + " - " + (pos + len - 1) + "/" + ch.size()); ByteBuffer b = ByteBuffer.allocate(len); ch.position(pos).read(b); b.flip(); return f.apply(b); } private static void skip(FileChannel ch, int bytes) throws IOException { ch.position(ch.position() + bytes); } public static List readSong(String filename) throws IOException { List notes = new LinkedList<>(); FileChannel file = FileChannel.open(Paths.get(filename)); skip(file, 14); while (file.position() < file.size()) { System.out.println("reading track"); skip(file, 4); int channelSize = readFromPos(file, file.position(), 4, ByteBuffer::getInt); long channelEnd = file.position() + channelSize; while (file.position() < channelEnd) { long pos = file.position(); skip(file, 1); byte status = readFromPos(file, file.position(), 1, ByteBuffer::get); System.out.println("status is " + status); if (status == -111) { byte noteIndex = readFromPos(file, file.position(), 1, ByteBuffer::get); notes.add(MusicalNote.values()[noteIndex % MusicalNote.values().length]); file.position(pos + 4); } else { byte msgType = readFromPos(file, file.position(), 1, ByteBuffer::get); System.out.println("msgType is " + msgType); switch (msgType) { case -111: file.position(pos + 5); break; case 3: file.position(pos + 4); break; case 47: file.position(pos + 4); break; case 89: file.position(pos + 3); break; default: System.err.println("Invalid msg type! " + msgType); break; } } } } return notes; } public static Function, Optional>> isSongInMode = mode -> song -> { Class cls = MusicalNote.class; return song.stream().filter(note -> { try { return !Arrays.asList(cls.getField(note.name()).getAnnotation(MusicalMode.class).value()).contains(mode); } catch (NoSuchFieldException | SecurityException e) { return true; }}).findFirst(); }; public static Set setUnion(Set set1, Set set2) { Set result = new HashSet<>(set1); result.addAll(set2); return result; } /* // Az alábbi megoldáshoz Java 9 szükséges. static Function>>> notesByDuration9 = noExc(filename -> Files.lines(Paths.get(filename)) .map(line -> line.split(" ")) .map(split -> Map.entry(parseInt(split[0]), MusicalNote.valueOf(split[1]))) .collect(Collectors.toMap( Map.Entry::getKey, e -> Set.of(e.getValue()), MusicalNote::setUnion)) .entrySet() .stream() .sorted(comparing((Map.Entry> e) -> e.getValue().size()) .thenComparing(comparing((Map.Entry> e) -> e.getKey()).reversed())) ); */ // Az alábbi megoldáshoz elég Java 8 is. static Function>>> notesByDuration8 = noExc(filename -> Files.lines(Paths.get(filename)) .map(line -> line.split(" ")) .map(split -> new AbstractMap.SimpleImmutableEntry<>(parseInt(split[0]), MusicalNote.valueOf(split[1]))) .collect(Collectors., Integer, Set>toMap( Map.Entry::getKey, e -> { Set s = new HashSet<>(); s.add(e.getValue()); return s; }, MusicalNote::setUnion)) .entrySet() .stream() .sorted(comparing((Map.Entry> e) -> e.getValue().size()) .thenComparing(comparing((Map.Entry> e) -> e.getKey()).reversed())) ); // Az alábbi megoldáshoz elég Java 8 is. // A notesByDuration8 egy változata, mely noExc helyett lines-t használ. static Function>>> notesByDuration8_2 = filename -> lines(filename) .map(line -> line.split(" ")) .map(split -> new AbstractMap.SimpleImmutableEntry<>(parseInt(split[0]), MusicalNote.valueOf(split[1]))) .collect(Collectors.toMap( Map.Entry::getKey, e -> { Set s = new HashSet<>(); s.add(e.getValue()); return s; }, MusicalNote::setUnion)) .entrySet() .stream() .sorted(comparing((Map.Entry> e) -> e.getValue().size()) .thenComparing(comparing((Map.Entry> e) -> e.getKey()).reversed())); static Stream lines(String filename) { try { return Files.lines(Paths.get(filename)); } catch (IOException e) { return Stream.empty(); } } static Function noExc(XFunction f) { return a -> { try { return f.apply(a); } catch (Exception e) { return null; } }; } interface XFunction { V apply(U u) throws Exception; } }