Ripped font gfx out of FastTracker 2 :)
This commit is contained in:
parent
dacb1a5852
commit
b7a2e58109
3 changed files with 127 additions and 21 deletions
BIN
ft2font.png
Normal file
BIN
ft2font.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
20
index.html
20
index.html
|
|
@ -3,10 +3,22 @@
|
||||||
<script src="xm.js"></script>
|
<script src="xm.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<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>
|
<pre>
|
||||||
code: <a href="http://github.com/a1k0n/jsxm/">github.com/a1k0n/jsxm</a>
|
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:
|
todo:
|
||||||
|
- file picker, upload more xms to a host we can access cross-origin
|
||||||
|
- make display even more faithfully FT2-like
|
||||||
|
- sync display & 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!
|
- fix bugs!
|
||||||
- envelope loops - are they right or not now?
|
- envelope loops - are they right or not now?
|
||||||
- multi-sample instruments
|
- multi-sample instruments
|
||||||
|
|
@ -15,12 +27,6 @@ todo:
|
||||||
- E3x, E4x, E5x, E6x, E7x, E9x, EDx, EEx
|
- E3x, E4x, E5x, E6x, E7x, E9x, EDx, EEx
|
||||||
- 7xy - tremolo
|
- 7xy - tremolo
|
||||||
- Gxx, Hxy, Kxx, Lxx, Pxy, Txy
|
- Gxx, Hxy, Kxx, Lxx, Pxy, Txy
|
||||||
- oscilloscopes
|
|
||||||
</pre>
|
|
||||||
<canvas id='vu' width=224 height=64></canvas>
|
|
||||||
<pre id='debug'>
|
|
||||||
</pre>
|
|
||||||
<pre id='pattern'>
|
|
||||||
</pre>
|
</pre>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
128
xm.js
128
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 _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
|
var f_smp = 44100; // updated by play callback, default value here
|
||||||
|
|
||||||
audioContext = window.AudioContext || window.webkitAudioContext;
|
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) {
|
function prettify_note(note) {
|
||||||
if (note < 0) return "---";
|
if (note < 0) return "---";
|
||||||
if (note == 96) return "^^^";
|
if (note == 96) return "^^^";
|
||||||
|
|
@ -94,11 +188,9 @@ function next_row() {
|
||||||
var p = patterns[cur_pat];
|
var p = patterns[cur_pat];
|
||||||
var r = p[cur_row];
|
var r = p[cur_row];
|
||||||
cur_row++;
|
cur_row++;
|
||||||
pretty_row = [];
|
|
||||||
for (var i = 0; i < r.length; i++) {
|
for (var i = 0; i < r.length; i++) {
|
||||||
var ch = channelinfo[i];
|
var ch = channelinfo[i];
|
||||||
ch.update = false;
|
ch.update = false;
|
||||||
pretty_row.push(prettify_notedata(r[i]));
|
|
||||||
var triggernote = false;
|
var triggernote = false;
|
||||||
// instrument trigger
|
// instrument trigger
|
||||||
if (r[i][1] != -1) {
|
if (r[i][1] != -1) {
|
||||||
|
|
@ -191,10 +283,6 @@ function next_row() {
|
||||||
ch.period = PeriodForNote(ch, note);
|
ch.period = PeriodForNote(ch, note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
patdisplay.push(pretty_row.join(" "));
|
|
||||||
if (patdisplay.length > 16) {
|
|
||||||
patdisplay.shift();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Envelope(points, type, sustain, loopstart, loopend) {
|
function Envelope(points, type, sustain, loopstart, loopend) {
|
||||||
|
|
@ -508,9 +596,21 @@ function audio_cb(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var debug = document.getElementById("debug");
|
var debug = document.getElementById("debug");
|
||||||
debug.innerHTML = 'pat ' + cur_pat + ' row ' + (cur_row-1);
|
debug.innerHTML = songname + '<br>pat ' + cur_pat + ' row ' + (cur_row-1);
|
||||||
var pat = document.getElementById("pattern");
|
|
||||||
pat.innerHTML = patdisplay.join("\n");
|
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';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -799,7 +899,7 @@ function playXM(arrayBuf) {
|
||||||
var dv = new DataView(arrayBuf);
|
var dv = new DataView(arrayBuf);
|
||||||
window.dv = dv;
|
window.dv = dv;
|
||||||
|
|
||||||
var name = getstring(dv, 17, 20);
|
songname = getstring(dv, 17, 20);
|
||||||
var hlen = dv.getUint32(0x3c, true) + 0x3c;
|
var hlen = dv.getUint32(0x3c, true) + 0x3c;
|
||||||
var songlen = dv.getUint16(0x40, true);
|
var songlen = dv.getUint16(0x40, true);
|
||||||
song_looppos = dv.getUint16(0x42, true);
|
song_looppos = dv.getUint16(0x42, true);
|
||||||
|
|
@ -847,7 +947,6 @@ function playXM(arrayBuf) {
|
||||||
idx += 9;
|
idx += 9;
|
||||||
for (var j = 0; patsize > 0 && j < patrows; j++) {
|
for (var j = 0; patsize > 0 && j < patrows; j++) {
|
||||||
row = [];
|
row = [];
|
||||||
pretty_row = [];
|
|
||||||
for (var k = 0; k < nchan; k++) {
|
for (var k = 0; k < nchan; k++) {
|
||||||
var byte0 = dv.getUint8(idx); idx++;
|
var byte0 = dv.getUint8(idx); idx++;
|
||||||
var note = -1, inst = -1, vol = -1, efftype = 0, effparam = 0;
|
var note = -1, inst = -1, vol = -1, efftype = 0, effparam = 0;
|
||||||
|
|
@ -877,7 +976,6 @@ function playXM(arrayBuf) {
|
||||||
effparam = dv.getUint8(idx); idx++;
|
effparam = dv.getUint8(idx); idx++;
|
||||||
}
|
}
|
||||||
var notedata = [note, inst, vol, efftype, effparam];
|
var notedata = [note, inst, vol, efftype, effparam];
|
||||||
pretty_row.push(prettify_notedata(notedata));
|
|
||||||
row.push(notedata);
|
row.push(notedata);
|
||||||
}
|
}
|
||||||
pattern.push(row);
|
pattern.push(row);
|
||||||
|
|
@ -1008,8 +1106,10 @@ function playXM(arrayBuf) {
|
||||||
jsNode.connect(gainNode);
|
jsNode.connect(gainNode);
|
||||||
|
|
||||||
var debug = document.getElementById("debug");
|
var debug = document.getElementById("debug");
|
||||||
console.log("loaded \"" + name + "\"");
|
console.log("loaded \"" + songname + "\"");
|
||||||
debug.innerHTML = name;
|
debug.innerHTML = songname;
|
||||||
|
var gfxpattern = document.getElementById("gfxpattern");
|
||||||
|
gfxpattern.width = _pattern_cellwidth * nchan + _pattern_border;
|
||||||
|
|
||||||
// start playing
|
// start playing
|
||||||
gainNode.connect(audioctx.destination);
|
gainNode.connect(audioctx.destination);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue