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)
This commit is contained in:
@@ -1,3 +1,20 @@
|
|||||||
# pi-midi-zone
|
# pi-midi-zone
|
||||||
A place for pi to create music!
|
|
||||||
|
|
||||||
|
A place for Pi to create music!
|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
### Skills
|
||||||
|
|
||||||
|
- **skills/90s-midi-composer/** — Skill for creating authentic 90s-style MIDI music files using Python + mido. Generates General MIDI/SC-55 aesthetic tracks with classic dance beats, synth leads, pads, bass, and piano.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
- **examples/compose_neon_dreams.py** — Python script that composes a 90s dance track ("Neon Dreams"), 48 bars / 1.5 min / 128 BPM in A minor.
|
||||||
|
- **examples/neon_dreams.mid** — Completed 6-track MIDI file. Uses fluidsynth to listen: `fluidsynth /path/to/FluidR3_GM.sf2 examples/neon_dreams.mid`
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. Install: `pip3 install mido`, `sudo apt install fluidsynth fluid-soundfont-gm`
|
||||||
|
2. Run: `python3 examples/compose_neon_dreams.py`
|
||||||
|
3. Listen: `fluidsynth /usr/share/sounds/sf2/FluidR3_GM.sf2 neon_dreams.mid`
|
||||||
|
|||||||
@@ -0,0 +1,310 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
NEON DREAMS — 90s-Style Dance Track
|
||||||
|
Key: A minor, Tempo: 128 BPM, 48 bars (~1:50 duration)
|
||||||
|
|
||||||
|
Structure:
|
||||||
|
0–3 : Intro (pads only)
|
||||||
|
4–11 : Verse (drums, bass, pads, arpeggio)
|
||||||
|
12–19 : Chorus (all instruments, piano melody added)
|
||||||
|
20–31 : Outro (drums simplify, piano + arpeggio only)
|
||||||
|
|
||||||
|
Instruments:
|
||||||
|
Drums (Ch10) — four-on-the-floor dance beat
|
||||||
|
Synth Lead (Ch1) — sawtooth arpeggios
|
||||||
|
Pads (Ch2) — warm New Age pad
|
||||||
|
Bass (Ch3) — synth bass walking roots
|
||||||
|
Piano (Ch4) — pentatonic melody
|
||||||
|
"""
|
||||||
|
|
||||||
|
from mido import MidiFile, MidiTrack, Message as M, MetaMessage
|
||||||
|
|
||||||
|
PPQ = 480 # ticks per quarter note
|
||||||
|
BPM = 128
|
||||||
|
TEMPO = 60_000_000 // BPM
|
||||||
|
|
||||||
|
def t(x):
|
||||||
|
"""Beat-time (fractional) to ticks."""
|
||||||
|
return int(x * PPQ)
|
||||||
|
|
||||||
|
# Am | F | G | E7
|
||||||
|
CHORD = [
|
||||||
|
[45, 60, 64], # Am
|
||||||
|
[41, 45, 60], # F
|
||||||
|
[43, 46, 50], # G
|
||||||
|
[40, 47, 52], # E7
|
||||||
|
]
|
||||||
|
|
||||||
|
def rel(items):
|
||||||
|
"""Absolute timestamps → relative Mido messages."""
|
||||||
|
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
|
||||||
|
|
||||||
|
# === DRUM PATTERNS ===
|
||||||
|
_K, _S, _HH, _HHO = 36, 38, 42, 46 # GM drum codes
|
||||||
|
|
||||||
|
def drum_events(total_bars=48):
|
||||||
|
events = []
|
||||||
|
|
||||||
|
def kick(tick):
|
||||||
|
events.append((tick, M('note_on', note=_K, velocity=110, channel=9)))
|
||||||
|
events.append((tick + t(0.5), M('note_off', note=_K, velocity=50, channel=9)))
|
||||||
|
|
||||||
|
def kick_soft(tick):
|
||||||
|
events.append((tick, M('note_on', note=_K, velocity=90, channel=9)))
|
||||||
|
events.append((tick + t(0.35), M('note_off', note=_K, velocity=40, channel=9)))
|
||||||
|
|
||||||
|
def snare(tick):
|
||||||
|
events.append((tick, M('note_on', note=_S, velocity=125, channel=9)))
|
||||||
|
events.append((tick + t(0.25), M('note_off', note=_S, velocity=60, channel=9)))
|
||||||
|
|
||||||
|
def hh(v=70):
|
||||||
|
events.append((v // 10 * 10, M('note_on', note=_HH, velocity=v, channel=9))) # hack, let me redo
|
||||||
|
events.append((v // 10 * 10, M('note_off', note=_HH, velocity=30, channel=9)))
|
||||||
|
|
||||||
|
# Actually, let me do this differently:
|
||||||
|
# I'll add events directly with tick offsets
|
||||||
|
|
||||||
|
for bar in range(total_bars):
|
||||||
|
base = bar * t(4.0)
|
||||||
|
# Determine bar type based on section
|
||||||
|
if bar < 4:
|
||||||
|
# Intro: no drums
|
||||||
|
continue
|
||||||
|
elif bar < 40:
|
||||||
|
# Normal dance beat (standard)
|
||||||
|
# Beat 1: Kick
|
||||||
|
kick(base)
|
||||||
|
events.append((base + t(0.5), M('note_on', note=_HH, velocity=65, channel=9)))
|
||||||
|
events.append((base + t(0.5), M('note_off', note=_HH, velocity=35, channel=9)))
|
||||||
|
|
||||||
|
# Beat 2: Kick + Snare + HH
|
||||||
|
kick(base + t(1.0))
|
||||||
|
snare(base + t(1.0))
|
||||||
|
events.append((base + t(1.5), M('note_on', note=_HH, velocity=60, channel=9)))
|
||||||
|
events.append((base + t(1.5), M('note_off', note=_HH, velocity=35, channel=9)))
|
||||||
|
|
||||||
|
# Beat 3: Kick + HH
|
||||||
|
kick(base + t(2.0))
|
||||||
|
events.append((base + t(2.5), M('note_on', note=_HH, velocity=60, channel=9)))
|
||||||
|
events.append((base + t(2.5), M('note_off', note=_HH, velocity=35, channel=9)))
|
||||||
|
|
||||||
|
# Beat 4: Kick + Snare + HH + Open HH
|
||||||
|
kick(base + t(3.0))
|
||||||
|
snare(base + t(3.0))
|
||||||
|
events.append((base + t(3.3), M('note_on', note=_HH, velocity=80, channel=9)))
|
||||||
|
events.append((base + t(3.3), M('note_off', note=_HH, velocity=35, channel=9)))
|
||||||
|
events.append((base + t(3.3), M('note_on', note=_HHO, velocity=75, channel=9)))
|
||||||
|
events.append((base + t(3.9), M('note_off', note=_HHO, velocity=35, channel=9)))
|
||||||
|
|
||||||
|
# HH fill at end of bar
|
||||||
|
events.append((base + t(3.8), M('note_on', note=_HH, velocity=55, channel=9)))
|
||||||
|
events.append((base + t(3.8), M('note_off', note=_HH, velocity=35, channel=9)))
|
||||||
|
|
||||||
|
# Extra kick at bar 6 (dance build-up)
|
||||||
|
if bar % 8 == 6 and bar < 40:
|
||||||
|
kick(base + t(2.5))
|
||||||
|
else:
|
||||||
|
# Outro: simplified, softer drums
|
||||||
|
events.append((base + t(0), M('note_on', note=_K, velocity=85, channel=9)))
|
||||||
|
events.append((base + t(0.5), M('note_off', note=_K, velocity=40, channel=9)))
|
||||||
|
events.append((base + t(1.0), M('note_on', note=_K, velocity=85, channel=9)))
|
||||||
|
events.append((base + t(1.5), M('note_off', note=_K, velocity=40, channel=9)))
|
||||||
|
events.append((base + t(2.0), M('note_on', note=_K, velocity=85, channel=9)))
|
||||||
|
events.append((base + t(2.5), M('note_off', note=_K, velocity=40, channel=9)))
|
||||||
|
events.append((base + t(3.0), M('note_on', note=_K, velocity=85, channel=9)))
|
||||||
|
events.append((base + t(3.5), M('note_off', note=_K, velocity=40, channel=9)))
|
||||||
|
events.append((base + t(3.8), M('note_on', note=_HH, velocity=50, channel=9)))
|
||||||
|
events.append((base + t(3.8), M('note_off', note=_HH, velocity=30, channel=9)))
|
||||||
|
|
||||||
|
return rel(events)
|
||||||
|
|
||||||
|
|
||||||
|
# === PADS ===
|
||||||
|
def pad_events(total_bars=48):
|
||||||
|
events = [
|
||||||
|
(0, M('program_change', program=88, channel=2)),
|
||||||
|
(0, M('control_change', control=11, value=127, channel=2)),
|
||||||
|
(0, M('control_change', control=91, value=45, channel=2)),
|
||||||
|
(0, M('control_change', control=93, value=55, channel=2)),
|
||||||
|
]
|
||||||
|
|
||||||
|
for bar in range(total_bars):
|
||||||
|
chord = CHORD[bar % 4]
|
||||||
|
on_tick = bar * t(4.0)
|
||||||
|
off_tick = on_tick + t(3.7)
|
||||||
|
# Crossfade: next chord starts 0.3 beats before this one ends
|
||||||
|
for note in chord:
|
||||||
|
events.append((on_tick, M('note_on', note=note, velocity=55, channel=2)))
|
||||||
|
events.append((off_tick, M('note_off', note=note, velocity=25, channel=2)))
|
||||||
|
|
||||||
|
return rel(events)
|
||||||
|
|
||||||
|
|
||||||
|
# === BASS ===
|
||||||
|
def bass_events(total_bars=48):
|
||||||
|
events = [
|
||||||
|
(0, M('program_change', program=37, channel=3)),
|
||||||
|
]
|
||||||
|
|
||||||
|
for bar in range(total_bars):
|
||||||
|
root, third, fifth = CHORD[bar % 4]
|
||||||
|
base = bar * t(4.0)
|
||||||
|
|
||||||
|
# Section-aware patterns
|
||||||
|
if bar < 4:
|
||||||
|
# Intro: no bass
|
||||||
|
continue
|
||||||
|
elif bar < 16 or bar % 4 == 0:
|
||||||
|
notes = [root, root + 9, root + 16, (root + 9)] # pentatonic walk
|
||||||
|
elif bar % 4 == 2:
|
||||||
|
notes = [root, root + 2, root + 5, root + 7] # chromatic climb
|
||||||
|
else:
|
||||||
|
notes = [root, root + 4, root + 7, root + 12] # triad arpeggio
|
||||||
|
|
||||||
|
for i, note in enumerate(notes):
|
||||||
|
on = base + t(1.0 * i)
|
||||||
|
off = base + t(1.0 + 0.01 * i)
|
||||||
|
events.append((on, M('note_on', note=note, velocity=92, channel=3)))
|
||||||
|
events.append((off, M('note_off', note=note, velocity=30, channel=3)))
|
||||||
|
|
||||||
|
return rel(events)
|
||||||
|
|
||||||
|
|
||||||
|
# === SYNTH LEAD ARPEGGIOS ===
|
||||||
|
def lead_events(total_bars=48):
|
||||||
|
events = [
|
||||||
|
(0, M('program_change', program=80, channel=1)),
|
||||||
|
(0, M('control_change', control=11, value=100, channel=1)),
|
||||||
|
(0, M('control_change', control=91, value=80, channel=1)),
|
||||||
|
(0, M('control_change', control=93, value=70, channel=1)),
|
||||||
|
(0, M('control_change', control=5, value=60, channel=1)), # filter cutoff
|
||||||
|
]
|
||||||
|
|
||||||
|
for bar in range(total_bars):
|
||||||
|
root, third, fifth = CHORD[bar % 4]
|
||||||
|
base = bar * t(4.0)
|
||||||
|
|
||||||
|
if bar < 4:
|
||||||
|
# Intro: sparse arpeggio (every 2nd bar)
|
||||||
|
if bar % 2 == 1:
|
||||||
|
continue
|
||||||
|
arp = [root + 12, root + 19, root + 24]
|
||||||
|
for i, note in enumerate(arp):
|
||||||
|
on = base + t(1.0 * i)
|
||||||
|
off = base + t(3.0 * i) + t(2.9)
|
||||||
|
events.append((on, M('note_on', note=note, velocity=55, channel=1)))
|
||||||
|
events.append((off, M('note_off', note=note, velocity=30, channel=1)))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Octave-shifted arpeggio
|
||||||
|
u3 = third + 12
|
||||||
|
u5 = fifth + 12
|
||||||
|
ur = root + 12
|
||||||
|
|
||||||
|
if bar < 40:
|
||||||
|
# Normal: 8 notes per bar (ascending-descending octaves)
|
||||||
|
arp = [ur, u3, u5, ur, u5, u3, ur, fifth]
|
||||||
|
else:
|
||||||
|
# Outro: sparse
|
||||||
|
arp = [ur, fifth, ur, u3]
|
||||||
|
|
||||||
|
for i, note in enumerate(arp):
|
||||||
|
on = base + t(0.25 * i)
|
||||||
|
off = base + t(0.25 * i) + t(0.2)
|
||||||
|
events.append((on, M('note_on', note=note, velocity=75, channel=1)))
|
||||||
|
events.append((off, M('note_off', note=note, velocity=40, channel=1)))
|
||||||
|
|
||||||
|
return rel(events)
|
||||||
|
|
||||||
|
|
||||||
|
# === PIANO MELODY ===
|
||||||
|
def piano_events(total_bars=48):
|
||||||
|
events = [
|
||||||
|
(0, M('program_change', program=1, channel=4)),
|
||||||
|
]
|
||||||
|
|
||||||
|
shapes = [
|
||||||
|
lambda p: [p[2], p[4], p[0], p[2]],
|
||||||
|
lambda p: [p[3], p[0], p[1], p[3]],
|
||||||
|
lambda p: [p[4], p[2], p[1], p[4]],
|
||||||
|
lambda p: [p[0], p[2], p[3], p[0]],
|
||||||
|
]
|
||||||
|
|
||||||
|
for bar in range(total_bars):
|
||||||
|
root = CHORD[bar % 4][0]
|
||||||
|
base = bar * t(4.0)
|
||||||
|
|
||||||
|
if bar < 12:
|
||||||
|
# Chorus starts at bar 12
|
||||||
|
continue
|
||||||
|
elif bar >= 47:
|
||||||
|
break
|
||||||
|
|
||||||
|
pent = [root + i for i in [0, 3, 5, 7, 10]]
|
||||||
|
mel = shapes[bar % 4](pent)
|
||||||
|
|
||||||
|
for i, note in enumerate(mel):
|
||||||
|
on = base + t(1.0 * i)
|
||||||
|
off = base + t(1.0 * i) + t(0.8)
|
||||||
|
events.append((on, M('note_on', note=note, velocity=85, channel=4)))
|
||||||
|
events.append((off, M('note_off', note=note, velocity=30, channel=4)))
|
||||||
|
|
||||||
|
return rel(events)
|
||||||
|
|
||||||
|
|
||||||
|
# === MAIN ===
|
||||||
|
def main():
|
||||||
|
total_bars = 48
|
||||||
|
mid = MidiFile(ticks_per_beat=PPQ)
|
||||||
|
|
||||||
|
# Track 0: Tempo
|
||||||
|
tempo_trk = MidiTrack()
|
||||||
|
tempo_trk.append(MetaMessage('set_tempo', tempo=TEMPO, time=0))
|
||||||
|
tempo_trk.append(MetaMessage('track_name', 'Tempo', time=0))
|
||||||
|
mid.tracks.append(tempo_trk)
|
||||||
|
|
||||||
|
# Track 1: Drums
|
||||||
|
drums_trk = MidiTrack()
|
||||||
|
drums_trk.append(MetaMessage('set_tempo', tempo=TEMPO, time=0))
|
||||||
|
drums_trk.extend(drum_events(total_bars))
|
||||||
|
mid.tracks.append(drums_trk)
|
||||||
|
|
||||||
|
# Track 2: Pads
|
||||||
|
pads_trk = MidiTrack()
|
||||||
|
pads_trk.extend(pad_events(total_bars))
|
||||||
|
mid.tracks.append(pads_trk)
|
||||||
|
|
||||||
|
# Track 3: Bass
|
||||||
|
bass_trk = MidiTrack()
|
||||||
|
bass_trk.extend(bass_events(total_bars))
|
||||||
|
mid.tracks.append(bass_trk)
|
||||||
|
|
||||||
|
# Track 4: Synth Lead
|
||||||
|
lead_trk = MidiTrack()
|
||||||
|
lead_trk.extend(lead_events(total_bars))
|
||||||
|
mid.tracks.append(lead_trk)
|
||||||
|
|
||||||
|
# Track 5: Piano
|
||||||
|
piano_trk = MidiTrack()
|
||||||
|
piano_trk.extend(piano_events(total_bars))
|
||||||
|
mid.tracks.append(piano_trk)
|
||||||
|
|
||||||
|
outfile = 'neon_dreams.mid'
|
||||||
|
mid.save(outfile)
|
||||||
|
|
||||||
|
total_ticks = sum(m.time for trk in mid.tracks for m in trk)
|
||||||
|
print(f"Saved {outfile} — {len(mid.tracks)} tracks")
|
||||||
|
for i, trk in enumerate(mid.tracks):
|
||||||
|
tt = sum(m.time for m in trk)
|
||||||
|
name = trk.name if hasattr(trk, 'name') and trk.name else f'Track {i}'
|
||||||
|
print(f" Track {i} ({name}): {tt} ticks = {tt/480/4:.0f} bars = {tt/480/128*60/60:.1f}s")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Binary file not shown.
@@ -0,0 +1,168 @@
|
|||||||
|
---
|
||||||
|
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
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
# GM Drum Kit Reference (Channel 10 = channel=9)
|
||||||
|
|
||||||
|
## Standard GM Drum Mapping
|
||||||
|
| Note | Voice | Note | Voice | Note | Voice | Note | Voice |
|
||||||
|
|------|---------------|------|--------------|------|----------------|------|------------|
|
||||||
|
| 35 | Low conga | 36 | **Kick 1** | 37 | High conga | 38 | **Snare 1** |
|
||||||
|
| 39 | Low tom | 40 | Crash cymbal | 41 | High tom | 42 | **Hi-hat Cl** |
|
||||||
|
| 43 | Ride cymbal 1 | 44 | Closed hi-hat | 45 | Open hi-hat | 46 | **Open Hi-hat** |
|
||||||
|
| 47 | Pedal hi-hat | 48 | Low tom | 49 | Crash cymbal | 50 | Low bongo |
|
||||||
|
| 51 | Ride cymbal 2| 52 | High bongo | 53 | Low-mid conga | 54 | Mid conga |
|
||||||
|
| 55 | High conga | 56 | Low-mid timbale | 57 | **Tambourine** | 58 | High timbale |
|
||||||
|
| 59 | Low agogo | 60 | High agogo | 61 | Cabasa | 62 | Maracas |
|
||||||
|
| 63 | Short whistle| 64 | Long whistle | 65 | Short guiro | 66 | Long guiro |
|
||||||
|
| 67 | Claves | 68 | Hi wood block | 69 | Low wood block | 70 | Mud claps / Cabasa |
|
||||||
|
| 71 | Hi snap | 72 | Maraca | 73 | Short shaker | 74 | Long shaker |
|
||||||
|
| 75 | Guiro | 76 | Cabasa | 77 | Whisper | 78 | Stroke hit / scrape |
|
||||||
|
| 79 | Stroke scrape | 80 | Slap | 81 | Scratch push | 82 | Slap |
|
||||||
|
| 83 | Scratch pull | 84 | Sticks | 85 | Square click | 86 | Metronome |
|
||||||
|
| 87 | Drum roll | 88 | Kick pedal | 89 | Kick pedal | 90 | Snare |
|
||||||
|
| 91 | Snare str. | 92 | Low tom | 93 | Snare str. | 94 | Rim shot |
|
||||||
|
| 95 | Open hh | 96 | Open hh | 97 | Low tom | 98 | Closed hh |
|
||||||
|
| 99 | Low-mid tom| 100 | Hi tom | 101 | Crash cym. | 102 | Mid tom |
|
||||||
|
| 103 | Closed hh| 104 | Ride cym| 105 | Kick 2 | 106 | Snare 2 |
|
||||||
|
| 107 | Timbale | 108 | Agogo | 109 | Steel drum | 110 | Bongos |
|
||||||
|
| 111 | congas | 112 | Bell | | | | |
|
||||||
|
|
||||||
|
## Classic Drum Patterns
|
||||||
|
|
||||||
|
### 128 BPM Dance (4/4)
|
||||||
|
```
|
||||||
|
Bar 1, 2, 3: Standard dance beat
|
||||||
|
Bar 4: Add fills
|
||||||
|
|
||||||
|
Kick: X . . . X . . . (on 1, 5)
|
||||||
|
Snare: . . . . X . . . (on 5)
|
||||||
|
HH closed:. . . . . . . . . (constant 8ths)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 120 BPM Half-time
|
||||||
|
```
|
||||||
|
Kick: X . . . . . . . (on 1 only)
|
||||||
|
Snare: . . . . X . . . (on 5)
|
||||||
|
HH closed:. X . . . X . . (subdued, syncopated)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 140 BPM Breakbeat-tinged
|
||||||
|
```
|
||||||
|
Kick: X . . X . . . . (16th feel)
|
||||||
|
Snare: . . . . X . . X (with accent on 9)
|
||||||
|
HH closed: (16th notes, accented)
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user