NOTE FOUNDRY

MP3 to MIDI: The Bugs, Version Hell, and Workarounds Nobody Documents

TL;DR — What Actually Works (and What Will Waste Your Afternoon)

Converting MP3 to MIDI is not a "click a button" task. Anyone who tells you otherwise is selling something. Here's the honest landscape as of 2026:

If you just need to capture a solo melody — a sung vocal, a clean lead — a browser-based tool like mp3frommidi.com does the job without installing anything: MP3 or WAV goes in, MIDI comes out, all processing stays in your browser. If you need to batch-process files, tune parameters, or integrate conversion into a pipeline, you'll need to install Basic Pitch or Omnizart locally — and that's where the pain begins. The rest of this article documents the specific errors, version combinations, and workarounds that people have hit and fixed, so you don't have to.


Pitfall #1: Basic Pitch + TensorFlow 2.16+ = '_UserObject' object has no attribute 'add_slot'

This is the single most common error people hit when installing Basic Pitch, and it's a TensorFlow version incompatibility — not a Basic Pitch bug. Multiple developers independently documented the same error and the same fix.

The Error

When you run basic-pitch output_dir/ audio.mp3 on a system with TensorFlow 2.16 or later, you get:

'_UserObject' object has no attribute 'add_slot'

The model loads, but inference fails because TensorFlow 2.16 changed the SavedModel loading API. The add_slot method that Basic Pitch's ICASSP 2022 model relies on was removed or restructured in TF 2.16.

Tested Environments and Versions

ComponentVersionResult
Python3.11.6
TensorFlow 2.15.1WorksBasic Pitch runs successfully
TensorFlow 2.16.1Failsadd_slot error
TensorFlow 2.18.0Failsadd_slot error
TensorFlow 2.15.0WorksDowngrade fix confirmed
OSWindows 11Same error on Windows

The Fix

Downgrade TensorFlow below 2.16:

pip install tensorflow==2.15.0

Or install Basic Pitch with the TensorFlow extra, which pins compatible versions:

pip install 'basic-pitch[tf]'

If you can't downgrade TF (e.g., other packages require 2.16+), a workaround is to disable GPU and force CPU-only inference:

import tensorflow as tf
tf.config.set_visible_devices([], 'GPU')

This doesn't fix the root cause, but in some environments it allows the model to load without hitting the add_slot path.

Why This Happens

TensorFlow 2.16 restructured the SavedModel loader. The add_slot attribute existed on optimizer objects in the Trackable hierarchy prior to 2.16. When Basic Pitch's pre-trained model was serialized (using TF ≤ 2.15), it captured references to this attribute. Loading that model under TF 2.16+ fails because the object reconstruction path changed.

This is not documented in Basic Pitch's official README as of 2026. The official install instructions still say pip install basic-pitch without any TF version warning.


Pitfall #2: librosa + numba = The Version Dependency Maze

Basic Pitch, Omnizart, and most Python audio-to-MIDI tools depend on librosa for audio feature extraction. librosa depends on numba for JIT compilation of DSP functions. numba depends on llvmlite for LLVM bindings. Get any one of these three versions wrong, and you get cryptic import errors.

The Three Most Common Errors

Error 1 — guvectorize signature missing:

TypeError: guvectorize() missing 1 required positional argument: 'signature'

This happens when a new version of numba changed the guvectorize API, but your librosa version hasn't been updated to match. The function signature requirement changed, and librosa's internal calls break.

Error 2 — numba.decorators module missing:

ModuleNotFoundError: No module named 'numba.decorators'

High versions of numba (e.g., 0.57.0) restructured their module layout. The numba.decorators submodule was removed or merged. Older librosa versions that import from numba.decorators directly will fail.

Error 3 — llvmlite binding attribute missing:

AttributeError: module 'llvmlite.binding.ffi' has no attribute 'register_lock_callback'

This typically happens on Python 3.7+ when an older llvmlite version is installed. The register_lock_callback attribute was present in newer llvmlite but not in the version that numba pinned.

The Version Compatibility Matrix

These are tested combinations:

PythonlibrosanumballvmliteStatus
3.60.7.00.48.00.31.0Works
3.70.7.20.53.00.36.0Works
3.60.6.00.48.0Works
Any0.9.x0.53.x – 0.56.xCompatible
Any0.10.x0.57.xCompatible
Any0.11.x+0.58.x+Compatible
Any0.7.00.57.0 (too new)Fails (Error 2)

The Debugging Process (Real-World)

One developer documented their full debugging journey:

  1. Tried pip install librosa → failed with guvectorize error
  2. Tried conda install librosa → also failed (conda resolved to incompatible versions)
  3. Uninstalled numba entirelypip uninstall numba
  4. Installed specific versionpip install numba==0.48.0 → worked

Another developer on a fully offline Ubuntu machine (Python 3.6, no internet access) had to:

  1. Download wheel packages on an external machine: pip download librosa==0.7.0 --python-version 3.6 --platform manylinux2014_x86_64
  2. Transfer to the intranet machine
  3. Install in strict order: llvmlitenumbalibrosa
  4. Install order matters — if you install librosa first, pip will pull in a numba version that doesn't match the available llvmlite

The Desperate Hacks

When you absolutely cannot get the right versions (e.g., locked into a specific Python version on a legacy system), two nuclear options exist:

Hack 1 — Hybrid llvmlite copy:

  1. Install a high llvmlite (e.g., 0.36.0)
  2. Copy its binding directory to a backup location
  3. Uninstall the high version, install the "correct" version (0.31.0)
  4. Copy the binding directory back

This works but may introduce subtle runtime issues. Use only as a last resort.

