Audio → MIDI

Audio to MIDI Converter

Audio to notes: what you get before it becomes MIDI

The short answer: “audio to notes” gives you a note list, and on this converter that list is the real product — the MIDI file is only one of two ways of writing it down. Both downloads are generated from a single array in memory, and the one that stays closest to what the analyser actually measured is the .csv, not the .mid. We ran a four-note melody through the site’s own dsp.js and midi.js: the CSV came out at 197 bytes with 7 columns (note name, decimal seconds to 0.1 ms, duration, index, velocity), while the MIDI came out at 89 bytes with the same four pitches re-encoded as note-on and note-off events on a 480 ticks per quarter note grid. The MIDI throws away the note names and the seconds, and rounds every start and end to the nearest tick — 1.0417 ms at 120 BPM, which cost up to 0.5003 ms per onset in that clip. It adds things the CSV never had: one tempo event, one 4/4 time-signature event, and an end-of-track marker. So if what you searched for was the notes themselves, download the CSV; if you need something a DAW can play, download the .mid. The two are the same observation in two different formats, and the difference between them is measurable rather than a matter of taste.

One array in memory, two files on disk

The most useful fact about “audio to notes” on this site is that there is only ever one list of notes. The pipeline is short:

  1. DSP.analyze() decodes your file, resamples it to 22 050 Hz and reads it with a 2048-point FFT stepped 256 samples at a time — one frame every 11.609977 ms, or 86.13 frames per second.
  2. DSP.segment() groups those frames into notes[], an array of plain objects with exactly four fields: { pitch, start, end, vel }. Times are in seconds, pitch is a MIDI number, velocity is an integer from 1 to 127.
  3. Two writers then read that same array: MIDI.buildCsv(notes) for the .csv and MIDI.buildMidi(notes, bpm) for the .mid. That is why the download buttons in the interface both say the result is ready at the same moment — there is no second detection pass and no conversion from one file into the other.

Here is the actual first object of a measured four-note clip (C4, E4, G4, C5, each about half a second), followed by what each writer did with it:

One note, four representations. Measured on this site’s engine, 120 BPM, polyphonic, sensitivity 60.
valuein notes[]in the .csv rowin the .mid events
pitch60midi_note = 60, note_name = C40x90 data byte 60 on note-on
start0 sstart_sec = 0.0000note-on at tick 0
end0.4179591836734694 send_sec = 0.4180note-off at tick 401
velocity113velocity = 113data byte 113 on both note-on and note-off
durationnot storedduration_sec = 0.4180 (computed)not stored — implied by the on/off pair
row numberarray positionindex = 1not stored
namenot storednote_name = C4 (derived)not stored — no text event exists

Two of the seven CSV columns — duration_sec and note_name — are computed at write time, not measured by the detector. That matters: a spread of durations in the CSV is a derived quantity, and a note name like C#4 is a spelling choice made by a lookup table, not something the audio contained. If you are comparing two conversions, compare start_sec and midi_note, which are the primary observations. Diffing two CSVs row by row is the fastest way to see whether a format or a setting changed anything at all.

The seven columns, measured

This is the complete CSV produced by that four-note clip, unedited. It is plain text with one header line and one line per note:

index,midi_note,note_name,start_sec,end_sec,duration_sec,velocity
1,60,C4,0.0000,0.4180,0.4180,113
2,64,E4,0.4992,0.9172,0.4180,113
3,67,G4,0.9985,1.4164,0.4180,113
4,72,C5,1.4977,1.9156,0.4180,113
What each column holds, and where its precision comes from.
columnsourceresolution / rule
index1-based row numberrows are sorted by start_sec, then by pitch
midi_notedetected pitchinteger 0–127; the detector searches 55–1800 Hz in monophonic mode and keeps peaks up to 4000 Hz in polyphonic mode
note_namelookup from pitchsharps only: C C# D D# E F F# G G# A A# B — flats never appear
start_secframe index × frame timetoFixed(4) → 0.1 ms display, max rounding 0.05 ms
end_secframe index × frame timesame 0.1 ms display
duration_secend − startderived, not measured
velocityloudness of the note’s framesround(20 + (dB + 60) × 100/54), clamped to 1–127

The times are not arbitrary decimals. Divide them by the frame time and they come out as whole frames: 0.4992 s = 43 frames, 0.9172 s = 79 frames, 0.9985 s = 86 frames, 1.4977 s = 129 frames. So the note list is a grid-locked object with a granularity of 11.610 ms; the CSV merely prints that grid in seconds. Where the note boundaries land explains what moves those boundaries if you switch on snapping — but with snapping off, every start and end in the CSV is an integer frame.

The name column deserves one warning. Because the lookup table has twelve sharp names and no flats, a detected pitch of 58 is always printed A#3 and never Bb3. That is not a music-theory decision: the file contains no key-signature event, so there is no information in it from which an enharmonic spelling could be chosen. Does audio to MIDI tell you the key? covers the same absence from the other side.

What the MIDI adds, and what it takes away

Parsing the bytes of a two-note export gives 71 bytes and two MTrk chunks. Track 1 holds exactly three meta events; track 2 holds the note events plus one end-of-track marker. Nothing else is in the file.

Fields present in only one of the two downloads. Verified by parsing the exported bytes.
only in the .csvonly in the .mid
index (row order)tempo meta event 0x51
note_name (e.g. C4)time-signature meta event 0x58, always 4/4
start_sec, end_sec in decimal secondsend-of-track 0x2F on both tracks
duration_sec as its own columndelta-time ticks in variable-length encoding
human-readable text, opens in a spreadsheeta separate note-off event for every note
byte-identical at any BPMa channel nibble and two track chunks

Shared: the pitch (a MIDI number in the CSV, the data byte of a 0x90 event in the MIDI), the velocity (an integer in the CSV, the second data byte in the MIDI), and the note count (rows versus note-on events). In neither file: a key signature (0x59), text, lyric, track name, program change, instrument, bar numbers, chord names, or any of the original audio. A two-note export’s complete meta-event list is 0x51, 0x58, 0x2F, 0x2F — nothing else. That is why lyrics never turn up in either download; why words do not become notes covers the detection side, and what is actually inside a MIDI file walks the byte layout event by event.

The practical consequence: if you lose the tempo you typed, the CSV cannot tell you what it was — buildCsv takes one argument and has no BPM parameter at all. The .mid does carry it, but only as a meta event that does not move any note; the tempo a converter writes is your input verbatim, exact to within the microsecond rounding of the tempo field.

Timing: 0.1 ms versus one tick

The CSV prints seconds with four decimal places. The MIDI stores times as integer ticks, computed as round(seconds × 480 × BPM / 60). One tick is therefore worth 60 / BPM / 480 seconds, and that value changes with the tempo you typed:

Tick duration and the largest onset rounding error measured on the same four-note clip.
BPM1 tick (ms)half-tick ceiling (ms)measured max onset error (ms)CSV error (ms)
602.08331.04170.7710≤ 0.05
901.38890.6944—≤ 0.05
1201.04170.52080.5003≤ 0.05
1400.89290.4464—≤ 0.05
1730.72250.36130.1453≤ 0.05
2000.62500.31250.2920≤ 0.05

The measured column is not a general bound — it is the worst case across those four particular onsets, which is why 173 BPM happens to land better than 200 BPM. The general bound is the half-tick ceiling beside it. The CSV’s 0.05 ms ceiling does not depend on BPM at all. So the plain-text file is the higher-fidelity record of timing, even though the binary file is the one people assume is exact.

There is a neat symmetry in the numbers. Take a note starting at 0.5 s and change the BPM: it becomes tick 240 at 60 BPM, 400 at 100, 480 at 120, 560 at 140, 692 at 173 and 800 at 200. Convert each tick back to seconds using its own tempo and every one returns 0.500000 s — the tick count and the tempo scale in opposite directions and cancel. The CSV of that same note list is byte-for-byte identical at all six tempos. In other words, BPM changes how the .mid writes the time, not when the note happens, and it does not touch the note list at all.

Three places where the two files stop agreeing

Same array, different encodings — but the encodings are not perfectly interchangeable. We fed the writer deliberately awkward note lists to find out where they diverge:

Edge cases run through buildCsv and buildMidi at 120 BPM.
casewhat the .csv sayswhat the .mid says
zero-length note at 1.0000 s1,60,C4,1.0000,1.0000,0.0000,90note-on @ tick 960, note-off @ tick 961 — the writer forces a minimum length of one tick (1.0417 ms)
two overlapping C4 notes (0–1 s and 0.5–1.5 s)two distinct rows, velocities 100 and 80on@0, on@480, off@960, off@1440 — a DAW pairing offs with ons can read this as one note
the four-note melody4 rows ordered by start time8 note events ordered by absolute tick; 3 note-offs occur before the final note-on

The first case cannot arise from this converter’s own analyser — segmentation enforces at least one frame per note, which is 11.610 ms, so a genuine zero-length note is impossible. It only appears if a note list arrives from somewhere else, and it shows the MIDI writer’s safety net: if (off <= on) off = on + 1. The second and third cases are ordinary. The CSV is sorted once, by start time then pitch; the MIDI sorts all note-on and note-off events together by tick, so its event order is not its row order. Overlapping notes of the same pitch are the messiest result: two rows in the CSV become an ambiguous on/on/off/off sequence in the MIDI, which is why cleaning up a converted MIDI starts by deleting overlapping and orphaned notes.

Size: the note list costs about four times as many bytes as the MIDI

Because the CSV repeats seven human-readable fields as text for every note and the MIDI uses three-byte events with variable-length delta times, the same list takes very different amounts of space. We generated note lists of increasing size and ran both writers on each:

Byte counts for identical note lists, 120 BPM. Measured by calling the writers directly.
notes.csv bytes.mid bytesratioCSV / noteMIDI / note
197621.56×97.0062.00
2130711.83×65.0035.50
103901432.73×39.0014.30
501 7615033.50×35.2210.06
2007 2341 8533.90×36.179.27
1 00037 8919 0534.19×37.899.05
3 000117 55327 0534.35×39.189.02

Both settle into a linear per-note cost — roughly 39 bytes per row for the CSV and 9 bytes per note for the MIDI once the fixed header is amortised. The ratio settles near 4.3×. Neither file is large in absolute terms: a 3 000-note list is about 118 KB as CSV and 27 KB as MIDI. How big an audio-to-MIDI file can get works through the MIDI side on its own, including why the note count, not the audio length or bitrate, is what decides the size.

Which one you should keep

Choose by what you are about to do with the list.
if you are about to…keepwhy, in measured terms
check whether a note was found, or compare two takes.csv7 columns, note names, 0.1 ms seconds, one row per note — nothing is hidden in a binary event stream
open the result in a DAW or play it back.midnote-on / note-off events on a 480 TPQ grid, plus tempo and 4/4 time signature
archive the conversion and re-check it laterboththe CSV preserves the seconds and names the MIDI discards; the MIDI preserves the tempo the CSV cannot store
work out the timing of a note boundary.csvstart/end are integer frames of 11.609977 ms; ticks introduce up to half a tick of rounding
send notes to a notation or scoring app.midit is the format those apps import — but note it carries no key signature, so the app will have to guess the spelling

Downloading both is free and takes one click each; the two files are built from the same array in the same page, so there is no extra analysis cost. If you are pushing several files through, batch converting several files to MIDI explains why it is a one-file-at-a-time workflow and what each file costs in device time.

Should you stay here, or walk away?

Three lines, each with a number from this engine rather than a promise:

The middle and third lines are the honest ones: a note list, however precise, is a flat list. It has one channel, one tempo, one time signature and no separation between instruments — what a full mix comes back as is one undifferentiated set of notes. If your job needs parts, lyrics or expression, no amount of editing the CSV will produce them.

How to check this on your own file

