From a9f57f70e534796a0bb6172d43028ed977abb404 Mon Sep 17 00:00:00 2001 From: infojunkie Date: Sun, 13 Oct 2024 15:59:59 -0700 Subject: [PATCH] Add midi-file for MIDI manipulations --- package-lock.json | 34 ++++++++++++++++++++++++---------- package.json | 6 ++++-- src/js/midi-file.js | 24 ++++++++++++++++++++++++ test/midi-file.test.js | 18 ++++++++++++++++++ test/midi-timemap.test.js | 2 +- 5 files changed, 71 insertions(+), 13 deletions(-) create mode 100755 src/js/midi-file.js create mode 100644 test/midi-file.test.js diff --git a/package-lock.json b/package-lock.json index f978ee60..230d36a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "musicxml-midi", - "version": "2.8.5", + "version": "2.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "musicxml-midi", - "version": "2.8.5", + "version": "2.9.0", "hasInstallScript": true, "license": "GPL-3.0-only", "dependencies": { @@ -15,6 +15,7 @@ "cors": "^2.8.5", "express": "^4.21.1", "express-fileupload": "^1.5.1", + "get-stdin": "^9.0.0", "morgan": "^1.10.0", "node-fetch": "^3.3.2", "node-jq": "^6.0.1", @@ -25,6 +26,7 @@ "xslt3": "^2.6.0" }, "bin": { + "midi-file": "src/js/midi-file.js", "midi-timemap": "src/js/midi-timemap.js", "musicxml-examples": "src/js/musicxml-examples.js", "musicxml-grooves": "src/js/musicxml-grooves.js", @@ -3083,6 +3085,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -5093,24 +5107,24 @@ } }, "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", + "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", "license": "MIT", "dependencies": { - "entities": "^4.4.0" + "entities": "^4.5.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", - "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", "license": "MIT", "dependencies": { - "domhandler": "^5.0.2", + "domhandler": "^5.0.3", "parse5": "^7.0.0" }, "funding": { diff --git a/package.json b/package.json index a8b81c6c..e27687c0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "musicxml-midi", - "version": "2.8.5", + "version": "2.9.0", "description": "MusicXML to MIDI converter", "type": "module", "directories": { @@ -9,6 +9,7 @@ "bin": { "musicxml-midi": "src/js/server.js", "midi-timemap": "src/js/midi-timemap.js", + "midi-file": "src/js/midi-file.js", "musicxml-examples": "src/js/musicxml-examples.js", "musicxml-grooves": "src/js/musicxml-grooves.js" }, @@ -30,7 +31,7 @@ "develop": "nodemon -e js,json src/js/server.js", "start": "node src/js/server.js", "test:xsl": "test/libs/bats/bin/bats test/*.bats", - "test:js": "cross-env PORT=1331 NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest", + "test:js": "cross-env PORT=1331 NODE_OPTIONS=--experimental-vm-modules NODE_ENV=test jest -t ${TEST:-''}", "test": "npm run test:xsl && npm run test:js", "server": "PORT=8085 npm run start", "postinstall": "./postinstall.sh" @@ -51,6 +52,7 @@ "cors": "^2.8.5", "express": "^4.21.1", "express-fileupload": "^1.5.1", + "get-stdin": "^9.0.0", "morgan": "^1.10.0", "node-fetch": "^3.3.2", "node-jq": "^6.0.1", diff --git a/src/js/midi-file.js b/src/js/midi-file.js new file mode 100755 index 00000000..1c30298b --- /dev/null +++ b/src/js/midi-file.js @@ -0,0 +1,24 @@ +#!/usr/bin/env node + +/** + * Parse a MIDI file into JSON and back. + * + * Usage: midi-file.js < source.mid | jq [..] | midi-file.js > target.mid + */ + +import { parseMidi, writeMidi } from 'midi-file' +import fs from 'fs' +import process from 'process' +import { buffer } from 'stream/consumers' + +const input = await buffer(process.stdin) +try { + const midi = JSON.parse(input) + const buffer = Buffer.from(writeMidi(midi)) + fs.writeFileSync(1, buffer) +} +catch { + const midi = parseMidi(input) + const buffer = Buffer.from(JSON.stringify(midi)) + process.stdout.write(buffer) +} diff --git a/test/midi-file.test.js b/test/midi-file.test.js new file mode 100644 index 00000000..ae7dbdc9 --- /dev/null +++ b/test/midi-file.test.js @@ -0,0 +1,18 @@ +import util from 'util' +import { createRequire } from 'module' +import fs from 'fs' +const require = createRequire(import.meta.url) +const exec = util.promisify(require('child_process').exec) + +describe('midi-file', () => { + test('should convert incoming MIDI files to JSON', async () => { + const execResult = await exec('node src/js/midi-file.js < test/data/midi-timemap.test.mid') + const json = JSON.parse(execResult.stdout) + expect(json.header.numTracks).toEqual(3) + }) + test('should convert incoming JSON to MIDI files', async () => { + const execResult = await exec('cat test/data/midi-timemap.test.mid | node src/js/midi-file.js | node src/js/midi-file.js', { encoding: 'buffer' }) + const original = fs.readFileSync('test/data/midi-timemap.test.mid') + expect(execResult.stdout).toEqual(original) + }) +}) diff --git a/test/midi-timemap.test.js b/test/midi-timemap.test.js index c2c66644..fae7ac2b 100644 --- a/test/midi-timemap.test.js +++ b/test/midi-timemap.test.js @@ -3,7 +3,7 @@ import { createRequire } from 'module' const require = createRequire(import.meta.url) const exec = util.promisify(require('child_process').exec) -describe('MIDI to timemap conversion cli', () => { +describe('midi-timemap', () => { test('should run successfully', async () => { const execResult = await exec('node src/js/midi-timemap.js test/data/midi-timemap.test.mid') const timemap = JSON.parse(execResult.stdout);