Skip to content
This repository was archived by the owner on Jul 31, 2026. It is now read-only.
This repository was archived by the owner on Jul 31, 2026. It is now read-only.

SpectrogramData silently falls back to DEFAULT_TIME_RESOLUTION on recording-metadata 404, drifting ~0.5s over a 5-minute recording #66

Description

@jon-myers

Problem

SpectrogramData.from_audio_id() (idtap/spectrogram.py) computes the true seconds-per-frame as recording_duration / time_frames using the audio recording's database entry. When client.get_audio_recording(audio_id) fails (e.g. HTTP 404 for user-uploaded audio that has no entry at /api/audioRecording/{id}), the except swallows the error and the instance silently falls back to DEFAULT_TIME_RESOLUTION = 0.015080 s/frame.

That default is close but not exact. Observed on transcription 6a6a7ca9cba5b283f2cc15f9 (audio 68f6ca1f9d93a4cd2923711e):

  • spectrogram frames: 21,344
  • transcription duration (piece.dur_tot): 322.344 s
  • true resolution: 322.344 / 21344 = 0.015102 s/frame
  • fallback resolution: 0.015080 s/frame (~0.15% fast)

The error accumulates linearly: by 4:25 into the recording, spectrogram content queried via crop_time() is ~0.4 s early relative to transcription time — about one short phrase — while the web editor (which stretches the spectrogram across the true audio duration) shows it correctly. In a figure built from crop_time(phrase.start_time, ...), each phrase box displayed the next phrase's audio content.

Verified empirically: the onset frame of a known band, multiplied by the corrected resolution, lands at 265.09 s — exactly the phrase boundary where the editor shows that band beginning.

Suggested fix

In SpectrogramData.from_piece(), when the recording lookup fell back to the default (and the piece is not an excerpt), recover the resolution from the transcription, since a transcription spans its full audio file:

spec = cls.from_audio_id(piece.audio_id, client)
if (spec._time_resolution == cls.DEFAULT_TIME_RESOLUTION
        and getattr(piece, 'excerpt_range', None) is None):
    dur = getattr(piece, 'dur_tot', None)
    if dur and abs(spec.duration - dur) / dur < 0.05:  # sanity guard
        spec._time_resolution = dur / spec.shape[1]
return spec

Also worth considering: log a warning when the DB lookup fails instead of failing silent, so the fallback is at least visible.

Downstream workaround to revert once fixed

DN-Book-Figures/visualization/lyric_contour_timeline_packed.py currently applies this same rescaling itself right after SpectrogramData.from_piece(...) (guarded by piece.excerpt_range is None and the 5% duration agreement check). Once this is fixed in the API, that block should be removed — and the regenerated figures manually re-checked against the editor (e.g. book_figures/vilayat_khan_shrutis/figure_03.png, where box 1's low band must start flush at its left edge at 4:25.1).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions