# core


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

This module loads Vāgdhenu and renders Sanskrit text with it. Vāgdhenu
([code](https://github.com/prathoshap/vagdhenu),
[weights](https://huggingface.co/prathoshap/vagdhenu), Apache-2.0) is a
fine-tune of IndicF5, a text-to-speech model, with a BigVGAN-v2 vocoder,
trained on one speaker’s chanting. Its source code, BigVGAN’s and
IndicF5’s (the `f5_tts` package, which is not the `f5-tts` on PyPI) are
not pip packages. So on first use
[`fetch_src`](https://vedicreader.github.io/vachaka/core.html#fetch_src)
downloads each of them at a fixed commit, listed in `SRCS`, into the
cache folder, and
[`setup_paths`](https://vedicreader.github.io/vachaka/core.html#setup_paths)
puts them on `sys.path`. The weights come from the Hugging Face Hub.
[`best_device`](https://vedicreader.github.io/vachaka/core.html#best_device)
picks CUDA if it is there, then Apple’s MPS, then the CPU.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L28"
target="_blank" style="float:right; font-size:smaller">source</a>

### fetch_src

``` python
def fetch_src(
    name
):
```

*The path to the source code of `name` at its fixed commit, downloaded
on first use.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L38"
target="_blank" style="float:right; font-size:smaller">source</a>

### setup_paths

``` python
def setup_paths():
```

*Put Vāgdhenu’s `src`, BigVGAN and IndicF5 on `sys.path`.*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L45"
target="_blank" style="float:right; font-size:smaller">source</a>

### best_device

``` python
def best_device():
```

*The best available torch device: cuda, then mps, then cpu.*

## Text

The model reads plain Devanagari, so `clean(s)` removes Vedic accent
marks and digits, and reduces each run of daṇḍas (`।` and `॥`, with the
verse number between them) to a single daṇḍa, keeping `॥` where there
was one. Vāgdhenu has no svaras (Vedic pitch accents) and chants in the
classical, laukika, style.

`n_syll(s)` counts the akṣaras, the syllables of Devanagari text: each
independent vowel and each consonant not followed by a virāma.
`chunk_prose(text, max_syll)` splits prose into pieces Vāgdhenu can
render: first at daṇḍas, then between words, so that no piece has more
than `max_syll` (24) akṣaras.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L53"
target="_blank" style="float:right; font-size:smaller">source</a>

### clean

``` python
def clean(
    s
):
```

*Plain Devanagari for the model, without accents, digits or verse
numbers between daṇḍas.*

``` python
test_eq(clean('अग्निमी॑ळे पु॒रोहि॑तं ॥ १ ॥'), 'अग्निमीळे पुरोहितं ॥')
test_eq(clean('धर्मक्षेत्रे कुरुक्षेत्रे ।'), 'धर्मक्षेत्रे कुरुक्षेत्रे ।')
```

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L61"
target="_blank" style="float:right; font-size:smaller">source</a>

### n_syll

``` python
def n_syll(
    s
):
```

*The number of akṣaras in Devanagari `s`: independent vowels and
consonants without a virāma.*

``` python
test_eq(n_syll('कर्मण्येवाधिकारस्ते'), 8)
```

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L66"
target="_blank" style="float:right; font-size:smaller">source</a>

### chunk_prose

``` python
def chunk_prose(
    text, max_syll:int=24
):
```

*Split prose at daṇḍas, then between words, into pieces of at most
`max_syll` akṣaras.*

``` python
p = chunk_prose('अथातो धर्मजिज्ञासा । चोदनालक्षणोऽर्थो धर्मः । तस्य निमित्तपरीष्टिः ॥')
test_eq(p, ['अथातो धर्मजिज्ञासा', 'चोदनालक्षणोऽर्थो धर्मः', 'तस्य निमित्तपरीष्टिः'])
assert all(n_syll(c) <= 24 for c in chunk_prose('क '*80))
```

## Sound

The raw render can sound thin, so `polish(src, dst, fx)` applies a named
sound treatment to an audio file with ffmpeg. The treatments in `FX`
were chosen by ear for listeners, not sound engineers. Each ends by
normalising loudness to -16 LUFS, so every recording plays at the same
volume, and none moves any sound in time: the echo adds decay after a
syllable, never delay before it, so line and word timings stay valid.
`warm`, the default, adds a little warmth below 250 Hz, softens hissing
s sounds and adds the short echo of a small room. `phone` brings the
words forward for small speakers. `plain` only normalises loudness.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L84"
target="_blank" style="float:right; font-size:smaller">source</a>

### polish

``` python
def polish(
    src, dst, fx:str='warm', kbps:int=96
):
```

*Apply the sound treatment `FX[fx]` to audio file `src` and write it to
`dst`.*

``` python
test_eq(sorted(FX), ['phone', 'plain', 'warm'])
assert all(v.endswith(LN) for v in FX.values())
```

## Recitation

A
[`Recitation`](https://vedicreader.github.io/vachaka/core.html#recitation)
is what every render returns: the audio as a numpy array, its sample
rate `sr`, one `(start_ms, end_ms)` span per piece in `spans`, the text
of each piece in `texts`, and, once Su-śrotā has checked it, the
character error rate
[`cer`](https://vedicreader.github.io/vachaka/check.html#cer). A piece
is a line given to
[`Chanter.chant`](https://vedicreader.github.io/vachaka/core.html#chanter.chant)
or a chunk made by
[`Chanter.read`](https://vedicreader.github.io/vachaka/core.html#chanter.read).
`ms` is the length in milliseconds. `save` writes a wav file, or an mp3
through ffmpeg when the path ends in `.mp3`, optionally shifted `pitch`
semitones and with an `FX` treatment; the timings stay valid.
[`concat`](https://vedicreader.github.io/vachaka/core.html#concat), or
`+`, joins recitations with a silence between them and moves the later
spans along to match.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L91"
target="_blank" style="float:right; font-size:smaller">source</a>

### Recitation

``` python
def Recitation(
    audio:numpy.ndarray, spans:list=<factory>, texts:list=<factory>, sr:int=24000, cer:float=None
)->None:
```

*Recitation(audio: numpy.ndarray, spans: list = <factory>, texts: list =
<factory>, sr: int = 24000, cer: float = None)*

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L115"
target="_blank" style="float:right; font-size:smaller">source</a>

### concat

``` python
def concat(
    recs, gap:float=0.8
):
```

*Join recitations with `gap` seconds of silence between them, moving
their spans to match.*

``` python
a = Recitation(np.zeros(SR, np.float32), [(0, 900)], ['क'])
b = a + a
test_eq(b.ms, 2800)
test_eq(b.spans, [(0, 900), (1800, 2700)])
```

## Chanter

[`Chanter()`](https://vedicreader.github.io/vachaka/core.html#chanter)
loads Vāgdhenu’s network, the vocoder and the bank of reference
recordings once, and then renders as often as you like. Vāgdhenu sings a
text in the style of a reference recording: the reference sets the pace
and melody, and the timbre is always the fine-tuned voice. The bank
holds one reference for each metre Vāgdhenu supports, plus `gadya`
references for prose.

- `chant(lines)` sings one verse given as a list of its lines, or as a
  string that it splits at newlines and daṇḍas. It finds the metre with
  [ganapati](https://github.com/vedicreader/ganapati) through `meter`,
  renders against that metre’s reference, and uses vasantatilakā when
  nothing is detected. Pass `meter=` to choose one. The result has one
  span per line.
- `read(text)` reads prose, split by
  [`chunk_prose`](https://vedicreader.github.io/vachaka/core.html#chunk_prose),
  against the `gadya` reference by default, or against another reference
  given as `ref`. The result has one span per chunk.
- `render(pieces, ref)` is what both call: it renders already cleaned
  pieces against one reference.
- `meters()` lists the metres in the bank, and `bank_ref(name)` finds a
  metre’s reference under any spelling (IAST, plain ASCII or the
  recording’s file name), or returns None.

`pace` stretches every syllable; the default of 1.15 is unhurried, for
chanting along. `seed` picks the random seed for a render; a different
seed gives a slightly different performance. `voice` chooses between the
two fine-tuned voices in `VOICES`. On Apple silicon the main network
runs on MPS and BigVGAN on the CPU, because MPS has no transposed
convolution with more than 65,536 output channels.

------------------------------------------------------------------------

<a
href="https://github.com/vedicreader/vachaka/blob/main/vachaka/core.py#L151"
target="_blank" style="float:right; font-size:smaller">source</a>

### Chanter

``` python
def Chanter(
    voice:str='steer', device:NoneType=None, nfe:int=64, speed:float=0.9, gap:float=0.55, prose_gap:float=0.3,
    tail:float=0.18, fade:float=0.12, pad:float=0.5, pace:float=1.15
):
```

*Renders Sanskrit verse and prose to audio with Vāgdhenu, with a time
span per line.*

Rendering needs the weights, about 1.5 GB downloaded on first use, and
takes several times real time on Apple silicon, so these cells do not
run in the tests.

``` python
ch = Chanter()
r = ch.chant(['वसुदेवसुतं देवं कंसचाणूरमर्दनम्', 'देवकीपरमानन्दं कृष्णं वन्दे जगद्गुरुम्'])
r.spans, r.save('/tmp/krishna.mp3')
```

``` python
ch.read('अथातो धर्मजिज्ञासा । चोदनालक्षणोऽर्थो धर्मः ।').save('/tmp/mimamsa.mp3')
```
