UNPKG

8.88 kBJavaScriptView Raw
1var circleOfFifths = require('./dict/circleOfFifths.json');
2var accidental = require('./dict/accidentals.json');
3var pitches = require('./dict/pitches.json');
4var durations = require('./dict/durations.json');
5var clefs = require('./dict/clefs.json');
6
7var Parser = require('./lib/abc_parser');
8
9/**
10 * Returns the abc notation string from given input
11 * @param {object} input - The parsed input from input file
12 * @returns {string}
13 */
14function getAbcString(input) {
15 var outputData = "";
16 outputData += "X:"
17 + input.id
18 + "\n";
19 outputData += "T:"
20 + input.id
21 + "\n";
22 outputData += "M:"
23 + input.attributes.time.beats
24 + "/"
25 + input.attributes.time["beat-type"]
26 + "\n";
27 outputData += "L:"
28 + "1/"
29 + (input.attributes.divisions * input.attributes.time["beat-type"])
30 + "\n";
31 outputData += "K:"
32 + getAbcKey(input.attributes.key.fifths, input.attributes.key.mode)
33 + "\n";
34 outputData += "K:"
35 + getAbcClef(input.attributes.clef.sign)
36 + "\n";
37
38 for (var i = 0; i < input.measures.length; i++) {
39 if (i % 5 === 0 && i > 0) { // 5 measures per line
40 outputData += "\n";
41 outputData += "|";
42 }
43 var measure = input.measures[i];
44 if (measure.attributes.repeat.left) {
45 outputData += ":";
46 }
47
48 for (var j = 0; j < measure.notes.length; j++) {
49
50 outputData += getAbcNote(measure.notes[j-1], measure.notes[j]);
51 }
52
53 if (measure.attributes.repeat.right) {
54 outputData += ":";
55 }
56 outputData += "|";
57
58 }
59
60 return outputData;
61}
62
63/**
64 * Returns the abc notation string from given input
65 * @param {object} input - The parsed input from input file
66 * @returns {string}
67 */
68function getMusicJSON(input) {
69 var outputData = {};
70 var parser = new Parser();
71 parser.parse(input);
72 var tune = parser.getTune();
73 outputData = createJsonFromLines(tune);
74 outputData.id = getJsonId(input);
75
76 return JSON.stringify(outputData);
77}
78
79/**
80 * Returns the key for abc notation from given fifths
81 * @param {number} fifths - The position inside the circle of fifths
82 * @param {string|undefined} mode - The mode (major / minor)
83 * @returns {string}
84 */
85function getAbcKey(fifths, mode) {
86 if (typeof mode === 'undefined') mode = 'major';
87 return circleOfFifths[mode][fifths];
88}
89
90/**
91 * Returns the key for abc notation from given fifths
92 * @param {string} sign - The clef sign
93 * @returns {string}
94 */
95function getAbcClef(sign) {
96 return clefs[sign];
97}
98
99/**
100 * Returns a note in abc notation from given note object (JSON)
101 * @param {object} prevNote - The previous note
102 * @param {object} curNote - The note that should be transformed to abc
103 * @returns {string}
104 */
105function getAbcNote(prevNote, curNote) {
106 var _accidental = accidental.json[curNote.pitch.accidental];
107 var _pitch = pitches.json[curNote.pitch.octave][curNote.pitch.step];
108 var _duration = curNote.duration;
109 if (typeof prevNote !== 'undefined') {
110 if (prevNote.dot) {
111 _duration = _duration * 2;
112 }
113 }
114 var _dotted = '';
115 if (curNote.dot) {
116 _dotted = '>';
117 }
118
119 // check if rest
120 if (curNote.rest) {
121 // return rest as abc
122 return "z" + _duration + _dotted;
123 } else {
124 // return note as abc
125 return _accidental + _pitch + _duration + _dotted;
126 }
127}
128
129/**
130 * Get id from abc string
131 * @param data
132 * @returns {string}
133 */
134var getJsonId = function getJSONId(data) {
135 var lines = data.split('\n');
136 for (var i = 0; i < lines.length; i++) {
137 if (lines[i].indexOf('X:') > -1) {
138 return lines[i].substr(lines[i].indexOf(':') + 1, lines[i].length);
139 }
140 }
141 throw new Error('Could not determine "X:" field');
142};
143
144/**
145 * Creates json object from abc tunes object
146 * @param {object} tune - The parsed tune object
147 * @returns {object}
148 */
149var createJsonFromLines = function(tune) {
150 var ret = {
151 attributes: {
152 divisions: 1 /tune.getBeatLength(),
153 clef: {
154 line: 2
155 },
156 key: {},
157 time: {}
158 }
159 };
160 var measures = [];
161 var measureCounter = 0;
162 var barlineCounter = 0;
163
164 // parse lines
165 for (var l = 0; l < tune.lines.length; l++) {
166 for (var s = 0; s < tune.lines[l].staff.length; s++) {
167 var staff = tune.lines[l].staff[s];
168
169 // parse default clef, key, meter
170 if (l === 0 && s === 0) {
171 ret.attributes.clef.sign = getKeyByValue(clefs, staff.clef.type);
172 ret.attributes.clef.line = staff.clef.clefPos / 2;
173 ret.attributes.key.fifths = parseInt(getKeyByValue(circleOfFifths, staff.key.root));
174 ret.attributes.time.beats = staff.meter.value[0].num;
175 ret.attributes.time['beat-type'] = staff.meter.value[0].den;
176 }
177
178 for (var v = 0; v < staff.voices.length; v++) {
179 for (var t = 0; t < staff.voices[v].length; t++) {
180 var token = staff.voices[v][t];
181
182 // init measure if none exists
183 if (measures[measureCounter] === undefined) {
184 measures[measureCounter] = new Measure();
185 }
186
187 switch (token.el_type) {
188 case "note":
189 measures[measureCounter].addNote(token, tune, ret.attributes.divisions, ret.attributes.time['beat-type']);
190 break;
191 case "bar":
192 if (token.type === 'bar_right_repeat') {
193 measures[measureCounter].setRepeatRight();
194 }
195 measureCounter++;
196 if (measures[measureCounter] === undefined) {
197 measures[measureCounter] = new Measure();
198 }
199 if (token.type === 'bar_left_repeat') {
200 measures[measureCounter].setRepeatLeft();
201 }
202 break;
203 }
204 }
205 }
206 }
207 }
208
209 // put measures together
210 ret.measures = [];
211 for (var i = 0; i < measures.length; i++) {
212 var measure = measures[i].get();
213 if (measure.notes.length > 0) {
214 ret.measures.push(measure);
215 }
216 }
217 return ret;
218};
219
220/**
221 * Constructor for measure objects
222 */
223var Measure = function() {
224 var attributes = {
225 repeat: {
226 left: false,
227 right: false
228 }
229 };
230 var notes = [];
231
232 this.setRepeatLeft = function () {
233 attributes.repeat.left = true;
234 };
235 this.setRepeatRight = function () {
236 attributes.repeat.right = true;
237 };
238
239 this.addNote = function(note, tune, divisions, beatType) {
240 var _note = {pitch:{}};
241 var _octave = 5, _step, _alter = 0;
242 if (note.hasOwnProperty('pitches')) {
243 _octave--;
244 _step = note.pitches[0].pitch;
245 while (_step > 6) {
246 _octave++;
247 _step -= 7;
248 }
249 while (_step < 0) {
250 _octave--;
251 _step +=7
252 }
253 _note.pitch.step = pitches.abc[_step];
254 _note.rest = false;
255 if (note.pitches[0].hasOwnProperty('accidental')) {
256 _alter = accidental.abc[note.pitches[0].accidental];
257 _note.pitch.accidental = note.pitches[0].accidental;
258 }
259 } else {
260 _note.pitch.step = "C";
261 _note.pitch.octave = 5;
262 _note.rest = true;
263 _note.pitch.alter = 0;
264 }
265 _note.pitch.octave = _octave;
266 _note.pitch.alter = _alter;
267
268 for (var i = 0; i < durations.length; i++) {
269 if (typeof durations[i+1] !== 'undefined') {
270 if (durations[i].duration > note.duration && durations[i+1].duration <= note.duration) {
271 var diff = note.duration - durations[i+1].duration;
272 _note.duration = durations[i+1].duration * divisions * beatType;
273 _note.type = durations[i+1].type;
274 if (diff > 0) {
275 if ((diff / durations[i+1].duration) === 0.5) {
276 _note.dot = true;
277 } else {
278 throw new Error('Unknown duration: ' + note.duration);
279 }
280 }
281 break;
282 }
283 } else {
284 throw new Error('Unknown duration: ' + note.duration);
285 }
286 }
287
288 notes.push(_note);
289 };
290
291 this.get = function() {
292 return {
293 attributes: attributes,
294 notes: notes
295 };
296 };
297};
298
299/**
300 * Get object key by value
301 * @param {object} object - The object to search in
302 * @param {string} value - The value to search for
303 * @returns {string}
304 */
305var getKeyByValue = function(object, value) {
306 for (var key in object) {
307 if (!object.hasOwnProperty(key)) continue;
308 if (typeof object[key] === 'object') {
309 return getKeyByValue(object[key], value);
310 } else {
311 if (object[key] == value) return key;
312 }
313 }
314};
315
316/**
317 * Returns a string in abc notation from given data
318 * @param {object} data - The JSON string data that should be transformed to abc
319 * @returns {string}
320 */
321exports.convert2Abc = function(data) {
322 return getAbcString(JSON.parse(data));
323};
324
325/**
326 * Returns a string in json notation from given abc data
327 * @param {object} data - The abc string that should be transformed to json
328 * @returns {string}
329 */
330exports.convert2JSON = function(data) {
331 return getMusicJSON(data);
332};
333
334// Run jsdoc with: jsdoc index.js -d doc -R README.md