1. QT and Pyside6: PythonGUIs GUI applications with Python
First thing I needed to do to was figure out how to build the sliders in the template MirageProgramTemplate.png. After much searching and trying out Maui and some others decided on Pyside6.
- Install the qt framework from Qt Software Downloads
- Install pyenv so you can more easily switch versions of Python when needed.
- Use pyenv to install Python 3.12.0 (3.13 is newer but doesn't support rtmidi yet)
- Use python to install the other prerequisites using
pip install pyside6
, etc... - Install VS Code from Download for your system. It's a large IDE with a lot of functionality, but well supported.
- I chose Python because I needed to learn it for a new position I'm starting in December, 2024
- I chose QT because it provides professional quality User Interfaces with a Python wrapper called PySide6 for easy programming and is cross platform, meaning one program works on Linux, Windows and Mac OS laptops, a requirement for musicians is Windows and Mac OS.
- Note I implemented this UI in Python using QT Widgets, and not QML. QML would be a good choice if you needed a tablet interface or were working with a separate UX team. As that is not the case here widgets are a simpler choice.
- There was a choice between PySide6 and PyQt for Python wrappers for QT but for licensing reasons went with PySide6. They are extremely similar though.
This all worked fine with Python 3.12.0
- app-1.py represents Creating your first app with PySide6
- app-2.py represents PySide6 Signals, Slots & Events Button signals.
- app-3.py represents PySide6 Signals, Slots & Events Connecting widgets together directly.
- app-4.py represents A Quick Demo: PySide6 Widgets
- app-5.py represents a Mockup of Sliders for Ensoniq Mirage Controller using a Custom Slider.
- app-6.py Cleaned up version of app-5.py with helper functions.
2. MIDI: python-rtmidi 1.5.8 from GitHub and Docs without Qt.
This is where I learned I needed to back up and use Python 3.12.0, here's how to do that:
In VS Code switch to Python 3.12.0 Press Ctrl + Shift + P and select "Python: Select Interpreter".
I had to find and install the python wheel from pypi. You'll need to find the one for your architecture.
pyenv install 3.12.0
pyenv global 3.12.0
pyenv rehash
pyenv shell 3.12.0
python -m pip install --upgrade pip # downgrade the installer.
from pypi.org python-rtmidi #files -> Built Distributions you'll find a download link: python_rtmidi-1.5.8-cp312-cp312-win_amd64.whl or use your own architecture to install the C# libraries you'll need.
download and install with:
pip install python_rtmidi-1.5.8-cp312-cp312-win_amd64.whl --force-reinstall # if you have the whl file.
# --or-- might work as well.
pip install python-rtmidi==1.5.8
- midi-app-1.py - simple programs to see if midi is working. Plays a few notes and prints out the midi ports available.
- midi-app-2.py - start exercising the SysEx functionality and printing out the messages in hex.
This explains what a Sampler is and where I started to realize I was in over my head.
- Wikipedia explains Samplers in general and is quite readable.
- Parameter Reference With some luck this may include the SysEx
command_id
parameters. - Filter Attack Explains just one of the parameters. This is when I started to get worried.
More incomprehensible documents I found but not sure what to do with.
- Mirage DSK-1 Musicians Manual see page 49
- Ensoniq Corporation - Mirage Musician's Manual Website.
- mirage_dsk-1_musicians_manual
- ensoniq_mirage_dsk-1_dsk-8_musicians_manual.pdf
- Mirage Chart
- Wikipedia explains Midi System Exclusive Message which are specific to each manufacturer and model. Original spec archived.
- Ensoniq manufacturer is
0F
as defined on MIDI Manufacturer IDs list.
On the plus side MIDI using mido is the most straightforward. Here are the other libraries for comparison.
Library | Real-Time MIDI | MIDI File I/O | Advanced Features | Use Case |
---|---|---|---|---|
Mido | Yes | Yes | Basic | General MIDI tasks |
pygame.midi | Yes | No | Limited | Real-time MIDI for games |
pretty_midi | No | Yes | High-level analysis tools | Music analysis and machine learning |
py-midi | No | Yes | Basic | Simple MIDI file handling |
midiutil | No | Yes | File creation | Generating MIDI files programmatically |
pyFluidSynth | Yes (playback) | No | Soundfont synthesis | MIDI playback with soundfonts |
python-rtmidi | Yes | No | Low-level real-time MIDI | Low-latency real-time MIDI communication |
mingus | No | Limited | Music theory integration | Score creation and musical constructs |
F0 <Manufacturer ID> <Device ID> <Command/Function> <Data Bytes> F7
F0 0E 01 20 7F F7
F0: Start of SysEx.
0F: Manufacturer ID for Ensoniq.
01: Device ID (usually 01 if there's only one device in the chain).
20: Command for a parameter (e.g., filter cutoff).
7F: Data byte representing the parameter value.
F7: End of SysEx.
Python
import mido
# Example SysEx message for filter cutoff
sysex_message = [0xF0, 0x0F, 0x01, 0x22, 0x64, 0xF7] # Adjust filter cutoff to 100
with mido.open_output("Your MIDI Port Name") as midi_out:
midi_out.send(mido.Message('sysex', data=sysex_message))
MANUFACTURER_ID = 0x0F
DEVICE_ID = 0x01
MIDI_PORT_NAME = 'ENSONIQ' #tbd
# Construct the SysEx message This one is supposed to be Filter Attack command (40) in the mirage-paramter-cards.pdf file.
sysex_data = [MANUFACTURER_ID, DEVICE_ID, 0x40, 12 ] # verify whether command_id in chart is dec or hex.
# Open a MIDI output port
with mido.open_output(MIDI_PORT_NAME) as midi_out:
# note (0xF0 and 0xF7) are added by the mido library since we specified 'sysex' as the type.
midi_out.send(mido.Message('sysex', data=sysex_message))
# Print the message in hex format
sysex_message_hex = ' '.join(f'{byte:02X}' for byte in sysex_data)
print(f"Sent SysEx message in hex: F0 {sysex_message_hex} F7")
- mirage.py - working version of user interface copied from app-6.py as a starting point.
- mirage-2.py - starting to add in midi sysex messages to user interface. This does work but is just using the first port it finds. Not sure how to find the Ensoniq Mirage port. This does operate and send out sysex messages, but of course it doesn't do anything useful with out the actual sampler to talk to.
Created additional folder to allow unit testing of mirage_slider via:
pip install pytest pytest-qt coverage
pytest
coverage run --source=ensoniq -m pytest
coverage report
coverage html # open htmlcov\index.html for details.
Made a modification to display the Hex command id on the custom sliders, and pass the command ids in Hex. This should make it easier to match up the correct hex commandid to the right slider title. To run the new version:
python ensoniq\mirage_main.py
note: The program will require configuration once the actual ensoniq is connected. Just choose the correct port from the output of the program currently Available MIDI Output Ports: ['Microsoft GS Wavetable Synth 0']
and modify File ensoniq/config.py
with the correct MIDI_PORT_NAME
and possibly DEVICE_ID
.
Title is also configurable. Other labels and turning on and off display of hex control ids can be added later, if needed.
You'll need to install Python 3.12 to begin from python.org/downloads
#Fedora
python -m venv .venv
source .venv/bin/activate
download https://files.pythonhosted.org/packages/f6/92/5a60f56dfb2740e644e932233928947423cd2101895319b331f84527eb31/python_rtmidi-1.5.8-cp312-cp312-manylinux_2_28_x86_64.whl
update the requirements.txt file with the correct whl file.
pip install -r mirage/requirements.txt
sudo dnf install timidity++ # or other midi program.
#Windows
python -m venv .venv
. .venv/bin/activate
download https://files.pythonhosted.org/packages/f6/92/5a60f56dfb2740e644e932233928947423cd2101895319b331f84527eb31/python_rtmidi-1.5.8-cp312-cp312-win_amd64.whl
update the requirements.txt file with the correct whl file.
pip install -r .\ensoniq\requirements.txt
# Both
# update the ensoniq/config.py with the correct MIDI_PORT_NAME (the last one takes effect)
pytest . # to run tests
python ensoniq\mirage_main.py