# Beat Detector - BPM, Drops & Video Edit Markers (`andrew_babo/audio-beat-analyzer`) Actor

Analyze any song or video soundtrack and get a precise beat grid: BPM, every beat and bar timestamp, energy curve, drop and breakdown points, section mood labels, plus ready-to-import markers for Premiere, DaVinci, Audacity and CapCut. No rental fee - you pay Apify compute only.

- **URL**: https://apify.com/andrew\_babo/audio-beat-analyzer.md
- **Developed by:** [Andrew Babo](https://apify.com/andrew_babo) (community)
- **Categories:** AI, Videos, Developer tools
- **Stats:** 7 total users, 0 monthly users, 73.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Beat Detector — BPM, Drops & Video Edit Markers

Give the Actor a song or a video soundtrack and it returns an exact beat map you can cut video to: BPM, every beat and bar timestamp, the energy curve over time, drop (climax) and breakdown moments, section labels with a mood, plus marker files ready to import into Adobe Premiere, DaVinci Resolve, Audacity and CapCut.

The Actor only measures. It never re-encodes or cuts your file.

### Input

| Field | Type | Notes |
|---|---|---|
| `audioUrls` | array of URLs | MP3, WAV, M4A, OGG, FLAC, MP4, MOV — audio is extracted from video automatically |
| `keyValueStoreRecords` | array of `{key, storeId?}` | analyze files you already uploaded to an Apify key-value store |
| `beatsPerBar` | 2–12, default 4 | time signature numerator |
| `fixedBpm` | number | lock the tempo, only the phase is fitted |
| `bpmRangeMin` / `bpmRangeMax` | number | fold half/double tempo into your range (default 60–190) |
| `peakSensitivity` | 1–10, default 5 | how many drops / breakdowns are reported |
| `maxSections` | 2–24, default 8 | structural segmentation resolution |
| `analyzeMood` | boolean, default true | mood label per section |
| `includeBeatList` | boolean, default true | full beat/downbeat arrays in the dataset |
| `exportMarkerFiles` | boolean, default true | write the four marker files to the key-value store |
| `maxDurationSecs` | number | analyze only the first N seconds |
| `depth` | `fast` / `standard` / `deep` | fast = beats + energy, standard (default) adds lead/vocal, drops, key and mood, deep adds chords and drum/bass/harmony/voice levels |
| `tempoMode` | `auto` / `constant` / `variable` | auto keeps a constant grid unless the track really drifts |
| `detectMeter` | boolean, default true | detect 3/4 vs 4/4 instead of trusting `beatsPerBar` |
| `precisionMode` | `high` / `standard`, default `high` | high snaps every beat to the real onset at 2.9 ms resolution with sub-frame interpolation, then re-fits tempo and phase. Grid drift typically drops from ~20 ms to 1–8 ms on produced music |

### Output

One dataset row per file: `bpm`, `bpmRaw`, `bpmConfidence`, `beatGridDriftMs`, `beatGridDriftP90Ms`, `driftFloorMs`, `gridAdherence`, `beatRefined`, `precisionMode`, `beatsPerBar`, `firstBeatSecs`, `firstDownbeatSecs`, `beatCount`, `barCount`, `beats[]`, `downbeats[]`, `tempoCurve`, `energyCurve`, `climaxPoints`, `breakdownPoints`, `transitionPoints`, `sections[] {startSecs, endSecs, role, mood, energy}`, `mood`, `energyMean`, `brightness`, `keyMode`, `markerFiles`, `analysisMs`.

Marker files written to the run key-value store:

- `*-premiere-markers.csv` — Premiere Pro / DaVinci Resolve marker import (bar, beat, CLIMAX, BREAKDOWN, section)
- `*-audacity-labels.txt` — Audacity label track
- `*-cuts.json` — CapCut / script-friendly JSON with beats, downbeats, cuts, drops
- `*-strong-beats.txt` — downbeat timestamps only, one per line

### Measured accuracy

Real Apify run, 6 files in one run, 4 GB container, 74 s total, 0.0823 compute units:

| Track | Duration | BPM | Grid confidence | Grid drift | Analysis time |
|---|---|---|---|---|---|
| SoundHelix Song 1 (electronic) | 6:13 | 134.98 | 0.94 | 16.5 ms | 11.8 s |
| SoundHelix Song 2 (rock) | 7:06 | 146.00 | 0.94 | 11.9 ms | 9.1 s |
| SoundHelix Song 5 (downtempo) | 5:54 | 95.33 | 0.93 | 8.9 ms | 7.0 s |
| SoundHelix Song 8 (ballad) | 5:25 | 96.00 | 0.95 | 8.5 ms | 7.2 s |
| SoundHelix Song 11 (cinematic) | 9:11 | 140.02 | 0.90 | 15.7 ms | 10.8 s |
| MP4 video soundtrack | 0:10 | 129.26 | 0.64 | 22.0 ms | 0.8 s |
| Synthetic 128 BPM click grid (local) | 1:00 | 127.98 | 1.00 | 17.2 ms | 7.6 s |

On the synthetic reference the tempo error is 0.02 BPM. Grid drift stays under 20 ms even on a nine-minute track, because the Actor optimises both the beat interval and its phase against the onset envelope instead of trusting a single tempo estimate. `bpmConfidence` is the share of grid beats that land on a real transient — use it to spot rubato or beat-free material.

In `precisionMode: high` (default) every beat is re-measured on a 2.9 ms onset grid with parabolic sub-frame interpolation, then tempo and phase are re-fitted through the snapped beats with outlier rejection. `driftFloorMs` reports the timing resolution of the run (2.9 ms high / 23.2 ms standard), `beatGridDriftP90Ms` the 90th-percentile drift (worst-case alignment), `gridAdherence` the share of grid beats that hit a real onset, and `beatRefined` whether the refinement pass ran. On quantized, produced music the median drift lands at 1–8 ms; on live rubato playing the residual is the performer's real timing, not measurement error.

### Cost

CPU-only and far faster than real time: 34 minutes of audio were analyzed in 74 seconds for 0.0823 compute units (about $0.02), roughly $0.003 per song. There is no rental fee — you pay Apify compute only.

### Limits

- Files up to 512 MB per URL.
- Tempo is reported as one stable grid; heavily rubato or live-drummer material will show a lower `bpmConfidence` and a higher `beatGridDriftMs`.
- Mood labels are derived from tempo, energy, brightness and major/minor colouring — they are a useful editing hint, not a musicological verdict.
- `vocalSegments` / `vocalRatio` mark where the lead line dominates the 300–3500 Hz band. This is a presence heuristic, not source separation: a lead synth or a solo instrument scores the same as a singer.

### Measured accuracy (real POC runs)

| Test | Result |
|---|---|
| Click track, exactly 128 BPM (ground truth) | 128.00 BPM measured, confidence 0.99, grid drift 28.6 ms over 1 minute |
| SoundHelix Song 1 (EDM, 6:13) | 134.98 BPM, confidence 0.94, drift 16.5 ms, key E minor, 5 drops, 200 ranked cut points |
| SoundHelix Song 8 (ballad, 5:25) | 96.00 BPM, confidence 0.95, drift 8.2 ms, key F# minor |
| "Votre Toast" from Carmen (live band + voices, 2:35) | 112.35 BPM, confidence 0.90, drift 23.2 ms, variable grid, key C major |
| Kevin MacLeod "Monkeys Spinning Monkeys" | 144.00 BPM (true 144), confidence 0.98 |
| Kevin MacLeod "Heartbreaking" (rubato piano) | `lowConfidence: true` — correctly flagged as uncertain instead of guessing |

**Speed on Apify (version 0.3, real run, 4 GB container)**

Two full songs (11:38 of audio) analysed at `standard` depth in 68 s wall clock including container start and download, 0.0755 compute units (about $0.019 for both).

**Speed on a full CPU core (version 0.3, per track)**

| Depth | 6:13 EDM track | 5:25 ballad | 2:35 live recording |
|---|---|---|---|
| `fast` | 1.8 s (180x realtime) | 1.8 s | 1.0 s |
| `standard` | 7.6 s (49x realtime) | 4.5 s (72x) | 2.4 s (66x) |
| `deep` | 4.6 s (80x) | 4.3 s | 2.2 s |

Version 0.3 runs the harmonic/percussive split on a 128-band reduction of the spectrogram instead of the full STFT, which is why standard depth is roughly 4x faster than 0.2 at identical BPM, drift and section output. Runs also process several files concurrently and cache identical files, so re-analysing the same track costs nothing.

Typical cost per song: ~$0.002 at `fast`, well under $0.01 at `standard`.

### For AI agents (MCP-ready)

This actor is built to be called by AI agents. It works out of the box with the
[Apify MCP Server](https://mcp.apify.com) — add it to Claude Desktop, Cursor or any MCP
client: the agent can analyse music tempo and structure on its own.

```json
{
  "mcpServers": {
    "audio-beat-analyzer": {
      "url": "https://mcp.apify.com/?actors=andrew_babo/audio-beat-analyzer",
      "headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }
    }
  }
}
```

#### Agent skill (paste into your agent's instructions)

```text
Use the "audio-beat-analyzer" tool when you need the tempo, beat grid,
downbeats, drops, sections or mood of an audio file — for video editing
markers, auto-cut timing, DJ/playlist work or music QA. Do NOT use it for
transcription or speech.

HOW TO CALL
- { "audioUrls": ["https://.../song.mp3"], "maxDurationSecs": 90 }
- Long files: always set "maxDurationSecs" (60-120) unless the user needs
  the whole track — it is the main cost driver.
- "depth": "quick" for BPM only, "standard" (default) for sections + mood,
  "deep" only when the user asks for maximum precision.
- Known tempo? Pass "fixedBpm" to skip detection. Non-4/4 music: set
  "beatsPerBar".
- Marker files for Premiere/Resolve/Audition come from
  "exportMarkerFiles": true (default).

OUTPUT CONTRACT
- bpm, bpmConfidence, beatGrid, downbeats, sections[], drops[], mood.
- Trust bpmConfidence: below ~0.5, say the tempo is uncertain instead of
  presenting it as fact.
- noResults: true with errorCode NO_INPUT means no audio was supplied;
  errorCode on a row means that file failed (bad URL, unsupported codec) —
  report it, never invent a BPM.
```

# Actor input Schema

## `audioUrls` (type: `array`):

Direct links to MP3, WAV, M4A, FLAC, OGG, MP4 or MOV files. Audio is extracted automatically from video.

## `keyValueStoreRecords` (type: `array`):

Files you uploaded to an Apify key-value store, e.g. \[{"storeId":"<store id>","key":"track.mp3"}]. Omit storeId to read from this run's own store.

## `beatsPerBar` (type: `integer`):

Time signature numerator used for bar (downbeat) markers. 4 covers most music.

## `depth` (type: `string`):

How deep the analysis goes. Fast is ~100x realtime, standard is the default, deep adds chord segments and drum/bass/harmony/voice levels.

## `tempoMode` (type: `string`):

Constant fits one BPM to the whole track (best for electronic music). Variable follows tempo changes (live bands, rubato piano, film score). Auto picks per track.

## `precisionMode` (type: `string`):

High snaps every beat to the real onset at 2.9 ms resolution with sub-frame interpolation, then re-fits tempo and phase. Grid drift typically drops from ~20 ms to 3-8 ms.

## `detectMeter` (type: `boolean`):

Guess 3/4 vs 4/4 automatically instead of always using the beats-per-bar value.

## `fixedBpm` (type: `string`):

Force the grid to a BPM you already know, e.g. 128. Leave empty to detect automatically.

## `bpmRangeMin` (type: `integer`):

Lower bound used to resolve half/double-tempo ambiguity.

## `bpmRangeMax` (type: `integer`):

Upper bound used to resolve half/double-tempo ambiguity.

## `peakSensitivity` (type: `integer`):

Higher values return more climax and breakdown points; lower values keep only the strongest ones.

## `maxSections` (type: `integer`):

Upper bound on how many structural sections the track is split into.

## `analyzeMood` (type: `boolean`):

Add happy / sad / intense / calm labels plus energy and brightness scores per section. Turn off for a faster run.

## `includeBeatList` (type: `boolean`):

Return the full beat and downbeat arrays in the dataset row. Turn off for compact output; marker files still contain them.

## `exportMarkerFiles` (type: `boolean`):

Write Premiere/DaVinci CSV, Audacity TXT, CapCut JSON and a strong-beat cue list to the run key-value store.

## `concurrency` (type: `integer`):

How many files are analyzed at the same time in one run. Higher values finish large batches sooner.

## `maxDurationSecs` (type: `integer`):

0 analyzes the whole file. Use a value to cap cost on long files.

## `failOnZeroResults` (type: `boolean`):

Off by default: unreadable links return a dataset row with an error code and the run still finishes successfully. Turn on if your pipeline relies on a failed run status.

## Actor input object example

```json
{
  "audioUrls": [
    "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
  ],
  "beatsPerBar": 4,
  "depth": "standard",
  "tempoMode": "auto",
  "precisionMode": "high",
  "detectMeter": true,
  "bpmRangeMin": 60,
  "bpmRangeMax": 190,
  "peakSensitivity": 5,
  "maxSections": 10,
  "analyzeMood": true,
  "includeBeatList": true,
  "exportMarkerFiles": true,
  "concurrency": 3,
  "maxDurationSecs": 0,
  "failOnZeroResults": false
}
```

# Actor output Schema

## `analysis` (type: `string`):

One dataset row per track: BPM, beat grid, energy curve, climax points, sections and mood.

## `markers` (type: `string`):

Premiere/DaVinci CSV, Audacity TXT, CapCut JSON and strong-beat cue files in the run key-value store.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "audioUrls": [
        "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("andrew_babo/audio-beat-analyzer").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "audioUrls": ["https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"] }

# Run the Actor and wait for it to finish
run = client.actor("andrew_babo/audio-beat-analyzer").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "audioUrls": [
    "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3"
  ]
}' |
apify call andrew_babo/audio-beat-analyzer --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,andrew_babo/audio-beat-analyzer"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/e71pONBFtt2dh8iy2/builds/dPxJXGv2B1nN1USnO/openapi.json
