Skip to content

Commit

Permalink
Ripped font gfx out of FastTracker 2 :)
Browse files Browse the repository at this point in the history
  • Loading branch information
a1k0n committed Oct 31, 2015
1 parent dacb1a5 commit b7a2e58
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 21 deletions.
Binary file added ft2font.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 13 additions & 7 deletions index.html
Expand Up @@ -3,10 +3,22 @@
<script src="xm.js"></script>
</head>
<body>
<div><canvas id='vu' width=224 height=64></canvas></div>
<div><canvas id='gfxpattern' width=640 height=200></canvas></div>
<pre id='debug'>
</pre>
<pre id='pattern'>
</pre>
<pre>
code: <a href="http://github.com/a1k0n/jsxm/">github.com/a1k0n/jsxm</a>
tune: my dirty old kamel - <a href="http://zalza.bandcamp.com">Zalza</a>
todo:
- file picker, upload more xms to a host we can access cross-origin
- make display even more faithfully FT2-like
- sync display &amp; sound to 60Hz precision (right now it only updates at
about 10Hz)
- buffer the song more after that's done
- render instrument list
- oscilloscopes
- fix bugs!
- envelope loops - are they right or not now?
- multi-sample instruments
Expand All @@ -15,12 +27,6 @@
- E3x, E4x, E5x, E6x, E7x, E9x, EDx, EEx
- 7xy - tremolo
- Gxx, Hxy, Kxx, Lxx, Pxy, Txy
- oscilloscopes
</pre>
<canvas id='vu' width=224 height=64></canvas>
<pre id='debug'>
</pre>
<pre id='pattern'>
</pre>
</body>

128 changes: 114 additions & 14 deletions xm.js
@@ -1,8 +1,102 @@
var songname = '';
var fontimg = new Image();
var pat_canvas = document.createElement('canvas');
fontimg.onload = function() {
/* NO!!!!!
var canv = document.createElement('canvas');
canv.width = fontimg.width;
canv.height = fontimg.height;
var c = canv.getContext('2d');
c.drawImage(fontimg, 0, 0);
var image_data = c.getImageData(0, 0, fontimg.width, fontimg.height);
var idx = 0;
var data = image_data.data;
for(var i = 0; i < fontimg.height; i++) {
for(var j = 0; j < fontimg.width; j++) {
// image_data.data[((i*(img.width*4)) + (j*4) + 3)] = 127;
if (data[idx] == 0)
data[idx+3] = 0;
idx += 4;
}
}
c.putImageData(image_data, 0, 0);
fontimg = canv;
*/
};

fontimg.src = "ft2font.png";

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

audioContext = window.AudioContext || window.webkitAudioContext;

var _fontmap_notes = [8*5, 8*22, 8*28];
var _pattern_cellwidth = 16 + 4 + 8 + 4 + 8 + 16 + 4;
var _pattern_border = 20;
var pat_canvas_patnum;
function RenderPattern(canv, pattern) {
// a pattern consists of NxM cells which look like
// N-O II VV EFF
var cellwidth = _pattern_cellwidth;
canv.width = pattern[0].length * cellwidth + _pattern_border;
canv.height = pattern.length * 8;
var ctx = canv.getContext('2d');
ctx.fillcolor='#000';
ctx.fillRect(0, 0, canv.width, canv.height);
for (var j = 0; j < pattern.length; j++) {
var row = pattern[j];
var dy = j * 8;
// render row number
ctx.drawImage(fontimg, 8*(j>>4), 0, 8, 8, 2, dy, 8, 8);
ctx.drawImage(fontimg, 8*(j&15), 0, 8, 8, 10, dy, 8, 8);

for (var i = 0; i < row.length; i++) {
var dx = i*cellwidth + 2 + _pattern_border;
var data = row[i];

// render note
var note = data[0];
if (note < 0) {
ctx.drawImage(fontimg, 0, 8*5, 16, 8, dx, dy, 16, 8);
} else {
var octave = (note/12)|0;
var note_fontrow = _fontmap_notes[(octave/3)|0];
note = (note % (12*3))|0;
ctx.drawImage(fontimg, 16+16*note, note_fontrow, 16, 8, dx, dy, 16, 8);
}
dx += 20;

// render instrument
var inst = data[1];
if (inst != -1) { // no instrument = render nothing
ctx.drawImage(fontimg, 8*(inst>>4), 4*8, 4, 8, dx, dy, 4, 8);
ctx.drawImage(fontimg, 8*(inst&15), 4*8, 4, 8, dx+4, dy, 4, 8);
}
dx += 12;

// render volume
var vol = data[2];
if (vol < 0x10) {
ctx.drawImage(fontimg, 312, 0, 8, 8, dx, dy, 8, 8);
} else {
vol -= 0x10;
ctx.drawImage(fontimg, 8*(vol>>4), 4*8, 4, 8, dx, dy, 4, 8);
ctx.drawImage(fontimg, 8*(vol&15), 4*8, 4, 8, dx+4, dy, 4, 8);
}
dx += 8;

// render effect
var eff = data[3];
var effdata = data[4];
ctx.drawImage(fontimg, 8*eff + 16*8, 4*8, 8, 8, dx, dy, 8, 8);
dx += 8;
ctx.drawImage(fontimg, 8*(effdata>>4), 4*8, 4, 8, dx, dy, 4, 8);
ctx.drawImage(fontimg, 8*(effdata&15), 4*8, 4, 8, dx+4, dy, 4, 8);
}
}
}

function prettify_note(note) {
if (note < 0) return "---";
if (note == 96) return "^^^";
Expand Down Expand Up @@ -94,11 +188,9 @@ function next_row() {
var p = patterns[cur_pat];
var r = p[cur_row];
cur_row++;
pretty_row = [];
for (var i = 0; i < r.length; i++) {
var ch = channelinfo[i];
ch.update = false;
pretty_row.push(prettify_notedata(r[i]));
var triggernote = false;
// instrument trigger
if (r[i][1] != -1) {
Expand Down Expand Up @@ -191,10 +283,6 @@ function next_row() {
ch.period = PeriodForNote(ch, note);
}
}
patdisplay.push(pretty_row.join(" "));
if (patdisplay.length > 16) {
patdisplay.shift();
}
}

function Envelope(points, type, sustain, loopstart, loopend) {
Expand Down Expand Up @@ -508,9 +596,21 @@ function audio_cb(e) {
}

var debug = document.getElementById("debug");
debug.innerHTML = 'pat ' + cur_pat + ' row ' + (cur_row-1);
var pat = document.getElementById("pattern");
pat.innerHTML = patdisplay.join("\n");
debug.innerHTML = songname + '<br>pat ' + cur_pat + ' row ' + (cur_row-1);

var gfx = document.getElementById("gfxpattern");
if (cur_pat != pat_canvas_patnum) {
RenderPattern(pat_canvas, patterns[cur_pat]);
pat_canvas_patnum = cur_pat;
}
var ctx = gfx.getContext('2d');
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, gfx.width, gfx.height);
ctx.fillStyle = '#2a5684';
ctx.fillRect(0, gfx.height/2 - 4, gfx.width, 8);
ctx.globalCompositeOperation = 'lighten';
ctx.drawImage(pat_canvas, 0, gfx.height / 2 - 4 - 8*(cur_row - 1));
ctx.globalCompositeOperation = 'source-over';
});
}

Expand Down Expand Up @@ -799,7 +899,7 @@ function playXM(arrayBuf) {
var dv = new DataView(arrayBuf);
window.dv = dv;

var name = getstring(dv, 17, 20);
songname = getstring(dv, 17, 20);
var hlen = dv.getUint32(0x3c, true) + 0x3c;
var songlen = dv.getUint16(0x40, true);
song_looppos = dv.getUint16(0x42, true);
Expand Down Expand Up @@ -847,7 +947,6 @@ function playXM(arrayBuf) {
idx += 9;
for (var j = 0; patsize > 0 && j < patrows; j++) {
row = [];
pretty_row = [];
for (var k = 0; k < nchan; k++) {
var byte0 = dv.getUint8(idx); idx++;
var note = -1, inst = -1, vol = -1, efftype = 0, effparam = 0;
Expand Down Expand Up @@ -877,7 +976,6 @@ function playXM(arrayBuf) {
effparam = dv.getUint8(idx); idx++;
}
var notedata = [note, inst, vol, efftype, effparam];
pretty_row.push(prettify_notedata(notedata));
row.push(notedata);
}
pattern.push(row);
Expand Down Expand Up @@ -1008,8 +1106,10 @@ function playXM(arrayBuf) {
jsNode.connect(gainNode);

var debug = document.getElementById("debug");
console.log("loaded \"" + name + "\"");
debug.innerHTML = name;
console.log("loaded \"" + songname + "\"");
debug.innerHTML = songname;
var gfxpattern = document.getElementById("gfxpattern");
gfxpattern.width = _pattern_cellwidth * nchan + _pattern_border;

// start playing
gainNode.connect(audioctx.destination);
Expand Down

0 comments on commit b7a2e58

Please sign in to comment.