CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-verovio

A fast, portable and lightweight library for engraving Music Encoding Initiative (MEI) music scores into SVG

Overview
Eval results
Files

advanced.mddocs/

Advanced Features

This document covers advanced functionality including experimental editor features, layout manipulation, locale management, runtime measurement, and other specialized capabilities.

Capabilities

Experimental Editor Functions

Experimental editing capabilities for modifying loaded MEI documents.

Warning: These functions are experimental and should not be relied upon for production use. The API may change in future versions.

class toolkit:
    def edit(self, editor_action: dict) -> bool:
        """
        Edit the MEI data (experimental).

        This is experimental code not to rely on. The API and
        behavior may change in future versions.

        Args:
            editor_action: Dictionary with editor action specifications

        Returns:
            True if edit action was successfully applied, False otherwise
        """

    def editInfo(self) -> dict:
        """
        Get editor status (experimental).

        This is experimental code not to rely on. Returns information
        about the current editor state.

        Returns:
            Dictionary with editor status information
        """

Layout Manipulation

Redo layout calculations with updated options or after modifications.

class toolkit:
    def redoLayout(self, options: dict | None = None) -> None:
        """
        Redo the layout of the loaded data.

        Call this after changing rendering options (e.g., page size,
        zoom level) to recalculate the layout without reloading data.

        Args:
            options: Dictionary with action options:
                - resetCache: bool, true to reset cache (default: True)
        """

    def redoPagePitchPosLayout(self) -> None:
        """
        Redo pitch position layout for the current page.

        Only recalculates vertical positions of notes. For full
        recalculation, use redoLayout() instead.
        """

Locale Management

Control locale settings for the toolkit instance.

class toolkit:
    def setLocale(self) -> None:
        """
        Set the global locale for the toolkit instance.
        """

    def resetLocale(self) -> None:
        """
        Reset the global locale to the previous value.
        """

Runtime Measurement

Measure and log execution time for performance analysis.

class toolkit:
    def initClock(self) -> None:
        """
        Initialize the runtime clock.

        Start timing for performance measurement.
        """

    def resetClock(self) -> None:
        """
        Reset the runtime clock.

        Reset timing measurements to zero.
        """

    def getRuntimeInSeconds(self) -> float:
        """
        Get the elapsed runtime in seconds.

        Returns:
            Runtime in seconds as float
        """

    def logRuntime(self) -> None:
        """
        Log runtime information.

        Write runtime information to the log.
        """

Usage Examples

Performance Measurement

import verovio

tk = verovio.toolkit()

# Initialize timing
tk.initClock()

# Load and render
tk.loadFile("large_score.mei")
page_count = tk.getPageCount()

for page in range(1, page_count + 1):
    svg = tk.renderToSVG(pageNo=page)

# Get elapsed time
elapsed = tk.getRuntimeInSeconds()
print(f"Rendered {page_count} pages in {elapsed:.2f} seconds")
print(f"Average: {elapsed/page_count:.3f} seconds per page")

# Log to Verovio's internal log
tk.logRuntime()

Benchmarking Different Options

import verovio

def benchmark_rendering(filename, options_list):
    """Benchmark rendering with different option sets."""
    tk = verovio.toolkit()
    tk.loadFile(filename)

    results = []

    for idx, options in enumerate(options_list):
        # Reset and start timing
        tk.resetClock()
        tk.initClock()

        # Apply options and redo layout
        tk.setOptions(options)
        tk.redoLayout()

        # Render all pages
        page_count = tk.getPageCount()
        for page in range(1, page_count + 1):
            svg = tk.renderToSVG(pageNo=page)

        # Record time
        elapsed = tk.getRuntimeInSeconds()
        results.append({
            'options': options,
            'time': elapsed,
            'pages': page_count
        })

        print(f"Config {idx+1}: {elapsed:.2f}s for {page_count} pages")

    return results

# Test different scales
options_to_test = [
    {'scale': 40},
    {'scale': 60},
    {'scale': 80},
    {'scale': 100}
]

