Skip to content

Instantly share code, notes, and snippets.

@OverlappingElvis
Last active December 23, 2020 22:51
Show Gist options
  • Save OverlappingElvis/09a6d402c2715c12489be5d3808533c8 to your computer and use it in GitHub Desktop.
Save OverlappingElvis/09a6d402c2715c12489be5d3808533c8 to your computer and use it in GitHub Desktop.

Revisions

  1. OverlappingElvis revised this gist Dec 23, 2020. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    // This script is deprecated, use https://github.com/OverlappingElvis/blob-opera-midi instead!

    const fs = require(`fs`)
    const _ = require(`lodash`)
    const { Player } = require(`midi-player-js`)
  2. OverlappingElvis revised this gist Dec 18, 2020. 1 changed file with 11 additions and 4 deletions.
    15 changes: 11 additions & 4 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -80,11 +80,16 @@ save.on(`press`, () => {

    const track = noteEventsOnly[parseInt(trackIndex, 10)]

    return track.map((event) => {
    return track.reduce((memo, event) => {

    if (!event.velocity) {

    return memo
    }

    const timeSeconds = Math.abs((event.tick / MAX_TICKS) * songTime + (Math.random() * 0.025 * _.sample([1, -1])))

    return {
    memo.push({
    timeSeconds: timeSeconds,
    midiPitch: event.noteNumber,
    librettoChunk: {
    @@ -99,8 +104,10 @@ save.on(`press`, () => {
    }
    ]
    }
    }
    })
    })

    return memo
    }, [])
    }).map((track) => {

    return {
  3. OverlappingElvis revised this gist Dec 18, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -9,9 +9,9 @@ const VOWELS = _.range(4)
    const CONSONANTS = _.range(5, 29)
    const VOICES = [`Soprano`, `Mezzo-Soprano`, `Tenor`, `Bass`]

    const getCurrentPhoneme = (time, collection) => {
    const getCurrentPhoneme = (tick, collection) => {

    const prng = new Alea(time)
    const prng = new Alea(tick)

    const tickValue = Math.round(prng() * 10)

    @@ -82,7 +82,7 @@ save.on(`press`, () => {

    return track.map((event) => {

    const timeSeconds = (event.tick / MAX_TICKS) * songTime + (Math.random() * 0.025 * _.sample([1, -1]))
    const timeSeconds = Math.abs((event.tick / MAX_TICKS) * songTime + (Math.random() * 0.025 * _.sample([1, -1])))

    return {
    timeSeconds: timeSeconds,
  4. OverlappingElvis revised this gist Dec 17, 2020. 1 changed file with 2 additions and 4 deletions.
    6 changes: 2 additions & 4 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -9,13 +9,11 @@ const VOWELS = _.range(4)
    const CONSONANTS = _.range(5, 29)
    const VOICES = [`Soprano`, `Mezzo-Soprano`, `Tenor`, `Bass`]

    const SEED = Math.random()

    const getCurrentPhoneme = (time, collection) => {

    const prng = new Alea(time * SEED)
    const prng = new Alea(time)

    const tickValue = Math.floor(prng() * 10)
    const tickValue = Math.round(prng() * 10)

    return collection[tickValue % collection.length]
    }
  5. OverlappingElvis revised this gist Dec 17, 2020. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion index.js
    Original file line number Diff line number Diff line change
    @@ -9,9 +9,11 @@ const VOWELS = _.range(4)
    const CONSONANTS = _.range(5, 29)
    const VOICES = [`Soprano`, `Mezzo-Soprano`, `Tenor`, `Bass`]

    const SEED = Math.random()

    const getCurrentPhoneme = (time, collection) => {

    const prng = new Alea(time)
    const prng = new Alea(time * SEED)

    const tickValue = Math.floor(prng() * 10)

  6. OverlappingElvis revised this gist Dec 17, 2020. 1 changed file with 33 additions and 14 deletions.
    47 changes: 33 additions & 14 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -3,14 +3,31 @@ const _ = require(`lodash`)
    const { Player } = require(`midi-player-js`)
    const blessed = require(`blessed`)
    const contrib = require(`blessed-contrib`)
    const Alea = require(`alea`)

    const VOWELS = _.range(5)
    const CONSONANTS = _.range(15)
    const VOWELS = _.range(4)
    const CONSONANTS = _.range(5, 29)
    const VOICES = [`Soprano`, `Mezzo-Soprano`, `Tenor`, `Bass`]

    const getCurrentPhoneme = (time, collection) => {

    const prng = new Alea(time)

    const tickValue = Math.floor(prng() * 10)

    return collection[tickValue % collection.length]
    }

    const player = new Player()

    player.loadFile(`./test.mid`)
    const inputFile = process.argv[2]

    if (_.isEmpty(inputFile) || _.last(inputFile.split(`.`)) !== `mid`) {

    throw new Error(`Must provide a midi file.`)
    }

    player.loadFile(`./${inputFile}`)

    const songTime = player.getSongTime()

    @@ -32,7 +49,7 @@ const timelines = noteEventsOnly.map((track, index) => {
    }
    })

    const trackAssignments = [``, ``, ``, ``]
    const trackAssignments = [0, 1, 2, 3]

    const screen = blessed.screen()
    const grid = new contrib.grid({
    @@ -41,18 +58,18 @@ const grid = new contrib.grid({
    screen: screen
    })

    const trackList = grid.set(0, 0, 6, 1, contrib.table, {
    const trackList = grid.set(0, 0, 3, 1, contrib.table, {
    keys: true,
    columnWidth: [14, 8]
    })

    const line = grid.set(0, 1, 6, 1, contrib.line, {
    const line = grid.set(0, 1, 3, 1, contrib.line, {
    showLegend: true
    })

    const log = grid.set(6, 0, 1, 2, contrib.log)
    const log = grid.set(4, 0, 1, 1, contrib.log)

    const save = grid.set(6, 1, 1, 2, blessed.button, {
    const save = grid.set(4, 1, 1, 1, blessed.button, {
    mouse: true,
    content: `Export`
    })
    @@ -65,17 +82,19 @@ save.on(`press`, () => {

    return track.map((event) => {

    const timeSeconds = (event.tick / MAX_TICKS) * songTime + (Math.random() * 0.025 * _.sample([1, -1]))

    return {
    timeSeconds: (event.tick / MAX_TICKS) * songTime + (Math.random() * 0.025 * _.sample([1, -1])),
    timeSeconds: timeSeconds,
    midiPitch: event.noteNumber,
    librettoChunk: {
    vowel: {
    name: _.sample(VOWELS),
    name: getCurrentPhoneme(event.tick, VOWELS),
    duration: 0.20000000298023224
    },
    suffix: [
    {
    name: _.sample(CONSONANTS),
    name: getCurrentPhoneme(event.tick, CONSONANTS),
    duration: 0.10000000149011612
    }
    ]
    @@ -100,9 +119,9 @@ save.on(`press`, () => {
    parts: parsedEvents
    }

    fs.writeFile(`test.json`, JSON.stringify(song), () => {
    fs.writeFile(`${inputFile}.json`, JSON.stringify(song), () => {

    log.log(`Wrote song to test.json`)
    log.log(`Wrote song to ${inputFile}.json`)

    screen.render()
    })
    @@ -141,7 +160,7 @@ trackList.rows.on(`select`, function(event) {

    prompt.input(`Set track number`, ``, (err, value) => {

    log.log(`Assigning track ${value} to ${event.index}`)
    log.log(`Assigning track ${value} to ${VOICES[event.index - 2]}`)

    trackAssignments[event.index - 2] = value

  7. OverlappingElvis revised this gist Dec 17, 2020. 1 changed file with 130 additions and 29 deletions.
    159 changes: 130 additions & 29 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -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()
  8. OverlappingElvis revised this gist Dec 16, 2020. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -2,8 +2,8 @@ const fs = require(`fs`)
    const _ = require(`lodash`)
    const { Player } = require(`midi-player-js`)

    const VOWELS = [`a`, `e`, `i`, `o`, `u`]
    const CONSONANTS = [`p`, `g`, `f`, `c`, `qu`, `h`, `j`, `n`, `l`, `m`, `t`, `d`, `ch`, `r`]
    const VOWELS = _.range(5)
    const CONSONANTS = _.range(15)

    const player = new Player()

    @@ -20,7 +20,7 @@ const parsedEvents = allEvents.map((track) => {
    return track.filter(event => event.name === `Note on`).map((event) => {

    return {
    timeSeconds: (event.tick / MAX_TICKS) * songTime,
    timeSeconds: (event.tick / MAX_TICKS) * songTime + (Math.random() * 0.1),
    midiPitch: event.noteNumber,
    librettoChunk: {
    vowel: {
    @@ -51,7 +51,7 @@ const parsedEvents = allEvents.map((track) => {

    const song = {
    theme: 1,
    parts: parsedEvents
    parts: parsedEvents.reverse()
    }

    fs.writeFile(`test.json`, JSON.stringify(song), () => console.log(`done`))
  9. OverlappingElvis revised this gist Dec 15, 2020. 1 changed file with 9 additions and 9 deletions.
    18 changes: 9 additions & 9 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -39,19 +39,19 @@ const parsedEvents = allEvents.map((track) => {
    }).filter(track => !_.isEmpty(track)).map((track) => {

    return {
    notes: track
    notes: track,
    startSuffix: [
    {
    name: _.sample(CONSONANTS),
    duration: 0.10000000149011612
    }
    ]
    }
    })

    const song = {
    theme: `Festive`,
    parts: parsedEvents,
    startSuffix: [
    {
    name: _.sample(CONSONANTS),
    duration: 0.10000000149011612
    }
    ]
    theme: 1,
    parts: parsedEvents
    }

    fs.writeFile(`test.json`, JSON.stringify(song), () => console.log(`done`))
  10. OverlappingElvis created this gist Dec 15, 2020.
    57 changes: 57 additions & 0 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,57 @@
    const fs = require(`fs`)
    const _ = require(`lodash`)
    const { Player } = require(`midi-player-js`)

    const VOWELS = [`a`, `e`, `i`, `o`, `u`]
    const CONSONANTS = [`p`, `g`, `f`, `c`, `qu`, `h`, `j`, `n`, `l`, `m`, `t`, `d`, `ch`, `r`]

    const player = new Player()

    player.loadFile(`./test.mid`)

    const songTime = player.getSongTime()

    const allEvents = player.getEvents()

    const MAX_TICKS = _.last(_.maxBy(allEvents, (events) => _.last(events).tick)).tick

    const parsedEvents = allEvents.map((track) => {

    return track.filter(event => event.name === `Note on`).map((event) => {

    return {
    timeSeconds: (event.tick / MAX_TICKS) * songTime,
    midiPitch: event.noteNumber,
    librettoChunk: {
    vowel: {
    name: _.sample(VOWELS),
    duration: 0.20000000298023224
    },
    suffix: [
    {
    name: _.sample(CONSONANTS),
    duration: 0.10000000149011612
    }
    ]
    }
    }
    })
    }).filter(track => !_.isEmpty(track)).map((track) => {

    return {
    notes: track
    }
    })

    const song = {
    theme: `Festive`,
    parts: parsedEvents,
    startSuffix: [
    {
    name: _.sample(CONSONANTS),
    duration: 0.10000000149011612
    }
    ]
    }

    fs.writeFile(`test.json`, JSON.stringify(song), () => console.log(`done`))