Hack 2 — Modify librosa source code:

  1. Find librosa's install location: pip show librosa
  2. Edit util/decorators.py
  3. Comment out the numba-related code

This disables numba JIT compilation, making librosa dramatically slower, but it will import and run.

The Tsinghua Mirror Tip

For users in China (or anyone hitting PyPI rate limits), use the Tsinghua University mirror to download packages faster:

pip install librosa -i https://pypi.tuna.tsinghua.edu.cn/simple

Pitfall #3: Omnizart — The Tool That Doesn't Run on Apple Silicon

Omnizart is an attractive alternative — it's a Python toolbox that bundles multiple SOTA models for automatic music transcription (AMT). It supports piano, pop music, drums, vocals, chords, and beat tracking. But it has its own set of issues.

ARM MacOS: Hard Incompatibility

Omnizart does not run on Apple Silicon (M1/M2/M3) Macs. This is a limitation of its underlying dependency chain, not something you can fix with environment variables or Rosetta. Your options:

Silent Model Download Failures

After installing Omnizart, you must download pre-trained model checkpoints:

omnizart download-checkpoints

If this fails (network issues, proxy, etc.), the error is often silent. You won't know until you try to transcribe and get:

Checkpoint file not found: .../variables/variables.data*

The fix is to re-run the download command, optionally specifying a custom path:

omnizart download-checkpoints --output-path /your/custom/path

Model Selection Matters

Omnizart offers different models for different music types. Using the wrong model produces garbage output:

# For pop music
omnizart music transcribe audio.wav --model-type pop

# For classical piano
omnizart music transcribe audio.wav --model-type piano

If you're transcribing a guitar solo with the piano model, expect wildly inaccurate results. The model types are not interchangeable.

Debugging Tips

Enable verbose logging to diagnose issues:

export OMNIZART_LOG_LEVEL=DEBUG
omnizart music transcribe audio.wav

Check system dependencies before filing bug reports:

which ffmpeg
which fluidsynth
python -c "import tensorflow as tf; print(tf.__version__)"

Pitfall #4: Conversion Quality Issues That Aren't Bugs

Even when your tools install and run correctly, the MIDI output can be wrong in ways that feel like bugs but are actually algorithmic limitations. These issues come from real-world debugging experience.

BPM Doubling

A 70 BPM song gets transcribed as 140 BPM. This is a classic pitch-tracking failure mode: the algorithm detects note onsets at twice the actual rate, or the tempo estimation picks the doubled BPM because the note density pattern repeats at half the actual period.

Fix: Always verify the output BPM against the source. If it's exactly double, halve it in your DAW. If it's exactly half, double it. This is a known limitation of automatic tempo estimation, not something you can fix by tuning parameters.

Don't Remove Silence Before Conversion

It's tempting to trim leading silence or quiet passages from your audio file before running it through a converter. Don't. Removing silence changes the time base of the audio relative to the original recording, which means your MIDI notes will be offset from where they should be. If you later try to align the MIDI with the original audio (for mixing, overdubbing, or synchronization), everything will be misaligned.

Don't Over-Quantize

After conversion, your DAW's quantize function will offer to snap MIDI notes to a grid. Resist the urge to quantize at 100% strength. Over-quantization destroys the natural timing variations (groove, swing, human feel) that the converter actually captured correctly. Start with 30-50% quantize strength and adjust by ear.

Parameter Tuning for Basic Pitch

Basic Pitch exposes several parameters that significantly affect output quality:

ParameterDefaultWhat It DoesWhen to Adjust
--onset-threshold0.5Note onset detection sensitivityLower for quiet notes; raise for noisy audio
--frame-threshold0.3Minimum probability for note sustainLower = more notes (including false positives)
--minimum-note-length127.70msFilters out notes shorter than thisRaise to eliminate noise artifacts
--minimum-frequencyLowest detectable pitch (Hz)Set to limit range (e.g., 80 Hz for guitar)
--maximum-frequencyHighest detectable pitch (Hz)Set to limit range (e.g., 1200 Hz for vocals)

If your MIDI output has too many spurious notes, raise --onset-threshold to 0.6-0.7 and --minimum-note-length to 200ms. If notes are being missed, lower --onset-threshold to 0.3-0.4.


Pitfall #5: Packaging Basic Pitch with PyInstaller

If you're building a desktop application that bundles Basic Pitch, you'll hit PyInstaller issues. One developer documented their experience on Windows with Python 3.11.15:

One-file mode (--onefile):

One-directory mode (--onedir):

The recommendation: use --onedir mode for Basic Pitch applications. The --onefile mode's temp-directory unpacking breaks TensorFlow's model loading path because relative paths to model assets resolve incorrectly.


Quick Reference: Installation Commands That Actually Work

Basic Pitch (with TF version fix):

pip install 'basic-pitch[tf]'
# If you get the add_slot error:
pip install tensorflow==2.15.0
basic-pitch output_dir/ your_audio.mp3

librosa (with correct dependency order):

pip install llvmlite==0.31.0    # adjust for your Python version
pip install numba==0.48.0        # must match llvmlite
pip install librosa==0.7.0       # must match numba

Omnizart (on Linux/Intel Mac/WSL2):

sudo apt-get install libsndfile-dev fluidsynth ffmpeg
pip install numpy Cython
pip install omnizart
omnizart download-checkpoints
omnizart music transcribe audio.wav --model-type piano

If all of this sounds like more trouble than it's worth for a single file — it is. For a clear solo melody, a browser-based converter like mp3frommidi.com runs the conversion entirely on your device: MP3 or WAV in, MIDI out, nothing installed, nothing uploaded. It won't separate a full mix into instruments or recognize chords, but for one melody at a time, there's nothing to install and no version matrix to memorize.