diff --git a/crates/zeph-llm/src/candle_whisper.rs b/crates/zeph-llm/src/candle_whisper.rs index 5460b1f58..8986da8b7 100644 --- a/crates/zeph-llm/src/candle_whisper.rs +++ b/crates/zeph-llm/src/candle_whisper.rs @@ -217,47 +217,49 @@ impl SpeechToText for CandleWhisperProvider { } fn decode_audio(bytes: &[u8]) -> Result, LlmError> { - use symphonia::core::audio::SampleBuffer; - use symphonia::core::codecs::DecoderOptions; - use symphonia::core::formats::FormatOptions; + use symphonia::core::codecs::CodecParameters; + use symphonia::core::codecs::audio::AudioDecoderOptions; + use symphonia::core::formats::probe::Hint; + use symphonia::core::formats::{FormatOptions, TrackType}; use symphonia::core::io::{MediaSourceStream, MediaSourceStreamOptions}; use symphonia::core::meta::MetadataOptions; - use symphonia::core::probe::Hint; let cursor = Cursor::new(bytes.to_vec()); let mss = MediaSourceStream::new(Box::new(cursor), MediaSourceStreamOptions::default()); - let probed = symphonia::default::get_probe() - .format( + let mut format = symphonia::default::get_probe() + .probe( &Hint::new(), mss, - &FormatOptions::default(), - &MetadataOptions::default(), + FormatOptions::default(), + MetadataOptions::default(), ) .map_err(|e| LlmError::TranscriptionFailed(format!("probe: {e}")))?; - let mut format = probed.format; let track = format - .default_track() + .default_track(TrackType::Audio) .ok_or_else(|| LlmError::TranscriptionFailed("no audio track".into()))?; - let sample_rate = track - .codec_params + let audio_params = match track.codec_params.as_ref() { + Some(CodecParameters::Audio(p)) => p.clone(), + _ => return Err(LlmError::TranscriptionFailed("non-audio track".into())), + }; + let sample_rate = audio_params .sample_rate .ok_or_else(|| LlmError::TranscriptionFailed("unknown sample rate".into()))?; - let channels = track - .codec_params + let channels = audio_params .channels + .as_ref() .map_or(1, symphonia::core::audio::Channels::count); let track_id = track.id; let mut decoder = symphonia::default::get_codecs() - .make(&track.codec_params, &DecoderOptions::default()) + .make_audio_decoder(&audio_params, &AudioDecoderOptions::default()) .map_err(|e| LlmError::TranscriptionFailed(format!("decoder: {e}")))?; let mut pcm = Vec::new(); - while let Ok(packet) = format.next_packet() { - if packet.track_id() != track_id { + while let Ok(Some(packet)) = format.next_packet() { + if packet.track_id != track_id { continue; } let audio_buf = match decoder.decode(&packet) { @@ -267,10 +269,9 @@ fn decode_audio(bytes: &[u8]) -> Result, LlmError> { continue; } }; - let spec = *audio_buf.spec(); - let mut sample_buf = SampleBuffer::::new(audio_buf.capacity() as u64, spec); - sample_buf.copy_interleaved_ref(audio_buf); - let samples = sample_buf.samples(); + let n_samples = audio_buf.frames() * audio_buf.spec().channels().count(); + let mut samples: Vec = vec![0f32; n_samples]; + audio_buf.copy_to_slice_interleaved(&mut samples[..]); if channels > 1 { for chunk in samples.chunks(channels) { @@ -279,7 +280,7 @@ fn decode_audio(bytes: &[u8]) -> Result, LlmError> { pcm.push(avg); } } else { - pcm.extend_from_slice(samples); + pcm.extend_from_slice(&samples); } } diff --git a/crates/zeph-llm/src/gonka/signer.rs b/crates/zeph-llm/src/gonka/signer.rs index 3fd4e4a28..d04237221 100644 --- a/crates/zeph-llm/src/gonka/signer.rs +++ b/crates/zeph-llm/src/gonka/signer.rs @@ -147,17 +147,18 @@ impl RequestSigner { /// Algorithm: SHA-256(compressed_pubkey) → RIPEMD-160 → `bech32(chain_prefix, data)`. /// This matches the Bitcoin/Cosmos address derivation standard. fn derive_address(pubkey: &k256::PublicKey, chain_prefix: &str) -> String { - use bech32::ToBase32 as _; + use bech32::{Bech32, Hrp}; let compressed = pubkey.to_encoded_point(true); let sha_hash = sha2::Sha256::digest(compressed.as_bytes()); let sha_bytes: &[u8] = &sha_hash[..]; let ripe_hash = ripemd::Ripemd160::digest(sha_bytes); let ripe_bytes: &[u8] = &ripe_hash[..]; - let data5 = ripe_bytes.to_base32(); // bech32::encode only fails on invalid HRP characters, which chain_prefix // is guaranteed to avoid in practice (lowercase ASCII letters only). - bech32::encode(chain_prefix, data5, bech32::Variant::Bech32) + let hrp = Hrp::parse(chain_prefix) + .unwrap_or_else(|e| panic!("invalid bech32 HRP '{chain_prefix}': {e}")); + bech32::encode::(hrp, ripe_bytes) .unwrap_or_else(|e| panic!("bech32 encode failed for prefix '{chain_prefix}': {e}")) }