UNPKG

furnace-module-interface

Version:

An interface for loading, processing and saving furnace modules in an object-oriented manner. Fully typed via TypeScript.

120 lines (119 loc) 5.64 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FurnaceSong = void 0; const channels_1 = require("./channels"); const defaults_1 = require("./defaults"); class FurnaceSong { constructor() { this.timebase = defaults_1.defaults.timebase; this.speed1 = defaults_1.defaults.speed1; this.speed2 = defaults_1.defaults.speed2; this.highlightA = defaults_1.defaults.highlightA; this.highlightB = defaults_1.defaults.highlightB; this.initialArpeggioTime = 1; this.ticksPerSecond = defaults_1.defaults.tps; this.virtualTempoNumerator = defaults_1.defaults.tempo; this.virtualTempoDenominator = defaults_1.defaults.tempo; this.name = defaults_1.defaults.songName; this.comment = defaults_1.defaults.songComment; this.rows = defaults_1.defaults.rows; this.channels = null; this.channelInfo = []; } static create() { return new FurnaceSong(); } static fromHelper(helper, info, version) { const song = FurnaceSong.create(); if (FurnaceSong.magic !== helper.getASCII(4)) { throw new Error("Expected format magic string \"" + FurnaceSong.magic + "\" when loading a subsong block."); } helper.skipBytes(4); song.timebase = helper.readUInt8() + 1; song.speed1 = helper.readUInt8(); song.speed2 = helper.readUInt8(); song.initialArpeggioTime = helper.readUInt8(); song.ticksPerSecond = helper.readFloat32LE(); song.rows = helper.readUInt16LE(); const numOrders = helper.readUInt16LE(); if (song.rows > 256) { throw new Error("Expected at most 256 rows in a pattern, but " + song.rows + " were loaded."); } if (numOrders > 256) { throw new Error("Expected at most 256 orders in a subsong, but " + numOrders + " were loaded."); } song.highlightA = helper.readUInt8(); song.highlightB = helper.readUInt8(); song.virtualTempoNumerator = helper.readUInt16LE(); song.virtualTempoDenominator = helper.readUInt16LE(); song.name = helper.readString("utf-8"); song.comment = helper.readString("utf-8"); song.fetchChannels(helper, version, (0, channels_1.computeChannelCount)(info), numOrders); return song; } fetchChannels(helper, version, channelCount, orderCount) { for (let i = 0; i < channelCount; i++) { this.channelInfo.push(new channels_1.FurnaceChannelInfo()); } for (let i = 0; i < this.channelInfo.length; i++) { this.channelInfo[i].orders = [...helper.readBuffer(orderCount),].map((v) => v & (version < 80 ? 0x7F : 0xFF)); } for (let i = 0; i < this.channelInfo.length; i++) { this.channelInfo[i].effects = helper.readUInt8(); } for (let i = 0; i < this.channelInfo.length; i++) { this.channelInfo[i].hidden = helper.readUInt8() === 0; } for (let i = 0; i < this.channelInfo.length; i++) { this.channelInfo[i].collapsed = helper.readUInt8() === 0; } for (let i = 0; i < this.channelInfo.length; i++) { this.channelInfo[i].longname = helper.readString("ascii"); } for (let i = 0; i < this.channelInfo.length; i++) { this.channelInfo[i].shortname = helper.readString("ascii"); } } writeToBuffer(helper) { const _begin = helper.getPosition(8); helper.writeString(FurnaceSong.magic, "ascii", false); helper.skipBytes(4); helper.writeUInt8(this.timebase - 1); helper.writeUInt8(this.speed1); helper.writeUInt8(this.speed2); helper.writeUInt8(this.initialArpeggioTime); helper.writeFloat32LE(this.ticksPerSecond); helper.writeUInt16LE(this.rows); helper.writeUInt16LE(this.channelInfo[0].orders.length); helper.writeUInt8(this.highlightA); helper.writeUInt8(this.highlightB); helper.writeUInt16LE(this.virtualTempoNumerator); helper.writeUInt16LE(this.virtualTempoDenominator); helper.writeString(this.name, "utf-8", true); helper.writeString(this.comment, "utf-8", true); this.writeChannels(helper); helper.writeUInt32LE(helper.getPosition() - _begin, _begin - 4); } writeChannels(helper) { for (let i = 0; i < this.channelInfo.length; i++) { helper.writeBuffer(Buffer.from(this.channelInfo[i].orders), 0, this.channelInfo[i].orders.length); } helper.writeBuffer(Buffer.from(this.channelInfo.map((c) => c.effects)), 0, this.channelInfo.length); helper.writeBuffer(Buffer.from(this.channelInfo.map((c) => c.hidden ? 0 : 1)), 0, this.channelInfo.length); helper.writeBuffer(Buffer.from(this.channelInfo.map((c) => c.collapsed ? 0 : 1)), 0, this.channelInfo.length); for (let i = 0; i < this.channelInfo.length; i++) { helper.writeString(this.channelInfo[i].longname, "ascii", true); } for (let i = 0; i < this.channelInfo.length; i++) { helper.writeString(this.channelInfo[i].shortname, "ascii", true); } } getChannels(info) { if (this.channels) { return this.channels; } return this.channels = (0, channels_1.computeChannelObjects)(info, this.channelInfo); } } exports.FurnaceSong = FurnaceSong; FurnaceSong.magic = "SONG";