A clean, real-time MIDI visualization app for iOS / macOS that transforms musical notes into animated visual elements. Built with SwiftUI, Core MIDI and AVFoundation, this app demonstrates modern development practices and creative audio-visual programming.
MIDIVisualizerDemo.mov
MIDI Visualizer provides an interactive music visualization experience. The app features a virtual piano keyboard that generates MIDI events, which are then visualized as animated shapes with properties mapped to musical characteristics:
- Pitch → Vertical Position: Higher notes appear higher on screen
- Velocity → Size & Color Intensity: Harder key presses create larger, more vibrant visuals
- Note Duration → Animation Length: Visual elements fade out after note release
- 🎹 Virtual Piano Keyboard: Interactive on-screen keyboard (C4-B4) with properly positioned black keys
- 🎨 Multiple Visualization Styles:
- Circles: Notes appear as colored circles positioned by pitch with note names
- Bars: Vertical bars showing all chromatic notes (C4-B4) with velocity-based height
- 🌈 Customizable Color Schemes: Rainbow (default), Fire, and Ocean themes
- ⚡ Real-time Performance: Smooth 60fps animations synchronized with MIDI input
- 🎵 Demo Playback: Built-in C major arpeggio for easy demonstration
- 🔊 Optional Audio Feedback: Toggle sound on/off in settings
- ⚙️ Adjustable Settings: Control animation speed and visual preferences
The app follows MVVM (Model-View-ViewModel) architecture with clear separation of concerns:
MIDIVisualizer/
├── App Layer
│ └── MIDIVisualizerApp.swift # App entry point
├── Views
│ ├── ContentView.swift # Main container view
│ ├── VisualizerView.swift # Visualization rendering
│ ├── VirtualKeyboardView.swift # Piano keyboard UI
│ └── SettingsView.swift # Settings interface
├── ViewModels
│ └── VisualizerViewModel.swift # Business logic & state management
└── Services
├── MIDIHandler.swift # Core MIDI integration
└── AudioEngine.swift # Audio playback (optional)
Handles all Core MIDI functionality, creating virtual endpoints and processing MIDI events.
- midiClient: Core MIDI client reference
- virtualSource/Destination: MIDI endpoints for sending/receiving
- noteRange: C4-B4 (MIDI notes 60-71)
- setupMIDI(): Creates MIDI client and virtual endpoints
- sendNoteOn(note:velocity:channel:): Generates Note On events with direct delegate callback
- sendNoteOff(note:channel:): Generates Note Off events
- handleMIDIPackets(_:): Processes incoming MIDI data
- playDemoSequence(): Plays a pre-programmed C major arpeggio with audio
- MIDIHandlerDelegate: Interface for receiving MIDI events
Manages visualization state and transforms MIDI events into visual representations.
- activeNotes: Array of currently visible note visualizations
- visualizationStyle: Current visualization mode (circles/bars)
- colorScheme: Active color theme (Rainbow default, Fire, Ocean)
- animationSpeed: Controls fade-out duration
- midiNoteOn(note:velocity:): Creates new visualization for note
- midiNoteOff(note:): Triggers fade-out animation
- calculateRadius(for:): Maps velocity to circle size (10-50pt)
- getColor(for:velocity:): Determines color based on scheme and note
- NoteVisualization: Represents a visual element with pitch, velocity, size, color, and opacity
Renders the actual visualizations based on the current style and active notes.
- CircleVisualizationView: Displays notes as positioned circles with note names
- BarVisualizationView: Shows chromatic scale (C4-B4) as vertical bars
- BackgroundGridView: Optional grid overlay
- calculateXPosition(for:in:): Maps pitch to horizontal position
- calculateYPosition(for:in:): Maps MIDI pitch (0-127) to vertical position
- calculateBarHeight(for:): Maps velocity to bar height (40-200pt)
- Circle view shows note names that move with the circles
- Bar view displays all 12 chromatic notes with proper spacing
Implements the interactive piano keyboard with proper touch handling.
- WhiteKeyView: Renders white keys with touch states
- BlackKeyView: Renders black keys with correct positioning between white keys
- whiteKeys: Array of MIDI notes for white keys [60,62,64,65,67,69,71]
- blackKeys: Individual positioning for C#4, D#4, F#4, G#4, A#4
- handleKeyPress(_:): Sends Note On with random velocity (60-100) and triggers audio
- handleKeyRelease(_:): Sends Note Off and stops audio
Provides user customization options in a modal sheet.
- Visualization Style (Circles/Bars)
- Color Scheme (Rainbow/Fire/Ocean)
- Animation Speed (0.5x-2.0x)
- Background Grid Toggle
- Sound Enable/Disable
Simple audio feedback system using AVFoundation.
- playNote(_:velocity:): Plays a sound for the given note
- stopNote(_:): Stops the sound for a note
- toggleSound(): Enables/disables audio feedback
- Horizontal Position: Represents the note (C through B)
- Vertical Position: Higher pitch = higher on screen
- Size: Larger shapes = higher velocity (louder)
- Color Intensity: Brighter = higher velocity
- Fade Out: Happens after note release
- Note Names: Displayed on circles for easy identification
- iOS 16.0+ (Deployment target)
- macOS Version: macOS 13.0+ (Deployment target)
- Xcode 16.0+ (Development)
- Swift 5.9+
- Frameworks: SwiftUI, Core MIDI, AVFoundation
- iOS: Full support with touch interactions
- macOS: Mouse-based interaction (click to play notes)
-
Velocity/Pressure: Currently randomized (60-100) for each key press
- Real devices could use 3D Touch/Haptic Touch for actual pressure sensitivity
- Demonstrates the visual system's response to different velocities
-
MIDI Implementation: Virtual MIDI endpoints only
- No external MIDI device support (yet), for demonstration without hardware requirements
- Core MIDI framework is properly implemented for future expansion
-
Audio Engine: Basic implementation using system sounds
- Focused on visualization rather than synthesis
- Can be enhanced with proper sampler or synthesizer
- Portfolio Accessibility: Anyone can run and test without MIDI hardware
- Visual Focus: Emphasizes the visualization system over audio complexity
- Simulator Friendly: Full functionality in Xcode Simulator for easy demos
- Expandability: Architecture supports adding real features later