{"_id":"@amplib/music-detection","_rev":"2-d860ddfea898679cdc2a98aa595f6f64","name":"@amplib/music-detection","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@amplib/music-detection","version":"0.1.0","author":{"name":"Another Machine"},"license":"Apache-2.0","_id":"@amplib/music-detection@0.1.0","maintainers":[{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"}],"homepage":"https://amplib.app/music-detection","bugs":{"url":"https://github.com/another-machine/public-library/issues"},"dist":{"shasum":"e73a2257d977f1ca7675f50928a69ef411a8e1b7","tarball":"https://registry.npmjs.org/@amplib/music-detection/-/music-detection-0.1.0.tgz","fileCount":6,"integrity":"sha512-y/J7ulVTSDc0+t0AQDYUPEiACOl4ACrQJTndyyPVZAva5hKtQHUi8DbhHxwaQXfkKnuPM/D6LgOv/J5QtVCFqg==","signatures":[{"sig":"MEYCIQD/nDWcoJiFFJbvcGehhT8jVkAkftXbe7EL4D9O2JcCOwIhANviRYJ/Rgrqk3TSvpfa+bROD8+8e+v28nc9EHbosBLY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":60987},"main":"dist/index.js","tsup":{"dts":true,"clean":true,"entry":["src/index.ts"],"format":"esm","minify":false,"external":["@amplib/music-theory"],"sourcemap":true,"splitting":false},"type":"module","types":"dist/index.d.ts","gitHead":"5736fd34ab408cd42a1c9241d2e85646e82d4d02","scripts":{"build":"tsup","watch":"tsup --watch","prepack":"npm run build"},"_npmUser":{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"},"repository":{"url":"git+https://github.com/another-machine/public-library.git","type":"git","directory":"packages/amplib-music-detection"},"_npmVersion":"10.2.4","description":"Real time detection of chromatic notes and combinations of notes from a device microphone.","directories":{},"_nodeVersion":"20.11.0","dependencies":{"@amplib/music-theory":"^0.1.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/music-detection_0.1.0_1785550294203_0.9138520836478161","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@amplib/music-detection","description":"Real time detection of chromatic notes and combinations of notes from a device microphone.","version":"0.2.0","main":"dist/index.js","types":"dist/index.d.ts","publishConfig":{"access":"public"},"homepage":"https://amplib.app/music-detection","repository":{"type":"git","url":"git+https://github.com/another-machine/public-library.git","directory":"packages/amplib-music-detection"},"scripts":{"build":"tsup","watch":"tsup --watch","prepack":"npm run build"},"author":{"name":"Another Machine"},"license":"Apache-2.0","type":"module","dependencies":{"@amplib/music-theory":"^0.1.0"},"_id":"@amplib/music-detection@0.2.0","gitHead":"698fc6679d04f3c6e45060e59738c29e54310054","bugs":{"url":"https://github.com/another-machine/public-library/issues"},"_nodeVersion":"20.11.0","_npmVersion":"10.2.4","dist":{"integrity":"sha512-oYhJcUrGvPpxvtZF7Ir37ts4kTbNPYKFGUot0yy+4gOq61u718YW5Q+AeKdtHDMazL/9bFBP8dQZJ24eVrWc8Q==","shasum":"60b4f49bc114b33ff62195300104efdabb119af4","tarball":"https://registry.npmjs.org/@amplib/music-detection/-/music-detection-0.2.0.tgz","fileCount":8,"unpackedSize":185575,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCNcRHZ5N0LAI4j88etsUDL1JZc6uD2dnLHCwP24XOiVgIgFFzppqUJL+XTAhirkHPVAH1HMS5vLkm4PX//K27dGf4="}]},"_npmUser":{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"},"directories":{},"maintainers":[{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/music-detection_0.2.0_1786995065581_0.24866899056650804"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-01T02:11:34.007Z","modified":"2026-08-17T19:31:05.967Z","0.1.0":"2026-08-01T02:11:34.357Z","0.2.0":"2026-08-17T19:31:05.786Z"},"bugs":{"url":"https://github.com/another-machine/public-library/issues"},"author":{"name":"Another Machine"},"license":"Apache-2.0","homepage":"https://amplib.app/music-detection","repository":{"type":"git","url":"git+https://github.com/another-machine/public-library.git","directory":"packages/amplib-music-detection"},"description":"Real time detection of chromatic notes and combinations of notes from a device microphone.","maintainers":[{"name":"jakealbaugh","email":"jake.albaugh@gmail.com"}],"readme":"# @amplib/music-detection\n\nHearing what is already playing. Chromatic note presence and a chord guess,\nframe by frame, from any Web Audio source.\n\n```ts\nimport { DetectTone } from \"@amplib/music-detection\";\n\nconst audioContext = new AudioContext();\nconst detector = new DetectTone({ audioContext });\nawait detector.initialize(source); // a media element, a stream, or a buffer\n\nloop();\nfunction loop() {\n  requestAnimationFrame(loop);\n  const { label, tones } = detector.tick();\n  // label: \"Am\" · tones: one entry per semitone, each with a prominence 0–1\n}\n```\n\n## Modules\n\n| Module       | Description                                                                          |\n| ------------ | ------------------------------------------------------------------------------------ |\n| `DetectTone` | Per-semitone presence over C2–B6, folded to pitch classes, plus the most likely chord |\n| `DetectBPM`  | Beat detection from low-band energy, frame by frame off a live `AnalyserNode`         |\n| `analyze`    | Tempo, beat count and key from raw PCM — no Web Audio, so it runs in Node too         |\n\n`DetectTone.tick()` returns `tones` — one entry per semitone it tracks, each an\n`@amplib/music-theory` `Note` plus a `prominence` — and `notes`, the same\nenergy folded to twelve pitch classes, most prominent first, with `label` and\n`guess` for the chord. `DetectTone.analyzeKey(buffer)` answers the same\nquestion offline, for a whole track at once.\n\n**`analyzeKey` and `detectKey` are not the same question.** `analyzeKey` names\nthe chord it hears, from note prominence. `detectKey` names a tonic and a mode,\nby scoring a chromagram against key profiles — the thing you would print on a\nrecord sleeve. A track can sit on an F# minor chord for eight bars and be in A\nmajor.\n\n`analyze` needs no `AudioContext` at all: pass it channel data and a sample\nrate. `detectTempo` autocorrelates an onset envelope, `detectLoopBeats` reports\nhow many beats a loop-length file holds, `detectKey` returns a root, a mode and\na confidence, and `chromagram` and `onsetEnvelope` are exposed for anything\nbuilding on top. `DetectBPM.analyzeBPM` now defers to `detectTempo`, so a\nbuffer gets one answer regardless of which one you reach for.\n\n## Design\n\n**The analyser is configured before anything is derived from it.** Both the\ndata buffer and the note-to-bin mapping are functions of `fftSize`, so setting\nit afterwards makes every index wrong by the ratio of the two sizes and leaves\na buffer covering only the bottom fraction of the spectrum. This package had\nexactly that bug until 0.1.0 — a 440Hz tone read **0** at the bin it looked in.\nIf you reconfigure the analyser, change `fftSize` first and rebuild\n`frequencyData` and `frequencyIndices` from it.\n\n**32768, the maximum an AnalyserNode allows.** At 44.1kHz that is 1.35Hz per\nbin, and the bottom octave needs it: C2 and C#2 are under 4Hz apart, so at the\ndefault 2048 they share a bin.\n\n**A band is ignored when a neighbor is louder.** A note's bin counts only if\nit beats its semitone and octave neighbors, which is what keeps a harmonic\nfrom being reported as a note in its own right.\n\n**Presence is sharpened, then eased asymmetrically.** Raw bin magnitude is\nraised to a high power, so only bands that are genuinely dominant survive; the\nresult rises slowly and falls four times faster, which is what makes a held\nchord read as steady rather than flickering.\n\n**Live and offline share one chord picker.** They had a copy each once, and the\ncopies disagreed — the offline one built its candidate chords from the loudest\n*notes* rather than the loudest pitch classes, so its top four were usually\nfour octaves of the same note and it could never name a triad. `analyzeKey`\nalso does its own accumulation and never writes to the instance state, which it\nused to: asking for a track's key corrupted the live detection running beside\nit.\n\n**`@amplib/music-theory` is a real dependency, not an inlined copy.** `tick()`\nhands back its `Note` and `Chord` instances, so a consumer holding both\npackages must hold the same one.\n\n[Live demo](https://amplib.app/music-detection) — a chord loop and a\nmicrophone through the same detector.\n","readmeFilename":"README.md"}