2440 lines
74 KiB
C++
2440 lines
74 KiB
C++
//
|
|
// Programmer: Craig Stuart Sapp <craig@ccrma.stanford.edu>
|
|
// Creation Date: Fri Feb 19 22:25:22 PST 2016
|
|
// Last Modified: Sat Feb 27 18:16:39 PST 2016
|
|
// Filename: tools/mid2svg.cpp
|
|
// URL: https://github.com/craigsapp/midifile/blob/master/tools/mid2svg.cpp
|
|
// Syntax: C++11
|
|
// vim: ts=3
|
|
//
|
|
// Description: Convert a MIDI file into an SVG piano roll.
|
|
//
|
|
|
|
#include "MidiFile.h"
|
|
#include "Options.h"
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
using namespace std;
|
|
using namespace smf;
|
|
|
|
|
|
void checkOptions (Options& opts, int argc, char* argv[]);
|
|
void usage (const char* command);
|
|
void example (void);
|
|
void convertMidiFileToSvg (stringstream& output, MidiFile& midifile,
|
|
Options& options);
|
|
int hasNotes (MidiEventList& eventlist);
|
|
void getMinMaxPitch (const MidiFile& midifile,
|
|
int& minpitch, int &maxpitch);
|
|
void getMinMaxTrackPitch (const MidiEventList& evl,
|
|
int& minpitch, int &maxpitch);
|
|
double getMaxTime (const MidiFile& midifile);
|
|
vector<double> getTrackHues (MidiFile& midifile);
|
|
void drawNote (ostream& out, MidiFile& midifile,
|
|
int i, int j, int dataQ,
|
|
int minpitch, int maxpitch);
|
|
void drawLines (ostream& out, MidiFile& midifile,
|
|
vector<double>& hues, Options& options);
|
|
void printLineToNextNote (ostream& out, MidiFile& midifile,
|
|
int track, int index, Options& options);
|
|
void drawStaves (ostream& out, double staffwidth,
|
|
const string& staffcolor,
|
|
double totalduration);
|
|
int base12ToBase7 (int pitch);
|
|
void printDoubleClass (ostream& out, double value);
|
|
void makeMappings (vector<int>& mapping,
|
|
const string& mapstring);
|
|
void drawNoteShape (ostream& out, string& shape, double x,
|
|
double y, double width, double height);
|
|
void drawRectangle (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawDiamond (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawEyelid (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawPlus (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawOval (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawAntiOval (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawRound1 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawRound2 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawRound3 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawRound4 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawAntiRound (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawAntiRound1 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawAntiRound2 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawAntiRound3 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawAntiRound4 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedInner (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedInner1 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedInner2 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedInner3 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedInner4 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedOuter (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedOuter1 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedOuter2 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedOuter3 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawCurvedOuter4 (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawTriangleUp (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawTriangleDown (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawTriangleLeft (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawTriangleRight (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawTriangleRoundLeft (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawTriangleRoundRight(ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawHexThick (ostream& out, double x, double y,
|
|
double width, double height);
|
|
void drawHexThin (ostream& out, double x, double y,
|
|
double width, double height);
|
|
string getTrackShape (int track);
|
|
void drawClefs (ostream& out);
|
|
void drawBrace (ostream& out);
|
|
|
|
// User interface variables:
|
|
Options options;
|
|
int dataQ = 0; // used with -d option
|
|
int roundedQ = 0; // used with -r option
|
|
int darkQ = 0; // used with --dark option
|
|
int bwQ = 0; // used with --bw option
|
|
double Scale = 1.0; // used with -s option
|
|
double Border = 2.0; // used with -b option
|
|
double Opacity = 0.75; // used with -o option
|
|
double drumQ = 0; // used with --drum option
|
|
int lineQ = 0; // used with -l option
|
|
int curveQ = 0; // used with --cl option
|
|
int radiusQ = 0; // used with --rl option
|
|
double radius = 1.0; // used with --rl option
|
|
int staffQ = 0; // used with --staff option
|
|
int clefQ = 0; // used with --clef option
|
|
int braceQ = 0; // used with --clef option
|
|
int diatonicQ = 0; // used with --diatonic option
|
|
int grandQ = 0; // used with --gs option
|
|
int finalQ = 0; // used with -f option
|
|
int doubleQ = 0; // used with --double option
|
|
int transparentQ = 1; // used with -T option
|
|
bool velocitybQ = false; // used with -v option
|
|
double ClefFactor = 6;
|
|
double StaffThickness = 2.0; // used with --staff-width
|
|
double LineThickness = 2.0; // used with --line-width
|
|
string StaffColor = "#555555"; // used with -staff-color
|
|
string ClefColor = "#555555"; // used with -clef-color
|
|
double MaxRest = 4.0; // used with --max-rest option
|
|
double EndSpace = 0.0; // used with -e option
|
|
int percmapQ = 0; // used with --perc option
|
|
double AspectRatio = 2.5; // used with -a option
|
|
vector<int> PercussionMap; // used with --perc option
|
|
vector<string> Shapes;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
int main(int argc, char* argv[]) {
|
|
checkOptions(options, argc, argv);
|
|
MidiFile midifile;
|
|
if (options.getArgCount() == 0) {
|
|
midifile.read(cin);
|
|
} else {
|
|
midifile.read(options.getArg(1));
|
|
}
|
|
stringstream notes;
|
|
int minpitch = -1;
|
|
int maxpitch = -1;
|
|
getMinMaxPitch(midifile, minpitch, maxpitch);
|
|
|
|
convertMidiFileToSvg(notes, midifile, options);
|
|
|
|
double minx = 0;
|
|
double miny = minpitch;
|
|
double width = getMaxTime(midifile);
|
|
double height = maxpitch - minpitch + 1;
|
|
|
|
double clefwidth = 0;
|
|
if (clefQ) {
|
|
clefwidth = 180 / 12;
|
|
}
|
|
|
|
cout << "<?xml version=\"1.0\""
|
|
<< " encoding=\"UTF-8\""
|
|
<< " standalone=\"no\""
|
|
<< "?>\n";
|
|
cout << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\""
|
|
<< " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\""
|
|
<< ">\n";
|
|
cout << "<svg"
|
|
<< " version=\"1.1\""
|
|
<< " xmlns=\"http://www.w3.org/2000/svg\""
|
|
<< " xmlns:xlink=\"http://www.w3.org/1999/xlink\""
|
|
<< " viewPort=\"" << minx << " " << miny << " "
|
|
<< width << " " << height << "\""
|
|
<< " viewBox=\"" << (-Border - clefwidth) * Scale << " "
|
|
<< -Border * Scale << " "
|
|
<< ((width + EndSpace) * AspectRatio + 2 * Border +
|
|
clefwidth) * Scale << " "
|
|
<< (height + (2 * Border)) * Scale << "\""
|
|
<< " width=\"" << ((width + EndSpace) * AspectRatio + 2 * Border +
|
|
clefwidth) * Scale << "\""
|
|
<< " height=\"" << (height + 2 * Border) * Scale << "\""
|
|
<< ">\n";
|
|
|
|
// Graphics setup:
|
|
|
|
// This filter is used to show overlap between notes:
|
|
if (transparentQ && !bwQ) {
|
|
cout << "<filter id=\"constantOpacity\">\n";
|
|
cout << "\t<feComponentTransfer>\n";
|
|
cout << "\t\t<feFuncA type=\"table\" tableValues=\"0 .5 .5\" />\n";
|
|
cout << "\t</feComponentTransfer>\n";
|
|
cout << "</filter>\n";
|
|
}
|
|
|
|
if (darkQ && !bwQ) {
|
|
cout << "<rect class=\"background\" x=\"-500%\" y=\"-500%\""
|
|
<< " width=\"1000%\" height=\"1000%\" style=\"fill:black\" />\n";
|
|
}
|
|
|
|
cout << "<g"
|
|
<< " transform=\""
|
|
<< "scale(" << 1 * Scale * AspectRatio << ", " << -Scale << ")"
|
|
<< " translate(0, " << -(maxpitch+1) << ")"
|
|
<< "\" >\n";
|
|
|
|
|
|
// Print the piano roll note boxes:
|
|
cout << notes.str();
|
|
|
|
// Graphics setup end closing:
|
|
cout << "</g>\n";
|
|
cout << "</svg>\n";
|
|
cout << "<?mid2svg\n\t";
|
|
for (int i=1; i<argc; i++) {
|
|
if (strchr(argv[i], ' ') != NULL) {
|
|
cout << '"';
|
|
}
|
|
cout << argv[i];
|
|
if (strchr(argv[i], ' ') != NULL) {
|
|
cout << '"';
|
|
}
|
|
if (i<argc-1) {
|
|
cout << " ";
|
|
}
|
|
}
|
|
cout << "\n?>\n";
|
|
return 0;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// convetMidiFileToSvg --
|
|
//
|
|
|
|
void convertMidiFileToSvg(stringstream& output, MidiFile& midifile,
|
|
Options& options) {
|
|
|
|
midifile.linkNotePairs(); // first link note-ons to note-offs
|
|
midifile.doTimeAnalysis(); // then create ticks to seconds mapping
|
|
|
|
stringstream notes;
|
|
|
|
if (staffQ) {
|
|
drawStaves(notes, StaffThickness, StaffColor,
|
|
midifile.getFileDurationInSeconds());
|
|
if (clefQ) {
|
|
drawClefs(notes);
|
|
}
|
|
if (braceQ) {
|
|
drawBrace(notes);
|
|
}
|
|
}
|
|
|
|
string strokecolor = options.getString("stroke-color");
|
|
double strokewidth = options.getDouble("stroke-width") * Scale;
|
|
notes << "\t<g"
|
|
<< " style=\""
|
|
<< "stroke-width:" << strokewidth << ";"
|
|
<< " stroke:" << strokecolor << ";"
|
|
<< "\""
|
|
<< ">\n";
|
|
|
|
vector<double> trackhues = getTrackHues(midifile);
|
|
|
|
if (lineQ) {
|
|
drawLines(notes, midifile, trackhues, options);
|
|
}
|
|
|
|
int minpitch = 0;
|
|
int maxpitch = 0;
|
|
|
|
// Draw background for increasing contrast of notes and background
|
|
// (needed due to constant opacity filter):
|
|
int track = 0;
|
|
if (!bwQ) {
|
|
for (int i=midifile.size()-1; i>=0; i--) {
|
|
if (!hasNotes(midifile[i])) {
|
|
continue;
|
|
}
|
|
getMinMaxTrackPitch(midifile[i], minpitch, maxpitch);
|
|
track = i;
|
|
notes << "\t\t<g"
|
|
<< " opacity=\"0.5\"";
|
|
notes << " class=\"note-backdrops " << "track-" << i << "\"";
|
|
if (trackhues[i] >= 0.0) {
|
|
if (bwQ) {
|
|
notes << " style=\""
|
|
<< "fill:none;"
|
|
<< "\"";
|
|
} else if (darkQ) {
|
|
notes << " style=\""
|
|
<< "fill:white;"
|
|
<< "\"";
|
|
} else {
|
|
notes << " style=\""
|
|
<< "fill:black;"
|
|
<< "\"";
|
|
}
|
|
}
|
|
notes << " >\n";
|
|
for (int j=0; j<midifile[i].size(); j++) {
|
|
if (!midifile[i][j].isNoteOn()) {
|
|
continue;
|
|
}
|
|
if (!drumQ) {
|
|
if (midifile[i][j].getChannel() == 0x09) {
|
|
continue;
|
|
}
|
|
}
|
|
drawNote(notes, midifile, i, j, 0, minpitch, maxpitch);
|
|
}
|
|
notes << "\t\t</g>\n";
|
|
}
|
|
}
|
|
|
|
// draw the actual notes:
|
|
for (int i=midifile.size()-1; i>=0; i--) {
|
|
if (!hasNotes(midifile[i])) {
|
|
continue;
|
|
}
|
|
track = i;
|
|
getMinMaxTrackPitch(midifile[i], minpitch, maxpitch);
|
|
notes << "\t\t<g"
|
|
<< " class=\"track-" << track << "\"";
|
|
if (transparentQ && !bwQ) {
|
|
notes << " filter=\"url(#constantOpacity)\"";
|
|
}
|
|
if (trackhues[i] >= 0.0) {
|
|
if (bwQ) {
|
|
notes << " style=\""
|
|
<< " fill:white;"
|
|
<< "\"";
|
|
} else {
|
|
notes << " style=\""
|
|
<< "opacity:" << Opacity << ";"
|
|
<< " fill:hsl(" << trackhues[i] << ", 100%, 75%);"
|
|
<< "\"";
|
|
}
|
|
}
|
|
notes << " >\n";
|
|
|
|
for (int j=0; j<midifile[i].size(); j++) {
|
|
if (!midifile[i][j].isNoteOn()) {
|
|
continue;
|
|
}
|
|
if (!drumQ) {
|
|
if (midifile[i][j].getChannel() == 0x09) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
drawNote(notes, midifile, i, j, dataQ, minpitch, maxpitch);
|
|
}
|
|
notes << "\t\t</g>\n";
|
|
}
|
|
notes << "\t</g>\n";
|
|
|
|
output << notes.str();
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawBrace -- Draw curley brace
|
|
//
|
|
|
|
void drawBrace(ostream& out) {
|
|
double unscale = 2.5 / AspectRatio;
|
|
string fill = ClefColor;
|
|
string stroke = ClefColor;
|
|
if (bwQ) {
|
|
fill = "transparent";
|
|
}
|
|
double strokewidth = StaffThickness * ClefFactor;
|
|
double xpos = 30.5;
|
|
double ypos = -324;
|
|
|
|
out << "<g transform=\"scale(" << unscale << ", 1)\">\n";
|
|
|
|
out <<
|
|
" <g class=\"brace\" transform=\"rotate(0.2) translate(" << xpos << "," << ypos << ") scale(1,-1) scale(.0175)\">\n"
|
|
" <path vector-effect=\"non-scaling-stroke\" stroke=\"" << stroke << "\" fill=\"" << fill << "\" stroke-width=\"" << strokewidth << "\" stroke-linejoin=\"round\" d=\"M-2031.812-22924.453\n"
|
|
" c-24.919,88.91-36.494,171.418-39.024,279.09c-2.379,101.156,9.877,207.264,16.991,305.533\n"
|
|
" c6.605,91.162,10.955,191.51-0.048,282.248c-2.757,22.732-8.614,43.227-13.597,63.254c-8.261,33.207-2.886,38.68,5.136,72.111\n"
|
|
" c10.485,43.689,11.808,90.887,13.172,140.795c3.604,132-16.215,270.613-24.675,400.074\n"
|
|
" c-8.818,134.883,3.651,255.861,34.113,366.164c-17.906-92.289-29.886-179.082-25.068-283.635\n"
|
|
" c4.572-99.275,18.379-195.207,24.078-294.23c5.11-88.826,9.914-203.154-8.344-284.342c-4.91-21.82-10.217-42.227-15.835-62.98\n"
|
|
" c-9.146-33.812-7.235-31.322,2.353-65.664c11.021-39.504,20.774-80.965,24.589-128.662c10.5-131.361-2.347-271.072-13.816-398.416\n"
|
|
" c-5.957-66.148-10.878-134.006-7.34-202.238C-2055.575-22803.877-2044.086-22863.117-2031.812-22924.453L-2031.812-22924.453z\"/>\n"
|
|
" </g>\n";
|
|
|
|
out << "</g>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawClefs --
|
|
//
|
|
|
|
void drawClefs(ostream& out) {
|
|
string fill = ClefColor;
|
|
string stroke = ClefColor;
|
|
double unscale = 2.5 / AspectRatio;
|
|
double strokewidth = StaffThickness * ClefFactor;
|
|
if (bwQ) {
|
|
fill = "transparent";
|
|
}
|
|
|
|
out << "<g transform=\"scale(" << unscale << ", 1)\">\n";
|
|
out <<
|
|
" <g vector-effect=\"non-scaling-stroke\" class=\"treble-clef\" stroke=\"" << stroke << "\" stroke-width=\"" << strokewidth << "\" fill=\"" << fill << "\" transform=\"translate(-17.25, 5.15) scale(0.06, 0.07)\">\n"
|
|
" <path vector-effect=\"non-scaling-stroke\" d=\"M250.026,1080.111c-2.375-5.813-4.501-12.037-5.616-19.656c-1.07-7.32-1.387-15.609-1.62-23.113\n"
|
|
" c-0.426-13.713,0.409-28.548,1.296-41.038c0.132-1.861,0.517-3.882,0.432-5.183c-0.118-1.794-1.622-3.834-2.484-5.401\n"
|
|
" c-4.551-8.266-9.023-16.291-13.176-25.488c-3.56-7.883-6.46-16.235-8.316-27.648c-1.087-6.682-1.406-14.26-2.053-21.816"
|
|
" c0-3.313,0-6.623,0-9.936c0.817-17.854,4.099-30.916,9.396-39.743c1.084-1.808,2.228-3.951,3.24-5.186\n"
|
|
" c6.218-7.579,16.081-9.873,25.812-7.343c0.598-8.887,1.858-19.53,2.376-28.944c0.193-3.49,0.54-7.194,0.54-10.368\n"
|
|
" c-0.003-13.193-3.44-20.603-7.452-26.135c-3.645-2.613-9.925-3.099-13.176,0.43c4.904-0.174,8.174,5.425,9.612,12.744\n"
|
|
" c2.427,12.351-1.738,24.386-7.344,25.918c-8.458,2.316-13.738-12.691-11.556-28.295c1.622-11.598,6.603-18.42,13.176-19.655\n"
|
|
" c5.073-0.956,10.149,0.426,13.608,5.399c1.988,2.858,4.009,7.685,4.968,11.879c1.246,5.448,2.18,12.212,2.16,18.792\n"
|
|
" c-0.01,3.246-0.453,6.761-0.648,10.152c-0.614,10.689-1.579,19.729-2.376,29.812c6.095,5.437,10.941,13.432,13.392,25.704\n"
|
|
" c1.302,6.52,2.035,15.517,1.727,23.111c-0.375,9.302-2.565,17.712-5.183,23.975c-3.863,9.248-9.852,13.985-17.604,14.257\n"
|
|
" c-0.849,9.749-1.633,19.633-2.593,29.159c2.376,4.619,4.55,9.643,6.805,14.906c2.185,5.095,3.892,11.286,5.508,17.712\n"
|
|
" c2.98,11.855,5.576,29.004,5.4,46.44c-0.102,10.157-1.538,19.319-3.24,27.215c-1.691,7.847-3.557,15.81-6.912,20.089\n"
|
|
" c-0.36,0-0.72,0-1.08,0C254.113,1090.306,252.068,1085.104,250.026,1080.111z M248.19,997.6\n"
|
|
" c-1.362,15.429-0.295,36.475,2.7,48.384c1.367,5.441,2.426,10.329,4.428,15.122c0.854,2.04,3.006,5.909,4.104,5.83\n"
|
|
" c2.104-0.153,3.064-9.245,3.24-13.824c0.542-14.092-2.623-28.387-5.832-37.366C254.247,1008.515,250.911,1002.572,248.19,997.6z\n"
|
|
" M249.27,935.823c-6.565-5.809-13.02-17.569-13.716-34.776c-0.425-10.517,1.463-18.693,3.888-24.624\n"
|
|
" c1.422-3.478,3.789-6.692,6.156-9.072c1.287-1.293,4.09-3.666,3.996,1.296c-0.048,2.584-2.504,4.06-3.888,6.263\n"
|
|
" c-2.557,4.079-4.489,12.399-3.348,21.385c0.481,3.784,1.636,7.983,2.808,10.153c1.458,2.697,3.725,4.34,6.048,5.615\n"
|
|
" c1.987-19.137,3.451-39.323,5.292-58.753c-4.493-1.347-9.224-1.49-13.068-0.213c-6.922,2.298-11.753,12.045-14.256,23.111\n"
|
|
" c-1.838,8.128-2.944,20.546-1.837,31.752c0.529,5.353,1.939,11.271,3.348,15.984c4.113,13.751,11.055,26.606,16.308,36.289\n"
|
|
" C247.971,952.523,248.466,943.862,249.27,935.823z M260.286,855.473c-1.479,19.361-3.545,38.701-4.86,57.671\n"
|
|
" c5.07-0.638,9.023-3.541,11.556-9.718c4.25-10.369,3.378-29.217-0.648-39.53C264.924,860.283,262.694,856.868,260.286,855.473z\"/>\n"
|
|
" </g>\n";
|
|
|
|
out <<
|
|
" <g class=\"bass-clef\" fill=\"" << fill << "\" stroke=\"" << stroke << "\" transform=\"scale(1.02, 1) translate(-0.25, 0.25)\" stroke-width=\"" << strokewidth << "\">\n"
|
|
" <g transform=\"matrix(0.1835537,0,0,0.1830159,-98.297967,-1128.8415)\">\n"
|
|
" <path vector-effect=\"non-scaling-stroke\" d=\"M514.308,6407.837c3.419,4.086,5.655,9.64,7.124,12.803\n"
|
|
" c2.204,4.648,4.046,10.097,5.538,16.384c1.492,6.287,2.238,13.068,2.238,20.385c0,4.115-0.338,7.925-0.991,11.507\n"
|
|
" c-0.653,3.543-1.527,6.4-2.647,8.459c-1.108,2.095-2.297,3.125-3.545,3.125c-1.142,0-2.262-0.687-3.369-2.135\n"
|
|
" c-1.119-1.41-2.029-3.505-2.752-6.325c-0.711-2.819-1.073-6.135-1.073-9.982c0-3.011,0.326-5.411,0.968-7.317\n"
|
|
" c0.653-1.867,1.446-2.78,2.379-2.78c0.513,0,0.991,0.342,1.422,1.104c0.431,0.724,0.769,1.753,1.014,3.011\n"
|
|
" c0.245,1.257,0.373,2.628,0.373,4.077c0,2.019-0.245,3.733-0.735,5.144c-0.501,1.41-1.049,2.132-1.667,2.132\n"
|
|
" c-0.21,0-0.466-0.152-0.781-0.38c-0.303-0.229-0.501-0.342-0.571-0.342c-0.14,0-0.292,0.152-0.466,0.495\n"
|
|
" c-0.175,0.342-0.268,0.799-0.292,1.41c0.07,0.495,0.117,0.838,0.117,1.104c0.338,2.896,0.944,4.953,1.796,6.249\n"
|
|
" c0.863,1.258,1.784,1.905,2.763,1.905c1.014,0,1.83-0.953,2.46-2.858c0.63-1.943,1.073-4.344,1.329-7.277\n"
|
|
" c0.268-2.896,0.396-5.981,0.396-9.22c-0.035-3.468-0.221-7.125-0.583-10.897c-0.361-3.81-0.804-7.163-1.317-10.059\n"
|
|
" c-0.653-3.925-1.458-7.468-2.414-10.593c-0.956-3.124-1.889-5.752-2.775-7.849c-0.886-2.133-2.731-5.973-4.104-9.297\n"
|
|
" C514,6409.468,514.126,6407.62,514.308,6407.837z\"/>\n"
|
|
" <path vector-effect=\"non-scaling-stroke\" d=\"M531.259,6465.557c0.313-0.979,0.705-1.415,1.189-1.415\n"
|
|
" c0.457,0,0.862,0.436,1.228,1.306c0.339,0.908,0.522,2.032,0.522,3.339c0,1.233-0.17,2.358-0.522,3.302\n"
|
|
" c-0.353,0.979-0.745,1.488-1.163,1.488c-0.483,0-0.888-0.436-1.215-1.343c-0.34-0.871-0.509-1.958-0.509-3.266\n"
|
|
" C530.789,6467.626,530.945,6466.5,531.259,6465.557z\"/>\n"
|
|
" <path vector-effect=\"non-scaling-stroke\" d=\"M531.272,6447.847c0.327-0.871,0.732-1.342,1.241-1.342\n"
|
|
" c0.457,0,0.849,0.471,1.189,1.378c0.327,0.908,0.497,2.033,0.497,3.447c0,1.162-0.183,2.214-0.522,3.193\n"
|
|
" c-0.366,0.98-0.745,1.452-1.163,1.452c-0.509,0-0.914-0.436-1.241-1.343c-0.313-0.87-0.483-1.996-0.483-3.301\n"
|
|
" C530.789,6449.879,530.958,6448.717,531.272,6447.847z\"/>\n"
|
|
" </g>\n"
|
|
" </g>\n";
|
|
|
|
out << "</g>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawStaves --
|
|
//
|
|
|
|
void drawStaves(ostream& out, double staffwidth, const string& staffcolor,
|
|
double totalduration) {
|
|
vector<double> vpos;
|
|
double unscale = 2.5 / AspectRatio;
|
|
if (diatonicQ) {
|
|
vpos.insert(vpos.end(), {37.5, 39.5, 41.5, 43.5, 45.5}); // treble clef
|
|
vpos.insert(vpos.end(), {25.5, 27.5, 29.5, 31.5, 33.5}); // bass clef
|
|
} else {
|
|
vpos.insert(vpos.end(), {64.5, 67.5, 71.5, 74.5, 77.5}); // treble clef
|
|
vpos.insert(vpos.end(), {43.5, 47.5, 50.5, 53.5, 57.5}); // bass clef
|
|
}
|
|
|
|
out << "\t<g"
|
|
<< " class=\"staff-lines\""
|
|
<< " stroke-width=\"" << staffwidth << "\""
|
|
<< " stroke=\"" << staffcolor << "\""
|
|
<< ">\n";
|
|
|
|
double start = 0.0;
|
|
if (clefQ) {
|
|
start = -4.65 * unscale;
|
|
}
|
|
double endx = totalduration + EndSpace;
|
|
for (int i=0; i<(int)vpos.size(); i++) {
|
|
out << "\t\t<path vector-effect=\"non-scaling-stroke\""
|
|
<< " d=\"M" << start << " " << vpos[i]
|
|
<< " L" << endx << " " << vpos[i] << "\" />\n";
|
|
}
|
|
|
|
double maxy = *max_element(vpos.begin(), vpos.end());
|
|
double miny = *min_element(vpos.begin(), vpos.end());
|
|
double thickness = 0.5 * unscale;
|
|
if (finalQ) {
|
|
out << "\t\t<path stroke=\"#cccccc\" fill=\"#cccccc\""
|
|
<< " d=\"M" << endx << "," << miny
|
|
<< " L" << endx << "," << maxy
|
|
<< " L" << endx - thickness << "," << maxy
|
|
<< " L" << endx - thickness << "," << miny
|
|
<< " z"
|
|
<< "\"/>\n";
|
|
|
|
out << "\t\t<path stroke=\"" << StaffColor << "\" fill=\"" << StaffColor << "\""
|
|
<< " d=\"M" << endx-thickness-thickness/2.0 << "," << miny
|
|
<< " L" << endx-thickness-thickness/2.0 << "," << maxy
|
|
<< "\"/>\n";
|
|
} else if (doubleQ) {
|
|
out << "\t\t<path vector-effect=\"non-scaling-stroke\""
|
|
<< " d=\"M" << endx-thickness << "," << miny
|
|
<< " L" << endx-thickness << "," << maxy
|
|
<< "\"/>\n";
|
|
out << "\t\t<path vector-effect=\"non-scaling-stroke\""
|
|
<< " d=\"M" << endx << "," << miny
|
|
<< " L" << endx << "," << maxy
|
|
<< "\"/>\n";
|
|
}
|
|
|
|
if (braceQ) {
|
|
// staffwidth = 5 * staffwidth;
|
|
out << "\t\t<path vector-effect=\"non-scaling-stroke\""
|
|
<< " d=\"M" << start << "," << miny
|
|
<< " L" << start << "," << maxy
|
|
<< " z"
|
|
<< "\"/>\n";
|
|
}
|
|
|
|
out << "\t</g>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawLines -- Draw lines to connect notes.
|
|
//
|
|
|
|
void drawLines(ostream& out, MidiFile& midifile, vector<double>& hues,
|
|
Options& options) {
|
|
int dashing = options.getBoolean("dash");
|
|
int track = 0;
|
|
for (int i=midifile.size()-1; i>=0; i--) {
|
|
if (!hasNotes(midifile[i])) {
|
|
continue;
|
|
}
|
|
track = i;
|
|
string color = "hsl(" + to_string(hues[i]) + ", 100%, 75%)";
|
|
if (bwQ) {
|
|
color = StaffColor;
|
|
}
|
|
out << "\t\t<g"
|
|
<< " class=\"note-lines track-" << track << "\""
|
|
<< " fill=\"none\" stroke=\"" << color << "\"";
|
|
|
|
// double scale = options.getDouble("scale");
|
|
if (dashing) {
|
|
double dwidth = 2.25;
|
|
out << " stroke-dasharray=\"" << dwidth << "\"";
|
|
out << " vector-effect=\"non-scaling-stroke\"";
|
|
}
|
|
out << " stroke-width=\""<< LineThickness << "\">\n";
|
|
for (int j=0; j<midifile[i].size(); j++) {
|
|
if (!midifile[i][j].isNoteOn()) {
|
|
continue;
|
|
}
|
|
printLineToNextNote(out, midifile, i, j, options);
|
|
}
|
|
out << "\t\t</g>\n";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// printLineToNextNote --
|
|
//
|
|
|
|
void printLineToNextNote(ostream& out, MidiFile& midifile, int track,
|
|
int index, Options& options) {
|
|
int p1 = midifile[track][index].getP1();
|
|
if (midifile[track][index].getChannel() == 9) {
|
|
p1 = PercussionMap[p1];
|
|
}
|
|
if (diatonicQ) {
|
|
p1 = base12ToBase7(p1);
|
|
}
|
|
double endtime = midifile[track][index].seconds
|
|
+ midifile[track][index].getDurationInSeconds();
|
|
|
|
stringstream path;
|
|
|
|
int nextindex = -1;
|
|
for (int i=index+1; i<midifile[track].size(); i++) {
|
|
if (!midifile[track][i].isNoteOn()) {
|
|
continue;
|
|
}
|
|
if (midifile[track][i].seconds >= endtime) {
|
|
nextindex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nextindex < 0) {
|
|
return;
|
|
}
|
|
|
|
int p2 = midifile[track][nextindex].getP1();
|
|
if (midifile[track][nextindex].getChannel() == 9) {
|
|
p2 = PercussionMap[p2];
|
|
}
|
|
if (diatonicQ) {
|
|
p2 = base12ToBase7(p2);
|
|
}
|
|
double nextstarttime = midifile[track][nextindex].seconds;
|
|
double difference = nextstarttime - endtime;
|
|
if (difference > MaxRest) {
|
|
// don't connect notes after long rests.
|
|
return;
|
|
}
|
|
|
|
double Ax = endtime;
|
|
double Ay = p1 + 0.5;
|
|
double Bx = nextstarttime;
|
|
double By = p2 + 0.5;
|
|
double Cx = nextstarttime;
|
|
double Cy = p1 + 0.5;
|
|
|
|
double tradius = radius;
|
|
if (tradius > fabs(Cx - Ax)) {
|
|
tradius = fabs(Cx - Ax);
|
|
}
|
|
if (tradius > fabs(By - Cy)) {
|
|
tradius = fabs(By - Cy);
|
|
}
|
|
|
|
double Rx = tradius / AspectRatio;
|
|
double Ry = tradius;
|
|
|
|
double Dx = Cx;
|
|
double Dy = Cy + Ry;
|
|
double Dxn = Cx;
|
|
double Dyn = Cy - Ry;
|
|
double Ex = Cx - Rx;
|
|
double Ey = Cy;
|
|
|
|
if (radiusQ && (radius > 0)) {
|
|
if (Ay < By) {
|
|
path << " M" << Ax << " " << Ay;
|
|
path << " L" << Ex << " " << Ey;
|
|
path << " A" << Rx << " " << Ry << " 0 0 1 ";
|
|
path << Dx << " " << Dy;
|
|
path << " L" << Bx << " " << By;
|
|
} else {
|
|
path << " M" << Ax << " " << Ay;
|
|
path << " L" << Ex << " " << Ey;
|
|
path << " A" << Rx << " " << Ry << " 0 0 0 ";
|
|
path << Dxn << " " << Dyn;
|
|
path << " L" << Bx << " " << By;
|
|
}
|
|
} else if (curveQ) {
|
|
if (difference > 0.0) {
|
|
path << " M" << Ax << "," << Ay;
|
|
path << " Q" << Cx << "," << Cy;
|
|
path << " " << Bx << " " << By;
|
|
} else {
|
|
// vertical line:
|
|
path << " M" << Cx << " " << Cy;
|
|
path << " L" << Bx << " " << By;
|
|
}
|
|
} else {
|
|
if (difference > 0.0) {
|
|
// there is a rest so extent the line horizontally
|
|
path << " M" << Ax << " " << Ay;
|
|
path << " L" << Cx << " " << Cy;
|
|
path << " L" << Bx << " " << By;
|
|
} else {
|
|
// vertical line:
|
|
path << " M" << Cx << " " << Cy;
|
|
path << " L" << Bx << " " << By;
|
|
}
|
|
}
|
|
|
|
|
|
out << "\t\t\t<path ";
|
|
out << " vector-effect=\"non-scaling-stroke\"";
|
|
out << " stroke-linejoin=\"round\"";
|
|
out << " d=\"" << path.str() << "\" />\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawNote --
|
|
//
|
|
|
|
void drawNote(ostream& out, MidiFile& midifile, int i, int j, int dataQ,
|
|
int minpitch, int maxpitch) {
|
|
int tickstart, tickend, tickdur;
|
|
double starttime, endtime, duration;
|
|
int height = 1;
|
|
tickstart = midifile[i][j].tick;
|
|
starttime = midifile[i][j].seconds;
|
|
if (midifile[i][j].isLinked()) {
|
|
tickdur = midifile[i][j].getTickDuration();
|
|
tickend = tickstart + tickdur;
|
|
duration = midifile[i][j].getDurationInSeconds();
|
|
endtime = starttime + duration;
|
|
} else {
|
|
tickdur = 0;
|
|
tickend = tickstart;
|
|
duration = 0.0;
|
|
endtime = starttime;
|
|
}
|
|
int pitch = midifile[i][j].getP1();
|
|
if (midifile[i][j].getChannel() == 9) {
|
|
pitch = PercussionMap[pitch];
|
|
}
|
|
int pitch12 = pitch;
|
|
if (diatonicQ) {
|
|
pitch = base12ToBase7(pitch);
|
|
}
|
|
int velocity = midifile[i][j].getP2();
|
|
int channel = midifile[i][j].getChannel(); // 0-offset
|
|
int track = i; // 0-offset
|
|
|
|
if (dataQ) {
|
|
out << "\t\t\t<!-- ==========================================" << endl;
|
|
out << "\t\t\t\t@Track: " << track << endl;
|
|
out << "\t\t\t\t@Pitch: " << pitch << endl;
|
|
out << "\t\t\t\t@Minpitch: " << minpitch << endl;
|
|
out << "\t\t\t\t@Maxpitch: " << maxpitch << endl;
|
|
out << "\t\t\t\t@Velocity: " << velocity << endl;
|
|
out << "\t\t\t\t@Channel: " << channel << endl;
|
|
out << "\t\t\t\t@Start-tick: " << tickstart << " ticks" << endl;
|
|
out << "\t\t\t\t@End-tick: " << tickend << " ticks" << endl;
|
|
out << "\t\t\t\t@Tick-dur: " << tickdur << " ticks" << endl;
|
|
out << "\t\t\t\t@Start-time: " << starttime << " seconds" << endl;
|
|
out << "\t\t\t\t@End-time: " << endtime << " seconds" << endl;
|
|
out << "\t\t\t\t@Duration: " << duration << " seconds" << endl;
|
|
out << "\t\t\t=========================================== -->" << endl;
|
|
}
|
|
|
|
|
|
// note box:
|
|
out << "\t\t\t<g";
|
|
if (velocitybQ) {
|
|
double bright = velocity / 127.0;
|
|
bright -= 0.2;
|
|
if (bright < 0.0) {
|
|
bright = 0.0;
|
|
}
|
|
bright *= 100;
|
|
bright = int(bright);
|
|
double hue = 0;
|
|
out << " fill=\"hsl(";
|
|
out << hue;
|
|
out << ",100%,";
|
|
out << bright;
|
|
out << "%)\"";
|
|
}
|
|
out << " class=\"note key-" << pitch12;
|
|
|
|
out << " ont-";
|
|
printDoubleClass(out, starttime);
|
|
|
|
out << " offt-";
|
|
printDoubleClass(out, starttime + duration);
|
|
|
|
if (pitch <= minpitch) {
|
|
out << " minima";
|
|
}
|
|
if (pitch >= maxpitch) {
|
|
out << " maxima";
|
|
}
|
|
out << "\"";
|
|
out << ">\n";
|
|
|
|
// string shape = "diamond";
|
|
// string shape = "rectangle";
|
|
// string shape = "eyelid";
|
|
// string shape = "hexthin";
|
|
// string shape = "hexthick";
|
|
// string shape = "plus";
|
|
// string shape = "round1";
|
|
// string shape = "round2";
|
|
// string shape = "round3";
|
|
// string shape = "round4";
|
|
// string shape = "oval";
|
|
// string shape = "antioval";
|
|
// string shape = "antiround";
|
|
// string shape = "antiround1";
|
|
// string shape = "antiround2";
|
|
// string shape = "antiround3";
|
|
// string shape = "antiround4";
|
|
// string shape = "triangleup";
|
|
// string shape = "triangledown";
|
|
// string shape = "triangleleft";
|
|
// string shape = "triangleright";
|
|
// string shape = "triangleroundleft";
|
|
// string shape = "triangleroundright";
|
|
// string shape = "curvedinner";
|
|
// string shape = "curvedinner1";
|
|
// string shape = "curvedinner2";
|
|
// string shape = "curvedinner3";
|
|
// string shape = "curvedinner4";
|
|
// string shape = "curvedouter";
|
|
// string shape = "curvedouter1";
|
|
// string shape = "curvedouter2";
|
|
// string shape = "curvedouter3";
|
|
// string shape = "curvedouter4";
|
|
string shape = getTrackShape(track);
|
|
drawNoteShape(out, shape, starttime, pitch, duration, height);
|
|
|
|
out << "\t\t\t</g>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// getTrackShape --
|
|
//
|
|
|
|
string getTrackShape(int track) {
|
|
if (track < 0) {
|
|
track = 0;
|
|
}
|
|
if (track > 0) {
|
|
track = track - 1;
|
|
}
|
|
if (track < (int)Shapes.size()) {
|
|
return Shapes[track];
|
|
} else {
|
|
return "rectangle";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawNoteShape -- Draw the desired note shape.
|
|
//
|
|
|
|
void drawNoteShape(ostream& out, string& shape, double x, double y,
|
|
double width, double height) {
|
|
if (shape == "rectangle") {
|
|
drawRectangle(out, x, y, width, height);
|
|
} else if (shape == "diamond") {
|
|
drawDiamond(out, x, y, width, height);
|
|
} else if (shape == "eyelid") {
|
|
drawEyelid(out, x, y, width, height);
|
|
} else if (shape == "hexthin") {
|
|
drawHexThin(out, x, y, width, height);
|
|
} else if (shape == "hexthick") {
|
|
drawHexThick(out, x, y, width, height);
|
|
} else if (shape == "plus") {
|
|
drawPlus(out, x, y, width, height);
|
|
} else if (shape == "round1") {
|
|
drawRound1(out, x, y, width, height);
|
|
} else if (shape == "round2") {
|
|
drawRound2(out, x, y, width, height);
|
|
} else if (shape == "round3") {
|
|
drawRound3(out, x, y, width, height);
|
|
} else if (shape == "round4") {
|
|
drawRound4(out, x, y, width, height);
|
|
} else if (shape == "oval") {
|
|
drawOval(out, x, y, width, height);
|
|
} else if (shape == "antioval") {
|
|
drawAntiOval(out, x, y, width, height);
|
|
} else if (shape == "antiround") {
|
|
drawAntiRound(out, x, y, width, height);
|
|
} else if (shape == "antiround1") {
|
|
drawAntiRound1(out, x, y, width, height);
|
|
} else if (shape == "antiround2") {
|
|
drawAntiRound2(out, x, y, width, height);
|
|
} else if (shape == "antiround3") {
|
|
drawAntiRound3(out, x, y, width, height);
|
|
} else if (shape == "antiround4") {
|
|
drawAntiRound4(out, x, y, width, height);
|
|
} else if (shape == "curvedinner") {
|
|
drawCurvedInner(out, x, y, width, height);
|
|
} else if (shape == "curvedinner1") {
|
|
drawCurvedInner1(out, x, y, width, height);
|
|
} else if (shape == "curvedinner2") {
|
|
drawCurvedInner2(out, x, y, width, height);
|
|
} else if (shape == "curvedinner3") {
|
|
drawCurvedInner3(out, x, y, width, height);
|
|
} else if (shape == "curvedinner4") {
|
|
drawCurvedInner4(out, x, y, width, height);
|
|
} else if (shape == "curvedouter") {
|
|
drawCurvedOuter(out, x, y, width, height);
|
|
} else if (shape == "curvedouter1") {
|
|
drawCurvedOuter1(out, x, y, width, height);
|
|
} else if (shape == "curvedouter2") {
|
|
drawCurvedOuter2(out, x, y, width, height);
|
|
} else if (shape == "curvedouter3") {
|
|
drawCurvedOuter3(out, x, y, width, height);
|
|
} else if (shape == "curvedouter4") {
|
|
drawCurvedOuter4(out, x, y, width, height);
|
|
} else if (shape == "triangleup") {
|
|
drawTriangleUp(out, x, y, width, height);
|
|
} else if (shape == "triangledown") {
|
|
drawTriangleDown(out, x, y, width, height);
|
|
} else if (shape == "triangleleft") {
|
|
drawTriangleLeft(out, x, y, width, height);
|
|
} else if (shape == "triangleright") {
|
|
drawTriangleRight(out, x, y, width, height);
|
|
} else if (shape == "triangleroundleft") {
|
|
drawTriangleRoundLeft(out, x, y, width, height);
|
|
} else if (shape == "triangleroundright") {
|
|
drawTriangleRoundRight(out, x, y, width, height);
|
|
} else {
|
|
drawRectangle(out, x, y, width, height);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawDiamond --
|
|
//
|
|
|
|
void drawDiamond(ostream& out, double x, double y, double width,
|
|
double height) {
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
|
|
out << "M " << x << " " << y+height/2.0;
|
|
out << " L " << x+width/2.0 << " " << y;
|
|
out << " L " << x+width << " " << y+height/2.0;
|
|
out << " L " << x+width/2.0 << " " << y+height;
|
|
out << " z";
|
|
out << "\"";
|
|
out << "/>\n";
|
|
|
|
//if (roundedQ) {
|
|
// out << "\trx=\"" << 1 << "\""
|
|
// << "\try=\"" << 1 << "\"";
|
|
//}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawEyelid --
|
|
//
|
|
|
|
void drawEyelid(ostream& out, double x, double y, double width,
|
|
double height) {
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << "M " << x << " " << y+height/2.0;
|
|
out << " Q " << x+width/2.0 << " " << y+height;
|
|
out << " " << x+width << " " << y+height/2.0;
|
|
out << " Q " << x+width/2.0 << " " << y;
|
|
out << " " << x << " " << y+height/2.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawHexThin --
|
|
//
|
|
|
|
void drawHexThin(ostream& out, double x, double y, double width,
|
|
double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << "M " << x << " " << y+h/3.0;
|
|
out << " L " << x << " " << y+h*2.0/3.0;
|
|
out << " L " << x+w/2.0 << " " << y+h;
|
|
out << " L " << x+w << " " << y+h*2.0/3.0;
|
|
out << " L " << x+w << " " << y+h/3.0;
|
|
out << " L " << x+w/2.0 << " " << y;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawHexThick --
|
|
//
|
|
|
|
void drawHexThick(ostream& out, double x, double y, double width,
|
|
double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double maxbevel = h/2.0/AspectRatio;
|
|
if (width*AspectRatio < height) {
|
|
maxbevel = w/2.0/AspectRatio;
|
|
}
|
|
double& b = maxbevel;
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << "M " << x << " " << y+h/2.0;
|
|
out << " L " << x+b << " " << y+h;
|
|
out << " L " << x+w-b << " " << y+h;
|
|
out << " L " << x+w << " " << y+h/2.0;
|
|
out << " L " << x+w-b << " " << y;
|
|
out << " L " << x+b << " " << y;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawRectangle --
|
|
//
|
|
|
|
void drawRectangle(ostream& out, double x, double y, double width,
|
|
double height) {
|
|
out << "\t\t\t\t<rect vector-effect=\"non-scaling-stroke\"";
|
|
if (roundedQ) {
|
|
out << "\trx=\"" << 1 << "\""
|
|
<< "\try=\"" << 1 << "\"";
|
|
}
|
|
out << "\tx=\"" << x << "\""
|
|
<< "\ty=\"" << y << "\""
|
|
<< "\twidth=\"" << width << "\""
|
|
<< "\theight=\"" << height << "\""
|
|
<< " />\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawTriangleUp --
|
|
//
|
|
|
|
void drawTriangleUp(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << "M " << x << " " << y;
|
|
out << " L " << x+w/2 << " " << y2;
|
|
out << " L " << x2 << " " << y;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawTriangleDown --
|
|
//
|
|
|
|
void drawTriangleDown(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << "M " << x << " " << y2;
|
|
out << " L " << x+w/2 << " " << y;
|
|
out << " L " << x2 << " " << y2;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawTriangleLeft --
|
|
//
|
|
|
|
void drawTriangleLeft(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << "M " << x << " " << y+h/2;
|
|
out << " L " << x2 << " " << y2;
|
|
out << " L " << x2 << " " << y;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawTriangleRight --
|
|
//
|
|
|
|
void drawTriangleRight(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << "M " << x2 << " " << y+h/2;
|
|
out << " L " << x << " " << y2;
|
|
out << " L " << x << " " << y;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawTriangleRoundLeft --
|
|
//
|
|
|
|
void drawTriangleRoundLeft(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double a = AspectRatio;
|
|
double wa = w * a;
|
|
double x2 = x+w;
|
|
double r = h/2.0;
|
|
double q = sqrt(wa*wa+r*r);
|
|
double ww = (w*a*q-w*a*r)/q;
|
|
double rr = (r*q-r*r)/q;
|
|
double xa = x + ww/a;
|
|
double ya = y + r + rr;
|
|
double xb = xa;
|
|
double yb = y + r - rr;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << "M " << x << " " << y+r;
|
|
out << " L " << xa << " " << ya;
|
|
out << " A " << r << " " << r << " 0 0 0 " << x2 << " " << y+r;
|
|
out << " A " << r << " " << r << " 0 0 0 " << xb << " " << yb;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawTriangleRoundRight --
|
|
//
|
|
|
|
void drawTriangleRoundRight(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << "M " << x << " " << y+h/2;
|
|
out << " L " << x2 << " " << y2;
|
|
out << " L " << x2 << " " << y;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawPlus --
|
|
//
|
|
|
|
void drawPlus(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " L " << x+d << " " << y+h*3.0/4.0;
|
|
out << " L " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " L " << x2-d << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " L " << x2-d << " " << y+h/4.0;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " L " << x+d << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedInner --
|
|
//
|
|
|
|
void drawCurvedInner(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedInner1 --
|
|
//
|
|
|
|
void drawCurvedInner1(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedInner2 --
|
|
//
|
|
|
|
void drawCurvedInner2(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedInner3 --
|
|
//
|
|
|
|
void drawCurvedInner3(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedInner4 --
|
|
//
|
|
|
|
void drawCurvedInner4(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedOuter --
|
|
//
|
|
|
|
void drawCurvedOuter(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedOuter1 --
|
|
//
|
|
|
|
void drawCurvedOuter1(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedOuter2 --
|
|
//
|
|
|
|
void drawCurvedOuter2(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedOuter3 --
|
|
//
|
|
|
|
void drawCurvedOuter3(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawCurvedOuter4 --
|
|
//
|
|
|
|
void drawCurvedOuter4(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/4;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+h/4.0;
|
|
out << " L " << x << " " << y+h*3.0/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h*3.0/4.0;
|
|
out << " L " << x2 << " " << y+h/4.0;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+h/4.0;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawOval --
|
|
//
|
|
|
|
void drawOval(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawAntiOval --
|
|
//
|
|
|
|
void drawAntiOval(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = 2.0;
|
|
if (d > w / 3) {
|
|
d = w/3;
|
|
}
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawAntiRound --
|
|
//
|
|
|
|
void drawAntiRound(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = w/2.0;
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawAntiRound1 --
|
|
//
|
|
|
|
void drawAntiRound1(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = w/2.0;
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawAntiRound2 --
|
|
//
|
|
|
|
void drawAntiRound2(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = w/2.0;
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawAntiRound3 --
|
|
//
|
|
|
|
void drawAntiRound3(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = w/2.0;
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawAntiRound4 --
|
|
//
|
|
|
|
void drawAntiRound4(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = w/2.0;
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawRound1 --
|
|
//
|
|
|
|
void drawRound1(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = w/2.0;
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawRound2 --
|
|
//
|
|
|
|
void drawRound2(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = w/2.0;
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawRound3 --
|
|
//
|
|
|
|
void drawRound3(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = w/2.0;
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// drawRound4 --
|
|
//
|
|
|
|
void drawRound4(ostream& out, double x, double y, double width, double height) {
|
|
double& h = height;
|
|
double& w = width;
|
|
double x2 = x+w;
|
|
double y2 = y+h;
|
|
double m = h/2.0;
|
|
double d = w/2.0;
|
|
double Rx = d;
|
|
double Ry = h/2;
|
|
|
|
out << "\t\t\t\t<path vector-effect=\"non-scaling-stroke\" d=\"";
|
|
out << " M " << x << " " << y+m;
|
|
out << " L " << x << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x+d << " " << y2;
|
|
out << " L " << x2-d << " " << y2;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " L " << x2 << " " << y+m;
|
|
out << " A " << Rx << " " << Ry << " 0 0 0 " << x2-d << " " << y;
|
|
out << " L " << x2-d << " " << y;
|
|
out << " L " << x+d << " " << y;
|
|
out << " A " << Rx << " " << Ry << " 0 0 1 " << x << " " << y+m;
|
|
out << " z\"";
|
|
out << "/>\n";
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// base12ToBase7 -- MIDI to diatonic pitch name. Middle C is 5th octave.
|
|
//
|
|
|
|
int base12ToBase7(int pitch) {
|
|
int octave = pitch / 12;
|
|
int chroma = pitch % 12;
|
|
int output = 0;
|
|
switch (chroma) {
|
|
case 0: output = 0; break; // C
|
|
case 1: output = 0; break; // C#
|
|
case 2: output = 1; break; // D
|
|
case 3: output = 2; break; // Eb
|
|
case 4: output = 2; break; // E
|
|
case 5: output = 3; break; // F
|
|
case 6: output = 3; break; // F#
|
|
case 7: output = 4; break; // G
|
|
case 8: output = 4; break; // G#
|
|
case 9: output = 5; break; // A
|
|
case 10: output = 6; break; // Bb
|
|
case 11: output = 6; break; // B
|
|
}
|
|
return output + 7 * octave;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// printDoubleClass -- print a double note with a "d" instead of
|
|
// decimal point.
|
|
//
|
|
|
|
void printDoubleClass(ostream& out, double value) {
|
|
value = int(value * 1000.0 + 0.5)/1000.0;
|
|
char buffer[32] = {0};
|
|
snprintf(buffer, 32, "%.3lf", value);
|
|
char* decimal = strchr(buffer, '.');
|
|
if (decimal != NULL) {
|
|
decimal[0] = 'd';
|
|
}
|
|
out << buffer;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// getTrackHues -- Assign track colors by maximally spaced hue. Maybe
|
|
// shuffle or randomize if there are a lot of tracks.
|
|
//
|
|
|
|
vector<double> getTrackHues(MidiFile& midifile) {
|
|
vector<double> output;
|
|
output.resize(midifile.size());
|
|
fill(output.begin(), output.end(), -1);
|
|
int tcount = 0;
|
|
int i;
|
|
for (i=0; i<midifile.size(); i++) {
|
|
if (hasNotes(midifile[i])) {
|
|
tcount++;
|
|
}
|
|
}
|
|
int count = 0;
|
|
double hue = 0;
|
|
for (i=0; i<midifile.size(); i++) {
|
|
if (!hasNotes(midifile[i])) {
|
|
continue;
|
|
}
|
|
hue = (double)count / (double)tcount * 360.0;
|
|
count++;
|
|
output[i] = hue;
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// hasNotes -- Return true if a track has any notes-ons in it.
|
|
//
|
|
|
|
int hasNotes(MidiEventList& eventlist) {
|
|
for (int i=0; i<eventlist.size(); i++) {
|
|
if (eventlist[i].isNoteOn()) {
|
|
if (drumQ) {
|
|
return 1;
|
|
} else if (eventlist[i].getChannel() != 0x09) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// getMinMaxPitch -- Determine the minimum and maximum pitch in the file.
|
|
//
|
|
|
|
void getMinMaxPitch(const MidiFile& midifile, int& minpitch, int& maxpitch) {
|
|
int key = 0;
|
|
for (int i=0; i<midifile.size(); i++) {
|
|
for (int j=0; j<midifile[i].size(); j++) {
|
|
if (midifile[i][j].isNoteOn()) {
|
|
key = midifile[i][j].getP1();
|
|
if ((minpitch < 0) || (minpitch > key)) {
|
|
minpitch = key;
|
|
}
|
|
if ((maxpitch < 0) || (maxpitch < key)) {
|
|
maxpitch = key;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (grandQ) {
|
|
if (minpitch > 40) {
|
|
minpitch = 40;
|
|
}
|
|
if (maxpitch < 80) {
|
|
maxpitch = 80;
|
|
}
|
|
}
|
|
if (diatonicQ) {
|
|
minpitch = base12ToBase7(minpitch);
|
|
maxpitch = base12ToBase7(maxpitch);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// getMinMaxTrackPitch -- Determine the minimum and maximum pitch in a
|
|
// particular Track.
|
|
//
|
|
|
|
void getMinMaxTrackPitch(const MidiEventList& evl, int& minpitch,
|
|
int &maxpitch) {
|
|
int key = 0;
|
|
minpitch = -1;
|
|
maxpitch = -1;
|
|
for (int i=0; i<evl.size(); i++) {
|
|
if (evl[i].isNoteOn()) {
|
|
key = evl[i].getP1();
|
|
if ((minpitch < 0) || (minpitch > key)) {
|
|
minpitch = key;
|
|
}
|
|
if ((maxpitch < 0) || (maxpitch < key)) {
|
|
maxpitch = key;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (diatonicQ) {
|
|
minpitch = base12ToBase7(minpitch);
|
|
maxpitch = base12ToBase7(maxpitch);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// getMaxTime -- return the ending time of the last note in any track.
|
|
//
|
|
|
|
double getMaxTime(const MidiFile& midifile) {
|
|
double maxtime = 0.0;
|
|
for (int i=0; i<midifile.size(); i++) {
|
|
for (int j=midifile[i].size()-1; j>=0; j--) {
|
|
if (midifile[i][j].isNoteOff()) {
|
|
if (maxtime < midifile[i][j].seconds) {
|
|
maxtime = midifile[i][j].seconds;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return maxtime;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// checkOptions --
|
|
//
|
|
|
|
void checkOptions(Options& opts, int argc, char* argv[]) {
|
|
opts.define("a|aspect-ratio=d:2.5", "Aspect ratio for SVG image");
|
|
opts.define("w|stroke-width=d:0.1", "Stroke width for line around note boxes");
|
|
opts.define("stroke-color=s:black", "Stroke color for line around note boxes");
|
|
opts.define("staff=b", "Draw staff lines.");
|
|
opts.define("gs|grand|grand-staff=b", "show at least all grand staff.");
|
|
opts.define("sc|staff-color=s:#555555", "staff line color.");
|
|
opts.define("cc|clef-color=s:#cdcdcd", "clef fill/stroke color.");
|
|
opts.define("sw|st|staff-width|staff-thickness=d:0.5", "staff line width.");
|
|
opts.define("lw|lt|line-width|line-thickness=d:0.5", "Width of note lines");
|
|
opts.define("dash|dashing=b", "Dash connecting lines");
|
|
opts.define("T|no-transparency=b", "Do not show notes with transparency");
|
|
opts.define("s|scale=d:1.0", "Scaling factor for SVG image");
|
|
opts.define("d|data=b", "Embed note data in SVG image");
|
|
opts.define("f|final|final-barline=b", "draw final barline");
|
|
opts.define("double|double-barline=b", "draw final double barline");
|
|
opts.define("bw|black-and-white=b", "Display as black and white (outlines only)");
|
|
opts.define("diatonic=b", "Vertical axis is base-7 pitch");
|
|
opts.define("drum=b", "Show drum track (channel 10)");
|
|
opts.define("pm|perc|percussion-map=s", "Map percussion notes to different pitch");
|
|
opts.define("r|round|rounded=b", "Round edges of note boxes");
|
|
opts.define("b|border=d:1.0", "Border around piano roll");
|
|
opts.define("dark=b", "Background is black");
|
|
opts.define("o|opacity=d:1.0", "Opacity for notes");
|
|
opts.define("l|line=b", "Draw lines between center of notes");
|
|
opts.define("cl|cline=b", "Draw curved lines between notes");
|
|
opts.define("rl|rline=d:0.25", "Draw lines with curved radius between notes");
|
|
opts.define("e|end-space=d:0.0", "extra horiz. space at end of piece");
|
|
opts.define("c|clef|clefs=b", "Draw clefs");
|
|
opts.define("v|velocity-brightness=b", "Show velocity as brightness on note");
|
|
opts.define("S|shapes=s:rectangle,rectangle", "shape of notes for each track");
|
|
opts.define("mr|rest|max-rest=d:4.0 seconds",
|
|
"Maximum rest through which to draw lines");
|
|
|
|
opts.define("author=b", "author of program");
|
|
opts.define("version=b", "compilation info");
|
|
opts.define("example=b", "example usages");
|
|
opts.define("h|help=b", "short description");
|
|
opts.process(argc, argv);
|
|
|
|
// handle basic options:
|
|
if (opts.getBoolean("author")) {
|
|
cout << "Written by Craig Stuart Sapp, "
|
|
<< "craig@ccrma.stanford.edu, 20 February 2016" << endl;
|
|
exit(0);
|
|
} else if (opts.getBoolean("version")) {
|
|
cout << argv[0] << ", version: 20 February 2016" << endl;
|
|
cout << "compiled: " << __DATE__ << endl;
|
|
exit(0);
|
|
} else if (opts.getBoolean("help")) {
|
|
usage(opts.getCommand().c_str());
|
|
exit(0);
|
|
} else if (opts.getBoolean("example")) {
|
|
example();
|
|
exit(0);
|
|
}
|
|
|
|
if (opts.getArgCount() > 1) {
|
|
usage(opts.getCommand().c_str());
|
|
exit(1);
|
|
}
|
|
|
|
dataQ = opts.getBoolean("data");
|
|
drumQ = opts.getBoolean("drum");
|
|
darkQ = opts.getBoolean("dark");
|
|
lineQ = opts.getBoolean("line");
|
|
curveQ = opts.getBoolean("cline");
|
|
if (curveQ) {
|
|
lineQ = 1;
|
|
}
|
|
radiusQ = opts.getBoolean("rline");
|
|
if (radiusQ) {
|
|
lineQ = 1;
|
|
curveQ = 1;
|
|
radius = opts.getDouble("rline");
|
|
}
|
|
clefQ = opts.getBoolean("clefs");
|
|
staffQ = opts.getBoolean("staff");
|
|
grandQ = opts.getBoolean("grand-staff");
|
|
bwQ = opts.getBoolean("black-and-white");
|
|
diatonicQ = opts.getBoolean("diatonic");
|
|
transparentQ = !opts.getBoolean("no-transparency");
|
|
roundedQ = opts.getBoolean("rounded");
|
|
Scale = opts.getDouble("scale");
|
|
Border = opts.getDouble("border");
|
|
AspectRatio = opts.getDouble("aspect-ratio");
|
|
Opacity = opts.getDouble("opacity");
|
|
MaxRest = opts.getDouble("max-rest");
|
|
if (clefQ) {
|
|
staffQ = 1;
|
|
braceQ = 1;
|
|
}
|
|
|
|
PercussionMap.resize(128);
|
|
for (int i=0; i<(int)PercussionMap.size(); i++) {
|
|
PercussionMap[i] = i;
|
|
}
|
|
percmapQ = opts.getBoolean("percussion-map");
|
|
if (percmapQ) {
|
|
makeMappings(PercussionMap, opts.getString("percussion-map"));
|
|
drumQ = 1;
|
|
}
|
|
|
|
finalQ = opts.getBoolean("final-barline");
|
|
doubleQ = opts.getBoolean("double-barline");
|
|
EndSpace = opts.getDouble("end-space");
|
|
if (finalQ || doubleQ) {
|
|
EndSpace += 2.0;
|
|
}
|
|
StaffThickness = opts.getDouble("staff-width");
|
|
if (bwQ) {
|
|
StaffThickness = StaffThickness * 0.25;
|
|
}
|
|
StaffColor = opts.getString("staff-color");
|
|
ClefColor = opts.getString("clef-color");
|
|
if (!opts.getBoolean("line-width")) {
|
|
LineThickness = StaffThickness;
|
|
}
|
|
|
|
velocitybQ = opts.getBoolean("velocity-brightness");
|
|
char buffer[12345] = {0};
|
|
strcpy(buffer, opts.getString("shapes").c_str());
|
|
Shapes.resize(0);
|
|
string current;
|
|
const char* spacers = "\t :,;|\n";
|
|
char* ptr = strtok(buffer, spacers);
|
|
while (ptr != NULL) {
|
|
current = ptr;
|
|
if (current == "r") {
|
|
Shapes.push_back("rectangle");
|
|
} else if (current == "e") {
|
|
Shapes.push_back("eyelid");
|
|
} else if (current == "d") {
|
|
Shapes.push_back("diamond");
|
|
} else if (current == "h") {
|
|
Shapes.push_back("hexthin");
|
|
} else if (current == "H") {
|
|
Shapes.push_back("hexthick");
|
|
} else if (current == "p") {
|
|
Shapes.push_back("plus");
|
|
} else if (current == "r1") {
|
|
Shapes.push_back("round1");
|
|
} else if (current == "r2") {
|
|
Shapes.push_back("round2");
|
|
} else if (current == "r3") {
|
|
Shapes.push_back("round3");
|
|
} else if (current == "r4") {
|
|
Shapes.push_back("round4");
|
|
} else if (current == "o") {
|
|
Shapes.push_back("oval");
|
|
} else if (current == "O") {
|
|
Shapes.push_back("antioval");
|
|
} else if (current == "R") {
|
|
Shapes.push_back("antiround");
|
|
} else if (current == "R1") {
|
|
Shapes.push_back("antiround1");
|
|
} else if (current == "R2") {
|
|
Shapes.push_back("antiround2");
|
|
} else if (current == "R3") {
|
|
Shapes.push_back("antiround3");
|
|
} else if (current == "R4") {
|
|
Shapes.push_back("antiround4");
|
|
} else if (current == "c") {
|
|
Shapes.push_back("curvedinner");
|
|
} else if (current == "c1") {
|
|
Shapes.push_back("curvedinner1");
|
|
} else if (current == "c2") {
|
|
Shapes.push_back("curvedinner2");
|
|
} else if (current == "c3") {
|
|
Shapes.push_back("curvedinner3");
|
|
} else if (current == "c4") {
|
|
Shapes.push_back("curvedinner4");
|
|
} else if (current == "C") {
|
|
Shapes.push_back("curvedouter");
|
|
} else if (current == "C1") {
|
|
Shapes.push_back("curvedouter1");
|
|
} else if (current == "C2") {
|
|
Shapes.push_back("curvedouter2");
|
|
} else if (current == "C3") {
|
|
Shapes.push_back("curvedouter3");
|
|
} else if (current == "C4") {
|
|
Shapes.push_back("curvedouter4");
|
|
} else if (current == "tu") {
|
|
Shapes.push_back("triangleup");
|
|
} else if (current == "td") {
|
|
Shapes.push_back("triangledown");
|
|
} else if (current == "tl") {
|
|
Shapes.push_back("triangleleft");
|
|
} else if (current == "tr") {
|
|
Shapes.push_back("triangleright");
|
|
} else if (current == "trl") {
|
|
Shapes.push_back("triangleroundleft");
|
|
} else if (current == "trr") {
|
|
Shapes.push_back("triangleroundright");
|
|
} else {
|
|
Shapes.push_back(current);
|
|
}
|
|
ptr = strtok(NULL, spacers);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// makeMappings -- Manually move percussion notes to other locations in pitch range.
|
|
// Maybe add separate coloring of percussion instruments.
|
|
// Maybe need to add automatic note offs for persussion instruments (as they
|
|
// do not always have note-offs.
|
|
//
|
|
// --perc "60>40, 61>51,62>44"
|
|
//
|
|
|
|
void makeMappings(vector<int>& mapping, const string& mapstring) {
|
|
string newmap = mapstring + ' ';
|
|
int ltx = 0;
|
|
int d = 1;
|
|
int digit1 = 0;
|
|
int digit2 = 0;
|
|
int ii;
|
|
for (ii=0; ii<(int)newmap.size(); ii++) {
|
|
if (isdigit(newmap[ii])) {
|
|
break;
|
|
}
|
|
}
|
|
for (int i=ii; i<(int)newmap.size(); i++) {
|
|
if (isdigit(newmap[i])) {
|
|
if (d) {
|
|
digit1 = digit1 * 10 + (newmap[i] - '0');
|
|
} else {
|
|
digit2 = digit2 * 10 + (newmap[i] - '0');
|
|
}
|
|
ltx = 0;
|
|
} else {
|
|
if (!ltx) {
|
|
d = !d;
|
|
}
|
|
ltx = 1;
|
|
if (d) {
|
|
digit1 = digit1 > 127 ? 127 : digit1;
|
|
digit1 = digit1 < 0 ? 0 : digit1;
|
|
digit2 = digit2 > 127 ? 127 : digit2;
|
|
digit2 = digit2 < 0 ? 0 : digit2;
|
|
mapping[digit1] = digit2;
|
|
digit1 = 0;
|
|
digit2 = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// example --
|
|
//
|
|
|
|
void example(void) {
|
|
// add example usages here
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////
|
|
//
|
|
// usage --
|
|
//
|
|
|
|
void usage(const char* command) {
|
|
cout << "Usage: " << command << " input.mid > output.svg" << endl;
|
|
}
|
|
|
|
|
|
|