MIDI 2.0 Patch Selection Application
Project description
R2MIDI - Enhanced MIDI Preset Selection System
A comprehensive MIDI preset selection application with enhanced performance, caching, and user experience features.
Overview
R2MIDI is a PyQt6-based application that provides an intuitive interface for browsing and selecting MIDI presets across multiple devices and manufacturers. The enhanced version includes significant improvements in performance, usability, and customization.
Key Features
Core Functionality
- Device Management: Browse devices by manufacturer with cascading selection
- Preset Selection: View and select presets with category filtering
- MIDI Control: Send preset changes to MIDI devices with configurable ports and channels
- Community Folders: Support for community-contributed preset collections
- Multi-Device Support: Control multiple MIDI devices simultaneously
- Sequencer Integration: Optional routing to a sequencer port
Enhanced Features
๐ Performance Optimizations
- Intelligent Caching: 1-hour cache for API responses significantly reduces server load
- Retry Logic: Automatic retry with exponential backoff for failed requests
- Debounced Controls: Prevents rapid API calls during UI interactions
- Performance Monitoring: Real-time CPU and memory usage tracking in debug mode
- Lazy Loading: Efficient handling of large preset datasets
- Parallel Processing: Multi-threaded device scanning and preset loading
๐จ User Interface
- Dark Mode: Professional dark theme with full UI integration
- Loading Indicators: Visual feedback for all async operations
- Search Functionality: Real-time preset search with debouncing
- Favorites System: Mark and filter favorite presets
- Keyboard Shortcuts: Comprehensive keyboard navigation
- Category Colors: Visual categorization with persistent color assignments
- Offline Mode: Work without syncing to remote repositories
โ๏ธ Configuration
- Preferences Dialog: Comprehensive settings management
- Persistent State: Remembers selections between sessions
- Configuration Export/Import: Share settings between installations
- Debug Mode: Advanced logging and performance monitoring
โจ๏ธ Keyboard Shortcuts
| Action | Shortcut |
|---|---|
| Send Preset | Enter/Return |
| Search Presetes | Ctrl+F |
| Clear Search | Esc |
| Toggle Favorites | Ctrl+D |
| Refresh Data | F5 |
| Preferences | Ctrl+, |
| Quit | Ctrl+Q |
| Next/Previous Preset | โ/โ or J/K |
| Next/Previous Category | โ/โ or L/H |
| MIDI Channel Up/Down | Ctrl+โ/โ |
Installation
R2MIDI can be installed in multiple ways:
Pre-built Executables
Download the latest pre-built executable for your platform from the GitHub Releases page:
- Windows: Download
r2midi-windows.exe - macOS: Download
r2midi-macos - Linux: Download
r2midi-linux
The executables are self-contained and don't require Python or any dependencies to be installed.
PyPI Installation
Available on PyPI: https://pypi.org/project/r2midi
pip install r2midi
r2midi
Manual Installation
Requirements
- Python 3.8+
- PyQt6
- httpx
- psutil
- SendMIDI command-line tool
- MIDI devices connected to your computer
Installation Steps
-
Clone the repository:
git clone https://github.com/tirans/r2midi.git cd r2midi
-
Initialize and update the midi-presets submodule:
git submodule init git submodule update --init --recursive
If you encounter issues with the submodule, you can also manually clone it:
git clone https://github.com/tirans/midi-presets.git midi-presets
-
Install dependencies:
pip install -r requirements.txt
Or using the pyproject.toml:
pip install -e .
-
Run the application:
python main.py
Configuration
Environment Variables
Create a .env file in the project root with the following variables:
PORT=7777 # The port for the API server (default: 7777)
Application Configuration
Configuration is stored in ~/.r2midi_config.json with the following options:
{
"server_url": "http://localhost:7777",
"cache_timeout": 3600,
"dark_mode": false,
"enable_favorites": true,
"enable_search": true,
"enable_keyboard_shortcuts": true,
"debounce_delay_ms": 300,
"max_presets_display": 1000,
"sync_enabled": true
}
The sync_enabled option controls whether the application syncs with remote repositories. Set to false to enable offline mode.
Device Configuration
Device definitions are stored as JSON files in the devices folder. Each device file should follow this format:
{
"name": "Device Name",
"midi_ports": {
"main": "MIDI Port Name",
"alternate": "Alternative MIDI Port Name"
},
"midi_channels": {
"main": 1,
"alternate": 2
},
"presets": [
{
"preset_name": "Preset 1",
"category": "Category",
"characters": ["warm", "bright"],
"cc_0": 0,
"pgm": 1
},
{
"preset_name": "Preset 2",
"category": "Another Category",
"characters": ["dark", "deep"],
"cc_0": 0,
"pgm": 2
}
]
}
An example device file for the Expressivee Osmose is included in the devices folder.
Usage
Starting the Application
Run the main application:
python main.py
This will:
- Start the API server on the configured port (default: 7777)
- Scan for connected MIDI devices
- Launch the GUI client
Using the GUI
The GUI is divided into two main panels:
Device Panel (Top)
- MIDI Output Port: Select the MIDI port to send commands to
- MIDI Channel: Select the MIDI channel (1-16)
- Sequencer Port: Optionally select a secondary port to send the same commands to (useful for recording in a DAW)
Preset Panel (Bottom)
- Browse presets by category
- Search for presets by name
- Select a preset to send to the device
- Mark favorites for quick access
- Filter by characteristics or categories
Sending MIDI Commands
- Select a MIDI output port and channel in the Device Panel
- Select a preset in the Preset Panel
- Click the "Send MIDI" button or press Enter to send the program change to your device
API Endpoints
The application provides a REST API that can be used by other applications:
GET /devices- Get a list of all devicesGET /presets- Get a list of all presetsGET /midi_port- Get a list of available MIDI portsPOST /preset- Send a preset to a MIDI port/channelGET /git/sync- Sync the midi-presets submodule
Example API usage with curl:
# Get all devices
curl http://localhost:7777/devices
# Get all presets
curl http://localhost:7777/presets
# Get MIDI ports
curl http://localhost:7777/midi_port
# Send a preset
curl -X POST http://localhost:7777/preset \
-H "Content-Type: application/json" \
-d '{"preset_name": "Preset Name", "midi_port": "MIDI Port Name", "midi_channel": 1}'
# Sync the midi-presets submodule
curl http://localhost:7777/git/sync
Architecture
Module Structure
r2midi/
โโโ midi_preset_client/
โ โโโ api_client.py # Enhanced API client with caching
โ โโโ config.py # Configuration management
โ โโโ models.py # Data models
โ โโโ performance.py # Performance monitoring
โ โโโ shortcuts.py # Keyboard shortcuts
โ โโโ themes.py # Theme management
โ โโโ ui/
โ โโโ device_panel.py # Device selection with debouncing and offline mode
โ โโโ main_window.py # Main application window
โ โโโ preset_panel.py # Preset display with search and persistent category colors
โ โโโ edit_dialog.py # Edit dialog for manufacturers, devices, and presets
โ โโโ preferences_dialog.py # Settings management
โโโ midi-presets/ # Git submodule containing device definitions
โ โโโ devices/ # Device definitions and presets
โโโ main.py # Main entry point and API server
โโโ device_manager.py # Handles device scanning and management with parallel processing
โโโ git_operations.py # Handles git submodule operations
โโโ midi_utils.py # MIDI utility functions
โโโ models.py # Data models
โโโ ui_launcher.py # Launches the GUI client
โโโ version.py # Contains the current version of the application
โโโ pre-commit # Git hook script to increment version on commit
โโโ .github/
โ โโโ workflows/ # GitHub Actions workflows for CI/CD and executable building
โโโ tests/
โโโ unit/ # Unit tests
โโโ temp/ # Temporary test files
Key Components
CachedApiClient
- Implements caching with configurable timeout
- Automatic retry with exponential backoff
- Persistent UI state management
- Thread-safe async operations
DebouncedComboBox
- Prevents rapid selection changes from triggering API calls
- Configurable delay (default: 300ms)
- Maintains smooth UI responsiveness
PerformanceMonitor
- Real-time CPU and memory tracking
- Operation timing with statistics
- Performance summary logging
- Context managers for easy integration
ThemeManager
- Light and dark theme support
- Comprehensive widget styling
- Native look and feel
- Smooth theme transitions
Testing
Run the comprehensive test suite:
# Install test dependencies
pip install -e ".[test]"
# Run all tests
python -m pytest tests/ -v
# Run specific test modules
python -m pytest tests/test_enhanced_functionality.py -v
python -m pytest tests/test_comprehensive_features.py -v
# Run with coverage
python -m pytest tests/ --cov=midi_preset_client --cov-report=html
Performance
Benchmarks
- Cache Hit Rate: >90% for repeated operations
- API Response Time: <100ms with caching (vs 500ms+ without)
- UI Responsiveness: <16ms frame time (60+ FPS)
- Memory Usage: ~50MB baseline, ~100MB with 1000+ presets
Optimization Tips
- Enable caching in preferences (default: on, now with 1-hour timeout)
- Adjust debounce delay for your network speed
- Use lazy loading for large preset collections
- Enable performance monitoring in debug mode
- The application now uses parallel processing for device scanning, significantly improving load times
Troubleshooting
Common Issues
-
No MIDI devices detected
- Check that your MIDI devices are connected and powered on
- Some devices may require specific drivers
- Verify MIDI port names in the device configuration
- The application automatically validates and initializes the midi-presets submodule on startup
- If you're having connectivity issues, try enabling offline mode in the UI
- If you still have issues, manually run the git sync operation via the API:
curl http://localhost:7777/git/sync?sync_enabled=true
-
UI client fails to start
- Check the logs in the
logsdirectory for error messages - Ensure PyQt6 is properly installed
- Verify that the API server is running
- Check the logs in the
-
Server Connection Failed
- Check server is running on configured port
- Verify firewall settings
- Check server URL in preferences
-
Presetes Not Loading
- Ensure manufacturer and device are selected
- Check server logs for errors
- Try refreshing data (F5)
-
High Memory Usage
- Reduce max_presets_display in config
- Clear cache in preferences
- Disable debug mode
Logs
Logs are stored in the logs directory:
main.log- Main application logsdevice_manager.log- Device manager logsmidi_utils.log- MIDI utility logsui_launcher.log- UI launcher logsuvicorn.log- Web server logsall.log- Combined logs from all components
Version Management
The application version is stored in version.py and is automatically incremented on each push to the master branch using GitHub Actions.
How Version Incrementing Works
The version incrementing is handled by the GitHub Actions workflow defined in .github/workflows/python-package.yml. When code is pushed to the master branch, the workflow:
- Builds and tests the application
- Increments the preset version (the third number in the version)
- Updates both
version.pyandpyproject.tomlwith the new version - Commits and pushes the version changes back to the repository
- Creates a GitHub release with the new version
- Publishes the package to PyPI
- Builds executable binaries for Windows, macOS, and Linux using PyInstaller
- Uploads the executables to the GitHub release
For example, if the current version is 0.1.0, after a push to master it will be 0.1.1.
Manual Version Updates
For major or minor version updates (first or second number), manually edit the version.py and pyproject.toml files before pushing to master. The GitHub Actions workflow will still handle creating the release and publishing to PyPI.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Add tests for new functionality
- Ensure all tests pass
- Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Note: You don't need to worry about incrementing the version number. This is handled automatically by GitHub Actions when your changes are merged to the master branch.
Acknowledgments
- PyQt6 for the excellent GUI framework
- httpx for async HTTP client
- psutil for system monitoring
- Contributors to the midi-presets repository
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file r2midi-0.1.53.tar.gz.
File metadata
- Download URL: r2midi-0.1.53.tar.gz
- Upload date:
- Size: 87.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
43669e4ddc1f2c0c11a55077c954ac7ba2469bf5654cd59ebd61f0790733ffd7
|
|
| MD5 |
963684b0e5f2a8848d2ae43159d93194
|
|
| BLAKE2b-256 |
8717c88816f4a50d3c1c0865498a2846ba80aff65f91b06273faaf810ad033cf
|
Provenance
The following attestation bundles were made for r2midi-0.1.53.tar.gz:
Publisher:
python-package.yml on tirans/r2midi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
r2midi-0.1.53.tar.gz -
Subject digest:
43669e4ddc1f2c0c11a55077c954ac7ba2469bf5654cd59ebd61f0790733ffd7 - Sigstore transparency entry: 226962680
- Sigstore integration time:
-
Permalink:
tirans/r2midi@c9c44f94ccfed54f20dc3206f70c8399da5679bb -
Branch / Tag:
refs/heads/master - Owner: https://github.com/tirans
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-package.yml@c9c44f94ccfed54f20dc3206f70c8399da5679bb -
Trigger Event:
push
-
Statement type:
File details
Details for the file r2midi-0.1.53-py3-none-any.whl.
File metadata
- Download URL: r2midi-0.1.53-py3-none-any.whl
- Upload date:
- Size: 90.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a01c713150b82c8ef21e84907fa1ea30d27309ebff441080a434fc9d8cf46aa9
|
|
| MD5 |
cb272190211d7df1eb1c778b632c59ac
|
|
| BLAKE2b-256 |
667307b0efa6b0f5f380cfb6a8f1ac0d0da49d90d888ac8fefa977e7512ded1b
|
Provenance
The following attestation bundles were made for r2midi-0.1.53-py3-none-any.whl:
Publisher:
python-package.yml on tirans/r2midi
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
r2midi-0.1.53-py3-none-any.whl -
Subject digest:
a01c713150b82c8ef21e84907fa1ea30d27309ebff441080a434fc9d8cf46aa9 - Sigstore transparency entry: 226962682
- Sigstore integration time:
-
Permalink:
tirans/r2midi@c9c44f94ccfed54f20dc3206f70c8399da5679bb -
Branch / Tag:
refs/heads/master - Owner: https://github.com/tirans
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-package.yml@c9c44f94ccfed54f20dc3206f70c8399da5679bb -
Trigger Event:
push
-
Statement type: