support original voicevox api

This commit is contained in:
mii443
2025-04-27 17:16:17 +09:00
parent 91044c7c25
commit 83fde399b2
5 changed files with 99 additions and 34 deletions

View File

@ -28,6 +28,7 @@ symphonia-core = "0.5.4"
tokio-util = { version = "0.7.14", features = ["compat"] }
futures = "0.3.31"
bytes = "1.10.1"
voicevox-client = { git = "https://github.com/mii443/rust" }
[dependencies.uuid]
version = "0.8"
@ -43,7 +44,19 @@ features = ["mp3"]
[dependencies.serenity]
version = "0.12"
features = ["builder", "cache", "client", "gateway", "model", "utils", "unstable_discord_api", "collector", "rustls_backend", "framework", "voice"]
features = [
"builder",
"cache",
"client",
"gateway",
"model",
"utils",
"unstable_discord_api",
"collector",
"rustls_backend",
"framework",
"voice",
]
[dependencies.tokio]

View File

@ -6,6 +6,7 @@ pub struct Config {
pub token: String,
pub application_id: u64,
pub redis_url: String,
pub voicevox_key: String,
pub voicevox_key: Option<String>,
pub voicevox_original_api_url: Option<String>,
pub otel_http_url: Option<String>,
}

View File

@ -61,7 +61,14 @@ async fn main() {
let application_id = env::var("NCB_APP_ID").unwrap();
let prefix = env::var("NCB_PREFIX").unwrap();
let redis_url = env::var("NCB_REDIS_URL").unwrap();
let voicevox_key = env::var("NCB_VOICEVOX_KEY").unwrap();
let voicevox_key = match env::var("NCB_VOICEVOX_KEY") {
Ok(key) => Some(key),
Err(_) => None,
};
let voicevox_original_api_url = match env::var("NCB_VOICEVOX_ORIGINAL_API_URL") {
Ok(url) => Some(url),
Err(_) => None,
};
let otel_http_url = match env::var("NCB_OTEL_HTTP_URL") {
Ok(url) => Some(url),
Err(_) => None,
@ -73,6 +80,7 @@ async fn main() {
prefix,
redis_url,
voicevox_key,
voicevox_original_api_url,
otel_http_url,
}
}
@ -91,7 +99,7 @@ async fn main() {
Err(err) => panic!("GCP init error: {}", err),
};
let voicevox = VOICEVOX::new(config.voicevox_key);
let voicevox = VOICEVOX::new(config.voicevox_key, config.voicevox_original_api_url);
let database_client = {
let redis_client = redis::Client::open(config.redis_url).unwrap();

View File

@ -57,23 +57,44 @@ impl TTS {
}
info!("Cache miss for VOICEVOX TTS");
let audio = self
.voicevox_client
.synthesize_stream(text.to_string(), speaker)
.await?;
tokio::spawn({
let cache = self.cache.clone();
let audio = audio.clone();
async move {
info!("Compressing stream audio");
let compressed = Compressed::new(audio.into(), Bitrate::Auto).await.unwrap();
let mut cache_guard = cache.write().unwrap();
cache_guard.put(cache_key, compressed.clone());
}
});
if self.voicevox_client.original_api_url.is_some() {
let audio = self
.voicevox_client
.synthesize_original(text.to_string(), speaker)
.await?;
Ok(audio.into())
tokio::spawn({
let cache = self.cache.clone();
let audio = audio.clone();
async move {
info!("Compressing stream audio");
let compressed = Compressed::new(audio.into(), Bitrate::Auto).await.unwrap();
let mut cache_guard = cache.write().unwrap();
cache_guard.put(cache_key, compressed.clone());
}
});
Ok(audio.into())
} else {
let audio = self
.voicevox_client
.synthesize_stream(text.to_string(), speaker)
.await?;
tokio::spawn({
let cache = self.cache.clone();
let audio = audio.clone();
async move {
info!("Compressing stream audio");
let compressed = Compressed::new(audio.into(), Bitrate::Auto).await.unwrap();
let mut cache_guard = cache.write().unwrap();
cache_guard.put(cache_key, compressed.clone());
}
});
Ok(audio.into())
}
}
#[tracing::instrument]

View File

@ -6,7 +6,8 @@ const BASE_API_URL: &str = "https://deprecatedapis.tts.quest/v2/";
#[derive(Clone, Debug)]
pub struct VOICEVOX {
pub key: String,
pub key: Option<String>,
pub original_api_url: Option<String>,
}
impl VOICEVOX {
@ -34,19 +35,27 @@ impl VOICEVOX {
speaker_list
}
pub fn new(key: String) -> Self {
Self { key }
pub fn new(key: Option<String>, original_api_url: Option<String>) -> Self {
Self {
key,
original_api_url,
}
}
#[tracing::instrument]
async fn get_speaker_list(&self) -> Vec<Speaker> {
let client = reqwest::Client::new();
match client
.post(BASE_API_URL.to_string() + "voicevox/speakers/")
.query(&[("key", self.key.clone())])
.send()
.await
{
let client = if let Some(key) = &self.key {
client
.get(BASE_API_URL.to_string() + "voicevox/speakers/")
.query(&[("key", key)])
} else if let Some(original_api_url) = &self.original_api_url {
client.get(original_api_url.to_string() + "/speakers")
} else {
panic!("No API key or original API URL provided.")
};
match client.send().await {
Ok(response) => response.json().await.unwrap(),
Err(err) => {
panic!("Cannot get speaker list. {err:?}")
@ -66,7 +75,7 @@ impl VOICEVOX {
.query(&[
("speaker", speaker.to_string()),
("text", text),
("key", self.key.clone()),
("key", self.key.clone().unwrap()),
])
.send()
.await
@ -79,6 +88,22 @@ impl VOICEVOX {
}
}
#[tracing::instrument]
pub async fn synthesize_original(
&self,
text: String,
speaker: i64,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
let client =
voicevox_client::Client::new(self.original_api_url.as_ref().unwrap().clone(), None);
let audio_query = client
.create_audio_query(&text, speaker as i32, None)
.await?;
println!("{:?}", audio_query.audio_query);
let audio = audio_query.synthesis(speaker as i32, true).await?;
Ok(audio.into())
}
#[tracing::instrument]
pub async fn synthesize_stream(
&self,
@ -91,7 +116,7 @@ impl VOICEVOX {
.query(&[
("speaker", speaker.to_string()),
("text", text),
("key", self.key.clone()),
("key", self.key.clone().unwrap()),
])
.send()
.await
@ -100,10 +125,7 @@ impl VOICEVOX {
let body = response.text().await.unwrap();
let response: TTSResponse = serde_json::from_str(&body).unwrap();
Ok(Mp3Request::new(
reqwest::Client::new(),
response.mp3_streaming_url,
))
Ok(Mp3Request::new(reqwest::Client::new(), response.mp3_streaming_url).into())
}
Err(err) => Err(Box::new(err)),
}