mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 21:50:28 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
023884d6d8 | ||
|
|
aa98547456 | ||
|
|
528343f375 | ||
|
|
7fa8b23c4e | ||
|
|
3171066f0c | ||
|
|
c1c93ea00a | ||
|
|
795dee84ad | ||
|
|
eb4575df23 | ||
|
|
f58b254aed | ||
|
|
e073c32e64 | ||
|
|
509477bafa | ||
|
|
867d373eb9 | ||
|
|
f727c5ca70 | ||
|
|
e48df7c106 |
@@ -5,7 +5,7 @@ on:
|
|||||||
#release:
|
#release:
|
||||||
# types: [published]
|
# types: [published]
|
||||||
# Build only
|
# Build only
|
||||||
#push:
|
#push: it
|
||||||
# branches: [main]
|
# branches: [main]
|
||||||
# TODO - enable build on pull requests if build times can be reduced
|
# TODO - enable build on pull requests if build times can be reduced
|
||||||
# pull_request:
|
# pull_request:
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Login to Github Container Registry
|
- name: Login to Github Container Registry
|
||||||
# Only if we need to push an image
|
# Only if we need to push an image
|
||||||
if: ${{ github.event_name == 'release' && github.event.action == 'published' }}
|
# if: ${{ github.event_name == 'release' && github.event.action == 'published' }}
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
@@ -51,11 +51,13 @@ jobs:
|
|||||||
context: ./abogen
|
context: ./abogen
|
||||||
file: ./abogen/Dockerfile
|
file: ./abogen/Dockerfile
|
||||||
# platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
|
# platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
|
||||||
platforms: linux/amd64,linux/arm64
|
# platforms: linux/amd64,linux/arm64
|
||||||
|
platforms: linux/amd64 # using the solution mentioned in https://github.com/denizsafak/abogen/issues/46
|
||||||
# Only push if we are publishing a release
|
# Only push if we are publishing a release
|
||||||
push: ${{ github.event_name == 'release' && github.event.action == 'published' }}
|
# push: ${{ github.event_name == 'release' && github.event.action == 'published' }}
|
||||||
|
push: true
|
||||||
# Use a 'temp' tag, that won't be pushed, for non-release builds
|
# Use a 'temp' tag, that won't be pushed, for non-release builds
|
||||||
tags: ${{ env.IMAGE_REPOSITORY }}:${{ github.event.release.tag_name || 'temp' }}
|
tags: ${{ env.IMAGE_REPOSITORY }}:${{ github.event.release.tag_name || 'latest' }}
|
||||||
# Use a cache to reduce build times
|
# Use a cache to reduce build times
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
# 1.1.7
|
||||||
|
- Added MPS GPU acceleration support for Silicon Mac, mentioned in https://github.com/denizsafak/abogen/issues/32#issuecomment-3155902040 by @jefro108. **Please read the [Mac](https://github.com/denizsafak/abogen?tab=readme-ov-file#mac) section in the documentation again, as it requires additional configuration.**
|
||||||
|
- Added word-by-word karaoke highlighting feature by @robmckinnon in PR #65
|
||||||
|
- Fixed sleep inhibition error occurring on some Linux systems that do not use systemd, mentioned in #67 by @hendrack
|
||||||
|
- Improvements in code and documentation.
|
||||||
|
|
||||||
# 1.1.6
|
# 1.1.6
|
||||||
- Improved EPUB chapter detection: Now reliably detects chapters from NAV HTML (TOC) files, even in non-standard EPUBs, fixes the issue mentioned by @jefro108 in #33
|
- Improved EPUB chapter detection: Now reliably detects chapters from NAV HTML (TOC) files, even in non-standard EPUBs, fixes the issue mentioned by @jefro108 in #33
|
||||||
- Fixed SRT subtitle numbering issue, mentioned by @page-muncher in #41
|
- Fixed SRT subtitle numbering issue, mentioned by @page-muncher in #41
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ Abogen is a powerful text-to-speech conversion tool that makes it easy to turn e
|
|||||||
<img title="Abogen Main" src='https://raw.githubusercontent.com/denizsafak/abogen/refs/heads/main/demo/abogen.png' width="380"> <img title="Abogen Processing" src='https://raw.githubusercontent.com/denizsafak/abogen/refs/heads/main/demo/abogen2.png' width="380">
|
<img title="Abogen Main" src='https://raw.githubusercontent.com/denizsafak/abogen/refs/heads/main/demo/abogen.png' width="380"> <img title="Abogen Processing" src='https://raw.githubusercontent.com/denizsafak/abogen/refs/heads/main/demo/abogen2.png' width="380">
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
https://github.com/user-attachments/assets/cb66512d-0a52-48c3-bda4-f1e6a03fb8d6
|
|
||||||
|
https://github.com/user-attachments/assets/094ba3df-7d66-494a-bc31-0e4b41d0b865
|
||||||
|
|
||||||
> This demo was generated in just 5 seconds, producing ∼1 minute of audio with perfectly synced subtitles. To create a similar video, see [the demo guide](https://github.com/denizsafak/abogen/tree/main/demo).
|
> This demo was generated in just 5 seconds, producing ∼1 minute of audio with perfectly synced subtitles. To create a similar video, see [the demo guide](https://github.com/denizsafak/abogen/tree/main/demo).
|
||||||
|
|
||||||
@@ -60,6 +61,10 @@ source venv/bin/activate
|
|||||||
|
|
||||||
# Install abogen
|
# Install abogen
|
||||||
pip3 install abogen
|
pip3 install abogen
|
||||||
|
|
||||||
|
# For Silicon Mac (M1, M2 etc.)
|
||||||
|
# After installing abogen, we need to install Kokoro's development version which includes MPS support.
|
||||||
|
pip3 install git+https://github.com/hexgrad/kokoro.git
|
||||||
```
|
```
|
||||||
### Linux
|
### Linux
|
||||||
```bash
|
```bash
|
||||||
@@ -129,7 +134,7 @@ Here’s Abogen in action: in this demo, it processes ∼3,000 characters of tex
|
|||||||
| **Select Voice** | First letter of the language code (e.g., `a` for American English, `b` for British English, etc.), second letter is for `m` for male and `f` for female. |
|
| **Select Voice** | First letter of the language code (e.g., `a` for American English, `b` for British English, etc.), second letter is for `m` for male and `f` for female. |
|
||||||
| **Voice mixer** | Create custom voices by mixing different voice models with a profile system. See [Voice Mixer](#voice-mixer) for more details. |
|
| **Voice mixer** | Create custom voices by mixing different voice models with a profile system. See [Voice Mixer](#voice-mixer) for more details. |
|
||||||
| **Voice preview** | Listen to the selected voice before processing. |
|
| **Voice preview** | Listen to the selected voice before processing. |
|
||||||
| **Generate subtitles** | `Disabled`, `Sentence`, `Sentence + Comma`, `1 word`, `2 words`, `3 words`, etc. (Represents the number of words in each subtitle entry) |
|
| **Generate subtitles** | `Disabled`, `Sentence`, `Sentence + Comma`, `Sentence + Highlighting`, `1 word`, `2 words`, `3 words`, etc. (Represents the number of words in each subtitle entry) |
|
||||||
| **Output voice format** | `.WAV`, `.FLAC`, `.MP3`, `.OPUS (best compression)` and `M4B (with chapters)` (Special thanks to [@jborza](https://github.com/jborza) for chapter support in PR [#10](https://github.com/denizsafak/abogen/pull/10)) |
|
| **Output voice format** | `.WAV`, `.FLAC`, `.MP3`, `.OPUS (best compression)` and `M4B (with chapters)` (Special thanks to [@jborza](https://github.com/jborza) for chapter support in PR [#10](https://github.com/denizsafak/abogen/pull/10)) |
|
||||||
| **Output subtitle format** | Configures the subtitle format as `SRT (standard)`, `ASS (wide)`, `ASS (narrow)`, `ASS (centered wide)`, or `ASS (centered narrow)`. |
|
| **Output subtitle format** | Configures the subtitle format as `SRT (standard)`, `ASS (wide)`, `ASS (narrow)`, `ASS (centered wide)`, or `ASS (centered narrow)`. |
|
||||||
| **Replace single newlines with spaces** | Replaces single newlines with spaces in the text. This is useful for texts that have imaginary line breaks. |
|
| **Replace single newlines with spaces** | Replaces single newlines with spaces in the text. This is useful for texts that have imaginary line breaks. |
|
||||||
@@ -156,6 +161,8 @@ Here’s Abogen in action: in this demo, it processes ∼3,000 characters of tex
|
|||||||
| **Disable Kokoro's internet access** | Prevents Kokoro from downloading models or voices from HuggingFace Hub, useful for offline use. |
|
| **Disable Kokoro's internet access** | Prevents Kokoro from downloading models or voices from HuggingFace Hub, useful for offline use. |
|
||||||
| **Reset to default settings** | Resets all settings to their default values. |
|
| **Reset to default settings** | Resets all settings to their default values. |
|
||||||
|
|
||||||
|
> Special thanks to [@robmckinnon](https://github.com/robmckinnon) for adding Sentence + Highlighting feature in PR [#65](https://github.com/denizsafak/abogen/pull/65)
|
||||||
|
|
||||||
## `Voice Mixer`
|
## `Voice Mixer`
|
||||||
<img title="Abogen Voice Mixer" src='https://raw.githubusercontent.com/denizsafak/abogen/refs/heads/main/demo/voice_mixer.png'>
|
<img title="Abogen Voice Mixer" src='https://raw.githubusercontent.com/denizsafak/abogen/refs/heads/main/demo/voice_mixer.png'>
|
||||||
|
|
||||||
@@ -221,6 +228,9 @@ Similar to chapter markers, it is possible to add metadata tags for `M4B` files.
|
|||||||
```
|
```
|
||||||
For a complete list of supported languages and voices, refer to Kokoro's [VOICES.md](https://huggingface.co/hexgrad/Kokoro-82M/blob/main/VOICES.md). To listen to sample audio outputs, see [SAMPLES.md](https://huggingface.co/hexgrad/Kokoro-82M/blob/main/SAMPLES.md).
|
For a complete list of supported languages and voices, refer to Kokoro's [VOICES.md](https://huggingface.co/hexgrad/Kokoro-82M/blob/main/VOICES.md). To listen to sample audio outputs, see [SAMPLES.md](https://huggingface.co/hexgrad/Kokoro-82M/blob/main/SAMPLES.md).
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Japanese audio may require additional configuration. Please check [#56](https://github.com/denizsafak/abogen/issues/56) for more information.
|
||||||
|
|
||||||
## `MPV Config`
|
## `MPV Config`
|
||||||
I highly recommend using [MPV](https://mpv.io/installation/) to play your audio files, as it supports displaying subtitles even without a video track. Here's my `mpv.conf`:
|
I highly recommend using [MPV](https://mpv.io/installation/) to play your audio files, as it supports displaying subtitles even without a video track. Here's my `mpv.conf`:
|
||||||
```
|
```
|
||||||
|
|||||||
+1
-1
@@ -263,7 +263,7 @@ if exist %PYPROJECT_FILE% (
|
|||||||
:: Install misaki again if MISAKI_LANG is not set to "en"
|
:: Install misaki again if MISAKI_LANG is not set to "en"
|
||||||
if "%MISAKI_LANG%" NEQ "en" (
|
if "%MISAKI_LANG%" NEQ "en" (
|
||||||
echo Configuring language pack: %MISAKI_LANG%
|
echo Configuring language pack: %MISAKI_LANG%
|
||||||
%PYTHON_CONSOLE_PATH% -m pip install misaki[lang] --upgrade --no-warn-script-location
|
%PYTHON_CONSOLE_PATH% -m pip install misaki[%MISAKI_LANG%] --upgrade --no-warn-script-location
|
||||||
if errorlevel 1 (
|
if errorlevel 1 (
|
||||||
echo Failed to install misaki language pack.
|
echo Failed to install misaki language pack.
|
||||||
pause
|
pause
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
1.1.6
|
1.1.7
|
||||||
+93
-6
@@ -23,6 +23,7 @@ import abogen.hf_tracker as hf_tracker
|
|||||||
import static_ffmpeg
|
import static_ffmpeg
|
||||||
import threading # for efficient waiting
|
import threading # for efficient waiting
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import platform
|
||||||
|
|
||||||
|
|
||||||
def get_sample_voice_text(lang_code):
|
def get_sample_voice_text(lang_code):
|
||||||
@@ -310,8 +311,15 @@ class ConversionThread(QThread):
|
|||||||
|
|
||||||
self.log_updated.emit("\nInitializing TTS pipeline...")
|
self.log_updated.emit("\nInitializing TTS pipeline...")
|
||||||
|
|
||||||
# Set device based on use_gpu setting
|
# Set device based on use_gpu setting and platform
|
||||||
device = "cuda" if self.use_gpu else "cpu"
|
if self.use_gpu:
|
||||||
|
if platform.system() == "Darwin" and platform.processor() == "arm":
|
||||||
|
device = "mps" # Use MPS for Apple Silicon
|
||||||
|
else:
|
||||||
|
device = "cuda" # Use CUDA for other platforms
|
||||||
|
else:
|
||||||
|
device = "cpu"
|
||||||
|
|
||||||
tts = self.KPipeline(
|
tts = self.KPipeline(
|
||||||
lang_code=self.lang_code, repo_id="hexgrad/Kokoro-82M", device=device
|
lang_code=self.lang_code, repo_id="hexgrad/Kokoro-82M", device=device
|
||||||
)
|
)
|
||||||
@@ -558,6 +566,11 @@ class ConversionThread(QThread):
|
|||||||
merged_subtitle_file.write("[Script Info]\n")
|
merged_subtitle_file.write("[Script Info]\n")
|
||||||
merged_subtitle_file.write("Title: Generated by Abogen\n")
|
merged_subtitle_file.write("Title: Generated by Abogen\n")
|
||||||
merged_subtitle_file.write("ScriptType: v4.00+\n\n")
|
merged_subtitle_file.write("ScriptType: v4.00+\n\n")
|
||||||
|
# Add style definitions for karaoke highlighting
|
||||||
|
if self.subtitle_mode == "Sentence + Highlighting":
|
||||||
|
merged_subtitle_file.write("[V4+ Styles]\n")
|
||||||
|
merged_subtitle_file.write("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n")
|
||||||
|
merged_subtitle_file.write("Style: Default,Arial,24,&H00FFFFFF,&H00808080,&H00000000,&H00404040,0,0,0,0,100,100,0,0,3,2,0,5,10,10,10,1\n\n")
|
||||||
merged_subtitle_file.write("[Events]\n")
|
merged_subtitle_file.write("[Events]\n")
|
||||||
merged_subtitle_file.write(
|
merged_subtitle_file.write(
|
||||||
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
|
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
|
||||||
@@ -700,6 +713,13 @@ class ConversionThread(QThread):
|
|||||||
chapter_subtitle_file.write("[Script Info]\n")
|
chapter_subtitle_file.write("[Script Info]\n")
|
||||||
chapter_subtitle_file.write("Title: Generated by Abogen\n")
|
chapter_subtitle_file.write("Title: Generated by Abogen\n")
|
||||||
chapter_subtitle_file.write("ScriptType: v4.00+\n\n")
|
chapter_subtitle_file.write("ScriptType: v4.00+\n\n")
|
||||||
|
|
||||||
|
# Add style definitions for karaoke highlighting
|
||||||
|
if self.subtitle_mode == "Sentence + Highlighting":
|
||||||
|
chapter_subtitle_file.write("[V4+ Styles]\n")
|
||||||
|
chapter_subtitle_file.write("Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding\n")
|
||||||
|
chapter_subtitle_file.write("Style: Default,Arial,24,&H00FFFFFF,&H00808080,&H00000000,&H00404040,0,0,0,0,100,100,0,0,3,2,0,5,10,10,10,1\n\n")
|
||||||
|
|
||||||
chapter_subtitle_file.write("[Events]\n")
|
chapter_subtitle_file.write("[Events]\n")
|
||||||
chapter_subtitle_file.write(
|
chapter_subtitle_file.write(
|
||||||
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
|
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
|
||||||
@@ -823,8 +843,10 @@ class ConversionThread(QThread):
|
|||||||
for start, end, text in new_entries:
|
for start, end, text in new_entries:
|
||||||
start_time = self._ass_time(start)
|
start_time = self._ass_time(start)
|
||||||
end_time = self._ass_time(end)
|
end_time = self._ass_time(end)
|
||||||
|
# Use karaoke effect for highlighting mode
|
||||||
|
effect = "karaoke" if self.subtitle_mode == "Sentence + Highlighting" else ""
|
||||||
merged_subtitle_file.write(
|
merged_subtitle_file.write(
|
||||||
f"Dialogue: 0,{start_time},{end_time},Default,,{merged_subtitle_margin},{merged_subtitle_margin},0,,{merged_subtitle_alignment_tag}{text}\n"
|
f"Dialogue: 0,{start_time},{end_time},Default,,{merged_subtitle_margin},{merged_subtitle_margin},0,{effect},{merged_subtitle_alignment_tag}{text}\n"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
for entry in new_entries:
|
for entry in new_entries:
|
||||||
@@ -850,8 +872,10 @@ class ConversionThread(QThread):
|
|||||||
for start, end, text in new_chapter_entries:
|
for start, end, text in new_chapter_entries:
|
||||||
start_time = self._ass_time(start)
|
start_time = self._ass_time(start)
|
||||||
end_time = self._ass_time(end)
|
end_time = self._ass_time(end)
|
||||||
|
# Use karaoke effect for highlighting mode
|
||||||
|
effect = "karaoke" if self.subtitle_mode == "Sentence + Highlighting" else ""
|
||||||
chapter_subtitle_file.write(
|
chapter_subtitle_file.write(
|
||||||
f"Dialogue: 0,{start_time},{end_time},Default,,{chapter_subtitle_margin},{chapter_subtitle_margin},0,,{chapter_subtitle_alignment_tag}{text}\n"
|
f"Dialogue: 0,{start_time},{end_time},Default,,{chapter_subtitle_margin},{chapter_subtitle_margin},0,{effect},{chapter_subtitle_alignment_tag}{text}\n"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
for entry in new_chapter_entries:
|
for entry in new_chapter_entries:
|
||||||
@@ -1152,7 +1176,61 @@ class ConversionThread(QThread):
|
|||||||
if not tokens_with_timestamps:
|
if not tokens_with_timestamps:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.subtitle_mode == "Sentence" or self.subtitle_mode == "Sentence + Comma":
|
if self.subtitle_mode == "Sentence + Highlighting":
|
||||||
|
# Sentence-based processing with karaoke highlighting
|
||||||
|
separator = r"[.!?]"
|
||||||
|
current_sentence = []
|
||||||
|
word_count = 0
|
||||||
|
|
||||||
|
for token in tokens_with_timestamps:
|
||||||
|
current_sentence.append(token)
|
||||||
|
word_count += 1
|
||||||
|
|
||||||
|
# Split sentences based on separator or word count
|
||||||
|
if (
|
||||||
|
re.search(separator, token["text"]) and token["whitespace"] == " "
|
||||||
|
) or word_count >= max_subtitle_words:
|
||||||
|
if current_sentence:
|
||||||
|
# Create karaoke subtitle entry for this sentence
|
||||||
|
start_time = current_sentence[0]["start"]
|
||||||
|
end_time = current_sentence[-1]["end"]
|
||||||
|
|
||||||
|
# Generate karaoke text with background highlighting
|
||||||
|
karaoke_text = ""
|
||||||
|
for t in current_sentence:
|
||||||
|
# Calculate duration in centiseconds
|
||||||
|
duration = t["end"] - t["start"] if t["end"] and t["start"] else 0.5
|
||||||
|
duration_cs = int(duration * 100)
|
||||||
|
# Add karaoke effect - relies on style's SecondaryColour for highlighting
|
||||||
|
karaoke_text += f"{{\\kf{duration_cs}}}{t['text']}{t.get('whitespace', '') or ''}"
|
||||||
|
|
||||||
|
subtitle_entries.append(
|
||||||
|
(start_time, end_time, karaoke_text.strip())
|
||||||
|
)
|
||||||
|
current_sentence = []
|
||||||
|
word_count = 0
|
||||||
|
|
||||||
|
# Add any remaining tokens as a sentence
|
||||||
|
if current_sentence:
|
||||||
|
start_time = current_sentence[0]["start"]
|
||||||
|
end_time = current_sentence[-1]["end"]
|
||||||
|
|
||||||
|
# Generate karaoke text for remaining tokens
|
||||||
|
karaoke_text = ""
|
||||||
|
for t in current_sentence:
|
||||||
|
duration = t["end"] - t["start"] if t["end"] and t["start"] else 0.5
|
||||||
|
duration_cs = int(duration * 100)
|
||||||
|
karaoke_text += f"{{\\kf{duration_cs}}}{t['text']}{t.get('whitespace', '') or ''}"
|
||||||
|
subtitle_entries.append((start_time, end_time, karaoke_text.strip()))
|
||||||
|
|
||||||
|
# Fallback for last entry
|
||||||
|
if subtitle_entries and fallback_end_time is not None:
|
||||||
|
last_entry = subtitle_entries[-1]
|
||||||
|
start, end, text = last_entry
|
||||||
|
if end is None or end <= start or end <= 0:
|
||||||
|
subtitle_entries[-1] = (start, fallback_end_time, text)
|
||||||
|
|
||||||
|
elif self.subtitle_mode == "Sentence" or self.subtitle_mode == "Sentence + Comma":
|
||||||
# Define separator pattern based on mode
|
# Define separator pattern based on mode
|
||||||
separator = r"[.!?]" if self.subtitle_mode == "Sentence" else r"[.!?,]"
|
separator = r"[.!?]" if self.subtitle_mode == "Sentence" else r"[.!?,]"
|
||||||
current_sentence = []
|
current_sentence = []
|
||||||
@@ -1319,7 +1397,16 @@ class VoicePreviewThread(QThread):
|
|||||||
|
|
||||||
# Generate the preview and save to cache
|
# Generate the preview and save to cache
|
||||||
try:
|
try:
|
||||||
device = "cuda" if self.use_gpu else "cpu"
|
|
||||||
|
# Set device based on use_gpu setting and platform
|
||||||
|
if self.use_gpu:
|
||||||
|
if platform.system() == "Darwin" and platform.processor() == "arm":
|
||||||
|
device = "mps" # Use MPS for Apple Silicon
|
||||||
|
else:
|
||||||
|
device = "cuda" # Use CUDA for other platforms
|
||||||
|
else:
|
||||||
|
device = "cpu"
|
||||||
|
|
||||||
tts = self.kpipeline_class(
|
tts = self.kpipeline_class(
|
||||||
lang_code=self.lang_code, repo_id="hexgrad/Kokoro-82M", device=device
|
lang_code=self.lang_code, repo_id="hexgrad/Kokoro-82M", device=device
|
||||||
)
|
)
|
||||||
|
|||||||
+45
-1
@@ -895,6 +895,7 @@ class abogen(QWidget):
|
|||||||
"Disabled: No subtitles will be generated.\n"
|
"Disabled: No subtitles will be generated.\n"
|
||||||
"Sentence: Subtitles will be generated for each sentence.\n"
|
"Sentence: Subtitles will be generated for each sentence.\n"
|
||||||
"Sentence + Comma: Subtitles will be generated for each sentence and comma.\n"
|
"Sentence + Comma: Subtitles will be generated for each sentence and comma.\n"
|
||||||
|
"Sentence + Highlighting: Subtitles with word-by-word karaoke highlighting.\n"
|
||||||
"1+ word: Subtitles will be generated for each word(s).\n\n"
|
"1+ word: Subtitles will be generated for each word(s).\n\n"
|
||||||
"Supported languages for subtitle generation:\n"
|
"Supported languages for subtitle generation:\n"
|
||||||
+ "\n".join(
|
+ "\n".join(
|
||||||
@@ -902,7 +903,7 @@ class abogen(QWidget):
|
|||||||
for lang in SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION
|
for lang in SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
subtitle_options = ["Disabled", "Sentence", "Sentence + Comma"] + [
|
subtitle_options = ["Disabled", "Sentence", "Sentence + Comma", "Sentence + Highlighting"] + [
|
||||||
f"{i} word" if i == 1 else f"{i} words" for i in range(1, 11)
|
f"{i} word" if i == 1 else f"{i} words" for i in range(1, 11)
|
||||||
]
|
]
|
||||||
self.subtitle_combo.addItems(subtitle_options)
|
self.subtitle_combo.addItems(subtitle_options)
|
||||||
@@ -970,6 +971,25 @@ class abogen(QWidget):
|
|||||||
lambda i: self.set_subtitle_format(self.subtitle_format_combo.itemData(i))
|
lambda i: self.set_subtitle_format(self.subtitle_format_combo.itemData(i))
|
||||||
)
|
)
|
||||||
subtitle_format_layout.addWidget(self.subtitle_format_combo)
|
subtitle_format_layout.addWidget(self.subtitle_format_combo)
|
||||||
|
# If subtitle mode requires highlighting, SRT is not supported. Disable SRT item
|
||||||
|
# and auto-switch to a compatible ASS format if SRT is currently selected.
|
||||||
|
try:
|
||||||
|
if hasattr(self, "subtitle_mode") and self.subtitle_mode == "Sentence + Highlighting":
|
||||||
|
idx_srt = self.subtitle_format_combo.findData("srt")
|
||||||
|
if idx_srt >= 0:
|
||||||
|
item = self.subtitle_format_combo.model().item(idx_srt)
|
||||||
|
if item is not None:
|
||||||
|
item.setEnabled(False)
|
||||||
|
# If current selection is SRT, switch to centered narrow ASS
|
||||||
|
if self.subtitle_format_combo.currentData() == "srt":
|
||||||
|
new_idx = self.subtitle_format_combo.findData("ass_centered_narrow")
|
||||||
|
if new_idx >= 0:
|
||||||
|
self.subtitle_format_combo.setCurrentIndex(new_idx)
|
||||||
|
# Persist the change
|
||||||
|
self.set_subtitle_format(self.subtitle_format_combo.itemData(new_idx))
|
||||||
|
except Exception:
|
||||||
|
# Fail-safe: don't crash UI if model manipulation isn't supported on some platforms
|
||||||
|
pass
|
||||||
controls_layout.addLayout(subtitle_format_layout)
|
controls_layout.addLayout(subtitle_format_layout)
|
||||||
|
|
||||||
# Replace single newlines dropdown (acts like checkbox)
|
# Replace single newlines dropdown (acts like checkbox)
|
||||||
@@ -2471,6 +2491,30 @@ class abogen(QWidget):
|
|||||||
self.subtitle_format_combo.setEnabled(False)
|
self.subtitle_format_combo.setEnabled(False)
|
||||||
else:
|
else:
|
||||||
self.subtitle_format_combo.setEnabled(True)
|
self.subtitle_format_combo.setEnabled(True)
|
||||||
|
# If highlighting mode selected, SRT is not supported. Disable SRT option and
|
||||||
|
# switch away from it if currently selected.
|
||||||
|
try:
|
||||||
|
idx_srt = self.subtitle_format_combo.findData("srt")
|
||||||
|
if mode == "Sentence + Highlighting":
|
||||||
|
if idx_srt >= 0:
|
||||||
|
item = self.subtitle_format_combo.model().item(idx_srt)
|
||||||
|
if item is not None:
|
||||||
|
item.setEnabled(False)
|
||||||
|
# If current format is SRT, switch to a compatible ASS format
|
||||||
|
if self.subtitle_format_combo.currentData() == "srt":
|
||||||
|
new_idx = self.subtitle_format_combo.findData("ass_centered_narrow")
|
||||||
|
if new_idx >= 0:
|
||||||
|
self.subtitle_format_combo.setCurrentIndex(new_idx)
|
||||||
|
self.set_subtitle_format(self.subtitle_format_combo.itemData(new_idx))
|
||||||
|
else:
|
||||||
|
# Re-enable SRT option when not in highlighting mode
|
||||||
|
if idx_srt >= 0:
|
||||||
|
item = self.subtitle_format_combo.model().item(idx_srt)
|
||||||
|
if item is not None:
|
||||||
|
item.setEnabled(True)
|
||||||
|
except Exception:
|
||||||
|
# Ignore errors interacting with model (defensive)
|
||||||
|
pass
|
||||||
|
|
||||||
def on_format_changed(self, fmt):
|
def on_format_changed(self, fmt):
|
||||||
self.selected_format = fmt
|
self.selected_format = fmt
|
||||||
|
|||||||
+34
-16
@@ -3,6 +3,7 @@ import sys
|
|||||||
import json
|
import json
|
||||||
import warnings
|
import warnings
|
||||||
import platform
|
import platform
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import re
|
import re
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@@ -239,13 +240,23 @@ def calculate_text_length(text):
|
|||||||
def get_gpu_acceleration(enabled):
|
def get_gpu_acceleration(enabled):
|
||||||
try:
|
try:
|
||||||
import torch
|
import torch
|
||||||
from torch.cuda import is_available
|
from torch.cuda import is_available as cuda_available
|
||||||
|
|
||||||
if not enabled:
|
if not enabled:
|
||||||
return "CUDA GPU available but using CPU.", False
|
return "GPU available but using CPU.", False
|
||||||
if is_available():
|
|
||||||
|
# Check for Apple Silicon MPS
|
||||||
|
if platform.system() == "Darwin" and platform.processor() == "arm":
|
||||||
|
if torch.backends.mps.is_available():
|
||||||
|
return "MPS GPU available and enabled.", True
|
||||||
|
else:
|
||||||
|
return "MPS GPU not available on Apple Silicon. Using CPU.", False
|
||||||
|
|
||||||
|
# Check for CUDA
|
||||||
|
if cuda_available():
|
||||||
return "CUDA GPU available and enabled.", True
|
return "CUDA GPU available and enabled.", True
|
||||||
# Gather more diagnostic info if CUDA is not available
|
|
||||||
|
# Gather CUDA diagnostic info if not available
|
||||||
try:
|
try:
|
||||||
cuda_devices = torch.cuda.device_count()
|
cuda_devices = torch.cuda.device_count()
|
||||||
cuda_error = (
|
cuda_error = (
|
||||||
@@ -257,7 +268,7 @@ def get_gpu_acceleration(enabled):
|
|||||||
cuda_error = str(e)
|
cuda_error = str(e)
|
||||||
return f"CUDA GPU is not available. Using CPU. ({cuda_error})", False
|
return f"CUDA GPU is not available. Using CPU. ({cuda_error})", False
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"Error checking CUDA GPU: {e}", False
|
return f"Error checking GPU: {e}", False
|
||||||
|
|
||||||
|
|
||||||
def prevent_sleep_start():
|
def prevent_sleep_start():
|
||||||
@@ -276,17 +287,24 @@ def prevent_sleep_start():
|
|||||||
# Add program name and reason for inhibition
|
# Add program name and reason for inhibition
|
||||||
program_name = PROGRAM_NAME
|
program_name = PROGRAM_NAME
|
||||||
reason = "Prevent sleep during abogen process"
|
reason = "Prevent sleep during abogen process"
|
||||||
_sleep_procs["Linux"] = create_process(
|
# Only attempt to use systemd-inhibit if it's available on the system.
|
||||||
[
|
if shutil.which("systemd-inhibit"):
|
||||||
"systemd-inhibit",
|
_sleep_procs["Linux"] = create_process(
|
||||||
f"--who={program_name}",
|
[
|
||||||
f"--why={reason}",
|
"systemd-inhibit",
|
||||||
"--what=sleep",
|
f"--who={program_name}",
|
||||||
"--mode=block",
|
f"--why={reason}",
|
||||||
"sleep",
|
"--what=sleep",
|
||||||
"infinity",
|
"--mode=block",
|
||||||
]
|
"sleep",
|
||||||
)
|
"infinity",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Non-systemd distro or systemd tools not installed: skip inhibition rather than crash
|
||||||
|
print(
|
||||||
|
"systemd-inhibit not found: skipping sleep inhibition on this Linux system."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def prevent_sleep_end():
|
def prevent_sleep_end():
|
||||||
|
|||||||
+9
-7
@@ -23,20 +23,22 @@ brew install ffmpeg
|
|||||||
sudo apt install ffmpeg
|
sudo apt install ffmpeg
|
||||||
```
|
```
|
||||||
|
|
||||||
## Create the Video (.webm)
|
## Create the Video (.mp4) (Recommended)
|
||||||
|
|
||||||
Run this FFmpeg command to create the tiny video:
|
Run this FFmpeg command to create the video:
|
||||||
|
|
||||||
```
|
```
|
||||||
ffmpeg -loop 1 -framerate 24 -i bg.jpg -i audio.wav -vf "ass=subtitle.ass" -c:v libvpx-vp9 -b:v 0 -crf 30 -c:a libopus -shortest demo.webm
|
ffmpeg -loop 1 -framerate 24 -i bg.jpg -i demo.wav -vf "ass=demo.ass" -c:v libx264 -pix_fmt yuv420p -preset slow -crf 18 -c:a aac -b:a 192k -movflags +faststart -shortest demo.mp4
|
||||||
```
|
```
|
||||||
|
|
||||||
## For Higher Quality (But Larger) Video (.mp4)
|
## For Smaller Filesize (.webm)
|
||||||
|
|
||||||
If you need better quality for distribution, use this command instead:
|
If you need a smaller video file, use this command:
|
||||||
|
|
||||||
```
|
```
|
||||||
ffmpeg -loop 1 -framerate 24 -i bg.jpg -i audio.wav -vf "ass=subtitle.ass" -c:v libx264 -preset slow -crf 18 -movflags +faststart -c:a copy -shortest demo.mp4
|
ffmpeg -loop 1 -framerate 24 -i bg.jpg -i demo.wav -vf "ass=demo.ass" -c:v libvpx-vp9 -b:v 0 -crf 30 -c:a libopus -shortest demo.webm
|
||||||
```
|
```
|
||||||
|
|
||||||
This creates an MP4 file that's compatible with more devices but larger in size.
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 12 KiB |
+124
-124
@@ -4,127 +4,127 @@ ScriptType: v4.00+
|
|||||||
|
|
||||||
[Events]
|
[Events]
|
||||||
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
|
||||||
Dialogue: 0,0:00:00.27,0:00:00.83,Default,,100,100,0,,{\an5}Abogen
|
Dialogue: 0,0:00:00.35,0:00:00.93,Default,,90,90,0,,{\an5}Abogen
|
||||||
Dialogue: 0,0:00:00.83,0:00:00.96,Default,,100,100,0,,{\an5}is
|
Dialogue: 0,0:00:00.93,0:00:01.07,Default,,90,90,0,,{\an5}is
|
||||||
Dialogue: 0,0:00:00.96,0:00:01.11,Default,,100,100,0,,{\an5}your
|
Dialogue: 0,0:00:01.07,0:00:01.27,Default,,90,90,0,,{\an5}your
|
||||||
Dialogue: 0,0:00:01.11,0:00:01.66,Default,,100,100,0,,{\an5}all-in-one
|
Dialogue: 0,0:00:01.27,0:00:01.86,Default,,90,90,0,,{\an5}all-in-one
|
||||||
Dialogue: 0,0:00:01.66,0:00:02.04,Default,,100,100,0,,{\an5}tool
|
Dialogue: 0,0:00:01.86,0:00:02.22,Default,,90,90,0,,{\an5}tool
|
||||||
Dialogue: 0,0:00:02.04,0:00:02.17,Default,,100,100,0,,{\an5}for
|
Dialogue: 0,0:00:02.22,0:00:02.35,Default,,90,90,0,,{\an5}for
|
||||||
Dialogue: 0,0:00:02.17,0:00:02.53,Default,,100,100,0,,{\an5}turning
|
Dialogue: 0,0:00:02.35,0:00:02.73,Default,,90,90,0,,{\an5}turning
|
||||||
Dialogue: 0,0:00:02.53,0:00:03.22,Default,,100,100,0,,{\an5}text
|
Dialogue: 0,0:00:02.73,0:00:03.13,Default,,90,90,0,,{\an5}text
|
||||||
Dialogue: 0,0:00:03.22,0:00:03.45,Default,,100,100,0,,{\an5}into
|
Dialogue: 0,0:00:03.13,0:00:03.35,Default,,90,90,0,,{\an5}into
|
||||||
Dialogue: 0,0:00:03.45,0:00:03.75,Default,,100,100,0,,{\an5}something
|
Dialogue: 0,0:00:03.35,0:00:03.70,Default,,90,90,0,,{\an5}something
|
||||||
Dialogue: 0,0:00:03.75,0:00:03.85,Default,,100,100,0,,{\an5}you
|
Dialogue: 0,0:00:03.70,0:00:03.79,Default,,90,90,0,,{\an5}you
|
||||||
Dialogue: 0,0:00:03.85,0:00:04.04,Default,,100,100,0,,{\an5}can
|
Dialogue: 0,0:00:03.79,0:00:03.98,Default,,90,90,0,,{\an5}can
|
||||||
Dialogue: 0,0:00:04.04,0:00:04.32,Default,,100,100,0,,{\an5}see
|
Dialogue: 0,0:00:03.98,0:00:04.25,Default,,90,90,0,,{\an5}see
|
||||||
Dialogue: 0,0:00:04.32,0:00:04.45,Default,,100,100,0,,{\an5}and
|
Dialogue: 0,0:00:04.25,0:00:04.37,Default,,90,90,0,,{\an5}and
|
||||||
Dialogue: 0,0:00:04.45,0:00:05.61,Default,,100,100,0,,{\an5}hear.
|
Dialogue: 0,0:00:04.37,0:00:05.37,Default,,90,90,0,,{\an5}hear.
|
||||||
Dialogue: 0,0:00:05.61,0:00:06.02,Default,,100,100,0,,{\an5}Easily
|
Dialogue: 0,0:00:05.37,0:00:05.79,Default,,90,90,0,,{\an5}Easily
|
||||||
Dialogue: 0,0:00:06.02,0:00:06.48,Default,,100,100,0,,{\an5}convert
|
Dialogue: 0,0:00:05.79,0:00:06.23,Default,,90,90,0,,{\an5}convert
|
||||||
Dialogue: 0,0:00:06.48,0:00:07.31,Default,,100,100,0,,{\an5}ePub,
|
Dialogue: 0,0:00:06.23,0:00:07.11,Default,,90,90,0,,{\an5}ePub,
|
||||||
Dialogue: 0,0:00:07.31,0:00:08.40,Default,,100,100,0,,{\an5}PDF,
|
Dialogue: 0,0:00:07.11,0:00:08.17,Default,,90,90,0,,{\an5}PDF,
|
||||||
Dialogue: 0,0:00:08.40,0:00:08.55,Default,,100,100,0,,{\an5}or
|
Dialogue: 0,0:00:08.17,0:00:08.33,Default,,90,90,0,,{\an5}or
|
||||||
Dialogue: 0,0:00:08.55,0:00:08.90,Default,,100,100,0,,{\an5}text
|
Dialogue: 0,0:00:08.33,0:00:08.67,Default,,90,90,0,,{\an5}text
|
||||||
Dialogue: 0,0:00:08.90,0:00:09.21,Default,,100,100,0,,{\an5}file
|
Dialogue: 0,0:00:08.67,0:00:08.98,Default,,90,90,0,,{\an5}file
|
||||||
Dialogue: 0,0:00:09.21,0:00:09.46,Default,,100,100,0,,{\an5}into
|
Dialogue: 0,0:00:08.98,0:00:09.23,Default,,90,90,0,,{\an5}into
|
||||||
Dialogue: 0,0:00:09.46,0:00:10.34,Default,,100,100,0,,{\an5}audio
|
Dialogue: 0,0:00:09.23,0:00:09.91,Default,,90,90,0,,{\an5}audio
|
||||||
Dialogue: 0,0:00:10.34,0:00:10.56,Default,,100,100,0,,{\an5}with
|
Dialogue: 0,0:00:09.91,0:00:10.09,Default,,90,90,0,,{\an5}with
|
||||||
Dialogue: 0,0:00:10.56,0:00:11.17,Default,,100,100,0,,{\an5}subtitles
|
Dialogue: 0,0:00:10.09,0:00:10.69,Default,,90,90,0,,{\an5}subtitles
|
||||||
Dialogue: 0,0:00:11.17,0:00:11.32,Default,,100,100,0,,{\an5}that
|
Dialogue: 0,0:00:10.69,0:00:10.87,Default,,90,90,0,,{\an5}that
|
||||||
Dialogue: 0,0:00:11.32,0:00:11.51,Default,,100,100,0,,{\an5}light
|
Dialogue: 0,0:00:10.87,0:00:11.06,Default,,90,90,0,,{\an5}light
|
||||||
Dialogue: 0,0:00:11.51,0:00:11.71,Default,,100,100,0,,{\an5}up
|
Dialogue: 0,0:00:11.06,0:00:11.26,Default,,90,90,0,,{\an5}up
|
||||||
Dialogue: 0,0:00:11.71,0:00:12.05,Default,,100,100,0,,{\an5}every
|
Dialogue: 0,0:00:11.26,0:00:11.59,Default,,90,90,0,,{\an5}every
|
||||||
Dialogue: 0,0:00:12.05,0:00:12.40,Default,,100,100,0,,{\an5}word
|
Dialogue: 0,0:00:11.59,0:00:11.90,Default,,90,90,0,,{\an5}word
|
||||||
Dialogue: 0,0:00:12.40,0:00:12.51,Default,,100,100,0,,{\an5}as
|
Dialogue: 0,0:00:11.90,0:00:12.02,Default,,90,90,0,,{\an5}as
|
||||||
Dialogue: 0,0:00:12.51,0:00:12.65,Default,,100,100,0,,{\an5}it’s
|
Dialogue: 0,0:00:12.02,0:00:12.17,Default,,90,90,0,,{\an5}it’s
|
||||||
Dialogue: 0,0:00:12.65,0:00:14.21,Default,,100,100,0,,{\an5}spoken.
|
Dialogue: 0,0:00:12.17,0:00:13.46,Default,,90,90,0,,{\an5}spoken.
|
||||||
Dialogue: 0,0:00:14.21,0:00:14.32,Default,,100,100,0,,{\an5}The
|
Dialogue: 0,0:00:13.46,0:00:13.59,Default,,90,90,0,,{\an5}The
|
||||||
Dialogue: 0,0:00:14.32,0:00:14.62,Default,,100,100,0,,{\an5}voice
|
Dialogue: 0,0:00:13.59,0:00:13.92,Default,,90,90,0,,{\an5}voice
|
||||||
Dialogue: 0,0:00:14.62,0:00:14.77,Default,,100,100,0,,{\an5}you
|
Dialogue: 0,0:00:13.92,0:00:14.07,Default,,90,90,0,,{\an5}you
|
||||||
Dialogue: 0,0:00:14.77,0:00:15.65,Default,,100,100,0,,{\an5}hear,
|
Dialogue: 0,0:00:14.07,0:00:14.86,Default,,90,90,0,,{\an5}hear,
|
||||||
Dialogue: 0,0:00:15.65,0:00:15.76,Default,,100,100,0,,{\an5}and
|
Dialogue: 0,0:00:14.86,0:00:14.98,Default,,90,90,0,,{\an5}and
|
||||||
Dialogue: 0,0:00:15.76,0:00:15.86,Default,,100,100,0,,{\an5}the
|
Dialogue: 0,0:00:14.98,0:00:15.08,Default,,90,90,0,,{\an5}the
|
||||||
Dialogue: 0,0:00:15.86,0:00:16.44,Default,,100,100,0,,{\an5}subtitles
|
Dialogue: 0,0:00:15.08,0:00:15.67,Default,,90,90,0,,{\an5}subtitles
|
||||||
Dialogue: 0,0:00:16.44,0:00:16.58,Default,,100,100,0,,{\an5}you
|
Dialogue: 0,0:00:15.67,0:00:15.81,Default,,90,90,0,,{\an5}you
|
||||||
Dialogue: 0,0:00:16.58,0:00:17.03,Default,,100,100,0,,{\an5}see
|
Dialogue: 0,0:00:15.81,0:00:16.13,Default,,90,90,0,,{\an5}see
|
||||||
Dialogue: 0,0:00:17.03,0:00:17.14,Default,,100,100,0,,{\an5}are
|
Dialogue: 0,0:00:16.13,0:00:16.27,Default,,90,90,0,,{\an5}are
|
||||||
Dialogue: 0,0:00:17.14,0:00:17.44,Default,,100,100,0,,{\an5}both
|
Dialogue: 0,0:00:16.27,0:00:16.57,Default,,90,90,0,,{\an5}both
|
||||||
Dialogue: 0,0:00:17.44,0:00:17.91,Default,,100,100,0,,{\an5}created
|
Dialogue: 0,0:00:16.57,0:00:17.03,Default,,90,90,0,,{\an5}created
|
||||||
Dialogue: 0,0:00:17.91,0:00:18.96,Default,,100,100,0,,{\an5}automatically
|
Dialogue: 0,0:00:17.03,0:00:17.96,Default,,90,90,0,,{\an5}automatically
|
||||||
Dialogue: 0,0:00:18.96,0:00:19.14,Default,,100,100,0,,{\an5}by
|
Dialogue: 0,0:00:17.96,0:00:18.14,Default,,90,90,0,,{\an5}by
|
||||||
Dialogue: 0,0:00:19.14,0:00:19.64,Default,,100,100,0,,{\an5}Abogen
|
Dialogue: 0,0:00:18.14,0:00:18.67,Default,,90,90,0,,{\an5}Abogen
|
||||||
Dialogue: 0,0:00:19.64,0:00:20.00,Default,,100,100,0,,{\an5}using
|
Dialogue: 0,0:00:18.67,0:00:19.02,Default,,90,90,0,,{\an5}using
|
||||||
Dialogue: 0,0:00:20.00,0:00:21.14,Default,,100,100,0,,{\an5}Kokoro,
|
Dialogue: 0,0:00:19.02,0:00:20.10,Default,,90,90,0,,{\an5}Kokoro,
|
||||||
Dialogue: 0,0:00:21.14,0:00:21.28,Default,,100,100,0,,{\an5}an
|
Dialogue: 0,0:00:20.10,0:00:20.27,Default,,90,90,0,,{\an5}an
|
||||||
Dialogue: 0,0:00:21.28,0:00:21.88,Default,,100,100,0,,{\an5}open-weight
|
Dialogue: 0,0:00:20.27,0:00:20.88,Default,,90,90,0,,{\an5}open-weight
|
||||||
Dialogue: 0,0:00:21.88,0:00:22.52,Default,,100,100,0,,{\an5}TTS
|
Dialogue: 0,0:00:20.88,0:00:21.55,Default,,90,90,0,,{\an5}TTS
|
||||||
Dialogue: 0,0:00:22.52,0:00:23.18,Default,,100,100,0,,{\an5}model
|
Dialogue: 0,0:00:21.55,0:00:21.94,Default,,90,90,0,,{\an5}model
|
||||||
Dialogue: 0,0:00:23.18,0:00:23.38,Default,,100,100,0,,{\an5}with
|
Dialogue: 0,0:00:21.94,0:00:22.14,Default,,90,90,0,,{\an5}with
|
||||||
Dialogue: 0,0:00:23.38,0:00:24.01,Default,,100,100,0,,{\an5}82
|
Dialogue: 0,0:00:22.14,0:00:22.78,Default,,90,90,0,,{\an5}82
|
||||||
Dialogue: 0,0:00:24.01,0:00:24.37,Default,,100,100,0,,{\an5}million
|
Dialogue: 0,0:00:22.78,0:00:23.14,Default,,90,90,0,,{\an5}million
|
||||||
Dialogue: 0,0:00:24.37,0:00:25.98,Default,,100,100,0,,{\an5}parameters.
|
Dialogue: 0,0:00:23.14,0:00:24.55,Default,,90,90,0,,{\an5}parameters.
|
||||||
Dialogue: 0,0:00:25.98,0:00:26.47,Default,,100,100,0,,{\an5}Everything
|
Dialogue: 0,0:00:24.55,0:00:25.05,Default,,90,90,0,,{\an5}Everything
|
||||||
Dialogue: 0,0:00:26.47,0:00:26.77,Default,,100,100,0,,{\an5}works
|
Dialogue: 0,0:00:25.05,0:00:25.35,Default,,90,90,0,,{\an5}works
|
||||||
Dialogue: 0,0:00:26.77,0:00:27.01,Default,,100,100,0,,{\an5}right
|
Dialogue: 0,0:00:25.35,0:00:25.58,Default,,90,90,0,,{\an5}right
|
||||||
Dialogue: 0,0:00:27.01,0:00:27.11,Default,,100,100,0,,{\an5}on
|
Dialogue: 0,0:00:25.58,0:00:25.68,Default,,90,90,0,,{\an5}on
|
||||||
Dialogue: 0,0:00:27.11,0:00:27.22,Default,,100,100,0,,{\an5}your
|
Dialogue: 0,0:00:25.68,0:00:25.80,Default,,90,90,0,,{\an5}your
|
||||||
Dialogue: 0,0:00:27.22,0:00:28.23,Default,,100,100,0,,{\an5}computer,
|
Dialogue: 0,0:00:25.80,0:00:26.69,Default,,90,90,0,,{\an5}computer,
|
||||||
Dialogue: 0,0:00:28.23,0:00:28.37,Default,,100,100,0,,{\an5}so
|
Dialogue: 0,0:00:26.69,0:00:26.87,Default,,90,90,0,,{\an5}so
|
||||||
Dialogue: 0,0:00:28.37,0:00:28.53,Default,,100,100,0,,{\an5}your
|
Dialogue: 0,0:00:26.87,0:00:27.06,Default,,90,90,0,,{\an5}your
|
||||||
Dialogue: 0,0:00:28.53,0:00:28.94,Default,,100,100,0,,{\an5}files
|
Dialogue: 0,0:00:27.06,0:00:27.47,Default,,90,90,0,,{\an5}files
|
||||||
Dialogue: 0,0:00:28.94,0:00:29.23,Default,,100,100,0,,{\an5}stay
|
Dialogue: 0,0:00:27.47,0:00:27.73,Default,,90,90,0,,{\an5}stay
|
||||||
Dialogue: 0,0:00:29.23,0:00:29.88,Default,,100,100,0,,{\an5}private
|
Dialogue: 0,0:00:27.73,0:00:28.28,Default,,90,90,0,,{\an5}private
|
||||||
Dialogue: 0,0:00:29.88,0:00:30.05,Default,,100,100,0,,{\an5}and
|
Dialogue: 0,0:00:28.28,0:00:28.44,Default,,90,90,0,,{\an5}and
|
||||||
Dialogue: 0,0:00:30.05,0:00:31.16,Default,,100,100,0,,{\an5}secure.
|
Dialogue: 0,0:00:28.44,0:00:29.51,Default,,90,90,0,,{\an5}secure.
|
||||||
Dialogue: 0,0:00:31.50,0:00:31.63,Default,,100,100,0,,{\an5}The
|
Dialogue: 0,0:00:29.90,0:00:30.03,Default,,90,90,0,,{\an5}The
|
||||||
Dialogue: 0,0:00:31.63,0:00:31.97,Default,,100,100,0,,{\an5}easy
|
Dialogue: 0,0:00:30.03,0:00:30.37,Default,,90,90,0,,{\an5}easy
|
||||||
Dialogue: 0,0:00:31.97,0:00:32.54,Default,,100,100,0,,{\an5}interface
|
Dialogue: 0,0:00:30.37,0:00:30.95,Default,,90,90,0,,{\an5}interface
|
||||||
Dialogue: 0,0:00:32.54,0:00:32.81,Default,,100,100,0,,{\an5}lets
|
Dialogue: 0,0:00:30.95,0:00:31.23,Default,,90,90,0,,{\an5}lets
|
||||||
Dialogue: 0,0:00:32.81,0:00:33.23,Default,,100,100,0,,{\an5}anyone
|
Dialogue: 0,0:00:31.23,0:00:31.71,Default,,90,90,0,,{\an5}anyone
|
||||||
Dialogue: 0,0:00:33.23,0:00:33.66,Default,,100,100,0,,{\an5}create
|
Dialogue: 0,0:00:31.71,0:00:32.13,Default,,90,90,0,,{\an5}create
|
||||||
Dialogue: 0,0:00:33.66,0:00:34.88,Default,,100,100,0,,{\an5}audiobooks,
|
Dialogue: 0,0:00:32.13,0:00:33.33,Default,,90,90,0,,{\an5}audiobooks,
|
||||||
Dialogue: 0,0:00:34.88,0:00:35.54,Default,,100,100,0,,{\an5}voiceovers
|
Dialogue: 0,0:00:33.33,0:00:34.07,Default,,90,90,0,,{\an5}voiceovers
|
||||||
Dialogue: 0,0:00:35.54,0:00:35.70,Default,,100,100,0,,{\an5}for
|
Dialogue: 0,0:00:34.07,0:00:34.23,Default,,90,90,0,,{\an5}for
|
||||||
Dialogue: 0,0:00:35.70,0:00:36.68,Default,,100,100,0,,{\an5}Instagram,
|
Dialogue: 0,0:00:34.23,0:00:35.30,Default,,90,90,0,,{\an5}Instagram,
|
||||||
Dialogue: 0,0:00:36.68,0:00:37.23,Default,,100,100,0,,{\an5}YouTube
|
Dialogue: 0,0:00:35.30,0:00:35.93,Default,,90,90,0,,{\an5}YouTube
|
||||||
Dialogue: 0,0:00:37.23,0:00:37.41,Default,,100,100,0,,{\an5}or
|
Dialogue: 0,0:00:35.93,0:00:36.11,Default,,90,90,0,,{\an5}or
|
||||||
Dialogue: 0,0:00:37.41,0:00:38.52,Default,,100,100,0,,{\an5}TikTok,
|
Dialogue: 0,0:00:36.11,0:00:36.92,Default,,90,90,0,,{\an5}TikTok,
|
||||||
Dialogue: 0,0:00:38.52,0:00:38.64,Default,,100,100,0,,{\an5}or
|
Dialogue: 0,0:00:36.92,0:00:37.07,Default,,90,90,0,,{\an5}or
|
||||||
Dialogue: 0,0:00:38.64,0:00:38.89,Default,,100,100,0,,{\an5}just
|
Dialogue: 0,0:00:37.07,0:00:37.30,Default,,90,90,0,,{\an5}just
|
||||||
Dialogue: 0,0:00:38.89,0:00:39.57,Default,,100,100,0,,{\an5}high-quality
|
Dialogue: 0,0:00:37.30,0:00:38.00,Default,,90,90,0,,{\an5}high-quality
|
||||||
Dialogue: 0,0:00:39.57,0:00:40.39,Default,,100,100,0,,{\an5}text-to-speech
|
Dialogue: 0,0:00:38.00,0:00:38.82,Default,,90,90,0,,{\an5}text-to-speech
|
||||||
Dialogue: 0,0:00:40.39,0:00:40.56,Default,,100,100,0,,{\an5}in
|
Dialogue: 0,0:00:38.82,0:00:38.97,Default,,90,90,0,,{\an5}in
|
||||||
Dialogue: 0,0:00:40.56,0:00:42.03,Default,,100,100,0,,{\an5}seconds.
|
Dialogue: 0,0:00:38.97,0:00:40.16,Default,,90,90,0,,{\an5}seconds.
|
||||||
Dialogue: 0,0:00:42.03,0:00:42.32,Default,,100,100,0,,{\an5}Whether
|
Dialogue: 0,0:00:40.16,0:00:40.45,Default,,90,90,0,,{\an5}Whether
|
||||||
Dialogue: 0,0:00:42.32,0:00:42.47,Default,,100,100,0,,{\an5}you
|
Dialogue: 0,0:00:40.45,0:00:40.57,Default,,90,90,0,,{\an5}you
|
||||||
Dialogue: 0,0:00:42.47,0:00:42.67,Default,,100,100,0,,{\an5}want
|
Dialogue: 0,0:00:40.57,0:00:40.77,Default,,90,90,0,,{\an5}want
|
||||||
Dialogue: 0,0:00:42.67,0:00:42.77,Default,,100,100,0,,{\an5}to
|
Dialogue: 0,0:00:40.77,0:00:40.87,Default,,90,90,0,,{\an5}to
|
||||||
Dialogue: 0,0:00:42.77,0:00:43.43,Default,,100,100,0,,{\an5}listen,
|
Dialogue: 0,0:00:40.87,0:00:41.58,Default,,90,90,0,,{\an5}listen,
|
||||||
Dialogue: 0,0:00:43.43,0:00:43.77,Default,,100,100,0,,{\an5}create
|
Dialogue: 0,0:00:41.58,0:00:41.92,Default,,90,90,0,,{\an5}create
|
||||||
Dialogue: 0,0:00:43.77,0:00:44.70,Default,,100,100,0,,{\an5}content,
|
Dialogue: 0,0:00:41.92,0:00:42.65,Default,,90,90,0,,{\an5}content,
|
||||||
Dialogue: 0,0:00:44.70,0:00:44.85,Default,,100,100,0,,{\an5}or
|
Dialogue: 0,0:00:42.65,0:00:42.79,Default,,90,90,0,,{\an5}or
|
||||||
Dialogue: 0,0:00:44.85,0:00:45.07,Default,,100,100,0,,{\an5}bring
|
Dialogue: 0,0:00:42.79,0:00:43.01,Default,,90,90,0,,{\an5}bring
|
||||||
Dialogue: 0,0:00:45.07,0:00:45.22,Default,,100,100,0,,{\an5}your
|
Dialogue: 0,0:00:43.01,0:00:43.12,Default,,90,90,0,,{\an5}your
|
||||||
Dialogue: 0,0:00:45.22,0:00:45.54,Default,,100,100,0,,{\an5}words
|
Dialogue: 0,0:00:43.12,0:00:43.42,Default,,90,90,0,,{\an5}words
|
||||||
Dialogue: 0,0:00:45.54,0:00:45.67,Default,,100,100,0,,{\an5}to
|
Dialogue: 0,0:00:43.42,0:00:43.54,Default,,90,90,0,,{\an5}to
|
||||||
Dialogue: 0,0:00:45.67,0:00:46.58,Default,,100,100,0,,{\an5}life,
|
Dialogue: 0,0:00:43.54,0:00:44.33,Default,,90,90,0,,{\an5}life,
|
||||||
Dialogue: 0,0:00:46.58,0:00:47.10,Default,,100,100,0,,{\an5}Abogen
|
Dialogue: 0,0:00:44.33,0:00:44.90,Default,,90,90,0,,{\an5}Abogen
|
||||||
Dialogue: 0,0:00:47.10,0:00:47.37,Default,,100,100,0,,{\an5}makes
|
Dialogue: 0,0:00:44.90,0:00:45.17,Default,,90,90,0,,{\an5}makes
|
||||||
Dialogue: 0,0:00:47.37,0:00:47.53,Default,,100,100,0,,{\an5}it
|
Dialogue: 0,0:00:45.17,0:00:45.35,Default,,90,90,0,,{\an5}it
|
||||||
Dialogue: 0,0:00:47.53,0:00:48.31,Default,,100,100,0,,{\an5}fast,
|
Dialogue: 0,0:00:45.35,0:00:46.12,Default,,90,90,0,,{\an5}fast,
|
||||||
Dialogue: 0,0:00:48.31,0:00:48.89,Default,,100,100,0,,{\an5}fun,
|
Dialogue: 0,0:00:46.12,0:00:46.65,Default,,90,90,0,,{\an5}fun,
|
||||||
Dialogue: 0,0:00:48.89,0:00:49.02,Default,,100,100,0,,{\an5}and
|
Dialogue: 0,0:00:46.65,0:00:46.77,Default,,90,90,0,,{\an5}and
|
||||||
Dialogue: 0,0:00:49.02,0:00:50.62,Default,,100,100,0,,{\an5}effortless.
|
Dialogue: 0,0:00:46.77,0:00:48.07,Default,,90,90,0,,{\an5}effortless.
|
||||||
Dialogue: 0,0:00:50.62,0:00:51.35,Default,,100,100,0,,{\an5}Experience
|
Dialogue: 0,0:00:48.07,0:00:48.76,Default,,90,90,0,,{\an5}Experience
|
||||||
Dialogue: 0,0:00:51.35,0:00:51.47,Default,,100,100,0,,{\an5}your
|
Dialogue: 0,0:00:48.76,0:00:48.87,Default,,90,90,0,,{\an5}your
|
||||||
Dialogue: 0,0:00:51.47,0:00:52.23,Default,,100,100,0,,{\an5}stories,
|
Dialogue: 0,0:00:48.87,0:00:49.61,Default,,90,90,0,,{\an5}stories,
|
||||||
Dialogue: 0,0:00:52.23,0:00:52.97,Default,,100,100,0,,{\an5}scripts,
|
Dialogue: 0,0:00:49.61,0:00:50.15,Default,,90,90,0,,{\an5}scripts,
|
||||||
Dialogue: 0,0:00:52.97,0:00:53.11,Default,,100,100,0,,{\an5}and
|
Dialogue: 0,0:00:50.15,0:00:50.29,Default,,90,90,0,,{\an5}and
|
||||||
Dialogue: 0,0:00:53.11,0:00:53.64,Default,,100,100,0,,{\an5}ideas
|
Dialogue: 0,0:00:50.29,0:00:50.87,Default,,90,90,0,,{\an5}ideas
|
||||||
Dialogue: 0,0:00:53.64,0:00:53.73,Default,,100,100,0,,{\an5}in
|
Dialogue: 0,0:00:50.87,0:00:50.97,Default,,90,90,0,,{\an5}in
|
||||||
Dialogue: 0,0:00:53.73,0:00:53.83,Default,,100,100,0,,{\an5}a
|
Dialogue: 0,0:00:50.97,0:00:51.10,Default,,90,90,0,,{\an5}a
|
||||||
Dialogue: 0,0:00:53.83,0:00:54.14,Default,,100,100,0,,{\an5}whole
|
Dialogue: 0,0:00:51.10,0:00:51.46,Default,,90,90,0,,{\an5}whole
|
||||||
Dialogue: 0,0:00:54.14,0:00:54.35,Default,,100,100,0,,{\an5}new
|
Dialogue: 0,0:00:51.46,0:00:51.65,Default,,90,90,0,,{\an5}new
|
||||||
Dialogue: 0,0:00:54.35,0:00:54.64,Default,,100,100,0,,{\an5}way
|
Dialogue: 0,0:00:51.65,0:00:51.95,Default,,90,90,0,,{\an5}way
|
||||||
Dialogue: 0,0:00:54.64,0:00:54.79,Default,,100,100,0,,{\an5}with
|
Dialogue: 0,0:00:51.95,0:00:52.10,Default,,90,90,0,,{\an5}with
|
||||||
Dialogue: 0,0:00:54.79,0:00:55.85,Default,,100,100,0,,{\an5}Abogen.
|
Dialogue: 0,0:00:52.10,0:00:53.20,Default,,90,90,0,,{\an5}Abogen.
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user