Add v2 Game Boy sound engine (isolated from v1)

- Authentic DMG-CPU sound chip implementation:
  - 4 Pulse channels with duty cycle control (12.5%, 25%, 50%, 75%)
  - 2 Wave channels with 4-bit wavetables
  - 2 Noise channels with LFSR (7-bit and 15-bit modes)

- GameBoy Colorizer effect chain:
  - Low-pass filter (natural GB rolloff)
  - Bit-crushing (4-bit DAC simulation)
  - Sample rate reduction
  - Saturation and high-pass filter
  - Presets: DMG, GBC, GBA, Clean

- Intelligent MIDI processing:
  - Track analysis and role detection (bass, lead, drums, etc.)
  - Automatic channel mapping to GB channels
  - Chord arpeggiator for polyphony handling
  - GameBoy Arranger for fuller sound

- BitMidi search integration
- Completely isolated from v1 (no changes to src/)
This commit is contained in:
b1rdmania
2026-01-20 19:36:13 +00:00
parent 63bd71202a
commit 5e127c3a3e
21 changed files with 6789 additions and 0 deletions
+603
View File
@@ -0,0 +1,603 @@
# Wario Synth v2: Phase 2 Implementation Plan
**Project:** Wario Synthesis Engine v2
**Owner:** Andy @ birdmania
**Status:** Implementation Phase
**Target:** Game Boy-authentic MIDI conversion with enhanced polyphony
**Repository:** https://github.com/b1rdmania/motif
---
## Project Overview
Build a Game Boy-authentic synthesis engine (v2) that captures the true DMG-CPU sound chip character while preserving v1's "any MIDI works" philosophy. The v2 engine will be hosted separately and won't intrude on v1's functionality.
**Key Architecture Decision:** 8-channel "Super Game Boy" setup (4 pulse, 2 wave, 2 noise) instead of authentic 4-channel limitation. This preserves GB sound character while handling complex MIDI files gracefully.
---
## Critical Constraint: v1 Isolation
**v1 MUST remain completely untouched and functional throughout v2 development.**
### What stays UNTOUCHED (v1):
- `src/synthesis/SynthesisEngine.ts` - current engine, no changes
- `src/core/MotifEngine.ts` - current orchestration, no changes
- All existing files in `src/` - completely untouched
- `index.html`, `play.html`, `embed.html` - no modifications
- Live site at wario.style continues to work exactly as today
### What gets CREATED (v2):
- Brand new directory: `src-v2/` - entirely separate codebase
- New entry point: `v2.html` - doesn't touch existing HTML files
- Separate Vite build config if needed
- Could become separate repo later if desired
### What v2 CAN import (read-only):
- `src/midi/MIDIParser.ts` - reuse existing MIDI parsing
- Shared types from `src/types/index.ts` if compatible
- Nothing else - all synthesis code is fresh
### Benefits of this approach:
1. Develop v2 while v1 stays live and stable
2. If v2 has issues, v1 is completely unaffected
3. A/B test with simple URL switch (`/` vs `/v2`)
4. Rollback is trivial - just don't deploy v2 code
5. Can eventually merge or keep separate forever
---
## Repository Structure
```
/Users/andy/MOTIF/
├── src/ # v1 (current, unchanged)
│ ├── core/MotifEngine.ts
│ ├── synthesis/SynthesisEngine.ts
│ └── ...
├── src-v2/ # v2 (NEW - separate tree)
│ ├── audio/
│ │ ├── apu/
│ │ │ ├── APU.ts # Main 8-channel coordinator
│ │ │ ├── PulseChannel.ts # GB pulse with duty cycles
│ │ │ ├── WaveChannel.ts # 4-bit wavetable
│ │ │ ├── NoiseChannel.ts # LFSR noise
│ │ │ └── Mixer.ts # Stereo mixing
│ │ ├── synthesis/
│ │ │ ├── DutyCycle.ts # 4 GB duty patterns
│ │ │ ├── LFSR.ts # Noise generator
│ │ │ ├── WaveTable.ts # 4-bit quantization
│ │ │ └── FrequencyCalc.ts # GB frequency formulas
│ │ └── midi/
│ │ ├── TrackAnalyzer.ts # Analyze MIDI structure
│ │ ├── ChannelMapper.ts # Assign tracks to 8 channels
│ │ └── Arpeggiator.ts # Convert chords to arps
│ ├── core/
│ │ └── GameBoyPlayer.ts # Main v2 entry point
│ └── types/
│ └── index.ts # v2-specific types
├── public/
│ └── v2/ # v2 UI assets
└── v2.html # v2 demo page
```
---
## Phase 1: Core Sound Engine (Week 1-2)
**Goal:** Implement authentic GB sound generation with Web Audio API
### 1.1 Duty Cycle Implementation
Create `src-v2/audio/synthesis/DutyCycle.ts`:
- Define 4 GB duty patterns (12.5%, 25%, 50%, 75%)
- Convert patterns to `PeriodicWave` for Web Audio
- Pattern format: 8-step arrays `[0,0,0,0,0,0,0,1]` etc
Create `src-v2/audio/apu/PulseChannel.ts`:
- Pre-create all 4 duty waveforms on init
- `playNote(midiNote, duration, velocity)` method
- Use GB frequency formula (not standard MIDI)
- Simple envelope (fast attack, quick release)
- Return `{ osc, gain }` for cleanup
**Success Criteria:** Each duty cycle sounds distinctly different when tested
### 1.2 GB Frequency Formulas
Create `src-v2/audio/synthesis/FrequencyCalc.ts`:
- `calculatePulseFrequency(midiNote)` → Hz
- Formula: `131072 / (2048 - registerValue)`
- Convert MIDI → standard freq → register → GB freq
- `calculateWaveFrequency(midiNote)` → Hz
- Formula: `65536 / (2048 - registerValue)`
- `calculateNoiseFrequency(pitch, mode)` → Hz
- Formula: `524288 / divisor / 2^(shift+1)`
**Success Criteria:** Frequencies are slightly "off" from standard tuning (GB characteristic)
### 1.3 LFSR Noise Generator
Create `src-v2/audio/synthesis/LFSR.ts`:
- Implement 15-bit LFSR (default mode)
- Implement 7-bit LFSR (tonal mode)
- `clock()` method returns 0 or 1
- XOR bits 0 and 1, shift right, set bit 14
Create `src-v2/audio/apu/NoiseChannel.ts`:
- `playNoise(duration, frequency, velocity)` method
- Generate buffer with LFSR output
- Clock LFSR at calculated rate
- Apply envelope to buffer playback
**Success Criteria:** Noise sounds crunchy/metallic, not smooth white noise
### 1.4 Wave Channel with 4-bit Quantization
Create `src-v2/audio/synthesis/WaveTable.ts`:
- Store 32 samples × 4-bit (0-15 values)
- `quantize(value)` rounds to 4-bit
- Presets: `generateBass()`, `generatePad()`, `generateLead()`
- `createBuffer(audioContext)` converts to AudioBuffer
Create `src-v2/audio/apu/WaveChannel.ts`:
- Load wavetable on construction
- `playNote(midiNote, duration, velocity)` method
- Use `BufferSource` with looping
- Set playback rate for pitch
- Volume levels: 0, 100%, 50%, 25% (bit-shift style)
**Success Criteria:** Wave channel has audible digital "staircase" effect
### 1.5 Testing Phase 1
Create `src-v2/audio/test/soundTest.ts`:
- Test all 4 duty cycles sequentially
- Test wave channel with bass preset
- Test both noise modes (7-bit and 15-bit)
- Play test sequence: duty sweeps, bass note, drum hits
---
## Phase 2: Channel Manager & 8-Channel APU (Week 2-3)
**Goal:** Coordinate 8 independent GB channels with mixing
### 2.1 APU Coordinator
Create `src-v2/audio/apu/APU.ts`:
- Initialize 4 pulse, 2 wave, 2 noise channels
- Channel IDs: `p1-p4`, `w1-w2`, `n1-n2`
- Master gain connected to destination
- Per-channel gain nodes for mixing
- `scheduleNote(note: ChannelNote)` routes to appropriate channel
- Track which channels are busy (`channelBusy` map)
- `isChannelFree(channelId, atTime)` for voice allocation
**Key Methods:**
- `scheduleNote({ channel, midiNote, startTime, duration, velocity })`
- `schedulePulseNote()`, `scheduleWaveNote()`, `scheduleNoiseNote()`
- `setChannelPan(channelId, pan)` for stereo
### 2.2 Channel Gain & Mixing
In `src-v2/audio/apu/APU.ts`:
- Each channel connects to individual `GainNode`
- Individual gains connect to master gain
- Master gain at ~0.7 to prevent clipping
- Per-channel volumes match role importance
**Success Criteria:** Can play 8 simultaneous notes without clipping
### 2.3 Integration Test
Create `src-v2/audio/test/apuTest.ts`:
- Schedule notes on all 8 channels simultaneously
- Verify no audio glitches or pops
- Test channel busy/free logic
- Test master volume control
---
## Phase 3: MIDI Intelligence Layer (Week 3-4)
**Goal:** Smart track analysis and channel assignment for arbitrary MIDIs
### 3.1 Track Analyzer
Create `src-v2/audio/midi/TrackAnalyzer.ts`:
- `analyzeTrack(track)` returns `TrackAnalysis`
- Detect drums (channel 9 or percussive patterns)
- Calculate note range (min, max, avg pitch)
- Calculate note density (notes per second)
- Detect chords (simultaneous notes)
- Assign role: `drums`, `bass`, `lead`, `harmony`, `pad`, `fx`
**Analysis Logic:**
- Drums: channel 9 OR very short notes with low pitch variation
- Bass: average pitch < 48 (C3)
- Lead: high pitch (>72) with high density (>5 notes/sec)
- Pad: low density (<2 notes/sec), long notes
- Harmony: medium density with detected chords
- FX: very high density (>10 notes/sec)
### 3.2 Arpeggiator
Create `src-v2/audio/midi/Arpeggiator.ts`:
- `arpeggiate(notes, speed)` converts chords to fast note sequences
- Group notes by time (10ms tolerance)
- Single notes pass through unchanged
- Chords (2+ simultaneous notes) → fast arpeggio
- Default speed: 1/64 note
- Sort chord notes low-to-high
- Cycle through chord notes for full duration
**Success Criteria:** 3-note chord becomes smooth fast arpeggio
### 3.3 Channel Mapper
Create `src-v2/audio/midi/ChannelMapper.ts`:
- `mapTracks(midiTracks)` returns array of `ChannelAssignment`
- Analyze all tracks first
- Sort by priority (drums > bass > lead > harmony)
- Assign intelligently:
- Drums → `n1`, `n2` (noise channels)
- Bass → `w1` (wave bass preset)
- Pads → `w2` (wave pad preset)
- Lead → `p1`, `p2` (pulse with sweep, 50% duty)
- Harmony → `p3`, `p4` (pulse, 25% duty, arpeggiated)
- Mark which tracks need arpeggiator
- Specify duty cycle per assignment
**Priority Calculation:**
- Drums +30 points
- Bass +25 points
- Lead +20 points
- Note density +up to 20 points
- Velocity +up to 10 points
**Success Criteria:** Mario theme maps melody to pulse, no bass assigned (no bass in song)
### 3.4 Integration Test
Create `src-v2/audio/test/mapperTest.ts`:
- Load test MIDI (simple melody + bass + drums)
- Run through analyzer and mapper
- Verify drum tracks → noise channels
- Verify bass → wave channel
- Verify melody → pulse channel
- Print channel assignments for inspection
---
## Phase 4: Main Player & Integration (Week 4)
**Goal:** Complete end-to-end MIDI → GB audio pipeline
### 4.1 Game Boy Player
Create `src-v2/core/GameBoyPlayer.ts`:
- Main entry point for v2 engine
- `async playMIDI(midiBuffer: ArrayBuffer)`
- Parse MIDI using existing `src/midi/MIDIParser.ts`
- Analyze tracks → assign channels → convert to GB notes
- Schedule all notes in APU
- `stop()` method resets APU
- Return playback info (duration, assignments for UI)
**Pipeline:**
1. Parse MIDI → `NoteEvent[]`
2. Analyze tracks → `TrackAnalysis[]`
3. Map to channels → `ChannelAssignment[]`
4. Apply arpeggiator where needed
5. Convert to `ChannelNote[]` format
6. Schedule in APU
### 4.2 V2 Types
Create `src-v2/types/index.ts`:
- `ChannelNote` interface
- `ChannelAssignment` interface
- `TrackAnalysis` interface
- `ArpNote` interface
- GB-specific config types
### 4.3 Demo Page
Create `v2.html`:
- Simple test UI for v2 engine
- File upload input for MIDI
- Play/stop buttons
- Volume slider
- Display channel assignments
- Show which channels are active (visual)
Create `src-v2/main.ts`:
- Wire up UI to `GameBoyPlayer`
- Handle file uploads
- Display playback state
- Show assignment information
### 4.4 Integration Test
Test with reference MIDIs:
- **Mario theme:** Simple melody (should use p1)
- **Tetris theme:** Bass + lead (should use w1 + p1)
- **Pokémon theme:** Chords (should arpeggiate to p3/p4)
- **Hotel California:** Complex (use all 8 channels)
**Success Criteria:**
- All test MIDIs sound recognizable
- No audio glitches or pops
- Drums sound punchy (noise)
- Bass sounds solid (wave)
- Melody is clear (pulse)
- Chords arpeggiate smoothly
---
## Phase 5: Polish & Optimization (Week 5)
### 5.1 Performance Optimization
In `src-v2/audio/apu/APU.ts`:
- Limit simultaneous notes to 32 total
- Implement voice stealing (oldest note first)
- Add `onended` cleanup for oscillators
- Pre-create reusable nodes where possible
### 5.2 Browser Compatibility
In `src-v2/core/GameBoyPlayer.ts`:
- Add AudioContext resume on user interaction
- Handle Safari audio quirks
- Add mobile audio unlock
- Test on Chrome, Firefox, Safari
### 5.3 Advanced Features (Nice-to-Have)
- **Stereo Panning:** GB-style hard L/R/center per channel
- **Duty Cycle Switching:** Change duty mid-playback for variation
- **Custom Wavetables:** User-editable wave presets
- **Export to WAV:** Offline rendering to downloadable file
---
## Phase 6: Deployment Strategy
### 6.1 Alpha Testing (Week 6)
- Deploy v2 to staging URL (e.g., `v2.wario.style` or `wario.style/beta`)
- Keep v1 at main URL unchanged
- Test with small group (5-10 people)
- Gather feedback on authenticity
- Fix critical bugs
### 6.2 Beta Release (Week 7)
- Deploy to production behind feature flag
- Add "Try v2 Beta" button on main site
- A/B test user preferences (v1 vs v2)
- Monitor performance metrics
- Gradual rollout: 10% → 50% → 100%
### 6.3 Full Release (Week 8)
- Make v2 the default engine
- Keep v1 available as "Classic Mode"
- Update README and docs
- Social media announcement
- Monitor error rates and feedback
**Rollback Plan:**
- Feature flag can instantly revert to v1
- Maintain "problematic MIDI" database
- User preference saved in localStorage
---
## Technical Notes
### Web Audio Implementation
**Key Web Audio APIs:**
- `PeriodicWave` for duty cycles
- `OscillatorNode` for pulse channels
- `AudioBufferSourceNode` for wave/noise
- `GainNode` for volume/envelopes
- `audioContext.currentTime` for precise scheduling
**Memory Management:**
```typescript
source.onended = () => {
source.disconnect()
gain.disconnect()
}
```
**Latency Target:** <50ms from schedule to sound
### What Matters vs What Doesn't
**✅ CRITICAL (Implement):**
- Exact duty cycle patterns
- LFSR noise generation
- 4-bit wave quantization
- GB frequency formulas
- Fast arpeggios
- Simple envelopes
**❌ SKIP (Emulator minutiae):**
- Length counter edge cases
- DIV-APU timing sync
- Wave RAM corruption bugs
- Sweep overflow quirks
- DAC pop suppression
- High-pass filter modeling
---
## Testing Strategy
Testing follows a layered approach - technical specs for implementation correctness, community feedback for authenticity.
### Layer 1: Technical Sanity Checks (Automated, Quick)
Quick checks that catch implementation bugs:
- **Waveform visualization** - view duty cycles in browser dev tools or canvas oscilloscope
- **LFSR sequence verification** - first 20 values match known GB sequence
- **Frequency spot-check** - play A4 (440Hz), verify it's slightly off (~438.5Hz due to GB register rounding)
Setup time: ~30 minutes. Run on every build.
### Layer 2: Reference MIDI Corpus (Manual, Essential)
Core QA loop with 5 test MIDIs:
| MIDI | What it tests |
|------|---------------|
| Mario Bros theme | Simple melody on pulse channels |
| Tetris theme | Bass + lead separation |
| Pokemon battle music | Chord arpeggiation |
| Any pop song with drums | Noise channel percussion |
| Hotel California | Complex multi-track mapping |
Process: Run each through v2, listen with headphones, note what sounds wrong.
### Layer 3: Community Vibe Check (Subjective, Final)
Post short clips to:
- r/chiptunes subreddit
- Chiptune Café Discord
Ask: "Does this sound like a Game Boy?"
Real chiptune people will identify specific issues ("duty cycles wrong", "noise too clean", etc.)
### What to Skip
- Automated audio comparison (too complex, diminishing returns)
- Cycle-accurate timing tests (emulator territory, not our goal)
- Formal A/B studies (overkill for this project)
### Success Metrics
- All Layer 1 checks pass
- All 5 reference MIDIs sound recognizable
- Community feedback: "yes, sounds like GB"
- Zero audio glitches or pops
- Works on Chrome, Firefox, Safari
- <100ms latency
---
## File Checklist
### Core Sound Engine (Phase 1)
- [ ] `src-v2/audio/synthesis/DutyCycle.ts`
- [ ] `src-v2/audio/synthesis/FrequencyCalc.ts`
- [ ] `src-v2/audio/synthesis/LFSR.ts`
- [ ] `src-v2/audio/synthesis/WaveTable.ts`
- [ ] `src-v2/audio/apu/PulseChannel.ts`
- [ ] `src-v2/audio/apu/WaveChannel.ts`
- [ ] `src-v2/audio/apu/NoiseChannel.ts`
- [ ] `src-v2/audio/test/soundTest.ts`
### APU & Mixing (Phase 2)
- [ ] `src-v2/audio/apu/APU.ts`
- [ ] `src-v2/audio/apu/Mixer.ts`
- [ ] `src-v2/audio/test/apuTest.ts`
### MIDI Intelligence (Phase 3)
- [ ] `src-v2/audio/midi/TrackAnalyzer.ts`
- [ ] `src-v2/audio/midi/Arpeggiator.ts`
- [ ] `src-v2/audio/midi/ChannelMapper.ts`
- [ ] `src-v2/audio/test/mapperTest.ts`
### Integration (Phase 4)
- [ ] `src-v2/core/GameBoyPlayer.ts`
- [ ] `src-v2/types/index.ts`
- [ ] `src-v2/main.ts`
- [ ] `v2.html`
### Documentation
- [ ] `docs/GB_SOUND_SPECS.md` (technical reference)
- [ ] `docs/V2_ARCHITECTURE.md` (system overview)
- [ ] `CHANGELOG_V2.md` (version history)
---
## Future Enhancements (v3+)
**Short Term:**
- User-adjustable duty cycles via UI
- Custom wavetable editor
- Real-time parameter tweaking
- Oscilloscope visualizer
- MIDI file upload (not just search)
**Medium Term:**
- Frequency sweep on pulse channels
- Vibrato effects
- Echo/delay using note repeats
- Better envelope shaping (ADSR editor)
- Recording/export to WAV
**Long Term:**
- Full tracker-style sequencer
- Multiple retro chips (NES APU, C64 SID)
- VST plugin version
- Mobile app with touch controls
- Collaborative editing
---
**Document Version:** 2.0
**Last Updated:** January 2026
**Status:** Ready for implementation