---
title: "Describe a single sound from a sample library (material, character, loop)"
method: POST
path: "/v1/signallab/sample"
tags: ["SignalLab"]
---

# Describe a single sound from a sample library (material, character, loop)

`POST /v1/signallab/sample`

## Request

- Base URL: `https://audiolab.tools`
- URL: `https://audiolab.tools/v1/signallab/sample`
- Auth: HTTP bearer

One SOUND, not a recording: a single one-shot from a sample library, for sorting
and searching a pack. Use /v1/signallab for a take or an episode. A single hit
returns the character vector with loop null; material with several onsets returns
tempo and loop QC with character null.

## Request body

- object
  - `url` string, uri: Public http(s) URL to the audio file (alternative to multipart upload).
  - `objectPath` string: Path returned by /api/upload-url, for audio that has no public URL. Readable only by the key that uploaded it, and kept for about an hour so a follow-up question about the same file does not need a re-upload.

## Response `200`

Sound described.

- SignalSample: One sound from a sample library. character and loop are mutually exclusive: a single hit fills character, material with several onsets fills loop, and the other is null.
  - `material` object, required: What kind of thing this is, and the evidence for that call.
    - `structure` 'silence' | 'continuous' | 'single-event' | 'phrase': silence = no energy; continuous = one sustained event; single-event = one hit; phrase = several onsets.
    - `durationClass` 'sample' | 'clip' | 'track' | 'long-form': sample under 5 s, clip under 30 s, track under 15 min, long-form beyond that.
    - `evidence` string[]: Plain sentences behind the call, such as "4 onsets" or "96% of windows above -40 dB".
    - `onsetCount` integer
    - `attackStrength` number: Peak-to-surroundings ratio of the strongest onset, 0 to 1.
  - `character` object, nullable: The character vector. Present for a single hit, null for material with several onsets.
    - `durationMs` number
    - `attackMs` number: Time from onset to peak.
    - `decayT20Ms` number: Decay from a Schroeder curve, not from a short envelope: at low frequencies an envelope swings more within one period than the decay it is meant to measure.
    - `decayFitR2` number: Fit quality of that decay line, 0 to 1. Low means the number is not worth much.
    - `decayRangeDb` number
    - `decayTruncated` boolean: True when the file ends before the sound does.
    - `band` object: Power shares across seven bands, summing to 1.
      - `sub` number
      - `bass` number
      - `lowMid` number
      - `mid` number
      - `highMid` number
      - `presence` number
      - `air` number
    - `bodyTilt` number: Spectral tilt of the body, dB per octave.
    - `bodyResonance` number: How peaked the body is: high means one resonance dominates.
    - `clickRatio` number: Share of the energy in the first few milliseconds.
    - `f0Hz` number, nullable
    - `note` string, nullable: Nearest note name for f0Hz, such as F1.
    - `cents` integer, nullable: Deviation from that note, -50 to +50.
    - `pitchDefiniteness` number, nullable: MPM clarity: 1.00 on a pure tone, 0.07 on white noise. Below the gate the pitch fields are omitted with a reason.
    - `glideSt` number, nullable: Pitch change in semitones between two windows whose centres sit exactly 180 ms apart, so this is NOT the glide over the whole sound. A glide of 12 semitones spread over 1.5 s reads about 1.4 here. Sign follows direction: negative falls.
    - `crestDb` number
    - `channelCorrelation` number, nullable: Null for mono input.
    - `omitted` object: Per field that could not be measured, the reason why.
  - `loop` object, nullable: Tempo and loop QC. Present for material with several onsets, null for a single hit.
    - `durationSec` number
    - `bpm` number, nullable
    - `bars` integer, nullable: How many bars that tempo makes the file, at 4/4.
    - `tempoStrength` number: Strength of the winning tempo peak.
    - `candidateMargin` number: How far the winner sits above the runner-up. Near 0 means the tempo is a coin flip.
    - `bpmRaw` number, nullable: Before snapping to the octave and the grid.
    - `octaveCandidates` object[]: The same tempo at half and double time, since 120 and 60 fit the same file.
      - `bpm` number
      - `bars` integer
      - `factor` number
    - `seamRatio` number: Level jump at the loop seam relative to the material. 0 is seamless; high means it clicks when it wraps.
    - `edgeSilence` object: Silence at the edges, which shifts the grid when the file is dropped into a DAW.
      - `startMs` number
      - `endMs` number
    - `omitted` object
  - `omitted` object: Top-level reasons, for whole blocks that could not be measured.

## Other responses

- `400`: Malformed request, a URL we could not use, or a parameter that is not valid. Codes: url_not_allowed, url_fetch_failed, invalid_json, missing_input, invalid_parameter, batch_too_large, upload_not_found.
- `401`: Invalid or revoked API key. /v1/batch and /v1/usage always need a key; the analysis routes also run without one, as a small keyless trial. Codes: invalid_api_key.
- `402`: A limit of your plan was reached: the calls or the data of the month, or a preset that needs a paid plan. The body carries usage where it applies. Codes: quota_exceeded, data_quota_exceeded, profile_requires_paid_plan, voice_minutes_exhausted.
- `413`: File too large for the route, or a recording too long to decode. Codes: file_too_large, track_too_long.
- `422`: File could not be decoded as audio. Codes: unsupported_format, room_not_measurable.
- `429`: Too many requests this minute. See Retry-After. Codes: rate_limited.
- `500`: Something went wrong on our side. The details are logged and never sent to you. Codes: storage_unavailable, analysis_error.

---

[API](https://skmtc.dev/audiolab/apis/audiolab-api.md) · [All operations](https://skmtc.dev/audiolab/apis/audiolab-api/llms.txt) · [OpenAPI document](https://skmtc.dev/audiolab/apis/audiolab-api/revisions/69ff81f4f4ba?raw)
