From f092f847f7ee789ccdea61c8ec0208405f24a2e8 Mon Sep 17 00:00:00 2001 From: Andy Sloane Date: Wed, 4 Nov 2015 06:22:49 -0800 Subject: [PATCH] instrument list rendering with FT2 font. --- index.html | 11 ++++---- xm.js | 79 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 74 insertions(+), 16 deletions(-) diff --git a/index.html b/index.html index 20a54db..5935e53 100644 --- a/index.html +++ b/index.html @@ -4,13 +4,14 @@ -
-
-
+
+
+
+
+
+
 
-
-
 code: github.com/a1k0n/jsxm
 todo:
diff --git a/xm.js b/xm.js
index 492a50f..7a0f867 100644
--- a/xm.js
+++ b/xm.js
@@ -8,10 +8,37 @@ fontimg.src = "ft2font.png";
 // canvas to render patterns onto
 var pat_canvas = document.createElement('canvas');
 
-// 
+// for pretty-printing notes
 var _note_names = ["C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"];
+
 var f_smp = 44100;  // updated by play callback, default value here
 
+// pixel widths of each character in the proportional font
+var _fontwidths = [
+  4, 7, 3, 6, 6, 6, 6, 5, 4, 5, 5, 5, 5, 5, 7, 7,
+  5, 5, 5, 6, 6, 6, 6, 6, 6, 7, 6, 7, 7, 7, 7, 7,
+  4, 2, 5, 7, 7, 7, 7, 3, 4, 4, 6, 6, 3, 6, 2, 7,
+  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 2, 3, 6, 6, 6, 7,
+  7, 7, 7, 7, 7, 7, 7, 7, 7, 2, 7, 7, 7, 8, 8, 7,
+  7, 7, 7, 7, 8, 7, 7, 8, 8, 8, 7, 4, 7, 4, 4, 5,
+  3, 6, 6, 6, 6, 6, 4, 6, 6, 2, 4, 6, 2, 8, 6, 6,
+  6, 6, 4, 6, 4, 6, 7, 8, 7, 6, 6, 4, 2, 4, 4, 4];
+
+// draw FT2 proportional font text to a drawing context
+// returns width rendered
+function DrawText(text, dx, dy, ctx) {
+  var dx0 = dx;
+  for (var i = 0; i < text.length; i++) {
+    var n = text.charCodeAt(i);
+    var sx = (n&63)*8;
+    var sy = (n>>6)*10 + 7*8;
+    var width = _fontwidths[n];
+    ctx.drawImage(fontimg, sx, sy, width, 10, dx, dy, width, 10);
+    dx += width + 1;
+  }
+  return dx - dx0;
+}
+
 var _fontmap_notes = [8*5, 8*22, 8*28];
 var _pattern_cellwidth = 16 + 4 + 8 + 4 + 8 + 16 + 4;
 var _scope_width = _pattern_cellwidth - 1;
@@ -176,10 +203,7 @@ function RedrawScreen() {
     for (var j = 0; j < xm.nchan; j++) {
       var x = _pattern_border + j * _pattern_cellwidth;
       // render channel number
-      if (j >= 10) {
-        ctx.drawImage(fontimg, 8*((j/10)|0), 4*8, 4, 8, x, 1, 4, 8);
-      }
-      ctx.drawImage(fontimg, 8*(j%10), 4*8, 4, 8, x+4, 1, 4, 8);
+      DrawText(j, x, 1, ctx);
 
       // volume in dB as a green bar
       var vu_y = -Math.log(VU[j])*10;
@@ -969,13 +993,46 @@ function loadXM(arrayBuf) {
   debug.innerHTML = xm.songname;
 
   var instrlist = document.getElementById("instruments");
-  var namelist = [];
-  for (var j = 0; j < xm.instruments.length; j++) {
-    var n = (j+1).toString(16);
-    if (j < 15) n = ' ' + n;
-    namelist.push(n + " " + xm.instruments[j].name);
+  var instrcols = ((xm.instruments.length + 7) / 8) | 0;
+  for (var i = 0; i < instrcols; i++) {
+    var canvas = document.createElement('canvas');
+    var ctx = canvas.getContext('2d');
+    var instrcolumnwidth = 8*22;
+    canvas.width = instrcolumnwidth;
+    canvas.height = 8 * 10;
+    ctx.fillStyle = '#000';
+    ctx.fillRect(0, 0, canvas.width, canvas.height);
+    var hasname = 0, hasdata = 0;
+    for (var j = 8*i; j < Math.min(8*i+8, xm.instruments.length); j++) {
+      var y = 10*(j - 8*i);
+      var n = (j+1).toString(16);
+      if (j < 15) n = '0' + n;
+      var data = xm.instruments[j].samples;
+      if (data) {
+        var len = data[0].len;
+        data = data[0].sampledata;
+        var scale = Math.ceil(len / instrcolumnwidth);
+        ctx.strokeStyle = '#55acff';
+        ctx.beginPath();
+        for (var k = 0; k < Math.min(len / scale, instrcolumnwidth - 20); k++) {
+          ctx.lineTo(k + 20, y + data[k*scale] * 4 + 4);
+        }
+        ctx.stroke();
+        hasdata++;
+      }
+      var name = xm.instruments[j].name;
+      ctx.globalCompositeOperation = 'lighten';
+      DrawText(n, 1, y, ctx);
+      if (name != '') {
+        DrawText(xm.instruments[j].name, 20, y, ctx);
+        hasname++;
+      }
+      ctx.globalCompositeOperation = 'source-over';
+    }
+    if (hasname || hasdata) {
+      instrlist.appendChild(canvas);
+    }
   }
-  instrlist.innerHTML = namelist.join("\n");
 
   return xm;
 }