furnace-module-interface
Version:
An interface for loading, processing and saving furnace modules in an object-oriented manner. Fully typed via TypeScript.
286 lines (285 loc) • 18.9 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getEffectName = exports.getVersionString = exports.loadFurnaceModule = exports.inflateBuffer = exports.deflateBuffer = exports.FurnaceWavetableModule = exports.FurnaceSongModule = exports.FurnaceInstrumentModule = void 0;
const instrumentModule_1 = require("./instrumentModule");
Object.defineProperty(exports, "FurnaceInstrumentModule", { enumerable: true, get: function () { return instrumentModule_1.FurnaceInstrumentModule; } });
const songModule_1 = require("./songModule");
Object.defineProperty(exports, "FurnaceSongModule", { enumerable: true, get: function () { return songModule_1.FurnaceSongModule; } });
const wavetableModule_1 = require("./wavetableModule");
Object.defineProperty(exports, "FurnaceWavetableModule", { enumerable: true, get: function () { return wavetableModule_1.FurnaceWavetableModule; } });
const zlib = __importStar(require("zlib"));
const bufferHelper_1 = require("./bufferHelper");
__exportStar(require("./channels"), exports);
__exportStar(require("./chips"), exports);
__exportStar(require("./info"), exports);
__exportStar(require("./instrument"), exports);
__exportStar(require("./macros"), exports);
__exportStar(require("./meta"), exports);
__exportStar(require("./pattern"), exports);
__exportStar(require("./sample"), exports);
__exportStar(require("./song"), exports);
__exportStar(require("./wavetable"), exports);
const chips_1 = require("./chips");
function deflateBuffer(data) {
return new Promise((res, rej) => {
zlib.deflate(data, (error, result) => {
if (error) {
rej(error);
}
res(result);
});
});
}
exports.deflateBuffer = deflateBuffer;
function inflateBuffer(data) {
return new Promise((res, rej) => {
zlib.inflate(data, (error, result) => {
if (error) {
rej(error);
}
res(result);
});
});
}
exports.inflateBuffer = inflateBuffer;
function loadFurnaceModule(data, compressed) {
return __awaiter(this, void 0, void 0, function* () {
let realBuffer = data;
if (compressed !== false) {
let newBuffer;
try {
newBuffer = yield inflateBuffer(data);
}
catch (error) {
if (compressed === true) {
throw error;
}
}
if (newBuffer) {
realBuffer = newBuffer;
}
}
const helper = new bufferHelper_1.BufferReadHelper(realBuffer);
return loadModuleFromHelper(helper);
});
}
exports.loadFurnaceModule = loadFurnaceModule;
function loadModuleFromHelper(helper) {
const magic = helper.getASCII(16, 0);
helper.setPosition(helper.getPosition(-16));
switch (magic) {
case songModule_1.FurnaceSongModule.magic:
return songModule_1.FurnaceSongModule.fromHelper(helper);
case instrumentModule_1.FurnaceInstrumentModule.magic:
return instrumentModule_1.FurnaceInstrumentModule.fromHelper(helper);
case wavetableModule_1.FurnaceWavetableModule.magic:
return wavetableModule_1.FurnaceWavetableModule.fromHelper(helper);
}
throw new Error("Unable to determine module type!");
}
function getVersionString(version) {
const name = versioNames[version];
if (name) {
return name;
}
if (Math.round(version) === version && version >= 57 && version <= 99) {
return "dev" + version;
}
return undefined;
}
exports.getVersionString = getVersionString;
const versioNames = {
12: "0.2",
13: "0.2.1",
14: "0.2.2",
15: "0.3",
16: "0.3.1",
17: "0.4pre1",
18: "0.4pre2",
19: "0.4pre3",
20: "0.4",
21: "0.4.1",
22: "0.4.2",
23: "0.4.3",
24: "0.4.4",
25: "0.4.5",
26: "0.4.6pre1",
27: "0.4.6",
35: "0.5",
36: "0.5.1",
37: "0.5.2pre1",
38: "0.5.2pre2",
39: "0.5.2pre3",
40: "0.5.2",
41: "0.5.3",
42: "0.5.4",
43: "0.5.5pre1",
44: "0.5.5pre2",
45: "0.5.5pre3",
46: "0.5.5",
47: "0.5.6pre1",
48: "0.5.6",
49: "0.5.7pre1",
50: "0.5.7pre2",
51: "0.5.7pre3",
52: "0.5.7pre4",
53: "0.5.7",
54: "0.5.8",
75: "0.6pre0/April Fools",
100: "0.6pre1",
101: "0.6pre1",
102: "0.6pre1",
};
function getEffectName(type, effect) {
let name = globalEffectNames[effect];
if (name) {
return name;
}
switch (type) {
case chips_1.FurnaceSystemEnum.YM2612:
case chips_1.FurnaceSystemEnum.YM2612Extended:
name = effectsYM2612[effect];
break;
case chips_1.FurnaceSystemEnum.Genesis:
case chips_1.FurnaceSystemEnum.GenesisExtended:
name = effectsYM2612[effect] || effectsSN76489[effect];
break;
case chips_1.FurnaceSystemEnum.SMS:
name = effectsSN76489[effect];
break;
}
if (name) {
return name;
}
return { parameters: [], };
}
exports.getEffectName = getEffectName;
const globalEffectNames = {
0x90: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x91: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x92: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x93: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x94: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x95: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x96: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x97: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x98: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x99: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x9A: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x9B: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x9C: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x9D: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0x9F: { minversion: 12, format: "9xxx", name: "Sample offset", parameters: [{ xxx: "Sample offset = xxx*256", },], },
0xC0: { minversion: 12, format: "Cxxx", name: "Set Tick Rate", parameters: [{ xxx: "Tick rate in hz = xxx. Minimum = 10, maximum = 3FF", },], },
0xC1: { minversion: 12, format: "Cxxx", name: "Set Tick Rate", parameters: [{ xxx: "Tick rate in hz = xxx. Minimum = 10, maximum = 3FF", },], },
0xC2: { minversion: 12, format: "Cxxx", name: "Set Tick Rate", parameters: [{ xxx: "Tick rate in hz = xxx. Minimum = 10, maximum = 3FF", },], },
0xC3: { minversion: 12, format: "Cxxx", name: "Set Tick Rate", parameters: [{ xxx: "Tick rate in hz = xxx. Minimum = 10, maximum = 3FF", },], },
0xF0: { minversion: 12, format: "F0xx", name: "Set Tick Rate", parameters: [{ xxx: "Tick rate in bpm = xx", },], },
0x00: { minversion: 12, format: "00xy", name: "Arpeggio", parameters: [{ x: "Semitone offset 1", y: "Semitone offset 2", },], },
0xE0: { minversion: 12, format: "E0xx", name: "Arpeggio Speed", parameters: [{ xx: "The number of ticks between arpeggio change", },], },
0x01: { minversion: 12, format: "01xx", name: "Pitch Slide Up", parameters: [{ xx: "Speed of the slide", },], },
0x02: { minversion: 12, format: "02xx", name: "Pitch Slide Down", parameters: [{ xx: "Speed of the slide", },], },
0x03: { minversion: 12, format: "03xx", name: "Portamento", parameters: [{ xx: "Speed of the portamento", },], },
0xE1: { minversion: 12, format: "E1xy", name: "Note Slide Up", parameters: [{ x: "Speed of the slide", y: "Number of semitones to slide", },], },
0xE2: { minversion: 12, format: "E2xy", name: "Note Slide Down", parameters: [{ x: "Speed of the slide", y: "Number of semitones to slide", },], },
0xF1: { minversion: 12, format: "F1xx", name: "Pitch Slide Up", explanation: "Single tick variant", parameters: [{ xx: "Speed of the slide", },], },
0xF2: { minversion: 12, format: "F2xx", name: "Pitch Slide Down", explanation: "Single tick variant", parameters: [{ xx: "Speed of the slide", },], },
0x0A: { minversion: 12, format: "0Axy", name: "Volume Slide", parameters: [{ x0: "slide up speed", "0y": "slide down speed", },], },
0xFA: { minversion: 12, format: "FAxy", name: "Volume Slide", explanation: "4x faster variant", parameters: [{ x0: "slide up", "0y": "slide down", },], },
0xF3: { minversion: 12, format: "F3xx", name: "Fine Volume Slide Up", parameters: [{ xx: "speed", },], },
0xF4: { minversion: 12, format: "F4xx", name: "Fine Volume Slide Down", parameters: [{ xx: "speed", },], },
0xF8: { minversion: 12, format: "F8xx", name: "Fine Volume Slide Up", explanation: "single tick variant", parameters: [{ xx: "speed", },], },
0xF9: { minversion: 12, format: "F9xx", name: "Fine Volume Slide Down", explanation: "single tick variant", parameters: [{ xx: "speed", },], },
0x04: { minversion: 12, format: "04xy", name: "Vibrato", parameters: [{ x: "speed", y: "depth", },], },
0xE4: { minversion: 12, format: "E4xx", name: "Vibrato Range", parameters: [{ xx: "range in 1/16th of a semitone", },], },
0xE3: { minversion: 12, format: "E3xx", name: "Vibrato Shape", parameters: [{ xx: "0 = sine up/down, 1 = sine up/0, 2 = sine 0/down", },], },
0x07: { minversion: 12, format: "07xy", name: "Tremolo", parameters: [{ x: "speed", y: "depth", },], },
0x08: { minversion: 12, format: "08xy", name: "Set Panning", parameters: [{ x: "left", y: "right", },], },
0x80: { minversion: 12, format: "80xx", name: "Set Panning", parameters: [{ xx: "00 = left, 80 = center, FF = right", },], },
0x81: { minversion: 12, format: "81xx", name: "Set Panning", explanation: "Left channel panning", parameters: [], },
0x82: { minversion: 12, format: "82xx", name: "Set Panning", explanation: "Right channel panning", parameters: [], },
0x09: { minversion: 12, format: "09xx", name: "Set Speed 1", parameters: [], },
0x0F: { minversion: 12, format: "0Fxx", name: "Set Speed 2", parameters: [], },
0x0B: { minversion: 12, format: "0Bxx", name: "Jump To Pattern", parameters: [], },
0x0D: { minversion: 12, format: "0Dxx", name: "Jump To Next Pattern Row", parameters: [], },
0xFF: { minversion: 12, format: "FFxx", name: "Stop Song", parameters: [], },
0x0C: { minversion: 12, format: "0Cxx", name: "Retrigger", parameters: [], },
0xEC: { minversion: 12, format: "ECxx", name: "Note Cut", parameters: [], },
0xED: { minversion: 12, format: "EDxx", name: "Note Delay", parameters: [], },
0xEA: { minversion: 12, format: "EAxx", name: "Legato", parameters: [{ xx: "0 = disabled", },], },
0xE5: { minversion: 12, format: "E5xx", name: "Set Pitch", parameters: [{ xx: "80 = center, FF = ~1 semitone, 00 = ~-1 semitone", },], },
0xEF: { minversion: 12, format: "EFxx", name: "Set Global Pitch", explanation: "Broken", parameters: [], },
0xEB: { minversion: 12, format: "EBxx", name: "Set Sample Bank", parameters: [], },
0xEE: { minversion: 12, format: "EExx", name: "External Command", parameters: [], },
};
const effectsYM2612 = {
0x17: { minversion: 12, format: "17xx", name: "Enable DAC", parameters: [{ xx: "00 = disabled", },], },
0x18: { minversion: 12, format: "18xx", name: "Enable Extended CH3 mode", parameters: [{ xx: "00 = disabled", },], },
0x10: { minversion: 12, format: "10xy", name: "Setup LFO", parameters: [{ x: "enable", y: "speed", },], },
0x11: { minversion: 12, format: "11xx", name: "Set Feedback", parameters: [{ xx: "values 00 to 07 supported", },], },
0x30: { minversion: 12, format: "30xx", name: "Toggle Hard Envelope Reset", parameters: [{ xx: "00 = disabled", },], },
0x16: { minversion: 12, format: "16xy", name: "Set Multiple", parameters: [{ x: "values 0 to F supported", y: "operator from 1 to 4", },], },
0x50: { minversion: 12, format: "50xy", name: "Set Amplitude Modulation", parameters: [{ x: "0 = disabled", y: "operator from 1 to 4, or 0 for all operators", },], },
0x51: { minversion: 12, format: "51xy", name: "Set Sustain Level", parameters: [{ x: "values 0 to F supported", y: "operator from 1 to 4, or 0 for all operators", },], },
0x52: { minversion: 12, format: "52xy", name: "Set Release Rate", parameters: [{ x: "values 0 to F supported", y: "operator from 1 to 4, or 0 for all operators", },], },
0x53: { minversion: 12, format: "53xy", name: "Set Detune", parameters: [{ x: "values 0 to 7 supported, 3 = center", y: "operator from 1 to 4, or 0 for all operators", },], },
0x54: { minversion: 12, format: "54xy", name: "Set Rate Scale", parameters: [{ x: "values 0 to 3 supported", y: "operator from 1 to 4, or 0 for all operators", },], },
0x55: { minversion: 12, format: "55xy", name: "Set SSG-EG", parameters: [{ x: "values 0 to 7 = envelope enabled, 8 - F = envelope disabled", y: "operator from 1 to 4, or 0 for all operators", },], },
0x12: { minversion: 12, format: "12xx", name: "Set Total Level Of Operator 1", parameters: [{ xx: "values 00 (highest) to 7F (lowest) supported", },], },
0x13: { minversion: 12, format: "13xx", name: "Set Total Level Of Operator 2", parameters: [{ xx: "values 00 (highest) to 7F (lowest) supported", },], },
0x14: { minversion: 12, format: "14xx", name: "Set Total Level Of Operator 3", parameters: [{ xx: "values 00 (highest) to 7F (lowest) supported", },], },
0x15: { minversion: 12, format: "15xx", name: "Set Total Level Of Operator 4", parameters: [{ xx: "values 00 (highest) to 7F (lowest) supported", },], },
0x19: { minversion: 12, format: "19xx", name: "Set Attack Rate All Operators", parameters: [{ xx: "values 00 to 1F supported", },], },
0x1A: { minversion: 12, format: "1Axx", name: "Set Attack Rate Of Operator 1", parameters: [{ xx: "values 00 to 1F supported", },], },
0x1B: { minversion: 12, format: "1Bxx", name: "Set Attack Rate Of Operator 2", parameters: [{ xx: "values 00 to 1F supported", },], },
0x1C: { minversion: 12, format: "1Cxx", name: "Set Attack Rate Of Operator 3", parameters: [{ xx: "values 00 to 1F supported", },], },
0x1D: { minversion: 12, format: "1Dxx", name: "Set Attack Rate Of Operator 4", parameters: [{ xx: "values 00 to 1F supported", },], },
0x56: { minversion: 12, format: "56xx", name: "Set Decay 1 Rate All Operators", parameters: [{ xx: "values 00 to 1F supported", },], },
0x57: { minversion: 12, format: "57xx", name: "Set Decay 1 Rate Of Operator 1", parameters: [{ xx: "values 00 to 1F supported", },], },
0x58: { minversion: 12, format: "58xx", name: "Set Decay 1 Rate Of Operator 2", parameters: [{ xx: "values 00 to 1F supported", },], },
0x59: { minversion: 12, format: "59xx", name: "Set Decay 1 Rate Of Operator 3", parameters: [{ xx: "values 00 to 1F supported", },], },
0x5A: { minversion: 12, format: "5Axx", name: "Set Decay 1 Rate Of Operator 4", parameters: [{ xx: "values 00 to 1F supported", },], },
0x5B: { minversion: 12, format: "5Bxx", name: "Set Decay 2 Rate All Operators", parameters: [{ xx: "values 00 to 1F supported", },], },
0x5C: { minversion: 12, format: "5Cxx", name: "Set Decay 2 Rate Of Operator 1", parameters: [{ xx: "values 00 to 1F supported", },], },
0x5D: { minversion: 12, format: "5Dxx", name: "Set Decay 2 Rate Of Operator 2", parameters: [{ xx: "values 00 to 1F supported", },], },
0x5E: { minversion: 12, format: "5Exx", name: "Set Decay 2 Rate Of Operator 3", parameters: [{ xx: "values 00 to 1F supported", },], },
0x5F: { minversion: 12, format: "5Fxx", name: "Set Decay 2 Rate Of Operator 4", parameters: [{ xx: "values 00 to 1F supported", },], },
};
const effectsSN76489 = {
0x20: { minversion: 12, format: "20xy", name: "Set Noise Mode", parameters: [{ x: "0 = preset freq, 1 = ch3 freq", y: "0 = periodic, 1 = white noise", },], },
};