/* * Computoser is a music-composition algorithm and a website to present the results * Copyright (C) 2012-2014 Bozhidar Bozhanov * * Computoser is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * Computoser 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Computoser. If not, see <http://www.gnu.org/licenses/>. */ package com.music.tools; import java.io.File; import java.io.FileReader; import java.io.Reader; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.XMLReaderFactory; import com.google.common.collect.Sets; public class SongDBAnalyzer { private static final DecimalFormat df = new DecimalFormat("#.##"); public static void main(String[] args) throws Exception { File dir = new File("C:\\workspace\\music\\analysis\\db"); File[] files = dir.listFiles(); XMLReader xr = XMLReaderFactory.createXMLReader(); NoteContentHandler handler = new NoteContentHandler(); xr.setContentHandler(handler); for (File file : files) { try (Reader reader = new FileReader(file)) { try { xr.parse(new InputSource(reader)); handler.getNotes().add(null); } catch (Exception ex) { System.out.println(file.getName()); throw ex; } } } List<NoteElement> noteList = handler.getNotes(); int[] degreeValue = new int[] {0,0,0,0,0,0,0}; int[] degreeBeforePause = new int[] {0,0,0,0,0,0,0}; int[] lengths = new int[1000]; int[] unisonLengths = new int[1000]; int[] intervals = new int[] {0,0,0,0,0,0,0,0,0,0,0,0,0}; int[] firstInMeasure = new int[] {0,0,0,0,0,0,0}; int[] lastInMeasure = new int[] {0,0,0,0,0,0,0}; int[][] lengthsPerDegree = new int[7][1000]; int[][] nextNotes = new int[7][7]; int measures = 0; int notes = 0; int stable = 0; int unstable = 0; NoteElement previousNote = null; int currentNoteCount = 0; List<Integer> noteCounts = new ArrayList<Integer>(); for (NoteElement note : noteList) { if (note == null) { previousNote = null; noteCounts.add(currentNoteCount); currentNoteCount = 0; continue; } else { currentNoteCount++; } if (note.isRest() && previousNote != null) { degreeBeforePause[previousNote.getScaleDegree()-1]++; } if (!note.isRest() && !note.isFlat() && !note.isSharp()) { notes++; degreeValue[note.getScaleDegree()-1]++; lengths[(int) (note.getNoteLength() * 100)]++; lengthsPerDegree[note.getScaleDegree()-1][(int)(note.getNoteLength() * 100)]++; if (previousNote != null) { nextNotes[previousNote.getScaleDegree()-1][note.getScaleDegree()-1]++; int currentNormalized = note.getScaleDegree() + note.getOctave() * 7; int previousNormalized = previousNote.getScaleDegree() + previousNote.getOctave() * 7; int interval = currentNormalized - previousNormalized; int idx = Math.abs(interval); if (intervals.length > idx) { intervals[idx]++; } if (interval == 0) { unisonLengths[(int) (note.getNoteLength() * 100)]++; } } if (previousNote == null || previousNote.getStartMeasure() != note.getStartMeasure()) { if (previousNote != null) { lastInMeasure[previousNote.getScaleDegree() - 1]++; } firstInMeasure[note.getScaleDegree() - 1] ++; measures++; } if (note.getScaleDegree() == 1 || note.getScaleDegree() == 3 || note.getScaleDegree() == 5) { stable ++; } else { unstable ++; } previousNote = note; } } System.out.println("Degrees: " + Arrays.toString(percentages(degreeValue))); System.out.println("Degrees before pause: " + Arrays.toString(percentages(degreeBeforePause))); System.out.println("Intervals: " + Arrays.toString(percentages(intervals))); System.out.println("First note in measure: " + Arrays.toString(percentages(firstInMeasure))); System.out.println("Last note in measure: " + Arrays.toString(percentages(lastInMeasure))); System.out.println("Lengths: " + Arrays.toString(percentages(lengths, true, true))); System.out.println("Unison lengths: " + Arrays.toString(percentages(unisonLengths, true, true))); System.out.println("Stable:Unstable = " + stable + ":" + unstable); System.out.println("Notes/measure = " + (1d*notes/measures)); for (int i = 0; i < 7; i++) { System.out.println("Lengths for degree " + (i+1)); double degreeTotal = 0; for (int length = 0; length < lengthsPerDegree[i].length; length++) { degreeTotal += lengthsPerDegree[i][length]; } for (int length = 0; length < lengthsPerDegree[i].length; length++) { if (lengthsPerDegree[i][length] > 0) { System.out.print(length + "=" + df.format((lengthsPerDegree[i][length]) / degreeTotal * 100)); System.out.print("; "); } } System.out.println(); System.out.println(); } System.out.println("-----------------------"); System.out.println(); for (int i = 0; i < 7; i++) { System.out.println("Next notes for degree " + (i+1)); double degreeTotal = 0; for (int k = 0; k < nextNotes[i].length; k++) { degreeTotal += nextNotes[i][k]; } for (int k = 0; k < nextNotes[i].length; k++) { System.out.print((k+1) + "=" + df.format((nextNotes[i][k]) / degreeTotal * 100)); System.out.print("; "); } System.out.println(); System.out.println(); } double sum = 0; int noteCountSize = 0; for (int count : noteCounts) { sum += count; if (count != 0) { noteCountSize ++; } } System.out.println(noteCounts); System.out.println("Average notes per piece: " + df.format(sum / noteCountSize)); } public static String[] percentages(int[] array) { return percentages(array, false, false); } public static String[] percentages(int[] array, boolean excludeZero, boolean includeIdx) { double total = 0; List<String> result = new ArrayList<>(); for (int val : array) { total += val; } for (int i = 0; i < array.length; i++) { double value = array[i] / total * 100; if (excludeZero && value == 0) { continue; } String prefix = ""; if (includeIdx) { prefix += i + "="; } result.add(prefix + df.format(value)); } return result.toArray(new String[result.size()]); } public static class NoteContentHandler extends DefaultHandler { private List<NoteElement> notes = new ArrayList<>(); private NoteElement currentNote; private Set<String> noteProperties = Sets.newHashSet("start_beat_abs", "start_measure", "start_beat", "note_length", "scale_degree", "octave", "isRest"); private String currentProperty; private StringBuilder currentPropertyValue = new StringBuilder(); @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (localName.equals("note")) { currentNote = new NoteElement(); } if (noteProperties.contains(localName)) { currentProperty = localName; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (currentProperty != null && currentNote != null) { currentPropertyValue.append(Arrays.copyOfRange(ch, start, start+length)); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (localName.equals("note") && currentNote != null) { notes.add(currentNote); currentNote = null; } if (noteProperties.contains(localName) && currentNote != null) { //System.out.println(currentProperty + " : " + currentPropertyValue.toString().trim()); if (currentProperty.equals("start_beat_abs")) { currentNote.setStartBeatAbs(Double.parseDouble(currentPropertyValue.toString().trim())); } if (currentProperty.equals("start_measure")) { currentNote.setStartMeasure(Double.parseDouble(currentPropertyValue.toString().trim())); } if (currentProperty.equals("start_beat")) { currentNote.setStartBeat(Double.parseDouble(currentPropertyValue.toString().trim())); } if (currentProperty.equals("note_length")) { currentNote.setNoteLength(Double.parseDouble(currentPropertyValue.toString().trim())); } if (currentProperty.equals("scale_degree")) { String value = currentPropertyValue.toString().trim(); if (value.equalsIgnoreCase("rest")) { currentNote.setScaleDegree(-1); } else { if (value.endsWith("f")) { currentNote.setFlat(true); value = value.substring(0, value.length() - 1); } else if (value.endsWith("s")) { currentNote.setSharp(true); value = value.substring(0, value.length() - 1); } currentNote.setScaleDegree(Integer.parseInt(value)); } } if (currentProperty.equals("octave")) { currentNote.setOctave(Integer.parseInt(currentPropertyValue.toString().trim())); } if (currentProperty.equals("isRest")) { currentNote.setRest(currentPropertyValue.toString().trim().equals("1") ? true : false); } currentPropertyValue = new StringBuilder(); currentProperty = null; } } public List<NoteElement> getNotes() { return notes; } } }