MIDI converter: stereo routing experiment
Tested: 2026-09-27 00:11 Asia/Shanghai = 2026-09-26 16:11 UTC.
Source: new, original synthetic sine tones; no human performance or third-party samples.
You may use, modify and redistribute these generated fixtures, scripts and MIDI outputs to reproduce this experiment.

QUESTION
Does this site's audio-to-MIDI conversion preserve the original left/right routing?
Eight isolated notes alternate between the left and right audio channels. A second WAV is the exact arithmetic average of those channels. This is not a new MIDI-to-audio engine or a real-music accuracy benchmark.

ENVIRONMENT USED
Python 3.12.14; mido 1.3.3; ego-browser CLI 0.5.1.13; Chromium 152.0.7977.54; browser user-agent reports Chrome/152.0.0.0; Ego Node v24.18.1. Desktop macOS, not a physical phone. Python generator uses only standard-library modules. mido is needed only for measurements.

REPRODUCE INPUTS AND RECHECK THE SAVED OUTPUTS
Unzip this bundle and run from that directory:
  python3 -m venv .venv
  .venv/bin/python -m pip install -r requirements.txt
  .venv/bin/python generate.py --out run
To remeasure the saved outputs, copy alternating-stereo.mid and downmix-mono.mid from this bundle into run/, then run:
  .venv/bin/python measure.py --dir run
This rechecks existing outputs; it does not constitute a fresh browser conversion.

REPEAT A FRESH CONVERSION MANUALLY
1. Open https://getmp3tomidi.com/ in a compatible desktop browser.
2. Choose run/alternating-stereo.wav. Wait for automatic conversion.
3. Under Shape the notes, use quiet-note filter -40 dB, minimum note length 40 ms, transpose 0.
4. Download MIDI and save it as run/alternating-stereo.mid.
5. Repeat for run/downmix-mono.wav, saving run/downmix-mono.mid.
6. Run .venv/bin/python measure.py --dir run.
Do not change the MIDI or shift its time axis before measuring.

AUTOMATE THE SAME BROWSER CONVERSION
This requires the installed Ego browser and ego-browser CLI, which are not Python dependencies.
The browser-test.mjs script exports run({spaceId,pageLabel,outputDirectory}). Use absolute paths, and use your own active task space. Example:
  ego-browser nodejs <<'JS'
  const task = await taskSpace('Reproduce MIDI converter stereo experiment');
  console.log({spaceId:task.spaceId});
  const {run} = await import('file:///ABSOLUTE/PATH/browser-test.mjs');
  await run({spaceId:task.spaceId,pageLabel:'p1',outputDirectory:'/ABSOLUTE/PATH/run'});
  await task.finish({keep:[]});
  JS
Replace both ABSOLUTE/PATH placeholders with the folder where you extracted the bundle. The generator must have already produced both WAVs in run/.

ACTUAL EXECUTION FOR THIS RECORD
Within the project, Python commands used benchmark/.venv/bin/python instead of .venv/bin/python:
  benchmark/.venv/bin/python dist/examples/midi-converter/generate.py --out dist/examples/midi-converter
  benchmark/.venv/bin/python dist/examples/midi-converter/measure.py --dir dist/examples/midi-converter
The browser command imported browser-test.mjs from that same directory and invoked run with active spaceId 42, pageLabel p2 and that directory as outputDirectory.
The first Blob-fetch attempt was blocked by the site's Content Security Policy. The completed script uses the actual Download MIDI link and browser download event; it does not reconstruct or re-encode the MIDI. Both successful downloads were parsed after saving.

SETTINGS AND VERSION IDENTITY
browser-results.json records time, browser user-agent, settings and observed note events. Its versions object maps exact SHA-256 hashes to live app.js, pitch-engine.js, midi-file.js and audio-limits.js. Those files were fetched before and after the test and hashes matched. Their byte-for-byte snapshots are saved as engine-*.js. These are archived engine source files for inspection, not a complete standalone website. The script reruns the public site: future engine/browser changes can change results; check the hashes rather than assuming identical behavior.

MEASUREMENT
measure.py reads WAV headers and sample integers, verifies mono = (left + right)/2 exactly, parses actual MIDI using file tempo, compares full output bytes, and counts note-ons, tracks, MIDI channels and control messages. Pan is controller 10 (CC10). Maximum onset error compares each matching-pitch event with the programmed onset without a global shift. Full event lists and formulas are in measurements.json and the script.
Observed: both outputs contain pitches 60,64,67,72,69,65,62,60; Type 0, one track, 960 ticks/beat, channel 0 (zero-based), no control-change events. Output bytes match exactly. Maximum absolute onset error is about 2.6042 ms in both; not a universal accuracy claim.

SCOPE
The fixture contains isolated tones with silence, not simultaneous left/right instruments. No claim about chords, room recordings, real songs, DAW import or a physical phone. MIDI-to-audio was not run in this test: the site has no WAV/MP3 export. The existing /midi-to-wav article separately documents an older external-renderer test, which is not counted as this experiment.
