2
0

rm gcloud

This commit is contained in:
2026-03-29 22:33:38 +09:00
parent bbb6e07b3a
commit aaebea081d
4 changed files with 21 additions and 31 deletions

1
.gitignore vendored
View File

@@ -26,3 +26,4 @@ Thumbs.db
/example
/config
/model
/examples

View File

@@ -20,7 +20,7 @@ libc = "0.2"
notify = { version = "7", features = ["macos_fsevent"] }
ratatui = "0.29"
crossterm = "0.28"
reqwest = { version = "0.12", features = ["json", "blocking"] }
reqwest = { version = "0.12", features = ["json", "blocking", "multipart"] }
rodio = "0.19"
base64 = "0.22"
cpal = "0.15"

View File

@@ -40,7 +40,7 @@ impl VoiceConfig {
tts_api_key,
tts_voice_id: std::env::var("ELEVENLABS_VOICE_ID").unwrap_or_default(),
tts_model: std::env::var("ELEVENLABS_MODEL_ID").unwrap_or_else(|_| "eleven_multilingual_v2".into()),
stt_language: std::env::var("STT_LANGUAGE").unwrap_or_else(|_| "ja-JP".into()),
stt_language: std::env::var("STT_LANGUAGE").unwrap_or_else(|_| "ja".into()),
})
}

View File

@@ -2,7 +2,7 @@ use crate::voice::VoiceConfig;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use std::sync::{Arc, Mutex, mpsc};
/// Record audio via VAD and recognize speech via Google Cloud STT.
/// Record audio via VAD and recognize speech via ElevenLabs STT.
pub fn recognize(config: &VoiceConfig) -> Result<String, String> {
let audio = record_vad().map_err(|e| format!("recording: {e}"))?;
if audio.is_empty() {
@@ -121,32 +121,24 @@ fn record_vad() -> Result<Vec<i16>, String> {
Ok(resample(&audio_buf, device_rate, 16000, output_len))
}
/// Send audio to Google Cloud STT and return transcript.
/// Send audio to ElevenLabs STT and return transcript.
fn transcribe(config: &VoiceConfig, audio: &[i16]) -> Result<String, String> {
let api_key = std::env::var("GOOGLE_API_KEY")
.map_err(|_| "GOOGLE_API_KEY not set".to_string())?;
// Convert i16 samples to WAV bytes
let wav_data = encode_wav(audio, 16000);
let encoded = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, &wav_data);
let body = serde_json::json!({
"config": {
"encoding": "LINEAR16",
"sampleRateHertz": 16000,
"languageCode": config.stt_language,
},
"audio": {
"content": encoded
}
});
let part = reqwest::blocking::multipart::Part::bytes(wav_data)
.file_name("audio.wav")
.mime_str("audio/wav")
.map_err(|e| format!("mime: {e}"))?;
let url = "https://speech.googleapis.com/v1/speech:recognize";
let form = reqwest::blocking::multipart::Form::new()
.text("model_id", "scribe_v1")
.text("language_code", config.stt_language.clone())
.part("file", part);
let client = reqwest::blocking::Client::new();
let resp = client.post(url)
.header("x-goog-api-key", &api_key)
.json(&body)
let resp = client.post("https://api.elevenlabs.io/v1/speech-to-text")
.header("xi-api-key", &config.tts_api_key)
.multipart(form)
.send()
.map_err(|e| format!("STT request: {e}"))?;
@@ -159,7 +151,7 @@ fn transcribe(config: &VoiceConfig, audio: &[i16]) -> Result<String, String> {
let json: serde_json::Value = resp.json()
.map_err(|e| format!("STT parse: {e}"))?;
let transcript = json["results"][0]["alternatives"][0]["transcript"]
let transcript = json["text"]
.as_str()
.unwrap_or("")
.to_string();
@@ -173,20 +165,17 @@ fn encode_wav(samples: &[i16], sample_rate: u32) -> Vec<u8> {
let file_len = 36 + data_len;
let mut buf = Vec::with_capacity(file_len as usize + 8);
// RIFF header
buf.extend_from_slice(b"RIFF");
buf.extend_from_slice(&file_len.to_le_bytes());
buf.extend_from_slice(b"WAVE");
// fmt chunk
buf.extend_from_slice(b"fmt ");
buf.extend_from_slice(&16u32.to_le_bytes()); // chunk size
buf.extend_from_slice(&16u32.to_le_bytes());
buf.extend_from_slice(&1u16.to_le_bytes()); // PCM
buf.extend_from_slice(&1u16.to_le_bytes()); // mono
buf.extend_from_slice(&sample_rate.to_le_bytes());
buf.extend_from_slice(&(sample_rate * 2).to_le_bytes()); // byte rate
buf.extend_from_slice(&2u16.to_le_bytes()); // block align
buf.extend_from_slice(&16u16.to_le_bytes()); // bits per sample
// data chunk
buf.extend_from_slice(&(sample_rate * 2).to_le_bytes());
buf.extend_from_slice(&2u16.to_le_bytes());
buf.extend_from_slice(&16u16.to_le_bytes());
buf.extend_from_slice(b"data");
buf.extend_from_slice(&data_len.to_le_bytes());
for &s in samples {