midiviz-prepare
Advanced tools
Comparing version 0.0.4 to 0.0.5
@@ -6,9 +6,22 @@ #!/usr/bin/env node | ||
var MidiFunctions_1 = require("./MidiFunctions"); | ||
function displayHelp() { | ||
console.error("Usage:"); | ||
console.error(); | ||
console.error("midiviz-prepare", "<input filename>", "<output filename> [<wanted start time>]"); | ||
} | ||
function parseArgs() { | ||
if (process.argv.length < 4) { | ||
throw new Error("Missing parameters! Please submit input and output file name."); | ||
var argv = process.argv; | ||
var args = argv.length; | ||
if (args < 4 || args > 5) { | ||
displayHelp(); | ||
return; | ||
} | ||
var wantedStartTime; | ||
if (args === 5) { | ||
wantedStartTime = parseFloat(argv[4]); | ||
} | ||
return { | ||
inputFileName: process.argv[2], | ||
outputFileName: process.argv[3], | ||
wantedStartTime: wantedStartTime, | ||
}; | ||
@@ -27,35 +40,99 @@ } | ||
/** | ||
* Split the minor notes from the first track a separate second track | ||
* Returns the minimum of two values that might be missing. | ||
* | ||
* The missing values are _not_ considered as a candidate. | ||
* If both values are missing, undefined is returned. | ||
*/ | ||
function splitMinors(music) { | ||
// const keys = Object.keys(music); | ||
// console.log("Objects in main scope: ", keys); | ||
// keys | ||
// .filter((key) => key !== "0") | ||
// .forEach((key) => console.log(key, ":", music[key])); | ||
var newMusic = MidiFunctions_1.createMusic(1, music.ppqn); | ||
var primaryTrack = MidiFunctions_1.addTrack(newMusic); | ||
var secondaryTrack = MidiFunctions_1.addTrack(newMusic); | ||
music[0] | ||
.filter(function (event) { return isMajorNote(event); }) // !isMinorNote(event)) | ||
.forEach(function (event) { | ||
primaryTrack.add(event.tt, event); | ||
}); | ||
music[0] | ||
.filter(function (event) { return isMinorNote(event); }) // !isMajorNote(event)) | ||
.forEach(function (event) { | ||
secondaryTrack.add(event.tt, event); | ||
}); | ||
console.log("Moved", primaryTrack.length, "major note events to primary track;", secondaryTrack.length, "minor note events to secondary track."); | ||
return newMusic; | ||
function saneMin(a1, a2) { | ||
if (a1 === undefined) { | ||
if (a2 === undefined) { | ||
// None of the input values are defined; we have nothing better to do than returning undefined | ||
return undefined; | ||
} | ||
else { | ||
// A1 is undefined, A2 is defined | ||
return a2; | ||
} | ||
} | ||
else { | ||
if (a2 === undefined) { | ||
// A1 is defined, A2 is undefined | ||
return a1; | ||
} | ||
else { | ||
// Both A1 and A2 are defined | ||
return Math.min(a1, a2); | ||
} | ||
} | ||
} | ||
function main() { | ||
var _a, _b; | ||
// Parse the args | ||
var args = parseArgs(); | ||
var music = MidiFunctions_1.loadMusic(args.inputFileName); | ||
if (!args) { | ||
return; | ||
} | ||
var inputFileName = args.inputFileName, outputFileName = args.outputFileName, wantedStartTime = args.wantedStartTime; | ||
// Load the music | ||
var music; | ||
try { | ||
music = MidiFunctions_1.loadMusic(inputFileName); | ||
} | ||
catch (error) { | ||
console.error("Error while reading specified input file:", error.message); | ||
return; | ||
} | ||
// Do the processing | ||
if (music.length !== 1) { | ||
throw new Error("Sorry, but I can only handle single-track MIDI files!"); | ||
console.error("Sorry, but I can only handle single-track MIDI files!"); | ||
return; | ||
} | ||
var newMusic = splitMinors(music); | ||
MidiFunctions_1.saveMusic(newMusic, args.outputFileName); | ||
var tempo = music.ppqn; | ||
// Filter the notes | ||
var majors = music[0].filter(function (event) { return isMajorNote(event); }); | ||
var minors = music[0].filter(function (event) { return isMinorNote(event); }); | ||
// Calculating required time adjustment | ||
var timeOffset = 0; | ||
if (wantedStartTime !== undefined) { | ||
console.log("Tempo is:", tempo, "ticks per half second"); | ||
var tickLength = 0.5 / tempo; | ||
var currentStartTicks = saneMin((_a = majors[0]) === null || _a === void 0 ? void 0 : _a.tt, (_b = minors[0]) === null || _b === void 0 ? void 0 : _b.tt); | ||
if (currentStartTicks === undefined) { | ||
console.log("There are no notes; not doing time adjustment."); | ||
} | ||
else { | ||
var wantedStartTicks = wantedStartTime / tickLength; | ||
timeOffset = wantedStartTicks - currentStartTicks; | ||
console.log("Current start time:", currentStartTicks, "ticks"); | ||
console.log("Wanted start time:", wantedStartTime, "secs,", wantedStartTicks, "ticks"); | ||
console.log("Adjusting timestamps with", timeOffset, "ticks"); | ||
} | ||
} | ||
else { | ||
console.log("No time adjustment requested."); | ||
} | ||
// Create a new MIDI file | ||
var newMusic = MidiFunctions_1.createMusic(1, tempo); | ||
// Add the track for the major notes | ||
var primaryTrack = MidiFunctions_1.addTrack(newMusic); | ||
majors.forEach(function (event) { | ||
primaryTrack.add(event.tt + timeOffset, event); | ||
}); | ||
// Add the tract for the minor notes | ||
var secondaryTrack = MidiFunctions_1.addTrack(newMusic); | ||
minors.forEach(function (event) { | ||
secondaryTrack.add(event.tt + timeOffset, event); | ||
}); | ||
// Log the results | ||
console.log("Moved", primaryTrack.length, "major note events to primary track;", secondaryTrack.length, "minor note events to secondary track."); | ||
// Save the result | ||
try { | ||
MidiFunctions_1.saveMusic(newMusic, outputFileName); | ||
console.log("Output saved to", outputFileName); | ||
} | ||
catch (error) { | ||
console.error("Error while writing specified output file:", error.message); | ||
return; | ||
} | ||
} | ||
main(); |
{ | ||
"name": "midiviz-prepare", | ||
"author": "Kristof Csillag <csillag.kristof@gmail.com>", | ||
"version": "0.0.4", | ||
"version": "0.0.5", | ||
"description": "A preprocessor for MIDIVisualizer", | ||
@@ -6,0 +6,0 @@ "main": "build/main.js", |
`midiviz-prepare` is a MIDI file pre-processor for [MIDIVisualizer](https://github.com/ekuiter/MIDIVisualizer). | ||
## Background | ||
## Problem | ||
@@ -10,3 +10,3 @@ - Once upon a time, there was a great utility called [MIDIVisualizer](https://github.com/kosua20/MIDIVisualizer), by [@kosua20](https://github.com/kosua20). | ||
## Solution | ||
## Reaction | ||
@@ -18,7 +18,9 @@ My answer is that I choose _both_. I need the fancy new features, _and_ the correct coloring. | ||
So instead of that, I just created a small MIDI pre-processor utility, which is able to move the minor keys to a separate track, and thus achieve the desired coloring with the new version. | ||
## Solution | ||
So I just created a small MIDI pre-processor utility, which is able to move the minor keys to a separate track, and thus achieve the desired coloring with the new version. | ||
## Installation | ||
Just install with npm. The command with appear in your normal npm binary directory. | ||
Just install with `npm`. The command with appear in your normal npm binary directory. | ||
@@ -34,4 +36,8 @@ ``` | ||
``` | ||
midiviz-prepare input.mid output.mid | ||
midiviz-prepare input.mid output.mid [start time in seconds] | ||
``` | ||
### But what does it do? | ||
Well, it reads the music in the input (single-track) MIDI file, and creates a new MIDI file out of it, by copying all the major notes on the first track, and all the minor notes to the second track. It can optionally adjust the timestamps, so that the music starts at the requested time. (Which can be helpful to get more deterministic timing, in case you want to mix the resulting animation with sound.) | ||
161
src/main.ts
@@ -6,9 +6,3 @@ #!/usr/bin/env node | ||
import { SMF } from "./MidiTypes"; | ||
import { | ||
addTrack, | ||
createMusic, | ||
describe, | ||
loadMusic, | ||
saveMusic, | ||
} from "./MidiFunctions"; | ||
import { addTrack, createMusic, loadMusic, saveMusic } from "./MidiFunctions"; | ||
import { MIDI } from "./JZZTypes"; | ||
@@ -19,13 +13,30 @@ | ||
outputFileName: string; | ||
wantedStartTime?: number; | ||
} | ||
function parseArgs(): Parameters { | ||
if (process.argv.length < 4) { | ||
throw new Error( | ||
"Missing parameters! Please submit input and output file name." | ||
); | ||
function displayHelp() { | ||
console.error("Usage:"); | ||
console.error(); | ||
console.error( | ||
"midiviz-prepare", | ||
"<input filename>", | ||
"<output filename> [<wanted start time>]" | ||
); | ||
} | ||
function parseArgs(): Parameters | undefined { | ||
const { argv } = process; | ||
const args = argv.length; | ||
if (args < 4 || args > 5) { | ||
displayHelp(); | ||
return; | ||
} | ||
let wantedStartTime: number | undefined; | ||
if (args === 5) { | ||
wantedStartTime = parseFloat(argv[4]); | ||
} | ||
return { | ||
inputFileName: process.argv[2], | ||
outputFileName: process.argv[3], | ||
wantedStartTime, | ||
}; | ||
@@ -48,23 +59,97 @@ } | ||
/** | ||
* Split the minor notes from the first track a separate second track | ||
* Returns the minimum of two values that might be missing. | ||
* | ||
* The missing values are _not_ considered as a candidate. | ||
* If both values are missing, undefined is returned. | ||
*/ | ||
function splitMinors(music: SMF): SMF { | ||
// const keys = Object.keys(music); | ||
// console.log("Objects in main scope: ", keys); | ||
// keys | ||
// .filter((key) => key !== "0") | ||
// .forEach((key) => console.log(key, ":", music[key])); | ||
const newMusic = createMusic(1, music.ppqn); | ||
function saneMin(a1: number, a2: number): number | undefined { | ||
if (a1 === undefined) { | ||
if (a2 === undefined) { | ||
// None of the input values are defined; we have nothing better to do than returning undefined | ||
return undefined; | ||
} else { | ||
// A1 is undefined, A2 is defined | ||
return a2; | ||
} | ||
} else { | ||
if (a2 === undefined) { | ||
// A1 is defined, A2 is undefined | ||
return a1; | ||
} else { | ||
// Both A1 and A2 are defined | ||
return Math.min(a1, a2); | ||
} | ||
} | ||
} | ||
function main() { | ||
// Parse the args | ||
const args = parseArgs(); | ||
if (!args) { | ||
return; | ||
} | ||
const { inputFileName, outputFileName, wantedStartTime } = args; | ||
// Load the music | ||
let music: SMF | undefined; | ||
try { | ||
music = loadMusic(inputFileName); | ||
} catch (error) { | ||
console.error("Error while reading specified input file:", error.message); | ||
return; | ||
} | ||
// Do the processing | ||
if (music.length !== 1) { | ||
console.error("Sorry, but I can only handle single-track MIDI files!"); | ||
return; | ||
} | ||
const tempo = music.ppqn; | ||
// Filter the notes | ||
const majors = music[0].filter((event) => isMajorNote(event)); | ||
const minors = music[0].filter((event) => isMinorNote(event)); | ||
// Calculating required time adjustment | ||
let timeOffset = 0; | ||
if (wantedStartTime !== undefined) { | ||
console.log("Tempo is:", tempo, "ticks per half second"); | ||
const tickLength = 0.5 / tempo; | ||
const currentStartTicks = saneMin(majors[0]?.tt, minors[0]?.tt); | ||
if (currentStartTicks === undefined) { | ||
console.log("There are no notes; not doing time adjustment."); | ||
} else { | ||
const wantedStartTicks = wantedStartTime / tickLength; | ||
timeOffset = wantedStartTicks - currentStartTicks; | ||
console.log("Current start time:", currentStartTicks, "ticks"); | ||
console.log( | ||
"Wanted start time:", | ||
wantedStartTime, | ||
"secs,", | ||
wantedStartTicks, | ||
"ticks" | ||
); | ||
console.log("Adjusting timestamps with", timeOffset, "ticks"); | ||
} | ||
} else { | ||
console.log("No time adjustment requested."); | ||
} | ||
// Create a new MIDI file | ||
const newMusic = createMusic(1, tempo); | ||
// Add the track for the major notes | ||
const primaryTrack = addTrack(newMusic); | ||
majors.forEach((event) => { | ||
primaryTrack.add(event.tt + timeOffset, event); | ||
}); | ||
// Add the tract for the minor notes | ||
const secondaryTrack = addTrack(newMusic); | ||
music[0] | ||
.filter((event) => isMajorNote(event)) // !isMinorNote(event)) | ||
.forEach((event) => { | ||
primaryTrack.add(event.tt, event); | ||
}); | ||
music[0] | ||
.filter((event) => isMinorNote(event)) // !isMajorNote(event)) | ||
.forEach((event) => { | ||
secondaryTrack.add(event.tt, event); | ||
}); | ||
minors.forEach((event) => { | ||
secondaryTrack.add(event.tt + timeOffset, event); | ||
}); | ||
// Log the results | ||
console.log( | ||
@@ -77,15 +162,13 @@ "Moved", | ||
); | ||
return newMusic; | ||
} | ||
function main() { | ||
const args = parseArgs(); | ||
const music = loadMusic(args.inputFileName); | ||
if (music.length !== 1) { | ||
throw new Error("Sorry, but I can only handle single-track MIDI files!"); | ||
// Save the result | ||
try { | ||
saveMusic(newMusic, outputFileName); | ||
console.log("Output saved to", outputFileName); | ||
} catch (error) { | ||
console.error("Error while writing specified output file:", error.message); | ||
return; | ||
} | ||
const newMusic = splitMinors(music); | ||
saveMusic(newMusic, args.outputFileName); | ||
} | ||
main(); |
@@ -38,3 +38,3 @@ { | ||
/* Additional Checks */ | ||
// "noUnusedLocals": true, /* Report errors on unused locals. */ | ||
"noUnusedLocals": true, /* Report errors on unused locals. */ | ||
// "noUnusedParameters": true, /* Report errors on unused parameters. */ | ||
@@ -41,0 +41,0 @@ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
58458
16
550
41