m8-js
Version:
Library for loading and interacting with Dirtywave M8 instrument/song files.
294 lines (257 loc) • 10.5 kB
JavaScript
/* Copyright 2023 Jeremy Whitlock
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { parseCLIHexInt, parseCLIInt } = require('./helpers')
const commander = require('commander')
const exportJSON = require('./export-json')
const importJSON = require('./import-json')
const instrumentEnvelope = require('./instrument-envelope')
const instrumentTable = require('./instrument-table')
const instrumentVersion = require('./instrument-version')
const instrumentView = require('./instrument-view')
const m8FileView = require('./m8-file-view')
const pkg = require('../../package.json')
const projectEffects = require('./project-effects')
const projectMIDIMapping = require('./project-midi-mapping')
const projectMIDISettings = require('./project-midi-settings')
const projectMixer = require('./project-mixer')
const projectVersion = require('./project-version')
const projectView = require('./project-view')
const scaleVersion = require('./scale-version')
const scaleView = require('./scale-view')
const songChain = require('./song-chain')
const songGroove = require('./song-groove')
const songInstrument = require('./song-instrument')
const songPhrase = require('./song-phrase')
const songPhraseAt = require('./song-phrase-at')
const songScale = require('./song-scale')
const songTable = require('./song-table')
const songView = require('./song-view')
const themeVersion = require('./theme-version')
const themeView = require('./theme-view')
/**
* Creates the CLI program.
*
* @param {Boolean} doNotExitProcess - Whether or not to exit when an error occurs
*/
const createProgram = (doNotExitProcess) => {
const program = new commander.Command()
// For CLI testing, we need to tell commander.js to not process.exit() upon error and to write all stuff to
// `console.log` since it's mocked in CLI tests.
if (doNotExitProcess) {
program.exitOverride().configureOutput({
writeErr (str) {
console.log(str)
},
writeOut (str) {
console.log(str)
}
})
}
program
.name('m8')
.description('Various utilities for interacting with M8 files')
.version(pkg.version)
// Create commands
program
.command('export')
.description('exports an M8 file to JSON')
.argument('<m8-file>', 'the m8file')
.option('-o, --output <path>', 'the path to save the M8 file')
.action(exportJSON)
program
.command('import')
.description('imports an M8 file from JSON')
.argument('<m8-file>', 'the m8file')
.requiredOption('-o, --output <path>', 'the path to save the M8 file')
.action(importJSON)
const instrumentCommand = program
.command('instrument')
.description('instrument specific commands')
const projectCommand = program
.command('project')
.description('project specific commands')
const scaleCommand = program
.command('scale')
.description('scale specific commands')
const songCommand = program
.command('song')
.description('song specific commands')
const themeCommand = program
.command('theme')
.description('theme specific commands')
// View (default) command
program
.command('view', { isDefault: true })
.description('view an M8 file of any type')
.argument('<m8-file>', 'the m8file')
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(m8FileView)
// Create instrument sub-commands
instrumentCommand
.command('envelope')
.description('print the m8 instrument envelope')
.argument('<m8-file>', 'the m8file')
.option('-i, --instrument <number>', 'the instrument whose envelope to display (required for Song files)',
parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(instrumentEnvelope)
instrumentCommand
.command('table')
.description('print the m8 instrument table')
.argument('<m8-file>', 'the m8file')
.option('-i, --instrument <number>', 'the instrument to display (required for Song files)', parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(instrumentTable)
instrumentCommand
.command('view')
.description('print the m8 instrument view')
.argument('<m8-file>', 'the m8file')
.option('-i, --instrument <number>', 'the instrument to display (required for Song files)', parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(instrumentView)
instrumentCommand
.command('version')
.description('print the m8 instrument version')
.argument('<m8-file>', 'the m8file')
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(instrumentVersion)
// Create project sub-commands
projectCommand
.command('effects')
.description('print the m8 project effects')
.argument('<m8-file>', 'the m8file')
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(projectEffects)
projectCommand
.command('midi-mapping')
.description('print the m8 project MIDI mappings')
.argument('<m8-file>', 'the m8file')
.option('-s, --starting-row <number>', 'the starting row to display', parseCLIInt, 0)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(projectMIDIMapping)
projectCommand
.command('midi-settings')
.description('print the m8 project MIDI settings')
.argument('<m8-file>', 'the m8file')
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(projectMIDISettings)
projectCommand
.command('mixer')
.description('print the m8 project mixer settings')
.argument('<m8-file>', 'the m8file')
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(projectMixer)
projectCommand
.command('view')
.description('print the m8 project view')
.argument('<m8-file>', 'the m8file')
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(projectView)
projectCommand
.command('version')
.description('print the m8 project version')
.argument('<m8-file>', 'the m8file')
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(projectVersion)
// Create scale sub-commands
scaleCommand
.command('view')
.description('print the m8 scale')
.argument('<m8-file>', 'the m8file')
.option('-k, --key <number>', 'the root key to use for the scale', parseCLIHexInt, 0x00)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(scaleView)
scaleCommand
.command('version')
.description('print the m8 scale version')
.argument('<m8-file>', 'the m8file')
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(scaleVersion)
// Create song sub-commands
songCommand
.command('chain')
.description('print the m8 song chain')
.argument('<m8-file>', 'the m8file')
.requiredOption('-c, --chain <number>', 'the chain to display', parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(songChain)
songCommand
.command('groove')
.description('print the m8 song groove')
.argument('<m8-file>', 'the m8file')
.requiredOption('-g, --groove <number>', 'the groove to display', parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(songGroove)
songCommand
.command('instrument')
.description('print the m8 song instrument view')
.argument('<m8-file>', 'the m8file')
.requiredOption('-i, --instrument <number>', 'the instrument to display', parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(songInstrument)
songCommand
.command('phrase')
.description('print the m8 song phrase (in isolation)')
.argument('<m8-file>', 'the m8file')
.requiredOption('-p, --phrase <number>', 'the phrase to display', parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(songPhrase)
songCommand
.command('phrase-at')
.description('print the m8 song phrase (at track location)')
.argument('<m8-file>', 'the m8file')
.requiredOption('-n, --track-num <number>', 'the track number', parseCLIInt)
.requiredOption('-s, --track-step <number>', 'the track step', parseCLIHexInt)
.requiredOption('-c, --chain-step <number>', 'the chain step for the phrase', parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(songPhraseAt)
songCommand
.command('scale')
.description('print the m8 song scale')
.argument('<m8-file>', 'the m8file')
.requiredOption('-s, --scale <number>', 'the scale to display', parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(songScale)
songCommand
.command('table')
.description('print the m8 song table')
.argument('<m8-file>', 'the m8file')
.requiredOption('-t, --table <number>', 'the table to display', parseCLIHexInt)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(songTable)
songCommand
.command('view')
.description('print the m8 song view')
.argument('<m8-file>', 'the m8file')
.option('-s, --starting-row <number>', 'the starting row to display', parseCLIHexInt, 0)
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(songView)
// Create theme sub-commands
themeCommand
.command('view')
.description('print the m8 theme view')
.argument('<m8-file>', 'the m8file')
.action(themeView)
themeCommand
.command('version')
.description('print the m8 theme version')
.argument('<m8-file>', 'the m8file')
.option('-T, --theme <path>', 'the path to an M8 theme file to use')
.action(themeVersion)
return program
}
module.exports = {
createProgram
}