None of the numbers above require you to trust us. This takes about two minutes:

  1. Convert a short, clean clip here with the default settings (polyphonic, sensitivity 60, shortest note 60 ms).
  2. Download both the .mid and the .csv. Row count in the CSV should equal note-on count in the MIDI.
  3. Open the CSV in any spreadsheet. Check that start_sec divided by 0.011610 lands near a whole number — that is the analysis grid showing through.
  4. For any row, compute round(start_sec × 480 × BPM / 60). That is the tick the .mid uses for that note-on. The difference between the tick converted back to seconds and start_sec is the rounding cost in the table above.
  5. Change the BPM and export the CSV again. It will be byte-identical; the .mid’s tick numbers will change but the notes will play at the same times.
  6. Look for # names and no flats, and confirm there is no tempo or time-signature column anywhere. That is the boundary between a note list and a MIDI file.

The same two-file check works for format comparisons: does sample rate change what the MIDI picks up uses a CSV diff to show that 44.1 kHz, 48 kHz and 22.05 kHz inputs return an identical note list.

Questions people actually ask

Is “audio to notes” the same thing as “audio to MIDI”?

Nearly, but the emphasis is different. Audio to MIDI asks for a file a DAW can open; audio to notes asks for the list itself. On this converter both come from the same notes[] array: the .csv is a near-verbatim printout of it with seven columns, and the .mid is a re-encoding into tick-based note events. Neither is derived from the other. If you want to read, count or compare what was detected, the CSV is the answer; if you want to play or edit it in a sequencer, take the .mid.

Can I open the note list in a spreadsheet?

Yes. It is plain UTF-8 text with a single header line, one row per note and seven comma-separated columns, so Excel, LibreOffice, Google Sheets, a text editor or a Python script will all read it directly. Times are decimal seconds, not bars or ticks, so no import dialog is needed. That is the whole reason the CSV exists alongside the MIDI.

Why does the CSV say C#4 when my score says Db4?

Because note_name is derived from the pitch number using a twelve-name table that contains sharps only: C C# D D# E F F# G G# A A# B. There are no flats in it. Deciding between C# and Db requires knowing the key, and the export contains zero key-signature events, so neither the note list nor the MIDI has the information needed to make that call. You spell it in your notation app after import.

Which file has more accurate timing?

The CSV, slightly. It prints seconds to four decimal places (0.1 ms, with at most 0.05 ms of display rounding) and that precision does not depend on tempo. The MIDI rounds every start and end to an integer tick: 1.0417 ms at 120 BPM, 2.0833 ms at 60 BPM, 0.6250 ms at 200 BPM, with a worst case of half a tick. On our four-note clip the largest measured onset error was 0.7710 ms at 60 BPM and 0.5003 ms at 120 BPM. In musical terms both are far below what a listener can hear; in analysis terms the CSV is the faithful one.

Does changing the BPM change my CSV?

No. buildCsv(notes) takes one argument and has no BPM parameter, so the same note list produces a byte-identical CSV at every tempo. The MIDI does change: a note at 0.5 s is tick 240 at 60 BPM and tick 800 at 200 BPM, but because the tempo event scales with it, both play back at exactly 0.500000 s. BPM changes the notation of time, not the time.

Can I load a CSV back into this converter and turn it into a MIDI?

No. The page reads audio files only; there is no CSV import path. It analyses sound into a note list and writes that list out as .mid and .csv. If you already have a note list and want a MIDI file from it, a spreadsheet-to-MIDI script or a DAW’s import function is the right tool. Everything on this page runs from an audio file you choose.

Why does the MIDI have note-offs before the last note-on?

Because the two files sort differently. The CSV is sorted once by start time, then pitch, and keeps each note on one row. The MIDI builds a single event stream — a note-on and a note-off for every note — and sorts all of them by absolute tick. On our four-note clip, three note-offs land before the final note-on. That is normal event ordering, not lost notes; the note count is unchanged.

What is the largest note the list can hold?

The list itself stores MIDI numbers, so the format allows 0–127. The detector is narrower: it searches 55–1800 Hz for a fundamental in monophonic mode and keeps polyphonic peaks up to 4000 Hz, and it keeps at most 6 spectral peaks per frame. Below 181 Hz a semitone is narrower than one 10.77 Hz FFT bin, so low notes are the least reliable part of the list. Those are properties of the analyser, not of the CSV or the MIDI format.

Convert a file now — runs in your browser, nothing is uploaded. Download the .csv for the notes and the .mid for your DAW.

Related guides