File size: 2,557 Bytes
c42fe7e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# webapp/components/phoneme_editor.py
from typing import List, Dict
import streamlit as st
from webapp.services.phonemes.phoneme_dict import PHONEMES


def default_phoneme_for_lyric(lyric: str) -> str:
    """
    Simple rule-based default phoneme suggestion for a lyric syllable.

    - Direct match if the lyric exactly matches a phoneme
    - Greedy two-letter prefix match
    - Single-letter fallback
    - Default to 'a'
    """
    l = lyric.lower()
    if l in PHONEMES:
        return l
    if len(l) >= 2 and l[:2] in PHONEMES:
        return l[:2]
    if l and l[0] in PHONEMES:
        return l[0]
    return "a"


def render_phoneme_editor(notes: List[Dict]) -> List[Dict]:
    """
    Display an editable phoneme alignment table in Streamlit, including duration edits.

    Args:
        notes: list of dicts each containing:
            - id: unique identifier
            - pitch: pitch string (e.g. "C4")
            - duration: float, original duration in seconds
            - lyric: string, the syllable/lyric text
            - is_slur: int (0 or 1)
    Returns:
        List of dicts with updated 'duration', 'phoneme', and 'is_slur' fields:
    """
    st.header("2. Phoneme & Duration Alignment")
    st.write("Adjust duration and select phoneme for each note:")

    edited_notes: List[Dict] = []
    for note in notes:
        # Columns: Pitch, Duration input, Lyric, Phoneme select, Slur checkbox
        cols = st.columns([1, 1.2, 1, 2, 1])
        cols[0].markdown(f"**Pitch:** {note['pitch']}")
        # Editable duration
        duration = cols[1].number_input(
            label="Dur (s)",
            min_value=0.0,
            value=note['duration'],
            step=0.01,
            format="%.3f",
            key=f"dur_{note['id']}"
        )
        cols[2].markdown(f"**Lyric:** {note['lyric']}")

        # Default phoneme suggestion
        default = default_phoneme_for_lyric(note['lyric'])
        default_idx = PHONEMES.index(default) if default in PHONEMES else 0
        phoneme = cols[3].selectbox(
            label="Phoneme",
            options=PHONEMES,
            index=default_idx,
            key=f"phoneme_{note['id']}"
        )

        # Slur toggle
        is_slur = cols[4].checkbox(
            label="Slur",
            value=bool(note.get('is_slur', 0)),
            key=f"slur_{note['id']}"
        )

        edited_notes.append({
            **note,
            "duration": duration,
            "phoneme": phoneme,
            "is_slur": int(is_slur)
        })

    return edited_notes