Skip to content

Commit

Permalink
Split each part of a chord into separate svg paths; add start and end…
Browse files Browse the repository at this point in the history
… char to more elements; add "data-name" attribute for identifying the svg elements; return more info on mouse click.
  • Loading branch information
paulrosen committed May 31, 2021
1 parent e402b22 commit 5c6c1e2
Show file tree
Hide file tree
Showing 29 changed files with 658 additions and 182 deletions.
23 changes: 2 additions & 21 deletions src/api/abc_tunebook_svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ function renderEachLineSeparately(div, tune, params, tuneNumber) {
obj.formatting = tune.formatting;
obj.media = tune.media;
obj.version = tune.version;
obj.metaText = {};
obj.lines = [];
return obj;
}

Expand All @@ -80,14 +78,7 @@ function renderEachLineSeparately(div, tune, params, tuneNumber) {

if (i === 0) {
// These items go on top of the music
tuneLine.metaText.tempo = tune.metaText.tempo;
tuneLine.metaText.title = tune.metaText.title;
tuneLine.metaText.header = tune.metaText.header;
tuneLine.metaText.rhythm = tune.metaText.rhythm;
tuneLine.metaText.origin = tune.metaText.origin;
tuneLine.metaText.composer = tune.metaText.composer;
tuneLine.metaText.author = tune.metaText.author;
tuneLine.metaText.partOrder = tune.metaText.partOrder;
tuneLine.copyTopInfo(tune);
}

// push the lines until we get to a music line
Expand All @@ -106,17 +97,7 @@ function renderEachLineSeparately(div, tune, params, tuneNumber) {

// These items go below the music
tuneLine = tunes[tunes.length-1];
tuneLine.metaText.unalignedWords = tune.metaText.unalignedWords;
tuneLine.metaText.book = tune.metaText.book;
tuneLine.metaText.source = tune.metaText.source;
tuneLine.metaText.discography = tune.metaText.discography;
tuneLine.metaText.notes = tune.metaText.notes;
tuneLine.metaText.transcription = tune.metaText.transcription;
tuneLine.metaText.history = tune.metaText.history;
tuneLine.metaText['abc-copyright'] = tune.metaText['abc-copyright'];
tuneLine.metaText['abc-creator'] = tune.metaText['abc-creator'];
tuneLine.metaText['abc-edited-by'] = tune.metaText['abc-edited-by'];
tuneLine.metaText.footer = tune.metaText.footer;
tuneLine.copyBottomInfo(tune);

// Now create sub-divs and render each line. Need to copy the params to change the padding for the interior slices.
var ep = {};
Expand Down
42 changes: 42 additions & 0 deletions src/data/abc_tune.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,48 @@ var delineTune = require("./deline-tune");
* @alternateClassName ABCJS.Tune
*/
var Tune = function() {
this.reset = function () {
this.version = "1.1.0";
this.media = "screen";
this.metaText = {};
this.metaTextInfo = {};
this.formatting = {};
this.lines = [];
this.staffNum = 0;
this.voiceNum = 0;
this.lineNum = 0;
this.runningFonts = {};
delete this.visualTranspose;
};
this.reset();

function copy(src, prop, attrs) {
for (var i = 0; i < attrs.length; i++)
this[prop][attrs[i]] = src[prop][attrs[i]];
}

this.copyTopInfo = function(src) {
var attrs = ['tempo', 'title', 'header', 'rhythm', 'origin', 'composer', 'author', 'partOrder'];
copy(src, "metaText", attrs);
copy(src, "metaTextInfo", attrs);
};

this.copyBottomInfo = function(src) {
var attrs = ['unalignedWords',
'book',
'source',
'discography',
'notes',
'transcription',
'history',
'abc-copyright',
'abc-creator',
'abc-edited-by',
'footer']
copy(src, "metaText", attrs);
copy(src, "metaTextInfo", attrs);
};

// The structure consists of a hash with the following two items:
// metaText: a hash of {key, value}, where key is one of: title, author, rhythm, source, transcription, unalignedWords, etc...
// tempo: { noteLength: number (e.g. .125), bpm: number }
Expand Down
3 changes: 2 additions & 1 deletion src/parse/abc_parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var Parse = function() {
lines: tune.lines,
media: tune.media,
metaText: tune.metaText,
metaTextInfo: tune.metaTextInfo,
version: tune.version,

addElementToEvents: tune.addElementToEvents,
Expand Down Expand Up @@ -473,7 +474,7 @@ var Parse = function() {
// switches.transpose: change the key signature, chords, and notes by a number of half-steps.
if (!switches) switches = {};
if (!startPos) startPos = 0;
tuneBuilder.reset();
tune.reset();

// Take care of whatever line endings come our way
// Tack on newline temporarily to make the last line continuation work
Expand Down
4 changes: 2 additions & 2 deletions src/parse/abc_parse_directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -1067,7 +1067,7 @@ var parseDirective = {};
case "-version":
case "-charset":
var subCmd = arr.shift();
tuneBuilder.addMetaText(cmd+subCmd, arr.join(' '));
tuneBuilder.addMetaText(cmd+subCmd, arr.join(' '), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+restOfString.length+5});
break;
default:
return "Unknown directive: " + cmd+arr[0];
Expand All @@ -1090,7 +1090,7 @@ var parseDirective = {};
if (footerArr.length > 3)
warn("Too many tabs in " + cmd + ": " + footerArr.length + " found.", restOfString, 0);

tuneBuilder.addMetaTextObj(cmd, footer);
tuneBuilder.addMetaTextObj(cmd, footer, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+str.length});
break;

case "midi":
Expand Down
14 changes: 7 additions & 7 deletions src/parse/abc_parse_header.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {

this.setTitle = function(title) {
if (multilineVars.hasMainTitle)
tuneBuilder.addSubtitle(tokenizer.translateString(tokenizer.stripComment(title))); // display secondary title
tuneBuilder.addSubtitle(tokenizer.translateString(tokenizer.stripComment(title)), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+title.length+2}); // display secondary title
else
{
var titleStr = tokenizer.translateString(tokenizer.theReverser(tokenizer.stripComment(title)));
if (multilineVars.titlecaps)
titleStr = titleStr.toUpperCase();
tuneBuilder.addMetaText("title", titleStr);
tuneBuilder.addMetaText("title", titleStr, { startChar: multilineVars.iChar, endChar: multilineVars.iChar+title.length+2});
multilineVars.hasMainTitle = true;
}
};
Expand Down Expand Up @@ -477,21 +477,21 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
var field = metaTextHeaders[line.charAt(0)];
if (field !== undefined) {
if (field === 'unalignedWords')
tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line.substring(2)))));
tuneBuilder.addMetaTextArray(field, parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line.substring(2)))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
else
tuneBuilder.addMetaText(field, tokenizer.translateString(tokenizer.stripComment(line.substring(2))));
tuneBuilder.addMetaText(field, tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
return {};
} else {
var startChar = multilineVars.iChar;
var endChar = startChar + line.length;
switch(line.charAt(0))
{
case 'H':
tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line.substring(2))));
tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
line = tokenizer.peekLine()
while (line && line.charAt(1) !== ':') {
tokenizer.nextLine()
tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line)));
tuneBuilder.addMetaText("history", tokenizer.translateString(tokenizer.stripComment(line)), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
line = tokenizer.peekLine()
}
break;
Expand All @@ -516,7 +516,7 @@ var ParseHeader = function(tokenizer, warn, multilineVars, tune, tuneBuilder) {
case 'P':
// TODO-PER: There is more to do with parts, but the writer doesn't care.
if (multilineVars.is_in_header)
tuneBuilder.addMetaText("partOrder", tokenizer.translateString(tokenizer.stripComment(line.substring(2))));
tuneBuilder.addMetaText("partOrder", tokenizer.translateString(tokenizer.stripComment(line.substring(2))), { startChar: multilineVars.iChar, endChar: multilineVars.iChar+line.length});
else
multilineVars.partForNextLine = { title: tokenizer.translateString(tokenizer.stripComment(line.substring(2))), startChar: startChar, endChar: endChar};
break;
Expand Down
10 changes: 7 additions & 3 deletions src/parse/abc_parse_music.js
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,7 @@ MusicParser.prototype.parseMusic = function(line) {
// TODO-PER: straighten this out so there is not so much copying: getCoreNote shouldn't change e'
if (core.accidental !== undefined) el.pitches[0].accidental = core.accidental;
el.pitches[0].pitch = core.pitch;
el.pitches[0].name = core.name;
if (core.midipitch || core.midipitch === 0)
el.pitches[0].midipitch = core.midipitch;
if (core.endSlur !== undefined) el.pitches[0].endSlur = core.endSlur;
Expand Down Expand Up @@ -1063,6 +1064,7 @@ var addEndBeam = function(el) {

var pitches = {A: 5, B: 6, C: 0, D: 1, E: 2, F: 3, G: 4, a: 12, b: 13, c: 7, d: 8, e: 9, f: 10, g: 11};
var rests = {x: 'invisible', X: 'invisible-multimeasure', y: 'spacer', z: 'rest', Z: 'multimeasure' };
var accMap = { 'dblflat': '__', 'flat': '_', 'natural': '=', 'sharp': '^', 'dblsharp': '^^', 'quarterflat': '_/', 'quartersharp': '^/'};
var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
//var el = { startChar: index };
var isComplete = function(state) {
Expand Down Expand Up @@ -1121,6 +1123,9 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
case 'g':
if (state === 'startSlur' || state === 'sharp2' || state === 'flat2' || state === 'pitch') {
el.pitch = pitches[line.charAt(index)];
el.name = line.charAt(index);
if (el.accidental)
el.name = accMap[el.accidental] + el.name;
transpose.note(multilineVars, el);
state = 'octave';
// At this point we have a valid note. The rest is optional. Set the duration in case we don't get one below
Expand All @@ -1135,7 +1140,6 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
(multilineVars.currentVoice && multilineVars.currentVoice.clef === "perc")) {
var key = line.charAt(index);
if (el.accidental) {
var accMap = { 'dblflat': '__', 'flat': '_', 'natural': '=', 'sharp': '^', 'dblsharp': '^^'};
key = accMap[el.accidental] + key;
}
if (tune.formatting && tune.formatting.midi && tune.formatting.midi.drummap)
Expand All @@ -1145,12 +1149,12 @@ var getCoreNote = function(line, index, el, canHaveBrokenRhythm) {
else return null;
break;
case ',':
if (state === 'octave') {el.pitch -= 7; }
if (state === 'octave') {el.pitch -= 7; el.name += ','; }
else if (isComplete(state)) {el.endChar = index;return el;}
else return null;
break;
case '\'':
if (state === 'octave') {el.pitch += 7; }
if (state === 'octave') {el.pitch += 7; el.name += "'"; }
else if (isComplete(state)) {el.endChar = index;return el;}
else return null;
break;
Expand Down
41 changes: 17 additions & 24 deletions src/parse/tune-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,6 @@ var parseCommon = require('../parse/abc_common');
var TuneBuilder = function(tune) {
var self = this;

this.reset = function () {
tune.version = "1.1.0";
tune.media = "screen";
tune.metaText = {};
tune.formatting = {};
tune.lines = [];
tune.staffNum = 0;
tune.voiceNum = 0;
tune.lineNum = 0;
tune.runningFonts = {};
delete tune.visualTranspose;
};

this.setVisualTranspose = function(visualTranspose) {
if (visualTranspose)
tune.visualTranspose = visualTranspose;
Expand Down Expand Up @@ -472,7 +459,7 @@ var TuneBuilder = function(tune) {
return currSlur;
};

this.reset();
tune.reset();

this.getLastNote = function() {
if (tune.lines[tune.lineNum] && tune.lines[tune.lineNum].staff && tune.lines[tune.lineNum].staff[tune.staffNum] &&
Expand Down Expand Up @@ -665,8 +652,8 @@ var TuneBuilder = function(tune) {
tune.lines.push(hash);
};

this.addSubtitle = function(str) {
this.pushLine({subtitle: str});
this.addSubtitle = function(str, info) {
this.pushLine({subtitle: str, startChar: info.startChar, endChar: info.endChar});
};

this.addSpacing = function(num) {
Expand Down Expand Up @@ -881,23 +868,29 @@ var TuneBuilder = function(tune) {
tune.lineNum = i;
};

this.addMetaText = function(key, value) {
if (tune.metaText[key] === undefined)
this.addMetaText = function(key, value, info) {
if (tune.metaText[key] === undefined) {
tune.metaText[key] = value;
else
tune.metaTextInfo[key] = info;
} else {
tune.metaText[key] += "\n" + value;
tune.metaTextInfo[key].endChar = info.endChar;
}
};

this.addMetaTextArray = function(key, value) {
if (tune.metaText[key] === undefined)
this.addMetaTextArray = function(key, value, info) {
if (tune.metaText[key] === undefined) {
tune.metaText[key] = [value];
else
tune.metaTextInfo[key] = info;
} else {
tune.metaText[key].push(value);
tune.metaTextInfo[key].endChar = info.endChar;
}
};
this.addMetaTextObj = function(key, value) {
this.addMetaTextObj = function(key, value, info) {
tune.metaText[key] = value;
tune.metaTextInfo[key] = info;
};

};

module.exports = TuneBuilder;
34 changes: 33 additions & 1 deletion src/test/abc_parser_lint.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ var ParserLint = function() {
endSlur: { type: 'array', optional: true, output: "join", items: { type: 'number', minimum: 0 } },
endTie: { type: 'boolean', Enum: [ true ], optional: true },
midipitch: { type: 'number', optional: true },
name: { type: 'string' },
pitch: { type: 'number' },
verticalPos: { type: 'number' },
startBeam: { type: 'boolean', Enum: [ true ], prohibits: [ 'endBeam', 'beambr' ], optional: true },
Expand All @@ -247,6 +248,7 @@ var ParserLint = function() {
endSlur: { type: 'array', optional: true, output: "join", items: { type: 'number', minimum: 0 } },
endTie: { type: 'boolean', Enum: [ true ], optional: true },
midipitch: { type: 'number', optional: true },
name: { type: 'string' },
pitch: { type: 'number' },
verticalPos: { type: 'number' },
startSlur: slurProperties,
Expand Down Expand Up @@ -679,7 +681,37 @@ var ParserLint = function() {
unalignedWords: { type: 'array', optional: true, items: textFieldProperties },
url: { type: "string", optional: true }
}
}
},
metaTextInfo: {type:"object",
description: "There can only be one of these per tune",
properties: {
"abc-copyright": { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
"abc-creator": { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
"abc-version": { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
"abc-charset": { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
"abc-edited-by": { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
author: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
book: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
composer: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
discography: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
footer: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
group: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
header: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
history: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
instruction: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
notes: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
origin: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
partOrder: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
rhythm: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
source: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
tempo: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
textBlock: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
title: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
transcription: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
unalignedWords: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
url: { type: "object", optional: true, properties: { startChar: { type: "number"}, endChar: { type: "number"}, } },
}
},
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/write/abc_create_note_head.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var createNoteHead = function(abselem, c, pitchelem, options) {
var adjust = (pitchelem.printer_shift==="same")?1:0;
shiftheadx = (dir==="down")?-glyphs.getSymbolWidth(c)*scale+adjust:glyphs.getSymbolWidth(c)*scale-adjust;
}
var opts = {scalex:scale, scaley: scale, thickness: glyphs.symbolHeightInPitches(c)*scale };
var opts = {scalex:scale, scaley: scale, thickness: glyphs.symbolHeightInPitches(c)*scale, name: pitchelem.name };
notehead = new RelativeElement(c, shiftheadx, glyphs.getSymbolWidth(c)*scale, pitch, opts);
notehead.stemDir = dir;
if (flag) {
Expand Down
2 changes: 1 addition & 1 deletion src/write/abc_engraver_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ EngraverController.prototype.setupTune = function (abcTune, tuneNumber) {
};

EngraverController.prototype.constructTuneElements = function (abcTune) {
abcTune.topText = new TopText(abcTune.metaText, abcTune.formatting, abcTune.lines, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.getTextSize);
abcTune.topText = new TopText(abcTune.metaText, abcTune.metaTextInfo, abcTune.formatting, abcTune.lines, this.width, this.renderer.isPrint, this.renderer.padding.left, this.renderer.spacing, this.getTextSize);

// Generate the raw staff line data
var i;
Expand Down
5 changes: 3 additions & 2 deletions src/write/abc_glyphs.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5c6c1e2

Please sign in to comment.