Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,95 @@ Launch the web application:
In debug mode:
```
FLASK_APP=shabda FLASK_DEBUG=1 flask run

or

FLASK_APP=shabda FLASK_DEBUG=1 poetry run flask run

```
In production:
```
gunicorn --workers=4 "shabda:create_app()" -b localhost:8000
```

Then navigate to `http://localhost:5000/` (or your production URL).

### Pack Tab

Fetch random audio samples from Freesound based on words.

- **Pack definition**: Enter one or more words separated by commas (e.g., `blue,red:3`).
- **Licenses**: Select which Creative Commons licenses to allow (cc0, by, by-nc).
- **Fetch pack**: Generates a reslist with sample URLs.

Use in Estuary:
```
!reslist "http://your-shabda-url/blue,red.json"
```

Use in Strudel:
```
samples('shabda:blue:2,red:3')
```

### Speech Tab

Generate Text-to-Speech samples from text input.

- **Speech definition**: Enter words or sentences (spaces become underscores; `hello_world` produces a "hello world" sample).
- **Gender**: Choose Female or Male voice.
- **Language**: Select from 50+ languages (e.g., en-GB, en-US, fr-FR, ja-JP).
- **Fetch speech**: Generates audio samples using the selected voice.

Use in Estuary:
```
!reslist "http://your-shabda-url/speech/hello_world.json?language=en-GB&gender=f"
```

Use in Strudel:
```
samples('shabda/speech/en-GB/f:hello_world')
```

### Phonemes Tab

Generate phoneme-level samples with musical timing alignment. Each phoneme chunk becomes a sample, with stress-aware line wrapping for Strudel integration.

**Inputs:**
- **Phoneme definition**: Enter lyrics (one or more sentences, any case). Commas and underscores separate phrases. Example:
```
Papa was a rolling stone
Wherever he laid his hat
```
- **ARPABET overrides**: Specify custom pronunciations for words in `word:PH1_PH2 format` (e.g., `papa:P_AA1_P_A;hat:HH_AE1_T`). Separate multiple overrides with semicolons.
- **Beats / bar**: Musical meter (default 4 for 4/4 time).
- **Bars / line**: Lines per phrase (default 2).
- **Target stress beat**: Which beat in the bar should primary stressed syllables land on (default 3 for a pickup feel; use 1 for downbeat emphasis).
- **Gender**: Choose Female or Male voice (default: Male).
- **Language**: Select pronunciation language (default: en-GB).

**Actions:**
- **Preview phonemes**: Returns text analysis and Strudel timing without synthesizing audio (fast).
- **Fetch phonemes**: Synthesizes all phoneme samples as WAV files (slower first time, then cached).

**Output includes:**
- IPA transcription of each word
- ARPABET phoneme breakdown
- Stress pattern analysis
- Timed Strudel phrases with beat alignment
- Direct audio sample URLs

Use in Strudel:
```
samples('shabda/phonemes/en-GB/m:papa_was_a_rolling_stone', { overrides: 'papa:P_AA1_P_A' })
s("phc_P_AA1_P_A phc_W_AA1_Z phc_AH0 ~ phc_R_OW1_L_IH0_NG ...")
```

**How stress alignment works:**
- If a line's first phoneme chunk is stressed (marked with 1 or 2), it lands on beat 1 (no rest needed).
- If a line starts unstressed, rests are added so the first stressed chunk lands on the target beat (default beat 3).
- This ensures consistent musical phrasing across all lines.

Test
----

Expand Down
110 changes: 107 additions & 3 deletions assets/shabda.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ body {
}

#definition,
#speechdefinition {
#speechdefinition,
#phonemedefinition {
border: 2px dotted #110072;
border-radius: 5px;
padding: 10px;
Expand All @@ -44,8 +45,15 @@ body {
color: #110072;
}

#phonemedefinition {
min-height: 4.5em;
width: min(42rem, calc(100% - 20px));
resize: vertical;
}

#definition:focus,
#speechdefinition:focus {
#speechdefinition:focus,
#phonemedefinition:focus {
outline: none;
border: 2px solid #110072;
}
Expand All @@ -69,6 +77,25 @@ body {
margin-left: 10px;
}

#timing {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
margin: 10px 0;
}

#timing label {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.85em;
}

#timing input[type=number] {
width: 5em;
}

#tabs span {
display: inline-block;
padding: 10px;
Expand Down Expand Up @@ -98,7 +125,6 @@ button,
margin: 10px;
color: #110072;
cursor: pointer;
background-color: white;
text-decoration-line: none;
font-size: 14px;
}
Expand All @@ -114,6 +140,31 @@ button:active,
box-shadow: 1px 1px 2px #110072;
}

#overrides {
margin: 8px 10px;
}

#overrides label {
display: block;
font-size: 0.85em;
margin-bottom: 4px;
}

#phoneme-overrides {
width: min(42rem, calc(100% - 20px));
border: 2px dotted #110072;
border-radius: 5px;
padding: 10px;
margin: 10px;
background-color: transparent;
color: #110072;
}

#phoneme-overrides:focus {
outline: none;
border: 2px solid #110072;
}

button img,
.button img {
vertical-align: top;
Expand All @@ -135,6 +186,59 @@ button img,
cursor: pointer;
}

.phoneme-results {
display: block;
}

.phoneme-sentences {
margin-top: 16px;
}

.phoneme-sentence {
display: flex;
flex-wrap: wrap;
gap: 10px;
margin-bottom: 12px;
}

.phoneme-word {
min-width: 10em;
padding: 10px 12px;
border: 1px solid #110072;
border-radius: 8px;
background-color: rgba(255, 255, 255, 0.5);
}

.phoneme-word strong {
display: block;
margin-bottom: 4px;
font-size: 1rem;
}

.phoneme-ipa,
.phoneme-arpabet,
.phoneme-stress {
display: block;
font-family: monospace;
font-size: 0.85em;
line-height: 1.3;
}

.phoneme-strudel,
.phoneme-audio {
margin-top: 18px;
}

.phoneme-bank {
margin-bottom: 12px;
}

.phoneme-bank h4,
.phoneme-strudel h3,
.phoneme-audio h3 {
margin-bottom: 6px;
}

.copied {
margin-left: 5px;
opacity: 0;
Expand Down
Loading