|
|
@@ -1,9 +1,12 @@ |
|
|
const fs = require(`fs`) |
|
|
const _ = require(`lodash`) |
|
|
const { Player } = require(`midi-player-js`) |
|
|
const blessed = require(`blessed`) |
|
|
const contrib = require(`blessed-contrib`) |
|
|
|
|
|
const VOWELS = _.range(5) |
|
|
const CONSONANTS = _.range(15) |
|
|
const VOICES = [`Soprano`, `Mezzo-Soprano`, `Tenor`, `Bass`] |
|
|
|
|
|
const player = new Player() |
|
|
|
|
|
@@ -15,43 +18,141 @@ const allEvents = player.getEvents() |
|
|
|
|
|
const MAX_TICKS = _.last(_.maxBy(allEvents, (events) => _.last(events).tick)).tick |
|
|
|
|
|
const parsedEvents = allEvents.map((track) => { |
|
|
const noteEventsOnly = allEvents.map(track => track.filter(event => event.name === `Note on`)).filter(track => !_.isEmpty(track)) |
|
|
|
|
|
return track.filter(event => event.name === `Note on`).map((event) => { |
|
|
const timelines = noteEventsOnly.map((track, index) => { |
|
|
|
|
|
return { |
|
|
timeSeconds: (event.tick / MAX_TICKS) * songTime + (Math.random() * 0.1), |
|
|
midiPitch: event.noteNumber, |
|
|
librettoChunk: { |
|
|
vowel: { |
|
|
name: _.sample(VOWELS), |
|
|
duration: 0.20000000298023224 |
|
|
}, |
|
|
suffix: [ |
|
|
{ |
|
|
name: _.sample(CONSONANTS), |
|
|
duration: 0.10000000149011612 |
|
|
} |
|
|
] |
|
|
return { |
|
|
title: `Track ${index}`, |
|
|
x: track.map(event => event.tick), |
|
|
y: track.map(event => event.noteNumber), |
|
|
style: { |
|
|
line: _.times(3, () => Math.random() * 255) |
|
|
} |
|
|
} |
|
|
}) |
|
|
|
|
|
const trackAssignments = [``, ``, ``, ``] |
|
|
|
|
|
const screen = blessed.screen() |
|
|
const grid = new contrib.grid({ |
|
|
rows: 8, |
|
|
cols: 2, |
|
|
screen: screen |
|
|
}) |
|
|
|
|
|
const trackList = grid.set(0, 0, 6, 1, contrib.table, { |
|
|
keys: true, |
|
|
columnWidth: [14, 8] |
|
|
}) |
|
|
|
|
|
const line = grid.set(0, 1, 6, 1, contrib.line, { |
|
|
showLegend: true |
|
|
}) |
|
|
|
|
|
const log = grid.set(6, 0, 1, 2, contrib.log) |
|
|
|
|
|
const save = grid.set(6, 1, 1, 2, blessed.button, { |
|
|
mouse: true, |
|
|
content: `Export` |
|
|
}) |
|
|
|
|
|
save.on(`press`, () => { |
|
|
|
|
|
const parsedEvents = trackAssignments.map((trackIndex) => { |
|
|
|
|
|
const track = noteEventsOnly[parseInt(trackIndex, 10)] |
|
|
|
|
|
return track.map((event) => { |
|
|
|
|
|
return { |
|
|
timeSeconds: (event.tick / MAX_TICKS) * songTime + (Math.random() * 0.025 * _.sample([1, -1])), |
|
|
midiPitch: event.noteNumber, |
|
|
librettoChunk: { |
|
|
vowel: { |
|
|
name: _.sample(VOWELS), |
|
|
duration: 0.20000000298023224 |
|
|
}, |
|
|
suffix: [ |
|
|
{ |
|
|
name: _.sample(CONSONANTS), |
|
|
duration: 0.10000000149011612 |
|
|
} |
|
|
] |
|
|
} |
|
|
} |
|
|
}) |
|
|
}).map((track) => { |
|
|
|
|
|
return { |
|
|
notes: track, |
|
|
startSuffix: [ |
|
|
{ |
|
|
name: _.sample(CONSONANTS), |
|
|
duration: 0.10000000149011612 |
|
|
} |
|
|
] |
|
|
} |
|
|
}) |
|
|
}).filter(track => !_.isEmpty(track)).map((track) => { |
|
|
|
|
|
return { |
|
|
notes: track, |
|
|
startSuffix: [ |
|
|
{ |
|
|
name: _.sample(CONSONANTS), |
|
|
duration: 0.10000000149011612 |
|
|
} |
|
|
] |
|
|
const song = { |
|
|
theme: 1, |
|
|
parts: parsedEvents |
|
|
} |
|
|
|
|
|
fs.writeFile(`test.json`, JSON.stringify(song), () => { |
|
|
|
|
|
log.log(`Wrote song to test.json`) |
|
|
|
|
|
screen.render() |
|
|
}) |
|
|
}) |
|
|
|
|
|
line.setData(timelines) |
|
|
|
|
|
screen.key(['escape', 'q', 'C-c'], function(ch, key) { |
|
|
|
|
|
return process.exit(0); |
|
|
}) |
|
|
|
|
|
const song = { |
|
|
theme: 1, |
|
|
parts: parsedEvents.reverse() |
|
|
trackList.focus() |
|
|
|
|
|
const setTracklistData = () => { |
|
|
|
|
|
trackList.setData({ |
|
|
headers: [`Part`, `Track`], |
|
|
data: VOICES.map((val, index) => [val, trackAssignments[index]]) |
|
|
}) |
|
|
} |
|
|
|
|
|
fs.writeFile(`test.json`, JSON.stringify(song), () => console.log(`done`)) |
|
|
setTracklistData() |
|
|
|
|
|
trackList.rows.on(`select`, function(event) { |
|
|
|
|
|
let prompt = blessed.prompt({ |
|
|
left: `center`, |
|
|
top: `center`, |
|
|
height: `shrink`, |
|
|
width: `shrink`, |
|
|
border: `line` |
|
|
}) |
|
|
|
|
|
screen.append(prompt) |
|
|
|
|
|
prompt.input(`Set track number`, ``, (err, value) => { |
|
|
|
|
|
log.log(`Assigning track ${value} to ${event.index}`) |
|
|
|
|
|
trackAssignments[event.index - 2] = value |
|
|
|
|
|
prompt = null |
|
|
|
|
|
setTracklistData() |
|
|
|
|
|
screen.render() |
|
|
}) |
|
|
}) |
|
|
|
|
|
log.log(`Started MIDI to Blob Opera.`) |
|
|
|
|
|
screen.render() |