retroload
Version:
Command line utility for converting tape archive files of historical computers into sound for loading them on real devices
135 lines (132 loc) • 6.33 kB
JavaScript
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());
});
};
import { Exception, Logger, encodeUint8, encodeUint8Wav, formatPosition, getAllEncodingOptions, getEncodingAdapters, identify, version as libVersion } from 'retroload-lib';
import { readFile, writeFile } from './Utils.js';
import { AplayWrapper } from './player/AplayWrapper.js';
import { CustomCommand } from './help/CustomCommand.js';
import { Option } from 'commander';
import { SoxWrapper } from './player/SoxWrapper.js';
import { SpeakerWrapper } from './player/SpeakerWrapper.js';
import { version as cliVersion } from './version.js';
const playerWrapperPriority = [
SpeakerWrapper,
AplayWrapper,
SoxWrapper,
];
main()
.catch((err) => {
Logger.error(err);
});
function main() {
return __awaiter(this, void 0, void 0, function* () {
const program = (new CustomCommand(getEncodingAdapters()))
.name('retroload')
.description('Play tape archive files of historical computers for loading them on real devices or convert them to WAVE files.')
.argument('infile', 'Path to file to play (default) or convert (when using -o <outfile> option)')
.allowExcessArguments(false)
.option('-o <outfile>', 'Generate WAVE file <outfile> instead of playback')
.option('-f, --format <format>', 'Format of input file (required when automatic format detection by content and filename fails). See list below for supported formats.')
.option('-l, --loglevel <loglevel>', 'Verbosity of log output', '1')
.option('-a, --annotations', 'Output annotations (if available)')
.version(`retroload: ${cliVersion}\nretroload-lib: ${libVersion}`)
.showHelpAfterError();
// Options defined in adapters/encoders
const allOptions = getAllEncodingOptions();
allOptions.sort((a, b) => a.common && !b.common ? -1 : 0);
for (const option of allOptions) {
program.addOption(new Option(getCommanderFlagsString(option), option.description).hideHelp());
}
program.parse();
const options = program.opts();
const infile = program.args[0];
const outfile = typeof options['o'] === 'string' ? options['o'] : undefined;
const shouldPlay = outfile === undefined;
const format = typeof options['format'] === 'string' ? options['format'] : undefined;
Logger.setVerbosity(parseInt(typeof options['loglevel'] === 'string' ? options['loglevel'] : '1', 10));
const data = readFile(infile);
Logger.debug(`Processing ${infile}...`);
try {
const adapterName = format !== null && format !== void 0 ? format : identify(data, infile);
if (adapterName === undefined) {
Logger.error(`Unable to identify ${infile}. Please specify format.`);
process.exit(1);
}
let result;
if (shouldPlay) {
result = encodeUint8(adapterName, data, options);
}
else {
result = encodeUint8Wav(adapterName, data, options);
}
if (options['annotations']) {
printAnnotations(result.annotations);
}
if (shouldPlay) {
const playerWrapper = yield getPlayerWrapper();
yield playerWrapper.play(result.data);
Logger.info('Finished.');
process.exit(0);
}
else {
// save
writeFile(outfile, result.data);
process.exit(0);
}
}
catch (e) {
if (e instanceof Exception.UsageError) {
Logger.error(e.message);
process.exit(1);
}
else {
throw e; // show full stack trace for unexpected errors
}
}
});
}
function getCommanderFlagsString(optionDefinition) {
return optionDefinition.type !== 'text' || optionDefinition.argument === undefined ? `--${optionDefinition.name}` : `--${optionDefinition.name} <${optionDefinition.argument}>`;
}
// eslint-disable-next-line consistent-return
function getPlayerWrapper() {
return __awaiter(this, void 0, void 0, function* () {
for (const wrapper of playerWrapperPriority) {
// eslint-disable-next-line no-await-in-loop
const pw = yield wrapper.create(44100, 8, 1);
if (pw !== undefined) {
return pw;
}
}
const msg = `
No player found. retroload supports the following options for playing audio:
speaker library:
Install it using "npm install speaker". To install (and build) it, you may need to install additional system packages like build-essential and libasound2-dev.
aplay:
Usually part of the "alsa-utils" package. Try installing it using "apt-get install alsa-utils" or "yum install alsa-utils".
(SoX) play:
Part of the SoX package. Try installing it using "apt-get install sox" or "yum installs sox".
`;
Logger.error(msg);
process.exit(1);
});
}
function printAnnotations(annotations, depth = 0) {
for (const annotation of annotations) {
const indentation = ' '.repeat(depth);
const range = annotation.end === undefined
? formatPosition(annotation.begin)
: `${formatPosition(annotation.begin)} - ${formatPosition(annotation.end)}`;
const line = `${range} ${indentation}${annotation.label}\n`;
process.stdout.write(line);
printAnnotations(annotation.annotations, depth + 1);
}
}
//# sourceMappingURL=retroload.js.map