Skip to main content

A reusable Web Audio API manager for FastHTML applications with multi-buffer support, parallel decode, and card-stack-compatible playback.

Project description

cjm-fasthtml-web-audio

Install

pip install cjm_fasthtml_web_audio

Project Structure

nbs/
├── components.ipynb # FastHTML component helpers for the Web Audio API manager
├── js.ipynb         # JavaScript generation for the Web Audio API manager
└── models.ipynb     # Configuration and HTML ID types for the Web Audio API manager

Total: 3 notebooks

Module Dependencies

graph LR
    components[components<br/>components]
    js[js<br/>js]
    models[models<br/>models]

    components --> js
    components --> models
    js --> models

3 cross-module dependencies detected

CLI Reference

No CLI commands found in this project.

Module Overview

Detailed documentation for each module in the project:

components (components.ipynb)

FastHTML component helpers for the Web Audio API manager

Import

from cjm_fasthtml_web_audio.components import (
    DEFAULT_STATIC_MOUNT_PATH,
    render_audio_urls_input,
    render_web_audio_script,
    mount_web_audio_static,
    render_initial_speed_sync,
    render_speed_selector
)

Functions

def render_audio_urls_input(
    config: WebAudioConfig,    # Instance configuration
    audio_urls: List[str],     # Audio file URLs to load
    oob: bool = False,         # Whether to render as OOB swap
) -> Any:  # Hidden input element with JSON-encoded URLs
    "Render a hidden input storing audio URLs as JSON."
def render_web_audio_script(
    config: WebAudioConfig,        # Instance configuration
    focus_input_id: str,           # Hidden input ID for focused index
    card_stack_id: str,            # Card stack container ID
    nav_down_btn_id: str = "",     # Nav down button ID (for auto-navigate)
) -> Any:  # Script element with complete Web Audio JS
    "Render the complete Web Audio API script for a configured instance."
def mount_web_audio_static(
    app,                                            # FastHTML/Starlette app
    mount_path: str = DEFAULT_STATIC_MOUNT_PATH,    # URL prefix to mount static dir at
) -> str:                                           # URL of the SoundTouch worklet processor
    """
    Mount the library's vendored static assets (SoundTouch worklet, license).
    
    Inserts the Mount at `app.routes[0]` so it's matched BEFORE any catch-all
    routes FastHTML may have registered. Returns the URL where the SoundTouch
    worklet processor is served; when `mount_path` is the default the URL equals
    `DEFAULT_WORKLET_URL` — so `WebAudioConfig(enable_speed=True)` with no explicit
    `worklet_url` just works.
    """
def render_initial_speed_sync(
    config: WebAudioConfig,     # Instance configuration
    speed: float,               # Persisted playback speed (e.g. from step state)
) -> Any:                       # Script element (empty if speed is default or speed is disabled)
    """
    Render a <Script> that syncs the JS state's playbackSpeed to `speed` after insertion.
    
    Polls briefly (~1 second, 20ms intervals) for `window.set{Ns}Speed` so the helper
    works regardless of whether the web-audio script or the toolbar script runs first.
    Returns an empty Script when `speed == 1.0` (state_init already defaults to 1.0)
    or when `config.enable_speed` is False.
    """
def render_speed_selector(
    config: WebAudioConfig,          # Instance configuration (must have enable_speed=True for sync to fire)
    current_speed: float = 1.0,      # Current / persisted playback speed
    change_url: str = "",            # URL to POST speed changes to (empty → no server persist)
    label: Optional[str] = "Speed:", # Leading label text (None/"" → omit label span)
) -> Any:                            # Div(Span?, Select, sync Script)
    """
    Render the shared playback speed selector.
    
    Emits a DaisyUI `<select>` whose `onchange` calls `window.set{Ns}Speed(...)` for
    immediate JS state update. When `change_url` is provided, also POSTs to that URL
    via HTMX for server-side persistence (consumers typically return `Script(generate_speed_change_js(...))`).
    
    Also emits `render_initial_speed_sync(config, current_speed)` so the JS state
    reconciles to `current_speed` on initial render and OOB swaps — the `<option selected>`
    attribute only restores the dropdown visually.
    """

Variables

