/* -*- 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 Simon Kainz <simon@familiekainz.at>.
*/
package freedots.gui.swing;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JPanel;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import freedots.musicxml.Note;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
* Draw a single note using staff notation.
*/
final class SingleNoteRenderer extends JPanel {
private static final long serialVersionUID = 8634349010555582625L;
private Note currentNote = null;
private int lineSpacing = 8;
private int lineLength = 20;
private int globalNotePos = 0;
private static final char TRANSPARENT = '-';
private static final char[] CHARTABLE = new char[] {
'#', // 0 Black
'$', // 1
'*', // 2
'0', // 3
'o', // 4
'+', // 5
'.', // 6
TRANSPARENT // 7
};
private Map<String, BufferedImage> icons =
new HashMap<String, BufferedImage>();
private Map<String, SingleIconSpecification> noteDefs =
new HashMap<String, SingleIconSpecification>();
private void readSingleIcon(String filename, String key, Map iconMap) {
Document D = null;
BufferedImage bImage = null;
try {
InputStream inputStream = getClass().getResourceAsStream(filename);
if (inputStream == null) {
System.err.println("File "+filename+" not found");
return;
}
D = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(inputStream);
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
XPathFactory xPathFactory = XPathFactory.newInstance();
XPath xPath = xPathFactory.newXPath();
NodeList nodeList = null;
String xPathExpression = "//icon/bitmap/row";
try {
nodeList = (NodeList) xPath.evaluate(xPathExpression,
D, XPathConstants.NODESET);
} catch (XPathExpressionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int count = nodeList.getLength();
bImage = new BufferedImage(nodeList.item(0).getTextContent().length(),
count, BufferedImage.TYPE_INT_ARGB);
for (int i = 0; i<count; i++) {
Node n = nodeList.item(i);
String line = n.getTextContent();
for (int x = 0; x<line.length(); x++) {
if (line.charAt(x) != TRANSPARENT) {
int col = toLevel(line.charAt(x));
int ncol = (0xFF000000 | (col<<16) | (col<<8)) | col;
bImage.setRGB(x, i, ncol);
}
}
}
iconMap.put(key, bImage);
}
private void readNoteIcons()
{
readSingleIcon("G_CLEF.xml", "G_CLEF", icons);
readSingleIcon("F_CLEF.xml", "F_CLEF", icons);
readSingleIcon("KEY_FLAT_1.xml", "KEY_FLAT_1", icons);
readSingleIcon("KEY_FLAT_2.xml", "KEY_FLAT_2", icons);
readSingleIcon("KEY_FLAT_3.xml", "KEY_FLAT_3", icons);
readSingleIcon("KEY_FLAT_4.xml", "KEY_FLAT_4", icons);
readSingleIcon("KEY_FLAT_5.xml", "KEY_FLAT_5", icons);
readSingleIcon("KEY_FLAT_6.xml", "KEY_FLAT_6", icons);
readSingleIcon("KEY_FLAT_7.xml", "KEY_FLAT_7", icons);
readSingleIcon("KEY_SHARP_1.xml", "KEY_SHARP_1", icons);
readSingleIcon("KEY_SHARP_2.xml", "KEY_SHARP_2", icons);
readSingleIcon("KEY_SHARP_3.xml", "KEY_SHARP_3", icons);
readSingleIcon("KEY_SHARP_4.xml", "KEY_SHARP_4", icons);
readSingleIcon("KEY_SHARP_5.xml", "KEY_SHARP_5", icons);
readSingleIcon("KEY_SHARP_6.xml", "KEY_SHARP_6", icons);
readSingleIcon("KEY_SHARP_7.xml", "KEY_SHARP_7", icons);
readSingleIcon("WHOLE_NOTE.xml", "WHOLE_NOTE", icons);
readSingleIcon("NOTEHEAD_BLACK.xml", "NOTEHEAD_BLACK", icons);
readSingleIcon("COMBINING_FLAG_1.xml", "COMBINING_FLAG_1", icons);
readSingleIcon("COMBINING_FLAG_2.xml", "COMBINING_FLAG_2", icons);
readSingleIcon("COMBINING_FLAG_3.xml", "COMBINING_FLAG_3", icons);
readSingleIcon("COMBINING_FLAG_4.xml", "COMBINING_FLAG_4", icons);
readSingleIcon("COMBINING_FLAG_5.xml", "COMBINING_FLAG_5", icons);
}
public SingleNoteRenderer()
{
readNoteIcons();
noteDefs.put("1/1",
new SingleIconSpecification("WHOLE_NOTE", null, false));
noteDefs.put("1/2",
new SingleIconSpecification("WHOLE_NOTE", null, true));
noteDefs.put("1/4",
new SingleIconSpecification("NOTEHEAD_BLACK", null, true));
noteDefs.put("1/8",
new SingleIconSpecification("NOTEHEAD_BLACK",
"COMBINING_FLAG_1", true));
noteDefs.put("1/16",
new SingleIconSpecification("NOTEHEAD_BLACK",
"COMBINING_FLAG_2", true));
noteDefs.put("1/32",
new SingleIconSpecification("NOTEHEAD_BLACK",
"COMBINING_FLAG_3", true));
noteDefs.put("1/64",
new SingleIconSpecification("NOTEHEAD_BLACK",
"COMBINING_FLAG_4", true));
noteDefs.put("1/128",
new SingleIconSpecification("NOTEHEAD_BLACK",
"COMBINING_FLAG_5", true));
noteDefs.put("G_CLEF",
new SingleIconSpecification(null, null, false, 3, 2, 0, 0));
noteDefs.put("F_CLEF",
new SingleIconSpecification(null, null, false, 3, 14, 0, 0));
}
public Dimension getPreferredSize() {
return new Dimension(60, 62);
}
public void setNote(Note note) {
this.currentNote = note;
//this.updateUI();
this.repaint();
}
private int toLevel(char c) {
// Check the char^
if (c == TRANSPARENT) {
return 255;
} else {
for (int i = CHARTABLE.length - 1; i >= 0; i--) {
if (CHARTABLE[i] == c) {
int level = 3 + (i * 36); // Range 3 .. 255 (not too bad)
return level;
}
}
}
// Unknown -> white^
return 255;
}
public void drawClef(Graphics g) {
BufferedImage currentClef = null;
int clefYOffset = 0;
int clefXOffset = 0;
switch (currentNote.getClef().sign) {
case G:
currentClef = icons.get("G_CLEF");
if (noteDefs.get("G_CLEF") != null) {
clefXOffset = noteDefs.get("G_CLEF").getOffsetX();
clefYOffset = noteDefs.get("G_CLEF").getOffsetY();
}
break;
case F:
currentClef = icons.get("F_CLEF");
if (noteDefs.get("F_CLEF") != null) {
clefXOffset = noteDefs.get("F_CLEF").getOffsetX();
clefYOffset = noteDefs.get("F_CLEF").getOffsetY();
}
break;
default: throw new AssertionError(currentNote.getClef().sign);
}
//BufferedImage currentClef=icons.get("G_CLEF");
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(currentClef, null, clefXOffset, clefYOffset);
globalNotePos = currentClef.getWidth()+4;
}
protected void drawLines(Graphics g) {
for (int i = 0; i<5; i++) {
g.drawLine(1, 14+i*lineSpacing, 40+lineLength, 14+i*lineSpacing);
}
}
protected void drawKey(Graphics g) {
int keyType = currentNote.getActiveKeySignature().getType();
String iconNameBase = null;
if (keyType==0) {
// at least move the followign note by some pixels right, to make it look sexier.
globalNotePos+=4;
return;
}
if (keyType>0) {
iconNameBase = "KEY_SHARP_"+keyType;
}
if (keyType<0) {
iconNameBase = "KEY_FLAT_"+keyType;
}
Graphics2D g2 = (Graphics2D)g;
g2.drawImage(icons.get(iconNameBase), null, 25+4, 0);
globalNotePos += icons.get(iconNameBase).getWidth() + 8;
}
protected int getNotePosition() {
int retval = 0;
switch (currentNote.getClef().sign)
{
case G:
retval = 14+5*lineSpacing-(currentNote.getPitch().getStep()*(lineSpacing/2))-4;
break;
case F:
retval = 14+5*lineSpacing-(currentNote.getPitch().getStep()*(lineSpacing/2))-4-lineSpacing*2-lineSpacing/2;
break;
}
return retval;
}
protected void drawNote(Graphics g)
{
if (currentNote.getPitch() == null) return;
Graphics2D g2 = (Graphics2D)g;
BufferedImage noteImage = null;
BufferedImage noteHead = null;
int notePosX;
int notePosY;
int stemDirection = 0;
boolean hasStem = false;
notePosX = globalNotePos;
notePosY = getNotePosition(); //14+5*lineSpacing-(currentNote.getPitch().getStep()*(lineSpacing/2))-4;
// System.out.println("Modifier type:"+currentNote.getActiveKeySignature().getType());
// System.out.println("Modifier count:"+currentNote.getActiveKeySignature().getModifierCount());
SingleIconSpecification iconSpec =
noteDefs.get(currentNote.getAugmentedFraction().numerator()
+"/"+currentNote.getAugmentedFraction().denominator());
if (iconSpec==null) return;
// First draw note head
if (icons.get(iconSpec.getNoteHeadImage())==null) return;
g2.drawImage(icons.get(iconSpec.getNoteHeadImage()), null,
notePosX, notePosY);
// Eventually draw the stem
if (iconSpec.isStem()) {
notePosX += icons.get(iconSpec.getNoteHeadImage()).getWidth();
g2.drawLine(notePosX, notePosY+3, notePosX, notePosY-27);
notePosY-=27;
}
// draw the flags, if any
g2.drawImage(icons.get(iconSpec.getFlagsImage()), null,
notePosX, notePosY);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (currentNote!=null)
{
drawLines(g);
drawClef(g);
drawKey(g);
drawNote(g);
// g.drawString(""+(currentNote.getPitch().getStep()),50,35);
}
}
}