results = benchmark_rendering("score.mei", options_to_test)

Dynamic Layout Adjustments

import verovio

tk = verovio.toolkit()
tk.loadFile("score.mei")

# Initial render
initial_options = {
    'pageHeight': 2970,
    'pageWidth': 2100,
    'scale': 40
}
tk.setOptions(initial_options)
svg1 = tk.renderToSVG()

# Change options and redo layout
new_options = {
    'pageHeight': 3960,
    'pageWidth': 2800,
    'scale': 60
}
tk.setOptions(new_options)
tk.redoLayout()

# Render with new layout
svg2 = tk.renderToSVG()
print(f"Pages after resize: {tk.getPageCount()}")

Incremental Layout Updates

import verovio

tk = verovio.toolkit()
tk.loadFile("score.mei")

# Full layout
tk.redoLayout()
svg = tk.renderToSVG(pageNo=1)

# Make a small pitch adjustment (if needed)
# Then do quick pitch position recalculation
tk.redoPagePitchPosLayout()

# Render updated page
svg_updated = tk.renderToSVG(pageNo=1)

Responsive Score Viewer

import verovio

class ResponsiveScoreViewer:
    def __init__(self, filename):
        self.tk = verovio.toolkit()
        self.tk.loadFile(filename)
        self.current_page = 1

    def resize_viewport(self, width, height):
        """Adjust score layout for new viewport size."""
        self.tk.resetClock()
        self.tk.initClock()

        # Update page dimensions
        options = {
            'pageWidth': width,
            'pageHeight': height,
            'adjustPageHeight': True
        }
        self.tk.setOptions(options)

        # Redo layout
        self.tk.redoLayout()

        elapsed = self.tk.getRuntimeInSeconds()
        print(f"Layout recalculated in {elapsed:.3f}s")

        return self.tk.getPageCount()

    def set_zoom(self, scale):
        """Change zoom level."""
        self.tk.setScale(scale)
        self.tk.redoLayout()

    def render_current_page(self):
        """Render the current page."""
        return self.tk.renderToSVG(pageNo=self.current_page)

# Usage
viewer = ResponsiveScoreViewer("score.mei")

# Simulate window resize
new_page_count = viewer.resize_viewport(2400, 3200)
print(f"Document now has {new_page_count} pages")

# Change zoom
viewer.set_zoom(60)

# Render
svg = viewer.render_current_page()

Layout Optimization

import verovio

def find_optimal_scale(filename, target_pages):
    """Find scale that produces target number of pages."""
    tk = verovio.toolkit()
    tk.loadFile(filename)

    # Binary search for optimal scale
    min_scale = 10
    max_scale = 150
    tolerance = 2  # pages

    while max_scale - min_scale > 5:
        test_scale = (min_scale + max_scale) // 2

        tk.setScale(test_scale)
        tk.redoLayout()
        pages = tk.getPageCount()

        print(f"Scale {test_scale}: {pages} pages")

        if abs(pages - target_pages) <= tolerance:
            print(f"Found optimal scale: {test_scale} ({pages} pages)")
            return test_scale

        if pages > target_pages:
            # Need larger scale to fit more on each page
            min_scale = test_scale
        else:
            # Need smaller scale
            max_scale = test_scale

    final_scale = (min_scale + max_scale) // 2
    tk.setScale(final_scale)
    tk.redoLayout()
    print(f"Best scale: {final_scale} ({tk.getPageCount()} pages)")
    return final_scale

# Find scale for ~10 pages
optimal = find_optimal_scale("score.mei", target_pages=10)

Locale-Aware Processing

import verovio

tk = verovio.toolkit()

# Set locale for proper text rendering
tk.setLocale()

# Load and process
tk.loadFile("score_with_text.mei")
svg = tk.renderToSVG()

# Reset locale when done
tk.resetLocale()

Performance Profiling Suite

import verovio
import statistics

class PerformanceProfiler:
    def __init__(self):
        self.tk = verovio.toolkit()
        self.measurements = []

    def profile_operation(self, operation_name, operation_func):
        """Profile a single operation."""
        self.tk.resetClock()
        self.tk.initClock()

        result = operation_func()

        elapsed = self.tk.getRuntimeInSeconds()
        self.measurements.append({
            'operation': operation_name,
            'time': elapsed
        })

        print(f"{operation_name}: {elapsed:.4f}s")
        return result

    def profile_file(self, filename):
        """Profile all major operations on a file."""
        print(f"\nProfiling: {filename}")

        # Load
        self.profile_operation(
            "Load",
            lambda: self.tk.loadFile(filename)
        )

        # Get page count
        page_count = self.profile_operation(
            "Get page count",
            lambda: self.tk.getPageCount()
        )

        # Render SVG
        self.profile_operation(
            "Render to SVG",
            lambda: self.tk.renderToSVG()
        )

        # Render MIDI
        self.profile_operation(
            "Render to MIDI",
            lambda: self.tk.renderToMIDI()
        )

        # Get MEI
        self.profile_operation(
            "Export MEI",
            lambda: self.tk.getMEI()
        )

        # Generate timemap
        self.profile_operation(
            "Generate timemap",
            lambda: self.tk.renderToTimemap()
        )

    def print_summary(self):
        """Print performance summary."""
        print("\n=== Performance Summary ===")
        total_time = sum(m['time'] for m in self.measurements)
        print(f"Total time: {total_time:.4f}s")

        times = [m['time'] for m in self.measurements]
        print(f"Mean operation time: {statistics.mean(times):.4f}s")
        print(f"Median operation time: {statistics.median(times):.4f}s")

        print("\nSlowest operations:")
        sorted_ops = sorted(self.measurements, key=lambda x: x['time'], reverse=True)
        for op in sorted_ops[:3]:
            print(f"  {op['operation']}: {op['time']:.4f}s")

# Usage
profiler = PerformanceProfiler()
profiler.profile_file("score.mei")
profiler.print_summary()

Cache Management

import verovio

tk = verovio.toolkit()
tk.loadFile("score.mei")

# Initial layout with cache
tk.redoLayout()

# Change options
tk.setScale(60)

# Redo with cache reset (full recalculation)
tk.redoLayout(options={'resetCache': True})

# Redo without cache reset (may use cached data)
tk.setScale(80)
tk.redoLayout(options={'resetCache': False})

Debug Timing for Operations

import verovio

def timed_render_pipeline(filename):
    """Time each step of the rendering pipeline."""
    tk = verovio.toolkit()

    # Time loading
    tk.initClock()
    tk.loadFile(filename)
    load_time = tk.getRuntimeInSeconds()
    print(f"Load time: {load_time:.4f}s")

    # Time layout
    tk.resetClock()
    tk.initClock()
    tk.redoLayout()
    layout_time = tk.getRuntimeInSeconds()
    print(f"Layout time: {layout_time:.4f}s")

    # Time first page render
    tk.resetClock()
    tk.initClock()
    svg = tk.renderToSVG(pageNo=1)
    render_time = tk.getRuntimeInSeconds()
    print(f"First page render: {render_time:.4f}s")

    # Time MIDI generation
    tk.resetClock()
    tk.initClock()
    midi = tk.renderToMIDI()
    midi_time = tk.getRuntimeInSeconds()
    print(f"MIDI generation: {midi_time:.4f}s")

    total = load_time + layout_time + render_time + midi_time
    print(f"\nTotal pipeline time: {total:.4f}s")

    return {
        'load': load_time,
        'layout': layout_time,
        'render': render_time,
        'midi': midi_time,
        'total': total
    }

# Profile a file
timings = timed_render_pipeline("score.mei")

Install with Tessl CLI

npx tessl i tessl/pypi-verovio

docs

advanced.md

data-access.md

format-conversion.md

index.md

loading-config.md

rendering.md

tile.json