DEFAULT_STATIC_MOUNT_PATH = '/static/cjm-web-audio'

js (js.ipynb)

JavaScript generation for the Web Audio API manager

Import

from cjm_fasthtml_web_audio.js import (
    DEFAULT_WORKLET_URL,
    PLAYBACK_SPEEDS,
    generate_state_init,
    generate_init_audio,
    generate_stop_audio,
    generate_play_segment,
    generate_optional_features,
    generate_speed_change_js,
    generate_focus_change,
    generate_htmx_settle_handler,
    generate_web_audio_js
)

Functions

def generate_state_init(
    config: WebAudioConfig,  # Instance configuration
) -> str:  # JS code that initializes the state object
    "Generate JS code to initialize the namespaced state object."
def generate_init_audio(
    config: WebAudioConfig,  # Instance configuration
) -> str:  # JS init function
    """
    Generate JS function that loads and decodes audio files in parallel.
    
    When `config.enable_speed=True`, the SoundTouch worklet processor is registered
    on the AudioContext before decoding. On registration failure, `workletRegistered`
    remains false and the play path falls back to naive (pitch-shifting) playbackRate.
    """
def generate_stop_audio(
    config: WebAudioConfig,  # Instance configuration
) -> str:  # JS stop function
    "Generate JS function that stops current playback."
def generate_play_segment(
    config: WebAudioConfig,  # Instance configuration
    nav_down_btn_id: str = "",  # Nav down button ID (for auto-navigate)
) -> str:  # JS play function
    """
    Generate JS function that plays a segment from a specific buffer.
    
    When `config.enable_speed=True` and speed != 1.0 and the SoundTouch worklet is
    registered, audio flows BufferSource -> SoundTouchNode -> destination, with
    pitch auto-compensated by the worklet so tempo changes preserve pitch. Otherwise
    audio flows BufferSource -> destination (naive path; pitch shifts at non-1x).
    """
def _speed_values_js_array() -> str:  # JS array literal of speed values
    """Render PLAYBACK_SPEEDS values as a JS array literal (e.g., '[0.5, 0.75, ...]')."""
    return "[" + ", ".join(str(s) for s, _ in PLAYBACK_SPEEDS) + "]"


def generate_optional_features(
    config: WebAudioConfig,  # Instance configuration
) -> str:  # JS for optional feature functions (empty if none enabled)
    "Render PLAYBACK_SPEEDS values as a JS array literal (e.g., '[0.5, 0.75, ...]')."
def generate_optional_features(
    config: WebAudioConfig,  # Instance configuration
) -> str:  # JS for optional feature functions (empty if none enabled)
    """
    Generate JS for optional features based on config flags.
    
    When `config.enable_speed=True`, emits `set{Ns}Speed`, `cycle{Ns}SpeedTo`,
    `cycle{Ns}SpeedUp`, `cycle{Ns}SpeedDown`. The cycle helpers drive the shared
    speed `<select>` via a synthetic `change` event so the dropdown, JS state, and
    HTMX-backed server persistence stay in sync through a single code path.
    """
def generate_speed_change_js(
    config: WebAudioConfig,  # Instance configuration
    speed: float,            # New playback speed
) -> str:  # JS snippet: updates JS playbackSpeed via set{Ns}Speed
    """
    Generate the JS body returned by a consumer's `/speed_change` POST handler.
    
    Consumers wrap this in a `Script(...)` and return it so the client-side state
    updates after the server persists the new speed. Guarded with `if (window.set{Ns}Speed)`
    to tolerate the Script running before the web-audio script initializes.
    """
def generate_focus_change(
    config: WebAudioConfig,  # Instance configuration
    focus_input_id: str,  # Hidden input ID for focused index (kept for API compat; no longer written to)
) -> str:  # JS focus change callback
    "Generate JS focus change callback for card stack integration."
def generate_htmx_settle_handler(
    config: WebAudioConfig,  # Instance configuration
    card_stack_id: str,  # Card stack container ID
) -> str:  # JS HTMX afterSettle handler
    """
    Generate HTMX afterSettle handler for card stack navigation.
    
    When `config.should_play_fn` is set, delegates the play guard to a consumer-defined
    window function (e.g., `window.shouldAlignPlay()`). Otherwise uses the default
    inline guard based on zone active state and auto-navigate flag.
    """
