midi-vibe
v0.0.20
Published
Typed music theory, tuning, MIDI control, and rhythm primitives for TypeScript.
Maintainers
Readme
midi-vibe
Typed music theory, tuning, MIDI control, and rhythm primitives for TypeScript.
Install
bun add midi-vibeUsage
import {
INTERVAL,
MidiReferenceNote,
createMidiNote,
midiNoteToFrequency,
frequencyHertz,
transpose,
} from "midi-vibe";
const middleC = createMidiNote(MidiReferenceNote.C4);
const eFlat = transpose(middleC, INTERVAL.MinorThird);
const frequency = midiNoteToFrequency(eFlat);
console.log(frequencyHertz(frequency));Generate the example MIDI projects from the repository:
bun run examplesBuild your own Standard MIDI File Type 1 composition:
import {
GeneralMidiProgram,
MidiFileChannel,
MidiFileController,
MidiFileEnvelopeCurve,
MidiFileFormSectionName,
MidiFileKeyMode,
MidiFilePitchClass,
MidiFileVelocity,
createMidiCatalogBuilder,
createMidiCompositionBuilder,
serializeMidiComposition,
} from "midi-vibe";
const composition = createMidiCompositionBuilder({
slug: "first-piece",
title: "First Piece",
description: "A two-bar chord sketch.",
tempoBpm: 96,
timeSignature: { numerator: 4, denominator: 4 },
keySignature: { fifths: 0, mode: MidiFileKeyMode.Major },
length: { bars: 2 },
})
.addFormSection({
name: MidiFileFormSectionName.Intro,
startBar: 0,
barCount: 2,
description: "Opening phrase.",
})
.addTrack({
name: "Piano",
channel: MidiFileChannel.Piano,
program: GeneralMidiProgram.AcousticGrandPiano,
}, (track) => {
track
.addPattern({
repeat: 2,
every: { beats: 2 },
events: [{
kind: "chord",
at: { bar: 0, beat: 0 },
duration: { beats: 1.5 },
notes: [
{ pitchClass: MidiFilePitchClass.C, octave: 4 },
{ pitchClass: MidiFilePitchClass.E, octave: 4 },
{ pitchClass: MidiFilePitchClass.G, octave: 4 },
],
velocity: MidiFileVelocity.Medium,
}],
})
.addEnvelope({
controller: MidiFileController.Expression,
sampleStep: { beats: 1 },
points: [
{ at: { bar: 0 }, value: 72, curve: MidiFileEnvelopeCurve.Step },
{ at: { bar: 1 }, value: 96 },
{ at: { bar: 2 }, value: 64 },
],
});
})
.build();
const midiBytes = serializeMidiComposition(composition);
const catalog = createMidiCatalogBuilder()
.addComposition(composition)
.build();API Areas
- Branded scalar primitives for frequencies, cent offsets, MIDI notes, controllers, PPQN values, ticks, intervals, and pitch classes.
- Equal temperament and just intonation helpers.
- Harmonic, melodic, rhythmic, tuplet, and polyrhythm context builders.
- MIDI control-change envelopes with deterministic step and linear sampling.
- Standard MIDI File Type 1 composition serialization, builder-based catalog generation, track metadata, General MIDI program/drum helpers, and pitch bend events.
- Pitch transforms, MIDI note/frequency conversion, inversion, transposition, and quantization.
For maintainers and agent workflows, see the public API surface map in docs/public-api-map.md before adding or moving root exports.
Project Structure
Source is organized into domain-driven layers. The public package API is unchanged; root modules re-export the new layout.
| Layer | Path | Responsibility |
| --- | --- | --- |
| Domain | src/domain/** | Branded scalars and pure theory models (pitch, harmony, tuning, rhythm, melody, MIDI control) |
| Application | src/application/** | Composition use-case orchestration (form, rhythm, motifs, analysis) |
| Infrastructure | src/infrastructure/** | SMF serialization, parsing, validation, catalog IO, and byte-level MIDI contracts |
| Shared | src/shared/** | Cross-cutting utilities such as timing conversion and rounding |
Compatibility entry points at the repository root still map to those layers:
src/index.ts— main public API for theory, tuning, rhythm, composition helpers, and MIDI utilitiessrc/midi-projects.ts— Standard MIDI File builders, serializers, General MIDI constants, and catalog helperssrc/composition-*.ts,src/time.ts— targeted re-exports for development and tests
Example MIDI generators live under examples/<project>/generate.ts. Generated .mid and catalog.json files are example artifacts, not source modules. Tests live in test/*.test.ts and import through the midi-vibe path alias.
Theory Naming Policy
- Octaves follow the common MIDI convention where
C4is MIDI note 60 andA4is MIDI note 69. MIDI rendering accepts octave-specific pitches only when they resolve to note numbers0..127. - Plan-level pitch names use ASCII spellings: letters
AthroughG, accidentals#,b,##, andbb. Unicode accidentals are not part of the public naming convention. - Enharmonic spelling is preserved in harmonic plans. For example,
C#andDbshare the same chromatic ordinal but keep distinct names until an explicit MIDI rendering boundary such asharmonicPitchToMidiPitch. - Minor modes are explicit:
natural-minor,harmonic-minor, andmelodic-minorare separate plan values. MIDI key-signature meta events can only encode major or minor, so minor variants collapse to MIDI minor only at export boundaries chosen by the caller. - Modal scale constants include
ionian/aeolianaliases, the diatonic modes, melodic-minor derived modes such aslydian-dominantandaltered, and harmonic-minor derived modes such asphrygian-dominant. - Chord labels are caller-authored lead-sheet or analysis labels stored alongside structured root, quality, optional bass, chord tones, and voicing. Roman numerals such as
V/Vare labels, not inferred pitch content. - MIDI key signatures use SMF fifths
-7..7plus modemajororminor. Modal tonal centers and enharmonic spelling that MIDI cannot represent remain plan metadata. - Export intent is explicit:
exact-intentpreserves spelling, Roman numerals, modal context, just-intonation ratios, cent offsets, and controller intent without claiming MIDI realization;midi-realized-approximationrecords nearest MIDI note, residual cents, pitch-bend range assumptions, channel allocation, and validation messages;metadata-only-warningkeeps unsupported meaning as metadata with warnings. - MIDI 1.0 pitch bend is channel state, not per-note tuning. Portable microtonal output requires a known receiver bend range and careful channel allocation; otherwise midi-vibe reports the approximation or preserves the tuning intent as metadata. Future export bridges can carry this same intent model into richer formats such as MusicXML, MIDI Tuning Standard, MIDI 2.0, or DAW package metadata.
- Composition-plan microtonal lanes render to MIDI only when
exportModeormodeismidi-realized-approximation. The renderer emits pitch bend before the nearest MIDI note and centers bend after note-off on the part channel; independent simultaneous bends should be split into separate parts/channels. SetpitchBendRangeCentswhen the receiver is configured, or midi-vibe records the default 200-cent assumption and a validation warning inmetadata.tuningAssumptions.microtonalRealizations.
Strategic Follow-Ons
These differentiators are intentionally documented as post-foundation explorations, not immediate sprint tickets. They should be picked up only after the public API map, composition-domain extraction, structured validation reports, readback fixtures, and example artifact metadata contract are stable enough to make their behavior reviewable.
| Exploration | Foundation dependencies | Notes before pickup | | --- | --- | --- | | Composition diffing | Requires API ownership to identify public comparison surfaces, domain extraction to compare plan primitives before MIDI bytes, validation reports to classify structural mismatches, readback fixtures to prove rendered differences, and artifact metadata to record source seeds, recipes, package versions, and transform chains. | Inputs must be deterministic and outputs must be reviewable as structured summaries, not only byte-level diffs. | | Hidden harmony explorer | Requires API ownership for theory and composition model placement, domain extraction for cycle relationships and Roman-numeral/chord models, validation reports for unsupported or ambiguous analysis, readback coverage when explorations render to MIDI, and metadata for provenance of generated studies. | Cycle-harmony concepts should remain explicit analysis and composition models: cycle type, tonic slot, offset, chord quality, alteration, and resolution intent. Do not encode them as opaque preset magic. | | Constraint-solved arrangement sketches | Requires API ownership for solver-facing builders, domain extraction for forms, rhythms, motifs, harmony, parts, and arrangement constraints, validation reports for unsatisfied constraints, readback fixtures for rendered sketches, and metadata for solver version, seed, constraints, and selected solution. | Solvers must accept deterministic inputs and return reviewable outputs, including accepted constraints, rejected constraints, and chosen tradeoffs. | | Branded timing conversions | Requires API ownership for timing entry points, domain extraction for musical time and tick/PPQN scalars, validation reports for invalid timing boundaries, readback fixtures for tempo/meter-sensitive conversions, and metadata only when conversions affect generated artifacts. | This can be picked up earlier only if sprint planning identifies active timing defects; otherwise it should stay behind the foundation and trust work. |
Development
Install dependencies:
bun installRun tests, type checks, and lint:
bun test
bun run typecheck
bun run checkBuild the publishable package:
bun run buildPreview the npm package contents:
bun run pack:dry-runGenerate the MIDI examples:
bun run examples