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

Commit 8114035

Browse files
jon-myersclaude
andcommitted
feat: add comprehensive spectrogram data access and visualization support
Resolves Issue #46 Add complete spectrogram support to enable programmatic access to the same high-quality constant-Q spectrograms used in the IDTAP web application, with extensive matplotlib integration for computational musicology research. ## New Features: ### SpectrogramData Class (`idtap/spectrogram.py`) - **Data Loading**: - `from_audio_id(audio_id, client)` - Download from server - `from_piece(piece, client)` - Load from Piece object - Auto-decompresses gzipped spectrogram data from swara.studio - **Transformations**: - `apply_intensity(power)` - Power-law contrast enhancement (1.0-5.0) - `apply_colormap(data, cmap)` - 35+ matplotlib colormaps - `crop_frequency(min_hz, max_hz)` - Frequency range cropping - `crop_time(start_time, end_time)` - Time range cropping - **Matplotlib Integration** (for research workflows): - `plot_on_axis(ax, ...)` - Plot on existing axis for overlays - `get_plot_data(power, apply_cmap, cmap)` - Get processed data + extent - `get_extent()` - Get matplotlib extent [left, right, bottom, top] - **Image Generation**: - `to_image(width, height, power, cmap)` - Generate PIL Image - `to_matplotlib(figsize, power, cmap)` - Generate standalone figure - `save(filepath, ...)` - Save to file (PNG, JPG, etc.) - **Properties**: - `shape`, `duration`, `time_resolution`, `freq_bins` ### SwaraClient Updates (`idtap/client.py`) - `download_spectrogram_data(audio_id)` - Download compressed data - `download_spectrogram_metadata(audio_id)` - Download shape metadata ### Dependencies (`pyproject.toml`, `Pipfile`) - Added `numpy>=1.20.0` for array processing - Added `pillow>=9.0.0` for image generation - Added `matplotlib>=3.5.0` for visualization ## Testing: - 36 comprehensive test cases covering: - Data loading and initialization - Intensity transforms and colormap application - Frequency and time cropping - Matplotlib integration methods - Image generation and saving - All properties and edge cases - **All 401 tests pass** (365 existing + 36 new) ## Usage Example: ```python from idtap import SpectrogramData, get_piece import matplotlib.pyplot as plt # Load spectrogram piece = get_piece("transcription_id") spec = SpectrogramData.from_piece(piece) # Create visualization with spectrogram underlay fig, ax = plt.subplots(figsize=(12, 6)) spec.plot_on_axis(ax, power=2.5, cmap='viridis', alpha=0.6, zorder=0) # Overlay pitch contour ax.plot(times, freqs, 'r-', linewidth=2, zorder=1) ax.set_xlabel('Time (s)') ax.set_ylabel('Frequency (Hz)') plt.savefig('figure.png', dpi=300) ``` ## Documentation Updates: - Updated CLAUDE.md with testing warning about browser authorization ## Design Decisions: - Follows librosa/matplotlib patterns for research workflows - Optional client parameter (creates if not provided) - No caching in MVP (users can cache manually) - Uses matplotlib colormaps (close enough to D3, simpler) - Loads entire spectrogram into memory (suitable for typical sizes) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2a32c12 commit 8114035

8 files changed

Lines changed: 1880 additions & 273 deletions

File tree

CLAUDE.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ The Python API (`idtap`) is a sophisticated client library for interacting with
1616
- **Integration tests**: `python python/api_testing/api_test.py` (requires live server auth)
1717
- Test structure: Complete coverage of data models, client functionality, and authentication
1818

19+
**⚠️ IMPORTANT FOR CLAUDE: Before running the full test suite (`pytest idtap/tests/`), ALWAYS warn Jon first!**
20+
- Some tests may require browser authorization for OAuth authentication
21+
- Running tests without warning can waste time waiting for authorization that Jon doesn't realize is needed
22+
- Best practice: Ask "Ready to run the full test suite? (May require browser authorization)" before executing
23+
1924
### Build/Package/Publish - AUTOMATED via GitHub Actions
2025
**⚠️ IMPORTANT: Manual publishing is now automated. See "Automated Version Management" section below.**
2126

Pipfile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ build = "*"
1414
twine = "*"
1515
requests-toolbelt = "*"
1616
pyhumps = "*"
17-
idtap = "*"
17+
idtap = "==0.1.34"
18+
numpy = "*"
19+
pillow = "*"
20+
matplotlib = "*"
1821

1922
[dev-packages]
2023
responses = "*"

Pipfile.lock

Lines changed: 875 additions & 271 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

idtap/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from .classes.trajectory import Trajectory
2222

2323
from .enums import Instrument
24+
from .spectrogram import SpectrogramData, SUPPORTED_COLORMAPS
2425
from .audio_models import (
2526
AudioMetadata,
2627
AudioUploadResult,
@@ -74,6 +75,9 @@
7475
"Trajectory",
7576
"Instrument",
7677
"login_google",
78+
# Spectrogram
79+
"SpectrogramData",
80+
"SUPPORTED_COLORMAPS",
7781
# Audio upload classes
7882
"AudioMetadata",
7983
"AudioUploadResult",

idtap/client.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,30 @@ def download_and_save_transcription_audio(self, piece: Union[Dict[str, Any], Pie
684684
# Save file and return path
685685
return self.save_audio_file(audio_data, filename, filepath)
686686

687+
def download_spectrogram_data(self, audio_id: str) -> bytes:
688+
"""Download gzip-compressed spectrogram data.
689+
690+
Args:
691+
audio_id: The audio recording ID
692+
693+
Returns:
694+
Gzipped binary data containing uint8 spectrogram array
695+
"""
696+
endpoint = f"spec_data/{audio_id}/spec_data.gz"
697+
return self._get(endpoint)
698+
699+
def download_spectrogram_metadata(self, audio_id: str) -> Dict[str, Any]:
700+
"""Download spectrogram shape metadata.
701+
702+
Args:
703+
audio_id: The audio recording ID
704+
705+
Returns:
706+
Dictionary with 'shape' key: [freq_bins, time_frames]
707+
"""
708+
endpoint = f"spec_data/{audio_id}/spec_shape.json"
709+
return self._get(endpoint)
710+
687711
def save_transcription(self, piece: Piece, fill_duration: bool = True) -> Any:
688712
"""Save a transcription piece to the server.
689713

0 commit comments

Comments
 (0)