/* -*- c-basic-offset: 2; indent-tabs-mode: nil; -*- */
/*
* FreeDots -- MusicXML to braille music transcription
*
* Copyright 2008-2010 Mario Lang All Rights Reserved.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3, as
* published by the Free Software Foundation.
*
* This code 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 General Public License
* for more details (a copy is included in the LICENSE.txt file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License
* along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This file is maintained by Mario Lang <mlang@delysid.org>.
*/
package freedots.musicxml;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import freedots.math.Fraction;
public final class Harmony extends AbstractDirection {
private static final Logger LOG = Logger.getLogger(Harmony.class.getName());
private final List<HarmonyChord> chords = new ArrayList<HarmonyChord>();
Harmony(final Element element,
final int durationMultiplier, final int divisions,
final Fraction moment) {
super(element, durationMultiplier, divisions, moment);
HarmonyChord currentChord = null;
for (Node node = element.getFirstChild(); node != null;
node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element)node;
if ("root".equals(child.getTagName())
|| "function".equals(child.getTagName())) {
chords.add(currentChord = new HarmonyChord(child));
} else if ("kind".equals(child.getTagName())) {
if (currentChord != null)
currentChord.setKind(child);
} else if ("inversion".equals(child.getTagName())) {
if (currentChord != null)
currentChord.setInversion(child);
} else if ("bass".equals(child.getTagName())) {
if (currentChord != null)
currentChord.setBass(child);
} else if ("degree".equals(child.getTagName())) {
if (currentChord != null)
currentChord.addDegree(child);
}
}
}
}
public List<HarmonyChord> getChords() { return chords; }
@Override public String toString() {
StringBuilder sb = new StringBuilder();
for (HarmonyChord chord: chords) sb.append(chord);
return sb.toString();
}
public class HarmonyChord {
private Element rootStep = null, rootAlter = null;
private Element function = null; //Unsupported
private Element kind;
private Element inversion = null;
private Element bassStep = null, bassAlter = null;
private List<Degree> degrees = new ArrayList<Degree>();
HarmonyChord(final Element initial) {
if ("root".equals(initial.getTagName()))
for (Node node = initial.getFirstChild(); node != null;
node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element)node;
if ("root-step".equals(child.getTagName())) {
rootStep = child;
} else if ("root-alter".equals(child.getTagName())) {
rootAlter = child;
}
}
}
else if ("function".equals(initial.getTagName()))
function = initial;
}
public int getRootStep() {
if (rootStep != null) {
return Pitch.convertStep(rootStep.getTextContent());
}
LOG.warning("Unsupported harmony type without root-step");
return 0;
}
public float getRootAlter() {
if (rootAlter != null) {
return Float.parseFloat(rootAlter.getTextContent());
}
return 0;
}
void setKind(Element kind) {
this.kind = kind;
}
public String getKind() {
return kind.getTextContent();
}
void setInversion(Element inversion) {
this.inversion = inversion;
}
/** Gets a number indicating which inversion is used.
* @return 0 for root position, 1 for first inversion, etc.
*/
public int getInversion() {
if (inversion != null) {
return Integer.parseInt(inversion.getTextContent());
}
return 0;
}
void setBass(Element bass) {
for (Node node = bass.getFirstChild(); node != null;
node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
if ("bass-step".equals(node.getNodeName()))
bassStep = (Element)node;
else if ("bass-alter".equals(node.getNodeName()))
bassAlter = (Element)node;
}
}
}
public boolean hasBass() {
return bassStep != null;
}
public int getBassStep() {
if (hasBass()) {
return Pitch.convertStep(bassStep.getTextContent());
}
return 0;
}
public float getBassAlter() {
if (hasBass() && bassAlter != null)
return Float.parseFloat(bassAlter.getTextContent());
return 0;
}
void addDegree(Element degree) { degrees.add(new Degree(degree)); }
public List<Degree> getAlterations() { return degrees; }
@Override public String toString() {
return (rootStep != null? rootStep.getTextContent(): function.getTextContent())
+ kind.getTextContent();
}
public class Degree {
private Element value, alter, type;
Degree(final Element degree) {
for (Node node = degree.getFirstChild(); node != null;
node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
if ("degree-value".equals(node.getNodeName()))
value = (Element)node;
else if ("degree-alter".equals(node.getNodeName()))
alter = (Element)node;
else if ("degree-type".equals(node.getNodeName()))
type = (Element)node;
}
}
}
public int getValue() {
return Integer.parseInt(value.getTextContent());
}
public float getAlter() {
return Float.parseFloat(alter.getTextContent());
}
}
}
}