Files
pi-agent bac1bd5686 feat: add 90s-midi-composer skill and Neon Dreams demo
- SKILL.md: complete reference for creating 90s-style MIDI with Python/mido
- drum_reference.md: full GM drum kit note mapping
- compose_neon_dreams.py: 1.5 min A minor dance track at 128 BPM
- neon_dreams.mid: compiled 6-track MIDI (drums, pads, bass, synth lead, piano)
2026-04-17 17:42:20 +00:00

169 lines
5.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: 90s-midi-composer
description: Create authentic 90s-style MIDI music files with General MIDI/SC-55 aesthetic. Use when creating MIDI music, composing dance/electronic tracks, or making 90s retro-sounding songs.
license: MIT
compatibility: Linux with Python3, mido library
---
# 90s MIDI Composer
Create authentic early-to-mid 90s-style MIDI music using Linux tools and Python + mido.
## Prerequisites
Install required tools (one-time setup, requires sudo):
```bash
sudo apt-get install -y fluidsynth fluid-soundfont-gm
pip3 install --break-system-packages mido
```
## The Tool: Python + Mido
Mido is a Python library for working with MIDI files. Key classes:
- `MidiFile(ticks_per_beat=480)` — MIDI file container
- `MidiTrack()` — list of Message objects (one per instrument)
- `Message('note_on', note=N, velocity=V, channel=C, time=T)` — start a note
- `Message('note_off', note=N, velocity=V, channel=C, time=T)` — stop a note
- `Message('program_change', program=N, channel=C, time=T)` — change instrument
- `Message('control_change', control=N, value=V, channel=C, time=T)` — CC control
- `MetaMessage('set_tempo', tempo=μs_per_beat, time=0)` — set tempo (μs/beat)
## Timing in Mido
Mido uses **relative** timing. Each message has `time` = ticks since the previous one.
```
ticks_per_beat = 480 (PPQ)
1 sixteenth-note = 120 ticks
1 eighth-note = 240 ticks
1 quarter-note = 480 ticks (1 beat)
1 bar (4/4) = 1920 ticks (4 beats × 480)
```
### Absolute → Relative Conversion
Build events with absolute tick offsets, then convert:
```python
def rel(items): # items = [(abs_tick, Message), ...]
items.sort(key=lambda x: x[0])
out, prev = [], 0
for at, msg in items:
msg.time = at - prev
out.append(msg)
prev = at
return out
```
## Creating a Basic MIDI File
```python
from mido import MidiFile, MidiTrack, Message as M, MetaMessage
PPQ = 480 # pulses per quarter note (ticks/beat)
BPM = 128
TEMPO = 60_000_000 // BPM
mid = MidiFile(ticks_per_beat=PPQ)
# Track 0: Tempo
trk = MidiTrack()
trk.append(MetaMessage('set_tempo', tempo=TEMPO, time=0))
mid.tracks.append(trk)
# Track 1: Drums (channel 10 = 9)
drums = MidiTrack()
# ... add drum messages ...
mid.tracks.append(drums)
mid.save('output.mid')
```
## 90s Aesthetic Guide
### Essential GM Patches (General MIDI)
| Patch | Name | 90s Role |
|-------|-------------------|-----------------------|
| 1 | Acoustic Grand | Piano / melodic |
| 37 | Synth Bass 1 | Driving synth bass |
| 59 | Brass Section | Hits, accents |
| 80 | Lead 1 Synth | **Arpeggio lead** |
| 88 | Pad 4 (Choir) | Warm atmospheric pads |
| 49 | Strings | Harmonic texture |
| 16 | Electric Piano 1 | Rhodes-style |
### GM Drum Set (Channel 10 = channel 9)
| Note | Voice | Note | Voice |
|------|--------------------|------|--------------------|
| 36 | **Kick 1** | 38 | **Snare 1** |
| 42 | **Hi-hat closed** | 46 | Hi-hat open |
| 49 | Crash cymbal | 57 | Tambourine |
| 51 | Ride cymbal | 50 | Low bongo |
### Classic 90s Dance Beat (120-140 BPM)
```
Kick on every quarter note (1, 2, 3, 4)
Snare on beats 2 & 4
Hi-hat on every eighth note
Open hi-hat on off-beats for groove
```
### 90s Chord Progressions
- **Am | F | G | E7** — vi-IV-V-I in C (the quintessential 90s pop progression)
- **C | G | Am | F** — I-V-vi-IV (the "axis of awesome")
- **Em | C | G | D** — vi-IV-I-V (melancholy 90s feel)
- **Am | C | G | E7** — i-III-VII-V (minor-key drama)
### Instrument Patterns
- **Arpeggio lead**: 16th-note chords cycling root-3rd-5th-octave, ascending and descending
- **Walking bass**: Quarter-note chord tone movement, alternating arpeggio directions
- **Pad chords**: 3-voice voicings, hold ~90% of bar for smooth crossfading
- **Piano melody**: Pentatonic or natural minor scale, sparse 4-note phrases per bar
### Effects (Control Changes)
- **Reverb**: CC91, value 40-80 (higher = more spacious hall)
- **Chorus**: CC93, value 50-90
- **Filter**: CC5 or CC74, value 40-127
- **Expression**: CC11, value 100-127
### Song Structure Template
```
Intro (4 bars) — Pads only, dreamy and sparse
Verse (8 bars) — Drums enter, bass + pads + arpeggio (no piano)
Chorus (8 bars) — Full arrangement, piano melody joins
Bridge (4 bars) — Breakdown or key change
Final Chorus (8 bars) — Return, full energy
Outro (8 bars) — Drums simplify, piano arpeggio fade
```
At 128 BPM: 40 bars ≈ 1.25 minutes. Example: 48 bars = 1.5 minutes.
### BPM Recommendations
| Genre | Typical BPM |
|--------------------|-----------|
| 90s slow jam | 80-100 |
| House / dance | 120-130 |
| Trance / techno | 130-150 |
| Drum & bass | 170-180 |
## Playing MIDI Files
```bash
# With fluidsynth
fluidsynth -a alsa /usr/share/sounds/sf2/FluidR3_GM.sf2 output.mid
# On macOS
fluidsynth -a core /path/to/FluidR3_GM.sf2 output.mid
```
## See Also
- Drum reference: `/drum_reference.md` in this skill directory