Skip to main content

ChapterTTS

Struct ChapterTTS 

Source
pub struct ChapterTTS {
    pub texts: Vec<TextSegment>,
    pub cancel_token: CancellationToken,
    pub active_index: Arc<Mutex<usize>>,
    pub tts: Arc<KokoroTts>,
    pub queue: Option<Arc<TTSQueueInput<SamplesBuffer>>>,
    pub generate_index: usize,
}
Expand description

TTS章节处理器,负责将文本转换为音频并管理播放队列

Fields§

§texts: Vec<TextSegment>§cancel_token: CancellationToken

取消令牌,用于取消TTS处理

§active_index: Arc<Mutex<usize>>§tts: Arc<KokoroTts>§queue: Option<Arc<TTSQueueInput<SamplesBuffer>>>§generate_index: usize

Implementations§

Source§

impl ChapterTTS

Source

pub fn new(tts: Arc<KokoroTts>, text: &str) -> Self

创建新的TTS章节处理器

§参数
  • tts - TTS引擎实例
§返回值

返回一个新的ChapterTTS实例

Source

pub fn stream( &mut self, voice: Voice, on_error: impl Fn(NovelTTSError) + Send + Sync + 'static, ) -> (TTSQueueOutput<SamplesBuffer>, Receiver<Option<usize>>)

流式处理文本并生成音频

将输入的文本按行分割,逐行转换为音频,并提供字符位置追踪功能。

§参数
  • text - 要转换的文本
  • voice - 使用的语音
  • on_error - 错误处理回调
§返回值

返回元组,包含音频队列输出和字符位置接收器

§注意事项
  • 音频是流式生成的,可以边生成边播放
  • 字符位置通过Receiver通道实时返回
  • 如果需要取消处理,可以调用cancel方法
Examples found in repository?
examples/basic.rs (lines 93-95)
5async fn main() -> Result<(), Box<dyn std::error::Error>> {
6    // 初始化模型和语音数据
7    let mut model = CheckpointModel::default();
8    let mut voices = VoicesData::default();
9
10    // 检查并下载必要的模型文件
11    if !model.is_downloaded() {
12        model
13            .async_download(|downloaded, total| {
14                println!("模型下载进度: {}/{}", downloaded, total);
15            })
16            .await?;
17    }
18
19    if !voices.is_downloaded() {
20        voices
21            .async_download(|downloaded, total| {
22                println!("语音数据下载进度: {}/{}", downloaded, total);
23            })
24            .await?;
25    }
26
27    // 创建TTS实例
28    let novel_tts = NovelTTS::new(&model, &voices).await?;
29    let text = "第005章 李小曼
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  两人都波澜不惊,礼貌性的相互问候,没有久别重逢后的喜悦,有的只是平淡如水,甚至有些云淡风轻的味道。
85  没有过多的话语,轻轻擦肩而过,有些事情无需多说,无言就是一种结果。";
86
87    let mut stream_handle = rodio::OutputStreamBuilder::open_default_stream()?;
88    stream_handle.log_on_drop(false);
89    let sink = rodio::Sink::connect_new(stream_handle.mixer());
90    let mut chapter_tts = novel_tts.chapter_tts(text);
91
92    // 流式处理文本到音频
93    let (audio_queue, mut position_rx) = chapter_tts.stream(Voice::Zf006(1), |error| {
94        eprintln!("TTS处理错误: {:?}", error)
95    });
96
97    // 监听字符位置更新
98    tokio::spawn(async move {
99        let active_index = chapter_tts.texts;
100        while let Some(index) = position_rx.recv().await {
101            if let Some(index) = index {
102                if let Some(chunk) = active_index.get(index) {
103                    println!("当前播放文本:{:?}", &text[chunk.start..chunk.end]);
104                }
105            } else {
106                println!("播放完成");
107            }
108        }
109    });
110
111    // 播放音频(需要rodio或其他音频播放库配合使用)
112
113    sink.append(audio_queue);
114
115    sink.sleep_until_end();
116
117    Ok(())
118}
Source

pub fn cancel(&self)

取消当前的TTS处理

调用此方法会取消正在进行的TTS处理任务

Source

pub fn set_index(&self, index: usize)

设置当前处理的章节索引

Source

pub fn retrieve_output( &self, index: usize, ) -> Option<TTSQueueOutput<SamplesBuffer>>

检索指定索引的音频队列输出

Trait Implementations§

Source§

impl Clone for ChapterTTS

Source§

fn clone(&self) -> ChapterTTS

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more