Audio → MIDI

Audio to MIDI Converter

Can audio to MIDI work in real time as you play?

No — this converter cannot emit MIDI while you play, and the reason is the design, not the speed of your machine. The decision about whether a frame contains a note is made against a single threshold computed from every frame in the whole file: the quietest tenth of all frame loudnesses, plus a fraction of the distance to the loudest frame. That number does not exist until the file has ended. We converted the exact same first second of audio five times, changing only what came after it, and the opening second came back with 2, 2, 3, 1 and 2 notes as the whole-file gate took the values 0.30800, 0.04497, 0.29941, 0.40075 and 0.04497. With a loud tail, both opening notes were deleted and only one phantom note remained. A note that has not happened yet can erase a note that already has, so nothing in the first second is final while the recording is still running. What this page does instead is quick and private: record a take, drop the finished file here, and get a Standard MIDI File in your browser, with no upload. The rest of this page is the measurement that explains why that boundary is real rather than a missing feature.

What "real time" would require, in three measured numbers

Before blaming hardware, it helps to write down the smallest delay that this analyser could ever have, even if it ran infinitely fast. The signal is resampled to 22 050 Hz and read with a 2048-sample window stepped 256 samples at a time. Three consequences follow, all measured from the source and from real runs on this site:

We then fed isolated tones of 40, 60, 80, 100, 120, 140, 150, 155, 160, 200, 300 and 500 ms through the real detector. Everything up to 150 ms returned zero notes; the first note appeared only at 155 ms (pitch 60, onset 0.0116 s, velocity 113). That 155 ms is not a latency budget we can tune down by buying a faster CPU — it is the sum of a forward-looking window and minimum-length gates. A live display that updated in under 155 ms would have nothing to show.

There is a separate, more fundamental limit on top of it. The window and the note-length gates are local; the threshold that decides which frames count as notes is not.

One gate belongs to the entire file

The grouping step in dsp.js computes a noise floor as the 10th percentile of the RMS of all frames, then sets the voice gate to floor + (rmsMax − floor) × (0.32 − 0.30 × sensitivity). At sensitivity 60 the multiplier is 0.14. For a 2.0-second four-note phrase the analyser produces 165 frames, the floor is 0.29586, the loudest frame is 0.32121, and the gate is therefore 0.29941. Every one of the 165 frames is compared with that single number, including frame 0.

Schematic of the whole-file gate: a 2.0-second clip produces 165 analysis frames spaced 11.609977 ms apart; all 165 frame RMS values are sorted, the 10th percentile gives a floor of 0.29586, the loudest frame is 0.32121, and at sensitivity 60 the gate is 0.29941. Frame 0 at 0.000 seconds is judged by the same gate as frame 164 at 1.904 seconds, so the last frame influences the first note.
Figure 1. The gate is one number for the whole recording. Frame 0 has to wait for frame 164 before it can be judged, which is the opposite of a streaming design. A 0.1 s slice has just one frame, so its gate is that frame's own RMS (0.30803) and the five-frame minimum makes any note impossible.

This is why "just process each chunk as it arrives" is not a small engineering change. The last quiet second of a take drags the 10th percentile down; the last loud second pushes the maximum up. Both rewrite the threshold used at the beginning. You can see the mechanism from the source (dsp.js is 12 466 bytes, midi.js is 2 768 bytes) without running anything, but the more convincing evidence is to hold the beginning fixed and change only the ending.

The future changes the past: five endings, one identical opening

We built a one-second head containing C4 then D4, then appended five different tails. The first second of audio is byte-identical in all five files. Each file was converted as a complete recording with identical settings (polyphonic, sensitivity 60, shortest note 60 ms). Here is what came back inside that unchanged first second:

Five conversions of the same first 1.0 s. Only the appended tail differs; the gate is always computed from the whole file.
Appended tailFramesWhole-file gateNotes in first 1.0 sOnsets (s)
None (1.0 s file)790.3080020.0000, 0.5108
1 s silence1650.0449720.0000, 0.4644
1 s normal second half1650.2994130.0000, 0.4992, 0.9985
1 s loud tone1650.4007510.9288
5 s silence5090.0449720.0000, 0.4644
Five rows, each with the identical first second of audio but a different tail. No tail gives a gate of 0.30800 and two notes at 0.0000 and 0.5108 seconds. One second of silence gives a gate of 0.04497 and moves the second note to 0.4644 seconds. A normal second half gives a gate of 0.29941 and three notes. A loud one-second tone raises the gate to 0.40075, erases both opening notes and leaves only a phantom pitch 67 at 0.9288 seconds with velocity 127. Five seconds of silence repeats the one-second-silence result.
Figure 2. The same opening, five different answers. Adding silence moved the second note 46 ms early; adding a loud tone deleted both opening notes and left one phantom G4 (MIDI 67, velocity 127) that belongs to the tail. The 92.88 ms window is what lets a frame at 0.9288 s read into audio starting at 1.000 s.

Three things in that table are worth isolating. First, silence does not just add quiet frames: it collapses the 10th percentile to 0.00000, so the gate falls from 0.29941 to 0.04497, and an earlier frame with RMS 0.05540 qualifies as the note's start. That is the same mechanism measured in detail on why a first note lands early, but here it is being used as a causality test. Second, a loud tail raises the loudest frame to 0.91558, lifting the gate to 0.40075 — above the entire opening second — so both real notes vanish. Third, the five-second silence tail gives exactly the same result as the one-second silence tail, because the gate depends on the distribution of frames, not on how long you keep waiting.

The consequence for live tracking is blunt: at the moment a performer plays the first note, the converter cannot know whether a loud chord is coming in three seconds. If one is, that first note may not exist in the final result. A streaming converter would have to commit to the note before the evidence that removes it arrives. The seven-column note list described in audio to notes shows the same seconds to four decimals, and it is only available after the whole pass finishes.

Feed the same audio in growing chunks and it rewrites its own beginning

A natural workaround is to convert repeatedly: take the audio so far, convert that as a file, then convert again once more has arrived. We ran exactly that experiment. One four-note phrase (C4, D4, E4, G4) was sliced into six prefixes, and each prefix was analysed and segmented as a complete file in its own right:

Six prefixes of the same clip, each converted independently. Every column is a real run, not a simulation of a stream.
PrefixFramesWhole-file gateNotesPitchesOnsets (s)
0.1 s10.308030——
0.2 s100.317241600.0116
0.3 s180.31724260, 600.0116, 0.1509
0.5 s360.317461600.1625
1.0 s790.30800260, 620.0000, 0.5108
2.0 s1650.29941460, 62, 64, 670.0000, 0.4992, 0.9985, 1.4977
Left: six prefixes of one clip return 0, 1, 2, 1, 2 and 4 notes, with the whole-file gate drifting 0.30803, 0.31724, 0.31724, 0.31746, 0.30800, 0.29941. Right: whole-file analysis wall time grows with length at 21.1, 32.3, 127.0, 201.3, 403.8, 610.5 and 1163.2 milliseconds for files of 1, 2, 5, 10, 20, 30 and 60 seconds, over 79 to 5160 frames.
Figure 3. Two independent measurements. Left: the number of notes is not monotone in how much audio has arrived — the 0.3 s prefix returns two notes, the 0.5 s prefix returns one. Right: the batch analysis pass scales with file length, but no amount of speed removes the whole-file gate.

Read the note counts in order: 0, 1, 2, 1, 2, 4. A live display built this way would show two notes at 0.3 seconds, then take one of them back at 0.5 seconds. The first onset is not stable either: 0.0116, 0.0116, 0.1625, 0.0000, 0.0000 seconds. The 0.1 s prefix produces no note at all because one frame is fewer than the five-frame minimum. This is the reproducible proof that a growing file keeps re-deciding its own past, and it is why the only honest output is one produced after the recording stops.

Why a faster computer does not fix it

The analysis pass is explicitly a batch job. The source contains one setTimeout call, used to split the work into chunks of 300 frames so the browser stays responsive; the promise resolves only after the last frame is done. Segmenting — the cheap step that reapplies the gate when you move a slider — is separate. We timed the whole analyze() pass on files from 1 to 60 seconds:

Wall-clock time of one complete analyze() pass on this machine. Frame counts are from the real analyser.
File lengthFramesanalyze() timems per second of audio
1 s7921.1 ms21.08
2 s16532.3 ms16.17
5 s423127.0 ms25.41
10 s854201.3 ms20.13
20 s1715403.8 ms20.19
30 s2576610.5 ms20.35
60 s51601163.2 ms19.39

A one-minute recording takes about 1.16 seconds to analyse, and the cost is roughly linear at 19–25 ms per second of audio on this machine. That is fast, and it is also beside the point: even if the number were zero, the gate would still be the 10th percentile of all 5160 frames, so the first note could not be released until the last frame had been read. Slow hardware is a legitimate complaint about a live pitch tracker; here it is not the binding constraint. The one-file-at-a-time workflow is a consequence of the same batch design.

Deterministic, local, and not a streaming model

It is worth being precise about what this engine is, because "AI audio to MIDI" is often assumed to mean a model inferring notes frame by frame. A read of the shipped code and ten repeated runs give a different picture:

Determinism on its own would not forbid a live implementation — a fixed streaming algorithm can be perfectly repeatable. The point is what the repeatability is attached to: the same whole file always gives the same answer, because the answer is a function of a statistic computed over the entire recording. There is no per-frame state that could be emitted as a provisional note, and no model that could be allowed to change its mind gradually. That is a different architectural commitment from a live pitch-to-MIDI tracker, and it is why the honest description of this tool is "fast offline conversion in your browser", not "real-time tracking".

Where that leaves you: three ways to go

Not every job belongs here, and it saves time to say which is which. Each line below hangs off a number measured on this site rather than a general claim about the market.

If you are starting from a microphone rather than a file, what this converter does with a recording covers the record-first workflow and how room noise changes the note list once the take is a file.

What about tools that advertise live tracking?

They are solving a different problem with a different architecture, usually a local pitch tracker with a short fixed buffer. We have not measured their latency or accuracy on this site, so we will not quote figures for them. The measurements above describe one specific converter — this one — and are reproducible from its own source.

How to reproduce the causality test yourself

This takes about two minutes and needs no special equipment:

  1. Record or synthesise a one-second phrase with two clear notes, and keep a copy of that exact first second.
  2. Make a second file by appending one second of silence, and a third by appending one second of a loud sustained tone.
  3. Convert all three here with identical settings, download the CSV for each, and compare the first two start_sec values.

On our phrase the first file gave 0.0000, 0.5108, the silence version gave 0.0000, 0.4644, and the loud-tail version gave neither note. The audio at the start never changed. That is the whole argument in one experiment, and it is also a useful habit: trust a published number only when you can rebuild the input that produced it.

Questions people actually ask

Can you convert audio to MIDI in real time?

Not with this converter: the first note is not decided until the last frame is read. The voice gate is the 10th percentile of all frame loudnesses, and on a 2.0-second, 165-frame file it works out to 0.29941. Change only the tail of that file and the gate becomes 0.40075, which deleted both notes in the unchanged opening second. A live system would have to output those notes before the tail existed. The shortest isolated tone that produced any note here was 155 ms, so even the theoretical floor is far from instantaneous.

Is audio to MIDI an offline process?

Here, yes — but a short one: a 60-second file takes 1163.2 ms to analyse. The analysis pass walks all 5160 frames in chunks of 300 before it resolves, at roughly 19–25 ms per second of audio on this machine. There are 0 network calls in the shipped code, so "offline" describes both the batch architecture and the fact that the file never leaves your browser.

Does a faster phone or computer make it real time?

No. Speed changes the 1163.2 ms, not the dependency on the whole file. Analysis time scales close to linearly (21.1 ms at 1 s, 403.8 ms at 20 s, 1163.2 ms at 60 s), so a faster device shortens the wait. It cannot make a 10th-percentile gate computable from a prefix: six prefixes of one clip returned 0, 1, 2, 1, 2 and 4 notes as the gate drifted from 0.30803 to 0.29941. What works on a phone covers the on-device time cost separately.

Does trimming the end of a file change the notes at the beginning?

Yes, and by more than a rounding error. With the first second byte-identical, appending one second of silence lowered the gate from 0.30800 to 0.04497 and moved the second note from 0.5108 s to 0.4644 s; appending a loud tone raised the gate to 0.40075 and erased both opening notes, leaving a single phantom pitch 67 at 0.9288 s. Trim and export a tight region before converting, then fix velocity and timing in the DAW.

Convert a finished file now — runs in your browser, nothing is uploaded.

Related guides