Anicet commited on
Commit
ba828c2
·
1 Parent(s): 319c04c

first commit

Browse files
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ venv
2
+ .env
Dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ WORKDIR /app
4
+
5
+ RUN apt-get update && apt-get install -y git ffmpeg
6
+
7
+ COPY . .
8
+
9
+ RUN pip install --no-cache-dir -r requirements.txt
10
+
11
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
functions/utils.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import soundfile as sf
2
+
3
+
4
+ def getAudioDuration(filePath: str) -> float:
5
+ try:
6
+ data, samplerate = sf.read(filePath)
7
+ duration = len(data) / samplerate
8
+ return duration
9
+ except Exception as e:
10
+ print(f"Error getting audio duration: {e}")
11
+ return 0.0
language/saudi_arabe/ar_stt.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64, tempfile, os, torch
2
+ from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
3
+ from functions.utils import getAudioDuration
4
+
5
+ MODEL_NAME = "openai/whisper-large-v3"
6
+ device = "cuda" if torch.cuda.is_available() else "cpu"
7
+
8
+ model = AutoModelForSpeechSeq2Seq.from_pretrained(MODEL_NAME, torch_dtype=torch.float16).to(device)
9
+ processor = AutoProcessor.from_pretrained(MODEL_NAME)
10
+ pipe = pipeline("automatic-speech-recognition", model=model, tokenizer=processor.tokenizer,
11
+ feature_extractor=processor.feature_extractor, torch_dtype=torch.float16, device=device)
12
+
13
+
14
+ def arSTT(audioBase64: str) -> dict:
15
+ audioBytes = base64.b64decode(audioBase64)
16
+
17
+ with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tempFile:
18
+ tempFile.write(audioBytes)
19
+ tempAudioPath = tempFile.name
20
+
21
+ try:
22
+ result = pipe(tempAudioPath, generate_kwargs={"language": "arabic"})
23
+ text = result["text"]
24
+ duration = getAudioDuration(tempAudioPath)
25
+ finally:
26
+ os.remove(tempAudioPath)
27
+
28
+ return {'text': text, 'language': 'ar', 'duration': duration}
language/saudi_arabe/ar_tts.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torchaudio as ta, tempfile, base64, os, torch
2
+ from huggingface_hub import snapshot_download
3
+ from safetensors.torch import load_file as load_safetensors
4
+ from chatterbox import mtl_tts
5
+
6
+ MODEL_NAME = "NAMAA-Space/NAMAA-Saudi-TTS"
7
+ device = "cuda" if torch.cuda.is_available() else "cpu"
8
+
9
+ ckpt_dir = snapshot_download(repo_id=MODEL_NAME)
10
+ model = mtl_tts.ChatterboxMultilingualTTS.from_pretrained(device=device)
11
+ t3_state = load_safetensors(f"{ckpt_dir}/t3_mtl23ls_v2.safetensors", device=device)
12
+ model.t3.load_state_dict(t3_state)
13
+ model.t3.to(device).eval()
14
+
15
+
16
+ def arTTS(text: str) -> str:
17
+ with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tempFile:
18
+ tempAudioPath = tempFile.name
19
+
20
+ try:
21
+ wav = model.generate(text, language_id="ar")
22
+ ta.save(tempAudioPath, wav, model.sr)
23
+
24
+ with open(tempAudioPath, "rb") as file:
25
+ audioBase64 = base64.b64encode(file.read()).decode("utf-8")
26
+ finally:
27
+ os.remove(tempAudioPath)
28
+
29
+ return audioBase64
main.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Request, HTTPException
2
+
3
+ from language.saudi_arabe.ar_stt import arSTT
4
+ from language.saudi_arabe.ar_tts import arTTS
5
+
6
+ import os
7
+ from huggingface_hub import login
8
+ login(token=os.environ["HF_TOKEN"])
9
+
10
+
11
+
12
+ app = FastAPI(
13
+ version='1.0.0',
14
+ root_path='/api',
15
+ )
16
+
17
+
18
+ @app.post("/mms/speechToText")
19
+ async def mmsSpeechToText(request: Request):
20
+ body: dict = await request.json()
21
+ try:
22
+ audioBase64 = body.get('audioBase64')
23
+ sourceLang = body.get('sourceLang')
24
+
25
+ if sourceLang == 'ar':
26
+ data = arSTT(audioBase64=audioBase64)
27
+ return data
28
+ else:
29
+ raise HTTPException(status_code=400, detail=f"STT error: Invalid sourceLang - {sourceLang}")
30
+ except Exception as e:
31
+ print(f"STT error: {e}")
32
+ raise HTTPException(status_code=400, detail=f"STT error: {e}")
33
+
34
+
35
+ @app.post("/mms/textToSpeech")
36
+ async def mmsTextToSpeech(request: Request):
37
+ body: dict = await request.json()
38
+ try:
39
+ text = body.get('text')
40
+ sourceLang = body.get('sourceLang')
41
+
42
+ if sourceLang == 'ar':
43
+ audioBase64 = arTTS(text=text)
44
+ return { 'audioBase64': audioBase64 }
45
+ else:
46
+ raise HTTPException(status_code=400, detail=f"STT error: Invalid sourceLang - {sourceLang}")
47
+ except Exception as e:
48
+ print(f"TTS error: {e}")
49
+ raise HTTPException(status_code=400, detail=f"TTS error: {e}")
requirements.txt ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ absl-py==2.4.0
2
+ aiofiles==24.1.0
3
+ annotated-doc==0.0.4
4
+ annotated-types==0.7.0
5
+ antlr4-python3-runtime==4.9.3
6
+ anyio==4.14.1
7
+ audioread==3.1.0
8
+ bitarray==3.8.2
9
+ bitstring==4.4.0
10
+ brotli==1.2.0
11
+ catalogue==2.0.10
12
+ certifi==2026.6.17
13
+ cffi==2.0.0
14
+ cfgv==3.5.0
15
+ charset-normalizer==3.4.7
16
+ chatterbox-tts @ git+https://github.com/resemble-ai/chatterbox.git@65b18437192794391a0308a8f705b1e33e633948
17
+ click==8.4.2
18
+ conformer==0.3.2
19
+ contourpy==1.3.2
20
+ cycler==0.12.1
21
+ decorator==5.3.1
22
+ Deprecated==1.3.1
23
+ diffusers==0.29.0
24
+ distlib==0.4.3
25
+ einops==0.8.2
26
+ exceptiongroup==1.3.1
27
+ fastapi==0.138.1
28
+ ffmpy==1.0.0
29
+ filelock==3.29.4
30
+ fonttools==4.63.0
31
+ fsspec==2026.6.0
32
+ gradio==6.8.0
33
+ gradio_client==2.2.0
34
+ groovy==0.1.2
35
+ grpcio==1.81.1
36
+ h11==0.16.0
37
+ hf-xet==1.5.1
38
+ httpcore==1.0.9
39
+ httpx==0.28.1
40
+ huggingface_hub==1.21.0
41
+ identify==2.6.19
42
+ idna==3.18
43
+ importlib_metadata==9.0.0
44
+ jaconv==0.5.0
45
+ Jinja2==3.1.6
46
+ joblib==1.5.3
47
+ kiwisolver==1.5.0
48
+ lazy-loader==0.5
49
+ librosa==0.11.0
50
+ llvmlite==0.47.0
51
+ Markdown==3.10.2
52
+ markdown-it-py==4.2.0
53
+ MarkupSafe==3.0.3
54
+ matplotlib==3.10.9
55
+ mdurl==0.1.2
56
+ ml_dtypes==0.5.4
57
+ mpmath==1.3.0
58
+ msgpack==1.2.1
59
+ networkx==3.4.2
60
+ nodeenv==1.10.0
61
+ numba==0.65.1
62
+ numpy==1.26.4
63
+ omegaconf==2.3.1
64
+ onnx==1.22.0
65
+ orjson==3.11.9
66
+ packaging==26.2
67
+ pandas==2.3.3
68
+ pillow==12.2.0
69
+ platformdirs==4.10.0
70
+ pooch==1.9.0
71
+ praat-parselmouth==0.4.7
72
+ pre_commit==4.6.0
73
+ protobuf==7.35.1
74
+ pycparser==3.0
75
+ pydantic==2.13.4
76
+ pydantic_core==2.46.4
77
+ pydub==0.25.1
78
+ Pygments==2.20.0
79
+ pykakasi==2.3.0
80
+ pyloudnorm==0.2.0
81
+ pyparsing==3.3.2
82
+ pyrubberband==0.4.0
83
+ python-dateutil==2.9.0.post0
84
+ python-discovery==1.4.2
85
+ python-multipart==0.0.32
86
+ pytz==2026.2
87
+ PyWavelets==1.8.0
88
+ PyYAML==6.0.3
89
+ regex==2026.5.9
90
+ requests==2.34.2
91
+ # resemble-perth @ git+https://github.com/resemble-ai/Perth.git@ce86c49d029f42272c1902eccb675556b9ed2330
92
+ rich==15.0.0
93
+ s3tokenizer==0.3.0
94
+ safehttpx==0.1.7
95
+ safetensors==0.5.3
96
+ scikit-learn==1.7.2
97
+ scipy==1.15.3
98
+ semantic-version==2.10.0
99
+ shellingham==1.5.4
100
+ six==1.17.0
101
+ soundfile==0.14.0
102
+ sox==1.5.0
103
+ soxr==1.1.0
104
+ spacy_pkuseg==1.0.1
105
+ srsly==2.5.3
106
+ starlette==0.52.1
107
+ sympy==1.13.1
108
+ tabulate==0.10.0
109
+ tensorboard==2.20.0
110
+ tensorboard-data-server==0.7.2
111
+ threadpoolctl==3.6.0
112
+ tibs==0.5.7
113
+ tokenizers==0.22.2
114
+ tomlkit==0.13.3
115
+ torch==2.6.0
116
+ torchaudio==2.6.0
117
+ tqdm==4.68.3
118
+ transformers==5.2.0
119
+ typer==0.25.1
120
+ typer-slim==0.24.0
121
+ typing-inspection==0.4.2
122
+ typing_extensions==4.15.0
123
+ tzdata==2026.2
124
+ urllib3==2.7.0
125
+ uvicorn==0.49.0
126
+ virtualenv==21.5.1
127
+ Werkzeug==3.1.8
128
+ wrapt==2.2.2
129
+ zipp==4.1.0