Skip to content

Repository files navigation

© 2026 Chiranjeev (@chiranr19) — All Rights Reserved. This project is source-available for viewing only; it is not open source. No copying, reuse, modification, deployment, or redistribution of any part of it (or its underlying ideas) without prior written permission — see LICENSE and SIGNATURE. Prospective employers and collaborators are welcome to read the code. · authorship sigil AQY3·QG3X·BBYY·R2WN

hookfinder

CI Python License: proprietary

Find the 30 seconds that hook you. An explainable, dependency-light Python package that locates the most memorable segment of a song — the chorus, the hook, the part you'd use as a preview clip — and tells you why it picked it.

Built on librosa. No cloud services, no API keys, no model downloads. Point it at an audio file and it returns a start/end timestamp plus a per-signal breakdown.

hookfinder locating the 30-second hook on a synthetic verse/chorus track: the loudness curve shows two choruses; the detected 30s window lands over the first chorus, and the lower panel breaks down why — repetition 0.78, rhythmic 0.94, energy 1.00, centrality 1.00.

The audio above is synthetic (a generated verse/chorus signal); the detected window and the per-signal scores are hookfinder's real output on it.

from hookfinder import find_hook

hook = find_hook("song.mp3")
print(hook)               # Hook(start=57.00s, end=87.00s, duration=30.00s, score=0.91)
print(hook.components)    # {'repetition': 0.98, 'harmonic': 0.71, 'rhythmic': 0.66, ...}
$ hookfinder song.mp3
song.mp3
  best hook: 0:57 -> 1:27 (30s, score 0.912)
    repetition=0.98  harmonic=0.71  rhythmic=0.66  energy=0.80  centrality=1.00

Why

The durable, reusable piece of a music app is rarely the app — it's the pipeline that answers "which slice of this track do I preview?" This package is that pipeline, extracted and made general. A Tamil-film-music app is just one consumer of it; so is a podcast teaser tool, a DJ crate-digging helper, or a dataset-builder for MIR research.

Install

pip install hookfinder

Or from source:

git clone https://github.com/chiranr19/hookfinder
cd hookfinder
pip install -e .

librosa pulls in soundfile/audioread for decoding. For formats beyond WAV (mp3, m4a, webm), having ffmpeg on your PATH is recommended.

How it works

Every candidate window (default 30 s, stepped across the song) is scored on five signals, each normalized to [0, 1]:

Signal Measures Intuition
repetition recurrence of the section elsewhere in the song the hook is the part the song keeps returning to
harmonic frame-to-frame chroma stability settled sections beat transitional churn
rhythmic percussive onset strength the groove is present and driving
energy fraction of the window above a loudness threshold sustained, not a quiet passage
centrality structural position prior hooks rarely live in the intro or outro

The signals are combined with configurable weights (repetition leads by default), the score curve is smoothed so the pick is a broad plateau rather than a one-window spike, the top window is chosen, and its start is snapped to a nearby beat for a clean cut. Because the score is a transparent weighted sum, every result carries the components that produced it — no black box.

Feature extraction runs once per song; scanning candidates is cheap.

Performance

Two quality modes, set with quality= (or --quality):

Mode Chroma Rhythm source Analysis time*
fast (default) chroma_stft on the mix onset strength on the mix ~1.5 s
high chroma_cqt on harmonic part onset/beats on percussive part ~19 s

*Excluding decode, on a 4.5-minute track. high runs harmonic/percussive separation (HPSS), which alone costs more than the entire fast pipeline.

Decode usually dominates total runtime, not analysis — a 4.5-minute .webm took ~21 s to decode via librosa's audioread fallback versus ~1.5 s to analyze. Installing ffmpeg and/or working from .wav makes the biggest difference.

On real songs the two modes tend to surface the same set of candidate regions but sometimes rank them differently — because songs often have several near-equally-hooky sections whose scores sit within a few percent of each other. When the top scores are that close, treat the pick as one reasonable answer among several and look at find_candidates() rather than assuming a single truth.

Usage

One-liner

from hookfinder import find_hook

hook = find_hook("song.mp3", clip_duration=30)
clip = (hook.start, hook.end)

Tune it

from hookfinder import HookFinder, Weights

finder = HookFinder(
    clip_duration=15,          # shorter teaser
    step=0.5,                  # finer search
    weights=Weights(repetition=0.5, energy=0.3, rhythmic=0.2,
                    harmonic=0.0, centrality=0.0),
    align_to_beat=True,
)
hook = finder.find("song.mp3")

Several candidates

finder = HookFinder()
for cand in finder.find_candidates("song.mp3", top_k=3):
    print(cand.start, cand.score, cand.components)

Reuse features across settings

from hookfinder import extract_from_file, HookFinder

feats = extract_from_file("song.mp3")        # analyze audio once
short = HookFinder(clip_duration=15).find(feats)
long  = HookFinder(clip_duration=45).find(feats)

Command line

hookfinder song.mp3                     # best hook + breakdown
hookfinder song.mp3 --top 3             # top 3 non-overlapping candidates
hookfinder song.mp3 -d 15 --json        # 15s clip, JSON output
hookfinder song.mp3 --quality high      # slower, cleaner per-signal sources
hookfinder song.mp3 --export teaser.wav # write the clip to disk
hookfinder song.mp3 --w-repetition 0.6 --w-energy 0.4

API

  • find_hook(audio, clip_duration=30, sr=None, weights=None, align_to_beat=True, step=1.0, quality="fast") -> Hook
  • HookFinder(...).find(audio) -> Hook
  • HookFinder(...).find_candidates(audio, top_k=5) -> list[Hook]
  • Hook: .start, .end, .duration, .score, .components, .to_dict()
  • Weights(repetition, harmonic, rhythmic, energy, centrality) — auto-normalized
  • extract_from_file(path, quality="fast") -> AudioFeatures / extract_features(y, sr, quality="fast") -> AudioFeatures

audio may be a file path, a NumPy waveform (pass sr; stereo is downmixed, integer PCM is scaled), or a precomputed AudioFeatures. Empty audio, clips under ~3 seconds, and non-finite samples raise ValueError.

Limitations

  • It's a signal-based heuristic, not a trained model. It finds the salient, repeated, high-energy section — which is usually the hook, but "catchiness" is subjective and it won't always agree with you.
  • The top pick is often not a clear winner. On real tracks the leading candidates frequently score within a few percent of each other; the ranking between them is not meaningful at that margin. Use find_candidates() when that matters to you.
  • Instrumental or through-composed music (no repeating chorus) leans on the other four signals and is inherently harder.
  • Beat tracking and recurrence degrade on very short or very noisy audio.
  • It does not download audio. Bring your own files. (See examples/ for how a downloader would sit on top of this.)

Development

pip install -e ".[dev]"
pytest

License

Proprietary — All Rights Reserved. Source-available for viewing only; not open source. No use, copy, or reuse without written permission. See LICENSE.

About

Find the 30 seconds that hook you — explainable hook/chorus detection for preview clips, built on librosa.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages