SSML to SSMD Conversion ======================= SSMD supports bidirectional conversion: you can convert SSML back to SSMD format. This is useful for editing existing SSML, migrating from other tools, or creating round-trip workflows. Basic Conversion ---------------- Using the Convenience Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python import ssmd # Convert SSML to SSMD ssml = 'Hello world' ssmd_text = ssmd.from_ssml(ssml) print(ssmd_text) # Output: *Hello* world Using the Document Class ~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from ssmd import Document ssml = 'Hello world' doc = Document.from_ssml(ssml) ssmd_text = doc.to_ssmd() print(ssmd_text) # Output: *Hello* world Supported SSML Elements ------------------------ Emphasis ~~~~~~~~ .. code-block:: python # Moderate emphasis ssmd.from_ssml('text') # → *text* # Strong emphasis ssmd.from_ssml('text') # → **text** Breaks ~~~~~~ .. code-block:: python # Time-based breaks ssmd.from_ssml('') # → ...500ms ssmd.from_ssml('') # → ...2s # Strength-based breaks ssmd.from_ssml('') # → ...w ssmd.from_ssml('') # → ...c ssmd.from_ssml('') # → ...s Language ~~~~~~~~ .. code-block:: python # Full locale ssmd.from_ssml('Bonjour') # → [Bonjour]{lang="fr"} # Non-standard locales preserved ssmd.from_ssml('Hello') # → [Hello]{lang="en-GB"} Phonemes ~~~~~~~~ .. code-block:: python # IPA notation ssmd.from_ssml('tomato') # → [tomato]{ph="təˈmeɪtoʊ" alphabet="ipa"} # X-SAMPA notation ssmd.from_ssml('tomato') # → [tomato]{ph="t@meIt@U" alphabet="x-sampa"} Prosody ~~~~~~~ .. code-block:: python # Volume ssmd.from_ssml('text') # → [text]{volume="loud"} ssmd.from_ssml('text') # → [text]{volume="x-loud"} # Rate ssmd.from_ssml('text') # → [text]{rate="fast"} # Pitch ssmd.from_ssml('text') # → [text]{pitch="high"} # Multiple attributes ssmd.from_ssml('text') # → [text]{volume="loud" rate="fast" pitch="high"} Say-As ~~~~~~ .. code-block:: python # Basic say-as ssmd.from_ssml('+1-555-1234') # → [+1-555-1234]{as="telephone"} # With format attribute ssmd.from_ssml('12/31/2024') # → [12/31/2024]{as="date" format="mdy"} Substitution ~~~~~~~~~~~~ .. code-block:: python ssmd.from_ssml('WWW') # → [WWW]{sub="World Wide Web"} Audio ~~~~~ .. code-block:: python # With description ssmd.from_ssml('') # → [Alternative text]{src="sound.mp3"} # With desc tag ssmd.from_ssml('') # → [doorbell]{src="bell.mp3"} # No description ssmd.from_ssml('') # → []{src="beep.mp3"} Marks ~~~~~ .. code-block:: python ssmd.from_ssml('Text more text') # → Text @here more text Paragraphs ~~~~~~~~~~ .. code-block:: python ssml = '''

First paragraph.

Second paragraph.

''' ssmd_text = ssmd.from_ssml(ssml) # Output: # First paragraph. # # Second paragraph. Platform Extensions ~~~~~~~~~~~~~~~~~~~ .. code-block:: python # Amazon whisper effect ssml = 'secret' ssmd.from_ssml(ssml) # → [secret]{ext="whisper"} Default Value Filtering ------------------------ SSMD automatically removes default/medium values to keep output clean: .. code-block:: python # Medium values are filtered out ssml = 'text' ssmd.from_ssml(ssml) # → text (not [text]{volume="medium" rate="medium" pitch="medium"}) # Only non-default values are included ssml = 'text' ssmd.from_ssml(ssml) # → [text]{volume="loud"} Round-Trip Conversion --------------------- Convert SSMD → SSML → SSMD preserving semantics: .. code-block:: python import ssmd # Original SSMD original = '*Hello* [world]{lang="fr"} ...500ms [loud]{volume="loud"}' # Convert to SSML ssml = ssmd.to_ssml(original) print(ssml) # Hello world # loud # Convert back to SSMD restored = ssmd.from_ssml(ssml) print(restored) # *Hello* [world]{lang="fr"} ...500ms [loud]{volume="loud"} # Semantically equivalent, even if syntax differs slightly Complex Examples ---------------- Nested Elements ~~~~~~~~~~~~~~~ .. code-block:: python ssml = '''

Important: Bonjour

''' ssmd_text = ssmd.from_ssml(ssml) # Output: *Important:* [Bonjour]{lang="fr" volume="loud"} Mixed Content ~~~~~~~~~~~~~ .. code-block:: python ssml = '''

Hello world

This is important

Goodbye

''' ssmd_text = ssmd.from_ssml(ssml) # Output: # *Hello* world # # This is [important]{volume="loud"} # # ...500ms # # Goodbye Whitespace Handling ------------------- SSMD normalizes whitespace during conversion: .. code-block:: python # Extra whitespace is normalized ssml = ''' Hello world ''' ssmd_text = ssmd.from_ssml(ssml) # → *Hello* world (whitespace normalized) Error Handling -------------- Invalid SSML ~~~~~~~~~~~~ .. code-block:: python import ssmd try: ssmd.from_ssml('text') except ValueError as e: print(f"Error: {e}") # Invalid/unknown tags are treated as plain text Malformed XML ~~~~~~~~~~~~~ .. code-block:: python try: ssmd.from_ssml('unclosed') except ValueError as e: print(f"XML Parse Error: {e}") Configuration Options --------------------- .. code-block:: python from ssmd import Document parser = Document(capabilities='espeak') # SSML features not supported by eSpeak will be simplified ssml = 'Hello' doc = Document.from_ssml(ssml, capabilities='espeak') ssmd_text = doc.to_ssmd() # eSpeak doesn't support emphasis, so output is just: Hello Use Cases --------- Migration from Raw SSML ~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from ssmd import Document # You have existing SSML files with open('old_ssml.xml') as f: ssml = f.read() # Convert to SSMD for easier editing doc = Document.from_ssml(ssml) ssmd_text = doc.to_ssmd() with open('new_ssmd.txt', 'w') as f: f.write(ssmd_text) SSML Editor Backend ~~~~~~~~~~~~~~~~~~~ .. code-block:: python from ssmd import Document # Load SSML for editing def load_document(ssml_file): with open(ssml_file) as f: ssml = f.read() return Document.from_ssml(ssml) # Save as SSML def save_document(doc, ssml_file): ssml = doc.to_ssml() with open(ssml_file, 'w') as f: f.write(ssml) Testing and Validation ~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python from ssmd import Document # Validate SSML by round-trip conversion def validate_ssml(ssml_text): try: doc = Document.from_ssml(ssml_text) restored_ssml = doc.to_ssml() return True except Exception as e: print(f"Validation failed: {e}") return False Limitations ----------- 1. **Syntax differences**: Round-trip conversion is semantically equivalent but may normalize attribute order or quoting in annotations 2. **Comments lost**: XML comments are not preserved 3. **Unknown elements**: Custom SSML elements are converted to plain text 4. **Attribute order**: Attribute order may change but semantics are preserved 5. **Whitespace**: Whitespace is normalized for readability