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

Commit f8714e5

Browse files
jon-myersclaude
andcommitted
feat: rewrite plot_pitch_patterns with compact layout, contour plots, and segmentation
Rewrites pitch pattern visualization to match the IDTAP web app's Analyzer component. Adds multiple pattern sizes, segmentation modes (section/phrase/ duration), chroma-colored cells with contrast-aware text, and optional melodic contour curves via Trajectory.compute(). Uses inch-based equal-aspect layout for compact spreadsheet-style rendering. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 788e058 commit f8714e5

3 files changed

Lines changed: 378 additions & 75 deletions

File tree

demo_visualizations.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import matplotlib.pyplot as plt
1010

1111
from idtap import SwaraClient, Piece
12-
from idtap.visualization import plot_pitch_prevalence
12+
from idtap.visualization import plot_pitch_prevalence, plot_pitch_patterns
1313

1414
OUTPUT_DIR = os.path.join(os.path.dirname(__file__), 'demo_output')
1515
os.makedirs(OUTPUT_DIR, exist_ok=True)
@@ -189,6 +189,55 @@ def main():
189189
import traceback
190190
traceback.print_exc()
191191

192+
# ------------------------------------------------------------------
193+
# Pitch pattern visualizations
194+
# ------------------------------------------------------------------
195+
pattern_configs = [
196+
{
197+
'label': '13_patterns_3gram',
198+
'desc': 'Patterns / 3-gram / pitchNumber',
199+
'kwargs': dict(pattern_size=3, output_type='pitchNumber'),
200+
},
201+
{
202+
'label': '14_patterns_3gram_plot',
203+
'desc': 'Patterns / 3-gram / pitchNumber / contour',
204+
'kwargs': dict(pattern_size=3, output_type='pitchNumber', plot=True),
205+
},
206+
{
207+
'label': '15_patterns_multi_section',
208+
'desc': 'Patterns / [2,3,4]-gram / section segmentation',
209+
'kwargs': dict(
210+
pattern_sizes=[2, 3, 4], segmentation='section',
211+
output_type='pitchNumber', max_patterns=10,
212+
),
213+
},
214+
{
215+
'label': '16_patterns_sargam',
216+
'desc': 'Patterns / 3-gram / sargamLetter',
217+
'kwargs': dict(pattern_size=3, output_type='sargamLetter'),
218+
},
219+
]
220+
221+
print(f"\nGenerating {len(pattern_configs)} pattern visualizations...\n")
222+
223+
for cfg in pattern_configs:
224+
label = cfg['label']
225+
desc = cfg['desc']
226+
kwargs = cfg['kwargs']
227+
228+
print(f" [{label}] {desc}...", end=' ', flush=True)
229+
try:
230+
fig = plot_pitch_patterns(piece, title=piece_title, **kwargs)
231+
path = os.path.join(OUTPUT_DIR, f'{label}.png')
232+
fig.savefig(path, dpi=300, bbox_inches='tight',
233+
pad_inches=0.2, facecolor='white', edgecolor='none')
234+
plt.close(fig)
235+
print(f"OK → {path}")
236+
except Exception as e:
237+
print(f"FAILED: {e}")
238+
import traceback
239+
traceback.print_exc()
240+
192241
print(f"\nAll outputs saved to: {OUTPUT_DIR}/")
193242

194243

idtap/tests/visualization_test.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,16 +217,31 @@ def test_empty_patterns(self):
217217
assert fig is not None
218218
plt.close(fig)
219219

220-
def test_with_existing_axes(self):
221-
fig, ax = plt.subplots()
220+
def test_multiple_sizes(self):
222221
trajs = [
223222
_fixed_traj('sa', dur=1.0),
224223
_fixed_traj('re', dur=1.0),
224+
_fixed_traj('ga', dur=1.0),
225225
_fixed_traj('sa', dur=1.0),
226226
_fixed_traj('re', dur=1.0),
227+
_fixed_traj('ga', dur=1.0),
227228
]
228-
returned_fig = plot_pitch_patterns(trajs, pattern_size=2, ax=ax)
229-
assert returned_fig is fig
229+
fig = plot_pitch_patterns(trajs, pattern_sizes=[2, 3])
230+
assert fig is not None
231+
assert hasattr(fig, 'savefig')
232+
plt.close(fig)
233+
234+
def test_with_contour_plot(self):
235+
trajs = [
236+
_fixed_traj('sa', dur=1.0),
237+
_fixed_traj('re', dur=1.0),
238+
_fixed_traj('ga', dur=1.0),
239+
_fixed_traj('sa', dur=1.0),
240+
_fixed_traj('re', dur=1.0),
241+
_fixed_traj('ga', dur=1.0),
242+
]
243+
fig = plot_pitch_patterns(trajs, pattern_size=2, plot=True)
244+
assert fig is not None
230245
plt.close(fig)
231246

232247
def test_max_patterns_limit(self):

0 commit comments

Comments
 (0)