step2
This commit is contained in:
+57
-2
@@ -5,6 +5,12 @@
|
||||
//! mixer's output rate, so samples are pushed directly into the TTS queue
|
||||
//! without resampling. A [`CancellationToken`] allows immediate interruption
|
||||
//! (barge-in).
|
||||
//!
|
||||
//! Drama 3 can sound overly theatrical on some references. Two mitigations are
|
||||
//! applied here, both configurable from the environment: optional sampling
|
||||
//! knobs (`temperature` / `top_p` / `top_k`) that trim the tails of the
|
||||
//! acoustic token distribution, and a neutral style tag prepended to untagged
|
||||
//! requests so plain LLM prose is read in a conversational register.
|
||||
|
||||
use crate::audio::SampleRingBuffer;
|
||||
use crate::config::Config;
|
||||
@@ -26,6 +32,11 @@ struct Reference {
|
||||
}
|
||||
|
||||
/// Request body for the SGLang `/v1/audio/speech` endpoint.
|
||||
///
|
||||
/// Sampling knobs are optional and only serialized when set. Drama 3 accepts
|
||||
/// `temperature`, `top_p` and `top_k` at this level; `repetition_penalty` is
|
||||
/// deliberately never sent because this server rejects any value other than
|
||||
/// `1.0` (`Drama 3 request: repetition_penalty must be 1.0`).
|
||||
#[derive(Debug, Serialize)]
|
||||
struct SpeechRequest<'a> {
|
||||
model: &'a str,
|
||||
@@ -35,6 +46,12 @@ struct SpeechRequest<'a> {
|
||||
stream: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
references: Option<Vec<Reference>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
temperature: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
top_p: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
top_k: Option<u32>,
|
||||
}
|
||||
|
||||
/// Streaming TTS client backed by Fish Audio / SGLang-Omni Drama 3.
|
||||
@@ -46,6 +63,12 @@ pub struct FishAudioTts {
|
||||
voice: String,
|
||||
/// Current voice-cloning reference (config default, overridable at runtime).
|
||||
reference: Arc<parking_lot::RwLock<Option<Reference>>>,
|
||||
/// Optional sampling overrides (de-emphasis of the recitative register).
|
||||
temperature: Option<f32>,
|
||||
top_p: Option<f32>,
|
||||
top_k: Option<u32>,
|
||||
/// Neutral style tag prepended to untagged synthesis requests.
|
||||
style_tag: String,
|
||||
}
|
||||
|
||||
impl FishAudioTts {
|
||||
@@ -61,6 +84,13 @@ impl FishAudioTts {
|
||||
"TTS voice-cloning reference configured"
|
||||
);
|
||||
}
|
||||
info!(
|
||||
temperature = ?config.tts_temperature,
|
||||
top_p = ?config.tts_top_p,
|
||||
top_k = ?config.tts_top_k,
|
||||
style_tag = %config.tts_style_tag,
|
||||
"TTS de-emphasis settings"
|
||||
);
|
||||
Self {
|
||||
http: Client::builder()
|
||||
.build()
|
||||
@@ -69,6 +99,23 @@ impl FishAudioTts {
|
||||
model: config.tts_model.clone(),
|
||||
voice: config.tts_voice.clone(),
|
||||
reference: Arc::new(parking_lot::RwLock::new(reference)),
|
||||
temperature: config.tts_temperature,
|
||||
top_p: config.tts_top_p,
|
||||
top_k: config.tts_top_k,
|
||||
style_tag: config.tts_style_tag.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The text actually sent to the model: the configured neutral style tag is
|
||||
/// prepended unless the text already opens with a style tag or the tag is
|
||||
/// disabled. LLM replies carry no tags, so they get de-emphasized; clip
|
||||
/// banks that start with their own tag are left untouched.
|
||||
fn styled<'a>(&self, text: &'a str) -> std::borrow::Cow<'a, str> {
|
||||
let tag = self.style_tag.trim();
|
||||
if tag.is_empty() || text.trim_start().starts_with('[') {
|
||||
std::borrow::Cow::Borrowed(text)
|
||||
} else {
|
||||
std::borrow::Cow::Owned(format!("{tag} {text}"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,14 +154,18 @@ impl TtsEngine for FishAudioTts {
|
||||
cancel: &CancellationToken,
|
||||
) -> Result<()> {
|
||||
let references = self.reference.read().clone().map(|r| vec![r]);
|
||||
let input = self.styled(text);
|
||||
|
||||
let body = SpeechRequest {
|
||||
model: &self.model,
|
||||
input: text,
|
||||
input: &input,
|
||||
voice: &self.voice,
|
||||
response_format: "pcm",
|
||||
stream: true,
|
||||
references,
|
||||
temperature: self.temperature,
|
||||
top_p: self.top_p,
|
||||
top_k: self.top_k,
|
||||
};
|
||||
|
||||
info!(model = %self.model, text_len = text.len(), "SGLang TTS request");
|
||||
@@ -125,7 +176,11 @@ impl TtsEngine for FishAudioTts {
|
||||
response_format = "pcm",
|
||||
stream = true,
|
||||
has_reference = body.references.is_some(),
|
||||
text = %text,
|
||||
temperature = ?self.temperature,
|
||||
top_p = ?self.top_p,
|
||||
top_k = ?self.top_k,
|
||||
styled = input.len() != text.len(),
|
||||
text = %input,
|
||||
"TTS request dispatched"
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user