def generate_web_audio_js(
    config: WebAudioConfig,  # Instance configuration
    focus_input_id: str,  # Hidden input ID for focused index
    card_stack_id: str,  # Card stack container ID
    nav_down_btn_id: str = "",  # Nav down button ID (for auto-navigate)
) -> str:  # Complete JS code for this instance
    "Generate the complete Web Audio API JS for a configured instance."

Variables

DEFAULT_WORKLET_URL = '/static/cjm-web-audio/soundtouch-processor.js'
PLAYBACK_SPEEDS = [8 items]

models (models.ipynb)

Configuration and HTML ID types for the Web Audio API manager

Import

from cjm_fasthtml_web_audio.models import (
    WebAudioConfig,
    WebAudioHtmlIds
)

Classes

@dataclass
class WebAudioConfig:
    "Configuration for a Web Audio API manager instance."
    
    namespace: str  # Unique prefix (e.g., "align", "review")
    indicator_selector: str  # CSS selector for playing indicators
    data_index_attr: str = 'audioFileIndex'  # Data attr name for buffer index
    data_start_attr: str = 'startTime'  # Data attr name for start time
    data_end_attr: str = 'endTime'  # Data attr name for end time
    enable_speed: bool = False  # Playback speed support (pitch-preserving via SoundTouch worklet)
    enable_replay: bool = False  # Replay current segment support
    enable_auto_nav: bool = False  # Auto-navigate on completion support
    should_play_fn: str = ''  # Named window function for custom play guard (replaces default zone guard)
    worklet_url: Optional[str]  # URL of vendored soundtouch-processor.js; None -> default mount path
    
    def ns(self) -> str:  # Capitalized namespace for JS function names
            """Capitalized namespace for JS function names (e.g., 'align' -> 'Align')."""
            return self.namespace.capitalize()
    
        @property
        def state_key(self) -> str:  # JS state object key
        "Capitalized namespace for JS function names (e.g., 'align' -> 'Align')."
    
    def state_key(self) -> str:  # JS state object key
        "JS global state object key (e.g., '_webAudio_align')."
class WebAudioHtmlIds:
    "HTML ID generators for Web Audio manager elements."
    
    def audio_urls_input(
            namespace: str  # Instance namespace
        ) -> str:  # HTML ID for audio URLs hidden input
        "ID for the hidden input storing audio file URLs as JSON."
    
    def speed_select(
            namespace: str  # Instance namespace
        ) -> str:  # HTML ID for the playback speed <select>
        "ID for the playback speed selector element."

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cjm_fasthtml_web_audio-0.0.11.tar.gz (45.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cjm_fasthtml_web_audio-0.0.11-py3-none-any.whl (47.5 kB view details)

Uploaded Python 3

File details

Details for the file cjm_fasthtml_web_audio-0.0.11.tar.gz.

File metadata

  • Download URL: cjm_fasthtml_web_audio-0.0.11.tar.gz
  • Upload date:
  • Size: 45.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.12

File hashes

Hashes for cjm_fasthtml_web_audio-0.0.11.tar.gz
Algorithm Hash digest
SHA256 8db52d80f1a990bf0d151abb00de7e6b008dd3ca4562d63e81a9e4c9679dbcbe
MD5 3aeaf6aad57f8cad89847b0b6df35945
BLAKE2b-256 863253e5442b903d77af3eb2a3124a8e9bec6bec37d605fb4bfe407327329240

See more details on using hashes here.

File details

Details for the file cjm_fasthtml_web_audio-0.0.11-py3-none-any.whl.

File metadata

File hashes

Hashes for cjm_fasthtml_web_audio-0.0.11-py3-none-any.whl
Algorithm Hash digest
SHA256 4cb214599e9ed231a81f5612ceb8dc3b7eb4419bc68d78b5628a34ca231d712a
MD5 7c9937c7e4a94755eac8c31dbb2a4ede
BLAKE2b-256 e94a911c6fa51bc870c6d1af3b13b19b9983df83a6a3f03033454a9858229053

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page