Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions specs/20260429-201758-auditory-scene-analysis/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
```bash
uv run python -c "
from senselab.audio.data_structures import Audio
from senselab.audio.tasks.classification.api import classify_audios_in_windows
from senselab.audio.tasks.classification import classify_audios
from senselab.utils.data_structures import HFModel

audio = Audio(filepath='tutorial_audio_files/audio_48khz_mono_16bits.wav')
model = HFModel(path_or_uri='MIT/ast-finetuned-audioset-10-10-0.4593')
results = classify_audios_in_windows([audio], model=model, window_size=1.0, hop_size=0.5)
results = classify_audios([audio], model=model, win_length=1.0, hop_length=0.5)
for r in results[0][:5]:
print(f'{r[\"start\"]:.1f}-{r[\"end\"]:.1f}s: {r[\"labels\"][0]} ({r[\"scores\"][0]:.3f})')
"
Expand Down
10 changes: 4 additions & 6 deletions src/senselab/audio/tasks/classification/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,12 @@ def _classify_windowed(
if len(batch) >= batch_size:
results = _classify_whole(batch, model=model, device=device, **kwargs)
for bp, result in zip(batch_positions, results):
k = min(top_k, len(result.labels)) if result.labels else 0
audio_results.append(
{
"start": bp / sr,
"end": min(bp + win_samples, n_samples) / sr,
"labels": result.labels[:k],
"scores": result.scores[:k],
"labels": result.labels[:top_k],
"scores": result.scores[:top_k],
Comment on lines +137 to +138

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The top_k parameter is used here for slicing the results, but it is not passed to the _classify_whole call on line 131 (and line 148). This means the underlying classifier will use its default top_k value (typically 5). If a user requests a top_k value larger than the model's default, they will still only receive the default number of labels. Consider passing top_k=top_k to _classify_whole to ensure the requested number of labels is retrieved from the model. Additionally, the default value of 5 for windowed mode (set in classify_audios) contradicts the docstring which states that None should keep all labels.

"win_length": win_length,
"hop_length": hop_length,
}
Expand All @@ -148,13 +147,12 @@ def _classify_windowed(
if batch:
results = _classify_whole(batch, model=model, device=device, **kwargs)
for bp, result in zip(batch_positions, results):
k = min(top_k, len(result.labels)) if result.labels else 0
audio_results.append(
{
"start": bp / sr,
"end": min(bp + win_samples, n_samples) / sr,
"labels": result.labels[:k],
"scores": result.scores[:k],
"labels": result.labels[:top_k],
"scores": result.scores[:top_k],
"win_length": win_length,
"hop_length": hop_length,
}
Comment on lines 150 to 158

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for processing classification results and appending them to audio_results is duplicated here and on lines 133-141. Consider refactoring this into a small helper function or restructuring the loop to avoid code duplication and improve maintainability.

Expand Down
10 changes: 5 additions & 5 deletions src/senselab/audio/tasks/classification/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ Note that this list of datasets is not exhaustive. If you are interested in benc
## Windowed Scene Classification

### Overview
`classify_audios_in_windows` applies a sliding-window approach to audio classification, enabling temporal analysis of how the acoustic scene evolves over time. Instead of producing a single label for an entire recording, it slices the audio into overlapping windows and classifies each one independently.
`classify_audios(..., win_length=1.0)` applies a sliding-window approach to audio classification, enabling temporal analysis of how the acoustic scene evolves over time. Instead of producing a single label for an entire recording, it slices the audio into overlapping windows using `Audio.window_generator()` and classifies each one independently.

### Default Parameters
- **window_size**: 1.0 second — the duration of each analysis window.
- **hop_size**: 0.5 seconds — the step between consecutive windows (50% overlap by default).
- **win_length**: Window duration in seconds (e.g., 1.0).
- **hop_length**: Hop duration in seconds. Defaults to `win_length / 2` (50% overlap).
- **top_k**: 5 — the number of top-scoring labels retained per window.

Windows are processed in batches of 32 for memory efficiency. If the audio is shorter than a single window, the full audio is used as one window.
Expand All @@ -58,12 +58,12 @@ Windows are processed in batches of 32 for memory efficiency. If the audio is sh
Use `scene_results_to_segments` to convert the per-window results into segment dicts compatible with `plot_aligned_panels`:

```python
from senselab.audio.tasks.classification import classify_audios_in_windows, scene_results_to_segments
from senselab.audio.tasks.classification import classify_audios, scene_results_to_segments
from senselab.audio.tasks.plotting.plotting import plot_aligned_panels
from senselab.utils.data_structures import HFModel

model = HFModel(path_or_uri="MIT/ast-finetuned-audioset-10-10-0.4593")
results = classify_audios_in_windows([audio], model=model)
results = classify_audios([audio], model=model, win_length=1.0)
segments = scene_results_to_segments(results[0])

plot_aligned_panels(audio, [
Expand Down
Loading