From 45332b8333cf1f5ac6d41cfbde7bf1d9ac9192e0 Mon Sep 17 00:00:00 2001 From: Sema Date: Sat, 16 Sep 2023 22:00:46 -0400 Subject: [PATCH] UMP text functions --- javascript/JZZ.js | 112 ++++++++++++++++++++++++++++++++++++++++------ test/common.js | 88 ++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+), 13 deletions(-) diff --git a/javascript/JZZ.js b/javascript/JZZ.js index 6701baf..64a1120 100644 --- a/javascript/JZZ.js +++ b/javascript/JZZ.js @@ -1692,6 +1692,7 @@ function _bad(x) { throw TypeError('Invalid value: ' + x); } function _oor(x) { throw RangeError('Out of range: ' + x); } function _ch(c) { _validateChannel(c); return parseInt(c); } + function _4b(n) { if (n != parseInt(n) || n < 0 || n > 0xf) throw RangeError('Expected a 4-bit value: ' + n); return parseInt(n); } function _7b(n, m) { if (n != parseInt(n) || n < 0 || n > 0x7f) _throw(typeof m == 'undefined' ? n : m); return parseInt(n); } function _8b(n) { if (n != parseInt(n) || n < 0 || n > 0xff) _throw(n); return parseInt(n); } function _14b(n) { if (n != parseInt(n) || n < 0 || n > 0x3fff) _throw(n); return parseInt(n); } @@ -2964,7 +2965,7 @@ var m = this[0] >> 4; if (m == 1 || m == 2 || m == 3 || m == 4 || m == 5 || m == 13) return this[0] & 15; }; - + var _zeros = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; var _helperNN = { noop: function() { return [0, 0, 0, 0]; }, umpClock: function(n) { n = _16b(n); return [0, 0x10, n >> 8, n & 0xff]; }, @@ -2975,7 +2976,7 @@ umpEndClip: function() { return [0xf0, 0x21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; } }; var _helperGN = { - umpTempo: function(g, n) { return [0xd0 + g, 0x10, 0, 0, n >> 24, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff, 0, 0, 0, 0, 0, 0, 0, 0]; }, + umpTempo: function(g, n) { return [0xd0 + _4b(g), 0x10, 0, 0, n >> 24, (n >> 16) & 0xff, (n >> 8) & 0xff, n & 0xff, 0, 0, 0, 0, 0, 0, 0, 0]; }, umpBPM: function(g, n) { return _helperGN.umpTempo(g, Math.round(6000000000 / n)); }, umpTimeSignature: function(g, a, b) { var nn, cc, dd; @@ -2992,7 +2993,7 @@ dd = 0; for (cc >>= 1; cc; cc >>= 1) dd++; cc = Math.round(nn * 32 / (1 << dd)); - if (cc < 0x100) return [0xd0 + g, 0x10, 0, 1, nn, dd, cc, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + if (cc < 0x100) return [0xd0 + _4b(g), 0x10, 0, 1, nn, dd, cc, 0, 0, 0, 0, 0, 0, 0, 0, 0]; } throw RangeError('Wrong time signature ' + a + (typeof b == 'undefined' ? '' : '/' + b)); } @@ -3002,14 +3003,63 @@ if (typeof v == 'undefined') v = 0xffff; t = t || 0; a = a || 0; v = _16b(v); a = _16b(a); - return [0x40 + g, 0x90 + _ch(c), _7bn(n), _8b(t), v >> 8, v & 255, a >> 8, a & 255]; + return [0x40 + _4b(g), 0x90 + _ch(c), _7bn(n), _8b(t), v >> 8, v & 255, a >> 8, a & 255]; }, umpNoteOff: function(g, c, n, v, t, a) { v = v || 0; t = t || 0; a = a || 0; v = _16b(v); a = _16b(a); - return [0x40 + g, 0x80 + _ch(c), _7bn(n), _8b(t), v >> 8, v & 255, a >> 8, a & 255]; + return [0x40 + _4b(g), 0x80 + _ch(c), _7bn(n), _8b(t), v >> 8, v & 255, a >> 8, a & 255]; } }; + var _helperGCG = { + umpCustomText: function(g, c, d, b, s, t) { + var i; + var a = []; + t = JZZ.lib.toUTF8('' + t); + for (i = 0; i < t.length; i++) a.push(t.charCodeAt(i)); + a = _slice(a, 12); + for (i = 0; i < a.length; i++) a[i] = [0xd0 + _4b(g), _umpseqstat(a.length, i) * 64 + (d ? 16 : 0) + _ch(c), b, s].concat(a[i], _zeros).slice(0, 16); + return a; + }, + umpCMetadata: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 0, t); }, + umpCProjectName: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 1, t); }, + umpCCompositionName: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 2, t); }, + umpCClipName: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 3, t); }, + umpCCopyright: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 4, t); }, + umpCComposerName: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 5, t); }, + umpCLyricistName: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 6, t); }, + umpCArrangerName: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 7, t); }, + umpCPublisherName: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 8, t); }, + umpCPerformerName: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 9, t); }, + umpCAccPerformerName: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 10, t); }, + umpCRecordingDate: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 11, t); }, + umpCRecordingLocation: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 1, 12, t); }, + umpCText: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 2, 0, t); }, + umpCLyrics: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 2, 1, t); }, + umpCLyricsLanguage: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 2, 2, t); }, + umpCRuby: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 2, 3, t); }, + umpCRubyLanguage: function(g, c, t) { return _helperGCG.umpCustomText(g, c, 0, 2, 4, t); } + }; + var _helperGNG = { + umpMetadata: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 0, t); }, + umpProjectName: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 1, t); }, + umpCompositionName: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 2, t); }, + umpClipName: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 3, t); }, + umpCopyright: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 4, t); }, + umpComposerName: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 5, t); }, + umpLyricistName: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 6, t); }, + umpArrangerName: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 7, t); }, + umpPublisherName: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 8, t); }, + umpPerformerName: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 9, t); }, + umpAccPerformerName: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 10, t); }, + umpRecordingDate: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 11, t); }, + umpRecordingLocation: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 1, 12, t); }, + umpText: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 2, 0, t); }, + umpLyrics: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 2, 1, t); }, + umpLyricsLanguage: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 2, 2, t); }, + umpRuby: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 2, 3, t); }, + umpRubyLanguage: function(g, t) { return _helperGCG.umpCustomText(g, 0, 1, 2, 4, t); } + }; var _helpersUmp = {}; function _copyHelperNN(name, func) { @@ -3030,6 +3080,40 @@ return this.send(func.apply(this, args)); }; } + function _copyHelperGCG(name, func) { + UMP[name] = function() { + var args = Array.prototype.slice.call(arguments); + if (typeof this._gr != 'undefined') args = [this._gr].concat(args); + if (typeof this._ch != 'undefined') args = [args[0]].concat([this._ch]).concat(args.slice(1)); + var a = func.apply(this, args); + for (var i = 0; i < a.length; i++) a[i] = new UMP(a[i]); + return a; + }; + _helpersUmp[name] = function() { + var args = Array.prototype.slice.call(arguments); + if (typeof this._gr != 'undefined') args = [this._gr].concat(args); + if (typeof this._ch != 'undefined') args = [args[0]].concat([this._ch]).concat(args.slice(1)); + var a = func.apply(this, args); + for (var i = 0; i < a.length; i++) this.send(a[i]); + return this; + }; + } + function _copyHelperGNG(name, func) { + UMP[name] = function() { + var args = Array.prototype.slice.call(arguments); + if (typeof this._gr != 'undefined') args = [this._gr].concat(args); + var a = func.apply(this, args); + for (var i = 0; i < a.length; i++) a[i] = new UMP(a[i]); + return a; + }; + _helpersUmp[name] = function() { + var args = Array.prototype.slice.call(arguments); + if (typeof this._gr != 'undefined') args = [this._gr].concat(args); + var a = func.apply(this, args); + for (var i = 0; i < a.length; i++) this.send(a[i]); + return this; + }; + } function _copyHelperGN(name, func) { UMP[name] = function() { var args = Array.prototype.slice.call(arguments); @@ -3046,11 +3130,11 @@ function _slice(m, n) { var a = []; for (var x = m; x.length; x = x.slice(n)) a.push(x.slice(0, n)); - return a; + return a.length ? a : [[]]; } function _sliceSX(gr, m) { var a = _slice(m.slice(1, m.length - 1), 6); - for (var i = 0; i < a.length; i++) a[i] = new UMP([0x30 + gr, _umpseqstat(a.length, i) * 16 + a[i].length].concat(a[i], [0, 0, 0, 0, 0, 0]).slice(0, 8)); + for (var i = 0; i < a.length; i++) a[i] = new UMP([0x30 + gr, _umpseqstat(a.length, i) * 16 + a[i].length].concat(a[i], _zeros).slice(0, 8)); return a; } function _copyHelperSX(name, func) { @@ -3096,6 +3180,8 @@ } _for(_helperNN, function(n) { _copyHelperNN(n, _helperNN[n]); }); _for(_helperGC, function(n) { _copyHelperGC(n, _helperGC[n]); }); + _for(_helperGCG, function(n) { _copyHelperGCG(n, _helperGCG[n]); }); + _for(_helperGNG, function(n) { _copyHelperGNG(n, _helperGNG[n]); }); _for(_helperMPE, function(n) { _copyHelperM1(n, _helperMPE[n]); }); _for(_helperCH, function(n) { _copyHelperM1(n, _helperCH[n]); }); _for(_helperNC, function(n) { _copyHelperM1N(n, _helperNC[n]); }); @@ -3134,14 +3220,14 @@ var c = (this[0] || 0) >> 4; var d = (this[1] || 0) >> 4; if (c == 4) return d == 9; - else if (c == 2) return d == 9 && !!this[3]; + else if (c == 2) return d == 9 && !!this[3]; return false; }; UMP.prototype.isNoteOff = function() { var c = (this[0] || 0) >> 4; var d = (this[1] || 0) >> 4; if (c == 4) return d == 8; - else if (c == 2) return d == 8 || (d == 9 && !this[3]); + else if (c == 2) return d == 8 || (d == 9 && !this[3]); return false; }; UMP.prototype.toString = MIDI.prototype.toString; @@ -3200,7 +3286,7 @@ else if (n == 1) { n = this[3]; s = { - 0: 'Metadata Text', + 0: 'Metadata', 1: 'Project Name', 2: 'Composition Name', 3: 'Clip Name', @@ -3213,17 +3299,17 @@ 10: 'Accompanying Performer Name', 11: 'Recording Date', 12: 'Recording Location' - }[n]; + }[n] || 'Unknown Text'; } else if (n == 2) { n = this[3]; s = { - 0: 'Performance Text', + 0: 'Text', 1: 'Lyrics', 2: 'Lyrics Language', 3: 'Ruby', 4: 'Ruby Language', - }[n]; + }[n] || 'Unknown Text'; } } if (t == 15) { diff --git a/test/common.js b/test/common.js index 2d64d7b..7b8b3c3 100644 --- a/test/common.js +++ b/test/common.js @@ -914,6 +914,89 @@ describe('UMP messages', function() { var s = 'd0100005 00000000 00000000 00000000 -- Key Signature'; assert.equal(JZZ.UMP([0xd0, 0x10, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).toString(), s); }); + it('umpCustomText', function() { + var m = JZZ.UMP.umpCustomText(5, 5, 1, 1, 15, 'This is a long text that spans over 4 messages!'); + console.log(m); + assert.equal(m[0].toString(), 'd555010f 54686973 20697320 61206c6f -- Unknown Text'); + assert.equal(m[1].toString(), 'd595010f 6e672074 65787420 74686174 -- Unknown Text'); + assert.equal(m[2].toString(), 'd595010f 20737061 6e73206f 76657220 -- Unknown Text'); + assert.equal(m[3].toString(), 'd5d5010f 34206d65 73736167 65732100 -- Unknown Text'); + assert.equal(JZZ.UMP.umpCustomText(5, 5, 0, 2, 15, '')[0].toString(), 'd505020f 00000000 00000000 00000000 -- Unknown Text'); + }); + it('umpMetadata', function() { + var s = 'd2100100 00000000 00000000 00000000 -- Metadata'; + assert.equal(JZZ.UMP.umpMetadata(2, '')[0].toString(), s); + assert.equal(JZZ.UMP.gr(2).umpMetadata('')[0].toString(), s); + s = 'd2050100 54455854 00000000 00000000 -- Metadata'; + assert.equal(JZZ.UMP.umpCMetadata(2, 5, 'TEXT')[0].toString(), s); + assert.equal(JZZ.UMP.gr(2).umpCMetadata(5, 'TEXT')[0].toString(), s); + assert.equal(JZZ.UMP.gr(2).ch(5).umpCMetadata('TEXT')[0].toString(), s); + assert.equal(JZZ.UMP.ch(5).umpCMetadata(2, 'TEXT')[0].toString(), s); + }); + it('umpProjectName', function() { + assert.equal(JZZ.UMP.umpProjectName(0, 'JZZ')[0].toString(), 'd0100101 4a5a5a00 00000000 00000000 -- Project Name'); + assert.equal(JZZ.UMP.umpCProjectName(0, 0, 'JZZ')[0].toString(), 'd0000101 4a5a5a00 00000000 00000000 -- Project Name'); + }); + it('umpCompositionName', function() { + assert.equal(JZZ.UMP.umpCompositionName(0, '...')[0].toString(), 'd0100102 2e2e2e00 00000000 00000000 -- Composition Name'); + assert.equal(JZZ.UMP.umpCCompositionName(0, 0, '...')[0].toString(), 'd0000102 2e2e2e00 00000000 00000000 -- Composition Name'); + }); + it('umpClipName', function() { + assert.equal(JZZ.UMP.umpClipName(0, '...')[0].toString(), 'd0100103 2e2e2e00 00000000 00000000 -- Clip Name'); + assert.equal(JZZ.UMP.umpCClipName(0, 0, '...')[0].toString(), 'd0000103 2e2e2e00 00000000 00000000 -- Clip Name'); + }); + it('umpCopyright', function() { + assert.equal(JZZ.UMP.umpCopyright(0, '...')[0].toString(), 'd0100104 2e2e2e00 00000000 00000000 -- Copyright'); + assert.equal(JZZ.UMP.umpCCopyright(0, 0, '...')[0].toString(), 'd0000104 2e2e2e00 00000000 00000000 -- Copyright'); + }); + it('umpComposerName', function() { + assert.equal(JZZ.UMP.umpComposerName(0, '...')[0].toString(), 'd0100105 2e2e2e00 00000000 00000000 -- Composer Name'); + assert.equal(JZZ.UMP.umpCComposerName(0, 0, '...')[0].toString(), 'd0000105 2e2e2e00 00000000 00000000 -- Composer Name'); + }); + it('umpLyricistName', function() { + assert.equal(JZZ.UMP.umpLyricistName(0, '...')[0].toString(), 'd0100106 2e2e2e00 00000000 00000000 -- Lyricist Name'); + assert.equal(JZZ.UMP.umpCLyricistName(0, 0, '...')[0].toString(), 'd0000106 2e2e2e00 00000000 00000000 -- Lyricist Name'); + }); + it('umpArrangerName', function() { + assert.equal(JZZ.UMP.umpArrangerName(0, '...')[0].toString(), 'd0100107 2e2e2e00 00000000 00000000 -- Arranger Name'); + assert.equal(JZZ.UMP.umpCArrangerName(0, 0, '...')[0].toString(), 'd0000107 2e2e2e00 00000000 00000000 -- Arranger Name'); + }); + it('umpPublisherName', function() { + assert.equal(JZZ.UMP.umpPublisherName(0, '...')[0].toString(), 'd0100108 2e2e2e00 00000000 00000000 -- Publisher Name'); + assert.equal(JZZ.UMP.umpCPublisherName(0, 0, '...')[0].toString(), 'd0000108 2e2e2e00 00000000 00000000 -- Publisher Name'); + }); + it('umpPerformerName', function() { + assert.equal(JZZ.UMP.umpPerformerName(0, '...')[0].toString(), 'd0100109 2e2e2e00 00000000 00000000 -- Primary Performer Name'); + assert.equal(JZZ.UMP.umpCPerformerName(0, 0, '...')[0].toString(), 'd0000109 2e2e2e00 00000000 00000000 -- Primary Performer Name'); + }); + it('umpAccPerformerName', function() { + assert.equal(JZZ.UMP.umpAccPerformerName(0, '...')[0].toString(), 'd010010a 2e2e2e00 00000000 00000000 -- Accompanying Performer Name'); + assert.equal(JZZ.UMP.umpCAccPerformerName(0, 0, '...')[0].toString(), 'd000010a 2e2e2e00 00000000 00000000 -- Accompanying Performer Name'); + }); + it('umpRecordingDate', function() { + assert.equal(JZZ.UMP.umpRecordingDate(0, '...')[0].toString(), 'd010010b 2e2e2e00 00000000 00000000 -- Recording Date'); + assert.equal(JZZ.UMP.umpCRecordingDate(0, 0, '...')[0].toString(), 'd000010b 2e2e2e00 00000000 00000000 -- Recording Date'); + }); + it('umpRecordingLocation', function() { + assert.equal(JZZ.UMP.umpRecordingLocation(0, '...')[0].toString(), 'd010010c 2e2e2e00 00000000 00000000 -- Recording Location'); + assert.equal(JZZ.UMP.umpCRecordingLocation(0, 0, '...')[0].toString(), 'd000010c 2e2e2e00 00000000 00000000 -- Recording Location'); + }); + it('umpText', function() { + assert.equal(JZZ.UMP.umpText(0, '音樂')[0].toString(), 'd0100200 e99fb3e6 a8820000 00000000 -- Text'); + assert.equal(JZZ.UMP.umpCText(0, 0, '音樂')[0].toString(), 'd0000200 e99fb3e6 a8820000 00000000 -- Text'); + }); + it('umpLyrics', function() { + assert.equal(JZZ.UMP.umpLyrics(0, '...')[0].toString(), 'd0100201 2e2e2e00 00000000 00000000 -- Lyrics'); + assert.equal(JZZ.UMP.umpCLyrics(0, 0, '...')[0].toString(), 'd0000201 2e2e2e00 00000000 00000000 -- Lyrics'); + assert.equal(JZZ.UMP.umpLyricsLanguage(0, '...')[0].toString(), 'd0100202 2e2e2e00 00000000 00000000 -- Lyrics Language'); + assert.equal(JZZ.UMP.umpCLyricsLanguage(0, 0, '...')[0].toString(), 'd0000202 2e2e2e00 00000000 00000000 -- Lyrics Language'); + }); + it('umpRuby', function() { + assert.equal(JZZ.UMP.umpRuby(0, '...')[0].toString(), 'd0100203 2e2e2e00 00000000 00000000 -- Ruby'); + assert.equal(JZZ.UMP.umpCRuby(0, 0, '...')[0].toString(), 'd0000203 2e2e2e00 00000000 00000000 -- Ruby'); + assert.equal(JZZ.UMP.umpRubyLanguage(0, '...')[0].toString(), 'd0100204 2e2e2e00 00000000 00000000 -- Ruby Language'); + assert.equal(JZZ.UMP.umpCRubyLanguage(0, 0, '...')[0].toString(), 'd0000204 2e2e2e00 00000000 00000000 -- Ruby Language'); + }); it('umpNoteOn', function() { var s = '41923d00 ffff0000 -- Note On'; var msg = JZZ.UMP.umpNoteOn(1, 2, 'C#5'); @@ -1365,6 +1448,11 @@ describe('JZZ.Widget2', function() { port.connect(function(msg) { sample.compare(msg); }); port.umpBPM(1, 120).gr(2).umpBPM(120); }); + it('umpText', function() { + var port = JZZ.Widget2(); + port.umpText(1, '').gr(2).umpText(''); + port.umpCText(1, 2, '').gr(2).umpText(2, '').ch(3).umpText(''); + }); }); describe('JZZ.Context', function() {