Skip to content

Commit

Permalink
Also generating scales now
Browse files Browse the repository at this point in the history
  • Loading branch information
Simon Heimler committed Aug 9, 2022
1 parent 5f3a5a2 commit 61ceab0
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 10 deletions.
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# MIDI Chord Pack
# MIDI Chords and Scales Pack

## Description

This projects generates (400+) MIDI chords and publishes it as a [free MIDI chord pack](https://github.com/Fannon/midi-chords/releases).
This projects generates (~400) MIDI chords and (~300) MIDI scales, which can be downloaded as a [free MIDI pack](https://github.com/Fannon/midi-chords/releases).

The goal is to have the MIDI files for quickly building a chord track, e.g. by searching for the chord in the DAW clip browser. When placed in the DAW the MIDI clips have a readable chord title.
A MIDI scale can help with quantizing the notes.

<img src="./assets/bitwig-example.png" width="500" title="Bitwig for a 'chord track' and use of the clip browser" />

Expand All @@ -21,12 +22,14 @@ If you are missing any chords or have other feedback, feel free to reach out via
Downloads can be found in the [releases page](https://github.com/Fannon/midi-chords/releases).

The can be multiple variants, depending on your needs. Currently there are:
* `chords-with-title` (recommended)
* `chords` (recommended)
* Contains a MIDI track title, which some DAWs (e.g. Ableton Live) display in the imported Clip
* The chord title may differ from the filename, as it can be shorter and case sensitive (which does not work well for the file names)
* `chords-without-title`:
* Contains no MIDI track title
* For a DAW (e.g. Reaper) that displays both MIDI title AND filename, this might work better.
* `scales` (recommended)
* `scales-without-title`

## Installation

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "midi-chords-gen",
"version": "0.3.0",
"version": "0.4.0",
"description": "Generates MIDI chords for quickly throwing together chord tracks.",
"main": "src/generateChordPack.mjs",
"homepage": "https://github.com/Fannon/midi-chord-pack",
Expand All @@ -9,7 +9,9 @@
"url": "https://github.com/Fannon/midi-chord-pack.git"
},
"scripts": {
"build": "ts-node src/generateChords.ts",
"build-chords": "ts-node src/generateChords.ts",
"build-scales": "ts-node src/generateScales.ts",
"build": "npm run build-chords && npm run build-scales",
"start": "npm run build",
"test": "echo \"Error: no test specified\" && exit 1"
},
Expand Down
12 changes: 7 additions & 5 deletions src/generateChords.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface ChordInfo {
const noteOctave = 4

/**
* Chord types to generate. Left (key) is title / filename, right (value) is the tonal library alias
* Chord types to generate.
* @see https://github.com/tonaljs/tonal/blob/main/packages/chord-type/data.ts for theoretically supported chord types
*/
const chordTypesVariant1: ChordInfo[] = [
Expand Down Expand Up @@ -51,13 +51,15 @@ const noteOctave = 4
]

fs.ensureDirSync('./dist')
fs.emptyDirSync('./dist')

generateChords('chords', notes, chordTypesVariant1, 4, true)
generateChords('chords-without-title', notes, chordTypesVariant1, 4, false)
generateChords('chords-with-title', notes, chordTypesVariant1, 4, true)

function generateChords(variantName: string, notes: string[], chordTypes: ChordInfo[], octave: number, writeTitle: boolean) {

fs.ensureDirSync(`./dist/${variantName}`)
fs.emptyDirSync(`./dist/${variantName}`)

for (const currentNote of notes) {

for (const chordInfo of chordTypes) {
Expand All @@ -71,7 +73,7 @@ function generateChords(variantName: string, notes: string[], chordTypes: ChordI

console.log(' ')
console.log('---------------------------------------------------------------')
console.log(`${variantName}/${currentNote}${chordFileName}.mid]`, chord.symbol, chord.aliases, chord.notes)
console.log(`${variantName}/${currentNote} ${chordFileName}.mid`, chord.symbol, chord.aliases, chord.notes)
// console.debug(chord)
if (chord.empty) {
console.error(chord)
Expand All @@ -88,7 +90,7 @@ function generateChords(variantName: string, notes: string[], chordTypes: ChordI
track.addEvent(note);

// Write to MIDI file
const filePath = `./dist/${variantName}/${chordFileName}/${currentNote}${chordFileName}.mid`
const filePath = `./dist/${variantName}/${chordFileName}/${currentNote} ${chordFileName}.mid`
const write = new midiWriter.Writer(track);
fs.ensureDirSync(`./dist/${variantName}/${chordFileName}`)
fs.writeFileSync(filePath, write.buildFile())
Expand Down
104 changes: 104 additions & 0 deletions src/generateScales.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { Scale } from "@tonaljs/tonal";
import * as midiWriter from 'midi-writer-js'
import * as fs from "fs-extra"

interface ScaleInfo {
/** scale symbol, as understood by tonal.js */
symbol: string
/** Filename. Falls back to symbol if not given */
fileName?: string
/** MIDI title. Falls back to symbol if not given */
title?: string
}

/**
* Notes that we want to generate scales for
*/
const notes = ['C', 'C#', 'Db', 'D', 'D#', 'Eb', 'E', 'F', 'F#', 'Gb', 'G', 'G#', 'Ab', 'A', 'A#', 'Bb', 'B']

/** Octave to generate the scales for */
const noteOctave = 4

/**
* Scales to generate.
* @see https://github.com/tonaljs/tonal/blob/main/packages/scale-type/data.ts for theoretically supported scale types
*/
const scaleTypesVariant1: ScaleInfo[] = [
// 5-note scales
{ symbol: 'major pentatonic'},
{ symbol: 'minor pentatonic'},

// 6-note scales
{ symbol: 'major blues'},
{ symbol: 'minor blues'},

// 7-note scales
{ symbol: 'major'},
{ symbol: 'minor'},
{ symbol: 'dorian'},
{ symbol: 'phrygian'},
{ symbol: 'lydian'},
{ symbol: 'mixolydian'},
{ symbol: 'locrian'},
{ symbol: 'harmonic minor'},
{ symbol: 'harmonic major'},
{ symbol: 'melodic minor'},

// 8-note scales
{ symbol: 'bebop major'},
{ symbol: 'bebop minor'},

// 12-note scales
{ symbol: 'chromatic'},
]

fs.ensureDirSync('./dist')

generateScales('scales', notes, scaleTypesVariant1, 4, true)
generateScales('scales-without-title', notes, scaleTypesVariant1, 4, false)

function generateScales(variantName: string, notes: string[], scaleTypes: ScaleInfo[], octave: number, writeTitle: boolean) {

fs.ensureDirSync(`./dist/${variantName}`)
fs.emptyDirSync(`./dist/${variantName}`)

for (const currentNote of notes) {

for (const scaleInfo of scaleTypes) {

const scaleSymbol = scaleInfo.symbol
const scaleFileName = scaleInfo.fileName != null ? scaleInfo.fileName : scaleSymbol
const scaleTitle = scaleInfo.title != null ? scaleInfo.title : scaleSymbol

// Generate scale
const scale = Scale.get(`${currentNote}${noteOctave} ${scaleSymbol}`);

console.log(' ')
console.log('---------------------------------------------------------------')
console.log(`${variantName}/${currentNote}${scaleFileName}.mid`, scale.aliases, scale.notes)
// console.debug(scale)
if (scale.empty) {
console.error(scale)
throw new Error(`Could not process scale alias: "${currentNote}${scaleSymbol}"`)
}

// Create MIDI track
const track = new midiWriter.Track()
if (writeTitle) {
track.addTrackName(`${currentNote} ${scaleTitle}`)
}
track.addCopyright(`https://github.com/Fannon/midi-scale-pack`)
const note = new midiWriter.NoteEvent({pitch: scale.notes as midiWriter.Pitch[], duration: '1', velocity: 64 });
track.addEvent(note);

// Write to MIDI file
const filePath = `./dist/${variantName}/${scaleFileName}/${currentNote} ${scaleFileName}.mid`
const write = new midiWriter.Writer(track);
fs.ensureDirSync(`./dist/${variantName}/${scaleFileName}`)
fs.writeFileSync(filePath, write.buildFile())
console.log(`Output: ${filePath}`)
console.log('---------------------------------------------------------------')
}
}

}

0 comments on commit 61ceab0

Please sign in to comment.