๐ต High-performance MIDI to NES ROM compiler featuring an MMC3 Macro-Driven Bytecode Engine, advanced pattern detection, and multiprocessing optimization
Convert MIDI files into playable NES ROMs or use the generated audio data in homebrew NES games and music applications.
Status: Fully operational end-to-end pipeline (MIDI โ playable NES ROM), with a comprehensive passing test suite. Current focus is stabilization on the road to v1.0.0. See HISTORY.md ยท docs/ROADMAP.md ยท MEMORY.md.
One-command MIDI to NES ROM conversion:
python main.py input.mid output.nesThat's it! The compiler handles the entire pipeline automatically:
- Fast MIDI Parsing - Optimized parser with 120x performance improvement
- Intelligent Channel Mapping - Automatic track-to-NES-channel assignment
- Frame Generation - High-accuracy NES audio frame data
- Macro Engine Bytecode Compilation - Advanced MMC3 pattern compression and instrument generation
- NES ROM Compilation - Ready-to-run NES ROM file with optional on-screen debugging
- 120+ times faster than previous versions
- Multi-core processing using all available CPU cores
- Complex, multi-track MIDI files processed in seconds rather than timing out
- Pattern compression achieving up to ~95.86% dedup reduction within
detected patterns (a percentage, not a multiplier -- and scoped to the
patterned subset, not the whole song; see
coverage_ratiofor how much of a song is actually patterned) - Smart sampling for very large files with quality preservation
Run python benchmarks/run_benchmarks.py to reproduce timing numbers for
yourself against the deterministic fixture set in benchmarks/fixtures/
(ships with the repo, so results are reproducible run to run).
- ๐โโ๏ธ High-performance MIDI parsing with optimized algorithms
- ๐ง Intelligent channel mapping with priority system
- ๐ฏ Accurate NES pitch tables with per-channel processing
- ๐ Macro-driven ADSR volume envelopes for pulse and noise channels
- ๐ Multiple duty cycle patterns for rich sound
- ๐๏ธ Advanced MMC3 bytecode engine for massive compression
- ** Multiple export formats** (CA65, FamiTracker; NSF not yet implemented)
- ๐น Hardware Arpeggiation for dynamic polyphonic voice allocation
- ๐ฅ DPCM Sample Support with automatic bank-switching
- ๏ฟฝ Multi-song support with bank switching
- โฑ๏ธ Enhanced tempo handling with accurate timing
- ๐บ On-screen Debug Overlay for real-time ROM diagnostics
- ๏ฟฝ Pattern and loop point support
- ๐ Visual progress bars with real-time speed and completion tracking
- ๐ก๏ธ Graceful fallback for compatibility
# Convert MIDI directly to NES ROM
python main.py song.mid
# Creates: song.nes (ready to play on emulators)
# Generate with on-screen debug overlay
python main.py song.mid --debug
# Specify output filename
python main.py song.mid my_game.nes
# Intelligent voice allocation + arpeggiation for polyphonic MIDI
python main.py --arranger song.mid my_game.nes
# Skip pattern compression for full-fidelity direct export
python main.py --no-patterns song.mid my_game.nes# Step-by-step processing for debugging/customization
python main.py parse input.mid parsed.json
python main.py map parsed.json mapped.json
python main.py frames mapped.json frames.json
python main.py detect-patterns frames.json patterns.json
python main.py export frames.json output.s --format ca65 --patterns patterns.json
python main.py prepare output.s nes_project/# Fast parsing only (for development)
python tracker/parser_fast.py input.mid output.json
# Run performance benchmarks against your own file(s)/directory
python main.py benchmark run path/to/your.mid
# Or, for the deterministic fixture-based benchmark (reproducible, no file needed):
python benchmarks/run_benchmarks.py
# Configuration management
python main.py config init my_config.yaml
python main.py config validate my_config.yaml
# Song bank management
python main.py song add input.mid --bank my_songs.json --name "My Song"
python main.py song list my_songs.json- Python 3.8 or higher
- CC65 toolchain (for NES ROM compilation)
- Required Python packages (install via requirements.txt)
# Clone the repository
git clone https://github.com/matiaszanolli/midi2nes.git
cd midi2nes
# Install dependencies
pip install -r requirements.txt
# Install CC65 toolchain
# macOS:
brew install cc65
# Ubuntu/Debian:
sudo apt-get install cc65
# Windows: Download from https://cc65.github.io/The MIDI2NES compiler automatically detects your system's CPU cores and distributes pattern detection work across multiple processes:
๐ Starting parallel pattern detection with 7 workers
๐ง Created 236 work chunks for parallel processing
Processing pattern chunks: 100%|โโโโโโโโโโโโ| 236/236 [00:12<00:00, 19.2chunk/s, patterns=5505]
๐ Found 5505 candidate patterns
โ
Parallel pattern detection completed in 12.49s
- Single-core: Traditional processing (legacy compatibility)
- Multi-core: Automatic work distribution across all CPU cores
- Large files: Smart sampling maintains quality while ensuring reasonable processing times
- Fallback safety: Graceful degradation if multiprocessing fails
- Minimum: Single-core system (fallback mode)
- Recommended: Multi-core CPU for optimal performance
- Memory: 1GB+ RAM for large MIDI files
- Storage: Minimal disk space for temporary files
The engine uses a sophisticated macro-driven bytecode to compress repeating musical patterns into minimal ROM space:
- Macro Recognition: Condenses volume, pitch, and duty cycle envelopes
- Variation Detection: Handles pitch shifts and volume changes
- MMC3 Bank Switching: Dynamically maps required sequence banks
- DPCM Optimization: Groups percussive hits
๐ Found 19,285 candidate patterns
๐ Selected 23 optimal patterns
๐ฆ Pattern dedup ratio: 95.86% reduction (patterned subset only)
๐ฆ Pattern coverage: 42.0% of events matched a detected pattern
- MIDI Files (.mid, .midi) - Standard MIDI format
- All MIDI Types - Format 0, 1, and 2 supported
- Complex Files - Multi-track, tempo changes, large files
- NES ROM (.nes) - Ready-to-run NES ROM files
- CA65 Assembly (.s) - For integration with NES projects
- NSF Audio (.nsf) - NES Sound Format โ not yet implemented (see #81); use the ROM/CA65 path
- FamiTracker (.txt) - Import into FamiTracker editor
| File Size | Events | Processing Time | Pattern Detection |
|---|---|---|---|
| Small (<1KB) | <100 | <1s | Instant |
| Medium (1-10KB) | 100-1K | 1-5s | <5s |
| Large (10-50KB) | 1K-10K | 5-15s | 5-15s |
| Very Large (50KB+) | 10K+ | 15-30s | Smart sampling |
- Simple melodies: <1 second total processing
- Complex orchestral: 15-30 seconds with high compression
- Game soundtracks: Optimal pattern detection and ROM generation
MIDI2NES includes a comprehensive suite of debugging and analysis tools to help troubleshoot conversion issues and analyze output quality.
# Check audio patterns in generated ROM
python -m debug.audio_checker output.nes
# Analyze pattern compression effectiveness
python -m debug.pattern_analysis output/patterns
# Examine music structure in ROM
python -m debug.music_structure_analyzer output.nes
# Test ROM generation pipeline
python -m debug.rom_tester
# Performance analysis of MIDI parsing
python -m debug.performance_analyzeraudio_checker: APU pattern validation in NES ROMspattern_analysis: Pattern detection result analysisca65_inspector: Assembly output inspectionframe_analyzer: Frame generation debuggingmusic_structure_analyzer: Comprehensive ROM music analysispattern_reference_debugger: Pattern reference table analysisperformance_analyzer: MIDI parser performance testingrom_tester: Complete pipeline validation
from debug import (
check_audio_simple,
analyze_patterns,
test_rom_generation
)
# Validate ROM audio patterns
check_audio_simple("output.nes")
# Run full test suite
success = test_rom_generation()For detailed debug tool documentation, see debug/README.md.
๐ midi2nes/
โโโ ๐ต tracker/ # Core MIDI processing
โ โโโ parser_fast.py # Optimized MIDI parser (120x)
โ โโโ pattern_detector_parallel.py # Multi-core pattern detection
โ โโโ track_mapper.py # NES channel assignment (legacy mode)
โ โโโ tempo_map.py # Advanced tempo handling
โโโ ๐น arranger/ # Intelligent arrangement (--arranger mode)
โ โโโ role_analyzer.py # Bass/melody/harmony detection
โ โโโ voice_allocator.py # Smart allocation + arpeggiation
โ โโโ gm_instruments.py # GM program โ NES channel/duty
โโโ ๐ฎ nes/ # NES components (frames, project builder, pitch)
โโโ ๐บ๏ธ mappers/ # NROM / MMC1 / MMC3 + auto-select factory
โโโ ๐ค exporter/ # CA65 macro engine, NSF, FamiStudio
โโโ ๐ฅ dpcm_sampler/ # Drum mapping + DPCM sample packing
โโโ ๐๏ธ compiler/ # CC65 wrapper + ROM compile/validate
โโโ ๐ง config/ # YAML configuration management
โโโ ๐ debug/ # ROM diagnostics + analysis tools
โโโ ๐ benchmarks/ # Performance testing
- Fork the repository
- Create a feature branch
- Run the test suite:
python -m pytest - Submit a pull request
# Run all tests
python -m pytest
# Run with coverage
python -m pytest --cov=.
# Run a single test file
python -m pytest tests/test_main.py
# Test the full ROM generation pipeline
python -m debug.rom_testerThis project is licensed under the MIT License - see the LICENSE file for details.
- NES development community for technical documentation
- CC65 development team for the excellent toolchain
- Contributors and testers who helped optimize performance