Skip to content
matiaszanolliPublic

About

Convert MIDI files to NES assembly code.

Topics

Resources

Stars

7 stars

Watchers

2 watching

Forks

Latest commit

ย 

History

527 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

MIDI to NES Compiler

๐ŸŽต 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.

Performance CPU Patterns Version Tests

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.

โšก Quick Start

One-command MIDI to NES ROM conversion:

python main.py input.mid output.nes

That's it! The compiler handles the entire pipeline automatically:

  1. Fast MIDI Parsing - Optimized parser with 120x performance improvement
  2. Intelligent Channel Mapping - Automatic track-to-NES-channel assignment
  3. Frame Generation - High-accuracy NES audio frame data
  4. Macro Engine Bytecode Compilation - Advanced MMC3 pattern compression and instrument generation
  5. NES ROM Compilation - Ready-to-run NES ROM file with optional on-screen debugging

๐Ÿš€ Performance

Real-World Performance Results

  • 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_ratio for 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).

๐Ÿ“‹ Features

Core Features

  • ๐Ÿƒโ€โ™‚๏ธ 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)

Advanced Features

  • ๐ŸŽน 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

๐ŸŽฎ Usage Examples

Simple ROM Generation

# 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

Advanced Pipeline Control

# 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/

Development and Testing

# 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

๐Ÿ”ง Installation

Prerequisites

  • Python 3.8 or higher
  • CC65 toolchain (for NES ROM compilation)
  • Required Python packages (install via requirements.txt)

Quick Install

# 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/

๐Ÿ–ฅ๏ธ Multiprocessing

How It Works

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

Performance Scaling

  • 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

System Requirements

  • 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

๐ŸŽฏ Pattern Detection

Advanced MMC3 Macro Compression

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

Example Output

๐Ÿ“ˆ 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

๐ŸŽผ Supported Formats

Input Formats

  • MIDI Files (.mid, .midi) - Standard MIDI format
  • All MIDI Types - Format 0, 1, and 2 supported
  • Complex Files - Multi-track, tempo changes, large files

Output Formats

  • 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

๐Ÿ“Š Performance Benchmarks

File Size Performance

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

Real-World Examples

  • Simple melodies: <1 second total processing
  • Complex orchestral: 15-30 seconds with high compression
  • Game soundtracks: Optimal pattern detection and ROM generation

๐Ÿ› Debug Tools

MIDI2NES includes a comprehensive suite of debugging and analysis tools to help troubleshoot conversion issues and analyze output quality.

Quick Debug Commands

# 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_analyzer

Available Debug Tools

  • audio_checker: APU pattern validation in NES ROMs
  • pattern_analysis: Pattern detection result analysis
  • ca65_inspector: Assembly output inspection
  • frame_analyzer: Frame generation debugging
  • music_structure_analyzer: Comprehensive ROM music analysis
  • pattern_reference_debugger: Pattern reference table analysis
  • performance_analyzer: MIDI parser performance testing
  • rom_tester: Complete pipeline validation

Programmatic Usage

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.

๐Ÿ› ๏ธ Development

Architecture

๐Ÿ“ 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

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Run the test suite: python -m pytest
  4. Submit a pull request

Testing

# 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_tester

๐Ÿ“ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿค Acknowledgments

  • NES development community for technical documentation
  • CC65 development team for the excellent toolchain
  • Contributors and testers who helped optimize performance

๐Ÿ”— Links

About

Convert MIDI files to NES assembly code.

Topics

Resources

Stars

7 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages